TEEM

2014-06-26

We're developing an experimental interface called "TEEM" at the moment. This is intended for streaming immersive 3D audio in a simple way that lets programs collaborate easily. We're hoping to make it "open".

TEEM supports sound objects with dynamic 3D positional information, and static multichannel beds including ambisonic beds. It has similarities to OpenAL, but is much simpler because it focusses only on 3D spatialisation (e.g. for cinema) and does not attempt to handle many rendering features needed for games (environmental modelling, effects, dynamic sample rate conversion and so on). That said, a game could render such features itself and then feed audio to a TEEM-compatible renderer or network stream for 3D spatialisation.

TEEM itself is a simple software interface (see below) that allows two TEEM-compatible software components to talk to each other in a standard way. We've also written a software development kit ("libteem"), which includes a lossless packet and file format and an example renderer, but you don't need this to use the TEEM interface itself.

We're still working out exactly what we should do with TEEM, partly because there is work happening in standards bodies with which we'd like to be compatible, many "object" concepts are in flux, and it is possible to have too many standards. To help the debate, we thought we'd give you a sneak peek at the technical core of the interface. What do you think? Please get in touch and let us know what we've broken or missed...

Possible Uses

We can see a lot of possible uses for this sort of thing. The interface itself can act as glue connecting components together. For instance, TEEM-compatible digital audio workstations or other audio and A/V packages could load or save TEEM-compatible data, using lossy or lossless coding formats, to aid cross-tool content creation. We could stream TEEM audio live or offline over a network, put it on disks, in audio files or embedded in A/V streams. TEEM audio could be rendered for playback on headphones for music, film, games, VR and more, using a wide variety of immersive audio rendering techniques, like traditional panning, VBAP, Wavefield synthesis or ambisonics. Of course, we could do this anyway, but by having a programming interface we massively increase the chance that when we (for instance) link a TEEM-compatible network receiver software component to (for instance) a TEEM-compatible Wavefield renderer, things will "just work".

The Technical Part - C/C++ Programmers Only!

The interface is specified as a C header file for direct compatibility with C and C++, and easy integration into other software. The lossless packet and file format in the SDK can be used for interchange, but other formats (e.g. lossy) would also work, if they follow the interface.

THE INTERFACE IS NOT FINAL - THIS IS JUST PRESENTED HERE FOR COMMENT. Please don't write any software using this just yet. Because it's not finished and we may yet make incompatible changes, we've not made it open yet (see the license below).

Here's the current header file:

/** @file teem.h

    Main C/C++ interface for TEEM streams. 

    Copyright (C) 2014 Blue Ripple Sound Limited
    
    Permission is hereby granted, free of charge, to the person to
    whom a copy of this software and associated documentation files is
    provided by Blue Ripple Sound Limited (the "Software"), to view
    and use the Software for the purpose only of assessing the quality
    and functionality of the Software and providing feedback on the
    same to Blue Ripple Sound Limited.
    
    No other rights are granted in respect of the Software and for the
    avoidance of doubt the following rights are hereby expressly 
    excluded: the right to copy, modify, merge, publish, distribute, 
    sublicense, and/or sell copies of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 

*/
    
#ifndef TEEM_H_INCLUDED
#define TEEM_H_INCLUDED

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/*****************************************************************************/

/** Correct behaviour should not be expected if interacting with code
    with a different major number. */
#define TEEM_API_VERSION_NUMBER_MAJOR 3

/** New backwards-compatible features, functions, metadata and audio
    types may be added in minor versions. */
#define TEEM_API_VERSION_NUMBER_MINOR 0

/*****************************************************************************/

/** Indicates the type of metadata being transmitted. Values are
    reserved. The value zero is not allowed. Other values under 1000
    will not be allocated and may be used for development purposes
    (only). */
typedef uint32_t TEEMMetadataType;

/*****************************************************************************/

/** TEEM object handles refer to audio, sources, beds or mixes. */
typedef void * TEEMObject;

/*****************************************************************************/

/** Timestamp, in samples, relative to some unspecified start point
    (NOT necessarily zero). Divide by the sample rate to find the
    sample index in seconds. Wrapping around back to zero is not
    defined behaviour. */
typedef uint64_t TEEMSampleIndex;

/** Sometimes end timestamps for objects are not known. In these
    cases, this symbol may be used instead until the information
    becomes available. */
#define TEEM_SAMPLE_INDEX_UNKNOWN (uint64_t(-1))

/*****************************************************************************/

/** Position in space, relative to a central position. +X is to the
    front, +Y is left, +Z is up. Units are metres. */
typedef struct {

  /** Positive values are to the front, negative values are to the
      back. Units are metres. */
  float atX;

  /** Positive values are to the left, negative values are to the
      right. Units are metres. */
  float atY;

  /** Positive values are upwards, negative values are
      downwards. Units are metres. */
  float atZ;

} TEEMVector;

/*****************************************************************************/

/** Return codes from the TEEM functions. TR_SUCCESS indicates
    success. Generally calls that fail can be assumed to leave data in
    the state they were in before the call, however this is not
    guaranteed for the TR_ALLOCATION_FAILED, TR_SYSTEM_ERROR or
    TR_UNKNOWN_ERROR cases. */
typedef enum {

  /** This return code generally means all has gone as intended. */
  TR_SUCCESS = 0,

  /** This indicates that the operation cannot yet complete, but the
      same operation may be possible in the future. */
  TR_NOT_READY = 1,

  /** An error has occurred, but its type is unclear. */
  TR_UNKNOWN_ERROR = 2,

  /** A system call has failed. */
  TR_SYSTEM_ERROR = 3,

  /** A memory or other resource allocation has failed. */
  TR_ALLOCATION_FAILED = 4,

  /** A parameter passed to the called routine was not valid. */
  TR_BAD_PARAMETER = 5,

  /** Data state is not well-defined. */
  TR_BAD_STATE = 6,

  /** Data is not compatible with the software in use. */
  TR_INCOMPATIBLE = 7,

  /** No more data in the stream or file. */
  TR_END_OF_STREAM = 8,

  /** Operation cannot be completed because of configuration. */
  TR_NOT_ALLOWED = 9

} TEEMResult;

/*****************************************************************************/

/** Audio types describe how the channels of an audio object should be
    interpreted (audio objects are used for both sources and
    beds). Examples of audio types are mono, stereo and 5.1. The type
    must correspond to the channel count present. This enumeration is
    expected to be extended in future versions of the API, so
    pass-through of unknown values is encouraged where possible. */
typedef enum {

  /** Mono audio. The channel count must be one. This is the only
      audio type which may be used with source objects. Beds may use
      any audio type.

      When used in a bed, mono audio provides the TEEM level
      reference. The word "level" is used here in a flexible
      way. Equal level would ideally constrain the sound pressure
      amplitude, intensity and other acoustic metrics around all
      listeners in the audience. In practice, this is not achieved (or
      generally desirable) and implementations manage level in
      different ways.

      To provide the TEEM level reference, mono bed audio is assumed
      to be presented to the listener from a position 1m directly in
      front of the head, without proximity effects such as wavefront
      curvature.

      In stereo rendering, mono bed audio might be scaled by -3dB and
      presented to both speakers, although some renderers may wish to
      handle even this differently, for instance for mono
      compatibility. */
  TAT_MONO = 0,

  /** Mono Low Frequency Effect audio. The channel count must be
      one. Audio is sent to an LFE ("Low Frequency Effect") channel if
      one is present.

      If no LFE speaker is present, it is usual to discard this rather
      than mix it into other channels. If this is not desired,
      TAT_MONO may be appropriate. */
  TAT_LFE = 1,

  /** Stereo audio. The channel count must be two (in order: left,
      right). TMT_STANDARD_STEREO and/or other metadata can be used to
      provide further information on handling.

      A sound presented to exactly one of the channels should sound at
      a similar level to TAT_MONO use. */
  TAT_STEREO = 2,

  /** Quadraphonic audio. The channel count must be four (in order:
      front left, front right, back left, back right).

      A sound presented to exactly one of the channels should sound at
      a similar level to TAT_MONO use. */
  TAT_QUAD = 3,

  /** 5.1 audio. The channel count must be six (in order: front left,
      front right, front centre, LFE, surround left, surround
      right). TMT_STANDARD_5_1 or other metadata can be used to
      provide further handling information.

      A sound presented to exactly one of the channels should sound at
      a similar level to TAT_MONO use. */
  TAT_5_1 = 4,

  /** 7.1 audio. The channel count must be eight (in order: front
      left, front right, front centre, LFE, back left, back right,
      surround left, surround right).

      A sound presented to exactly one of the channels should sound at
      a similar level to TAT_MONO use. */
  TAT_7_1 = 5,

  /** Auro-3D audio. The channel count must be 10, 11 or 12,
      corresponding to Auro-3D 9.1, 10.1 or 11.1 (in
      order: front left, front right, front centre, LFE, surround
      left, surround right, height front left, height front right,
      height surround left, height surround right, top ceiling, height
      front centre). 

      A sound presented to exactly one of the channels should sound at
      a similar level to TAT_MONO use. */
  TAT_AURO_3D = 6,

  /** 22.2 audio. The channel count must be 24 (in order: FL, FR, FC,
      LFE1, BL, BR, FLc, FRc, BC, LFE2, SiL, SiR, TpFL, TpFR, TpFC,
      TpC, TpBL, TpBR, TpSiL, TpSiR, TpBC, BtFC, BtFL, BtFR).

      A sound presented to exactly one of the channels should sound at
      a similar level to TAT_MONO use. */
  TAT_22_2 = 7,

  /** Ambisonic audio using FuMa encoding. The channel count is used
      to infer the order (or mixed order). The following channel
      sequences (and channel counts) are supported: W (1), WXY (3),
      WXYZ (4), WXYUV (5), WXYZUV (6), WXYUVPQ (7), WXYZUVPQ (8),
      WXYZRSTUV (9), WXYZRSTUVPQ (11), WXYZRSTUVKLMNOPQ (16).

      Playback should be at a level which ensures a source panned
      using the usual equations has a similar level to TAT_MONO
      use. */
  TAT_AMBISONIC_BFORMAT_FUMA = 8,

  /** Ambisonic audio using N3D encoding. The channel count is used to
      infer the order using (order+1)*(order+1)=count, and other
      channel counts are disallowed. ACN channel ordering and
      expressions are used.

      Playback should be at a level which ensures a source panned
      using the usual equations has a similar level to TAT_MONO
      use. */
  TAT_AMBISONIC_BFORMAT_N3D = 9

} TEEMAudioType;

/*****************************************************************************/
  
/** Source properties form a bitmask describing the various properties
    of a source. See TEEMSourceDetails. */
typedef enum {

  /** Nothing defined. */
  TSP_NONE = 0,

  /** The source should be considered non-diegetic rather than part of
      the main sound scene. This typically applies to "HUD" sounds,
      narration, and background music. */
  TSP_NDG = 0x01,

  /** The source is part of a mutually dependent group of sources,
      from which it should not be separated. This is typically used
      where the objects have been reduced by lossy compression with an
      expectation of later masking, which also suggests that overly
      precise spatial rendering may be a bad idea. */
  TSP_DEPENDENT_GROUP = 0x02

} TEEMSourceProperties;

/*****************************************************************************/
  
/** Bed properties form a bitmask describing the various properties of
    a bed. See TEEMBedDetails. */
typedef enum {

  /** Nothing defined. */
  TBP_NONE = 0,

  /** The bed should be considered non-diegetic rather than part of
      the main sound scene. This typically applies to "HUD" sounds,
      narration, and background music. */
  TBP_NDG = 0x01

} TEEMBedProperties;

/*****************************************************************************/

/** Mix properties form a bitmask of properties defined for a mix. */
typedef enum {

  /** Nothing defined. */
  TMP_NONE = 0,
  
  /** This mix includes commentary audio. */
  TMP_COMMENTARY = 0x01,

  /** This mix includes audio descriptive material. */
  TMP_AUDIO_DESCRIPTIVE = 0x02

} TEEMMixProperties;

/*****************************************************************************/

/** Fixed (non-varying) data for a source. */
typedef struct {
  
  /** The audio object which will provide an audio stream for the
      source. The audio object type must be of type TAT_MONO. Note
      that if the linked audio object ends then the source will
      too. */
  TEEMObject audio;

  /** Bitmap of properties applying to the source. */
  TEEMSourceProperties properties;

} TEEMSourceDetails;

/*****************************************************************************/

/** Fixed (non-varying) data for a bed. */
typedef struct {
  
  /** The audio object which will provide an audio stream for the
      bed. Note that if the linked audio object ends then the bed will
      too. */
  TEEMObject audio;

  /** Bitmap of properties applying to the bed. */
  TEEMBedProperties properties;

} TEEMBedDetails;

/*****************************************************************************/

/** Fixed (non-varying) data for a mix. */
typedef struct {
  
  /** Bitmap of properties applying to a mix. */
  TEEMMixProperties properties;

  /** The language code for this mix as a zero-terminated string, or
      an empty string if there is no language associated. Uses ISO
      639-2 (three character lower case) codes. */
  char language[4];

  /** Ordering number used to decide between mixes, overriding other
      information. Values are normally between 0 and 10, and large
      numbers go first. */
  uint8_t majorPreference;

  /** Ordering number used to decide between mixes in the absence of
      other information. Values are normally between 0 and 10, and
      large numbers go first. */
  uint8_t minorPreference;

} TEEMMixDetails;

/*****************************************************************************/

/** A source step describes how a source moves to a position in space,
    potentially over a period in time.

    Steps are defined over a time interval and the values here are
    defined for the end of that interval. During the interval, values
    are interpolated from preceding ones. */
typedef struct {

  /** The position from which the audio should ideally seem to
      come. This is specified in 3D Cartesian coordinates, in metres,
      with X forward, Y left and Z upwards, relative to the
      listener. Interpreting positions where multiple listeners are
      present is left to the discretion of the renderer.

      It is assumed that major distance cues are embedded in the audio
      data upstream. For instance, distant sources may be delayed and
      attenuated. Therefore, renderers might use only the position’s
      direction and not its distance.

      Positions are interpolated linearly in space. */
  TEEMVector position;

  /** The size of the source at the end timestamp, in metres, with 0
      indicating a point source. Changing the radius should not change
      the source's overall level. 

      Radii are interpolated linearly. */
  float radius;

  /** Sources can be made diffuse, without losing their directivity or
      changing in overall level. This is most relevant for sources
      with larger radii and results in sound components from different
      directions arriving with reduced correlation. Values are between
      zero and one and a value of zero indicates no diffuseness.

      Diffuseness values are interpolated linearly. */
  float diffuseness;

  /** The non-negative scalar control gain of the source at the end
      timestamp.

      Control gains are interpolated linearly. 

      Note that audio is represented using integers, and this gain
      needs to contain an appropriate scalar to take those integers
      down to a sensible level. Typically, the mix peak amplitude
      should be under 1. For instance, if 16bit audio (-32768 to
      +32767) is to be presented at -6dB (0.5) relative to its normal
      full level, this control gain might be 0.5/32768. */
  float controlGain;
  
} TEEMSourceStep;
  
/*****************************************************************************/

/** A bed step includes information about how a bed should be
    rendered.

    Steps are defined over a time interval and the values here are
    defined for the end of that interval. During the interval, values
    are interpolated from preceding ones. */
typedef struct {

  /** The non-negative scalar control gain of the bed at the end
      timestamp.

      Control gains are interpolated linearly. 

      Note that audio is represented using integers, and this gain
      needs to contain an appropriate scalar to take those integers
      down to a sensible level. Typically, the mix peak amplitude
      should be under 1. For instance, if 16bit audio (-32768 to
      +32767) is to be presented at -6dB (0.5) relative to its normal
      full level, this control gain might be 0.5/32768. */
  float controlGain;

} TEEMBedStep;
  
/*****************************************************************************/

/** Main stream handle. */
typedef struct TEEMStreamFunctionsStruct * TEEMStream;

/*****************************************************************************/
  
/** Streams control the overall state of an audio stream and the
    objects within it. Basic usage is for the 'upstream' code to call
    the routines provided here to maintain the objects and update the
    audio which they are linked to, and then to call flush() to push
    all the data 'downstream'. Object data is carried forward from
    block to block (although it can be restated regularly or
    irregularly for recovery purposes), and audio is read from memory
    during each flush() call (only).

    The downstream component is responsible for audio synchronisation
    and so provides the sample rate and current timestamp (sample
    index).

    This interface controls operation of the stream but not how it is
    initialised or cleaned up. It is assumed that some other function
    calls or interface(s) are used to manage this in a way which
    depends on the stream implementation. */
typedef struct TEEMStreamFunctionsStruct {

  /* Stream-Level Calls:
     ------------------- */

  /** Get the version of the TEEM application programming interface
      (API) that the stream implementation is using. The major and
      minor version numbers are written at the pointers provided. A
      difference in major version indicates that streaming should not
      proceed as the interface may not be directly compatible.

      @param stream A handle to the stream in use.

      @param majorVersion A difference in major version indicates that
      versions are not compatible.

      @param minorVersion A difference in minor version indicates that
      not all features may be fully supported. */
  TEEMResult (*getAPIVersion)(TEEMStream   stream,
                              uint32_t   * majorVersion,
                              uint32_t   * minorVersion);

  /** Get the sample rate, in Hz. The sample rate is written at the
      pointer provided.

      @param stream A handle to the stream in use.

      @param sampleRate On success, (*sampleRate) contains the sample
      rate, in Hz. */
  TEEMResult (*getSampleRate)(TEEMStream   stream,
                              float      * sampleRate);

  /** Get the index of the current sample index, as calculated by the
      downstream component. The result is written at the pointer
      provided. Note that the sample index start point is determined
      by the downstream component and may not necessarily start at
      zero.

      @param stream A handle to the stream in use.

      @param sampleIndex On success, (*sampleIndex) contains the
      current sample index. */
  TEEMResult (*getSampleIndex)(TEEMStream        stream,
                               TEEMSampleIndex * sampleIndex);

  /** Flush all data downstream for the current block. Returns an
      error code on failure, or TR_SUCCESS on success. This call
      releases all objects downstream and is the (only) point at which
      audio is read from memory. This call may have real-time
      dependencies and in practice may take a while and even
      block. After this, the current sample index (as returned by
      getSampleIndex()) will move forward by the block size provided.

      @param stream A handle to the stream in use.

      @param blockSize The block size, in samples, between 1 and
      65535. This determines how many samples of audio are read from
      each audio channel. It also determines how far the sample index
      is moved along. */
  TEEMResult (*flush)(TEEMStream stream,
                      uint16_t   blockSize);

  /* Audio:
     ------ */

  /** Declare an audio object for future use. Audio objects carry one
      or more channels of audio PCM data. They need to be linked to
      sources or beds for actual playback.

      The object is not well-defined until connectAudio() has been
      called successfully for it. After that point, full buffers of
      audio will be expected during flush() calls until the object has
      been destroyed. */
  TEEMResult (*declareAudio)(TEEMStream      stream,
                             TEEMAudioType   audioType,
                             uint16_t        channelCount,
                             TEEMObject    * audio);

  /** Connect the audio object to a memory location pointing to an
      array of channel pointers, each of which indicates an array of
      integer PCM samples for one channel.

      During a stream flush operation, the samples address is
      dereferenced and a number of samples are read from each of the
      channels. 

      The number of samples is given by the flush block size only and
      is <em>not</em> affected by the end time of any objects. In
      other words, after calling this method you must provide a full
      buffer of audio for every block until the scheduled end time for
      the audio object is less than or equal to the current block's
      sample index. (Of course, some of the block(s) of audio may be
      silent.)

      Note that the connection indicates just one memory location, so
      the indirect channel pointers (e.g. address samples[0]) may be
      changed upstream without another call to connectAudio() and must
      not be referenced downstream except during flush() calls. */
  TEEMResult (*connectAudio)(TEEMStream    stream,
                             TEEMObject    audio,
                             int32_t    ** samples);

  /** Schedule the end-point for the audio object. The audio object
      can be well-defined without this. The audio object, and any
      linked beds or sources, will end when this time is reached.

      The timestamp may or may not be exactly on a block boundary. An
      object is destroyed when its end timestamp is on or before the
      current block start. This can occur either through scheduling a
      new end timestamp or through time moving forward. Immediately
      after this happens (i.e. not only at flush points), the object's
      handle may become invalid and should not be used by upstream
      code. Further, no audio may be read in flush phases. Note that
      this means that an object can effectively be deleted immediately
      at any time. Regardless of the exact timing of object
      destruction, if the object ends within the current block, audio
      will not be heard after the end timestamp, although it may be
      before. However, a full block of audio must still be present in
      this case during the flush() call.

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send a later value, but equal or earlier values are allowed. */
  TEEMResult (*scheduleAudioEnd)(TEEMStream      stream,
                                 TEEMObject      audio,
                                 TEEMSampleIndex timestamp);
  
  /* Sources:
     -------- */

  /** Declare a source for future use, and set its basic details. A
      source is a virtual sound-producing agent placed somewhere in 3D
      space. It presents a mono sound stream.

      The source is not well-defined (and will not play) until the
      sample at which it has a valid source step and well-defined
      audio. */
  TEEMResult (*declareSource)(TEEMStream                stream,
                              const TEEMSourceDetails * details,
                              TEEMObject              * source);

  /** Schedule a change to the source settings. The startTimestamp
      must be less than or equal to the endTimestamp. Steps must all
      have distinct end timestamps and may not overlap (although the
      end timestamp of one event may match the start timestamp of
      another).

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send different values. */
  TEEMResult (*scheduleSourceStep)(TEEMStream             stream,
                                   TEEMObject             source,
                                   TEEMSampleIndex        startTimestamp,
                                   TEEMSampleIndex        endTimestamp,
                                   const TEEMSourceStep * step);

  /** Schedule the end time for the source object. The source can be
      well-defined without this. The source will end when this time is
      reached.

      The timestamp may or may not be exactly on a block boundary. An
      object is destroyed when its end timestamp is on or before the
      current block start. This can occur either through scheduling a
      new end timestamp or through time moving forward. Immediately
      after this happens (i.e. not only at flush points), the object's
      handle may become invalid and should not be used by upstream
      code. Note that this means that an object can effectively be
      deleted immediately at any time. Regardless of the exact timing
      of object destruction, if the object ends within the current
      block, audio will not be heard after the end timestamp, although
      it may be before.

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send a later value, but equal or earlier values are allowed. */
  TEEMResult (*scheduleSourceEnd)(TEEMStream      stream,
                                  TEEMObject      source,
                                  TEEMSampleIndex timestamp);

  /* Beds:
     ----- */

  /** Declare a bed for future use, and set its basic details. A bed
      presents (typically) pre-rendered multichannel audio
      (e.g. stereo or 5.1) into the virtual space in a specially
      defined way, not using the 3D spatialisation features of source
      objects.
      
      The bed is not well-defined (and will not play) until the sample
      at which it has a valid bed step and well-defined audio. */
  TEEMResult (*declareBed)(TEEMStream             stream,
                           const TEEMBedDetails * details,
                           TEEMObject           * bed);

  /** Schedule a change to the bed settings. The startTimestamp must
      be less than or equal to the endTimestamp. Steps must all have
      distinct end timestamps and may not overlap (although the end
      timestamp of one event may match the start timestamp of
      another).

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send different values. */
  TEEMResult (*scheduleBedStep)(TEEMStream          stream,
                                TEEMObject          bed,
                                TEEMSampleIndex     startTimestamp,
                                TEEMSampleIndex     endTimestamp,
                                const TEEMBedStep * step);

  /** Schedule the end time for the bed object. The bed can be
      well-defined without this. The bed will end when this time is
      reached.

      The timestamp may or may not be exactly on a block boundary. An
      object is destroyed when its end timestamp is on or before the
      current block start. This can occur either through scheduling a
      new end timestamp or through time moving forward. Immediately
      after this happens (i.e. not only at flush points), the object's
      handle may become invalid and should not be used by upstream
      code. Note that this means that an object can effectively be
      deleted immediately at any time. Regardless of the exact timing
      of object destruction, if the object ends within the current
      block, audio will not be heard after the end timestamp, although
      it may be before.

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send a later value, but equal or earlier values are
      allowed. */
  TEEMResult (*scheduleBedEnd)(TEEMStream      stream,
                               TEEMObject      bed,
                               TEEMSampleIndex timestamp);

  /* Mixes:
     ----- */

  /** Declare a mix for future use, and set its basic details. A mix
      selects a particular collection of sound objects (sources and
      beds) all of which are to be played at once.

      The mix is not well-defined (and will not play) until the sample
      at which it has a valid mix content (which may be empty). If no
      mix is well-defined then a default mix is used, containing all
      source and bed objects. */
  TEEMResult (*declareMix)(TEEMStream             stream,
                           const TEEMMixDetails * details,
                           TEEMObject           * mix);

  /** Schedule a change to the mix settings. Changes must all have
      distinct timestamps.

      Objects may be sources or beds. They may not be audio objects
      directly. Note that sources and beds may appear in any number of
      mixes and may share audio objects.

      The mix is well-defined once it has content, even if some of the
      objects provided are not well-defined themselves. Only the
      well-defined objects within the mix will sound. Also, the mix is
      still well-defined if some or all of these objects end.

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send different values. */
  TEEMResult (*scheduleMixContent)(TEEMStream        stream,
                                   TEEMObject        mix,
                                   TEEMSampleIndex   timestamp,
                                   TEEMObject      * objects,
                                   uint32_t          objectCount);

  /** Schedule the end time for the mix object. The mix can be
      well-defined without this. The mix will end when this time is
      reached. Objects within the mix are not destroyed.

      The timestamp may or may not be exactly on a block boundary. An
      object is destroyed when its end timestamp is on or before the
      current block start. This can occur either through scheduling a
      new end timestamp or through time moving forward. Immediately
      after this happens (i.e. not only at flush points), the object's
      handle may become invalid and should not be used by upstream
      code. Note that this means that an object can effectively be
      deleted immediately at any time. Regardless of the exact timing
      of object destruction, if the object ends within the current
      block, audio will not be heard after the end timestamp, although
      it may be before.

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send a later value, but equal or earlier values are
      allowed. */
  TEEMResult (*scheduleMixEnd)(TEEMStream      stream,
                               TEEMObject      mix,
                               TEEMSampleIndex timestamp);

  /* Metadata:
     --------- */

  /** Schedule a change in metadata at a particular point in time. Use
      a size of zero to make the metadata undefined from that
      point. Metadata can be applied to audio, source, bed or mix
      objects.

      This call may be used to add data, or to restate data that
      downstream should already have. When restating, it is an error
      to send different values. */
  TEEMResult (*scheduleMetadata)(TEEMStream         stream,
                                 TEEMObject         object,
                                 TEEMMetadataType   metadataType,
                                 TEEMSampleIndex    timestamp,
                                 const void       * data,
                                 uint32_t           dataSize);
                                
} TEEMStreamFunctions;

/*****************************************************************************/

#ifdef __cplusplus
}
#endif

#endif

/* EOF */