diff --git a/CLAUDE.md b/CLAUDE.md index 21eee4b..1a297b6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,7 +26,15 @@ ctest --output-on-failure cpack -G DEB ``` -Alternatively, `libsmart_tools/run` provides a Docker-based build environment. +When `cmake` is not installed locally, use `libsmart_tools/run` to build inside a Docker container. It wraps any command with the correct build environment: + +```bash +# Build inside Docker +libsmart_tools/run bash -c "cd build && cmake .. && cmake --build . -j4" + +# Build with tests inside Docker +libsmart_tools/run bash -c "cd build && cmake -DBUILD_TESTS=ON .. && cmake --build . -j4 && ctest --output-on-failure" +``` ## Architecture @@ -49,7 +57,17 @@ Alternatively, `libsmart_tools/run` provides a Docker-based build environment. - `smart::ends_with` - Suffix check (requires suffix strictly shorter than string) - `smart::Path::combine` - Path joining (always adds separator, doesn't normalize) +### WAV File Components + +- **WavFile** (`smart/WavFile.h`) - Base WAV file writer producing standard RIFF/WAVE with `fmt` and `data` chunks. Supports `fillBuffer` (in-memory) and `writeFile` (to disk). +- **WavFileDisk** (`smart/WavFileDisk.h`) - Streaming WAV writer for incremental sample output to disk. +- **WavFileSimple** (`smart/WavFileSimple.h`) - Extended WAV writer supporting cue points and LIST/adtl metadata (labels, notes, files). +- **wav_verify** (`tests/wav_verify.h`) - Header-only WAV structure validator. `wav_verify()` checks an in-memory buffer; `wav_verify_file()` reads from disk. Reports issues (errors/warnings/info) and parses fmt, data, cue, and LIST/adtl chunks. + +WAV format reference documentation is in `doc/` — see `doc/CLAUDE.md` for a guide to each document. + ### Build Outputs - `libsmart.so` - Shared library - `uio` - CLI tool for UIO device interaction (links against libcrack2) +- `wav-verify` - CLI tool for WAV file structure verification diff --git a/CMakeLists.txt b/CMakeLists.txt index 489ca91..68e1b9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,11 @@ set(CPACK_DEBIAN_TOOLS_DESCRIPTION¶ "Tools and sample programs based on libsmar find_package(PkgConfig) find_package(Threads) +include(FetchContent) + # Testing option(BUILD_TESTS "Build unit tests" OFF) if(BUILD_TESTS) - include(FetchContent) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git @@ -102,16 +103,11 @@ INSTALL( COMPONENT smartdev ) -# uio -file(GLOB uio_sources src_uio/*.cpp) -add_executable(uio ${uio_sources} ) -target_compile_features(uio PUBLIC cxx_std_20) # C++ 20 adds coroutines. -if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11) - target_compile_options(uio PUBLIC "-fcoroutines") -endif() -target_link_libraries(uio smart crack crypt m) -target_compile_options(uio PUBLIC -I${CMAKE_CURRENT_SOURCE_DIR}) -install(TARGETS uio RUNTIME DESTINATION bin COMPONENT tools) +# Apps +add_subdirectory(apps) + +# Examples +add_subdirectory(examples) # Debian packages include(CPack) # this must come after all install statements. diff --git a/README.md b/README.md index 8cd5d85..43bf59d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ C++ routines for: This is in use in customer project firmware, both Petalinux and Debian. +# Examples + +The `examples/` directory contains sample programs: + +* **uart_terminal** — Simple serial terminal for a 16550-compatible UART exposed via UIO. Multiplexes keyboard input and UART RX interrupts using `poll()`. +* **gpio_blink** — Blinks the lowest bit of a Xilinx AXI GPIO at 1 Hz via UIO. # Building diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt new file mode 100644 index 0000000..e8f36b9 --- /dev/null +++ b/apps/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(uio) +add_subdirectory(wav-verify) diff --git a/apps/uio/CMakeLists.txt b/apps/uio/CMakeLists.txt new file mode 100644 index 0000000..026c529 --- /dev/null +++ b/apps/uio/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB uio_sources *.cpp) +add_executable(uio ${uio_sources}) +target_compile_features(uio PUBLIC cxx_std_20) +if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11) + target_compile_options(uio PUBLIC "-fcoroutines") +endif() +target_link_libraries(uio smart crack crypt m) +target_compile_options(uio PUBLIC -I${CMAKE_SOURCE_DIR}) +install(TARGETS uio RUNTIME DESTINATION bin COMPONENT tools) diff --git a/src_uio/uio_main.cpp b/apps/uio/uio_main.cpp similarity index 100% rename from src_uio/uio_main.cpp rename to apps/uio/uio_main.cpp diff --git a/apps/wav-verify/CMakeLists.txt b/apps/wav-verify/CMakeLists.txt new file mode 100644 index 0000000..de1d29c --- /dev/null +++ b/apps/wav-verify/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(wav-verify wav_verify_main.cpp) +target_compile_features(wav-verify PUBLIC cxx_std_20) +target_compile_options(wav-verify PUBLIC -I${CMAKE_SOURCE_DIR}/tests) +install(TARGETS wav-verify RUNTIME DESTINATION bin COMPONENT tools) diff --git a/apps/wav-verify/wav_verify_main.cpp b/apps/wav-verify/wav_verify_main.cpp new file mode 100644 index 0000000..90ddf72 --- /dev/null +++ b/apps/wav-verify/wav_verify_main.cpp @@ -0,0 +1,20 @@ +#include +#include "wav_verify.h" + +int main(int argc, char** argv) { + if (argc < 2) { + fprintf(stderr, "Usage: wav-verify FILE...\n"); + return 1; + } + + bool any_errors = false; + for (int i = 1; i < argc; ++i) { + auto r = wav_verify_file(argv[i]); + printf("%s: %s\n", argv[i], r.valid ? "OK" : "FAIL"); + if (!r.valid) { + printf("%s", r.summary().c_str()); + any_errors = true; + } + } + return any_errors ? 1 : 0; +} diff --git a/doc/CLAUDE.md b/doc/CLAUDE.md new file mode 100644 index 0000000..566d3e5 --- /dev/null +++ b/doc/CLAUDE.md @@ -0,0 +1,13 @@ +# Introduction +This directory holds reference documentation for file formats used by libsmart. + +# WAV / RIFF WAVE file format + +When working on WAV-related code (`smart/WavFile.h`, `smart/WavFileDisk.h`, `smart/WavFileSimple.h`, `tests/wav_verify.h`, `apps/wav-verify`), consult these references: + +- **WAVE_Specification.md** — Primary reference. Readable markdown covering the RIFF/WAVE structure, `fmt` chunk variants (PCM, non-PCM, extensible), `fact` chunk, `data` chunk, format codes, and full byte-layout examples. Start here for any WAV format question. +- **riffmci.pdf** — Original Microsoft RIFF/WAVE specification v1.0 (1991). Pages 56-65 cover WAVE. Authoritative for base chunk definitions and RIFF container rules. +- **RIFFNEW.pdf** — Microsoft Revision 3.0 update (1994). Pages 12-22 cover WAVE extensions including the `fact` chunk requirement for non-PCM formats and `cbSize` extension field. +- **Multiple_channel_audio_data_and_WAVE_files.pdf** — Microsoft spec for `WAVE_FORMAT_EXTENSIBLE` (0xFFFE), multi-channel speaker masks, and `wValidBitsPerSample`. +- **rfc2361.txt** — IANA registry of WAVE format codec codes (`wFormatTag` values). +- **Pages_from_mmreg.h.pdf** - List of chunks, this document shows a huge number of (proprietary) compressed formats, most of which are now obsolete. diff --git a/doc/Multiple_channel_audio_data_and_WAVE_files.pdf b/doc/Multiple_channel_audio_data_and_WAVE_files.pdf new file mode 100644 index 0000000..ee8cca5 Binary files /dev/null and b/doc/Multiple_channel_audio_data_and_WAVE_files.pdf differ diff --git a/doc/Pages_from_mmreg.h.pdf b/doc/Pages_from_mmreg.h.pdf new file mode 100644 index 0000000..176129f Binary files /dev/null and b/doc/Pages_from_mmreg.h.pdf differ diff --git a/doc/RIFFNEW.pdf b/doc/RIFFNEW.pdf new file mode 100644 index 0000000..e412b92 Binary files /dev/null and b/doc/RIFFNEW.pdf differ diff --git a/doc/WAVE_Specification.md b/doc/WAVE_Specification.md new file mode 100644 index 0000000..6784b27 --- /dev/null +++ b/doc/WAVE_Specification.md @@ -0,0 +1,257 @@ +# Audio File Format Specifications + +**File Description:** WAVE or RIFF WAVE sound file +**File Extension:** Commonly `.wav`, sometimes `.wave` +**File Byte Order:** Little-endian + +[Prof. Peter Kabal](http://www.mcgill.ca/directory/staff/?LastName=kabal&FirstName=peter), MMSP Lab, ECE, McGill University: Last update: 2022-09-27 + +--- + +## WAVE Specifications + +The WAVE file specifications came from Microsoft. The WAVE file format uses RIFF chunks, each chunk consisting of a chunk identifier, chunk length and chunk data. + +- **WAVE specifications, Version 1.0, 1991-08:** + [riffmci.rtf](http://www.seanet.com/Users/matts/riffmci/riffmci.rtf) (broken link) + Local copy: [Multimedia Programming Interface and Data Specifications 1.0](riffmci.pdf) (see pages 56-65) + +- **WAVE update (Revision: 3.0), 1994-04-15:** + [Multimedia Registration Kit Revision 3.0 (Q120253)](http://support.microsoft.com/support/kb/articles/Q120/2/53.asp) (broken link) + Local copy: [New Multimedia Data Types and Data Techniques](RIFFNEW.pdf) (see pages 12-22) + +- **Multiple channel audio data, 2017-06-01:** + [Multiple Channel Audio Data and WAVE Files](https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653308(v=vs.85)) + Local copy: [Multiple Channel Audio Data and WAVE Files](Multiple_channel_audio_data_and_WAVE_files.pdf) + +The European Broadcast Union (EBU) has standardized on an extension to the WAVE format that they call Broadcast WAVE format (BWF). It is aimed at carrying PCM or MPEG audio data. In its simplest form, it adds a `` chunk with additional metadata. Full documentation is available online from the EBU. + +--- + +## Data Types + +The data in WAVE files can be of many different types. Data format codes are listed in the following: + +- **Internet RFC, Codec registrations, 1998-06:** + + Local copy: [rfc2361.txt](rfc2361.txt) + +- **Microsoft include files** (part of the MSVC compiler or the *DirectX SDK*: from [Microsoft Download Center](http://www.microsoft.com/downloads/search.asp)). For new installations of Visual Studio, the `mmreg.h` include file is installed into `C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared`. This document shows a huge number of (proprietary) compressed formats, most of which are now obsolete. + Local copy: [mmreg.h](Pages_from_mmreg.h.pdf) (extract of Version 1.58) + +--- + +## Wave File Format + +Wave files have a master RIFF chunk which includes a WAVE identifier followed by sub-chunks. The data is stored in little-endian byte order. + +| Field | | Length | Contents | +|----------|-------------|--------|-----------------------------------------------------------| +| `ckID` | | 4 | Chunk ID: `"RIFF"` | +| `cksize` | | 4 | Chunk size: `4+n` | +| | `WAVEID` | 4 | WAVE ID: `"WAVE"` | +| | WAVE chunks | `n` | Wave chunks containing format information and sampled data | + +--- + +## fmt Chunk + +The `fmt` chunk specifies the format of the data. There are 3 variants of the Format chunk for sampled data. These differ in the extensions to the basic `fmt` chunk. + +| Field | | Length | Contents | +|----------|-----------------------|--------|----------------------------------------------| +| `ckID` | | 4 | Chunk ID: `"fmt "` | +| `cksize` | | 4 | Chunk size: 16, 18 or 40 | +| | `wFormatTag` | 2 | Format code | +| | `nChannels` | 2 | Number of interleaved channels | +| | `nSamplesPerSec` | 4 | Sampling rate (blocks per second) | +| | `nAvgBytesPerSec` | 4 | Data rate | +| | `nBlockAlign` | 2 | Data block size (bytes) | +| | `wBitsPerSample` | 2 | Bits per sample | +| | `cbSize` | 2 | Size of the extension (0 or 22) | +| | `wValidBitsPerSample` | 2 | Number of valid bits | +| | `dwChannelMask` | 4 | Speaker position mask | +| | `SubFormat` | 16 | GUID, including the data format code | + +The standard format codes for waveform data are given below. The references above give more format codes for compressed data, a good fraction of which are now obsolete. + +| Format Code | PreProcessor Symbol | Data | +|-------------|----------------------------|--------------------------| +| `0x0001` | `WAVE_FORMAT_PCM` | PCM | +| `0x0003` | `WAVE_FORMAT_IEEE_FLOAT` | IEEE float | +| `0x0006` | `WAVE_FORMAT_ALAW` | 8-bit ITU-T G.711 A-law | +| `0x0007` | `WAVE_FORMAT_MULAW` | 8-bit ITU-T G.711 µ-law | +| `0xFFFE` | `WAVE_FORMAT_EXTENSIBLE` | Determined by `SubFormat`| + +### PCM Format + +The first part of the Format chunk is used to describe PCM data. + +- For PCM data, the Format chunk in the header declares the number of bits/sample in each sample (`wBitsPerSample`). The original documentation (Revision 1) specified that the number of bits per sample is to be rounded up to the next multiple of 8 bits. This rounded-up value is the container size. This information is redundant in that the container size (in bytes) for each sample can also be determined from the block size divided by the number of channels (`nBlockAlign / nChannels`). + - This redundancy has been appropriated to define new formats. For instance, *Cool Edit* uses a format which declares a sample size of 24 bits together with a container size of 4 bytes (32 bits) determined from the block size and number of channels. With this combination, the data is actually stored as 32-bit IEEE floats. The normalization (full scale 2²³) is however different from the standard float format. + +- PCM data is two's-complement except for resolutions of 1-8 bits, which are represented as offset binary. + +### Non-PCM Formats + +An extended Format chunk is used for non-PCM data. The `cbSize` field gives the size of the extension. + +- For all formats other than PCM, the Format chunk *must* have an extended portion. The extension can be of zero length, but the size field (with value 0) must be present. +- For float data, full scale is 1. The bits/sample would normally be 32 or 64. +- For the log-PCM formats (µ-law and A-law), the Rev. 3 documentation indicates that the bits/sample field (`wBitsPerSample`) should be set to 8 bits. +- The non-PCM formats must have a `fact` chunk. + +### Extensible Format + +The `WAVE_FORMAT_EXTENSIBLE` format code indicates that there is an extension to the Format chunk. The extension has one field which declares the number of "valid" bits/sample (`wValidBitsPerSample`). Another field (`dwChannelMask`) contains bits which indicate the mapping from channels to loudspeaker positions. The last field (`SubFormat`) is a 16-byte globally unique identifier (GUID). + +- With the `WAVE_FORMAT_EXTENSIBLE` format, the original bits/sample field (`wBitsPerSample`) must match the container size (`8 * nBlockAlign / nChannels`). This means that `wBitsPerSample` must be a multiple of 8. Reduced precision within the container size is now specified by `wValidBitsPerSample`. + +- The number of valid bits (`wValidBitsPerSample`) is informational only. The data is correctly represented in the precision of the container size. The number of valid bits can be any value from 1 to the container size in bits. + +- The loudspeaker position mask uses 18 bits, each bit corresponding to a speaker position (e.g. Front Left or Top Back Right), to indicate the channel to speaker mapping. More details are in the document cited above. This field is informational. An all-zero field indicates that channels are mapped to outputs in order: first channel to first output, second channel to second output, etc. + +- The first two bytes of the GUID form the sub-code specifying the data format code, e.g. `WAVE_FORMAT_PCM`. The remaining 14 bytes contain a fixed string: + ``` + \x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71 + ``` + +The `WAVE_FORMAT_EXTENSIBLE` format should be used whenever: + +- PCM data has more than 16 bits/sample. +- The number of channels is more than 2. +- The actual number of bits/sample is not equal to the container size. +- The mapping from channels to speakers needs to be specified. + +--- + +## fact Chunk + +All (compressed) non-PCM formats *must* have a `fact` chunk (Rev. 3 documentation). The chunk contains at least one value, the number of samples in the file. + +| Field | | Length | Contents | +|----------|------------------|-----------|----------------------------------| +| `ckID` | | 4 | Chunk ID: `"fact"` | +| `cksize` | | 4 | Chunk size: minimum 4 | +| | `dwSampleLength` | 4 | Number of samples (per channel) | + +- The Rev. 3 documentation states that the Fact chunk "is required for all new WAVE formats", but "is not required" for the standard `WAVE_FORMAT_PCM` file. One presumes that files with IEEE float data (introduced after the Rev. 3 documentation) need a `fact` chunk. + +- The number of samples field is redundant for sampled data, since the Data chunk indicates the length of the data. The number of samples can be determined from the length of the data and the container size as determined from the Format chunk. + +- There is an ambiguity as to the meaning of "number of samples" for multichannel data. The implication in the Rev. 3 documentation is that it should be interpreted to be "number of samples per channel". The statement in the Rev. 3 documentation is: + + > The `nSamplesPerSec` field from the wave format header is used in conjunction with the `dwSampleLength` field to determine the length of the data in seconds. + + With no mention of the number of channels in this computation, this implies that `dwSampleLength` is the number of samples per channel. + +- There is a question as to whether the `fact` chunk should be used for (including those with PCM) `WAVE_FORMAT_EXTENSIBLE` files. One example of a `WAVE_FORMAT_EXTENSIBLE` with PCM data from Microsoft, does not have a `fact` chunk. + +--- + +## data Chunk + +The `data` chunk contains the sampled data. + +| Field | | Length | Contents | +|----------|--------------|----------|----------------------------------------| +| `ckID` | | 4 | Chunk ID: `"data"` | +| `cksize` | | 4 | Chunk size: `n` | +| | sampled data | `n` | Samples | +| | pad byte | 0 or 1 | Padding byte if `n` is odd | + +--- + +## Examples + +Consider sampled data with the following parameters: + +- `Nc` channels +- The total number of blocks is `Ns`. Each block consists of `Nc` samples. +- Sampling rate `F` (blocks per second) +- Each sample is `M` bytes long + +### PCM Data + +| Field | | | Length | Contents | +|----------|----------|----------------|------------------------------------|-------------------------------------------------------| +| `ckID` | | | 4 | Chunk ID: `"RIFF"` | +| `cksize` | | | 4 | Chunk size: `4 + 24 + (8 + M*Nc*Ns + (0 or 1))` | +| | `WAVEID` | | 4 | WAVE ID: `"WAVE"` | +| | `ckID` | | 4 | Chunk ID: `"fmt "` | +| | `cksize` | | 4 | Chunk size: 16 | +| | | `wFormatTag` | 2 | `WAVE_FORMAT_PCM` | +| | | `nChannels` | 2 | `Nc` | +| | | `nSamplesPerSec` | 4 | `F` | +| | | `nAvgBytesPerSec` | 4 | `F*M*Nc` | +| | | `nBlockAlign` | 2 | `M*Nc` | +| | | `wBitsPerSample` | 2 | rounds up to `8*M` | +| | `ckID` | | 4 | Chunk ID: `"data"` | +| | `cksize` | | 4 | Chunk size: `M*Nc*Ns` | +| | | sampled data | `M*Nc*Ns` | `Nc*Ns` channel-interleaved `M`-byte samples | +| | | pad byte | 0 or 1 | Padding byte if `M*Nc*Ns` is odd | + +#### Notes + +- WAVE files often have information chunks that precede or follow the sound data (`data` chunk). Some programs (naively) assume that for PCM data, the preamble in the file header is exactly 44 bytes long (as in the table above) and that the rest of the file contains sound data. This is not a safe assumption. + +### Non-PCM Data + +| Field | | | Length | Contents | +|----------|------------------|----------------|----------------------------------------------|-------------------------------------------------------| +| `ckID` | | | 4 | Chunk ID: `"RIFF"` | +| `cksize` | | | 4 | Chunk size: `4 + 26 + 12 + (8 + M*Nc*Ns + (0 or 1))` | +| | `WAVEID` | | 4 | WAVE ID: `"WAVE"` | +| | `ckID` | | 4 | Chunk ID: `"fmt "` | +| | `cksize` | | 4 | Chunk size: 18 | +| | | `wFormatTag` | 2 | Format code | +| | | `nChannels` | 2 | `Nc` | +| | | `nSamplesPerSec` | 4 | `F` | +| | | `nAvgBytesPerSec` | 4 | `F*M*Nc` | +| | | `nBlockAlign` | 2 | `M*Nc` | +| | | `wBitsPerSample` | 2 | `8*M` (float data) or `16` (log-PCM data) | +| | | `cbSize` | 2 | Size of the extension: 0 | +| | `ckID` | | 4 | Chunk ID: `"fact"` | +| | `cksize` | | 4 | Chunk size: 4 | +| | `dwSampleLength` | | 4 | `Nc*Ns` | +| | `ckID` | | 4 | Chunk ID: `"data"` | +| | `cksize` | | 4 | Chunk size: `M*Nc*Ns` | +| | | sampled data | `M*Nc*Ns` | `Nc*Ns` channel-interleaved `M`-byte samples | +| | | pad byte | 0 or 1 | Padding byte if `M*Nc*Ns` is odd | + +- Microsoft *Windows Media Player* will not play non-PCM data (e.g. µ-law data) if the `fmt` chunk does not have the extension size field (`cbSize`) or a `fact` chunk is not present. + +### Extensible Format + +| Field | | | Length | Contents | +|----------|------------------|-----------------------|-----------------------------------------------|-------------------------------------------------------| +| `ckID` | | | 4 | Chunk ID: `"RIFF"` | +| `cksize` | | | 4 | Chunk size: `4 + 48 + 12 + (8 + M*Nc*Ns + (0 or 1))` | +| | `WAVEID` | | 4 | WAVE ID: `"WAVE"` | +| | `ckID` | | 4 | Chunk ID: `"fmt "` | +| | `cksize` | | 4 | Chunk size: 40 | +| | | `wFormatTag` | 2 | `WAVE_FORMAT_EXTENSIBLE` | +| | | `nChannels` | 2 | `Nc` | +| | | `nSamplesPerSec` | 4 | `F` | +| | | `nAvgBytesPerSec` | 4 | `F*M*Nc` | +| | | `nBlockAlign` | 2 | `M*Nc` | +| | | `wBitsPerSample` | 2 | `8*M` | +| | | `cbSize` | 2 | Size of the extension: 22 | +| | | `wValidBitsPerSample` | 2 | at most `8*M` | +| | | `dwChannelMask` | 4 | Speaker position mask | +| | | `SubFormat` | 16 | GUID (first two bytes are the data format code) | +| | `ckID` | | 4 | Chunk ID: `"fact"` | +| | `cksize` | | 4 | Chunk size: 4 | +| | | `dwSampleLength` | 4 | `Nc*Ns` | +| | `ckID` | | 4 | Chunk ID: `"data"` | +| | `cksize` | | 4 | Chunk size: `M*Nc*Ns` | +| | | sampled data | `M*Nc*Ns` | `Nc*Ns` channel-interleaved `M`-byte samples | +| | | pad byte | 0 or 1 | Padding byte if `M*Nc*Ns` is odd | + +- The `fact` chunk can normally be omitted if the sampled data is in PCM format. +- In some cases, Microsoft *Windows Media Player* enforces the use of the `WAVE_FORMAT_EXTENSIBLE` format code. For instance a file with 24-bit data declared as a standard `WAVE_FORMAT_PCM` format code will not play, but a file with 24-bit data declared as a `WAVE_FORMAT_EXTENSIBLE` file with a `WAVE_FORMAT_PCM` subcode can be played. + +--- + +#### [Sample Wave Files](Samples.html) + diff --git a/doc/rfc2361.txt b/doc/rfc2361.txt new file mode 100644 index 0000000..d81defc --- /dev/null +++ b/doc/rfc2361.txt @@ -0,0 +1,3979 @@ + + + + + + +Network Working Group E. Fleischman +Request for Comments: 2361 Microsoft Corporation +Category: Informational June 1998 + + + WAVE and AVI Codec Registries + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1998). All Rights Reserved. + +Abstract + + Internet applications may reference specific codecs within the WAVE + and AVI registries as follows: + * video/vnd.avi; codec=XXX identifies a specific video codec (i.e., + XXX) within the AVI Registry. + * audio/vnd.wave; codec=YYY identifies a specific audio codec + (i.e., YYY) within the WAVE Registry. + + Appendix A and Appendix B provides an authoritative reference for the + interpretation of the required "codec" parameter. That is, the + current set of audio codecs that are registered within the WAVE + Registry are enumerated in Appendix A. Appendix B enumerates the + current set of video codecs that have been registered to date within + the AVI Registry. + +1 Introduction + + Internet-oriented multimedia applications reference multimedia + content via predefined mechanisms (e.g., [2]). In the general case, + this content was created primarily for the use of these Internet- + oriented applications. Unfortunately, this Internet-oriented + multimedia content represents a small minority of the total amount of + multimedia content that has been created to date. + + For this reason, a growing interest is forming in establishing + mechanisms by which the repertoire of multimedia content available to + Internet-oriented applications(e.g., for RTSP [3]) may be greatly + extended to include multimedia content that has been created outside + of distinctly Internet contexts. For this to occur, a mechanism must + + + + +Fleischman Informational [Page 1] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + be created for Internet protocols (e.g., [1], [3], [4]) to be able to + identify the codecs by which this so-called "traditional" multimedia + content has been encoded. + + Unfortunately, several distinct encoding systems exist for + traditional multimedia content. Each system has its own registry to + ensure unique and stable codec identifications within that system. + Perhaps the best known of these registries are Microsoft (for WAVE + and AVI content) and Apple (for QuickTime content). + + The purpose of this paper is to establish a mechanism by which codecs + registered within Microsoft's WAVE and AVI Registries may be + referenced within the IANA Namespace by Internet applications. + +2 References to Registries within the IANA Vendor Tree + + Reference [7] specifies that the IANA Namespace encompasses several + trees. Discussions within the IETF-Types mailing list concluded that + the most appropriate tree in which to reference codecs, which had + already been registered by non-IANA Registries, is the Vendor Tree. + + As a result, the non-IANA registry is identified within the IANA + Vendor tree by vnd.RegistryName. A specific codec, which has been + registered within that registry, is identified by a required codec + parameter as specified by Section 2.2.3 of [7]. + +3 WAVE and AVI Registries + + Both the WAVE and AVI Registries are historic databases that have + been maintained by Microsoft as a free service. The Registries sought + to assist developers of WAVE and AVI content and to standardize WAVE + and AVI content by + 1) avoiding conflict and/or duplication with current definitions, and + 2) providing the registered information in a standard document and + format that is publicly available. + The historic nature of these databases implies that unless the + original registrants informed the registrar of a change of status + (e.g., company acquired, new contact, new location, new phone), the + contact information has generally not been updated from the + originally registered values. + + Audio codecs within the WAVE Registry are identified by WAVE Format + IDs. The (audio) WAVE Format ID is officially known as "WAVE form + Registration Number". The WAVE Format ID is a hexadecimal integer + value. These codecs may be referenced within the IANA namespace as + + + + + + +Fleischman Informational [Page 2] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + "audio/vnd.wave; codec=XXX", where XXX represents a valid WAVE Format + ID (e.g., the WAVE Format ID of "123" is referenced within the IANA + namespace by "audio/vnd.wave; codec=123"). + + Video codecs within the AVI Registry are identified by AVI Codec IDs. + The AVI Codec ID value is a FourCC encoding. A FourCC is 32-bits long + and represents a (case-sensitive) four-character (i.e., ASCII) code + value. These codecs may be referenced within the IANA namespace as + "video/vnd.avi; codec=XXX", where XXX represents a valid AVI Codec ID + (e.g., the WAVE Format ID of "SCRN" is referenced within the IANA + namespace by "video/vnd.avi; codec=SCRN"). + + Appendix A is an authoritative list of the complete set of audio + codecs that have been registered (as of January 1998) within the WAVE + Registry. Appendix B is an authoritative list of the complete set of + video codecs that have been registered (as of January 1998) within + the AVI registry. + +4 Mapping Codec IDs to GUID Values + + Direct mappings exist between WAVE Format IDs and GUIDs and between + FourCC codec values and GUIDs [5]. [Note: GUIDs are Globally Unique + Identifiers that are also known as Universally Unique Identifiers + (UUIDs). UUIDs have been standardized within the Open Software + Foundation's (OSF) Distributed Computing Environment (DCE).] These + mappings enable GUID-oriented software to directly refer to these + historic codec values. For example, the Advanced Streaming Format + (ASF) [6] uses GUID values to refer to codecs, and the following + mechanism is used to convert the historic WAVE and AVI codec values + into the appropriate GUID value for use within ASF. + + WAVE Format IDs are converted to GUIDs by inserting the hexadecimal + value of the WAVE Format ID into the XXXXXXXX part of the following + template: {XXXXXXXX-0000-0010-8000-00AA00389B71}. For example, a WAVE + Format ID of 123 has the GUID value of {00000123-0000-0010-8000- + 00AA00389B71}. + + FourCC values are converted to GUIDs by inserting the FourCC value + into the XXXXXXXX part of the same template: {XXXXXXXX-0000-0010- + 8000-00AA00389B71}. For example, a conversion of the FourCC value of + "H260" would result in the GUID value of {30363248-0000-0010-8000- + 00AA00389B71}. [Note: the 32-bit FourCC value of "H260" is converted + into hexadecimal 32-bit value (i.e., 30363248) because the initial + XXXXXXXX of the GUID is defined as a DWORD and thus takes a 32-bit + hexadecimal value. Endian considerations account for the apparent + re-ordering of the original ASCII text.] + + + + + +Fleischman Informational [Page 3] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + +5 Security Considerations + + This document merely registers a set of formats. It does nothing to + address the security considerations of these formats. The format + itself must be investigated for security issues with each format. + +Author's Address + + Eric Fleischman + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + EMail: ericfl@microsoft.com + http://www.microsoft.com/asf/ + + Subsequent to this submittal the author has changed employers. He now + can be reached at: + + The Boeing Company + PO Box 3707, MS 7M-FM + Seattle, WA 98124-2207 + + Phone: 425-865-2424 + EMail: Eric.Fleischman@PSS.Boeing.com + + + + + + + + + + + + + + + + + + + + + + + + + + +Fleischman Informational [Page 4] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + +References + + [1] Schulzrinne, H., Casner, S., Frederick, R., and V. Jacobson,"RTP: + A Transport Protocol for Real-Time Applications", RFC 1889, + January 1996. + + [2] Schulzrinne, H., "RTP Profile for Audio and Video Conferences + with Minimal Control", RFC 1890, January 1996. + + [3] Schulzrinne, H., Rao, A., and R. Lanphier, "Real Time Streaming + Protocol (RTSP)", RFC 2326, April 1998. + + [4] Handley, M., and V. Jacobson, "SDP: Session Description + Protocol", RFC 2327, April 1998. + + [5] "ASF Codec GUIDs", http://www.microsoft.com/asf/guids.htm + + [6] Microsoft Corporation, "Advanced Streaming Format (ASF) + Specification", September 1997, + http://www.microsoft.com/asf/specs.htm. + + [7] Freed, N., Klensin, J., and J. Postel, "Multipurpose Internet + Mail Extensions (MIME) Part Four: Registration Procedures", RFC + 2048, November 1996. + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fleischman Informational [Page 5] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + +Appendix A Audio Codecs from the Microsoft WAVE Registry + + A.1 Microsoft Unknown Wave Format + + WAVE form Registration Number (hex): 0x0000 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=0 + WAVE form wFormatTag ID: WAVE_FORMAT_UNKNOWN + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.2 Microsoft PCM Format + + WAVE form Registration Number (hex): 0x0001 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1 + WAVE form wFormatTag ID: WAVE_FORMAT_PCM + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.3 Microsoft ADPCM Format + + WAVE form Registration Number (hex): 0x0002 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=2 + WAVE form wFormatTag ID: WAVE_FORMAT_ADPCM + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.4 IEEE Float + + WAVE form Registration Number (hex): 0x0003 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=3 + WAVE form wFormatTag ID: WAVE_FORMAT_IEEE_FLOAT + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + +Fleischman Informational [Page 6] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.5 Compaq Computer's VSELP + + WAVE form Registration Number (hex): 0x0004 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=4 + WAVE form wFormatTag ID: WAVE_FORMAT_VSELP + Additional information: VSELP codec for Windows CE 2.0 devices + Contact: + Doug Stewart + 713-374-7925 + Compaq Computer Corporation + 20555 SH 249 + Houston, TX 77269-2000 USA + + A.6 IBM CVSD + + WAVE form Registration Number (hex): 0x0005 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=5 + WAVE form wFormatTag ID: WAVE_FORMAT_IBM_CVSD + Contact: + IBM Corporation + + A.7 Microsoft ALAW + + WAVE form Registration Number (hex): 0x0006 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=6 + WAVE form wFormatTag ID: WAVE_FORMAT_ALAW + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.8 Microsoft MULAW + + WAVE form Registration Number (hex): 0x0007 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=7 + WAVE form wFormatTag ID: WAVE_FORMAT_MULAW + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + + + + + +Fleischman Informational [Page 7] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.9 OKI ADPCM + + WAVE form Registration Number (hex): 0x0010 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=10 + WAVE form wFormatTag ID: WAVE_FORMAT_OKI_ADPCM + Contact: + Oki + + A.10 Intel's DVI ADPCM + + WAVE form Registration Number (hex): 0x0011 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=11 + WAVE form wFormatTag ID: WAVE_FORMAT_DVI_ADPCM + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, OR 97124 + 503-696-2448 + + A.11 Videologic's MediaSpace ADPCM + + WAVE form Registration Number (hex): 0x0012 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=12 + WAVE form wFormatTag ID: WAVE_FORMAT_MEDIASPACE_ADPCM + Contact: + Videologic + Home Park Estate + Kings Langley England WD4 8LZ + Telephone: 44-92-326-0511 + + A.12 Sierra ADPCM + + WAVE form Registration Number (hex): 0x0013 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=13 + WAVE form wFormatTag ID: WAVE_FORMAT_SIERRA_ADPCM + Contact: + Stuart Goldstein + 72170.301@compuserve.com + Sierra Semiconductor Corp + 2075 North Capitol Avenue + San Jose, California 95132 USA + 408-263-9300 + + + + + + + + + +Fleischman Informational [Page 8] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.13 G.723 ADPCM + + WAVE form Registration Number (hex): 0x0014 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=14 + WAVE form wFormatTag ID: WAVE_FORMAT_G723_ADPCM + Contact: + Bob Bauman + 310-532-3092 + Antex Electronics Coporation + 3184-H Airway Ave. + Costa Mesa, California 92627 USA + + A.14 DSP Solution's DIGISTD + + WAVE form Registration Number (hex): 0x0015 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=15 + WAVE form wFormatTag ID: WAVE_FORMAT_DIGISTD + Contact: + DSP Solutions, Inc + 2464 Embarcadero Way + Palo Alto, California 94303 USA + 415-494-8086 + + A.15 DSP Solution's DIGIFIX + + WAVE form Registration Number (hex): 0x0016 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=16 + WAVE form wFormatTag ID: WAVE_FORMAT_DIGIFIX + Contact: + DSP Solutions, Inc + 2464 Embarcadero Way + Palo Alto, California 94303 USA + 415-494-8086 + + A.16 Dialogic OKI ADPCM + + WAVE form Registration Number (hex): 0x0017 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=17 + WAVE form wFormatTag ID: WAVE_FORMAT_DIALOGIC_OKI_ADPCM + WAVEFORMAT use: for OKI ADPCM chips or firmware + Contact: + Dialogic Corporation + 300 Littleton Road + Parsippany, NJ 07054 USA + 201-334-1268 + + + + + + +Fleischman Informational [Page 9] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.17 MediaVision ADPCM + + WAVE form Registration Number (hex): 0x0018 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=18 + WAVE form wFormatTag ID: WAVE_FORMAT_MEDIAVISION_ADPCM + WAVEFORMAT Name: ADPCM for Jazz 16 chip set + Contact: + Alex Cheng + Media Vision, Inc + California USA + + A.18 HP CU + + WAVE form Registration Number (hex): 0x0019 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=19 + WAVE form wFormatTag ID: WAVE_FORMAT_CU_CODEC + Contact: + Cliff Chiang + Telephone: 65-3747005 + Hewlett-Packard Company + 452 Alexandra Road + Singapore 119961 Singapore + + A.19 Yamaha ADPCM + + WAVE form Registration Number (hex): 0x0020 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=20 + WAVE form wFormatTag ID: WAVE_FORMAT_YAMAHA_ADPCM + Contact: + Yamaha Corporation of America + Systems Technology Division + 981 Ridder Park Drive + San Jose, California 95131 USA + 408-437-3133 + + A.20 Speech Compression's Sonarc + + WAVE form Registration Number (hex): 0x0021 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=21 + WAVE form wFormatTag ID: WAVE_FORMAT_SONARC + Contact: + Speech Compression + + + + + + + + + +Fleischman Informational [Page 10] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.21 DSP Group's True Speech + + WAVE form Registration Number (hex): 0x0022 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=22 + WAVE form wFormatTag ID: WAVE_FORMAT_DSPGROUP_TRUESPEECH + Contact: + DSP Group, Inc + 2464 Embarcadero Way + Palo Alto, California 94303 USA + 415-494-8086 + + A.22 Echo Speech's EchoSC1 + + WAVE form Registration Number (hex): 0x0023 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=23 + WAVE form wFormatTag ID: WAVE_FORMAT_ECHOSC1 + Contact: + Billy Brackenridge + billy@isi.edu + Echo Speech Corporation + 6460 Via Real + Carpinteria, California 93013 USA + 805-684-4593 + + A.23 Audiofile AF36 + + WAVE form Registration Number (hex): 0x0024 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=24 + WAVE form wFormatTag ID: WAVE_FORMAT_AUDIOFILE_AF36 + Contact: + Alan Miller + 617-271-0900 + Virtual Music, Inc. + 19 Crosby Drive + Bedford, MA 01730-1419 USA + + A.24 APTX + + WAVE form Registration Number (hex): 0x0025 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=25 + WAVE form wFormatTag ID: WAVE_FORMAT_APTX + Contact: + Calypso Software + Audio Processing Technology + Edgewater Road + Belfast, Northern Ireland + 44-232-371110 + + + + +Fleischman Informational [Page 11] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.25 AudioFile AF10 + + WAVE form Registration Number (hex): 0x0026 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=26 + WAVE form wFormatTag ID: WAVE_FORMAT_AUDIOFILE_AF10 + Contact: + Alan Miller + 617-271-0900 + Virtual Music, Inc. + 19 Crosby Drive + Bedford, MA 01730-1419 USA + + A.26 Prosody 1612 + + WAVE form Registration Number (hex): 0x0027 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=27 + WAVE form wFormatTag ID: WAVE_FORMAT_PROSODY_1612 + Additional Information: Prosody CTI Speech Card + Contact: + Phil Cambridge + Phil.Cambridge@aculab.com + Aculab plc + Lakeside, Bramley Road + Mount Farm, Milton Keynes MK1 1PT UK + +44 1908 273800 + + A.27 LRC + + WAVE form Registration Number (hex): 0x0028 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=28 + WAVE form wFormatTag ID: WAVE_FORMAT_LRC + Contact: + Patrick Wassmer + pwassmer@merging.com + +41 21 931 50 11 + Merging Technologies S.A. + Le Verney, E + Puidoux, Switzerland CH-1604 + + + + + + + + + + + + + +Fleischman Informational [Page 12] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.28 Dolby AC2 + + WAVE form Registration Number (hex): 0x0030 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=30 + WAVE form wFormatTag ID: WAVE_FORMAT_DOLBY_AC2 + Contact: + Dolby Laboratories + 100 Portrero Avenue + San Francisco, California 94103-4813 USA + 415-558-0200 + + A.29 GSM610 + + WAVE form Registration Number (hex): 0x0031 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=31 + WAVE form wFormatTag ID: WAVE_FORMAT_GSM610 + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.30 MSNAudio + + WAVE form Registration Number (hex): 0x0032 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=32 + WAVE form wFormatTag ID: WAVE_FORMAT_MSNAUDIO + WAVEFORMAT Name: Microsoft MSN Audio Codec + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.31 Antex ADPCME + + WAVE form Registration Number (hex): 0x0033 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=33 + WAVE form wFormatTag ID: WAVE_FORMAT_ANTEX_ADPCME + Contact: + Bob Bauman + Antex Electronics Corporation + 3184-H Airway Ave. + Costa Mesa, California 92627 USA + 310-532-3092 + + + + + + +Fleischman Informational [Page 13] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.32 Control Res VQLPC + + WAVE form Registration Number (hex): 0x0034 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=34 + WAVE form wFormatTag ID: WAVE_FORMAT_CONTROL_RES_VQLPC + Contact: + Charles Larson + Control Resources Limited + PO Box 8694 + Roland Heights, California 91748 USA + 818-912-5722 + + A.33 Digireal + + WAVE form Registration Number (hex): 0x0035 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=35 + WAVE form wFormatTag ID: WAVE_FORMAT_DIGIREAL + Contact: + DSP Solutions, Inc + 2464 Embarcadero Way + Palo Alto, California 94303 USA + 415-494-8086 + + A.34 DigiADPCM + + WAVE form Registration Number (hex): 0x0036 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=36 + WAVE form wFormatTag ID: WAVE_FORMAT_DIGIADPCM + Contact: + DSP Solutions, Inc + 2464 Embarcadero Way + Palo Alto, California 94303 USA + 415-494-8086 + + A.35 Control Res CR10 + + WAVE form Registration Number (hex): 0x0037 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=37 + WAVE form wFormatTag ID: WAVE_FORMAT_CONTROL_RES_CR10 + Contact: + Charles Larson + Control Resources Limited + PO Box 8694 + Roland Heights, California 91748 USA + 818-912-5722 + + + + + + +Fleischman Informational [Page 14] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.36 NMS VBXADPCM + + WAVE form Registration Number (hex): 0x0038 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=38 + WAVE form wFormatTag ID: WAVE_FORMAT_NMS_VBXADPCM + Contact: + Joel Feldman, Steve Mors + Natural MicroSystems + + A.37 Roland RDAC + + WAVE form Registration Number (hex): 0x0039 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=39 + WAVE form wFormatTag ID: WAVE_FORMAT_ROLAND_RDAC + WAVEFORMAT Name: Roland RDAC Proprietary format + Contact: + Takera Tanigawa + email: tanigawa@roland.co.jp + 001-81-6-682-4584 + + A.38 EchoSC3 + + WAVE form Registration Number (hex): 0x003A + Codec ID in the IANA Namespace: audio/vnd.wave;codec=3A + WAVE form wFormatTag ID: WAVE_FORMAT_ECHOSC3 + WAVEFORMAT Description: Proprietary compressed format + Contact: + Billy Brackenridge + billy@isi.edu + Echo Speech Corporation + 6460 Via Real + Carpinteria, California 93013 USA + 805-684-4593 + + A.39 Rockwell ADPCM + + WAVE form Registration Number (hex): 0x003B + Codec ID in the IANA Namespace: audio/vnd.wave;codec=3B + WAVE form wFormatTag ID: WAVE_FORMAT_ROCKWELL_ADPCM + WAVEFORMAT Name: Rockwell ADPCM + Contact: + Rockwell International + Digital Communications Division + 4311 Jamboree Rd. + PO Box C + Newport Beach, California 92658-8902 USA + 714-833-4600 + + + + +Fleischman Informational [Page 15] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.40 Rockwell Digit LK + + WAVE form Registration Number (hex): 0x003C + Codec ID in the IANA Namespace: audio/vnd.wave;codec=3C + WAVE form wFormatTag ID: WAVE_FORMAT_ROCKWELL_DIGITALK + WAVEFORMAT Name: Rockwell DIGITALK + Contact: + Rockwell International + Digital Communications Division + 4311 Jamboree Rd. + PO Box C + Newport Beach, California 92658-8902 USA + 714-833-4600 + + A.41 Xebec + + WAVE form Registration Number (hex): 0x003D + Codec ID in the IANA Namespace: audio/vnd.wave;codec= + WAVE form wFormatTag ID: WAVE_FORMAT_XEBEC + Additonal Information: proprietary compression + Contact: + David Emberton + 44-453-835482 + Xebec Multimedia Solutions Limited + Smith House + 1-3 George Street + Nailsworth, Gloucestershire, England GL6 OAG + + A.42 Antex Electronics G.721 + + WAVE form Registration Number (hex): 0x0040 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=40 + WAVE form wFormatTag ID: WAVE_FORMAT_G721_ADPCM + Contact: + Bob Bauman + 310-532-3092 + Antex Electronics Coporation + 3184-H Airway Ave. + Costa Mesa, California 92627 USA + + + + + + + + + + + + +Fleischman Informational [Page 16] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.43 G.728 CELP + + WAVE form Registration Number (hex): 0x0041 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=41 + WAVE form wFormatTag ID: WAVE_FORMAT_G728_CELP + Contact: + Bob Bauman + 310-532-3092 + Antex Electronics Coporation + 3184-H Airway Ave. + Costa Mesa, California 92627 USA + + A.44 MSG723 + + WAVE form Registration Number (hex): 0x0042 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=42 + WAVE form wFormatTag ID: WAVE_FORMAT_MSG723 + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.45 MPEG + + WAVE form Registration Number (hex): 0x0050 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=50 + WAVE form wFormatTag ID: WAVE_FORMAT_MPEG + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + + + + + + + + + + + + + + + + +Fleischman Informational [Page 17] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.46 RT24 + + WAVE form Registration Number (hex): 0x0052 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=52 + WAVE form wFormatTag ID: WAVE_FORMAT_RT24 + Additional Information: This ACM codec is an alternative codec + ID to refer to the Voxware Metavoice codec (Codec ID 0x0074). Only the + Voxware reference should be used in the general case. + Contact: + Alexander V. Sokolsky + 717-730-9501 + InSoft, Inc. + 4718 Old Gettysburg Rd + Suite 307 + Mechanicsburg, PA 17055-4378 USA + + A.47 PAC + + WAVE form Registration Number (hex): 0x0053 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=53 + WAVE form wFormatTag ID: WAVE_FORMAT_PAC + Contact: + Alexander V. Sokolsky + 717-730-9501 + InSoft, Inc. + 4718 Old Gettysburg Rd + Suite 307 + Mechanicsburg, PA 17055-4378 USA + + A.48 MPEG Layer 3 + + WAVE form Registration Number (hex): 0x0055 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=55 + WAVE form wFormatTag ID: WAVE_FORMAT_MPEGLAYER3 + Additional Information: ISO/MPEG Layer3 Format Tag + Contact: + Tomislav Grcanac + (408) 576-1361 + AT&T Labs, Inc. + 2665 North First Street + San Jose, California 95134 USA + + + + + + + + + + +Fleischman Informational [Page 18] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.49 Lucent G.723 + + WAVE form Registration Number (hex): 0x0059 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=59 + WAVE form wFormatTag ID: WAVE_FORMAT_LUCENT_G723 + Contact: + Ray Jones + (raykj@lucent.com) + Lucent Technologies + + A.50 Cirrus + + WAVE form Registration Number (hex): 0x0060 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=60 + WAVE form wFormatTag ID: WAVE_FORMAT_CIRRUS + Contact: + Mr Scott MacDonald + 512 442-7555 + Cirrus Logic (USA) + + A.51 ESPCM + + WAVE form Registration Number (hex): 0x0061 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=61 + WAVE form wFormatTag ID: WAVE_FORMAT_ESPCM + Contact: + Paul Sung + 510-226-1088 + ESS Technology + 46107 Landing Parkway + Fremont, California 94538 USA + + A.52 Voxware + + WAVE form Registration Number (hex): 0x0062 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=62 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE + Additional Information: This format is now obsolete + Contact: + Lee Stewart + rover@pipeline.com or compuserve 75570,3525 or lees@voxware.com + Voxware Inc + 172 Tamarack Circle + Skillman, NJ 08558 USA + + + + + + + +Fleischman Informational [Page 19] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.53 Canopus Atrac + + WAVE form Registration Number (hex): 0x0063 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=63 + WAVE form wFormatTag ID: WAVE_FORMAT_CANOPUS_ATRAC + Additional Information: ATRACWAVEFORMAT + Contact: + Masayoshi Araki + m-araki@canopus.co.jp + 81-78-992-7812 + Canopus, Co., Ltd. + Kobe Hi-Tech Park + 1-2-2 Murotani, Nishi-ku + Kobe, Hyogo 651-22 Japan + + A.54 G.726 ADPCM + + WAVE form Registration Number (hex): 0x0064 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=64 + WAVE form wFormatTag ID: WAVE_FORMAT_G726_ADPCM + Contact: + Jean-Claude Anaya + 100433.3121@compuserve.com + (33) 57-26-99-24 + APICOM + 218, Avenue du Haut-Leveque + Pessac France 33605 + + A.55 G.722 ADPCM + + WAVE form Registration Number (hex): 0x0065 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=65 + WAVE form wFormatTag ID: WAVE_FORMAT_G722_ADPCM + Contact: + Jean-Claude Anaya + 100433.3121@compuserve.com + (33) 57-26-99-24 + APICOM + 218, Avenue du Haut-Leveque + Pessac France 33605 + + + + + + + + + + + +Fleischman Informational [Page 20] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.56 DSAT + + WAVE form Registration Number (hex): 0x0066 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=66 + WAVE form wFormatTag ID: WAVE_FORMAT_DSAT + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.57 DSAT Display + + WAVE form Registration Number (hex): 0x0067 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=67 + WAVE form wFormatTag ID: WAVE_FORMAT_DSAT_DISPLAY + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.58 Voxware Byte Aligned + + WAVE form Registration Number (hex): 0x0069 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=69 + WAVE form wFormatTag ID:n WAVE_FORMAT_VOXWARE_BYTE_ALIGNED + Additional Information: This format is now obsolete + Contact: + Lee Stewart + rover@pipeline.com or compuserve 75570,3525 or lees@voxware.com + Voxware Inc + 172 Tamarack Circle + Skillman, NJ 08558 USA + + + + + + + + + + + + + + + +Fleischman Informational [Page 21] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.59 Voxware AC8 + + WAVE form Registration Number (hex): 0x0070 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=70 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_AC8 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.60 Voxware AC10 + + WAVE form Registration Number (hex): 0x0071 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=71 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_AC10 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.61 Voxware AC16 + + WAVE form Registration Number (hex): 0x0072 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=72 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_AC16 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + + + + + + + + + + + + +Fleischman Informational [Page 22] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.62 Voxware AC20 + + WAVE form Registration Number (hex): 0x0073 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=73 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_AC20 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.63 Voxware MetaVoice + + WAVE form Registration Number (hex): 0x0074 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=74 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_RT24 + Additional Information: file and stream oriented + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.64 Voxware MetaSound + + WAVE form Registration Number (hex): 0x0075 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=75 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_RT29 + Additional Information: file and stream oriented + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + + + + + + + + + + + + +Fleischman Informational [Page 23] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.65 Voxware RT29HW + + WAVE form Registration Number (hex): 0x0076 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=76 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_RT29HW + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.66 Voxware VR12 + + WAVE form Registration Number (hex): 0x0077 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=77 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_VR12 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.67 Voxware VR18 + + WAVE form Registration Number (hex): 0x0078 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=78 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_VR18 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + + + + + + + + + + + + +Fleischman Informational [Page 24] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.68 Voxware TQ40 + + WAVE form Registration Number (hex): 0x0079 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=79 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_TQ40 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + A.69 Softsound + + WAVE form Registration Number (hex): 0x0080 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=80 + WAVE form wFormatTag ID: WAVE_FORMAT_SOFTSOUND + Contact: + AJ Robinson + 44-1727-847949 + Softsound, Ltd. + 12 St. Stephens Avenue + St. Albans, Herts, UK AL3 4AD + + A.70 Voxware TQ60 + + WAVE form Registration Number (hex): 0x0081 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=81 + WAVE form wFormatTag ID: WAVE_FORMAT_VOXWARE_TQ60 + Additional Information: This format ID is now obsolete + Contact: + Lee Stewart + lees@voxware.com + Voxware Inc. + 172 Tamarack Circle + Skillman, NJ 08558 USA + + + + + + + + + + + + + + +Fleischman Informational [Page 25] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.71 MSRT24 + + WAVE form Registration Number (hex): 0x0082 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=82 + WAVE form wFormatTag ID: WAVE_FORMAT_MSRT24 + Additional Information: This ACM codec is an alternative codec + ID to refer to the Voxware Metavoice codec (Codec ID 0x0074). Only the + Voxware reference should be used in the general case. + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + A.72 G.729A + + WAVE form Registration Number (hex): 0x0083 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=83 + WAVE form wFormatTag ID: WAVE_FORMAT_G729A + Contact: + AT&T Laboratories + + A.73 MVI MV12 + + WAVE form Registration Number (hex): 0x0084 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=84 + WAVE form wFormatTag ID: WAVE_FORMAT_MVI_MV12 + Contact: + David R. Whipple + whipple@mail.webtek.com + Motion Pixels + 7802 North 132 East Court + Owasso, OK 74055 USA + (918) 272-5328 + + + + + + + + + + + + + + + + +Fleischman Informational [Page 26] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.74 DF G.726 + + WAVE form Registration Number (hex): 0x0085 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=85 + WAVE form wFormatTag ID: WAVE_FORMAT_DF_G726 + Contact: + Jarno van Rooyen + Jarno.VanRooyen@DataVoice.co.za + DataFusion Systems (Pty) (Ltd) + PO Box 582 + Stellenbosch Stellenbosch South Africa + 27 21 888 2000 + + A.75 DF GSM610 + + WAVE form Registration Number (hex): 0x0086 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=86 + WAVE form wFormatTag ID: WAVE_FORMAT_DF_GSM610 + Contact: + Jarno van Rooyen + Jarno.VanRooyen@DataVoice.co.za + DataFusion Systems (Pty) (Ltd) + PO Box 582 + Stellenbosch 7600 South Africa + 27 21 888 2000 + + A.76 ISIAudio + + WAVE form Registration Number (hex): 0x0088 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=88 + WAVE form wFormatTag ID: WAVE_FORMAT_ISIAUDIO + Contact: + Iterated Systems, Inc. + 5550-a Peachtree Parkway + Suite 650 + Norcross, GA 30092 USA + 404-840-0633 + + + + + + + + + + + + + + +Fleischman Informational [Page 27] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.77 Onlive + + WAVE form Registration Number (hex): 0x0089 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=89 + WAVE form wFormatTag ID: WAVE_FORMAT_ONLIVE + Contact: + Dr. Ajit L. Lalwani + ajit@onlive.com + (408) 617 - 3595 + OnLive! Technologies, Inc. + 1039 S. Mary Ave. + Sunnyvale, California 94087 USA + (408) 617-7000 + + A.78 SBC24 + + WAVE form Registration Number (hex): 0x0091 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=91 + WAVE form wFormatTag ID: WAVE_FORMAT_SBC24 + Contact: + Dieter Rencken + Dieter.W.Rencken@siemenscom.com + (408) 492-6539 + Siemens Business Communications Systems + 4900 Old Ironsides Drive + Santa Clara, California 95054 USA + (408) 492-2000 + + A.79 Dolby AC3 SPDIF + + WAVE form Registration Number (hex): 0x0092 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=92 + WAVE form wFormatTag ID: WAVE_FORMAT_DOLBY_AC3_SPDIF + Contact: + Monty Schmidt + Sonic Foundry + 100 South Baldwin, Suite 204 + Madison, WI 53703 USA + 608-256-3133 + + + + + + + + + + + + +Fleischman Informational [Page 28] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.80 ZyXEL ADPCM + + WAVE form Registration Number (hex): 0x0097 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=97 + WAVE form wFormatTag ID: WAVE_FORMAT_ZYXEL_ADPCM + Contact: + Nasser Tarazi + nasser@ZyXEL.COM + 714-693-0808 ext 206 + ZyXEL Communications, Inc. + 4920 E. La Palma Ave + Anaheim, California 92807 USA + 714-693-0808 + + A.81 Philips LPCBB + + WAVE form Registration Number (hex): 0x0098 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=98 + WAVE form wFormatTag ID: WAVE_FORMAT_PHILIPS_LPCBB + Contact: + Kurt Kornmuller + Philips Speech Processing + Computerstrasse 6 + Vienna A-1101 Austria + 43 1 601 01 + + A.82 Packed + + WAVE form Registration Number (hex): 0x0099 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=99 + WAVE form wFormatTag ID: WAVE_FORMAT_PACKED + Contact: + Alex Ruegg + alex.ruegg@studer.ch + 41-1-870-1252 + Studer Professional Audio AG + Althardstrasse 30 + Regensdorf, CH 8105 + 41-1-870-7252 + + + + + + + + + + + + +Fleischman Informational [Page 29] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.83 Rhetorex ADPCM + + WAVE form Registration Number (hex): 0x0100 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=100 + WAVE form wFormatTag ID: WAVE_FORMAT_RHETOREX_ADPCM + Contact: + Roger Dang + roger.dang@octel.com + 408-371-0881-x195 + Rhetorex, Inc. + 200 E Hacienda Ave + Campbell, California 95008 USA + + A.84 BeCubed Software's IRAT + + WAVE form Registration Number (hex): 0x0101 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=101 + WAVE form wFormatTag ID: WAVE_FORMAT_IRAT + WAVEFORMAT name: + Contact: + William J. Locke + bill@becubed.com + BeCubed Software Inc. + 1750 Marietta Hwy STE 240 + Canton, GA 30114 USA + 770-720-1077 + + A.85 Vivo G.723 + + WAVE form Registration Number (hex): 0x00111 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=111 + WAVE form wFormatTag ID: WAVE_FORMAT_VIVO_G723 + Contact: + Vivo Software + 411 Waverley Oaks Road, Suite 313 + Waltham, MA 02154 USA + (617) 899-8900 + + + + + + + + + + + + + + +Fleischman Informational [Page 30] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.86 Vivo Siren + + WAVE form Registration Number (hex): 0x0112 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=112 + WAVE form wFormatTag ID: WAVE_FORMAT_VIVO_SIREN + Contact: + Vivo Software + 411 Waverley Oaks Road, Suite 313 + Waltham, MA 02154 USA + (617) 899-8900 + + A.87 Digital G.723 + + WAVE form Registration Number (hex): 0x0123 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=123 + WAVE form wFormatTag ID: WAVE_FORMAT_DIGITAL_G723 + Contact: + John Forecast + forecast@shell.lkg.dec.com + 508-486-5264 + Digital Equipment Corporation + 146 Main Street + Maynard, MA 01754-2571 USA + 1-800-DIGITAL + + A.88 Creative ADPCM + + WAVE form Registration Number (hex): 0x0200 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=200 + WAVE form wFormatTag ID: WAVE_FORMAT_CREATIVE_ADPCM + Contact: + Peter Ridge + 408-428-2366 + Creative Labs, Inc + California, USA + + + + + + + + + + + + + + + + +Fleischman Informational [Page 31] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.89 Creative FastSpeech8 + + WAVE form Registration Number (hex): 0x0202 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=202 + WAVE form wFormatTag ID: WAVE_FORMAT_CREATIVE_FASTSPEECH8 + Contact: + Peter Ridge + 408-428-2366 + Creative Labs, Inc + California, USA + + A.90 Creative FastSpeech10 + + WAVE form Registration Number (hex): 0x0203 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=203 + WAVE form wFormatTag ID: WAVE_FORMAT_ CREATIVE_FASTSPEECH10 + Contact: + Peter Ridge + 408-428-2366 + Creative Labs, Inc + California, USA + + A.91 Quarterdeck + + WAVE form Registration Number (hex): 0x0220 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=220 + WAVE form wFormatTag ID: WAVE_FORMAT_QUARTERDECK + Contact: + Eugene Olsen + 310-309-3700 + Quarterdeck Corporation + 13160 Mindanao Way FL 3 + Marina del Rey, California 90292-9705 USA + + A.92 FM Towns Snd + + WAVE form Registration Number (hex): 0x0300 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=300 + WAVE form wFormatTag ID: WAVE_FORMAT_FM_TOWNS_SND + Contact: + Fujitsu Corporation + + + + + + + + + + +Fleischman Informational [Page 32] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.93 BTV Digital + + WAVE form Registration Number (hex): 0x0400 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=400 + WAVE form wFormatTag ID: WAVE_FORMAT_BTV_DIGITAL + Additional Information: Brooktree digital audio format + Contact: + Dave Wilson + 512-502-1725 + Brooktree Corporation + 9868 Scranton Road + San Diego, California 92121-3707 USA + 1-800-228-2777 + + A.94 VME VMPCM + + WAVE form Registration Number (hex): 0x0680 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=680 + WAVE form wFormatTag ID: WAVE_FORMAT_VME_VMPCM + Contact: + Tomislav Grcanac + (408) 576-1361 + AT&T Labs, Inc. + 2665 North First Street + San Jose, California 95134 USA + + A.95 OLIGSM + + WAVE form Registration Number (hex): 0x1000 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1000 + WAVE form wFormatTag ID: WAVE_FORMAT_OLIGSM + Contact: + Harry Sinn + Ing C. Olivetti & C., S.p.A. + Via G. Jervis 77 + Via Montalenghe 8 Scarmagno + Ivrea (To) 10015 Italy + 39-125-527056 + + + + + + + + + + + + + +Fleischman Informational [Page 33] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.96 OLIADPCM + + WAVE form Registration Number (hex): 0x1001 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1001 + WAVE form wFormatTag ID: WAVE_FORMAT_OLIADPCM + Contact: + Harry Sinn + Ing C. Olivetti & C., S.p.A. + Via G. Jervis 77 + Via Montalenghe 8 Scarmagno + Ivrea (To) 10015 Italy + 39-125-527056 + + A.97 OLICELP + + WAVE form Registration Number (hex): 0x1002 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1002 + WAVE form wFormatTag ID: WAVE_FORMAT_OLICELP + Contact: + Harry Sinn + Ing C. Olivetti & C., S.p.A. + Via G. Jervis 77 + Via Montalenghe 8 Scarmagno + Ivrea (To) 10015 Italy + 39-125-527056 + + A.98 OLISBC + + WAVE form Registration Number (hex): 0x1003 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1003 + WAVE form wFormatTag ID: WAVE_FORMAT_OLISBC + Contact: + Harry Sinn + Ing C. Olivetti & C., S.p.A. + Via G. Jervis 77 + Via Montalenghe 8 Scarmagno + Ivrea (To) 10015 Italy + 39-125-527056 + + + + + + + + + + + + + +Fleischman Informational [Page 34] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.99 OLIOPR + + WAVE form Registration Number (hex): 0x1004 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1004 + WAVE form wFormatTag ID: WAVE_FORMAT_OLIOPR + Contact: + Harry Sinn + Ing C. Olivetti & C., S.p.A. + Via G. Jervis 77 + Via Montalenghe 8 Scarmagno + Ivrea (To) 10015 Italy + 39-125-527056 + + A.100 LH Codec + + WAVE form Registration Number (hex): 0x1100 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1100 + WAVE form wFormatTag ID: WAVE_FORMAT_LH_CODEC + Contact: + David Ray + Lernout & Hauspie + 20 Mall Road + Burlington, MA 01803 USA + + A.101 Norris + + WAVE form Registration Number (hex): 0x1400 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1400 + WAVE form wFormatTag ID: WAVE_FORMAT_NORRIS + Contact: + Rick Davis + Norris Communications, Inc + 12725 Stowe Drive + Poway, California 92064 USA + 619-679-1504 + + + + + + + + + + + + + + + + +Fleischman Informational [Page 35] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + A.102 ISIAudio + + WAVE form Registration Number (hex): 0x1401 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1401 + WAVE form wFormatTag ID: WAVE_FORMAT_ISIAUDIO + Contact: + Tomislav Grcanac + (408) 576-1361 + AT&T Labs, Inc. + 2665 North First Street + San Jose, California 95134 USA + + A.103 Soundspace Music Compression + + WAVE form Registration Number (hex): 0x1500 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=1500 + WAVE form wFormatTag ID: WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS + Contact: + Tomislav Grcanac + (408) 576-1361 + AT&T Labs, Inc. + 2665 North First Street + San Jose, California 95134 USA + + A.104 DVM + + WAVE form Registration Number (hex): 0x2000 + Codec ID in the IANA Namespace: audio/vnd.wave;codec=2000 + WAVE form wFormatTag ID: WAVE_FORMAT_DVM + Contact: + Martin Regen + FAST Multimedia AG + Lansbergerstrasse 76 + Munchen 80339 Germany + 49-89-50206-0 + + + + + + + + + + + + + + + + +Fleischman Informational [Page 36] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + +Appendix B Video Codecs from the Microsoft AVI Registry + + B.1 Intel RDX + + Compression Code or FourCC Codec ID: ANIM + Codec ID in the IANA Namespace: video/vnd.avi;codec=ANIM + Description: Intel RDX + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.2 AuraVision Aura 2 + + Compression Code or FourCC Codec ID: AUR2 + Codec ID in the IANA Namespace: video/vnd.avi;codec=AUR2 + Description: AuraVision Aura 2: YUV 422 + Bit Depth: 8 + Contact: + Steve Gibson + 510-440-7180 + Fast Multimedia + 47865 Fremont Blvd + Fremont, California 94538 USA + + B.3 AuraVision Aura 1 + + Compression Code or FourCC Codec ID: AURA + Codec ID in the IANA Namespace: video/vnd.avi;codec=AURA + Description: AuraVision Aura 1: YUV 411 + Bit Depth: 6 + Contact: + Steve Gibson + 510-440-7180 + Fast Multimedia + 47865 Fremont Blvd + Fremont, California 94538 USA + + B.4 Brooktree MediaStream + + Compression Code or FourCC Codec ID: BT20 + Codec ID in the IANA Namespace: video/vnd.avi;codec=BT20 + Description: Brooktree MediaStream + Contact: + Dave Wilson + 512-502-1725 + + + + +Fleischman Informational [Page 37] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + Brooktree Corporation + 9868 Scranton Road + San Diego, California 92121-3707 USA + + B.5 Brooktree Composite Video + + Compression Code or FourCC Codec ID: BTCV + Codec ID in the IANA Namespace: video/vnd.avi;codec=BTCV + Description: Brooktree Composite Video + Contact: + Dave Wilson + 512-502-1725 + Brooktree Corporation + 9868 Scranton Road + San Diego, California 92121-3707 USA + + B.6 Intel YUV12 + + Compression Code or FourCC Codec ID: CC12 + Codec ID in the IANA Namespace: video/vnd.avi;codec=CC12 + Description: AuraVision Aura 2: Intel YUV12 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.7 Canopus DV + + Compression Code or FourCC Codec ID: CDVC + Codec ID in the IANA Namespace: video/vnd.avi;codec=CDVC + Description: Canopus DV + Contact: + Masayoshi Araki + 81-78-992-7812 + m-araki@canopus.co.jp + Canopus, Co., Ltd. + Kobe Hi-Tech Park + 1-2-2 Murotani, Nishi-ku + Kobe, Hyogo 651-22 Japan + + + + + + + + + + + +Fleischman Informational [Page 38] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.8 Winnov Caviara Cham + + Compression Code or FourCC Codec ID: CHAM + Codec ID in the IANA Namespace: video/vnd.avi;codec=CHAM + Description: Winnov Caviara Cham + Contact: + Winnov, Inc. + 1230 Oakmead Parkway, Suite 312 + Sunnyvale, California 94086 USA + 408-733-7419 + + B.9 Proprietary YUV 4 pixels + + Compression Code or FourCC Codec ID: CLJR + Codec ID in the IANA Namespace: video/vnd.avi;codec=CLJR + Description: Proprietary YUV 4 pixels/DWORD + Contact: + Mr Scott MacDonald + 512 442-7555 + Cirrus Logic + + B.10 Common Data Format in Printing + + Compression Code or FourCC Codec ID: CMYK + Codec ID in the IANA Namespace: video/vnd.avi;codec=CMYK + Description: Common Data Format in Printing + Bit Depth: 32bits (8 per component) + Contact: + Colorgraph (UK) + 2 Mars House, Calleva Park + Aldermaston, Reading, Berkshire RG7 8LB UK + +44-118-9819435 + + B.11 Weitek 4:2:0 YUV Planar + + Compression Code or FourCC Codec ID: CPLA + Codec ID in the IANA Namespace: video/vnd.avi;codec=CPLA + Description: Weitek 4:2:0 YUV Planar + Contact: + Weitek + 408-522-7541 + + + + + + + + + + +Fleischman Informational [Page 39] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.12 Cinepak by Supermac + + Compression Code or FourCC Codec ID: CVID + Codec ID in the IANA Namespace: video/vnd.avi;codec=CVID + Description: Cenepac by Supermac + Contact: + Lou Doctor + Supermac + + B.13 Microsoft Color WLT DIB + + Compression Code or FourCC Codec ID: CWLT + Codec ID in the IANA Namespace: video/vnd.avi;codec=CWLT + Description: Microsoft Color WLT DIB + Bit Depth: 24 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.14 Creative Labs YUV + + Compression Code or FourCC Codec ID: CYUV + Codec ID in the IANA Namespace: video/vnd.avi;codec=CYUV + Description: Creative Labs YUV + Contact: + Peter Ridge + 408-428-2366 + Creative Labs, Inc + California, USA + + B.15 H.261 + + Compression Code or FourCC Codec ID: D261 + Codec ID in the IANA Namespace: video/vnd.avi;codec=D261 + Description: H.261 Video Format + Bit Depth: 24 + Contact: + John Forecast + forecast@shell.lkg.dec.com + 508-486-5264 + Digital Equipment Corporation + 146 Main Street + Maynard, MA 01754-2571 USA + 1-800-DIGITAL + + + + +Fleischman Informational [Page 40] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.16 H.263 + + Compression Code or FourCC Codec ID: D263 + Codec ID in the IANA Namespace: video/vnd.avi;codec=D263 + Description: H.263 Video Format + Bit Depth: 24 + Contact: + John Forecast + forecast@shell.lkg.dec.com + 508-486-5264 + Digital Equipment Corporation + 146 Main Street + Maynard, MA 01754-2571 USA + 1-800-DIGITAL + + B.17 True Motion 1.0 + + Compression Code or FourCC Codec ID: DUCK + Codec ID in the IANA Namespace: video/vnd.avi;codec=DUCK + Description: TrueMotion 1.0 + Contact: + David Silver + david@duck.com + (212)941-2403 + The Duck Corporation + 375 Greenwich Street + New York, NY 10013 USA + (212) 941-2400 + http://www.duck.com/ + + B.18 DVE-2 Videoconferencing + + Compression Code or FourCC Codec ID: DVE2 + Codec ID in the IANA Namespace: video/vnd.avi;codec=DVE2 + Description: DVE-2 Videoconferencing + Contact: + Alexander V. Sokolsky + InSoft, Inc. + 4718 Old Gettysburg Rd, Suite 307 + Mechanicsburg, PA 17055-4378 USA + 717-730-9501 + + + + + + + + + + +Fleischman Informational [Page 41] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.19 Field Encoded Motion JPEG + + Compression Code or FourCC Codec ID: FLJP + Codec ID in the IANA Namespace: video/vnd.avi;codec=FLJP + Description: Field Encoded Motion JPEG With LSI + Bitstream Format + Contact: + Dale Weaver + weaverdm@dvision.com + 312-714-1400 -2169 + D-Vision Systems, Inc. + 8755 W. Higgins, Second Floor + Chicago, IL 60631 USA + + B.20 Fractal Video Frame + + Compression Code or FourCC Codec ID: FVF1 + Codec ID in the IANA Namespace: video/vnd.avi;codec=FVF1 + Description: Fractal Video Frame + Contact: + Iterated Systems, Inc. + 5550-a Peachtree Parkway, Suite 650 + Norcross, GA 30092 USA + 404-840-0633 + + B.21 Microsoft Greyscale WLT DIB + + Compression Code or FourCC Codec ID: GWLT + Codec ID in the IANA Namespace: video/vnd.avi;codec=GWLT + Description: Microsoft Greyscale WLT DIB + Bit Depth: 8 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.22 H.260 + + Compression Code or FourCC Codec ID: H260 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H260 + Description: H.260 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + +Fleischman Informational [Page 42] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.23 H.261 + + Compression Code or FourCC Codec ID: H261 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H261 + Description: H.261 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.24 H.262 + + Compression Code or FourCC Codec ID: H262 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H262 + Description: H.262 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.25 H.263 + + Compression Code or FourCC Codec ID: H263 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H263 + Description: H.263 + Bit Depth: 12/pixel + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.26 H.264 + Compression Code or FourCC Codec ID: H264 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H264 + Description: H.264 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 43] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.27 H.265 + + Compression Code or FourCC Codec ID: H265 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H265 + Description: H.265 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.28 H.266 + + Compression Code or FourCC Codec ID: H266 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H266 + Description: H.266 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.29 H.267 + + Compression Code or FourCC Codec ID: H267 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H267 + Description: H.267 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.30 H.268 + + Compression Code or FourCC Codec ID: H268 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H268 + Description: H.268 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 44] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.31 H.260 + + Compression Code or FourCC Codec ID: H269 + Codec ID in the IANA Namespace: video/vnd.avi;codec=H269 + Description: H.269 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.32 I263 + + Compression Code or FourCC Codec ID: I263 + Codec ID in the IANA Namespace: video/vnd.avi;codec=I263 + Description: Intel I263 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.33 Intel Indeo 4 + + Compression Code or FourCC Codec ID: I420 + Codec ID in the IANA Namespace: video/vnd.avi;codec=I420 + Description: Intel Indeo 4 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.34 Intel RDX + + Compression Code or FourCC Codec ID: IAN + Codec ID in the IANA Namespace: video/vnd.avi;codec=IAN + Description: Intel RDX + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 45] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.35 CellB Videoconferencing Codec + + Compression Code or FourCC Codec ID: ICLB + Codec ID in the IANA Namespace: video/vnd.avi;codec=ICLB + Description: CellB Videoconferencing Codec + Contact: + Alexander V. Sokolsky + InSoft, Inc. + 4718 Old Gettysburg Rd, Suite 307 + Mechanicsburg, PA 17055-4378 USA + 717-730-9501 + + B.36 Intel Layered Video + + Compression Code or FourCC Codec ID: ILVC + Codec ID in the IANA Namespace: video/vnd.avi;codec=ILVC + Description: Intel Layered Video + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.37 ITU-T H.263+ + + Compression Code or FourCC Codec ID: ILVR + Codec ID in the IANA Namespace: video/vnd.avi;codec=ILVR + Description: nITU-T's H.263+ compression standard + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.38 Intel YUV Uncompressed + + Compression Code or FourCC Codec ID: IRAW + Codec ID in the IANA Namespace: video/vnd.avi;codec=IRAW + Description: Intel YUV uncompressed + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + +Fleischman Informational [Page 46] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.39 Intel Indeo Video 3 + + Compression Code or FourCC Codec ID: IV30 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV30 + Description: Intel Indeo Video 3 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.40 Intel Indeo Video 3.1 + + Compression Code or FourCC Codec ID: IV31 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV31 + Description: Intel Indeo Video 3.1 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.41 Intel Indeo Video 3.2 + + Compression Code or FourCC Codec ID: IV32 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV32 + Description: Intel Indeo Video 3.2 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.42 Intel Indeo Video 3.3 + + Compression Code or FourCC Codec ID: IV33 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV33 + Description: Intel Indeo Video 3.3 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 47] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.43 Intel Indeo Video 3.4 + + Compression Code or FourCC Codec ID: IV34 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV34 + Description: Intel Indeo Video 3.4 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.44 Intel Indeo Video 3.5 + + Compression Code or FourCC Codec ID: IV35 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV35 + Description: Intel Indeo Video 3.5 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.45 Intel Indeo Video 3.6 + + Compression Code or FourCC Codec ID: IV36 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV36 + Description: Intel Indeo Video 3.6 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.46 Intel Indeo Video 3.7 + + Compression Code or FourCC Codec ID: IV37 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV37 + Description: Intel Indeo Video 3.7 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 48] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.47 Intel Indeo Video 3.8 + + Compression Code or FourCC Codec ID: IV38 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV38 + Description: Intel Indeo Video 3.8 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.48 Intel Indeo Video 3.9 + + Compression Code or FourCC Codec ID: IV39 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV39 + Description: Intel Indeo Video 3.9 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.49 Intel Indeo Video 4.0 + + Compression Code or FourCC Codec ID: IV40 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV40 + Description: Intel Indeo Video 4.0 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.50 Intel Indeo Video 4.1 + + Compression Code or FourCC Codec ID: IV41 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV41 + Description: Intel Indeo Video 4.1 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 49] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.51 Intel Indeo Video 4.2 + + Compression Code or FourCC Codec ID: IV42 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV42 + Description: Intel Indeo Video 4.2 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.52 Intel Indeo Video 4.3 + + Compression Code or FourCC Codec ID: IV43 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV43 + Description: Intel Indeo Video 4.3 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.53 Intel Indeo Video 4.4 + + Compression Code or FourCC Codec ID: IV44 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV44 + Description: Intel Indeo Video 4.4 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.54 Intel Indeo Video 4.5 + + Compression Code or FourCC Codec ID: IV45 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV45 + Description: Intel Indeo Video 4.5 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 50] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.55 Intel Indeo Video 4.6 + + Compression Code or FourCC Codec ID: IV46 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV46 + Description: Intel Indeo Video 4.6 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.56 Intel Indeo Video 4.7 + + Compression Code or FourCC Codec ID: IV47 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV47 + Description: Intel Indeo Video 4.7 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.57 Intel Indeo Video 4.8 + + Compression Code or FourCC Codec ID: IV48 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV48 + Description: Intel Indeo Video 4.8 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.58 Intel Indeo Video 4.9 + + Compression Code or FourCC Codec ID: IV49 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV49 + Description: Intel Indeo Video 4.9 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + + + + +Fleischman Informational [Page 51] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.59 Intel Indeo Video 5.0 + + Compression Code or FourCC Codec ID: IV50 + Codec ID in the IANA Namespace: video/vnd.avi;codec=IV50 + Description: Intel Indeo Video 5.0 + Bit Depth: 8, 16, 24, 32 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.60 Still Image JPEG DIB + + Compression Code or FourCC Codec ID: JPEG + Codec ID in the IANA Namespace: video/vnd.avi;codec=JPEG + Description: Still Image JPEG DIB + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.61 Motion JPEG DIB + + Compression Code or FourCC Codec ID: MJPG + Codec ID in the IANA Namespace: video/vnd.avi;codec=MJPG + Description: Motion JPEG DIB Format + Bit Depth: 24, 8 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.62 Microsoft MPEG-4 Video Codec + + Compression Code or FourCC Codec ID: MP42 + Codec ID in the IANA Namespace: video/vnd.avi;codec=MP42 + Description: Microsoft MPEG-4 Video Codec V2 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + +Fleischman Informational [Page 52] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.63 MPEG 1 Video Frame + + Compression Code or FourCC Codec ID: MPEG + Codec ID in the IANA Namespace: video/vnd.avi;codec=MPEG + Description: MPEG 1 Video I Frame + Contact: + Greg Stiehl + stiehl@chromatic.com + Chromatic Research, Inc + 615 Tasman Dr + Sunnyvale, California 94089 USA + 408-752-9100 + + B.64 MR Codec + + Compression Code or FourCC Codec ID: MRCA + Codec ID in the IANA Namespace: video/vnd.avi;codec=MRCA + Description: MR Codec + Contact: + Martin Regen + 49/89/50206-252 + FAST Multimedia AG + Lansbergerstrasse 76 + Munchen 80339 Germany + 49/89/50206-0 + + B.65 Run Length Encoding + + Compression Code or FourCC Codec ID: MRLE + Codec ID in the IANA Namespace: video/vnd.avi;codec=MRLE + Description: Run Length Encoding + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.66 Video 1 + + Compression Code or FourCC Codec ID: MSVC + Codec ID in the IANA Namespace: video/vnd.avi;codec=MSVC + Description: Microsoft Video 1 + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + + +Fleischman Informational [Page 53] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.67 Photomotion + + Compression Code or FourCC Codec ID: PHMO + Codec ID in the IANA Namespace: video/vnd.avi;codec=PHMO + Description: Photomotion + Contact: + IBM Corporation + + B.68 QPEG 1.1 Format Video + + Compression Code or FourCC Codec ID: qpeq + Codec ID in the IANA Namespace: video/vnd.avi;codec=qpeq + Description: QPEG 1.1 Format Video Codec + Contact: + Dr. Knabe + 0049.2161.6181.0 + Q-Team + Brauereistr. 11 + D-41352 Korschenbroich Germany + 0049.2161.6181.0 + + B.69 RGBT + + Compression Code or FourCC Codec ID: RGBT + Codec ID in the IANA Namespace: video/vnd.avi;codec=RGBT + Description: 32 bits support + Contact: + Andy Pennell + Computer Concepts Ltd. + Gaddesden Place + Hemel Hempstead, Herts HP2 6EX UK + 44 1442 63933 + + B.70 Run Length Encoded 4 + + Compression Code or FourCC Codec ID: RLE4 + Codec ID in the IANA Namespace: video/vnd.avi;codec=RLE4 + Description: Run Length Encoded 4 + Bit Depth: 4 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + + + + +Fleischman Informational [Page 54] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.71 Run Length Encoded 8 + + Compression Code or FourCC Codec ID: RLE8 + Codec ID in the IANA Namespace: video/vnd.avi;codec=RLE8 + Description: Run Length Encoded 8 + Bit Depth: 8 + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.72 Indeo 2.1 + + Compression Code or FourCC Codec ID: RT21 + Codec ID in the IANA Namespace: video/vnd.avi;codec=RT21 + Description: Indeo 2.1 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.73 Intel RDX + + Compression Code or FourCC Codec ID: RVX + Codec ID in the IANA Namespace: video/vnd.avi;codec=RVX + Description: Intel RDX + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.74 Sun Digital Camera Codec + + Compression Code or FourCC Codec ID: SDCC + Codec ID in the IANA Namespace: video/vnd.avi;codec=SDCC + Description: Sun Digital Camera Codec + Contact: + Hideki Inoue + hinoue@sun-denshi.co.jp + Sun Communications, Inc. + GLORIA Bld. 6F, 1-3, AGEBA-CHO, SHINJYUKU-KU + Tokyo 162 Japan + 81-3-5261-1801 + + + + + +Fleischman Informational [Page 55] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.75 Crystal Net SFM Codec + + Compression Code or FourCC Codec ID: SFMC + Codec ID in the IANA Namespace: video/vnd.avi;codec=SFMC + Description: Crystal Net SFM Codec + Contact: + Dr. Itzhak Levit + Crystal Net Corporation + 1485 Saratoga Ave. + San Jose, California 95129 USA + 408-446-2966 + + B.76 SMSC + + Compression Code or FourCC Codec ID: SMSC + Codec ID in the IANA Namespace: video/vnd.avi;codec=SMSC + Description: SMSC + Contact: + Lee Boekelheide + Radius (USA) + 503-968-1270 + + B.77 SMSD + + Compression Code or FourCC Codec ID: SMSD + Codec ID in the IANA Namespace: video/vnd.avi;codec=SMSD + Description: SMSD + Contact: + Lee Boekelheide + Radius (USA) + 503-968-1270 + + B.78 Splash Studios ACM Audio Codec + + Compression Code or FourCC Codec ID: SPLC + Codec ID in the IANA Namespace: audio/vnd.wave;codec=SPLC + Description: Splash Studios ACM Audio Codec + Contact: + Mark Cutter + Splash Studios + 8573 154th Avenue NE + Redmond, WA 98052 USA + 425-882-0300 + + + + + + + + +Fleischman Informational [Page 56] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.79 Microsoft VXtreme Video Codec + + Compression Code or FourCC Codec ID: SQZ2 + Codec ID in the IANA Namespace: video/vnd.avi;codec=SQZ2 + Description: Microsoft Vxtreme Video Codec V2 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.80 Sorenson Video R1 + + Compression Code or FourCC Codec ID: SV10 + Codec ID in the IANA Namespace: video/vnd.avi;codec=SV10 + Description: Sorenson Video R1 + Contact: + Evan Hillman + ehillman@s-vision.com + Sorenson Vision + 570 East Research Park Way + Logan, Utah 84341 USA + 801-792-1114 + + B.81 TeraLogic Motion Infraframe Codec A + + Compression Code or FourCC Codec ID: TLMS + Codec ID in the IANA Namespace: video/vnd.avi;codec=TLMS + Description: TeraLogic Motion Intraframe Codec + Contact: + Charles Chui + chui@teralogic-inc.com + 650-526-6003 + TeraLogic, Inc. + 707 California Street + Mountain View, California 94041 USA + 650-526-2000 + + + + + + + + + + + + + +Fleischman Informational [Page 57] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.82 TeraLogic Motion Infraframe Codec B + + Compression Code or FourCC Codec ID: TLST + Codec ID in the IANA Namespace: video/vnd.avi;codec=TLST + Description: TeraLogic Motion Intraframe Codec + Contact: + Charles Chui + chui@teralogic-inc.com + 650-526-6003 + TeraLogic, Inc. + 707 California Street + Mountain View, California 94041 USA + 650-526-2000 + + B.83 TrueMotion 2.0 + + Compression Code or FourCC Codec ID: TM20 + Codec ID in the IANA Namespace: video/vnd.avi;codec=TM20 + Description: TrueMotion 2.0 + Contact: + David Silver + david@duck.com + (212) 941-2403 + The Duck Corporation + 375 Greenwich Street + New York, New York 10013 USA + (212) 941-2400 + + B.84 TeraLogic Motion Intraframe Codec 2 + + Compression Code or FourCC Codec ID: TMIC + Codec ID in the IANA Namespace: video/vnd.avi;codec=TMIC + Description: TeraLogic Motion Intraframe Codec 2 + Contact: + Charles Chui + chui@teralogic-inc.com + 650-526-6003 + TeraLogic, Inc. + 707 California Street + Mountain View, California 94041 USA + 650-526-2000 + + + + + + + + + + +Fleischman Informational [Page 58] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.85 TrueMotion Video Compression + + Compression Code or FourCC Codec ID: tmot + Codec ID in the IANA Namespace: video/vnd.avi;codec=tmot + Description: True Motion Video Compression + Contact: + Glen D. Johnson + Horizons Technology, Inc + 3990 Ruffin Road + San Diego, California 92123 USA + 619-292-8331 + + B.86 TrueMotion RT 2.0 + + Compression Code or FourCC Codec ID: TR20 + Codec ID in the IANA Namespace: video/vnd.avi;codec=TR20 + Description: TrueMotionRT 2.0 + Contact: + David Silver + david@duck.com + (212) 941-2403 + The Duck Corporation + 375 Greenwich Street + New York, New York 10013 USA + (212) 941-2400 + + B.87 Ultimotion + + Compression Code or FourCC Codec ID: ULTI + Codec ID in the IANA Namespace: video/vnd.avi;codec=ULTI + Description: Ultimotion + Contact: + IBM Corporation + + B.88 UYVY 4:2:2 byte ordering + + Compression Code or FourCC Codec ID: UYVY + Codec ID in the IANA Namespace: video/vnd.avi;codec=UYVY + Description: UYVY 4:2:2 byte ordering + Bit Depth: 16 + Contact: + Terri Hendry + 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + + + + +Fleischman Informational [Page 59] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.89 24 bit YUV 4:2:2 Format + + Compression Code or FourCC Codec ID: V422 + Codec ID in the IANA Namespace: video/vnd.avi;codec=V422 + Description: 24 bit YUV 4:2:2 format (CCIR 601). + For this format, 2 consecutive pixels are represented by a 32 bit (4 + byte) Y1UY2V color value. + Contact: + Gueirard RJ + 33 1 46 29 0300 + Vitec Multimedia + 99 rue Pierre Semard + F-92320 Chatillon France + 33-1-46-73-06-06 + + B.90 16 bit YUV 4:2:2 Format + + Compression Code or FourCC Codec ID: V655 + Codec ID in the IANA Namespace: video/vnd.avi;codec=V655 + Description: 16 bit YUV 4:2:2 Format Codec + Contact: + Gueirard RJ + 33 1 46 29 0300 + Vitec Multimedia + 99 rue Pierre Semard + F-92320 Chatillon France + 33-1-46-73-06-06 + + B.91 ATI VCR 1.0 + + Compression Code or FourCC Codec ID: VCR1 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR1 + Description: ATI VCR 1.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + + + + + + + + + +Fleischman Informational [Page 60] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.92 ATI VCR 2.0 + + Compression Code or FourCC Codec ID: VCR2 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR2 + Description: ATI VCR 2.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + B.93 ATI VCR 3.0 + + Compression Code or FourCC Codec ID: VCR3 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR3 + Description: ATI VCR 3.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + B.94 ATI VCR 4.0 + + Compression Code or FourCC Codec ID: VCR4 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR4 + Description: ATI VCR 4.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + + + + + + + + + +Fleischman Informational [Page 61] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.95 ATI VCR 5.0 + + Compression Code or FourCC Codec ID: VCR5 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR5 + Description: ATI VCR 5.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + B.96 ATI VCR 6.0 + + Compression Code or FourCC Codec ID: VCR6 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR6 + Description: ATI VCR 6.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + B.97 ATI VCR 7.0 + + Compression Code or FourCC Codec ID: VCR7 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR7 + Description: ATI VCR 7.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + + + + + + + + + +Fleischman Informational [Page 62] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.98 ATI VCR 8.0 + + Compression Code or FourCC Codec ID: VCR8 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR8 + Description: ATI VCR 8.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + B.99 ATI VCR 9.0 + + Compression Code or FourCC Codec ID: VCR9 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VCR9 + Description: ATI VCR 9.0 + Contact: + Ivan Yang + Ivan@atitech.ca + 905-882-2600 x3243 + ATI Technologies Inc. + 33 Commerce Valley Dr. E. + Thornhill, Ontario L3T 7N6 Canada + 905-882-2600 + + B.100 Video Maker Pro DIB + + Compression Code or FourCC Codec ID: VDCT + Codec ID in the IANA Namespace: video/vnd.avi;codec=VDCT + Description: Video Maker Pro DIB + Bit Depth: 16 + Contact: + Gueirard RJ + 33 1 46 29 0300 + Vitec Multimedia + 99 rue Pierre Semard + F-92320 Chatillon France + 33-1-46-73-06-06 + + + + + + + + + + +Fleischman Informational [Page 63] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.101 YUV 4:2:2 CCIR 601 for V422 + + Compression Code or FourCC Codec ID: VIDS + Codec ID in the IANA Namespace: video/vnd.avi;codec=VIDS + Description: YUV 4:2:2 CCIR for V422 + Bit Depth: 24, 16 + Contact: + Gueirard RJ + 33 1 46 29 0300 + Vitec Multimedia + 99 rue Pierre Semard + F-92320 Chatillon France + 33-1-46-73-06-06 + + B.102 Vivo H.263 + + Compression Code or FourCC Codec ID: VIVO (Note: it is also + registered as vivo) + Codec ID in the IANA Namespace: video/vnd.avi;codec=VIVO + (Note: it is also registered as video/vnd.avi;codec=vivo) + Description: Vivo H.263 + Bit Depth: 16 + Contact: + Vivo Software + 411 Waverley Oaks Road, Suite 313 + Waltham, MA 02154 USA + (617) 899-8900 + + B.103 VIXL + + Compression Code or FourCC Codec ID: VIXL + Codec ID in the IANA Namespace: video/vnd.avi;codec=VIXL + Description: for use with the miro video + and movie products + Bit Depth: 8, 16, 24 + Contact: + Matthias Huebner + 49-531-2113-519 + Miro Computer Products AG + Carl-Miele-Strasse 4 + Braunsweig 38112 Germany + 49-531-2113-0 + + + + + + + + + +Fleischman Informational [Page 64] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.104 VLCAP.DRV + + Compression Code or FourCC Codec ID: VLV1 + Codec ID in the IANA Namespace: video/vnd.avi;codec=VLV1 + Description: VLCAP.DRV + Contact: + Videologic + Home Park Estate + Kings Langley WD4 8LZ UK + 44923260511 + + B.105 W9960 + + Compression Code or FourCC Codec ID: WBVC + Codec ID in the IANA Namespace: video/vnd.avi;codec=WBVC + Description: W9960 + Contact: + Jason Lin + JLLIN@winbond.com.tw + Winbond Electronics Corp + PG41, No. 9, Li Hsin Rd. + Science-Based Industrial Park + Hsinchu, Taiwan + 886-3-5790666 x6641 + + B.106 mmioFOURCC('X",'2','6','3') + + Compression Code or FourCC Codec ID: X263 + Codec ID in the IANA Namespace: video/vnd.avi;codec=X263 + Description: mmioFOURCC('X",'2','6','3') + Bit Depth: 12 bits/pixel + Contact: + Min-Hsiung Lin + Min.Lin@xirlink.com + Xirlink, Inc. + 2210 O'Toole Ave. + San Jose, California 95131 USA + 408-324-2100 + + + + + + + + + + + + + +Fleischman Informational [Page 65] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.107 XL Video Decoder + + Compression Code or FourCC Codec ID: XLV0 + Codec ID in the IANA Namespace: video/vnd.avi;codec=XLV0 + Description: PC1 4:1:1 with transparency + Contact: + Gary Grandbois + NetXL, Inc + 48521 Warm Springs Blvd., Suite 310 + Fremont, California 94539 USA + 510-445-8734 + + B.108 YUV 2:1:1 Packed + + Compression Code or FourCC Codec ID: Y211 + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y211 + Description: YUV 2:1:1 Packed + Bit Depth: 8 + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.109 YUV 4:1:1 Packed + + Compression Code or FourCC Codec ID: Y411 + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y411 + Description: YUV 4:1:1 Packed + Bit Depth: 16 + Contact: + Terri Hendry, 425-936-2069 + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.110 YUV 4:1:1 Planar + + Compression Code or FourCC Codec ID: Y41B + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y41B + Description: YUV 4:1:1 Planar + Contact: + 408-522-7541 + Weitek (USA) + + + + + + + +Fleischman Informational [Page 66] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.111 PC1 4:1:1 + + Compression Code or FourCC Codec ID: Y41P + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y41P + Description: PC1 4:1:1 + Bit Depth: 12 + Contact: + Dave Wilson + 512-502-1725 + Brooktree Corporation + 9868 Scranton Road + San Diego, California 92121-3707 USA + 1-800-228-2777 + + B.112 PC1 4:1:1 with transparency + + Compression Code or FourCC Codec ID: Y41T + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y41T + Description: PC1 4:1:1 with transparency + Bit Depth: 12 + Contact: + Dave Wilson + 512-502-1725 + Brooktree Corporation + 9868 Scranton Road + San Diego, California 92121-3707 USA + 1-800-228-2777 + + B.113 YUV 4:2:2 Planar + + Compression Code or FourCC Codec ID: Y42B + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y42B + Description: YUV 4:2:2 Planar + Contact: + Weitek (USA) + 408-522-7541 + + + + + + + + + + + + + + + +Fleischman Informational [Page 67] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.114 PCI 4:2:2 with transparency + + Compression Code or FourCC Codec ID: Y42T + Codec ID in the IANA Namespace: video/vnd.avi;codec=Y42T + Description: PCI 4:2:2 with transparency + Bit Depth: 12 + Contact: + Dave Wilson + 512-502-1725 + Brooktree Corporation + 9868 Scranton Road + San Diego, California 92121-3707 USA + 1-800-228-2777 + + B.115 Intel YUV12 Codec + + Compression Code or FourCC Codec ID: YC12 + Codec ID in the IANA Namespace: video/vnd.avi;codec=YC12 + Description: Intel YUV12 Codec + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + B.116 Winnov Caviar YUV8 + + Compression Code or FourCC Codec ID: YUV8 + Codec ID in the IANA Namespace: video/vnd.avi;codec=YUV8 + Description: Winnov Caviar YUV8 + Contact: + Winnov, Inc. + 1230 Oakmead Parkway, Suite 312 + Sunnyvale, California 94086 + 408-733-7419 + + B.117 YUV9 + + Compression Code or FourCC Codec ID: YUV9 + Codec ID in the IANA Namespace: video/vnd.avi;codec=YUV9 + Description: YUV9 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + +Fleischman Informational [Page 68] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.118 YUYV 4:2:2 byte ordering packed + + Compression Code or FourCC Codec ID: YUY2 + Codec ID in the IANA Namespace: video/vnd.avi;codec=YUY2 + Description: YUYV 4:2:2 byte ordering packed + Bit Depth: 16 + Contact: + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.119 BI_YUYV, Canopus + + Compression Code or FourCC Codec ID: YUYV + Codec ID in the IANA Namespace: video/vnd.avi;codec=YUYV + Description: BI_YUYV, Canopus + Bit Depth: 16 + Contact: + Masayoshi Araki + m-araki@canopus.co.jp + 81-78-992-7812 + Canopus, Co., Ltd. + Kobe Hi-Tech Park + 1-2-2 Murotani, Nishi-ku + Kobe, Hyogo 651-22 Japan + + B.120 YVU12 Planar + + Compression Code or FourCC Codec ID: YV12 + Codec ID in the IANA Namespace: video/vnd.avi;codec=YV12 + Description: YVU12 Planar + Contact: + Weitek + 408-522-7541 + + B.121 YVU9 Planar + + Compression Code or FourCC Codec ID: YVU9 + Codec ID in the IANA Namespace: video/vnd.avi;codec=YVU9 + Description: YVU9 Planar + Bit Depth: 9 + Contact: + Intel Corporation + 5200 NE Elam Young Parkway + Hillsboro, Oregon 97124 USA + 503-696-2448 + + + + + +Fleischman Informational [Page 69] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + + B.122 YVYU 4:2:2 byte ordering + + Compression Code or FourCC Codec ID: YVYU + Codec ID in the IANA Namespace: video/vnd.avi;codec=YUV9 + Description: YVYU + Bit Depth: 16 + Contact: + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 USA + + B.123 Video Zipper + + Compression Code or FourCC Codec ID: ZPEG + Codec ID in the IANA Namespace: video/vnd.avi;codec=ZPEG + Description: for the Video Zipper + Contact: + Metheus + 1600 NW Compton Drive + Beaverton, Oregon 97006-6905 USA + 503-690-1550 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fleischman Informational [Page 70] + +RFC 2361 WAVE and AVI Codec Registries June 1998 + + +C. Full Copyright Statement + + Copyright (C) The Internet Society (1998). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Fleischman Informational [Page 71] + diff --git a/doc/riffmci.pdf b/doc/riffmci.pdf new file mode 100644 index 0000000..142bf4a Binary files /dev/null and b/doc/riffmci.pdf differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..3c74a98 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(uart-terminal uart_terminal.cpp) +target_compile_features(uart-terminal PUBLIC cxx_std_20) +target_compile_options(uart-terminal PUBLIC -I${CMAKE_SOURCE_DIR}) +target_link_libraries(uart-terminal smart crack crypt m) +install(TARGETS uart-terminal RUNTIME DESTINATION bin COMPONENT tools) + +add_executable(gpio-blink gpio_blink.cpp) +target_compile_features(gpio-blink PUBLIC cxx_std_20) +target_compile_options(gpio-blink PUBLIC -I${CMAKE_SOURCE_DIR}) +target_link_libraries(gpio-blink smart crack crypt m) +install(TARGETS gpio-blink RUNTIME DESTINATION bin COMPONENT tools) diff --git a/examples/gpio_blink.cpp b/examples/gpio_blink.cpp new file mode 100644 index 0000000..d9a95f2 --- /dev/null +++ b/examples/gpio_blink.cpp @@ -0,0 +1,55 @@ +// gpio_blink.cpp — Blink the lowest bit of a Xilinx AXI GPIO at 1 Hz +// +// Device tree should expose the GPIO as a UIO device, e.g.: +// gpio0: gpio@41200000 { +// compatible = "trenz.biz,smartio-1.0"; +// reg = <0x41200000 0x1000>; +// }; + +#include +#include +#include +#include + +#include "smart/UioDevice.h" + +// Xilinx AXI GPIO register indices (32-bit word addressing) +enum Reg : uint32_t { + GPIO_DATA = 0, // Channel 1 Data + GPIO_TRI = 1, // Channel 1 Tri-state (0 = output) + GPIO2_DATA = 2, // Channel 2 Data + GPIO2_TRI = 3, // Channel 2 Tri-state +}; + +static volatile sig_atomic_t running = 1; + +static void sighandler(int) { running = 0; } + +int main(int argc, char* argv[]) +{ + const char* dev = argc > 1 ? argv[1] : "gpio"; + + smart::UioDevice uio(dev); + smart::MappedFile* regs = uio.getRequiredMap(0); + + // Configure bit 0 as output + uint32_t tri = regs->read32(GPIO_TRI); + regs->write32(GPIO_TRI, tri & ~1u); + + std::fprintf(stderr, "gpio_blink: %s bit 0, 1 Hz (ctrl-C to quit)\n", dev); + + std::signal(SIGINT, sighandler); + std::signal(SIGTERM, sighandler); + + uint32_t state = 0; + while (running) { + state ^= 1; + regs->write32(GPIO_DATA, (regs->read32(GPIO_DATA) & ~1u) | state); + usleep(500'000); // 500 ms half-period → 1 Hz + } + + // Turn off on exit + regs->write32(GPIO_DATA, regs->read32(GPIO_DATA) & ~1u); + std::fprintf(stderr, "\ngpio_blink: exiting\n"); + return 0; +} diff --git a/examples/uart_terminal.cpp b/examples/uart_terminal.cpp new file mode 100644 index 0000000..d2c0446 --- /dev/null +++ b/examples/uart_terminal.cpp @@ -0,0 +1,143 @@ +// uart_terminal.cpp — Simple serial terminal for a 16550 UART via UIO +// +// Device tree should expose the UART as a UIO device, e.g.: +// uart0: serial@43c00000 { +// compatible = "trenz.biz,smartio-1.0"; +// reg = <0x43c00000 0x1000>; +// interrupts = <0 29 4>; +// }; + +#include +#include +#include +#include +#include +#include +#include + +#include "smart/UioDevice.h" + +// 16550 register indices (32-bit word addressing) +enum Reg : uint32_t { + RBR = 0, // Receive Buffer (read) + THR = 0, // Transmit Holding (write) + IER = 1, // Interrupt Enable + IIR = 2, // Interrupt Identification (read) + FCR = 2, // FIFO Control (write) + LCR = 3, // Line Control + MCR = 4, // Modem Control + LSR = 5, // Line Status + MSR = 6, // Modem Status + SCR = 7, // Scratch + DLL = 0, // Divisor Latch Low (LCR.DLAB=1) + DLM = 1, // Divisor Latch High (LCR.DLAB=1) +}; + +// LSR bits +constexpr uint32_t LSR_DR = 0x01; // Data Ready +constexpr uint32_t LSR_THRE = 0x20; // TX Holding Register Empty + +// IER bits +constexpr uint32_t IER_RDI = 0x01; // Receive Data Available + +static void set_baud(smart::MappedFile* regs, uint32_t clk_hz, uint32_t baud) +{ + uint32_t divisor = clk_hz / (16 * baud); + uint32_t lcr = regs->read32(LCR); + regs->write32(LCR, lcr | 0x80); // Set DLAB + regs->write32(DLL, divisor & 0xFF); + regs->write32(DLM, (divisor >> 8) & 0xFF); + regs->write32(LCR, lcr & ~0x80u); // Clear DLAB +} + +static void uart_init(smart::MappedFile* regs, uint32_t clk_hz, uint32_t baud) +{ + regs->write32(IER, 0x00); // Disable all interrupts + regs->write32(FCR, 0x07); // Enable & reset FIFOs + regs->write32(LCR, 0x03); // 8N1 + regs->write32(MCR, 0x00); // No flow control + set_baud(regs, clk_hz, baud); + regs->write32(IER, IER_RDI); // Enable RX interrupt +} + +// Drain all available bytes from the UART RX FIFO +static void drain_rx(smart::MappedFile* regs) +{ + while (regs->read32(LSR) & LSR_DR) { + char c = static_cast(regs->read32(RBR) & 0xFF); + write(STDOUT_FILENO, &c, 1); + } +} + +// Transmit one byte, busy-wait for THRE +static void tx_byte(smart::MappedFile* regs, uint8_t b) +{ + while (!(regs->read32(LSR) & LSR_THRE)) + ; + regs->write32(THR, b); +} + +int main(int argc, char* argv[]) +{ + const char* dev = argc > 1 ? argv[1] : "serial"; + uint32_t clk_hz = 100'000'000; // 100 MHz typical for PL UARTs + uint32_t baud = 115200; + + // Open the UIO device by name + smart::UioDevice uio(dev); + smart::MappedFile* regs = uio.getRequiredMap(0); + + uart_init(regs, clk_hz, baud); + std::fprintf(stderr, "uart_terminal: %s @ %u baud (ctrl-C to quit)\n", dev, baud); + + // Put stdin in raw mode so keypresses are sent immediately + struct termios orig, raw; + tcgetattr(STDIN_FILENO, &orig); + raw = orig; + cfmakeraw(&raw); + tcsetattr(STDIN_FILENO, TCSANOW, &raw); + + int uio_fd = uio.getFileHandle(); + + struct pollfd fds[2] = { + { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 }, + { .fd = uio_fd, .events = POLLIN, .revents = 0 }, + }; + + bool running = true; + while (running) { + int ret = poll(fds, 2, -1); + if (ret < 0) { + if (errno == EINTR) continue; + break; + } + + // Keyboard → UART TX + if (fds[0].revents & POLLIN) { + char buf[64]; + ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); + for (ssize_t i = 0; i < n; ++i) { + if (buf[i] == 0x03) { running = false; break; } // Ctrl-C + tx_byte(regs, static_cast(buf[i])); + } + } + + // UART RX interrupt → screen + if (fds[1].revents & POLLIN) { + // Acknowledge the UIO interrupt (read the count) + uint32_t irq_count; + read(uio_fd, &irq_count, sizeof(irq_count)); + + drain_rx(regs); + + // Re-enable the UIO interrupt + uint32_t unmask = 1; + write(uio_fd, &unmask, sizeof(unmask)); + } + } + + // Restore terminal + tcsetattr(STDIN_FILENO, TCSANOW, &orig); + std::fprintf(stderr, "\nuart_terminal: exiting\n"); + return 0; +} diff --git a/libsmart_tools/Dockerfile b/libsmart_tools/Dockerfile index b6845ce..aaa74ac 100644 --- a/libsmart_tools/Dockerfile +++ b/libsmart_tools/Dockerfile @@ -10,6 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ git \ ca-certificates \ libcrack2-dev \ + pkg-config \ && rm -rf /var/lib/apt/lists/* #mount ..:/work diff --git a/smart/WavFile.cpp b/smart/WavFile.cpp new file mode 100644 index 0000000..9b4a4ef --- /dev/null +++ b/smart/WavFile.cpp @@ -0,0 +1,915 @@ +/* + * WavFile.cpp + * + * Created on: Dec 16, 2015 + * Author: peeter + */ + +#include "WavFileSimple.h" + +namespace smart { + +WavFile::fourcc_t::fourcc_t( const char *init ) +{ + asU32 = 0; + for( int i=0; i<4; i++ ) + { + if( *init ) + { + asChr[i] = *init; + init ++; + } + else + asChr[i] = ' '; + } +} + + +//--------------------------------------------------------------------------------------------------------- + +WavFile::Chunk::Chunk( Chunk *parent, uint32_t header_size, fourcc_t ckID ) +{ + header_size = header_size < sizeof(chunk_t) ? sizeof(chunk_t) : header_size; + _header.resize(header_size, 0); + _min_size = sizeof(chunk_t); + chunk_t *hdr = (chunk_t*)_header.data(); + if( parent ) + _filebuf = parent->_filebuf; + + if( _filebuf != nullptr ) + { + _filepos = ftell( _filebuf->_file ); + if( fread( hdr, header_size, 1, _filebuf->_file ) <= 0 ) + { + hdr->ckID.asU32 = 0; + hdr->ckSize = 0; + seekFileStartOfChunk(); + } + else if( ckID.asU32 && (hdr->ckID.asU32 != ckID.asU32) ) + { + hdr->ckID.asU32 = 0; + hdr->ckSize = 0; + seekFileStartOfChunk(); + } + else + { + if( parent ) + parent->setChild( this ); + } + } + else + { + hdr->ckID = ckID; + hdr->ckSize = 0; + if( parent ) + parent->setChild( this ); + } +} + +WavFile::Chunk::Chunk( std::string fname, uint32_t header_size, fourcc_t ckID ) +{ + header_size = header_size < sizeof(chunk_t) ? sizeof(chunk_t) : header_size; + _header.resize(header_size, 0); + _min_size = sizeof(chunk_t); + _filebuf = FileBuffer::make_shared( fname ); + chunk_t *hdr = (chunk_t*)_header.data(); + + if( _filebuf != nullptr ) + { + // file found, read data and verify + _filepos = ftell( _filebuf->_file ); + if( fread( hdr, header_size, 1, _filebuf->_file ) <= 0 ) + _filebuf = nullptr; + else if( hdr->ckID.asU32 != ckID.asU32 ) + _filebuf = nullptr; // destroy the file as the assumption failed, fall back + } + + // if filebuf becomes nullptr, then it is like creating empty chunk above to fall back and avoid required user intervention + if( _filebuf == nullptr ) + { + hdr->ckID = ckID; + hdr->ckSize = 0; + // note the ckSize will be set when writing the data, after construction if this is 0- no file opened + // some chunks do not have data fields, so they have ckSize always 0, but it will have no sense when the root is 0 size + } +} + + +uint32_t WavFile::Chunk::getSize() +{ + uint32_t rv=0; + rv += _header.size(); + + rv += getDataSize(); + + // important assumption that the chunk must have data. + if( rv <= _min_size ) + return 0; + + return rv; +}; + +uint32_t WavFile::Chunk::getDataSize() +{ + uint32_t rv=0; + + if( _filebuf != nullptr ) + { + chunk_t *hdr = (chunk_t*)_header.data(); + if( hdr->ckSize == 0 ) + if( _header.size() > sizeof(chunk_t) ) + rv = 0; // error case the ckSize must be bigger than 0 + else + rv = sizeof(chunk_t); + else if( hdr->ckSize + sizeof(chunk_t) < _header.size() ) + rv = 0; // incorrect ckSize, result would be very big number + else + rv = hdr->ckSize - (_header.size() - sizeof(chunk_t)); + } + else if( _data.size() ) + { + // leaf chunk + for( auto i = _data.begin(); i != _data.end(); i++ ) + { + rv += (*i)->size(); + } + } + else for( auto i = _contents.begin(); i != _contents.end(); i++ ) + { + // compound chunk: each child occupies getSize() + pad byte on disk + uint32_t child_sz = (*i)->getSize(); + if( child_sz > 0 ) + { + rv += child_sz; + if( child_sz & 1 ) + rv += 1; // RIFF word-alignment pad byte + } + } + + return rv; +}; + + +uint32_t WavFile::Chunk::fillBuffer( uint8_t **buf, uint32_t &maxlen ) +{ + uint32_t rv = getSize(); + + // important assumption that the chunk must have data. + if( !rv ) + return 0; + + // the data must fit into buffer + if( maxlen < rv ) + return 0; + + // TODO: add support for writing from file + if( _filebuf != nullptr ) + return 0; + + chunk_t *hdr = (chunk_t*)_header.data(); + hdr->ckSize = rv - sizeof(chunk_t); // the chunk size does not contain chunk_t header bytes + + if( *buf != _header.data() ) + memcpy( *buf, _header.data(), _header.size() ); + *buf += _header.size(); maxlen -= _header.size(); + rv = _header.size(); + + if( _data.size() ) + { + for( auto i = _data.begin(); i != _data.end(); i++ ) + { + if( *buf != (*i)->data() ) + memcpy( *buf, (*i)->data(), (*i)->size() ); + *buf += (*i)->size(); maxlen -= (*i)->size(); + rv += (*i)->size(); + } + } + else for( auto i = _contents.begin(); i != _contents.end(); i++ ) + { + uint32_t child_written = (*i)->fillBuffer( buf, maxlen ); + rv += child_written; + if( child_written > 0 && (child_written & 1) ) + { + // RIFF word-alignment pad byte + **buf = 0; + (*buf)++; + maxlen--; + rv++; + } + } + + return rv; +} + +uint32_t WavFile::Chunk::writeFile( FILE *fp ) +{ + if( fp == NULL ) + return 0; + + uint32_t rv = getSize(); + + // important assumption that the chunk must have data. + if( !rv ) + return 0; + + // TODO: add support for writing from file + if( _filebuf != nullptr ) + return 0; + + + chunk_t *hdr = (chunk_t*)_header.data(); + hdr->ckSize = rv - sizeof(chunk_t); // the chunk size does not contain chunk_t header bytes + + rv = fwrite( _header.data(), 1, _header.size(), fp ); + + if( _data.size() ) + { + for (auto &i : _data) + { + if (!i->empty()) + { + rv += fwrite(i->data(), 1, i->size(), fp); + } + } + } + else for( auto i = _contents.begin(); i != _contents.end(); i++ ) + { + uint32_t child_written = (*i)->writeFile( fp ); + rv += child_written; + if( child_written > 0 && (child_written & 1) ) + { + // RIFF word-alignment pad byte + uint8_t pad = 0; + rv += fwrite( &pad, 1, 1, fp ); + } + } + + return rv; +} + +void WavFile::Chunk::setChild( Chunk *child ) +{ + if( _data.size() ) + return; // this is leaf chunk + + _contents.push_back( child ); + child->_filebuf = _filebuf; // all children use the same file +}; + +ByteBufferPtr WavFile::Chunk::getData( uint32_t i ) +{ + if( _filebuf != nullptr ) + { + // at the moment, onlu i==0 is supported + i = 0; + if( _data.size() ) + return _data[0]; + + // read the data from disk + chunk_t *hdr = (chunk_t*)_header.data(); + if( hdr->ckSize == 0 ) + return std::make_shared(); // chunk with no data + if( hdr->ckID.asU32 == 0 ) + return std::make_shared(); // errant chunk + + // create the buffer into memory and read data in + auto bf = std::make_shared( getDataSize() ); + seekFileStartOfData(); + fread( bf->data(), bf->size(), 1, _filebuf->_file ); + _data.push_back( bf ); + } + + if( _data.size() > i ) + { + return _data[i]; + } + + // default return empty buffer + return std::make_shared(); +}; + + +void WavFile::Chunk::addPiece( + uint8_t *origin, + uint32_t size ) +{ + if( _contents.size() ) + return; // this is not leaf chunk + + // TODO: add support for adding additional data to existing file + if( _filebuf != nullptr ) + return; + + addPiece( std::make_shared(origin, origin + size) ); +} + +ByteBufferPtr WavFile::Chunk::addPiece( uint32_t size ) +{ + if( _contents.size() ) + return std::make_shared(); + if( size == 0 ) + return std::make_shared(); + // TODO: add support for adding additional data to existing file + if( _filebuf != nullptr ) + return std::make_shared(); + + ByteBufferPtr rv = std::make_shared( size ); + + addPiece( rv ); + + return rv; +} + +void WavFile::Chunk::addPiece( ByteBufferPtr buf ) +{ + if( _contents.size() ) + return; // this is not leaf chunk + // TODO: add support for adding additional data to existing file + if( _filebuf != nullptr ) + return; + + _data.push_back( buf ); +} + +void WavFile::Chunk::seekFileEndOfChunk() +{ + if( _filebuf == nullptr ) + return; + + chunk_t *hdr = (chunk_t*)_header.data(); + int64_t pos = _filepos + sizeof(chunk_t) + hdr->ckSize; + if( hdr->ckSize & 1 ) + pos++; // skip RIFF word-alignment pad byte + fseek( _filebuf->_file, static_cast(pos), SEEK_SET ); +} + +/** seek file to start of data of chunk + * + */ +void WavFile::Chunk::seekFileStartOfData( int64_t dataseek ) +{ + if( _filebuf == nullptr ) + return; + + int64_t pos = _filepos + _header.size() + dataseek; + fseek( _filebuf->_file, static_cast(pos), SEEK_SET ); +} + +void WavFile::Chunk::seekFileStartOfChunk() +{ + if( _filebuf == nullptr ) + return; + + int64_t pos = _filepos; + fseek( _filebuf->_file, static_cast(pos), SEEK_SET ); +} + +uint32_t WavFile::Chunk::getPadSize() +{ + uint32_t sz = getSize(); + if( sz == 0 ) + return 0; + return (sz & 1) ? 1 : 0; +} + +bool WavFile::Chunk::inFileRange() +{ + if( _filebuf == nullptr ) + return false; + + int64_t pos = ftell( _filebuf->_file ); + + if( pos < _filepos ) + return false; + + chunk_t *hdr = (chunk_t*)_header.data(); + int64_t end = _filepos + hdr->ckSize + sizeof(chunk_t); + if( hdr->ckSize & 1 ) + end++; // account for RIFF word-alignment pad byte + if( pos >= end ) + return false; + + return true; +} + +//--------------------------------------------------------------------------------------------------------- +WavFile::LeafChunk::LeafChunk( Chunk *parent, uint32_t header_size, uint32_t data_size, fourcc_t ckID ) +: Chunk( parent, header_size, ckID ) +{ + if( data_size > 0 ) + addPiece( data_size ); +} + +//--------------------------------------------------------------------------------------------------------- + +WavFile::RiffChunk::RiffChunk( fourcc_t formType ) +: Chunk( 0, sizeof(riff_chunk_t), "RIFF" ) +{ + riff_chunk_t *hdr = (riff_chunk_t*)_header.data(); + _min_size = sizeof(riff_chunk_t); + + hdr->formType = formType; +} + +WavFile::RiffChunk::RiffChunk( std::string fname, fourcc_t formType ) +: Chunk( fname, sizeof(riff_chunk_t), "RIFF" ) +{ + riff_chunk_t *hdr = (riff_chunk_t*)_header.data(); + _min_size = sizeof(riff_chunk_t); + + // verify if the file is in correct form + if( hdr->formType.asU32 != formType.asU32 ) + { + // form error + _filebuf = nullptr; + hdr->riff.ckSize = 0; + } +} + +//--------------------------------------------------------------------------------------------------------- + +WavFile::WaveChunk::WaveChunk( Chunk *parent, uint32_t header_size, + uint16_t channels, + uint32_t samples_per_sec, + uint16_t bytes_per_value +): Chunk( parent, header_size, "fmt ") +{ + wave_format_t *hdr = (wave_format_t*)_header.data(); + + hdr->wFormatTag = 0; // the category is initiatet to 0 + hdr->wChannels = channels; + hdr->dwSamplesPerSec = samples_per_sec; + hdr->dwAvgBytesPerSec = samples_per_sec * channels * bytes_per_value; + hdr->wBlockAlign = channels * bytes_per_value; +} + +WavFile::WaveChunk::WaveChunk( Chunk *parent ): Chunk( parent, sizeof(pcm_format_t), "fmt ") +{ +} + +WavFile::PcmChunk::PcmChunk( Chunk *parent, + uint16_t channels, + uint32_t samples_per_sec, + uint16_t bits_per_sample +): WaveChunk( parent, sizeof(pcm_format_t), channels, samples_per_sec, (bits_per_sample+7)/8) +{ + pcm_format_t *hdr =(pcm_format_t*)_header.data(); + + hdr->wBitsPerSample = bits_per_sample; + hdr->waveFmt.wFormatTag = 1; +} + +WavFile::PcmChunk::PcmChunk( Chunk *parent ): + WaveChunk( parent ) +{ +} + + +//--------------------------------------------------------------------------------------------------------- + +void WavFile::PcmDataChunk::setSampleWidth(unsigned int widthInBits) +{ + if (_nchannels == 0u) { + return; + } + + const unsigned int nbits = ((widthInBits + 7u) / 8u) * 8u; + _row_length = ((nbits * _nchannels + 31) /32) * 4; +} + +uint32_t WavFile::PcmDataChunk::writeFile( FILE *fp ) +{ + uint32_t rv = 0; + if( _ratefactor > 1 ) + { + // generic chunk header part + if( fp == NULL ) + return 0; + + rv = getSize(); + + // important assumption that the chunk must have data. + if( !rv ) + return 0; + + chunk_t *hdr = (chunk_t*)_header.data(); + hdr->ckSize = rv - sizeof(chunk_t); // the chunk size does not contain chunk_t header bytes + + rv = fwrite( _header.data(), 1, _header.size(), fp ); + + // custom data part + int16_t *sample; + int32_t samplelen = _nchannels*sizeof(int16_t); + ByteBuffer obuf( 1024*samplelen ); + int16_t *pobuf = (int16_t*)obuf.data(); + int16_t *end = (int16_t*)((int8_t*)(obuf.data())+obuf.size()); + auto sit = getSampleIterator(samplelen); + for(;;) + { + auto sbuf = sit->getSampleInc( samplelen, _ratefactor ); + if( sbuf->empty() ) + break; + sample = (int16_t*)sbuf->data(); + memcpy( pobuf, sample, samplelen ); + pobuf += _nchannels; + if( pobuf >= end ) + { + // temporary buffer is full, write the file + pobuf = (int16_t*)obuf.data(); + rv += fwrite( obuf.data(), 1, obuf.size(), fp ); + } + } + // write the remaining of the not full buffer + size_t remains = (size_t)pobuf - (size_t)(obuf.data()); + if( remains > 0 ) + { + rv += fwrite( obuf.data(), 1, remains, fp ); + } + } + else + { + uint32_t limited = getDataSize(); + uint32_t full = Chunk::getDataSize(); + if( limited < full ) + { + // _row_length truncation: write only limited bytes + if( fp == NULL ) + return 0; + uint32_t total = _header.size() + limited; + if( !total ) + return 0; + + chunk_t *hdr = (chunk_t*)_header.data(); + hdr->ckSize = total - sizeof(chunk_t); + + rv = fwrite( _header.data(), 1, _header.size(), fp ); + + uint32_t remaining = limited; + for( auto &d : _data ) + { + if( d->empty() || remaining == 0 ) + break; + uint32_t to_write = (d->size() < remaining) ? static_cast(d->size()) : remaining; + rv += fwrite( d->data(), 1, to_write, fp ); + remaining -= to_write; + } + } + else + { + rv = Chunk::writeFile( fp ); + } + } + //printf("WavFile::PcmDataChunk::writeFile rv=%u\n", rv); + return rv; +} + +uint32_t WavFile::PcmDataChunk::getDataSize() +{ + const unsigned int raw = Chunk::getDataSize(); + unsigned int data_size; + + if( _ratefactor > 1 ) + { + // Decimation: the write loop starts at sample 0 and steps by _ratefactor, + // producing ceil(total_samples / _ratefactor) output samples. + const unsigned int samplelen = _nchannels * sizeof(int16_t); + if( samplelen == 0 ) + return 0; + const unsigned int total_samples = raw / samplelen; + if( total_samples == 0 ) + return 0; + const unsigned int output_samples = (total_samples - 1) / _ratefactor + 1; + data_size = output_samples * samplelen; + } + else + { + data_size = raw; + } + + if( _row_length > 0u ) + { + data_size = (data_size / _row_length) * _row_length; + } + + return data_size; +} + +/** + * THE INTERFACE SAMPLE ITERATOR + */ + +WavFile::PcmDataChunk::SampleIterator::SampleIterator( PcmDataChunk *chunk, uint32_t len ) +{ + _chunk = chunk; + _samplelen = len; +} + +#if (1) //SampleIteratorFile ................................................................................. + +/** + * sample iterator that iterates inside file on disk + * + */ +class SampleIteratorFile : public WavFile::PcmDataChunk::SampleIterator { +public: + /** + * Important assumption: each buffer in data divides exactly with len + */ + SampleIteratorFile( WavFile::PcmDataChunk *chunk, uint32_t len, uint32_t index = 0, uint32_t fraction = 0 ); + /** get single sample from the data + * return: + * pointer to the sample of all channels, 0 if the sample does not exist + */ + virtual ByteBufferPtr getSample( uint32_t count = 1 ); + /** set iterator position + * return: + * pointer to this iterator + */ + virtual SampleIterator *setPos( uint32_t index, uint32_t fraction = 0 ); + virtual SampleIterator *nextPos( uint32_t index=1, uint32_t fraction = 0 ); + /** get single sample from the data and increase the iterator afterwards + * return: + * pointer to the sample of all channels, 0 if the sample does not exist + */ + virtual ByteBufferPtr getSampleInc( uint32_t count = 1, uint32_t index=1, uint32_t fraction = 0 ); +protected: + int64_t _cursor; +}; + +WavFile::PcmDataChunk::SampleIterator *SampleIteratorFile::setPos( uint32_t index, uint32_t fraction ) +{ + _cursor = (int64_t)_samplelen * index; + _fraction = fraction; + return this; +} + +WavFile::PcmDataChunk::SampleIterator *SampleIteratorFile::nextPos( uint32_t index, uint32_t fraction ) +{ + _fraction += fraction; + uint32_t f = fraction >> FIX; + _fraction &= (1<seekFileStartOfData( _cursor ); + ByteBufferPtr rv = std::make_shared( count * _samplelen ); + fread( rv->data(), _samplelen, count, _chunk->_filebuf->_file ); + return rv; +} + +ByteBufferPtr SampleIteratorFile::getSampleInc( uint32_t count , uint32_t index, uint32_t fraction ) +{ + ByteBufferPtr rv = getSample( count ); + nextPos( index, fraction ); + return rv; +} + +#endif //SampleIteratorFile + +#if (1) //SampleIteratorMemory ............................................................................. + +/** + * sample iterator that iterates inside memory + * + */ +class SampleIteratorMemory : public WavFile::PcmDataChunk::SampleIterator { +public: + /** + * Important assumption: each buffer in data divides exactly with len + * + * chunk - the chunk where to iterate + * len - sample len in bytes + * index - starting index of the sample + * fraction - starting fraction of the sample index + */ + SampleIteratorMemory( WavFile::PcmDataChunk *chunk, uint32_t len, uint32_t index = 0, uint32_t fraction = 0 ); + /** get single sample from the data + * return: + * pointer to the sample of all channels, 0 if the sample does not exist + */ + virtual ByteBufferPtr getSample( uint32_t count = 1 ); + /** set iterator position + * return: + * pointer to this iterator + */ + virtual SampleIterator *setPos( uint32_t index, uint32_t fraction = 0 ); + virtual SampleIterator *nextPos( uint32_t index=1, uint32_t fraction = 0 ); + /** get single sample from the data and increase the iterator afterwards + * return: + * pointer to the sample of all channels, 0 if the sample does not exist + */ + virtual ByteBufferPtr getSampleInc( uint32_t count = 1, uint32_t index=1, uint32_t fraction = 0 ); +protected: + /// Index to the data junk + uint32_t _junkid; + /// Offset from the junk + uint8_t *_junkoffset; + /// Buffer end + uint8_t *_junkend; +}; + +SampleIteratorMemory::SampleIteratorMemory( WavFile::PcmDataChunk *chunk, uint32_t len, uint32_t index, uint32_t fraction ): + SampleIterator( chunk, len ) +{ + _junkid = 0xFFffFFff; + _junkoffset = 0; + _junkend = 0; + setPos( index, fraction ); +} + +WavFile::PcmDataChunk::SampleIterator* +SampleIteratorMemory::setPos( uint32_t index, uint32_t fraction ) +{ + _fraction = fraction; + index *= _samplelen; + _junkid = 0xFFffFFff; // invalidate iterator for the case we will not find the index location + for ( unsigned int i=0; i<_chunk->_data.size(); i++ ) + { + if( _chunk->_data[i]->size() < index ) + { + // note error will happen if _size do not divide with _samplelen + index -= _chunk->_data[i]->size(); + } + else if( (index+_samplelen) <= _chunk->_data[i]->size() ) // protect against buffer overrun + { + _junkid = i; + _junkoffset = (uint8_t*)(_chunk->_data[_junkid]->data()) + index; + _junkend = (uint8_t*)(_chunk->_data[_junkid]->data()) + _chunk->_data[_junkid]->size(); + break; + } + else + break; // TODO: fatal error, should throw exception instead, the buffer size must be incorrect + } + return this; +} + +WavFile::PcmDataChunk::SampleIterator* +SampleIteratorMemory::nextPos( uint32_t index, uint32_t fraction ) +{ + if( _junkid >= _chunk->_data.size() ) + { + _junkid = 0xFFffFFff; + _junkoffset = 0; + _junkend = 0; + return this; + } + + _fraction += fraction; + uint32_t f = fraction >> FIX; + _fraction &= (1<= _junkend ) + { + _junkid++; + if( _junkid >= _chunk->_data.size() ) + { + _junkid = 0xFFffFFff; + _junkoffset = 0; + _junkend = 0; + return this; + } + _junkoffset = ((uint8_t*)_chunk->_data[_junkid]->data()) + ((size_t)_junkoffset - (size_t)_junkend); + _junkend = ((uint8_t*)_chunk->_data[_junkid]->data()) + _chunk->_data[_junkid]->size(); + } + + if( (_junkoffset+_samplelen) <= _junkend ) + { + // returns only if offset and all the content fits into buffer + return this; + } + + _junkid++; + if( _junkid < _chunk->_data.size() ) + { + //printf("next buffer\n"); + _junkoffset = (uint8_t*)(_chunk->_data[_junkid]->data()); + _junkend = (uint8_t*)(_chunk->_data[_junkid]->data()) + _chunk->_data[_junkid]->size(); + return this; + } + // no more data + //printf("No more data _junkid=%u, _chunk->_data.size()=%u\n ", _junkid, _chunk->_data.size() ); + _junkid = 0xFFffFFff; + _junkoffset = 0; + _junkend = 0; + return this; +} + +ByteBufferPtr SampleIteratorMemory::getSample( uint32_t count ) +{ + if( _junkid == 0xFFffFFff ) + return std::make_shared(); + //FIXME: the _samplelen * count can leap over the buffer boundary, new buffer shall be made + return std::make_shared( _junkoffset, _junkoffset + _samplelen * count ); +} + +ByteBufferPtr SampleIteratorMemory::getSampleInc( uint32_t count, uint32_t index, uint32_t fraction ) +{ + auto rv = getSample( count ); + nextPos( index, fraction ); + return rv; +} + +#endif //SampleIteratorMemory + +/** + * Get the proper sample iterator + */ +std::shared_ptr +WavFile::PcmDataChunk::getSampleIterator( uint32_t len, uint32_t index, uint32_t fraction ) +{ + if( _filebuf != nullptr ) + { + return std::make_shared( this, len, index, fraction ); + } + return std::make_shared( this, len, index, fraction ); +} + + +//--------------------------------------------------------------------------------------------------------- + +WavFile::CueChunk::CueChunk( Chunk *parent ) +: Chunk( parent, sizeof(cue_chunk_t), "cue ") +{ + cue_chunk_t *hdr = (cue_chunk_t*)_header.data(); + _min_size = sizeof(cue_chunk_t); + + hdr->dwCuePoints = 0; +} + +void WavFile::CueChunk::setPoint( fourcc_t name, + fourcc_t chunk_name, + uint32_t chunk_start, + uint32_t block_start, + uint32_t sample_offset +) +{ + cue_chunk_t *hdr = (cue_chunk_t*)_header.data(); + + cue_point_t *point = (cue_point_t *)addPiece( sizeof(cue_point_t) )->data(); + + point->dwName = name; + point->dwPosition = sample_offset; + point->fccChunk = chunk_name; + point->dwChunkStart = chunk_start; + point->dwBlockStart = block_start; + point->dwSampleOffset = sample_offset; + + hdr->dwCuePoints++; +} + +/// set point of single data chunk wav file +void WavFile::CueChunk::setWavPoint( const char* name, + const char* chunk_name, uint32_t sample_offset ) +{ + setPoint( name, + chunk_name, 0, 0, sample_offset ); +} + +//--------------------------------------------------------------------------------------------------------- + +WavFile::AssocListChunk::AssocListChunk( Chunk *parent ) : Chunk( parent, sizeof(assoc_data_t), "LIST" ) +{ + assoc_data_t *hdr = (assoc_data_t*)_header.data(); + _min_size = sizeof(assoc_data_t); + + hdr->fccAdtl = fourcc_t("adtl"); +} + +//--------------------------------------------------------------------------------------------------------- + +WavFile::LabelChunk::LabelChunk( Chunk *parent, fourcc_t name, const char *label ) +: LeafChunk( parent, sizeof(label_chunk_t), strlen(label)+1, "labl" ) +{ + label_chunk_t *hdr =(label_chunk_t*)_header.data(); + _min_size = sizeof(label_chunk_t); + + hdr->fccName = name; + strncpy( (char*)_data[0]->data(), (const char*)label, _data[0]->size() ); +} + +//--------------------------------------------------------------------------------------------------------- + +WavFile::FileChunk::FileChunk( Chunk *parent, fourcc_t name, fourcc_t media, const void *file, uint32_t file_size ) : + LeafChunk( parent, sizeof(file_chunk_t), file_size, "file" ) +{ + file_chunk_t *hdr = (file_chunk_t*)_header.data(); + _min_size = sizeof(file_chunk_t); + + hdr->name = name; + hdr->media = media; + + memcpy( _data[0]->data(), file, file_size ); +} + +} // namespace smart diff --git a/smart/WavFile.h b/smart/WavFile.h new file mode 100644 index 0000000..8aa48ae --- /dev/null +++ b/smart/WavFile.h @@ -0,0 +1,548 @@ +/* + * WavFile.h + * + * Created on: Dec 16, 2015 + * Author: peeter + */ + +#pragma once + + +#include +#include +#include + +#include // std::string +#include +#include + +#include + +using ByteBuffer = std::vector; +using ByteBufferPtr = std::shared_ptr; + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4200) +#endif + +namespace smart { + +class WavFile +{ +public: + WavFile(){}; + ~WavFile(){}; +public: + + /// File buffer handling + class FileBuffer + { + public: + static std::shared_ptr make_shared( std::string fname ){ + auto rv = std::shared_ptr( new FileBuffer(fname) ); + if( rv->_file ) + return rv; + return nullptr; + }; + protected: + /// initialise file buffer + FileBuffer( std::string fname ){ _file = fopen( fname.c_str(), "r+b" ); }; + public: + virtual ~FileBuffer(){ if(_file) fclose(_file); }; + FILE *_file; + }; + +#pragma pack(push, 1) + /// the fourcharacter type + union fourcc_t + { + char asChr[4]; /// the field as 4 characters + uint32_t asU32; /// as 32 bit number + + fourcc_t( uint32_t init ){ asU32 = init; } + fourcc_t( int32_t init ){ asU32 = (uint32_t)init; } + fourcc_t( const char *init ); + }; + + struct chunk_header_t { + fourcc_t ckID; /// Chunk type identifier + uint32_t ckSize; /// Chunk size + }; + + /// the riff chunk declaration + struct chunk_t : chunk_header_t + { + uint8_t ckData[]; /// Chunk data follows + }; +#pragma pack(pop) + + /** + * class Chunk is the base building component of the Wav file + */ + class Chunk + { + public: + /** Create a new chunk with predefined buffer + * + * This is generic object made for easy creation of special instances + * + * arguments: + * parent - the chunk that contains this chunk + * header_size - the size of header of the new chunk + * ckID - the chunk ID, if the ckID is 0 while reading from file, then no ID check will be performed + */ + Chunk( Chunk *parent, uint32_t header_size = sizeof(chunk_t), fourcc_t ckID = 0 ); + + /** Create a new root chunk based on file data + * + * arguments: + * fname - filename to open + * header_size - the size of header of the root chunk + * ckID - the chunk ID, for verification of file + */ + Chunk( std::string fname, uint32_t header_size, fourcc_t ckID ); + + /** Return true if chunk is valid + * + */ + bool valid(){ chunk_t *hdr = (chunk_t*)_header.data(); return (hdr->ckID.asU32 != 0); }; + + + /** Get size of the chunk hierarchy + * + * return: + * total size of the chunk or 0 if empty + */ + virtual uint32_t getSize(); + + /** Get size of data part excluding the header + * + * return: + * total size of the chunk data or 0 if empty + */ + virtual uint32_t getDataSize(); + + /** Fill the buffer with chunk contents + * + * arguments: + * buf - the pointer to pointer of memory field to fill. The memory must be large enough for the process + * maxlen - reference to the value of the buffer remaining amount of bytes + * + * returns: + * buf value will be increased + * maxlen value will be decreased + * returns actual amount of bytes written, 0 on error + */ + virtual uint32_t fillBuffer( uint8_t **buf, uint32_t &maxlen ); + + /** Write the contents into the file. + * Note: the chunks with null pointers are ignored. + * + * \param fp - the file pointer for fwrite + * \return number of bytes written. + * increments the fp internals (with fwrite); + */ + virtual uint32_t writeFile( FILE *fp ); + + /** Get the pointer to "allocated" data field + * + * returns: + * 0 if no allocated data field + */ + virtual ByteBufferPtr getData( uint32_t i=0 ); + + /** add piece of externally managed data into list of data + * + * parameters: + * origin - pointer to the beginning of the buffer + * size - buffer length in bytes + */ + void addPiece( + uint8_t *origin, /// the origin of data + uint32_t size ); /// the size of data + + /** add piece of internally managed data into list of data + * + * parameters: + * size - buffer length in bytes + * + * returns: + * pointer to the allocated memory + */ + ByteBufferPtr addPiece( uint32_t size ); /// the size of data + + /** add piece of premade buffer + * + * parameters: + * buf - the buffer + */ + void addPiece( ByteBufferPtr buf ); + + /** Get number of contaned chunks + * + */ + uint32_t getContained() { return _contents.size(); }; + + /** Add a chunk as child to this chunk + * + * arguments: + * child - the chunk to add as child + */ + void setChild( Chunk *child ); + + /** Seek file to the end of chunk + * + */ + void seekFileEndOfChunk(); + + /** seek file to start of data of chunk + * + */ + void seekFileStartOfData( int64_t dataseek=0 ); + + /** seek file to start of chunk + * + */ + void seekFileStartOfChunk(); + + /** Check if the current file location is in range + * + */ + bool inFileRange(); + + /** Get the RIFF word-alignment pad byte size for this chunk. + * + * Returns 1 if ckSize is odd (pad byte needed), 0 otherwise. + * The pad byte is NOT included in ckSize but occupies space in the file. + */ + uint32_t getPadSize(); + + protected: + ByteBuffer _header; + std::vector< ByteBufferPtr > _data; + std::vector _contents; // list of child chunks managed elsewhere + uint32_t _min_size; /// minimum size of the chunk, consider it empty if less or this + // when reading wav file from disk: + std::shared_ptr _filebuf; // the file containing the data + int64_t _filepos; // position in file where this chunk starts if read from _filebuf + }; + + /** + * LeafChunk base class + * + * LeafChunk is Chunk that does not contain other chunks. + * It does have data field of some other type than Chunk. + * + * This class is used to easily implement leaf chunks + */ + class LeafChunk : public Chunk + { + public: + LeafChunk( Chunk *parent, uint32_t header_size, uint32_t data_size, fourcc_t ckID ); + }; + +#pragma pack(push, 1) + /// the wave format declaration + struct riff_chunk_t + { + chunk_header_t riff; /// the riff part + fourcc_t formType; /// the format identifier + uint8_t data[]; + }; +#pragma pack(pop) + + /** + * RIFF chunk + * + * note: usually the Riff chunk is not contained by another chunk, although it is possible + */ + class RiffChunk : public Chunk + { + public: + RiffChunk( fourcc_t formType ); + RiffChunk( std::string fname, fourcc_t formType ); + }; + +#pragma pack(push, 1) + /// the wave format chunk + struct wave_format_t + { + chunk_header_t fmt; /// the chunk part + uint16_t wFormatTag; /// format category + uint16_t wChannels; /// number of channels + uint32_t dwSamplesPerSec;/// sampling rate + uint32_t dwAvgBytesPerSec;/// for buffer estimateion + uint16_t wBlockAlign; /// Data block size + }; +#pragma pack(pop) + + /** + * basic wave format chunk, used for creation concrete chunk + */ + class WaveChunk : public Chunk + { + public: + WaveChunk( Chunk *parent, uint32_t header_size, + uint16_t channels, /// number of channels + uint32_t samples_per_sec, /// sample rate + uint16_t bytes_per_value /// how many bytes per value is needed + ); + WaveChunk( Chunk *parent ); + }; + +#pragma pack(push, 1) + /// the Pulse Code Modulation (PCM) format; wFormatTag = 1 + struct pcm_format_t + { + wave_format_t waveFmt; + uint16_t wBitsPerSample; // Sample size + }; +#pragma pack(pop) + + /** + * Pulse Code Modulation chunk + */ + class PcmChunk : public WaveChunk + { + public: + PcmChunk( Chunk *parent, + uint16_t channels, + uint32_t samples_per_sec, + uint16_t bits_per_sample + ); + PcmChunk( Chunk *parent ); + pcm_format_t *getPcmFormat() { return (pcm_format_t *)_header.data(); }; + }; + +#pragma pack(push, 1) + /// Wave data chunk + struct wave_data_chunk_t + { + /// The chunk part + /// The samples follow the header immediately. + chunk_header_t data; + }; +#pragma pack(pop) + + + /** + * PCM data chunk with one or multiple fraction + */ + class PcmDataChunk : public Chunk + { + public: + /// chunk owns the buffer of waveform + PcmDataChunk( Chunk *parent): Chunk( parent, sizeof(wave_data_chunk_t), "data" ), _row_length(0), _ratefactor(0), _nchannels(0) + { + setSampleFactor(); + } + + /// set samplerate reduction factor for saving time + /// important assumption is that the data is signed 16bits for value + void setSampleFactor( uint32_t factor = 1, uint32_t nchannels = 0){ + _ratefactor = factor > 0 ? ( factor <= 100000 ? factor : 100000 ) : 1 ; + _nchannels = nchannels; + } + + void setSampleWidth(unsigned int widthInBits); + + /** Write the file directly into file + * + * arguments: + * fp - the file pointer for fwrite + * + * returns: + * number of bytes written + * increments the fp internals (with fwrite); + */ + virtual uint32_t writeFile( FILE *fp ) override; + + /** Get size of data part excluding the header + * + * return: + * total size of the chunk data or 0 if empty + */ + virtual uint32_t getDataSize() override; + + /** sample iterator + * + * This is efficient way of traversing trhough the junked wav file data + */ + class SampleIterator{ + public: + enum{ FIX= 16 }; //fixed point for fraction + /** + * Important assumption: each buffer in data divides exactly with len + * + * chunk - the chunk where to iterate + * len - sample len in bytes + * index - starting index of the sample + * fraction - starting fraction of the sample index + */ + SampleIterator( PcmDataChunk *chunk, uint32_t len ); + /** get single sample from the data + * return: + * pointer to the sample of all channels, 0 if the sample does not exist + */ + virtual ByteBufferPtr getSample( uint32_t count = 1 ) = 0; + /** set iterator position + * return: + * pointer to this iterator + */ + virtual SampleIterator *setPos( uint32_t index, uint32_t fraction = 0 ) = 0; + virtual SampleIterator *nextPos( uint32_t index=1, uint32_t fraction = 0 ) = 0; + /** get single sample from the data and increase the iterator afterwards + * return: + * pointer to the sample of all channels, 0 if the sample does not exist + */ + virtual ByteBufferPtr getSampleInc( uint32_t count = 1, uint32_t index=1, uint32_t fraction = 0 ) = 0; + /** */ + virtual ~SampleIterator(){}; + protected: + /// Length of the sample + uint32_t _samplelen; + /// Chunk that contains the data + PcmDataChunk *_chunk; + /// Accumulated fraction, unity is (1<<16) + uint32_t _fraction; + }; + + friend class SampleIterator; + friend class SampleIteratorFile; + friend class SampleIteratorMemory; + + /** Get sample iterator + * + * len - sample size in bytes + * index - the sample index + * fraction - fractional index of sample + */ + std::shared_ptr getSampleIterator( uint32_t len, uint32_t index = 0, uint32_t fraction = 0 ); + + protected: + /// Length of the sample + uint32_t _row_length; + /// the factor of reducing the PCM samplerate + uint32_t _ratefactor; + /// number of channels in data + uint32_t _nchannels; + }; + + +#pragma pack(push, 1) + /// cue-Point + struct cue_point_t + { + fourcc_t dwName; /// unique name of cue point + uint32_t dwPosition; /// sample position of cue point + fourcc_t fccChunk; /// name of chunk containing the cue point + uint32_t dwChunkStart; /// start position of the chunk containing the cue point + uint32_t dwBlockStart; /// position of the start of the block containing the position + uint32_t dwSampleOffset; /// sample offset of the cue point from start of the block + }; +#pragma pack(pop) + +#pragma pack(push, 1) + /// cue-Points chunk + struct cue_chunk_t + { + chunk_header_t cue; /// the chunk part + uint32_t dwCuePoints;/// number of cue points + cue_point_t points[]; /// the array of cue points + }; +#pragma pack(pop) + + class CueChunk : public Chunk + { + public: + CueChunk( Chunk *parent ); + + void setPoint( fourcc_t name, + fourcc_t chunk_name, + uint32_t chunk_start, + uint32_t block_start, + uint32_t sample_offset + ); + + /// set point of single data chunk wav file + void setWavPoint( const char* name, + const char* chunk_name, + uint32_t sample_offset + ); + }; + +#pragma pack(push, 1) + /// associated data list + struct assoc_data_t + { + chunk_header_t assoc; /// the chunk part + fourcc_t fccAdtl; /// the 'adtl' + uint8_t data[]; /// the list items + }; +#pragma pack(pop) + + /** Associated data list + * + * It is used to embed different kind of chunks into the wav file header + */ + class AssocListChunk : public Chunk + { + public: + AssocListChunk( Chunk *parent ); + }; + + +#pragma pack(push, 1) + /// labl or note chunk + struct label_chunk_t + { + chunk_header_t labl; /// the chunk part + fourcc_t fccName; /// the cue point name + char str[]; /// the string 0 terminated + }; +#pragma pack(pop) + + /** Label + * + * Used for describing cue points in wav file header + */ + class LabelChunk : public LeafChunk + { + public: + LabelChunk( Chunk *parent, fourcc_t name, const char *label ); + /// constructor for initiating chunk when describing file on disk + LabelChunk( Chunk *parent ) : LeafChunk( parent, sizeof(label_chunk_t), 0, "labl" ){}; + label_chunk_t *getLabelChunkHeader(){ return (label_chunk_t*)_header.data(); }; + }; + +#pragma pack(push, 1) + /// file chunk + struct file_chunk_t + { + chunk_header_t file; /// the chunk part + fourcc_t name; /// the name of the file + fourcc_t media; /// the media type + char str[]; /// the string 0 terminated + }; +#pragma pack(pop) + + /** File + * + * Used for embedding any kind of files into the Wav file + */ + class FileChunk : public LeafChunk + { + public: + FileChunk( Chunk *parent, fourcc_t name, fourcc_t media, const void *file, uint32_t file_size ); + /// constructor for initiating chunk when describing file on disk + FileChunk( Chunk *parent ) : LeafChunk( parent, sizeof(file_chunk_t), 0, "file" ){}; + file_chunk_t *getFileChunkHeader(){ return (file_chunk_t*)_header.data(); }; + }; +}; + +} // namespace smart + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/smart/WavFileDisk.h b/smart/WavFileDisk.h new file mode 100644 index 0000000..649855e --- /dev/null +++ b/smart/WavFileDisk.h @@ -0,0 +1,156 @@ +/* + * WavFile.h + * + * Created on: Dec 16, 2015 + * Author: peeter + */ + +#pragma once + +#include + +#include "WavFile.h" + +namespace smart { + +class WavFileDiskPcm : protected WavFile +{ +public: + /** create simple wav file based on data on the disk file + * + * example: + * WavFileDiskPcm thepcm( "/path/to/the/filename.wav" ); + */ + WavFileDiskPcm( std::string filename ){ + _riffchunk = std::make_shared( filename, "WAVE" ); + do{ + if( test_create_chunk( _pcmchunk, &*_riffchunk ) ) + { + _pcmchunk->seekFileEndOfChunk(); + continue; + } + if( test_create_chunk( _cuechunk, &*_riffchunk ) ) + { + _cuechunk->seekFileEndOfChunk(); + continue; + } + if( test_create_chunk( _assocchunk, &*_riffchunk ) ) + { + + do + { + std::shared_ptr lchunk = nullptr; + if( test_create_chunk( lchunk, &*_assocchunk) ) + { + lchunk->seekFileEndOfChunk(); + label_chunk_t *hdr = lchunk->getLabelChunkHeader(); + std::string name( hdr->fccName.asChr, 4 ); + _labelchunks[name] = lchunk; + continue; + } + std::shared_ptr fchunk = nullptr; + if( test_create_chunk( fchunk, &*_assocchunk) ) + { + fchunk->seekFileEndOfChunk(); + file_chunk_t *hdr = fchunk->getFileChunkHeader(); + std::string name( hdr->name.asChr, 4 ); + _filechunks[name] = fchunk; + continue; + } + // file reading breaks if the file contains a chunk that is unknown, + // use then generic chunk for getting around this issue. + std::shared_ptr achunk = nullptr; + if( test_create_chunk( achunk, &*_assocchunk ) ) + { + achunk->seekFileEndOfChunk(); + continue; + } + break; + }while( _assocchunk->inFileRange() ); + + _assocchunk->seekFileEndOfChunk(); + continue; + } + if( test_create_chunk( _datachunk, &*_riffchunk ) ) + { + _datachunk->seekFileEndOfChunk(); + continue; + } + // file reading breaks if the file contains a chunk that is unknown, + // use then generic chunk for getting around this issue. + std::shared_ptr achunk = nullptr; + if( test_create_chunk( achunk, &*_riffchunk ) ) + { + achunk->seekFileEndOfChunk(); + continue; + } + break; + }while( _riffchunk->inFileRange() ); + } + + /** get pointer to associated file + * + * arguments: + * name- the cue point name + */ + ByteBufferPtr getAssocFile( std::string name ){ return _filechunks[name]->getData(); } + + /** get pointer to associated label + * + * arguments: + * name- the cue point name + */ + ByteBufferPtr getAssocLabel( std::string name ){ return _labelchunks[name]->getData(); } + + /** get bytes per sample */ + std::uint32_t getBytesPerSample(){ + return _pcmchunk->getPcmFormat()->waveFmt.wBlockAlign; + } + + /** get number of samples */ + std::uint32_t getSampleCount(){ + return _datachunk->getDataSize() / getBytesPerSample(); + } + + /** get number of channels */ + std::uint32_t getNumOfChannels(){ + return _pcmchunk->getPcmFormat()->waveFmt.wChannels; + } + + /** get sample rate */ + std::uint32_t getSampleRate(){ + return _pcmchunk->getPcmFormat()->waveFmt.dwSamplesPerSec; + } + + /** get iterator for traversion of wave data */ + std::shared_ptr getIterator(uint32_t index = 0){ + return _datachunk->getSampleIterator( getBytesPerSample(), index ); + } + + /// Do we have data? + bool hasData() const { return !!_datachunk; } +protected: + template + bool test_create_chunk( std::shared_ptr &result, Chunk *parent ) + { + if( result != nullptr ) + return false; + result = std::make_shared( parent ); + if( result->valid() ) + return true; + result = nullptr; + return false; + } + +protected: + std::shared_ptr _riffchunk; + std::shared_ptr _pcmchunk; + std::shared_ptr _cuechunk; + std::shared_ptr _assocchunk; + std::shared_ptr _datachunk; + std::map< std::string, std::shared_ptr > _labelchunks; + std::map< std::string, std::shared_ptr > _filechunks; +}; + +} // namespace smart + diff --git a/smart/WavFileSimple.h b/smart/WavFileSimple.h new file mode 100644 index 0000000..226e3b0 --- /dev/null +++ b/smart/WavFileSimple.h @@ -0,0 +1,99 @@ +/* + * WavFile.h + * + * Created on: Dec 16, 2015 + * Author: peeter + */ + +#pragma once + +#include "WavFile.h" + +namespace smart { + +class WavFileSimplePcm : protected WavFile +{ +public: + /** create simple wav file + * + * example: + * FILE *fp = fopen( "test.wav","wb" ); + * WavFileSimplePcm thepcm( 2, 44100, 16 ); + * uint32_t length = 2*2*44100; + * uint8_t *buf = thepcm.newData( length ); + * for( int i=0; i( &_assocchunk,name,description) ); + } + } + + /** add associated file into file + * + * arguments: + * name- the cue point name + * media- the media type + * file- pointer to the file + * file_size- size of the memory field and file + */ + void addAssocFile( const char *name, const char *media, const void *file, uint32_t file_size ){ _filechunks.push_back( std::make_shared( &_assocchunk, name, media, file, file_size ) );} + + /// allocate memory for data field + ByteBufferPtr newData( uint32_t data_size ){ return _datachunk.addPiece( data_size ); } + + /// Add externally managed fraction into data chunk. + /// Note: in the case of adding a null pointer, the user is responsible for writing the data chunk themselves. + /// \param data Pointer to the data, can be null. + /// \param size Size of the data, in bytes. + void addData( uint8_t *data, uint32_t data_size ){ _datachunk.addPiece( data, data_size );} + + /// get iterator for traversion of wave data + std::shared_ptr getIterator(uint32_t index = 0){ + return _datachunk.getSampleIterator( (uint32_t)_pcmchunk.getPcmFormat()->waveFmt.wBlockAlign, index ); + } + + /// write the wav file + uint32_t writeFile( FILE *fp ){ return _riffchunk.writeFile( fp ); } + + /// get number of samples in the file + uint32_t getNumOfSamples(){ + auto pcm = _pcmchunk.getPcmFormat(); + if( !pcm ) + return 0; + return _datachunk.getDataSize() / pcm->waveFmt.wBlockAlign; + } + +protected: + RiffChunk _riffchunk; + PcmChunk _pcmchunk; + CueChunk _cuechunk; + AssocListChunk _assocchunk; + PcmDataChunk _datachunk; + std::vector< std::shared_ptr > _labelchunks; + std::vector< std::shared_ptr > _filechunks; +}; + +} // namespace smart + diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md new file mode 100644 index 0000000..7dc838c --- /dev/null +++ b/tests/CLAUDE.md @@ -0,0 +1,183 @@ +# WAV File Test Suite + +## Background + +The WAV writer (`smart/WavFile.h`, `smart/WavFile.cpp`) was reviewed against +the RIFF and WAV specifications (Multimedia Programming Interface and Data +Specifications 1.0, Microsoft/IBM 1991). Seven problems were identified and +documented in `WAVFILE_PROBLEMS.md` at the repository root. This file describes +each problem, the fix applied, and how the test suite covers it. + +## Problems and Fixes + +### P1. Missing RIFF word-alignment pad bytes [HIGH] — Fixed + +**What was wrong:** The RIFF spec requires every chunk to start on a 2-byte +boundary. If a chunk's data has an odd byte count, a zero pad byte must follow. +Neither `fillBuffer()` nor `writeFile()` ever wrote pad bytes. + +**What broke:** Strict RIFF parsers could not locate chunks after an odd-sized +one (e.g., a `labl` chunk for string "ab" — 7 bytes of data). Tolerant parsers +like Audacity masked the problem. + +**Fix:** Added `Chunk::getPadSize()` (returns 1 when `ckSize` is odd). +Updated the compound-chunk branches of `getDataSize()`, `fillBuffer()`, and +`writeFile()` to account for and write pad bytes after each child chunk. The +pad byte is NOT included in `ckSize`, per spec. + +### P2. LeafChunk inflated ckSize with 4-byte alignment [MEDIUM] — Fixed + +**What was wrong:** The `LeafChunk` constructor rounded the data allocation up +to a 4-byte boundary. The extra bytes became part of the data buffer and were +counted in `ckSize`. A label "ab" would have `ckSize=8` instead of `ckSize=7`. + +**What broke:** Readers trusting `ckSize` would interpret the padding bytes as +real content (e.g., a garbage byte appended to a label string). + +**Fix:** Removed the 4-byte rounding from the `LeafChunk` constructor. +`malloc()` already returns suitably aligned memory, so the original alignment +trap concern does not apply to the logical data size. + +### P3. Rate-reduced writeFile produced ckSize/data mismatch [MEDIUM] — Fixed + +**What was wrong:** When `_ratefactor > 1`, `PcmDataChunk::getDataSize()` used +floor division (`raw / _ratefactor`), but the write loop produces +`ceil(total_samples / _ratefactor)` output samples. When the sample count was +not divisible by the rate factor, the header claimed a different size than what +was actually written. + +**What broke:** The `data` chunk header could be off by a few bytes. The RIFF +container size would also be wrong, producing a corrupt WAV. + +**Fix:** Changed `getDataSize()` to use ceiling division: +`output_samples = (total_samples - 1) / _ratefactor + 1`, matching the write +loop's actual output. + +### P4. Row-length truncation vs. actual bytes written [MEDIUM] — Fixed + +**What was wrong:** When `_row_length > 0` and `_ratefactor == 1`, +`getDataSize()` truncated the result to a multiple of `_row_length`. But +`writeFile()` delegated to `Chunk::writeFile()`, which wrote the full +untruncated data buffers. Result: more bytes written than `ckSize` claimed. + +**What broke:** `ckSize` was smaller than the actual data on disk. Readers +would stop early, losing trailing samples. The RIFF container size would also +be wrong. + +**Fix:** `PcmDataChunk::writeFile()` now detects when `getDataSize()` is +smaller than `Chunk::getDataSize()` and limits the bytes written accordingly. + +### P5. dwPosition used as sequential index [LOW] — Fixed + +**What was wrong:** `CueChunk::setPoint()` set `dwPosition` to the cue point's +sequential index (0, 1, 2, ...) instead of the sample position. + +**What broke:** Per spec, `dwPosition` should be the sample offset in the play +order. Most readers ignore this field when `dwSampleOffset` is present, so the +practical impact was minimal. + +**Fix:** Changed `point->dwPosition = hdr->dwCuePoints` to +`point->dwPosition = sample_offset`. + +### P6. No RF64 / large file support [LOW] — Not fixed + +All size fields are `uint32_t`, so files exceeding ~4 GB will silently produce +corrupt headers. This is a larger architectural change and is not addressed by +the current test suite. + +### P7. Non-standard chunk ordering [LOW] — Not a bug + +`WavFileSimple.h` produces chunks in order: `fmt`, `cue`, `LIST(adtl)`, +`data`. The spec requires `fmt` before `data` (satisfied). Some strict readers +prefer `data` immediately after `fmt`, but this ordering was never a spec +violation. The writer was never changed for P7. + +The verifier does check for the real spec violation (data before fmt → +`P7_FMT_AFTER_DATA`), but this is defensive — the library never produced files +with this problem. The test blob `fault_p7.wav` is a synthetic hand-crafted +file that demonstrates this detection. + +## Reader Fixes + +After the P1/P2 writer fixes, files produced by the library can have odd +`ckSize` values (previously masked by 4-byte rounding). The disk reader +(`WavFileDiskPcm`) was updated so that `seekFileEndOfChunk()` and +`inFileRange()` skip the pad byte when `ckSize` is odd. This is +backward-compatible: files from the old writer always had even `ckSize` values, +so the pad-byte logic never activates for them. + +## Test Structure + +### test_wavfile.cpp — Round-trip tests + +These tests write WAV files using the library, then verify the output with +`wav_verify` (structural validator) and/or `WavFileDiskPcm` (read-back). + +| Test | What it covers | +|------|---------------| +| `WavFile fillBuffer creates valid WAV in memory` | Basic in-memory WAV creation | +| `WavFile writeFile creates file on disk` | Basic file output | +| `WavFileSimplePcm write and read back` | Cue points, labels, associated files, read-back | +| `WavFileSimplePcm with cue points and associated data` | Cue read-back, label read-back, assoc file read-back | +| `Round-trip: write then read back verifies sample integrity` | Sample-level data verification | +| `wav_verify: in-memory buffer from fillBuffer` | Structural validation of fillBuffer output | +| `wav_verify_file: file written with writeFile` | Structural validation of writeFile output | +| `wav_verify: WavFileSimplePcm with cue, labels, files` | Structure + **P5 round-trip** (`CHECK_FALSE(P5_SEQ_POSITION)`) | +| `wav_verify: detect P1/P2 issues with odd-length labels` | **P1/P2 round-trip**: writer with odd-length label "ab" produces clean output | +| `wav_verify: ratefactor>1 round-trip (P3)` | **P3 round-trip**: 1001 samples with ratefactor=3, verifies `data_ck_size == 1336` and valid structure | +| `wav_verify: row_length truncation round-trip (P4)` | **P4 round-trip**: 24-bit mono with 13 bytes (not a multiple of `_row_length`=4), verifies `data_ck_size == 12` | +| `wav_verify: 24-bit 3-channel odd-frame round-trip` | 24-bit 3-channel format with `block_align=9`, sample-level read-back | + +### test_wav_faults.cpp — Fault detection tests + +These tests use hand-crafted binary WAV blobs to verify that `wav_verify` +correctly detects each class of problem. The blobs are built at runtime using +byte-level helpers, and also stored as binary files in `tests/data/`. + +| Test | Fault injected | Expected detection | +|------|---------------|-------------------| +| `baseline: helpers produce valid WAVs` | None | `valid == true` | +| `P1: missing pad byte after odd-sized chunk` | Odd-sized LIST with no pad byte | `P1_NO_PAD` | +| `P2: ckSize inflated by 4-byte alignment padding` | labl `ckSize=8` instead of 7 | `P2_PADDED_CKSIZE` | +| `P3: data ckSize smaller than actual payload` | data `ckSize=333` (not a multiple of `block_align=4`) | `P3_DATA_NOT_BLOCK_ALIGNED` | +| `P4: data ckSize larger than actual payload` | data `ckSize=500` overflows RIFF | `CHUNK_OVERFLOW` | +| `P5: dwPosition set to sequential index` | `dwPosition=0,1` but `dwSampleOffset=100,500` | `P5_SEQ_POSITION` | +| `P7: non-standard chunk ordering (cue before fmt)` | cue before fmt, but fmt still before data | No `P7_FMT_AFTER_DATA` (not a violation) | +| `P7b: data chunk before fmt chunk` | data chunk appears before fmt | `P7_FMT_AFTER_DATA` | +| `stored blobs: verify fault detection` | Loads `tests/data/fault_p*.wav` | Same tags as runtime tests | + +### tests/data/ — Stored fault blobs + +Pre-built binary WAV files with known faults, used by the stored-blob test +section. Each file is under 5 KB. + +| File | Fault | +|------|-------| +| `fault_p1.wav` | Odd-sized labl chunk with no pad byte | +| `fault_p2.wav` | labl `ckSize` inflated to 4-byte boundary | +| `fault_p3.wav` | data `ckSize` not a multiple of `block_align` | +| `fault_p5.wav` | `dwPosition` is sequential index, not sample offset | +| `fault_p7.wav` | data chunk appears before fmt chunk | + +### wav_verify.h — Structural validator + +Header-only WAV verifier. Parses RIFF/WAVE structure and reports issues. + +| Tag | Level | Meaning | +|-----|-------|---------| +| `RIFF_SIZE_MISMATCH` | error | `riff_ck_size + 8` does not match buffer length | +| `CHUNK_OVERFLOW` | error | A chunk extends past the RIFF payload end | +| `MISSING_FMT` | error | No `fmt` chunk found | +| `MISSING_DATA` | error | No `data` chunk found | +| `FMT_TOO_SHORT` | error | `fmt` chunk smaller than 16 bytes | +| `BAD_BLOCK_ALIGN` | error | `blockAlign` does not match `channels * bytesPerSample` | +| `BAD_AVG_BYTES` | error | `avgBytesPerSec` does not match `samplesPerSec * blockAlign` | +| `LIST_SUBCHUNK_OVERFLOW` | error | Sub-chunk within LIST overflows LIST payload | +| `CUE_COUNT_MISMATCH` | warning | Declared cue point count does not match what fits | +| `P1_NO_PAD` | warning | Odd-sized chunk missing its pad byte | +| `P2_PADDED_CKSIZE` | warning | labl `ckSize` includes zero-padding beyond the null-terminated string | +| `P3_DATA_NOT_BLOCK_ALIGNED` | warning | data `ckSize` is not a multiple of `blockAlign` | +| `P5_SEQ_POSITION` | info | `dwPosition` differs from `dwSampleOffset` for a data-chunk cue point | +| `P7_FMT_AFTER_DATA` | warning | data chunk appears before fmt chunk | +| `FILE_OPEN_FAILED` | error | Cannot open file (file wrapper only) | +| `FILE_EMPTY` | error | File is empty or unreadable (file wrapper only) | diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 470d554..03b9a39 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,9 +3,14 @@ add_executable(test_smart test_path.cpp test_circular_buffer.cpp test_wav_format.cpp + test_wavfile.cpp + test_wav_faults.cpp ) target_link_libraries(test_smart PRIVATE smart crack crypt Catch2::Catch2WithMain) target_include_directories(test_smart PRIVATE ${CMAKE_SOURCE_DIR}) +target_compile_definitions(test_smart PRIVATE + TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) include(CTest) include(Catch) diff --git a/tests/data/fault_p1.wav b/tests/data/fault_p1.wav new file mode 100644 index 0000000..ee81d5f Binary files /dev/null and b/tests/data/fault_p1.wav differ diff --git a/tests/data/fault_p2.wav b/tests/data/fault_p2.wav new file mode 100644 index 0000000..b0ccb81 Binary files /dev/null and b/tests/data/fault_p2.wav differ diff --git a/tests/data/fault_p3.wav b/tests/data/fault_p3.wav new file mode 100644 index 0000000..a4188e3 Binary files /dev/null and b/tests/data/fault_p3.wav differ diff --git a/tests/data/fault_p5.wav b/tests/data/fault_p5.wav new file mode 100644 index 0000000..1ea2493 Binary files /dev/null and b/tests/data/fault_p5.wav differ diff --git a/tests/data/fault_p7.wav b/tests/data/fault_p7.wav new file mode 100644 index 0000000..5135a7b Binary files /dev/null and b/tests/data/fault_p7.wav differ diff --git a/tests/test_wav_faults.cpp b/tests/test_wav_faults.cpp new file mode 100644 index 0000000..943a9d9 --- /dev/null +++ b/tests/test_wav_faults.cpp @@ -0,0 +1,454 @@ +#include +#include "wav_verify.h" + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Byte-level write helpers (little-endian) +// --------------------------------------------------------------------------- + +static void put_u32_le(std::vector& buf, size_t off, uint32_t v) { + buf[off+0] = static_cast(v); + buf[off+1] = static_cast(v >> 8); + buf[off+2] = static_cast(v >> 16); + buf[off+3] = static_cast(v >> 24); +} + +static void push_u16(std::vector& b, uint16_t v) { + b.push_back(static_cast(v)); + b.push_back(static_cast(v >> 8)); +} + +static void push_u32(std::vector& b, uint32_t v) { + b.push_back(static_cast(v)); + b.push_back(static_cast(v >> 8)); + b.push_back(static_cast(v >> 16)); + b.push_back(static_cast(v >> 24)); +} + +static void push_cc(std::vector& b, const char* cc) { + for (int i = 0; i < 4; i++) + b.push_back(static_cast(cc[i])); +} + +static void push_zeros(std::vector& b, size_t n) { + b.insert(b.end(), n, 0); +} + +static void push_str(std::vector& b, const std::string& s) { + for (char c : s) + b.push_back(static_cast(c)); + b.push_back(0); +} + +static void dump_file(const char* path, const std::vector& buf) { + FILE* f = fopen(path, "wb"); + if (f) { fwrite(buf.data(), 1, buf.size(), f); fclose(f); } +} + +static void fix_riff_size(std::vector& w) { + put_u32_le(w, 4, static_cast(w.size() - 8)); +} + +// --------------------------------------------------------------------------- +// Reusable chunk builders +// --------------------------------------------------------------------------- + +static void push_fmt_chunk(std::vector& b) { + push_cc(b, "fmt "); + push_u32(b, 16); + push_u16(b, 1); // PCM + push_u16(b, 2); // channels + push_u32(b, 44100); // sample rate + push_u32(b, 176400); // avg bytes/sec + push_u16(b, 4); // block align + push_u16(b, 16); // bits per sample +} + +struct CuePointDef { + uint32_t id; + uint32_t position; + uint32_t sample_offset; +}; + +static void push_cue_chunk(std::vector& b, + const std::vector& pts) { + uint32_t n = static_cast(pts.size()); + push_cc(b, "cue "); + push_u32(b, 4 + n * 24); + push_u32(b, n); + for (auto& p : pts) { + push_u32(b, p.id); + push_u32(b, p.position); + push_cc(b, "data"); + push_u32(b, 0); + push_u32(b, 0); + push_u32(b, p.sample_offset); + } +} + +// --------------------------------------------------------------------------- +// build_minimal_wav: RIFF + fmt + data (stereo 16-bit 44100, 100 samples) +// Total: 12 + 24 + 408 = 444 bytes. data ckSize field is at offset 40. +// --------------------------------------------------------------------------- + +static std::vector build_minimal_wav() { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + push_fmt_chunk(w); + push_cc(w, "data"); + push_u32(w, 400); + push_zeros(w, 400); + fix_riff_size(w); + return w; +} + +// --------------------------------------------------------------------------- +// build_wav_with_cue_and_label: RIFF + fmt + cue(1) + LIST/adtl(1 labl) + data +// Correctly word-aligned. +// --------------------------------------------------------------------------- + +static std::vector build_wav_with_cue_and_label(const std::string& label) { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + push_fmt_chunk(w); + push_cue_chunk(w, {{1, 0, 0}}); + + uint32_t labl_data = 4 + static_cast(label.size()) + 1; + uint32_t labl_padded = labl_data + (labl_data & 1); + uint32_t list_data = 4 + 8 + labl_padded; + + push_cc(w, "LIST"); + push_u32(w, list_data); + push_cc(w, "adtl"); + push_cc(w, "labl"); + push_u32(w, labl_data); + push_u32(w, 1); + push_str(w, label); + if (labl_data & 1) + w.push_back(0); + + if (list_data & 1) + w.push_back(0); + + push_cc(w, "data"); + push_u32(w, 400); + push_zeros(w, 400); + fix_riff_size(w); + return w; +} + +// =========================================================================== +// Baseline: verify helpers produce valid WAVs +// =========================================================================== + +TEST_CASE("baseline: helpers produce valid WAVs", "[wav-faults]") { + SECTION("minimal wav") { + auto w = build_minimal_wav(); + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + CHECK(r.valid); + CHECK(r.has_fmt); + CHECK(r.has_data); + CHECK(r.data_ck_size == 400); + } + + SECTION("wav with cue and even-length label") { + auto w = build_wav_with_cue_and_label("tes"); + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + CHECK(r.valid); + CHECK(r.has_cue); + CHECK(r.has_list_adtl); + } + + SECTION("wav with cue and odd-length label") { + auto w = build_wav_with_cue_and_label("ab"); + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + CHECK(r.valid); + } +} + +// =========================================================================== +// P1: Missing pad byte after odd-sized chunk +// =========================================================================== + +TEST_CASE("P1: missing pad byte after odd-sized chunk", "[wav-faults]") { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + push_fmt_chunk(w); + push_cue_chunk(w, {{1, 0, 0}}); + + // LIST/adtl with labl "ab": data = dwName(4) + "ab\0"(3) = 7 bytes (odd) + // No pad byte after labl, no pad byte after LIST. + // LIST ckSize = "adtl"(4) + "labl"(4) + ckSize(4) + data(7) = 19 (odd) + push_cc(w, "LIST"); + push_u32(w, 19); + push_cc(w, "adtl"); + push_cc(w, "labl"); + push_u32(w, 7); + push_u32(w, 1); + w.push_back('a'); + w.push_back('b'); + w.push_back(0); + // NO pad bytes — this is the fault + + push_cc(w, "data"); + push_u32(w, 400); + push_zeros(w, 400); + fix_riff_size(w); + + dump_file("/tmp/fault_p1.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + // wav_verify sees LIST with odd ckSize=19, finds 'd' (0x64) where pad + // byte should be → reports P1_NO_PAD + CHECK(r.has_issue_tagged("P1_NO_PAD")); +} + +// =========================================================================== +// P2: ckSize inflated by 4-byte alignment padding +// =========================================================================== + +TEST_CASE("P2: ckSize inflated by 4-byte alignment padding", "[wav-faults]") { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + push_fmt_chunk(w); + push_cue_chunk(w, {{1, 0, 0}}); + + // labl true data: dwName(4) + "ab\0"(3) = 7 bytes + // Inflated ckSize = 8 (4-byte aligned), extra byte is 0x00 + // ckSize 8 is even → no word-alignment pad needed after labl + push_cc(w, "LIST"); + push_u32(w, 20); // "adtl"(4) + labl sub-chunk(4+4+8 = 16) + push_cc(w, "adtl"); + push_cc(w, "labl"); + push_u32(w, 8); // inflated ckSize (true size is 7) + push_u32(w, 1); + w.push_back('a'); + w.push_back('b'); + w.push_back(0); + w.push_back(0); // padding byte counted in ckSize — this is the fault + + push_cc(w, "data"); + push_u32(w, 400); + push_zeros(w, 400); + fix_riff_size(w); + + dump_file("/tmp/fault_p2.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + CHECK(r.has_issue_tagged("P2_PADDED_CKSIZE")); +} + +// =========================================================================== +// P3: data ckSize smaller than actual payload +// =========================================================================== + +TEST_CASE("P3: data ckSize smaller than actual payload", "[wav-faults]") { + auto w = build_minimal_wav(); + + // data ckSize at offset 40: change 400 → 333 (simulates integer division) + // RIFF ckSize stays correct for actual file size (444 bytes) + put_u32_le(w, 40, 333); + + dump_file("/tmp/fault_p3.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + CHECK(r.has_data); + CHECK(r.data_ck_size == 333); + // data ckSize=333 is not a multiple of blockAlign=4 + CHECK(r.has_issue_tagged("P3_DATA_NOT_BLOCK_ALIGNED")); +} + +// =========================================================================== +// P4: data ckSize larger than actual payload +// =========================================================================== + +TEST_CASE("P4: data ckSize larger than actual payload", "[wav-faults]") { + auto w = build_minimal_wav(); + + // data ckSize at offset 40: change 400 → 500 (claims more than exists) + // RIFF ckSize unchanged → data extends past RIFF payload end + put_u32_le(w, 40, 500); + + dump_file("/tmp/fault_p4.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + // wav_verify detects this: the data chunk overflows the RIFF payload + CHECK(r.has_issue_tagged("CHUNK_OVERFLOW")); +} + +// =========================================================================== +// P5: dwPosition set to sequential index instead of sample offset +// =========================================================================== + +TEST_CASE("P5: dwPosition set to sequential index instead of sample offset", "[wav-faults]") { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + push_fmt_chunk(w); + + // 2 cue points: dwPosition = 0,1 (sequential index) + // but dwSampleOffset = 100,500 (actual sample positions) + push_cue_chunk(w, { + {1, 0, 100}, + {2, 1, 500}, + }); + + push_cc(w, "data"); + push_u32(w, 4000); + push_zeros(w, 4000); + fix_riff_size(w); + + dump_file("/tmp/fault_p5.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + CHECK(r.has_cue); + CHECK(r.cue_points_declared == 2); + CHECK(r.has_issue_tagged("P5_SEQ_POSITION")); +} + +// =========================================================================== +// P7: Non-standard chunk ordering (cue before fmt) +// =========================================================================== + +TEST_CASE("P7: non-standard chunk ordering (cue before fmt)", "[wav-faults]") { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + + // Non-standard order: cue → LIST/adtl → fmt → data + push_cue_chunk(w, {{1, 0, 0}}); + + // labl with "tes" → data = dwName(4) + "tes\0"(4) = 8 bytes (even) + push_cc(w, "LIST"); + push_u32(w, 20); + push_cc(w, "adtl"); + push_cc(w, "labl"); + push_u32(w, 8); + push_u32(w, 1); + w.push_back('t'); + w.push_back('e'); + w.push_back('s'); + w.push_back(0); + + push_fmt_chunk(w); + + push_cc(w, "data"); + push_u32(w, 400); + push_zeros(w, 400); + fix_riff_size(w); + + dump_file("/tmp/fault_p7.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + CHECK(r.has_fmt); + CHECK(r.has_data); + CHECK(r.has_cue); + CHECK(r.has_list_adtl); + // File is structurally valid — fmt is still before data in this test + CHECK(r.valid); + // fmt IS before data here, so P7 should not fire + CHECK_FALSE(r.has_issue_tagged("P7_FMT_AFTER_DATA")); +} + +// =========================================================================== +// P7b: data chunk before fmt chunk (spec violation) +// =========================================================================== + +TEST_CASE("P7b: data chunk before fmt chunk", "[wav-faults]") { + std::vector w; + push_cc(w, "RIFF"); + push_u32(w, 0); + push_cc(w, "WAVE"); + + // Spec-violating order: cue → data → fmt + push_cue_chunk(w, {{1, 0, 0}}); + + push_cc(w, "data"); + push_u32(w, 400); + push_zeros(w, 400); + + push_fmt_chunk(w); + fix_riff_size(w); + + dump_file("/tmp/fault_p7b.wav", w); + + auto r = wav_verify(w.data(), w.size()); + INFO(r.summary()); + + CHECK(r.has_fmt); + CHECK(r.has_data); + CHECK(r.has_issue_tagged("P7_FMT_AFTER_DATA")); +} + +// =========================================================================== +// Stored-blob tests: load pre-built fault WAVs from tests/data/ +// =========================================================================== + +#ifndef TEST_DATA_DIR +#define TEST_DATA_DIR "." +#endif + +static std::string data_path(const char* name) { + return std::string(TEST_DATA_DIR) + "/" + name; +} + +TEST_CASE("stored blobs: verify fault detection", "[wav-faults][stored]") { + SECTION("fault_p1.wav — missing pad byte") { + auto r = wav_verify_file(data_path("fault_p1.wav")); + INFO(r.summary()); + CHECK(r.has_issue_tagged("P1_NO_PAD")); + } + + SECTION("fault_p2.wav — ckSize inflated by padding") { + auto r = wav_verify_file(data_path("fault_p2.wav")); + INFO(r.summary()); + CHECK(r.has_issue_tagged("P2_PADDED_CKSIZE")); + } + + SECTION("fault_p3.wav — data ckSize not block-aligned") { + auto r = wav_verify_file(data_path("fault_p3.wav")); + INFO(r.summary()); + CHECK(r.has_issue_tagged("P3_DATA_NOT_BLOCK_ALIGNED")); + } + + SECTION("fault_p5.wav — dwPosition != dwSampleOffset") { + auto r = wav_verify_file(data_path("fault_p5.wav")); + INFO(r.summary()); + CHECK(r.has_issue_tagged("P5_SEQ_POSITION")); + } + + SECTION("fault_p7.wav — data before fmt") { + auto r = wav_verify_file(data_path("fault_p7.wav")); + INFO(r.summary()); + CHECK(r.has_issue_tagged("P7_FMT_AFTER_DATA")); + } +} diff --git a/tests/test_wav_format.cpp b/tests/test_wav_format.cpp index 5178ce1..bbd6467 100644 --- a/tests/test_wav_format.cpp +++ b/tests/test_wav_format.cpp @@ -1,20 +1,11 @@ #include #include +#include "wav_verify.h" #include #include #include -// Helper to read little-endian uint32 from buffer -static uint32_t read_u32_le(const uint8_t* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - -// Helper to read little-endian uint16 from buffer -static uint16_t read_u16_le(const uint8_t* buf) { - return buf[0] | (buf[1] << 8); -} - TEST_CASE("makeHeader structure verification", "[wav]") { std::vector header; smart::WavFormat::makeHeader(header, 2, 16, 44100, 1000); diff --git a/tests/test_wavfile.cpp b/tests/test_wavfile.cpp new file mode 100644 index 0000000..4163708 --- /dev/null +++ b/tests/test_wavfile.cpp @@ -0,0 +1,546 @@ +#include +#include +#include +#include "wav_verify.h" + +#include +#include +#include +#include + +static const char* test_wav_path = "/tmp/test_wavfile.wav"; + +// 24-bit LE helpers for the 3-channel round-trip test +static void write_i24_le(uint8_t* dst, int32_t val) +{ + dst[0] = static_cast(val & 0xFF); + dst[1] = static_cast((val >> 8) & 0xFF); + dst[2] = static_cast((val >> 16) & 0xFF); +} + +static int32_t read_i24_le(const uint8_t* src) +{ + uint32_t raw = static_cast(src[0]) + | (static_cast(src[1]) << 8) + | (static_cast(src[2]) << 16); + // sign-extend from 24-bit + if (raw & 0x800000) + raw |= 0xFF000000; + return static_cast(raw); +} + +#pragma pack(push, 1) +struct sample_stereo_16_t { + int16_t ch0; + int16_t ch1; +}; +#pragma pack(pop) + +// Helper: fill a buffer with a stereo sawtooth pattern +static void fill_sawtooth(uint8_t* buf, uint32_t num_samples) +{ + int16_t sig0 = 0; + int16_t sig1 = 0; + for (uint32_t i = 0; i < num_samples; i++) { + auto sample = reinterpret_cast(buf + i * sizeof(sample_stereo_16_t)); + sig0 += 65; + sig1 -= 65; + sample->ch0 = sig0; + sample->ch1 = sig1; + } +} + +TEST_CASE("WavFile fillBuffer creates valid WAV in memory", "[wavfile]") { + const uint32_t num_samples = 1000; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + smart::WavFile::RiffChunk riffchunk("WAVE"); + smart::WavFile::PcmChunk pcmchunk(&riffchunk, 2, 44100, 16); + smart::WavFile::CueChunk cuechunk(&riffchunk); + cuechunk.setWavPoint("CNFG", "data", 0); + cuechunk.setWavPoint("TRIG", "data", 500); + + smart::WavFile::AssocListChunk listchunk(&riffchunk); + smart::WavFile::LabelChunk triglabel(&listchunk, "TRIG", "Test trigger."); + const char* filedata = "key=value\n"; + smart::WavFile::FileChunk configfile(&listchunk, "CNFG", "TXT", filedata, strlen(filedata) + 1); + + smart::WavFile::PcmDataChunk pcmdata(&riffchunk); + pcmdata.addPiece(sound_data, data_bytes); + + uint32_t maxlen = riffchunk.getSize(); + REQUIRE(maxlen > 0); + + std::vector file_buf(maxlen); + uint8_t* ptr = file_buf.data(); + uint32_t written = riffchunk.fillBuffer(&ptr, maxlen); + + REQUIRE(written > 0); + REQUIRE(file_buf[0] == 'R'); + REQUIRE(file_buf[1] == 'I'); + REQUIRE(file_buf[2] == 'F'); + REQUIRE(file_buf[3] == 'F'); +} + +TEST_CASE("WavFile writeFile creates file on disk", "[wavfile]") { + const uint32_t num_samples = 1000; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + smart::WavFile::RiffChunk riffchunk("WAVE"); + smart::WavFile::PcmChunk pcmchunk(&riffchunk, 2, 44100, 16); + smart::WavFile::PcmDataChunk pcmdata(&riffchunk); + pcmdata.addPiece(sound_data, data_bytes); + + FILE* f = fopen(test_wav_path, "wb"); + REQUIRE(f != nullptr); + uint32_t written = riffchunk.writeFile(f); + fclose(f); + + REQUIRE(written > 0); + + // Verify the file starts with RIFF + f = fopen(test_wav_path, "rb"); + REQUIRE(f != nullptr); + char magic[4]; + fread(magic, 1, 4, f); + fclose(f); + REQUIRE(memcmp(magic, "RIFF", 4) == 0); + + std::remove(test_wav_path); +} + +TEST_CASE("WavFileSimplePcm write and read back", "[wavfile]") { + const uint32_t num_samples = 1000; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + // Write via WavFileSimplePcm + { + smart::WavFileSimplePcm simple(2, 44100, 16); + simple.addData(sound_data, data_bytes); + simple.addCuePoint("MARK", 100, "Test marker"); + + FILE* f = fopen(test_wav_path, "wb"); + REQUIRE(f != nullptr); + uint32_t written = simple.writeFile(f); + fclose(f); + REQUIRE(written > 0); + } + + // Read back via WavFileDiskPcm + { + smart::WavFileDiskPcm reader(test_wav_path); + REQUIRE(reader.hasData()); + REQUIRE(reader.getNumOfChannels() == 2); + REQUIRE(reader.getSampleRate() == 44100); + REQUIRE(reader.getBytesPerSample() == 4); // 2 channels * 2 bytes + REQUIRE(reader.getSampleCount() == num_samples); + } + + std::remove(test_wav_path); +} + +TEST_CASE("WavFileSimplePcm with cue points and associated data", "[wavfile]") { + const uint32_t num_samples = 500; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + const char* filedata = "property1=hello\nproperty2=world\n"; + + // Write + { + smart::WavFileSimplePcm simple(2, 44100, 16); + simple.addData(sound_data, data_bytes); + simple.addCuePoint("CNFG", 0); + simple.addCuePoint("TRIG", 250, "Trigger point"); + simple.addAssocFile("CNFG", "TXT", filedata, strlen(filedata) + 1); + + FILE* f = fopen(test_wav_path, "wb"); + REQUIRE(f != nullptr); + simple.writeFile(f); + fclose(f); + } + + // Read back and verify + { + smart::WavFileDiskPcm reader(test_wav_path); + REQUIRE(reader.hasData()); + REQUIRE(reader.getSampleCount() == num_samples); + + // Verify associated file data + auto assoc = reader.getAssocFile("CNFG"); + REQUIRE(assoc->size() > 0); + REQUIRE(memcmp(assoc->data(), filedata, strlen(filedata)) == 0); + + // Verify label + auto label = reader.getAssocLabel("TRIG"); + REQUIRE(label->size() > 0); + REQUIRE(strncmp((const char*)label->data(), "Trigger point", 13) == 0); + } + + std::remove(test_wav_path); +} + +TEST_CASE("Round-trip: write then read back verifies sample integrity", "[wavfile]") { + const uint32_t num_samples = 256; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + // Write + { + smart::WavFileSimplePcm simple(2, 44100, 16); + simple.addData(sound_data, data_bytes); + + FILE* f = fopen(test_wav_path, "wb"); + REQUIRE(f != nullptr); + simple.writeFile(f); + fclose(f); + } + + // Read back and verify sample data + { + smart::WavFileDiskPcm reader(test_wav_path); + REQUIRE(reader.hasData()); + + auto it = reader.getIterator(0); + REQUIRE(it != nullptr); + + uint32_t bytes_per_sample = reader.getBytesPerSample(); + auto first_sample = it->getSample(1); + REQUIRE(first_sample->size() == bytes_per_sample); + + // First sample should match what we wrote + auto* s = reinterpret_cast(first_sample->data()); + REQUIRE(s->ch0 == 65); + REQUIRE(s->ch1 == -65); + } + + std::remove(test_wav_path); +} + +// =========================================================================== +// wav_verify tests +// =========================================================================== + +TEST_CASE("wav_verify: in-memory buffer from fillBuffer", "[wavfile][verify]") { + const uint32_t num_samples = 1000; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + smart::WavFile::RiffChunk riffchunk("WAVE"); + smart::WavFile::PcmChunk pcmchunk(&riffchunk, 2, 44100, 16); + smart::WavFile::PcmDataChunk pcmdata(&riffchunk); + pcmdata.addPiece(sound_data, data_bytes); + + uint32_t maxlen = riffchunk.getSize(); + std::vector file_buf(maxlen); + uint8_t* ptr = file_buf.data(); + uint32_t written = riffchunk.fillBuffer(&ptr, maxlen); + REQUIRE(written > 0); + + auto r = wav_verify(file_buf.data(), written); + INFO(r.summary()); + + // RIFF structure + REQUIRE(r.has_riff); + REQUIRE(r.has_wave_form); + + // fmt fields + REQUIRE(r.has_fmt); + REQUIRE(r.format_tag == 1); // PCM + REQUIRE(r.channels == 2); + REQUIRE(r.samples_per_sec == 44100); + REQUIRE(r.bits_per_sample == 16); + REQUIRE(r.block_align == 4); // 2 channels * 2 bytes + + // data chunk + REQUIRE(r.has_data); + REQUIRE(r.data_ck_size == data_bytes); + + // RIFF size consistency + REQUIRE(r.riff_ck_size + 8 == written); +} + +TEST_CASE("wav_verify_file: file written with writeFile", "[wavfile][verify]") { + const uint32_t num_samples = 1000; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + smart::WavFile::RiffChunk riffchunk("WAVE"); + smart::WavFile::PcmChunk pcmchunk(&riffchunk, 2, 44100, 16); + smart::WavFile::PcmDataChunk pcmdata(&riffchunk); + pcmdata.addPiece(sound_data, data_bytes); + + const char* path = "/tmp/test_wavfile_verify.wav"; + FILE* f = fopen(path, "wb"); + REQUIRE(f != nullptr); + uint32_t written = riffchunk.writeFile(f); + fclose(f); + REQUIRE(written > 0); + + auto r = wav_verify_file(path); + INFO(r.summary()); + + REQUIRE(r.has_riff); + REQUIRE(r.has_wave_form); + REQUIRE(r.has_fmt); + REQUIRE(r.has_data); + REQUIRE(r.format_tag == 1); + REQUIRE(r.channels == 2); + REQUIRE(r.samples_per_sec == 44100); + REQUIRE(r.data_ck_size == data_bytes); + + std::remove(path); +} + +TEST_CASE("wav_verify: WavFileSimplePcm with cue, labels, files", "[wavfile][verify]") { + const uint32_t num_samples = 500; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + const char* filedata = "property1=hello\nproperty2=world\n"; + + smart::WavFileSimplePcm simple(2, 44100, 16); + simple.addData(sound_data, data_bytes); + simple.addCuePoint("CNFG", 0); + simple.addCuePoint("TRIG", 250, "Trigger point"); + simple.addAssocFile("CNFG", "TXT", filedata, strlen(filedata) + 1); + + const char* path = "/tmp/test_wavfile_verify_cue.wav"; + FILE* f = fopen(path, "wb"); + REQUIRE(f != nullptr); + simple.writeFile(f); + fclose(f); + + auto r = wav_verify_file(path); + INFO(r.summary()); + + // Basic structure + REQUIRE(r.has_riff); + REQUIRE(r.has_wave_form); + REQUIRE(r.has_fmt); + REQUIRE(r.has_data); + + // Cue chunk + REQUIRE(r.has_cue); + REQUIRE(r.cue_points_declared == 2); + + // LIST/adtl + REQUIRE(r.has_list_adtl); + REQUIRE(r.label_count >= 1); // "Trigger point" label + REQUIRE(r.file_count >= 1); // CNFG file + + // P5: after fix, dwPosition == dwSampleOffset for all cue points + CHECK_FALSE(r.has_issue_tagged("P5_SEQ_POSITION")); + + std::remove(path); +} + +TEST_CASE("wav_verify: detect P1/P2 issues with odd-length labels", "[wavfile][verify]") { + const uint32_t num_samples = 100; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + // Use a label string whose null-terminated length is odd, to trigger P1/P2 + // "ab" -> strlen=2, +1 null = 3 bytes string data, +4 dwName = 7 bytes total -> odd ckSize + smart::WavFileSimplePcm simple(2, 44100, 16); + simple.addData(sound_data, data_bytes); + simple.addCuePoint("TEST", 50, "ab"); + + const char* path = "/tmp/test_wavfile_verify_p1p2.wav"; + FILE* f = fopen(path, "wb"); + REQUIRE(f != nullptr); + simple.writeFile(f); + fclose(f); + + auto r = wav_verify_file(path); + INFO(r.summary()); + + // After P1/P2 fixes, the writer produces correct output + CHECK_FALSE(r.has_issue_tagged("P1_NO_PAD")); + CHECK_FALSE(r.has_issue_tagged("P2_PADDED_CKSIZE")); + CHECK(r.valid); + + std::remove(path); +} + +TEST_CASE("wav_verify: ratefactor>1 round-trip (P3)", "[wavfile][verify]") { + // Exercise the decimation path with a sample count not divisible by ratefactor. + // 1001 stereo 16-bit samples, ratefactor=3 → ceil(1001/3) = 334 output samples. + const uint32_t num_samples = 1001; + const uint32_t data_bytes = num_samples * sizeof(sample_stereo_16_t); + + uint8_t sound_data[data_bytes]; + fill_sawtooth(sound_data, num_samples); + + const char* path = "/tmp/test_wavfile_p3_roundtrip.wav"; + + { + smart::WavFileSimplePcm simple(2, 44100, 16, 3); + simple.addData(sound_data, data_bytes); + + FILE* f = fopen(path, "wb"); + REQUIRE(f != nullptr); + uint32_t written = simple.writeFile(f); + fclose(f); + REQUIRE(written > 0); + } + + { + auto r = wav_verify_file(path); + INFO(r.summary()); + + REQUIRE(r.has_fmt); + REQUIRE(r.has_data); + CHECK(r.format_tag == 1); + CHECK(r.channels == 2); + CHECK(r.samples_per_sec == 14700); // 44100 / 3 + + // ceil(1001/3) = 334 output samples × 4 bytes = 1336 + CHECK(r.data_ck_size == 1336); + CHECK_FALSE(r.has_issue_tagged("RIFF_SIZE_MISMATCH")); + CHECK_FALSE(r.has_issue_tagged("P3_DATA_NOT_BLOCK_ALIGNED")); + CHECK(r.valid); + } + + std::remove(path); +} + +TEST_CASE("wav_verify: row_length truncation round-trip (P4)", "[wavfile][verify]") { + // 24-bit mono: block_align=3, internal _row_length=4. + // Provide 13 bytes (not a multiple of _row_length=4). + // Writer should truncate to 12 bytes (4 complete frames). + const uint32_t data_bytes = 13; + uint8_t sound_data[data_bytes]; + for (uint32_t i = 0; i < data_bytes; i++) + sound_data[i] = static_cast(i + 1); + + const char* path = "/tmp/test_wavfile_p4_roundtrip.wav"; + + { + smart::WavFileSimplePcm simple(1, 48000, 24); + simple.addData(sound_data, data_bytes); + + FILE* f = fopen(path, "wb"); + REQUIRE(f != nullptr); + uint32_t written = simple.writeFile(f); + fclose(f); + REQUIRE(written > 0); + } + + { + auto r = wav_verify_file(path); + INFO(r.summary()); + + REQUIRE(r.has_fmt); + REQUIRE(r.has_data); + CHECK(r.format_tag == 1); + CHECK(r.channels == 1); + CHECK(r.block_align == 3); + // Truncated to floor(13/4)*4 = 12 bytes = 4 frames of 24-bit mono + CHECK(r.data_ck_size == 12); + CHECK_FALSE(r.has_issue_tagged("RIFF_SIZE_MISMATCH")); + CHECK_FALSE(r.has_issue_tagged("P3_DATA_NOT_BLOCK_ALIGNED")); + CHECK(r.valid); + } + + std::remove(path); +} + +TEST_CASE("wav_verify: 24-bit 3-channel odd-frame round-trip", "[wavfile][verify]") { + // blockAlign = 3 ch * 3 bytes = 9 (odd), exercising non-trivial packing. + // The library's PcmDataChunk rounds rows to 4-byte alignment internally + // (_row_length=12 for this format), so num_frames must satisfy + // num_frames * 9 % 12 == 0, i.e. num_frames is a multiple of 4. + const uint32_t num_frames = 136; + const uint32_t num_channels = 3; + const uint32_t bytes_per_frame = num_channels * 3; // 3 ch * 3 bytes = 9 + const uint32_t data_bytes = num_frames * bytes_per_frame; // 136 * 9 = 1224 + + // Generate counter data: frame 0 = {1,2,3}, frame 1 = {4,5,6}, ... + std::vector sound_data(data_bytes); + int32_t counter = 1; + for (uint32_t f = 0; f < num_frames; f++) { + for (uint32_t ch = 0; ch < num_channels; ch++) { + write_i24_le(sound_data.data() + f * bytes_per_frame + ch * 3, counter); + counter++; + } + } + + const char* path = "/tmp/test_wavfile_24bit_3ch.wav"; + + // Write via WavFileSimplePcm + { + smart::WavFileSimplePcm simple(num_channels, 48000, 24); + simple.addData(sound_data.data(), data_bytes); + + FILE* f = fopen(path, "wb"); + REQUIRE(f != nullptr); + uint32_t written = simple.writeFile(f); + fclose(f); + REQUIRE(written > 0); + } + + // Structural verification via wav_verify + { + auto r = wav_verify_file(path); + INFO(r.summary()); + + REQUIRE(r.has_fmt); + REQUIRE(r.format_tag == 1); + REQUIRE(r.channels == 3); + REQUIRE(r.bits_per_sample == 24); + REQUIRE(r.block_align == 9); + REQUIRE(r.samples_per_sec == 48000); + REQUIRE(r.avg_bytes_per_sec == 48000u * 9u); + REQUIRE(r.has_data); + REQUIRE(r.data_ck_size == 1224); + } + + // Read back and verify every sample + { + smart::WavFileDiskPcm reader(path); + REQUIRE(reader.hasData()); + REQUIRE(reader.getBytesPerSample() == 9); + REQUIRE(reader.getSampleCount() == 136); + + auto it = reader.getIterator(0); + REQUIRE(it != nullptr); + + counter = 1; + for (uint32_t f = 0; f < num_frames; f++) { + auto sample = it->getSampleInc(); + REQUIRE(!sample->empty()); + REQUIRE(sample->size() == bytes_per_frame); + + const auto* p = static_cast(sample->data()); + for (uint32_t ch = 0; ch < num_channels; ch++) { + int32_t got = read_i24_le(p + ch * 3); + REQUIRE(got == counter); + counter++; + } + } + } + + std::remove(path); +} diff --git a/tests/wav_verify.h b/tests/wav_verify.h new file mode 100644 index 0000000..1186394 --- /dev/null +++ b/tests/wav_verify.h @@ -0,0 +1,443 @@ +#pragma once + +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Byte-level read helpers (little-endian, no alignment requirement) +// --------------------------------------------------------------------------- + +inline uint16_t read_u16_le(const uint8_t* buf) { + return static_cast(buf[0] | (buf[1] << 8)); +} + +inline uint32_t read_u32_le(const uint8_t* buf) { + return static_cast( + buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); +} + +inline std::string read_fourcc(const uint8_t* buf) { + return std::string(reinterpret_cast(buf), 4); +} + +// --------------------------------------------------------------------------- +// Issue severity / issue record +// --------------------------------------------------------------------------- + +enum class WavIssueLevel { error, warning, info }; + +struct WavIssue { + WavIssueLevel level; + std::string tag; + std::string detail; +}; + +// --------------------------------------------------------------------------- +// Chunk info record (one per chunk discovered during iteration) +// --------------------------------------------------------------------------- + +struct WavChunkInfo { + std::string id; // fourcc, e.g. "fmt ", "data" + uint32_t ck_size; // value from the chunk header + size_t offset; // byte offset of the chunk header in the buffer +}; + +// --------------------------------------------------------------------------- +// Verification result +// --------------------------------------------------------------------------- + +struct WavVerifyResult { + bool valid = false; + + // RIFF + bool has_riff = false; + uint32_t riff_ck_size = 0; + bool has_wave_form = false; + + // fmt + bool has_fmt = false; + uint16_t format_tag = 0; + uint16_t channels = 0; + uint32_t samples_per_sec = 0; + uint32_t avg_bytes_per_sec = 0; + uint16_t block_align = 0; + uint16_t bits_per_sample = 0; + + // data + bool has_data = false; + uint32_t data_ck_size = 0; + size_t data_payload_offset = 0; + + // cue (optional) + bool has_cue = false; + uint32_t cue_points_declared = 0; + uint32_t cue_points_fit = 0; + + // LIST/adtl (optional) + bool has_list_adtl = false; + uint32_t label_count = 0; + uint32_t file_count = 0; + + // All chunks discovered, in order + std::vector chunks; + + // Issues + std::vector issues; + + // Helpers + bool has_errors() const { + for (auto& i : issues) + if (i.level == WavIssueLevel::error) return true; + return false; + } + + bool has_issue_tagged(const std::string& tag) const { + for (auto& i : issues) + if (i.tag == tag) return true; + return false; + } + + std::string summary() const { + std::string s; + s += "WAV verify: valid=" + std::string(valid ? "yes" : "no") + "\n"; + s += " RIFF: " + std::string(has_riff ? "yes" : "no"); + if (has_riff) + s += " ckSize=" + std::to_string(riff_ck_size); + s += " WAVE=" + std::string(has_wave_form ? "yes" : "no") + "\n"; + if (has_fmt) { + s += " fmt: tag=" + std::to_string(format_tag) + + " ch=" + std::to_string(channels) + + " rate=" + std::to_string(samples_per_sec) + + " avgBps=" + std::to_string(avg_bytes_per_sec) + + " blockAlign=" + std::to_string(block_align) + + " bits=" + std::to_string(bits_per_sample) + "\n"; + } + if (has_data) + s += " data: ckSize=" + std::to_string(data_ck_size) + + " payloadOff=" + std::to_string(data_payload_offset) + "\n"; + if (has_cue) + s += " cue: declared=" + std::to_string(cue_points_declared) + + " fit=" + std::to_string(cue_points_fit) + "\n"; + if (has_list_adtl) + s += " LIST/adtl: labels=" + std::to_string(label_count) + + " files=" + std::to_string(file_count) + "\n"; + s += " chunks(" + std::to_string(chunks.size()) + "):"; + for (auto& c : chunks) + s += " [" + c.id + " sz=" + std::to_string(c.ck_size) + + " @" + std::to_string(c.offset) + "]"; + s += "\n"; + if (!issues.empty()) { + s += " issues(" + std::to_string(issues.size()) + "):\n"; + for (auto& i : issues) { + const char* lv = (i.level == WavIssueLevel::error) ? "ERROR" + : (i.level == WavIssueLevel::warning) ? "WARN" + : "INFO"; + s += " " + std::string(lv) + " " + i.tag + ": " + i.detail + "\n"; + } + } + return s; + } +}; + +// --------------------------------------------------------------------------- +// Internal helpers +// --------------------------------------------------------------------------- + +namespace wav_verify_detail { + +inline void add_issue(WavVerifyResult& r, WavIssueLevel lv, + const std::string& tag, const std::string& detail) { + r.issues.push_back({lv, tag, detail}); +} + +inline void parse_fmt(WavVerifyResult& r, const uint8_t* data, size_t len, + uint32_t ck_size) { + r.has_fmt = true; + if (ck_size < 16) { + add_issue(r, WavIssueLevel::error, "FMT_TOO_SHORT", + "fmt ckSize=" + std::to_string(ck_size) + " < 16"); + return; + } + r.format_tag = read_u16_le(data + 0); + r.channels = read_u16_le(data + 2); + r.samples_per_sec = read_u32_le(data + 4); + r.avg_bytes_per_sec = read_u32_le(data + 8); + r.block_align = read_u16_le(data + 12); + r.bits_per_sample = read_u16_le(data + 14); + + // PCM consistency checks + if (r.format_tag == 1) { + uint16_t bytes_per_sample = r.bits_per_sample / 8; + uint16_t expected_align = r.channels * bytes_per_sample; + if (r.block_align != expected_align) { + add_issue(r, WavIssueLevel::error, "BAD_BLOCK_ALIGN", + "blockAlign=" + std::to_string(r.block_align) + + " expected=" + std::to_string(expected_align)); + } + uint32_t expected_avg = r.samples_per_sec * r.block_align; + if (r.avg_bytes_per_sec != expected_avg) { + add_issue(r, WavIssueLevel::error, "BAD_AVG_BYTES", + "avgBytesPerSec=" + std::to_string(r.avg_bytes_per_sec) + + " expected=" + std::to_string(expected_avg)); + } + } +} + +inline void parse_cue(WavVerifyResult& r, const uint8_t* data, size_t len, + uint32_t ck_size) { + r.has_cue = true; + if (ck_size < 4) return; + r.cue_points_declared = read_u32_le(data); + // Each cue point is 24 bytes + r.cue_points_fit = (ck_size - 4) / 24; + if (r.cue_points_declared != r.cue_points_fit) { + add_issue(r, WavIssueLevel::warning, "CUE_COUNT_MISMATCH", + "declared=" + std::to_string(r.cue_points_declared) + + " fit=" + std::to_string(r.cue_points_fit)); + } + + // P5: check dwPosition vs dwSampleOffset for simple data-chunk cue points + for (uint32_t i = 0; i < r.cue_points_fit && 4 + (i + 1) * 24 <= ck_size; i++) { + const uint8_t* pt = data + 4 + i * 24; + uint32_t dwPosition = read_u32_le(pt + 4); + std::string fccChunk = read_fourcc(pt + 8); + uint32_t dwChunkStart = read_u32_le(pt + 12); + uint32_t dwBlockStart = read_u32_le(pt + 16); + uint32_t dwSampleOffset = read_u32_le(pt + 20); + + if (fccChunk == "data" && dwChunkStart == 0 && dwBlockStart == 0) { + if (dwPosition != dwSampleOffset) { + add_issue(r, WavIssueLevel::info, "P5_SEQ_POSITION", + "cue point " + std::to_string(i) + + ": dwPosition=" + std::to_string(dwPosition) + + " != dwSampleOffset=" + std::to_string(dwSampleOffset)); + } + } + } +} + +inline void parse_list(WavVerifyResult& r, const uint8_t* chunk_data, + size_t chunk_data_len, uint32_t ck_size, + const uint8_t* buf, size_t buf_len) { + if (ck_size < 4) return; + std::string form = read_fourcc(chunk_data); + if (form != "adtl") return; + + r.has_list_adtl = true; + + // Iterate sub-chunks within LIST payload (after the 4-byte form type) + size_t cursor = 4; + while (cursor + 8 <= ck_size) { + std::string sub_id = read_fourcc(chunk_data + cursor); + uint32_t sub_sz = read_u32_le(chunk_data + cursor + 4); + + if (cursor + 8 + sub_sz > ck_size) { + add_issue(r, WavIssueLevel::error, "LIST_SUBCHUNK_OVERFLOW", + "sub-chunk '" + sub_id + "' at LIST offset " + + std::to_string(cursor) + " overflows LIST payload"); + break; + } + + if (sub_id == "labl" || sub_id == "ltxt" || sub_id == "note") { + r.label_count++; + + // Detect P2: check if ckSize includes padding bytes + // A label's data is: 4-byte dwName + null-terminated string. + // The true data length should be 4 + strlen(str) + 1. + // If ckSize is larger due to 4-byte alignment padding, flag it. + if (sub_id == "labl" && sub_sz > 4) { + const uint8_t* str_start = chunk_data + cursor + 8 + 4; + size_t str_max = sub_sz - 4; + size_t str_len = 0; + while (str_len < str_max && str_start[str_len] != 0) str_len++; + uint32_t true_data = 4 + static_cast(str_len) + 1; // dwName + string + null + if (sub_sz > true_data) { + // Check if the extra bytes are zero padding + bool all_zero = true; + for (uint32_t p = true_data; p < sub_sz; p++) { + if ((chunk_data + cursor + 8)[p] != 0) { all_zero = false; break; } + } + if (all_zero) { + add_issue(r, WavIssueLevel::warning, "P2_PADDED_CKSIZE", + "labl ckSize=" + std::to_string(sub_sz) + + " includes " + std::to_string(sub_sz - true_data) + + " padding byte(s)"); + } + } + } + } else if (sub_id == "file") { + r.file_count++; + } + + // Advance past sub-chunk, with word-alignment + size_t advance = 8 + sub_sz; + if (sub_sz & 1) advance++; // pad byte + cursor += advance; + } +} + +} // namespace wav_verify_detail + +// --------------------------------------------------------------------------- +// Main verification function +// --------------------------------------------------------------------------- + +inline WavVerifyResult wav_verify(const uint8_t* data, size_t len) { + using namespace wav_verify_detail; + WavVerifyResult r; + + // --- RIFF header (offsets 0-11) --- + if (len < 12) { + add_issue(r, WavIssueLevel::error, "MISSING_FMT", "buffer too short for RIFF header"); + add_issue(r, WavIssueLevel::error, "MISSING_DATA", "buffer too short for RIFF header"); + return r; + } + + std::string magic = read_fourcc(data); + if (magic != "RIFF") { + add_issue(r, WavIssueLevel::error, "MISSING_FMT", "not a RIFF file"); + add_issue(r, WavIssueLevel::error, "MISSING_DATA", "not a RIFF file"); + return r; + } + + r.has_riff = true; + r.riff_ck_size = read_u32_le(data + 4); + + std::string form = read_fourcc(data + 8); + r.has_wave_form = (form == "WAVE"); + + if (static_cast(r.riff_ck_size) + 8 != len) { + add_issue(r, WavIssueLevel::error, "RIFF_SIZE_MISMATCH", + "riff_ck_size+8=" + std::to_string(r.riff_ck_size + 8) + + " buffer_len=" + std::to_string(len)); + } + + // End of RIFF payload (clamp to buffer length for safety) + size_t riff_end = std::min(static_cast(r.riff_ck_size) + 8, len); + + // --- Iterate sub-chunks at cursor=12 --- + size_t cursor = 12; + while (cursor + 8 <= riff_end) { + std::string ck_id = read_fourcc(data + cursor); + uint32_t ck_size = read_u32_le(data + cursor + 4); + + r.chunks.push_back({ck_id, ck_size, cursor}); + + // Check chunk doesn't overflow RIFF payload + if (cursor + 8 + ck_size > riff_end) { + add_issue(r, WavIssueLevel::error, "CHUNK_OVERFLOW", + "chunk '" + ck_id + "' at offset " + std::to_string(cursor) + + " ckSize=" + std::to_string(ck_size) + + " extends past RIFF payload end=" + std::to_string(riff_end)); + break; + } + + const uint8_t* ck_data = data + cursor + 8; + size_t ck_data_len = ck_size; + + // Dispatch + if (ck_id == "fmt ") { + parse_fmt(r, ck_data, ck_data_len, ck_size); + } else if (ck_id == "data") { + r.has_data = true; + r.data_ck_size = ck_size; + r.data_payload_offset = cursor + 8; + } else if (ck_id == "cue ") { + parse_cue(r, ck_data, ck_data_len, ck_size); + } else if (ck_id == "LIST") { + parse_list(r, ck_data, ck_data_len, ck_size, data, len); + } + + // Advance cursor: 8 (header) + ckSize + optional pad byte + size_t advance = 8 + ck_size; + if (ck_size & 1) { + // Odd-sized chunk: check for pad byte + size_t pad_pos = cursor + 8 + ck_size; + if (pad_pos < riff_end) { + if (data[pad_pos] != 0) { + add_issue(r, WavIssueLevel::warning, "P1_NO_PAD", + "chunk '" + ck_id + "' at offset " + std::to_string(cursor) + + " has odd ckSize=" + std::to_string(ck_size) + + " but pad byte is 0x" + + std::string(1, "0123456789abcdef"[(data[pad_pos] >> 4) & 0xf]) + + std::string(1, "0123456789abcdef"[data[pad_pos] & 0xf]) + + " instead of 0x00"); + } + advance++; // skip pad byte + } else { + add_issue(r, WavIssueLevel::warning, "P1_NO_PAD", + "chunk '" + ck_id + "' at offset " + std::to_string(cursor) + + " has odd ckSize=" + std::to_string(ck_size) + + " but no room for pad byte"); + } + } + cursor += advance; + } + + // --- Post-parse checks --- + if (!r.has_fmt) + add_issue(r, WavIssueLevel::error, "MISSING_FMT", "no fmt chunk found"); + if (!r.has_data) + add_issue(r, WavIssueLevel::error, "MISSING_DATA", "no data chunk found"); + + // P3: data ckSize not frame-aligned + if (r.has_fmt && r.has_data && r.format_tag == 1) { + if (r.block_align > 0 && r.data_ck_size % r.block_align != 0) { + add_issue(r, WavIssueLevel::warning, "P3_DATA_NOT_BLOCK_ALIGNED", + "data ckSize=" + std::to_string(r.data_ck_size) + + " is not a multiple of blockAlign=" + std::to_string(r.block_align)); + } + } + + // P7: fmt must appear before data + { + size_t fmt_idx = SIZE_MAX, data_idx = SIZE_MAX; + for (size_t i = 0; i < r.chunks.size(); i++) { + if (r.chunks[i].id == "fmt " && fmt_idx == SIZE_MAX) fmt_idx = i; + if (r.chunks[i].id == "data" && data_idx == SIZE_MAX) data_idx = i; + } + if (data_idx != SIZE_MAX && (fmt_idx == SIZE_MAX || fmt_idx > data_idx)) { + add_issue(r, WavIssueLevel::warning, "P7_FMT_AFTER_DATA", + "data chunk at index " + std::to_string(data_idx) + + " appears before fmt chunk"); + } + } + + r.valid = !r.has_errors(); + return r; +} + +// --------------------------------------------------------------------------- +// File wrapper +// --------------------------------------------------------------------------- + +inline WavVerifyResult wav_verify_file(const std::string& path) { + WavVerifyResult r; + + FILE* f = fopen(path.c_str(), "rb"); + if (!f) { + r.issues.push_back({WavIssueLevel::error, "FILE_OPEN_FAILED", + "cannot open: " + path}); + return r; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + fclose(f); + r.issues.push_back({WavIssueLevel::error, "FILE_EMPTY", + "empty or unreadable: " + path}); + return r; + } + + std::vector buf(static_cast(fsize)); + size_t nread = fread(buf.data(), 1, buf.size(), f); + fclose(f); + + return wav_verify(buf.data(), nread); +}