pure-data/src/d_soundfile_wave.c

/* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* ref: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html */

#include "d_soundfile.h"

/* WAVE (Waveform Audio File Format)

  * RIFF variant with sections split into data "chunks"
  * chunk sizes do not include the chunk id or size (- 8)
  * chunk and sound data are little endian
  * format and sound data chunks are required
  * format tags:
    - PCM   linear PCM
    - FLOAT 32-bit float
    - EXT   extended format -> described in subformat fields
    - the rest are not relevant to Pd...
  * extended format must be used when:
    - PCM data has more than 16 bits per sample
    - number of channels is more than 2
    - mapping of channels to speakers is required (not relevant to Pd)
  * a fact chunk is required when the format is non-PCM
  * if sound data length is odd, a 0 pad byte should appended
  * limited to ~4 GB files as sizes are unsigned 32 bit ints
  * there are variants with 64-bit sizes (W64 and RF64) as well as extension
    formats which can split sound data across multiple files (BWF)

  this implementation:

  * supports basic and extended format chunks (WAVE Rev. 3)
  * implicitly writes extended format for 32 or 64 bit float (see below)
  * implements chunks: format, fact, sound data
  * ignores chunks: info, cset, cue, playlist, associated data, instrument,
                    sample, display, junk, pad, time code, digitization time
  * assumes format chunk is always before sound data chunk
  * assumes there is only 1 sound data chunk
  * does not support 64-bit size variants or BWF file-splitting
  * sample format: 16 and 24 bit lpcm, 32 and 64 bit float, no 32 bit lpcm

  Pd versions < 0.55 did not read or write 64 bit float.

  Pd versions < 0.51 did *not* read or write extended format explicitly, but
  ignored the format chunk format tag and interpreted the sample type based on
  the bits per sample: 2 : int 16, 3 : int 24, 4 : float 32. This means files
  created by newer Pd versions are currently more or less compatible with older
  Pd versions. This may change if 32 bit int support is introduced.

*/

    /* explicit byte sizes, sizeof(struct) may return alignment-padded values */
#define WAVECHUNKSIZE
#define WAVEHEADSIZE
#define WAVEFORMATSIZE
#define WAVEFACTSIZE

#define WAVEMAXBYTES

#define WAVE_FORMAT_PCM
#define WAVE_FORMAT_FLOAT
#define WAVE_FORMAT_EXT

/** extended format header and data length */
#define WAVE_EXT_SIZE

/** 14 byte extended subformat GUID */
#define WAVE_EXT_GUID

    /** basic chunk header, 8 bytes */
t_chunk;

    /** file head container chunk, 12 bytes */
t_head;

    /** format chunk, 24 (basic) or 48 (extended) */
t_formatchunk;

    /** fact chunk, 12 bytes */
t_factchunk;

/* ----- helpers ----- */

    /** returns 1 if format requires extended format and fact chunk */
static int wave_isextended(const t_soundfile *sf)
{}

    /** read first chunk, returns filled chunk and offset on success or -1 */
static off_t wave_firstchunk(const t_soundfile *sf, t_chunk *chunk)
{}

    /** read next chunk, chunk should be filled when calling
        returns filled chunk offset on success or -1 */
static off_t wave_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk)
{}

#ifdef DEBUG_SOUNDFILE

    /** post chunk info for debugging */
static void wave_postchunk(const t_chunk *chunk, int swap)
{
    post("%.4s %d", chunk->c_id, swap4s(chunk->c_size, swap));
}

    /** post head info for debugging */
static void wave_posthead(const t_head *head, int swap)
{
    wave_postchunk((const t_chunk *)head, swap);
    post("  %.4s", head->h_formtype);
}

    /** post format info for debugging */
static void wave_postformat(const t_formatchunk *format, int swap)
{
    uint16_t formattag = swap2(format->fc_fmttag, swap);
    wave_postchunk((const t_chunk *)format, swap);
    switch (formattag)
    {
        case WAVE_FORMAT_PCM:
            post("  format %d (PCM)", formattag);
            break;
        case WAVE_FORMAT_FLOAT:
            post("  format %d (FLOAT)", formattag);
            break;
        case WAVE_FORMAT_EXT:
            post("  format %d (EXT)", formattag);
            break;
        default:
            post("  format %d (unsupported)", formattag);
            break;
    }
    post("  channels %d", swap2(format->fc_nchannels, swap));
    post("  sample rate %d", swap4(format->fc_samplerate, swap));
    post("  bytes per sec %d", swap4(format->fc_bytespersecond, swap));
    post("  block align %d", swap2(format->fc_blockalign, swap));
    post("  bits per sample %u", swap2(format->fc_bitspersample, swap));
    if (formattag == WAVE_FORMAT_EXT)
    {
        formattag = swap2(*(uint16_t *)&format->fc_subformat, swap);
        post("  ext size %d", swap2(format->fc_extsize, swap));
        post("  ext valid bits per sample %d",
            swap2(format->fc_validbitspersample, swap));
        post("  ext channel mask 0x%04x", swap4(format->fc_channelmask, swap));
        switch (formattag)
        {
            case WAVE_FORMAT_PCM:
                post("  ext format %d (PCM)", formattag);
                break;
            case WAVE_FORMAT_FLOAT:
                post("  ext format %d (FLOAT)", formattag);
                break;
            default:
                post("  ext format %d (unsupported)", formattag);
                break;
        }
    }
}

    /** post fact info for debugging */
static void wave_postfact(const t_factchunk *fact, int swap)
{
    wave_postchunk((const t_chunk *)fact, swap);
    post("  sample length %d", swap4(fact->fc_samplelength, swap));
}

#endif /* DEBUG_SOUNDFILE */

/* ------------------------- WAVE ------------------------- */

static int wave_isheader(const char *buf, size_t size)
{}

static int wave_readheader(t_soundfile *sf)
{}

static int wave_writeheader(t_soundfile *sf, size_t nframes)
{}

    /** assumes chunk order:
        * basic:    head format data
        * extended: head format+ext fact data */
static int wave_updateheader(t_soundfile *sf, size_t nframes)
{}

static int wave_hasextension(const char *filename, size_t size)
{}

static int wave_addextension(char *filename, size_t size)
{}

    /* force little endian */
static int wave_endianness(int endianness, int bytespersample)
{}

/* ------------------------- setup routine ------------------------ */

t_soundfile_type wave =;

void soundfile_wave_setup( void)
{}