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 */