commit 50fb3c6b1c2bad2d035ed7cb73393a59f377452b Author: superp00t Date: Tue Aug 19 10:12:56 2025 -0400 chore(fmod): add files from Chensne/DragonNest diff --git a/lib/dlmalloc/dlmalloc.cpp b/lib/dlmalloc/dlmalloc.cpp new file mode 100755 index 0000000..0bd4e05 --- /dev/null +++ b/lib/dlmalloc/dlmalloc.cpp @@ -0,0 +1,3976 @@ +/* + ======================================================================== + To make a fully customizable malloc.h header file, cut everything + above this line, put into file malloc.h, edit to suit, and #include it + on the next line, as well as in programs that use this malloc. + ======================================================================== +*/ + +#include "dlmalloc.h" +#include "fmod_globals.h" + +#ifdef FMOD_SUPPORT_DLMALLOC +/*------------------------------ internal #includes ---------------------- */ + +#ifdef WIN32 +#include +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* WIN32 */ + +#include /* for printing in malloc_stats */ + +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ +#if FOOTERS +#include /* for magic initialization */ +#endif /* FOOTERS */ +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#define assert(x) +#endif /* DEBUG */ +#ifndef LACKS_STRING_H +#include /* for FMOD_memset etc */ +#endif /* LACKS_STRING_H */ +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +// CPS #include /* for mmap */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ +#if HAVE_MORECORE +#ifndef LACKS_UNISTD_H +#include /* for sbrk */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ +#endif /* HAVE_MMAP */ + +#ifndef WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# if defined(_SC_PAGE_SIZE) && !defined(__sun__) +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# // CPS include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some plaftorms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + + +namespace FMOD +{ + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* + If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if !HAVE_MMAP +#define IS_MMAPPED_BIT (SIZE_T_ZERO) +#define USE_MMAP_BIT (SIZE_T_ZERO) +#define CALL_MMAP(s) MFAIL +#define CALL_MUNMAP(a, s) (-1) +#define DIRECT_MMAP(s) MFAIL + +#else /* HAVE_MMAP */ +#define IS_MMAPPED_BIT (SIZE_T_ONE) +#define USE_MMAP_BIT (SIZE_T_ONE) + +#ifndef WIN32 +#define CALL_MUNMAP(a, s) munmap((a), (s)) +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif /* MAP_ANON */ +#ifdef MAP_ANONYMOUS +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) +#define CALL_MMAP(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) +#else /* MAP_ANONYMOUS */ +/* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. +*/ +#define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ +#define CALL_MMAP(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#endif /* MAP_ANONYMOUS */ + +#define DIRECT_MMAP(s) CALL_MMAP(s) +#else /* WIN32 */ + +/* Win32 MMAP via VirtualAlloc */ +static void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* This function supports releasing coalesed segments */ +static int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char *)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; +} + +#define CALL_MMAP(s) win32mmap(s) +#define CALL_MUNMAP(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MMAP && HAVE_MREMAP +#define CALL_MREMAP(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#else /* HAVE_MMAP && HAVE_MREMAP */ +#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +#if HAVE_MORECORE +#define CALL_MORECORE(S) MORECORE(S) +#else /* HAVE_MORECORE */ +#define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/* mstate bit set if continguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +/* --------------------------- Lock preliminaries ------------------------ */ + +#if USE_LOCKS + +/* + When locks are defined, there are up to two global locks: + + * If HAVE_MORECORE, morecore_mutex protects sequences of calls to + MORECORE. In many cases sys_alloc requires two calls, that should + not be interleaved with calls by other threads. This does not + protect against direct calls to MORECORE by other threads not + using this lock, so there is still code to cope the best we can on + interference. + + * magic_init_mutex ensures that mparams->magic and other + unique mparams values are initialized only once. +*/ + +#ifndef WIN32 +/* By default use posix locks */ +#include +#define MLOCK_T pthread_mutex_t +#define INITIAL_LOCK(l) pthread_mutex_init(l, NULL) +#define ACQUIRE_LOCK(l) pthread_mutex_lock(l) +#define RELEASE_LOCK(l) pthread_mutex_unlock(l) + +#if HAVE_MORECORE +static MLOCK_T morecore_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif /* HAVE_MORECORE */ + +static MLOCK_T magic_init_mutex = PTHREAD_MUTEX_INITIALIZER; + +#else /* WIN32 */ +/* + Because lock-protected regions have bounded times, and there + are no recursive lock calls, we can use simple spinlocks. +*/ + +#define MLOCK_T long +static int win32_acquire_lock (MLOCK_T *sl) { + for (;;) { +#ifdef InterlockedCompareExchangePointer + if (!InterlockedCompareExchange(sl, 1, 0)) + return 0; +#else /* Use older void* version */ + if (!InterlockedCompareExchange((void**)sl, (void*)1, (void*)0)) + return 0; +#endif /* InterlockedCompareExchangePointer */ + Sleep (0); + } +} + +static void win32_release_lock (MLOCK_T *sl) { + InterlockedExchange (sl, 0); +} + +#define INITIAL_LOCK(l) *(l)=0 +#define ACQUIRE_LOCK(l) win32_acquire_lock(l) +#define RELEASE_LOCK(l) win32_release_lock(l) +#if HAVE_MORECORE +static MLOCK_T morecore_mutex; +#endif /* HAVE_MORECORE */ +static MLOCK_T magic_init_mutex; +#endif /* WIN32 */ + +#define USE_LOCK_BIT (2U) +#else /* USE_LOCKS */ +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) +#endif /* USE_LOCKS */ + +#if USE_LOCKS && HAVE_MORECORE +#define ACQUIRE_MORECORE_LOCK() ACQUIRE_LOCK(&morecore_mutex); +#define RELEASE_MORECORE_LOCK() RELEASE_LOCK(&morecore_mutex); +#else /* USE_LOCKS && HAVE_MORECORE */ +#define ACQUIRE_MORECORE_LOCK() +#define RELEASE_MORECORE_LOCK() +#endif /* USE_LOCKS && HAVE_MORECORE */ + +#if USE_LOCKS +#define ACQUIRE_MAGIC_INIT_LOCK() ACQUIRE_LOCK(&magic_init_mutex); +#define RELEASE_MAGIC_INIT_LOCK() RELEASE_LOCK(&magic_init_mutex); +#else /* USE_LOCKS */ +#define ACQUIRE_MAGIC_INIT_LOCK() +#define RELEASE_MAGIC_INIT_LOCK() +#endif /* USE_LOCKS */ + + +/* ----------------------- Chunk representations ------------------------ */ + +/* + (The following includes lightly edited explanations by Colin Plumb.) + + The malloc_chunk declaration below is misleading (but accurate and + necessary). It declares a "view" into memory allowing access to + necessary fields at known offsets from a given base. + + Chunks of memory are maintained using a `boundary tag' method as + originally described by Knuth. (See the paper by Paul Wilson + ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such + techniques.) Sizes of free chunks are stored both in the front of + each chunk and at the end. This makes consolidating fragmented + chunks into bigger chunks fast. The head fields also hold bits + representing whether chunks are free or in use. + + Here are some pictures to make it clearer. They are "exploded" to + show that the state of a chunk can be thought of as extending from + the high 31 bits of the head field of its header through the + prev_foot and PINUSE_BIT bit of the following chunk header. + + A chunk that's in use looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk (if P = 1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | + chunk-> +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| + | Size of next chunk (may or may not be in use) | +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + And if it's free, it looks like this: + + chunk-> +- -+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| + | Size of next chunk (must be in use, or we would have merged)| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- User payload -+ + : | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0| + +-+ + Note that since we always merge adjacent free chunks, the chunks + adjacent to a free chunk must be in use. + + Given a pointer to a chunk (which can be derived trivially from the + payload pointer) we can, in O(1) time, find out whether the adjacent + chunks are free, and if so, unlink them from the lists that they + are on and merge them with the current chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + The P (PINUSE_BIT) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, preventing + access to non-existent (or non-owned) memory. If pinuse is set for + any given chunk, then you CANNOT determine the size of the + previous chunk, and might even get a memory addressing fault when + trying to do so. + + The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of + the chunk size redundantly records whether the current chunk is + inuse. This redundancy enables usage checks within free and realloc, + and reduces indirection when freeing and consolidating chunks. + + Each freshly allocated chunk must have both cinuse and pinuse set. + That is, each allocated chunk borders either a previously allocated + and still in-use chunk, or the base of its memory arena. This is + ensured by making all allocations from the the `lowest' part of any + found chunk. Further, no free chunk physically borders another one, + so each free chunk is known to be preceded and followed by either + inuse chunks or the ends of memory. + + Note that the `foot' of the current chunk is actually represented + as the prev_foot of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The exceptions to all this are + + 1. The special chunk `top' is the top-most available chunk (i.e., + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. + + 3. Chunks allocated via mmap, which have the lowest-order bit + (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set + PINUSE_BIT in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use. If the chunk was obtained with mmap, the prev_foot field has + IS_MMAPPED_BIT set, otherwise holding the offset of the base of the + mmapped region to the base of the chunk. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define chunksize(p) ((p)->head & ~(INUSE_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +#define is_mmapped(p)\ + (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If IS_MMAPPED_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & IS_MMAPPED_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams->magic. + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams-> +*/ + +// FMOD : CAN BE CALLED FROM DIFFERENT DLLS - MOVE INTO GLOBAL STRUCT +//struct malloc_params { +// size_t magic; +// size_t page_size; +// size_t granularity; +// size_t mmap_threshold; +// size_t trim_threshold; +// flag_t default_mflags; +//}; +// +//static struct malloc_params mparams; +static struct malloc_params *mparams = 0; + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams->page_size)) & ~(mparams->page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams->granularity)) & ~(mparams->granularity - SIZE_T_ONE)) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams->page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams->granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* + PREACTION should be defined to return 0 on success, and nonzero on + failure. If you are not using locking, you can redefine these to do + anything you like. +*/ + +#if USE_LOCKS + /* Ensure locks are initialized */ + #define GLOBALLY_INITIALIZE() (mparams->page_size == 0 && init_mparams()) + + #define PREACTION(M) ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) + #define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + + #ifndef PREACTION + #define PREACTION(M) init_mparams() + #endif /* PREACTION */ + + #ifndef POSTACTION + #define POSTACTION(M) + #endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* + CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. + USAGE_ERROR_ACTION is triggered on detected bad frees and + reallocs. The argument p is an address that might have triggered the + fault. It is ignored by the two predefined actions, but might be + useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + +/* -------------------------- Debugging setup ---------------------------- */ + +#ifndef DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I */ +#if defined(__GNUC__) && defined(i386) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* index corresponding to given bit */ + +#if defined(__GNUC__) && defined(i386) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\ + I = (bindex_t)J;\ +} + +#else /* GNUC */ +#if USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else /* USE_BUILTIN_FFS */ +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* USE_BUILTIN_FFS */ +#endif /* GNUC */ + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probablistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has its cinuse bit on */ +#define ok_cinuse(p) cinuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_cinuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams->magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__SNC__) +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams->magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams->magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +/* Initialize mparams */ +static int init_mparams(void) +{ + if (!mparams) + { + mparams = &gGlobal->gDLMalloc_mparams; + } + else + { + return 0; + } + + if (mparams->page_size == 0) { + size_t s; + + mparams->mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams->trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams->default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams->default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if (FOOTERS && !INSECURE) + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + s = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ + s = (size_t)(time(0) ^ (size_t)0x55555555U); + + s |= (size_t)8U; /* ensure nonzero */ + s &= ~(size_t)7U; /* improve chances of fault for bad values */ + + } +#else /* (FOOTERS && !INSECURE) */ + s = (size_t)0x58585858U; +#endif /* (FOOTERS && !INSECURE) */ + ACQUIRE_MAGIC_INIT_LOCK(); + if (mparams->magic == 0) { + mparams->magic = s; + /* Set up lock for main malloc area */ + INITIAL_LOCK(&gm->mutex); + gm->mflags = mparams->default_mflags; + } + RELEASE_MAGIC_INIT_LOCK(); + +#ifndef WIN32 + mparams->page_size = malloc_getpagesize; + mparams->granularity = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : mparams->page_size); +#else /* WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + mparams->page_size = system_info.dwPageSize; + mparams->granularity = system_info.dwAllocationGranularity; + } +#endif /* WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((mparams->granularity & (mparams->granularity-SIZE_T_ONE)) != 0) || + ((mparams->page_size & (mparams->page_size-SIZE_T_ONE)) != 0)) + { + ABORT + } + } + return 0; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val = (size_t)value; + init_mparams(); + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams->trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams->page_size && ((val & (val-1)) == 0)) { + mparams->granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams->mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#ifdef DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = chunksize(p); + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!next_pinuse(p)); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams->page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(cinuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!cinuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || cinuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!cinuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + { + assert(empty); + } + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + { + assert(empty); + } + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (cinuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + assert(m->topsize == chunksize(m->top)); + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!cinuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + + +/* +static void internal_malloc_stats(mstate m) { + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!cinuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + + POSTACTION(m); + } +} +*/ + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (F == B)\ + clear_smallmap(M, I);\ + else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ + (B == smallbin_at(M,I) || ok_address(M, B)))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F)\ + clear_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, F))) {\ + B->fd = F;\ + F->bk = B;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + assert(is_small(DVS));\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F))) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + (m == gm)? dlmalloc(b) : mspace_malloc(m, b) +#define internal_free(m, mem)\ + if (m == gm) dlfree(mem); else mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) dlmalloc(b) +#define internal_free(m, mem) dlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* + Directly mmapped chunks are set up with an offset to the start of + the mmapped region stored in the prev_foot field of the chunk. This + allows reconstruction of the required argument to MUNMAP when freed, + and also allows adjustment of the returned chunk to meet alignment + requirements (especially in memalign). There is also enough space + allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain + the PINUSE bit so frees can be checked. +*/ + +/* Malloc using mmap */ +#if 0 +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = granularity_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset | IS_MMAPPED_BIT; + (p)->head = (psize|CINUSE_BIT); + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + + return 0; +} +#endif + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams->granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = granularity_align(nb + SIX_SIZE_T_SIZES + + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, 1); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = (psize|CINUSE_BIT); + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams->trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallbins = m->treebins = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!cinuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +#if 0 +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + + init_mparams(); + + /* Directly map large chunks */ + if (use_mmap(m) && nb >= mparams->mmap_threshold) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + */ + + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + size_t asize = 0; + ACQUIRE_MORECORE_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + asize += (page_align((size_t)base) - (size_t)base); + /* Can't call MORECORE if size is negative when treated as signed */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == base) { + tbase = base; + tsize = asize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + asize = granularity_align(nb - m->topsize + TOP_FOOT_SIZE + SIZE_T_ONE); + /* Use mem here only if it did continuously extend old space */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { + tbase = br; + tsize = asize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (asize < HALF_MAX_SIZE_T && + asize < nb + TOP_FOOT_SIZE + SIZE_T_ONE) { + size_t esize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE - asize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + asize += esize; + else { /* Can't use; try to release */ + CALL_MORECORE(-asize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = asize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MORECORE_LOCK(); + } + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE; + size_t rsize = granularity_align(req); + if (rsize > nb) { /* Fail if wraps around zero */ + char* mp = (char*)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + mmap_flag = IS_MMAPPED_BIT; + } + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MORECORE_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MORECORE_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + m->seg.base = m->least_addr = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams->magic; + init_bins(m); + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + while (sp != 0 && tbase != sp->base + sp->size) + sp = sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & IS_MMAPPED_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + + return 0; +} +#endif + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + pred = sp; + sp = next; + } + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams->granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MORECORE_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MORECORE_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +/* --------------------------- realloc support --------------------------- */ + +static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + return 0; + } + if (!PREACTION(m)) { + mchunkptr oldp = mem2chunk(oldmem); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + void* extra = 0; + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + + if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) && + ok_next(oldp, next) && ok_pinuse(next))) { + size_t nb = request2size(bytes); + if (is_mmapped(oldp)) + newp = mmap_resize(m, oldp, nb); + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr remainder = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse(m, remainder, rsize); + extra = chunk2mem(remainder); + } + } + else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + POSTACTION(m); + return 0; + } + + POSTACTION(m); + + if (newp != 0) { + if (extra != 0) { + internal_free(m, extra); + } + check_inuse_chunk(m, newp); + return chunk2mem(newp); + } + else { + void* newmem = internal_malloc(m, bytes); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + FMOD_memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + return newmem; + } + } + return 0; +} + +/* --------------------------- memalign support -------------------------- */ + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ + return internal_malloc(m, bytes); + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + char* mem = (char*)internal_malloc(m, req); + if (mem != 0) { + void* leader = 0; + void* trailer = 0; + mchunkptr p = mem2chunk(mem); + + if (PREACTION(m)) return 0; + if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)(mem + + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = (newsize|CINUSE_BIT); + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + leader = chunk2mem(p); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + trailer = chunk2mem(remainder); + } + } + + assert (chunksize(p) >= nb); + assert((((size_t)(chunk2mem(p))) % alignment) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + if (leader != 0) { + internal_free(m, leader); + } + if (trailer != 0) { + internal_free(m, trailer); + } + return chunk2mem(p); + } + } + return 0; +} + +/* ------------------------ comalloc/coalloc support --------------------- */ + +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + /* + This provides common support for independent_X routines, handling + all of the combinations that can result. + + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed + */ + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + FMOD_memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#ifdef DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + + +/* -------------------------- public routines ---------------------------- */ + +#if !ONLY_MSPACES + +void* dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +void dlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_MMAPPED_BIT) != 0) { + prevsize &= ~IS_MMAPPED_BIT; + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + insert_chunk(fm, p, psize); + check_free_chunk(fm, p); + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + FMOD_memset(mem, 0, req); + return mem; +} + +void* dlrealloc(void* oldmem, size_t bytes) { + if (oldmem == 0) + return dlmalloc(bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + dlfree(oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(mem2chunk(oldmem)); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + return internal_realloc(m, oldmem, bytes); + } +} + +void* dlmemalign(size_t alignment, size_t bytes) { + return internal_memalign(gm, alignment, bytes); +} + +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +void* dlvalloc(size_t bytes) { + size_t pagesz; + init_mparams(); + pagesz = mparams->page_size; + return dlmemalign(pagesz, bytes); +} + +void* dlpvalloc(size_t bytes) { + size_t pagesz; + init_mparams(); + pagesz = mparams->page_size; + return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +int dlmalloc_trim(size_t pad) { + int result = 0; + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t dlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +#if !NO_MALLINFO +struct mallinfo dlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +/* +void dlmalloc_stats() { + internal_malloc_stats(gm); +} +*/ + +size_t dlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (cinuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* !ONLY_MSPACES */ + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + FMOD_memset(m, 0, msize); + INITIAL_LOCK(&m->mutex); + msp->head = (msize|PINUSE_BIT|CINUSE_BIT); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams->magic; + m->mflags = mparams->default_mflags; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +#if 0 +mspace create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize = pad_request(sizeof(struct malloc_state)); + init_mparams(); /* Ensure pagesize etc initialized */ + + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams->page_size)) { + size_t rs = ((capacity == 0)? mparams->granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = IS_MMAPPED_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} +#endif + +mspace create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize = pad_request(sizeof(struct malloc_state)); + init_mparams(); /* Ensure pagesize etc initialized */ + + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams->page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +#if 0 +size_t destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + sp = sp->next; + if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} +#endif + +/* + mspace versions of routines are near-clones of the global + versions. This is not so nice but better than the alternatives. +*/ + +void* mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + // mem = sys_alloc(ms, nb); // CPS - Don't allocate more memory than the given pool!!! + mem = 0; + + postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_MMAPPED_BIT) != 0) { + prevsize &= ~IS_MMAPPED_BIT; + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + insert_chunk(fm, p, psize); + check_free_chunk(fm, p); + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +} + +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + FMOD_memset(mem, 0, req); + return mem; +} + +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + if (oldmem == 0) + return mspace_malloc(msp, bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + mspace_free(msp, oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if FOOTERS + mchunkptr p = mem2chunk(oldmem); + mstate ms = get_mstate_for(p); +#else /* FOOTERS */ + mstate ms = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_realloc(ms, oldmem, bytes); + } +} + +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_memalign(ms, alignment, bytes); +} + +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +int mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +/* +void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +*/ + +size_t mspace_footprint(mspace msp) { + size_t result; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + USAGE_ERROR_ACTION(ms,ms); + return result; +} + + +size_t mspace_max_footprint(mspace msp) { + size_t result; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + USAGE_ERROR_ACTION(ms,ms); + return result; +} + + +#if !NO_MALLINFO +struct mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + +} // namespace FMOD +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* + Guidelines for creating a custom version of MORECORE: + + * For best performance, MORECORE should allocate in multiples of pagesize. + * MORECORE may allocate more memory than requested. (Or even less, + but this will usually result in a malloc failure.) + * MORECORE must not allocate memory when given argument zero, but + instead return one past the end address of memory from previous + nonzero call. + * For best performance, consecutive calls to MORECORE with positive + arguments should return increasing addresses, indicating that + space has been contiguously extended. + * Even though consecutive calls to MORECORE need not return contiguous + addresses, it must be OK for malloc'ed chunks to span multiple + regions in those cases where they do happen to be contiguous. + * MORECORE need not handle negative arguments -- it may instead + just return MFAIL when given negative arguments. + Negative arguments are always multiples of pagesize. MORECORE + must not misinterpret negative args as large positive unsigned + args. You can suppress all such calls from even occurring by defining + MORECORE_CANNOT_TRIM, + + As an example alternative MORECORE, here is a custom allocator + kindly contributed for pre-OSX macOS. It uses virtually but not + necessarily physically contiguous non-paged memory (locked in, + present and won't get swapped out). You can use it by uncommenting + this section, adding some #includes, and setting up the appropriate + defines above: + + #define MORECORE osMoreCore + + There is also a shutdown routine that should somehow be called for + cleanup upon program exit. + + #define MAX_POOL_ENTRIES 100 + #define MINIMUM_MORECORE_SIZE (64 * 1024U) + static int next_os_pool; + void *our_os_pools[MAX_POOL_ENTRIES]; + + void *osMoreCore(int size) + { + void *ptr = 0; + static void *sbrk_top = 0; + + if (size > 0) + { + if (size < MINIMUM_MORECORE_SIZE) + size = MINIMUM_MORECORE_SIZE; + if (CurrentExecutionLevel() == kTaskLevel) + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + if (ptr == 0) + { + return (void *) MFAIL; + } + // save ptrs so they can be freed during cleanup + our_os_pools[next_os_pool] = ptr; + next_os_pool++; + ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); + sbrk_top = (char *) ptr + size; + return ptr; + } + else if (size < 0) + { + // we don't currently support shrink behavior + return (void *) MFAIL; + } + else + { + return sbrk_top; + } + } + + // cleanup any allocated memory pools + // called as last thing before shutting down driver + + void osCleanupMem(void) + { + void **ptr; + + for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) + if (*ptr) + { + PoolDeallocate(*ptr); + *ptr = 0; + } + } + +*/ + + +/* ----------------------------------------------------------------------- +History: + V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) + * Add max_footprint functions + * Ensure all appropriate literals are size_t + * Fix conditional compilation problem for some #define settings + * Avoid concatenating segments with the one provided + in create_mspace_with_base + * Rename some variables to avoid compiler shadowing warnings + * Use explicit lock initialization. + * Better handling of sbrk interference. + * Simplify and fix segment insertion, trimming and mspace_destroy + * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x + * Thanks especially to Dennis Flanagan for help on these. + + V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) + * Fix memalign brace error. + + V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) + * Fix improper #endif nesting in C++ + * Add explicit casts needed for C++ + + V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) + * Use trees for large bins + * Support mspaces + * Use segments to unify sbrk-based and mmap-based system allocation, + removing need for emulation on most platforms without sbrk. + * Default safety checks + * Optional footer checks. Thanks to William Robertson for the idea. + * Internal code refactoring + * Incorporate suggestions and platform-specific changes. + Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, + Aaron Bachmann, Emery Berger, and others. + * Speed up non-fastbin processing enough to remove fastbins. + * Remove useless cfree() to avoid conflicts with other apps. + * Remove internal FMOD_memcpy, FMOD_memset. Compilers handle builtins better. + * Remove some options that no one ever used and rename others. + + V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) + * Fix malloc_state bitmap array misdeclaration + + V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) + * Allow tuning of FIRST_SORTED_BIN_SIZE + * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. + * Better detection and support for non-contiguousness of MORECORE. + Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger + * Bypass most of malloc if no frees. Thanks To Emery Berger. + * Fix freeing of old top non-contiguous chunk im sysmalloc. + * Raised default trim and map thresholds to 256K. + * Fix mmap-related #defines. Thanks to Lubos Lunak. + * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. + * Branch-free bin calculation + * Default trim and mmap thresholds now 256K. + + V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) + * Introduce independent_comalloc and independent_calloc. + Thanks to Michael Pachos for motivation and help. + * Make optional .h file available + * Allow > 2GB requests on 32bit systems. + * new WIN32 sbrk, mmap, munmap, lock code from . + Thanks also to Andreas Mueller , + and Anonymous. + * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for + helping test this.) + * memalign: check alignment arg + * realloc: don't try to shift chunks backwards, since this + leads to more fragmentation in some programs and doesn't + seem to help in any others. + * Collect all cases in malloc requiring system memory into sysmalloc + * Use mmap as backup to sbrk + * Place all internal state in malloc_state + * Introduce fastbins (although similar to 2.5.1) + * Many minor tunings and cosmetic improvements + * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK + * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS + Thanks to Tony E. Bennett and others. + * Include errno.h to support default failure action. + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ + +#endif // FMOD_SUPPORT_DLMALLOC diff --git a/lib/dlmalloc/dlmalloc.h b/lib/dlmalloc/dlmalloc.h new file mode 100755 index 0000000..9c5c780 --- /dev/null +++ b/lib/dlmalloc/dlmalloc.h @@ -0,0 +1,1173 @@ +#ifndef _DLMALLOC_H +#define _DLMALLOC_H + +/* + This is a version (aka dlmalloc) of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/licenses/publicdomain. Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu + +* Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Quickstart + + This library is all in one file to simplify the most common usage: + ftp it, compile it (-O3), and link it into another program. All of + the compile-time options default to reasonable values for use on + most platforms. You might later want to step through various + compile-time and dynamic tuning options. + + For convenience, an include file for code using this malloc is at: + ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.3.h + You don't really need this .h file unless you call functions not + defined in your system include files. The .h file contains only the + excerpts from this file needed for using this malloc on ANSI C/C++ + systems, so long as you haven't changed compile-time options about + naming and tuning parameters. If you do, then you can create your + own malloc.h that does include all settings by cutting at the point + indicated below. Note that you may already by default be using a C + library containing a malloc that is based on some version of this + malloc (for example in linux). You might still want to use the one + in this file to customize settings or to avoid overheads associated + with library versions. + +* Vital statistics: + + Supported pointer/size_t representation: 4 or 8 bytes + size_t MUST be an unsigned type of the same width as + pointers. (If you are using an ancient system that declares + size_t as a signed type, or need it to be a different width + than pointers, you can use a previous release of this malloc + (e.g. 2.7.2) supporting these.) + + Alignment: 8 bytes (default) + This suffices for nearly all current machines and C compilers. + However, you can define MALLOC_ALIGNMENT to be wider than this + if necessary (up to 128bytes), at the expense of using more space. + + Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) + 8 or 16 bytes (if 8byte sizes) + Each malloced chunk has a hidden word of overhead holding size + and status information, and additional cross-check word + if FOOTERS is defined. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) + 8-byte ptrs: 32 bytes (including overhead) + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + The maximum overhead wastage (i.e., number of extra bytes + allocated than were requested in malloc) is less than or equal + to the minimum size, except for requests >= mmap_threshold that + are serviced via mmap(), where the worst case wastage is about + 32 bytes plus the remainder from a system page (the minimal + mmap unit); typically 4096 or 8192 bytes. + + Security: static-safe; optionally more or less + The "security" of malloc refers to the ability of malicious + code to accentuate the effects of errors (for example, freeing + space that is not currently malloc'ed or overwriting past the + ends of chunks) in code that calls malloc. This malloc + guarantees not to modify any memory locations below the base of + heap, i.e., static variables, even in the presence of usage + errors. The routines additionally detect most improper frees + and reallocs. All this holds as long as the static bookkeeping + for malloc itself is not corrupted by some other means. This + is only one aspect of security -- these checks do not, and + cannot, detect all possible programming errors. + + If FOOTERS is defined nonzero, then each allocated chunk + carries an additional check word to verify that it was malloced + from its space. These check words are the same within each + execution of a program using malloc, but differ across + executions, so externally crafted fake chunks cannot be + freed. This improves security by rejecting frees/reallocs that + could corrupt heap memory, in addition to the checks preventing + writes to statics that are always on. This may further improve + security at the expense of time and space overhead. (Note that + FOOTERS may also be worth using with MSPACES.) + + By default detected errors cause the program to abort (calling + "abort()"). You can override this to instead proceed past + errors by defining PROCEED_ON_ERROR. In this case, a bad free + has no effect, and a malloc that encounters a bad address + caused by user overwrites will ignore the bad address by + dropping pointers and indices to all known memory. This may + be appropriate for programs that should continue if at all + possible in the face of programming errors, although they may + run out of memory because dropped memory is never reclaimed. + + If you don't like either of these options, you can define + CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything + else. And if if you are sure that your program using malloc has + no errors or vulnerabilities, you can define INSECURE to 1, + which might (or might not) provide a small performance improvement. + + Thread-safety: NOT thread-safe unless USE_LOCKS defined + When USE_LOCKS is defined, each public call to malloc, free, + etc is surrounded with either a pthread mutex or a win32 + spinlock (depending on WIN32). This is not especially fast, and + can be a major bottleneck. It is designed only to provide + minimal protection in concurrent environments, and to provide a + basis for extensions. If you are using malloc in a concurrent + program, consider instead using ptmalloc, which is derived from + a version of this malloc. (See http://www.malloc.de). + + System requirements: Any combination of MORECORE and/or MMAP/MUNMAP + This malloc can use unix sbrk or any emulation (invoked using + the CALL_MORECORE macro) and/or mmap/munmap or any emulation + (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system + memory. On most unix systems, it tends to work best if both + MORECORE and MMAP are enabled. On Win32, it uses emulations + based on VirtualAlloc. It also uses common C library functions + like FMOD_memset. + + Compliance: I believe it is compliant with the Single Unix Specification + (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably + others as well. + +* Overview of algorithms + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and + tunable. Consistent balance across these factors results in a good + general-purpose allocator for malloc-intensive programs. + + In most ways, this malloc is a best-fit allocator. Generally, it + chooses the best-fitting existing chunk for a request, with ties + broken in approximately least-recently-used order. (This strategy + normally maintains low fragmentation.) However, for requests less + than 256bytes, it deviates from best-fit when there is not an + exactly fitting available chunk by preferring to use space adjacent + to that used for the previous small request, as well as by breaking + ties in approximately most-recently-used order. (These enhance + locality of series of small allocations.) And for very large requests + (>= 256Kb by default), it relies on system memory mapping + facilities, if supported. (This helps avoid carrying around and + possibly fragmenting memory used only for large chunks.) + + All operations (except malloc_stats and mallinfo) have execution + times that are bounded by a constant factor of the number of bits in + a size_t, not counting any clearing in calloc or copying in realloc, + or actions surrounding MORECORE and MMAP that have times + proportional to the number of non-contiguous regions returned by + system allocation routines, which is often just 1. + + The implementation is not very modular and seriously overuses + macros. Perhaps someday all C compilers will do as good a job + inlining modular code as can now be done by brute-force expansion, + but now, enough of them seem not to. + + Some compilers issue a lot of warnings about code that is + dead/unreachable only on some platforms, and also about intentional + uses of negation on unsigned types. All known cases of each can be + ignored. + + For a longer but out of date high-level description, see + http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES + If MSPACES is defined, then in addition to malloc, free, etc., + this file also defines mspace_malloc, mspace_free, etc. These + are versions of malloc routines that take an "mspace" argument + obtained using create_mspace, to control all internal bookkeeping. + If ONLY_MSPACES is defined, only these versions are compiled. + So if you would like to use this allocator for only some allocations, + and your system malloc for others, you can compile with + ONLY_MSPACES and then do something like... + static mspace mymspace = create_mspace(0,0); // for example + #define mymalloc(bytes) mspace_malloc(mymspace, bytes) + + (Note: If you only need one instance of an mspace, you can instead + use "USE_DL_PREFIX" to relabel the global malloc.) + + You can similarly create thread-local allocators by storing + mspaces as thread-locals. For example: + static __thread mspace tlms = 0; + void* tlmalloc(size_t bytes) { + if (tlms == 0) tlms = create_mspace(0, 0); + return mspace_malloc(tlms, bytes); + } + void tlfree(void* mem) { mspace_free(tlms, mem); } + + Unless FOOTERS is defined, each mspace is completely independent. + You cannot allocate from one and free to another (although + conformance is only weakly checked, so usage errors are not always + caught). If FOOTERS is defined, then each chunk carries around a tag + indicating its originating mspace, and frees are directed to their + originating spaces. + + ------------------------- Compile-time options --------------------------- + +Be careful in setting #define values for numerical constants of type +size_t. On some systems, literal values are not automatically extended +to size_t precision unless they are explicitly casted. + +WIN32 default: defined if _WIN32 defined + Defining WIN32 sets up defaults for MS environment and compilers. + Otherwise defaults are for unix. + +MALLOC_ALIGNMENT default: (size_t)8 + Controls the minimum alignment for malloc'ed chunks. It must be a + power of two and at least 8, even on machines for which smaller + alignments would suffice. It may be defined as larger than this + though. Note however that code and data structures are optimized for + the case of 8-byte alignment. + +MSPACES default: 0 (false) + If true, compile in support for independent allocation spaces. + This is only supported if HAVE_MMAP is true. + +ONLY_MSPACES default: 0 (false) + If true, only compile in mspace versions, not regular versions. + +USE_LOCKS default: 0 (false) + Causes each call to each public routine to be surrounded with + pthread or WIN32 mutex lock/unlock. (If set true, this can be + overridden on a per-mspace basis for mspace versions.) + +FOOTERS default: 0 + If true, provide extra checking and dispatching by placing + information in the footers of allocated chunks. This adds + space and time overhead. + +INSECURE default: 0 + If true, omit checks for usage errors and heap space overwrites. + +USE_DL_PREFIX default: NOT defined + Causes compiler to prefix all public routines with the string 'dl'. + This can be useful when you only want to use this malloc in one part + of a program, using your regular system malloc elsewhere. + +ABORT default: defined as abort() + Defines how to abort on failed checks. On most systems, a failed + check cannot die with an "assert" or even print an informative + message, because the underlying print routines in turn call malloc, + which will fail again. Generally, the best policy is to simply call + abort(). It's not very useful to do more than this because many + errors due to overwriting will show up as address faults (null, odd + addresses etc) rather than malloc-triggered checks, so will also + abort. Also, most compilers know that abort() does not return, so + can better optimize code conditionally calling it. + +PROCEED_ON_ERROR default: defined as 0 (false) + Controls whether detected bad addresses cause them to bypassed + rather than aborting. If set, detected bad arguments to free and + realloc are ignored. And all bookkeeping information is zeroed out + upon a detected overwrite of freed heap space, thus losing the + ability to ever return it from malloc again, but enabling the + application to proceed. If PROCEED_ON_ERROR is defined, the + static variable malloc_corruption_error_count is compiled in + and can be examined to see if errors have occurred. This option + generates slower code than the default abort policy. + +DEBUG default: NOT defined + The DEBUG setting is mainly intended for people trying to modify + this code or diagnose problems when porting to new platforms. + However, it may also be able to better isolate user errors than just + using runtime checks. The assertions in the check routines spell + out in more detail the assumptions and invariants underlying the + algorithms. The checking is fairly extensive, and will slow down + execution noticeably. Calling malloc_stats or mallinfo with DEBUG + set will attempt to check every non-mmapped allocated and free chunk + in the course of computing the summaries. + +ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) + Debugging assertion failures can be nearly impossible if your + version of the assert macro causes malloc to be called, which will + lead to a cascade of further failures, blowing the runtime stack. + ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), + which will usually make debugging easier. + +MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 + The action to take before "return 0" when malloc fails to be able to + return memory because there is none available. + +HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES + True if this system supports sbrk or an emulation of it. + +MORECORE default: sbrk + The name of the sbrk-style system routine to call to obtain more + memory. See below for guidance on writing custom MORECORE + functions. The type of the argument to sbrk/MORECORE varies across + systems. It cannot be size_t, because it supports negative + arguments, so it is normally the signed type of the same width as + size_t (sometimes declared as "intptr_t"). It doesn't much matter + though. Internally, we only call it with arguments less than half + the max value of a size_t, which should work across all reasonable + possibilities, although sometimes generating compiler warnings. See + near the end of this file for guidelines for creating a custom + version of MORECORE. + +MORECORE_CONTIGUOUS default: 1 (true) + If true, take advantage of fact that consecutive calls to MORECORE + with positive arguments always return contiguous increasing + addresses. This is true of unix sbrk. It does not hurt too much to + set it true anyway, since malloc copes with non-contiguities. + Setting it false when definitely non-contiguous saves time + and possibly wasted space it would take to discover this though. + +MORECORE_CANNOT_TRIM default: NOT defined + True if MORECORE cannot release space back to the system when given + negative arguments. This is generally necessary only if you are + using a hand-crafted MORECORE function that cannot handle negative + arguments. + +HAVE_MMAP default: 1 (true) + True if this system supports mmap or an emulation of it. If so, and + HAVE_MORECORE is not true, MMAP is used for all system + allocation. If set and HAVE_MORECORE is true as well, MMAP is + primarily used to directly allocate very large blocks. It is also + used as a backup strategy in cases where MORECORE fails to provide + space from system. Note: A single call to MUNMAP is assumed to be + able to unmap memory that may have be allocated using multiple calls + to MMAP, so long as they are adjacent. + +HAVE_MREMAP default: 1 on linux, else 0 + If true realloc() uses mremap() to re-allocate large blocks and + extend or shrink allocation spaces. + +MMAP_CLEARS default: 1 on unix + True if mmap clears memory so calloc doesn't need to. This is true + for standard unix mmap using /dev/zero. + +USE_BUILTIN_FFS default: 0 (i.e., not used) + Causes malloc to use the builtin ffs() function to compute indices. + Some compilers may recognize and intrinsify ffs to be faster than the + supplied C version. Also, the case of x86 using gcc is special-cased + to an asm instruction, so is already as fast as it can be, and so + this setting has no effect. (On most x86s, the asm version is only + slightly faster than the C version.) + +malloc_getpagesize default: derive from system includes, or 4096. + The system page size. To the extent possible, this malloc manages + memory from the system in page-size units. This may be (and + usually is) a function rather than a constant. This is ignored + if WIN32, where page size is determined using getSystemInfo during + initialization. + +USE_DEV_RANDOM default: 0 (i.e., not used) + Causes malloc to use /dev/random to initialize secure magic seed for + stamping footers. Otherwise, the current time is used. + +NO_MALLINFO default: 0 + If defined, don't compile "mallinfo". This can be a simple way + of dealing with mismatches between system declarations and + those in this file. + +MALLINFO_FIELD_TYPE default: size_t + The type of the fields in the mallinfo struct. This was originally + defined as "int" in SVID etc, but is more usefully defined as + size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set + +REALLOC_ZERO_BYTES_FREES default: not defined + This should be set if a call to realloc with zero bytes should + be the same as a call to free. Some people think it should. Otherwise, + since this malloc returns a unique pointer for malloc(0), so does + realloc(p, 0). + +LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H +LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H +LACKS_STDLIB_H default: NOT defined unless on WIN32 + Define these if your system does not have these header files. + You might need to manually insert some of the declarations they provide. + +DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, + system_info.dwAllocationGranularity in WIN32, + otherwise 64K. + Also settable using mallopt(M_GRANULARITY, x) + The unit for allocating and deallocating memory from the system. On + most systems with contiguous MORECORE, there is no reason to + make this more than a page. However, systems with MMAP tend to + either require or encourage larger granularities. You can increase + this value to prevent system allocation functions to be called so + often, especially if they are slow. The value must be at least one + page and must be a power of two. Setting to 0 causes initialization + to either page size or win32 region size. (Note: In previous + versions of malloc, the equivalent of this option was called + "TOP_PAD") + +DEFAULT_TRIM_THRESHOLD default: 2MB + Also settable using mallopt(M_TRIM_THRESHOLD, x) + The maximum amount of unused top-most memory to keep before + releasing via malloc_trim in free(). Automatic trimming is mainly + useful in long-lived programs using contiguous MORECORE. Because + trimming via sbrk can be slow on some systems, and can sometimes be + wasteful (in cases where programs immediately afterward allocate + more large chunks) the value should be high enough so that your + overall system performance would improve by releasing this much + memory. As a rough guide, you might set to a value close to the + average size of a process (program) running on your system. + Releasing this much memory would allow such a process to run in + memory. Generally, it is worth tuning trim thresholds when a + program undergoes phases where several large chunks are allocated + and released in ways that can reuse each other's storage, perhaps + mixed with phases where there are no such chunks at all. The trim + value must be greater than page size to have any useful effect. To + disable trimming completely, you can set to MAX_SIZE_T. Note that the trick + some people use of mallocing a huge space and then freeing it at + program startup, in an attempt to reserve system memory, doesn't + have the intended effect under automatic trimming, since that memory + will immediately be returned to the system. + +DEFAULT_MMAP_THRESHOLD default: 256K + Also settable using mallopt(M_MMAP_THRESHOLD, x) + The request size threshold for using MMAP to directly service a + request. Requests of at least this size that cannot be allocated + using already-existing space will be serviced via mmap. (If enough + normal freed space already exists it is used instead.) Using mmap + segregates relatively large chunks of memory so that they can be + individually obtained and released from the host system. A request + serviced through mmap is never reused by any other request (at least + not directly; the system may just so happen to remap successive + requests to the same locations). Segregating space in this way has + the benefits that: Mmapped space can always be individually released + back to the system, which helps keep the system level memory demands + of a long-lived program low. Also, mapped memory doesn't become + `locked' between other chunks, as can happen with normally allocated + chunks, which means that even trimming via malloc_trim would not + release them. However, it has the disadvantage that the space + cannot be reclaimed, consolidated, and then used to service later + requests, as happens with normal chunks. The advantages of mmap + nearly always outweigh disadvantages for "large" chunks, but the + value of "large" may vary across systems. The default is an + empirically derived value that works well in most systems. You can + disable mmap by setting to MAX_SIZE_T. + +*/ +#include + +#ifndef WIN32 + #if defined(_WIN32) && !defined(_XENON) + #define WIN32 1 + #endif /* _WIN32 */ +#endif /* WIN32 */ + +#define LACKS_ERRNO_H +#define LACKS_STDLIB_H +#define MALLOC_FAILURE_ACTION +#define ABORT + +#ifdef WIN32 + #define HAVE_MMAP 1 + #define HAVE_MORECORE 0 + #define LACKS_UNISTD_H + #define LACKS_SYS_PARAM_H + #define LACKS_SYS_MMAN_H + #define LACKS_STRING_H + #define LACKS_STRINGS_H + #define LACKS_SYS_TYPES_H + #define MMAP_CLEARS 0 /* WINCE and some others apparently don't clear */ +#endif /* WIN32 */ + +#if defined(DARWIN) || defined(_DARWIN) + /* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ + #ifndef HAVE_MORECORE + #define HAVE_MORECORE 0 + #define HAVE_MMAP 1 + #endif /* HAVE_MORECORE */ +#endif /* DARWIN */ + +#ifndef LACKS_SYS_TYPES_H + #include /* For size_t */ +#endif /* LACKS_SYS_TYPES_H */ + +/* The maximum possible size_t value has all bits set */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef ONLY_MSPACES + #define ONLY_MSPACES 1 +#endif /* ONLY_MSPACES */ +#ifndef MSPACES + #if ONLY_MSPACES + #define MSPACES 1 + #else /* ONLY_MSPACES */ + #define MSPACES 0 + #endif /* ONLY_MSPACES */ +#endif /* MSPACES */ +#ifndef MALLOC_ALIGNMENT + #define MALLOC_ALIGNMENT ((size_t)8U) +#endif /* MALLOC_ALIGNMENT */ +#ifndef FOOTERS + #define FOOTERS 0 +#endif /* FOOTERS */ +#ifndef ABORT + #error shit + #define ABORT abort(); +#endif /* ABORT */ +#ifndef ABORT_ON_ASSERT_FAILURE + //#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ +#ifndef PROCEED_ON_ERROR +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ +#ifndef USE_LOCKS +#define USE_LOCKS 0 +#endif /* USE_LOCKS */ +#ifndef INSECURE +#define INSECURE 0 +#endif /* INSECURE */ +#ifndef HAVE_MMAP + #define HAVE_MMAP 0 +#endif /* HAVE_MMAP */ +#ifndef MMAP_CLEARS +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ +#ifndef HAVE_MREMAP +#ifdef linux +#define HAVE_MREMAP 1 +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ +#ifndef MALLOC_FAILURE_ACTION + #define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef HAVE_MORECORE +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ +#if !HAVE_MORECORE +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#ifndef MORECORE +#define MORECORE sbrk +#endif /* MORECORE */ +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ +#ifndef DEFAULT_GRANULARITY +#if MORECORE_CONTIGUOUS +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ +#ifndef DEFAULT_TRIM_THRESHOLD +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef DEFAULT_MMAP_THRESHOLD +#if HAVE_MMAP + #define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ + #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef USE_BUILTIN_FFS +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ +#ifndef USE_DEV_RANDOM +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ + +/* + mallopt tuning options. SVID/XPG defines four standard parameter + numbers for mallopt, normally defined in malloc.h. None of these + are used in this malloc, so setting them has no effect. But this + malloc does support the following options. +*/ + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + +namespace FMOD +{ + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any system that has a + /usr/include/malloc.h defining struct mallinfo. The main + declaration needed is the mallinfo struct that is returned (by-copy) + by mallinfo(). The malloinfo struct contains a bunch of fields that + are not even meaningful in this version of malloc. These fields are + are instead filled by mallinfo() with other numbers that might be of + interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else a compliant version is + declared below. These must be precisely the same for mallinfo() to + work. The original SVID version of this struct, defined on most + systems with mallinfo, declares all fields as ints. But some others + define as unsigned long. If your system defines the fields using a + type of different width than listed here, you MUST #include your + system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ + +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; + +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ + +//#ifdef __cplusplus +//extern "C" { +//#endif /* __cplusplus */ + +#if !ONLY_MSPACES + +/* ------------------- Declarations of public routines ------------------- */ + +#ifndef USE_DL_PREFIX +#define dlcalloc calloc +#define dlfree free +#define dlmalloc malloc +#define dlmemalign memalign +#define dlrealloc realloc +#define dlvalloc valloc +#define dlpvalloc pvalloc +#define dlmallinfo mallinfo +#define dlmallopt mallopt +#define dlmalloc_trim malloc_trim +#define dlmalloc_stats malloc_stats +#define dlmalloc_usable_size malloc_usable_size +#define dlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define dlindependent_calloc independent_calloc +#define dlindependent_comalloc independent_comalloc +#endif /* USE_DL_PREFIX */ + + +/* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. +*/ +void* dlmalloc(size_t); + +/* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cause the current program to abort. +*/ +void dlfree(void*); + +/* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. +*/ +void* dlcalloc(size_t, size_t); + +/* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. +*/ + +void* dlrealloc(void*, size_t); + +/* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. +*/ +void* dlmemalign(size_t, size_t); + +/* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. +*/ +void* dlvalloc(size_t); + +/* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (MAX_SIZE_T disables) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) +*/ +int dlmallopt(int, int); + +/* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. +*/ +size_t dlmalloc_footprint(void); + +/* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. +*/ +size_t dlmalloc_max_footprint(void); + +#if !NO_MALLINFO +/* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. +*/ +struct mallinfo dlmallinfo(void); +#endif /* NO_MALLINFO */ + +/* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use regular calloc and assign pointers into this + space to represent elements. (In this case though, you cannot + independently free elements.) + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } +*/ +void** dlindependent_calloc(size_t, size_t, void**); + +/* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be individually freed when it is no longer + needed. If you'd like to instead be able to free all at once, you + should instead use a single regular malloc, and assign pointers at + particular offsets in the aggregate space. (In this case though, you + cannot independently free elements.) + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. +*/ +void** dlindependent_comalloc(size_t, size_t*, void**); + + +/* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ +void* dlpvalloc(size_t); + +/* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. +*/ +int dlmalloc_trim(size_t); + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); +*/ +size_t dlmalloc_usable_size(void*); + +/* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. +*/ +void dlmalloc_stats(void); + +#endif /* ONLY_MSPACES */ + +#if MSPACES + +/* + mspace is an opaque type representing an independent + region of space that supports mspace_malloc, etc. +*/ +typedef void* mspace; + +/* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). +*/ +mspace create_mspace(size_t capacity, int locked); + +/* + destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. +*/ +size_t destroy_mspace(mspace msp); + +/* + create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. +*/ +mspace create_mspace_with_base(void* base, size_t capacity, int locked); + +/* + mspace_malloc behaves as malloc, but operates within + the given space. +*/ +void* mspace_malloc(mspace msp, size_t bytes); + +/* + mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_free is not actually needed. + free may be called instead of mspace_free because freed chunks from + any space are handled by their originating spaces. +*/ +void mspace_free(mspace msp, void* mem); + +/* + mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_realloc is not actually + needed. realloc may be called instead of mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. +*/ +void* mspace_realloc(mspace msp, void* mem, size_t newsize); + +/* + mspace_calloc behaves as calloc, but operates within + the given space. +*/ +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + +/* + mspace_memalign behaves as memalign, but operates within + the given space. +*/ +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); + +/* + mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. +*/ +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + +/* + mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. +*/ +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + +/* + mspace_footprint() returns the number of bytes obtained from the + system for this space. +*/ +size_t mspace_footprint(mspace msp); + +/* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. +*/ +size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO +/* + mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. +*/ +struct mallinfo mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + +/* + mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. +*/ +void mspace_malloc_stats(mspace msp); + +/* + mspace_trim behaves as malloc_trim, but + operates within the given space. +*/ +int mspace_trim(mspace msp, size_t pad); + +/* + An alias for mallopt. +*/ +int mspace_mallopt(int, int); + +#endif /* MSPACES */ + +//#ifdef __cplusplus +//} /* end of extern "C" */ +//#endif /* __cplusplus */ + + + + + + +// MOVED HERE BY FMOD + +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +struct malloc_params { + size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + + + +} // namespace FMOD + +#endif + +/* + ======================================================================== + To make a fully customizable malloc.h header file, cut everything + above this line, put into file malloc.h, edit to suit, and #include it + on the next line, as well as in programs that use this malloc. + ======================================================================== +*/ + + diff --git a/lib/flac-1.2.1/include/FLAC/all.h b/lib/flac-1.2.1/include/FLAC/all.h new file mode 100755 index 0000000..c542c0d --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/all.h @@ -0,0 +1,370 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ALL_H +#define FLAC__ALL_H + +#include "export.h" + +#include "assert.h" +#include "callback.h" +#include "format.h" +#include "metadata.h" +#include "ordinals.h" +#include "stream_decoder.h" +#include "stream_encoder.h" + +/** \mainpage + * + * \section intro Introduction + * + * This is the documentation for the FLAC C and C++ APIs. It is + * highly interconnected; this introduction should give you a top + * level idea of the structure and how to find the information you + * need. As a prerequisite you should have at least a basic + * knowledge of the FLAC format, documented + * here. + * + * \section c_api FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. The public include files will be installed + * in your include area (for example /usr/include/FLAC/...). + * + * By writing a little code and linking against libFLAC, it is + * relatively easy to add FLAC support to another program. The + * library is licensed under Xiph's BSD license. + * Complete source code of libFLAC as well as the command-line + * encoder and plugins is available and is a useful source of + * examples. + * + * Aside from encoders and decoders, libFLAC provides a powerful + * metadata interface for manipulating metadata in FLAC files. It + * allows the user to add, delete, and modify FLAC metadata blocks + * and it can automatically take advantage of PADDING blocks to avoid + * rewriting the entire FLAC file when changing the size of the + * metadata. + * + * libFLAC usually only requires the standard C library and C math + * library. In particular, threading is not used so there is no + * dependency on a thread library. However, libFLAC does not use + * global variables and should be thread-safe. + * + * libFLAC also supports encoding to and decoding from Ogg FLAC. + * However the metadata editing interfaces currently have limited + * read-only support for Ogg FLAC files. + * + * \section cpp_api FLAC C++ API + * + * The FLAC C++ API is a set of classes that encapsulate the + * structures and functions in libFLAC. They provide slightly more + * functionality with respect to metadata but are otherwise + * equivalent. For the most part, they share the same usage as + * their counterparts in libFLAC, and the FLAC C API documentation + * can be used as a supplement. The public include files + * for the C++ API will be installed in your include area (for + * example /usr/include/FLAC++/...). + * + * libFLAC++ is also licensed under + * Xiph's BSD license. + * + * \section getting_started Getting Started + * + * A good starting point for learning the API is to browse through + * the modules. Modules are logical + * groupings of related functions or classes, which correspond roughly + * to header files or sections of header files. Each module includes a + * detailed description of the general usage of its functions or + * classes. + * + * From there you can go on to look at the documentation of + * individual functions. You can see different views of the individual + * functions through the links in top bar across this page. + * + * If you prefer a more hands-on approach, you can jump right to some + * example code. + * + * \section porting_guide Porting Guide + * + * Starting with FLAC 1.1.3 a \link porting Porting Guide \endlink + * has been introduced which gives detailed instructions on how to + * port your code to newer versions of FLAC. + * + * \section embedded_developers Embedded Developers + * + * libFLAC has grown larger over time as more functionality has been + * included, but much of it may be unnecessary for a particular embedded + * implementation. Unused parts may be pruned by some simple editing of + * src/libFLAC/Makefile.am. In general, the decoders, encoders, and + * metadata interface are all independent from each other. + * + * It is easiest to just describe the dependencies: + * + * - All modules depend on the \link flac_format Format \endlink module. + * - The decoders and encoders depend on the bitbuffer. + * - The decoder is independent of the encoder. The encoder uses the + * decoder because of the verify feature, but this can be removed if + * not needed. + * - Parts of the metadata interface require the stream decoder (but not + * the encoder). + * - Ogg support is selectable through the compile time macro + * \c FLAC__HAS_OGG. + * + * For example, if your application only requires the stream decoder, no + * encoder, and no metadata interface, you can remove the stream encoder + * and the metadata interface, which will greatly reduce the size of the + * library. + * + * Also, there are several places in the libFLAC code with comments marked + * with "OPT:" where a #define can be changed to enable code that might be + * faster on a specific platform. Experimenting with these can yield faster + * binaries. + */ + +/** \defgroup porting Porting Guide for New Versions + * + * This module describes differences in the library interfaces from + * version to version. It assists in the porting of code that uses + * the libraries to newer versions of FLAC. + * + * One simple facility for making porting easier that has been added + * in FLAC 1.1.3 is a set of \c #defines in \c export.h of each + * library's includes (e.g. \c include/FLAC/export.h). The + * \c #defines mirror the libraries' + * libtool version numbers, + * e.g. in libFLAC there are \c FLAC_API_VERSION_CURRENT, + * \c FLAC_API_VERSION_REVISION, and \c FLAC_API_VERSION_AGE. + * These can be used to support multiple versions of an API during the + * transition phase, e.g. + * + * \code + * #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 + * legacy code + * #else + * new code + * #endif + * \endcode + * + * The the source will work for multiple versions and the legacy code can + * easily be removed when the transition is complete. + * + * Another available symbol is FLAC_API_SUPPORTS_OGG_FLAC (defined in + * include/FLAC/export.h), which can be used to determine whether or not + * the library has been compiled with support for Ogg FLAC. This is + * simpler than trying to call an Ogg init function and catching the + * error. + */ + +/** \defgroup porting_1_1_2_to_1_1_3 Porting from FLAC 1.1.2 to 1.1.3 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.2 to FLAC 1.1.3. + * + * The main change between the APIs in 1.1.2 and 1.1.3 is that they have + * been simplified. First, libOggFLAC has been merged into libFLAC and + * libOggFLAC++ has been merged into libFLAC++. Second, both the three + * decoding layers and three encoding layers have been merged into a + * single stream decoder and stream encoder. That is, the functionality + * of FLAC__SeekableStreamDecoder and FLAC__FileDecoder has been merged + * into FLAC__StreamDecoder, and FLAC__SeekableStreamEncoder and + * FLAC__FileEncoder into FLAC__StreamEncoder. Only the + * FLAC__StreamDecoder and FLAC__StreamEncoder remain. What this means + * is there is now a single API that can be used to encode or decode + * streams to/from native FLAC or Ogg FLAC and the single API can work + * on both seekable and non-seekable streams. + * + * Instead of creating an encoder or decoder of a certain layer, now the + * client will always create a FLAC__StreamEncoder or + * FLAC__StreamDecoder. The old layers are now differentiated by the + * initialization function. For example, for the decoder, + * FLAC__stream_decoder_init() has been replaced by + * FLAC__stream_decoder_init_stream(). This init function takes + * callbacks for the I/O, and the seeking callbacks are optional. This + * allows the client to use the same object for seekable and + * non-seekable streams. For decoding a FLAC file directly, the client + * can use FLAC__stream_decoder_init_file() and pass just a filename + * and fewer callbacks; most of the other callbacks are supplied + * internally. For situations where fopen()ing by filename is not + * possible (e.g. Unicode filenames on Windows) the client can instead + * open the file itself and supply the FILE* to + * FLAC__stream_decoder_init_FILE(). The init functions now returns a + * FLAC__StreamDecoderInitStatus instead of FLAC__StreamDecoderState. + * Since the callbacks and client data are now passed to the init + * function, the FLAC__stream_decoder_set_*_callback() functions and + * FLAC__stream_decoder_set_client_data() are no longer needed. The + * rest of the calls to the decoder are the same as before. + * + * There are counterpart init functions for Ogg FLAC, e.g. + * FLAC__stream_decoder_init_ogg_stream(). All the rest of the calls + * and callbacks are the same as for native FLAC. + * + * As an example, in FLAC 1.1.2 a seekable stream decoder would have + * been set up like so: + * + * \code + * FLAC__SeekableStreamDecoder *decoder = FLAC__seekable_stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__seekable_stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * FLAC__seekable_stream_decoder_set_read_callback(decoder, my_read_callback); + * FLAC__seekable_stream_decoder_set_seek_callback(decoder, my_seek_callback); + * FLAC__seekable_stream_decoder_set_tell_callback(decoder, my_tell_callback); + * FLAC__seekable_stream_decoder_set_length_callback(decoder, my_length_callback); + * FLAC__seekable_stream_decoder_set_eof_callback(decoder, my_eof_callback); + * FLAC__seekable_stream_decoder_set_write_callback(decoder, my_write_callback); + * FLAC__seekable_stream_decoder_set_metadata_callback(decoder, my_metadata_callback); + * FLAC__seekable_stream_decoder_set_error_callback(decoder, my_error_callback); + * FLAC__seekable_stream_decoder_set_client_data(decoder, my_client_data); + * if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) do_something; + * \endcode + * + * In FLAC 1.1.3 it is like this: + * + * \code + * FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * if(FLAC__stream_decoder_init_stream( + * decoder, + * my_read_callback, + * my_seek_callback, // or NULL + * my_tell_callback, // or NULL + * my_length_callback, // or NULL + * my_eof_callback, // or NULL + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or you could do; + * + * \code + * [...] + * FILE *file = fopen("somefile.flac","rb"); + * if(file == NULL) do_somthing; + * if(FLAC__stream_decoder_init_FILE( + * decoder, + * file, + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or just: + * + * \code + * [...] + * if(FLAC__stream_decoder_init_file( + * decoder, + * "somefile.flac", + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * Another small change to the decoder is in how it handles unparseable + * streams. Before, when the decoder found an unparseable stream + * (reserved for when the decoder encounters a stream from a future + * encoder that it can't parse), it changed the state to + * \c FLAC__STREAM_DECODER_UNPARSEABLE_STREAM. Now the decoder instead + * drops sync and calls the error callback with a new error code + * \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM. This is + * more robust. If your error callback does not discriminate on the the + * error state, your code does not need to be changed. + * + * The encoder now has a new setting: + * FLAC__stream_encoder_set_apodization(). This is for setting the + * method used to window the data before LPC analysis. You only need to + * add a call to this function if the default is not suitable. There + * are also two new convenience functions that may be useful: + * FLAC__metadata_object_cuesheet_calculate_cddb_id() and + * FLAC__metadata_get_cuesheet(). + * + * The \a bytes parameter to FLAC__StreamDecoderReadCallback, + * FLAC__StreamEncoderReadCallback, and FLAC__StreamEncoderWriteCallback + * is now \c size_t instead of \c unsigned. + */ + +/** \defgroup porting_1_1_3_to_1_1_4 Porting from FLAC 1.1.3 to 1.1.4 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.3 to FLAC 1.1.4. + * + * There were no changes to any of the interfaces from 1.1.3 to 1.1.4. + * There was a slight change in the implementation of + * FLAC__stream_encoder_set_metadata(); the function now makes a copy + * of the \a metadata array of pointers so the client no longer needs + * to maintain it after the call. The objects themselves that are + * pointed to by the array are still not copied though and must be + * maintained until the call to FLAC__stream_encoder_finish(). + */ + +/** \defgroup porting_1_1_4_to_1_2_0 Porting from FLAC 1.1.4 to 1.2.0 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.4 to FLAC 1.2.0. + * + * There were only very minor changes to the interfaces from 1.1.4 to 1.2.0. + * In libFLAC, \c FLAC__format_sample_rate_is_subset() was added. + * In libFLAC++, \c FLAC::Decoder::Stream::get_decode_position() was added. + * + * Finally, value of the constant \c FLAC__FRAME_HEADER_RESERVED_LEN + * has changed to reflect the conversion of one of the reserved bits + * into active use. It used to be \c 2 and now is \c 1. However the + * FLAC frame header length has not changed, so to skip the proper + * number of bits, use \c FLAC__FRAME_HEADER_RESERVED_LEN + + * \c FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN + */ + +/** \defgroup flac FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. + * + * You should start with the format components as all other modules + * are dependent on it. + */ + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/assert.h b/lib/flac-1.2.1/include/FLAC/assert.h new file mode 100755 index 0000000..3fc03f3 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/assert.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ASSERT_H +#define FLAC__ASSERT_H + +/* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ +#ifdef DEBUG +#include +#define FLAC__ASSERT(x) assert(x) +#define FLAC__ASSERT_DECLARATION(x) x +#else +#define FLAC__ASSERT(x) +#define FLAC__ASSERT_DECLARATION(x) +#endif + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/callback.h b/lib/flac-1.2.1/include/FLAC/callback.h new file mode 100755 index 0000000..c954121 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/callback.h @@ -0,0 +1,184 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__CALLBACK_H +#define FLAC__CALLBACK_H + +#include "ordinals.h" +#include /* for size_t */ + +/** \file include/FLAC/callback.h + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * See the detailed documentation for callbacks in the + * \link flac_callbacks callbacks \endlink module. + */ + +/** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures + * \ingroup flac + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * The purpose of the I/O callback functions is to create a common way + * for the metadata interfaces to handle I/O. + * + * Originally the metadata interfaces required filenames as the way of + * specifying FLAC files to operate on. This is problematic in some + * environments so there is an additional option to specify a set of + * callbacks for doing I/O on the FLAC file, instead of the filename. + * + * In addition to the callbacks, a FLAC__IOHandle type is defined as an + * opaque structure for a data source. + * + * The callback function prototypes are similar (but not identical) to the + * stdio functions fread, fwrite, fseek, ftell, feof, and fclose. If you use + * stdio streams to implement the callbacks, you can pass fread, fwrite, and + * fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or + * FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle + * is required. \warning You generally CANNOT directly use fseek or ftell + * for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems + * these use 32-bit offsets and FLAC requires 64-bit offsets to deal with + * large files. You will have to find an equivalent function (e.g. ftello), + * or write a wrapper. The same is true for feof() since this is usually + * implemented as a macro, not as a function whose address can be taken. + * + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the opaque handle type used by the callbacks. Typically + * this is a \c FILE* or address of a file descriptor. + */ +typedef void* FLAC__IOHandle; + +/** Signature for the read callback. + * The signature and semantics match POSIX fread() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the read buffer. + * \param size The size of the records to be read. + * \param nmemb The number of records to be read. + * \param handle The handle to the data source. + * \retval size_t + * The number of records read. + */ +typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the write callback. + * The signature and semantics match POSIX fwrite() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the write buffer. + * \param size The size of the records to be written. + * \param nmemb The number of records to be written. + * \param handle The handle to the data source. + * \retval size_t + * The number of records written. + */ +typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the seek callback. + * The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \param offset The new position, relative to \a whence + * \param whence \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END + * \retval int + * \c 0 on success, \c -1 on error. + */ +typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence); + +/** Signature for the tell callback. + * The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \retval FLAC__int64 + * The current position on success, \c -1 on error. + */ +typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle); + +/** Signature for the EOF callback. + * The signature and semantics mostly match POSIX feof() but WATCHOUT: + * on many systems, feof() is a macro, so in this case a wrapper function + * must be provided instead. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 if not at end of file, nonzero if at end of file. + */ +typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle); + +/** Signature for the close callback. + * The signature and semantics match POSIX fclose() implementations + * and can generally be used interchangeably. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 on success, \c EOF on error. + */ +typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); + +/** A structure for holding a set of callbacks. + * Each FLAC interface that requires a FLAC__IOCallbacks structure will + * describe which of the callbacks are required. The ones that are not + * required may be set to NULL. + * + * If the seek requirement for an interface is optional, you can signify that + * a data sorce is not seekable by setting the \a seek field to \c NULL. + */ +typedef struct { + FLAC__IOCallback_Read read; + FLAC__IOCallback_Write write; + FLAC__IOCallback_Seek seek; + FLAC__IOCallback_Tell tell; + FLAC__IOCallback_Eof eof; + FLAC__IOCallback_Close close; +} FLAC__IOCallbacks; + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/export.h b/lib/flac-1.2.1/include/FLAC/export.h new file mode 100755 index 0000000..a525f29 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/export.h @@ -0,0 +1,91 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__EXPORT_H +#define FLAC__EXPORT_H + +/** \file include/FLAC/export.h + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * See the \link flac_export export \endlink module. + */ + +/** \defgroup flac_export FLAC/export.h: export symbols + * \ingroup flac + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * If you are compiling with MSVC and will link to the static library + * (libFLAC.lib) you should define FLAC__NO_DLL in your project to + * make sure the symbols are exported properly. + * + * \{ + */ + +#if defined(FLAC__NO_DLL) || !defined(_MSC_VER) +#define FLAC_API + +#else + +#ifdef FLAC_API_EXPORTS +#define FLAC_API _declspec(dllexport) +#else +#define FLAC_API _declspec(dllimport) + +#endif +#endif + +/** These #defines will mirror the libtool-based library version number, see + * http://www.gnu.org/software/libtool/manual.html#Libtool-versioning + */ +#define FLAC_API_VERSION_CURRENT 10 +#define FLAC_API_VERSION_REVISION 0 /**< see above */ +#define FLAC_API_VERSION_AGE 2 /**< see above */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \c 1 if the library has been compiled with support for Ogg FLAC, else \c 0. */ +extern FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC; + +#ifdef __cplusplus +} +#endif + +/* \} */ + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/format.h b/lib/flac-1.2.1/include/FLAC/format.h new file mode 100755 index 0000000..77e2d01 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/format.h @@ -0,0 +1,1010 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__FORMAT_H +#define FLAC__FORMAT_H + +#include "export.h" +#include "ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/format.h + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * See the detailed documentation in the + * \link flac_format format \endlink module. + */ + +/** \defgroup flac_format FLAC/format.h: format components + * \ingroup flac + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * First, you should be familiar with the + * FLAC format. Many of the values here + * follow directly from the specification. As a user of libFLAC, the + * interesting parts really are the structures that describe the frame + * header and metadata blocks. + * + * The format structures here are very primitive, designed to store + * information in an efficient way. Reading information from the + * structures is easy but creating or modifying them directly is + * more complex. For the most part, as a user of a library, editing + * is not necessary; however, for metadata blocks it is, so there are + * convenience functions provided in the \link flac_metadata metadata + * module \endlink to simplify the manipulation of metadata blocks. + * + * \note + * It's not the best convention, but symbols ending in _LEN are in bits + * and _LENGTH are in bytes. _LENGTH symbols are \#defines instead of + * global variables because they are usually used when declaring byte + * arrays and some compilers require compile-time knowledge of array + * sizes when declared on the stack. + * + * \{ + */ + + +/* + Most of the values described in this file are defined by the FLAC + format specification. There is nothing to tune here. +*/ + +/** The largest legal metadata type code. */ +#define FLAC__MAX_METADATA_TYPE_CODE (126u) + +/** The minimum block size, in samples, permitted by the format. */ +#define FLAC__MIN_BLOCK_SIZE (16u) + +/** The maximum block size, in samples, permitted by the format. */ +#define FLAC__MAX_BLOCK_SIZE (65535u) + +/** The maximum block size, in samples, permitted by the FLAC subset for + * sample rates up to 48kHz. */ +#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u) + +/** The maximum number of channels permitted by the format. */ +#define FLAC__MAX_CHANNELS (8u) + +/** The minimum sample resolution permitted by the format. */ +#define FLAC__MIN_BITS_PER_SAMPLE (4u) + +/** The maximum sample resolution permitted by the format. */ +#define FLAC__MAX_BITS_PER_SAMPLE (32u) + +/** The maximum sample resolution permitted by libFLAC. + * + * \warning + * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, + * the reference encoder/decoder is currently limited to 24 bits because + * of prevalent 32-bit math, so make sure and use this value when + * appropriate. + */ +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) + +/** The maximum sample rate permitted by the format. The value is + * ((2 ^ 16) - 1) * 10; see FLAC format + * as to why. + */ +#define FLAC__MAX_SAMPLE_RATE (655350u) + +/** The maximum LPC order permitted by the format. */ +#define FLAC__MAX_LPC_ORDER (32u) + +/** The maximum LPC order permitted by the FLAC subset for sample rates + * up to 48kHz. */ +#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u) + +/** The minimum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MIN_QLP_COEFF_PRECISION (5u) + +/** The maximum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MAX_QLP_COEFF_PRECISION (15u) + +/** The maximum order of the fixed predictors permitted by the format. */ +#define FLAC__MAX_FIXED_ORDER (4u) + +/** The maximum Rice partition order permitted by the format. */ +#define FLAC__MAX_RICE_PARTITION_ORDER (15u) + +/** The maximum Rice partition order permitted by the FLAC Subset. */ +#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u) + +/** The version string of the release, stamped onto the libraries and binaries. + * + * \note + * This does not correspond to the shared library version number, which + * is used to determine binary compatibility. + */ +extern FLAC_API const char *FLAC__VERSION_STRING; + +/** The vendor string inserted by the encoder into the VORBIS_COMMENT block. + * This is a NUL-terminated ASCII string; when inserted into the + * VORBIS_COMMENT the trailing null is stripped. + */ +extern FLAC_API const char *FLAC__VENDOR_STRING; + +/** The byte string representation of the beginning of a FLAC stream. */ +extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ + +/** The 32-bit integer big-endian representation of the beginning of + * a FLAC stream. + */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */ + +/** The length of the FLAC signature in bits. */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */ + +/** The length of the FLAC signature in bytes. */ +#define FLAC__STREAM_SYNC_LENGTH (4u) + + +/***************************************************************************** + * + * Subframe structures + * + *****************************************************************************/ + +/*****************************************************************************/ + +/** An enumeration of the available entropy coding methods. */ +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0, + /**< Residual is coded by partitioning into contexts, each with it's own + * 4-bit Rice parameter. */ + + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 + /**< Residual is coded by partitioning into contexts, each with it's own + * 5-bit Rice parameter. */ +} FLAC__EntropyCodingMethodType; + +/** Maps a FLAC__EntropyCodingMethodType to a C string. + * + * Using a FLAC__EntropyCodingMethodType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; + + +/** Contents of a Rice partitioned residual + */ +typedef struct { + + unsigned *parameters; + /**< The Rice parameters for each context. */ + + unsigned *raw_bits; + /**< Widths for escape-coded partitions. Will be non-zero for escaped + * partitions and zero for unescaped partitions. + */ + + unsigned capacity_by_order; + /**< The capacity of the \a parameters and \a raw_bits arrays + * specified as an order, i.e. the number of array elements + * allocated is 2 ^ \a capacity_by_order. + */ +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +/** Header for a Rice partitioned residual. (c.f. format specification) + */ +typedef struct { + + unsigned order; + /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ + + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; + /**< The context's Rice parameters and/or raw bits. */ + +} FLAC__EntropyCodingMethod_PartitionedRice; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +/**< == (1<format specification) + */ +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ + +/*****************************************************************************/ + +/** An enumeration of the available subframe types. */ +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +/** Maps a FLAC__SubframeType to a C string. + * + * Using a FLAC__SubframeType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__SubframeTypeString[]; + + +/** CONSTANT subframe. (c.f. format specification) + */ +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + + +/** VERBATIM subframe. (c.f. format specification) + */ +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + + +/** FIXED subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The polynomial order. */ + + FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_Fixed; + + +/** LPC subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The FIR order. */ + + unsigned qlp_coeff_precision; + /**< Quantized FIR filter coefficient precision in bits. */ + + int quantization_level; + /**< The qlp coeff shift needed. */ + + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + /**< FIR filter coefficients. */ + + FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_LPC; + +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ + + +/** FLAC subframe structure. (c.f. format specification) + */ +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + unsigned wasted_bits; +} FLAC__Subframe; + +/** == 1 (bit) + * + * This used to be a zero-padding bit (hence the name + * FLAC__SUBFRAME_ZERO_PAD_LEN) but is now a reserved bit. It still has a + * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1 + * to mean something else. + */ +extern FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN; +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ + +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Frame structures + * + *****************************************************************************/ + +/** An enumeration of the available channel assignments. */ +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +/** Maps a FLAC__ChannelAssignment to a C string. + * + * Using a FLAC__ChannelAssignment as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__ChannelAssignmentString[]; + +/** An enumeration of the possible frame numbering methods. */ +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +/** Maps a FLAC__FrameNumberType to a C string. + * + * Using a FLAC__FrameNumberType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; + + +/** FLAC frame header structure. (c.f. format specification) + */ +typedef struct { + unsigned blocksize; + /**< The number of samples per subframe. */ + + unsigned sample_rate; + /**< The sample rate in Hz. */ + + unsigned channels; + /**< The number of channels (== number of subframes). */ + + FLAC__ChannelAssignment channel_assignment; + /**< The channel assignment for the frame. */ + + unsigned bits_per_sample; + /**< The sample resolution. */ + + FLAC__FrameNumberType number_type; + /**< The numbering scheme used for the frame. As a convenience, the + * decoder will always convert a frame number to a sample number because + * the rules are complex. */ + + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + /**< The frame number or sample number of first sample in frame; + * use the \a number_type value to determine which to use. */ + + FLAC__uint8 crc; + /**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + * of the raw frame header bytes, meaning everything before the CRC byte + * including the sync code. + */ +} FLAC__FrameHeader; + +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ + + +/** FLAC frame footer structure. (c.f. format specification) + */ +typedef struct { + FLAC__uint16 crc; + /**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with + * 0) of the bytes before the crc, back to and including the frame header + * sync code. + */ +} FLAC__FrameFooter; + +extern FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ + + +/** FLAC frame structure. (c.f. format specification) + */ +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[FLAC__MAX_CHANNELS]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Meta-data structures + * + *****************************************************************************/ + +/** An enumeration of the available metadata block types. */ +typedef enum { + + FLAC__METADATA_TYPE_STREAMINFO = 0, + /**< STREAMINFO block */ + + FLAC__METADATA_TYPE_PADDING = 1, + /**< PADDING block */ + + FLAC__METADATA_TYPE_APPLICATION = 2, + /**< APPLICATION block */ + + FLAC__METADATA_TYPE_SEEKTABLE = 3, + /**< SEEKTABLE block */ + + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ + + FLAC__METADATA_TYPE_CUESHEET = 5, + /**< CUESHEET block */ + + FLAC__METADATA_TYPE_PICTURE = 6, + /**< PICTURE block */ + + FLAC__METADATA_TYPE_UNDEFINED = 7 + /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + +} FLAC__MetadataType; + +/** Maps a FLAC__MetadataType to a C string. + * + * Using a FLAC__MetadataType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__MetadataTypeString[]; + + +/** FLAC STREAMINFO structure. (c.f. format specification) + */ +typedef struct { + unsigned min_blocksize, max_blocksize; + unsigned min_framesize, max_framesize; + unsigned sample_rate; + unsigned channels; + unsigned bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ + +/** The total stream length of the STREAMINFO block in bytes. */ +#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) + +/** FLAC PADDING structure. (c.f. format specification) + */ +typedef struct { + int dummy; + /**< Conceptually this is an empty struct since we don't store the + * padding bytes. Empty structs are not allowed by some C compilers, + * hence the dummy. + */ +} FLAC__StreamMetadata_Padding; + + +/** FLAC APPLICATION structure. (c.f. format specification) + */ +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ + +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) + */ +typedef struct { + FLAC__uint64 sample_number; + /**< The sample number of the target frame. */ + + FLAC__uint64 stream_offset; + /**< The offset, in bytes, of the target frame with respect to + * beginning of the first frame. */ + + unsigned frame_samples; + /**< The number of samples in the target frame. */ +} FLAC__StreamMetadata_SeekPoint; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ + +/** The total stream length of a seek point in bytes. */ +#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) + +/** The value used in the \a sample_number field of + * FLAC__StreamMetadataSeekPoint used to indicate a placeholder + * point (== 0xffffffffffffffff). + */ +extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + + +/** FLAC SEEKTABLE structure. (c.f. format specification) + * + * \note From the format specification: + * - The seek points must be sorted by ascending sample number. + * - Each seek point's sample number must be the first sample of the + * target frame. + * - Each seek point's sample number must be unique within the table. + * - Existence of a SEEKTABLE block implies a correct setting of + * total_samples in the stream_info block. + * - Behavior is undefined when more than one SEEKTABLE block is + * present in a stream. + */ +typedef struct { + unsigned num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + + +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length, i.e. + * \code strlen(entry) == length \endcode + */ +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ + + +/** FLAC VORBIS_COMMENT structure. (c.f. format specification) + */ +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ + + +/** FLAC CUESHEET track index structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Offset in samples, relative to the track offset, of the index + * point. + */ + + FLAC__byte number; + /**< The index point number. */ +} FLAC__StreamMetadata_CueSheet_Index; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ + + +/** FLAC CUESHEET track structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Track offset in samples, relative to the beginning of the FLAC audio stream. */ + + FLAC__byte number; + /**< The track number. */ + + char isrc[13]; + /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ + + unsigned type:1; + /**< The track type: 0 for audio, 1 for non-audio. */ + + unsigned pre_emphasis:1; + /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ + + FLAC__byte num_indices; + /**< The number of track index points. */ + + FLAC__StreamMetadata_CueSheet_Index *indices; + /**< NULL if num_indices == 0, else pointer to array of index points. */ + +} FLAC__StreamMetadata_CueSheet_Track; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ + + +/** FLAC CUESHEET structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + char media_catalog_number[129]; + /**< Media catalog number, in ASCII printable characters 0x20-0x7e. In + * general, the media catalog number may be 0 to 128 bytes long; any + * unused characters should be right-padded with NUL characters. + */ + + FLAC__uint64 lead_in; + /**< The number of lead-in samples. */ + + FLAC__bool is_cd; + /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ + + unsigned num_tracks; + /**< The number of tracks. */ + + FLAC__StreamMetadata_CueSheet_Track *tracks; + /**< NULL if num_tracks == 0, else pointer to array of tracks. */ + +} FLAC__StreamMetadata_CueSheet; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ + + +/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +/** Maps a FLAC__StreamMetadata_Picture_Type to a C string. + * + * Using a FLAC__StreamMetadata_Picture_Type as the index to this array + * will give the string equivalent. The contents should not be + * modified. + */ +extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; + +/** FLAC PICTURE structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + /**< The kind of picture stored. */ + + char *mime_type; + /**< Picture data's MIME type, in ASCII printable characters + * 0x20-0x7e, NUL terminated. For best compatibility with players, + * use picture data of MIME type \c image/jpeg or \c image/png. A + * MIME type of '-->' is also allowed, in which case the picture + * data should be a complete URL. In file storage, the MIME type is + * stored as a 32-bit length followed by the ASCII string with no NUL + * terminator, but is converted to a plain C string in this structure + * for convenience. + */ + + FLAC__byte *description; + /**< Picture's description in UTF-8, NUL terminated. In file storage, + * the description is stored as a 32-bit length followed by the UTF-8 + * string with no NUL terminator, but is converted to a plain C string + * in this structure for convenience. + */ + + FLAC__uint32 width; + /**< Picture's width in pixels. */ + + FLAC__uint32 height; + /**< Picture's height in pixels. */ + + FLAC__uint32 depth; + /**< Picture's color depth in bits-per-pixel. */ + + FLAC__uint32 colors; + /**< For indexed palettes (like GIF), picture's number of colors (the + * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth). + */ + + FLAC__uint32 data_length; + /**< Length of binary picture data in bytes. */ + + FLAC__byte *data; + /**< Binary picture data. */ + +} FLAC__StreamMetadata_Picture; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ + + +/** Structure that is used when a metadata block of unknown type is loaded. + * The contents are opaque. The structure is used only internally to + * correctly handle unknown metadata. + */ +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + + +/** FLAC metadata block structure. (c.f. format specification) + */ +typedef struct { + FLAC__MetadataType type; + /**< The type of the metadata block; used determine which member of the + * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED + * then \a data.unknown must be used. */ + + FLAC__bool is_last; + /**< \c true if this metadata block is the last, else \a false */ + + unsigned length; + /**< Length, in bytes, of the block data as it appears in the stream. */ + + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; + /**< Polymorphic block data; use the \a type value to determine which + * to use. */ +} FLAC__StreamMetadata; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ + +/** The total stream length of a metadata block header in bytes. */ +#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Utility functions + * + *****************************************************************************/ + +/** Tests that a sample rate is valid for FLAC. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification, else + * \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); + +/** Tests that a sample rate is valid for the FLAC subset. The subset rules + * for valid sample rates are slightly more complex since the rate has to + * be expressible completely in the frame header. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification for the + * subset, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate); + +/** Check a Vorbis comment entry name to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment names must be composed only of characters from + * [0x20-0x3C,0x3E-0x7D]. + * + * \param name A NUL-terminated string to be checked. + * \assert + * \code name != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name); + +/** Check a Vorbis comment entry value to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment values must be valid UTF-8 sequences. + * + * \param value A string to be checked. + * \param length A the length of \a value in bytes. May be + * \c (unsigned)(-1) to indicate that \a value is a plain + * UTF-8 NUL-terminated string. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length); + +/** Check a Vorbis comment entry to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment entries must be of the form 'name=value', and 'name' and + * 'value' must be legal according to + * FLAC__format_vorbiscomment_entry_name_is_legal() and + * FLAC__format_vorbiscomment_entry_value_is_legal() respectively. + * + * \param entry An entry to be checked. + * \param length The length of \a entry in bytes. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length); + +/** Check a seek table to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seek table. + * + * \param seek_table A pointer to a seek table to be checked. + * \assert + * \code seek_table != NULL \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table); + +/** Sort a seek table's seek points according to the format specification. + * This includes a "unique-ification" step to remove duplicates, i.e. + * seek points with identical \a sample_number values. Duplicate seek + * points are converted into placeholder points and sorted to the end of + * the table. + * + * \param seek_table A pointer to a seek table to be sorted. + * \assert + * \code seek_table != NULL \endcode + * \retval unsigned + * The number of duplicate seek points converted into placeholders. + */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param cue_sheet A pointer to an existing cue sheet to be checked. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code cue_sheet != NULL \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation); + +/** Check picture data to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param picture A pointer to existing picture data to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c false if picture data is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/metadata.h b/lib/flac-1.2.1/include/FLAC/metadata.h new file mode 100755 index 0000000..d207626 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/metadata.h @@ -0,0 +1,2181 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__METADATA_H +#define FLAC__METADATA_H + +#include /* for off_t */ +#include "export.h" +#include "callback.h" +#include "format.h" + +/* -------------------------------------------------------------------- + (For an example of how all these routines are used, see the source + code for the unit tests in src/test_libFLAC/metadata_*.c, or + metaflac in src/metaflac/) + ------------------------------------------------------------------*/ + +/** \file include/FLAC/metadata.h + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in FLAC files. + * + * See the detailed documentation for each interface in the + * \link flac_metadata metadata \endlink module. + */ + +/** \defgroup flac_metadata FLAC/metadata.h: metadata interfaces + * \ingroup flac + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in native FLAC files. + * Note that currently only the Chain interface (level 2) supports Ogg + * FLAC files, and it is read-only i.e. no writing back changed + * metadata to file. + * + * There are three metadata interfaces of increasing complexity: + * + * Level 0: + * Read-only access to the STREAMINFO, VORBIS_COMMENT, CUESHEET, and + * PICTURE blocks. + * + * Level 1: + * Read-write access to all metadata blocks. This level is write- + * efficient in most cases (more on this below), and uses less memory + * than level 2. + * + * Level 2: + * Read-write access to all metadata blocks. This level is write- + * efficient in all cases, but uses more memory since all metadata for + * the whole file is read into memory and manipulated before writing + * out again. + * + * What do we mean by efficient? Since FLAC metadata appears at the + * beginning of the file, when writing metadata back to a FLAC file + * it is possible to grow or shrink the metadata such that the entire + * file must be rewritten. However, if the size remains the same during + * changes or PADDING blocks are utilized, only the metadata needs to be + * overwritten, which is much faster. + * + * Efficient means the whole file is rewritten at most one time, and only + * when necessary. Level 1 is not efficient only in the case that you + * cause more than one metadata block to grow or shrink beyond what can + * be accomodated by padding. In this case you should probably use level + * 2, which allows you to edit all the metadata for a file in memory and + * write it out all at once. + * + * All levels know how to skip over and not disturb an ID3v2 tag at the + * front of the file. + * + * All levels access files via their filenames. In addition, level 2 + * has additional alternative read and write functions that take an I/O + * handle and callbacks, for situations where access by filename is not + * possible. + * + * In addition to the three interfaces, this module defines functions for + * creating and manipulating various metadata objects in memory. As we see + * from the Format module, FLAC metadata blocks in memory are very primitive + * structures for storing information in an efficient way. Reading + * information from the structures is easy but creating or modifying them + * directly is more complex. The metadata object routines here facilitate + * this by taking care of the consistency and memory management drudgery. + * + * Unless you will be using the level 1 or 2 interfaces to modify existing + * metadata however, you will not probably not need these. + * + * From a dependency standpoint, none of the encoders or decoders require + * the metadata module. This is so that embedded users can strip out the + * metadata module from libFLAC to reduce the size and complexity. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup flac_metadata_level0 FLAC/metadata.h: metadata level 0 interface + * \ingroup flac_metadata + * + * \brief + * The level 0 interface consists of individual routines to read the + * STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring + * only a filename. + * + * They try to skip any ID3v2 tag at the head of the file. + * + * \{ + */ + +/** Read the STREAMINFO metadata block of the given FLAC file. This function + * will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param streaminfo A pointer to space for the STREAMINFO block. Since + * FLAC__StreamMetadata is a simple structure with no + * memory allocation involved, you pass the address of + * an existing structure. It need not be initialized. + * \assert + * \code filename != NULL \endcode + * \code streaminfo != NULL \endcode + * \retval FLAC__bool + * \c true if a valid STREAMINFO block was read from \a filename. Returns + * \c false if there was a memory allocation error, a file decoder error, + * or the file contained no STREAMINFO block. (A memory allocation error + * is possible because this function must set up a file decoder.) + */ +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(void *context, const char *filename, FLAC__StreamMetadata *streaminfo); + +/** Read the VORBIS_COMMENT metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param tags The address where the returned pointer will be + * stored. The \a tags object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code tags != NULL \endcode + * \retval FLAC__bool + * \c true if a valid VORBIS_COMMENT block was read from \a filename, + * and \a *tags will be set to the address of the metadata structure. + * Returns \c false if there was a memory allocation error, a file + * decoder error, or the file contained no VORBIS_COMMENT block, and + * \a *tags will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_tags(void *context, const char *filename, FLAC__StreamMetadata **tags); + +/** Read the CUESHEET metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param cuesheet The address where the returned pointer will be + * stored. The \a cuesheet object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code cuesheet != NULL \endcode + * \retval FLAC__bool + * \c true if a valid CUESHEET block was read from \a filename, + * and \a *cuesheet will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no CUESHEET + * block, and \a *cuesheet will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(void *context, const char *filename, FLAC__StreamMetadata **cuesheet); + +/** Read a PICTURE metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * Since there can be more than one PICTURE block in a file, this + * function takes a number of parameters that act as constraints to + * the search. The PICTURE block with the largest area matching all + * the constraints will be returned, or \a *picture will be set to + * \c NULL if there was no such block. + * + * \param filename The path to the FLAC file to read. + * \param picture The address where the returned pointer will be + * stored. The \a picture object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \param type The desired picture type. Use \c -1 to mean + * "any type". + * \param mime_type The desired MIME type, e.g. "image/jpeg". The + * string will be matched exactly. Use \c NULL to + * mean "any MIME type". + * \param description The desired description. The string will be + * matched exactly. Use \c NULL to mean "any + * description". + * \param max_width The maximum width in pixels desired. Use + * \c (unsigned)(-1) to mean "any width". + * \param max_height The maximum height in pixels desired. Use + * \c (unsigned)(-1) to mean "any height". + * \param max_depth The maximum color depth in bits-per-pixel desired. + * Use \c (unsigned)(-1) to mean "any depth". + * \param max_colors The maximum number of colors desired. Use + * \c (unsigned)(-1) to mean "any number of colors". + * \assert + * \code filename != NULL \endcode + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c true if a valid PICTURE block was read from \a filename, + * and \a *picture will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no PICTURE + * block, and \a *picture will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors); + +/* \} */ + + +/** \defgroup flac_metadata_level1 FLAC/metadata.h: metadata level 1 interface + * \ingroup flac_metadata + * + * \brief + * The level 1 interface provides read-write access to FLAC file metadata and + * operates directly on the FLAC file. + * + * The general usage of this interface is: + * + * - Create an iterator using FLAC__metadata_simple_iterator_new() + * - Attach it to a file using FLAC__metadata_simple_iterator_init() and check + * the exit code. Call FLAC__metadata_simple_iterator_is_writable() to + * see if the file is writable, or only read access is allowed. + * - Use FLAC__metadata_simple_iterator_next() and + * FLAC__metadata_simple_iterator_prev() to traverse the blocks. + * This is does not read the actual blocks themselves. + * FLAC__metadata_simple_iterator_next() is relatively fast. + * FLAC__metadata_simple_iterator_prev() is slower since it needs to search + * forward from the front of the file. + * - Use FLAC__metadata_simple_iterator_get_block_type() or + * FLAC__metadata_simple_iterator_get_block() to access the actual data at + * the current iterator position. The returned object is yours to modify + * and free. + * - Use FLAC__metadata_simple_iterator_set_block() to write a modified block + * back. You must have write permission to the original file. Make sure to + * read the whole comment to FLAC__metadata_simple_iterator_set_block() + * below. + * - Use FLAC__metadata_simple_iterator_insert_block_after() to add new blocks. + * Use the object creation functions from + * \link flac_metadata_object here \endlink to generate new objects. + * - Use FLAC__metadata_simple_iterator_delete_block() to remove the block + * currently referred to by the iterator, or replace it with padding. + * - Destroy the iterator with FLAC__metadata_simple_iterator_delete() when + * finished. + * + * \note + * The FLAC file remains open the whole time between + * FLAC__metadata_simple_iterator_init() and + * FLAC__metadata_simple_iterator_delete(), so make sure you are not altering + * the file during this time. + * + * \note + * Do not modify the \a is_last, \a length, or \a type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * If any of the modification functions + * (FLAC__metadata_simple_iterator_set_block(), + * FLAC__metadata_simple_iterator_delete_block(), + * FLAC__metadata_simple_iterator_insert_block_after(), etc.) return \c false, + * you should delete the iterator as it may no longer be valid. + * + * \{ + */ + +struct FLAC__Metadata_SimpleIterator; +/** The opaque structure definition for the level 1 iterator type. + * See the + * \link flac_metadata_level1 metadata level 1 module \endlink + * for a detailed description. + */ +typedef struct FLAC__Metadata_SimpleIterator FLAC__Metadata_SimpleIterator; + +/** Status type for FLAC__Metadata_SimpleIterator. + * + * The iterator's current status can be obtained by calling FLAC__metadata_simple_iterator_status(). + */ +typedef enum { + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0, + /**< The iterator is in the normal OK state */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE, + /**< The iterator could not open the target file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE, + /**< The iterator could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE, + /**< The iterator tried to write to a file that was not writable */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA, + /**< The iterator encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR, + /**< The iterator encountered an error while reading the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, + /**< The iterator encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR, + /**< The iterator encountered an error while writing the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR, + /**< The iterator encountered an error renaming the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR, + /**< The iterator encountered an error removing the temporary file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR + /**< The caller violated an assertion or an unexpected error occurred */ + +} FLAC__Metadata_SimpleIteratorStatus; + +/** Maps a FLAC__Metadata_SimpleIteratorStatus to a C string. + * + * Using a FLAC__Metadata_SimpleIteratorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[]; + + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_SimpleIterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator); + +/** Get the current status of the iterator. Call this after a function + * returns \c false to get the reason for the error. Also resets the status + * to FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__Metadata_SimpleIteratorStatus + * The current status of the iterator. + */ +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given FLAC file. + * + * \param iterator A pointer to an existing iterator. + * \param filename The path to the FLAC file. + * \param read_only If \c true, the FLAC file will be opened + * in read-only mode; if \c false, the FLAC + * file will be opened for edit even if no + * edits are performed. + * \param preserve_file_stats If \c true, the owner and modification + * time will be preserved even if the FLAC + * file is written to. + * \assert + * \code iterator != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c false if a memory allocation error occurs, the file can't be + * opened, or another error occurs, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats); + +/** Returns \c true if the FLAC file is writable. If \c false, calls to + * FLAC__metadata_simple_iterator_set_block() and + * FLAC__metadata_simple_iterator_insert_block_after() will fail. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator); + +/** Returns a flag telling if the current metadata block is the last. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the current metadata block is the last in the file, + * else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the offset of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval off_t + * The offset of the metadata block at the current iterator position. + * This is the byte offset relative to the beginning of the file of + * the current metadata block's header. + */ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the type of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the length of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval unsigned + * The length of the metadata block at the current iterator position. + * The is same length as that in the + * metadata block header, + * i.e. the length of the metadata body that follows the header. + */ +FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the application ID of the \c APPLICATION block at the current + * position. This avoids reading the actual block data which can save + * time for large blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \param id A pointer to a buffer of at least \c 4 bytes where + * the ID will be stored. + * \assert + * \code iterator != NULL \endcode + * \code id != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the ID was successfully read, else \c false, in which + * case you should check FLAC__metadata_simple_iterator_status() to + * find out why. If the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, then the + * current metadata block is not an \c APPLICATION block. Otherwise + * if the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR or + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, an I/O error + * occurred and the iterator can no longer be used. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id); + +/** Get the metadata block at the current position. You can modify the + * block but must use FLAC__metadata_simple_iterator_set_block() to + * write it back to the FLAC file. + * + * You must call FLAC__metadata_object_delete() on the returned object + * when you are finished with it. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block, or \c NULL if there was a memory + * allocation error. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator); + +/** Write a block back to the FLAC file. This function tries to be + * as efficient as possible; how the block is actually written is + * shown by the following: + * + * Existing block is a STREAMINFO block and the new block is a + * STREAMINFO block: the new block is written in place. Make sure + * you know what you're doing when changing the values of a + * STREAMINFO block. + * + * Existing block is a STREAMINFO block and the new block is a + * not a STREAMINFO block: this is an error since the first block + * must be a STREAMINFO block. Returns \c false without altering the + * file. + * + * Existing block is not a STREAMINFO block and the new block is a + * STREAMINFO block: this is an error since there may be only one + * STREAMINFO block. Returns \c false without altering the file. + * + * Existing block and new block are the same length: the existing + * block will be replaced by the new block, written in place. + * + * Existing block is longer than new block: if use_padding is \c true, + * the existing block will be overwritten in place with the new + * block followed by a PADDING block, if possible, to make the total + * size the same as the existing block. Remember that a padding + * block requires at least four bytes so if the difference in size + * between the new block and existing block is less than that, the + * entire file will have to be rewritten, using the new block's + * exact size. If use_padding is \c false, the entire file will be + * rewritten, replacing the existing block by the new block. + * + * Existing block is shorter than new block: if use_padding is \c true, + * the function will try and expand the new block into the following + * PADDING block, if it exists and doing so won't shrink the PADDING + * block to less than 4 bytes. If there is no following PADDING + * block, or it will shrink to less than 4 bytes, or use_padding is + * \c false, the entire file is rewritten, replacing the existing block + * with the new block. Note that in this case any following PADDING + * block is preserved as is. + * + * After writing the block, the iterator will remain in the same + * place, i.e. pointing to the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** This is similar to FLAC__metadata_simple_iterator_set_block() + * except that instead of writing over an existing block, it appends + * a block after the existing block. \a use_padding is again used to + * tell the function to try an expand into following padding in an + * attempt to avoid rewriting the entire file. + * + * This function will fail and return \c false if given a STREAMINFO + * block. + * + * After writing the block, the iterator will be pointing to the + * new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** Deletes the block at the current position. This will cause the + * entire FLAC file to be rewritten, unless \a use_padding is \c true, + * in which case the block will be replaced by an equal-sized PADDING + * block. The iterator will be left pointing to the block before the + * one just deleted. + * + * You may not delete the STREAMINFO block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding); + +/* \} */ + + +/** \defgroup flac_metadata_level2 FLAC/metadata.h: metadata level 2 interface + * \ingroup flac_metadata + * + * \brief + * The level 2 interface provides read-write access to FLAC file metadata; + * all metadata is read into memory, operated on in memory, and then written + * to file, which is more efficient than level 1 when editing multiple blocks. + * + * Currently Ogg FLAC is supported for read only, via + * FLAC__metadata_chain_read_ogg() but a subsequent + * FLAC__metadata_chain_write() will fail. + * + * The general usage of this interface is: + * + * - Create a new chain using FLAC__metadata_chain_new(). A chain is a + * linked list of FLAC metadata blocks. + * - Read all metadata into the the chain from a FLAC file using + * FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and + * check the status. + * - Optionally, consolidate the padding using + * FLAC__metadata_chain_merge_padding() or + * FLAC__metadata_chain_sort_padding(). + * - Create a new iterator using FLAC__metadata_iterator_new() + * - Initialize the iterator to point to the first element in the chain + * using FLAC__metadata_iterator_init() + * - Traverse the chain using FLAC__metadata_iterator_next and + * FLAC__metadata_iterator_prev(). + * - Get a block for reading or modification using + * FLAC__metadata_iterator_get_block(). The pointer to the object + * inside the chain is returned, so the block is yours to modify. + * Changes will be reflected in the FLAC file when you write the + * chain. You can also add and delete blocks (see functions below). + * - When done, write out the chain using FLAC__metadata_chain_write(). + * Make sure to read the whole comment to the function below. + * - Delete the chain using FLAC__metadata_chain_delete(). + * + * \note + * Even though the FLAC file is not open while the chain is being + * manipulated, you must not alter the file externally during + * this time. The chain assumes the FLAC file will not change + * between the time of FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg() + * and FLAC__metadata_chain_write(). + * + * \note + * Do not modify the is_last, length, or type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * The metadata objects returned by FLAC__metadata_iterator_get_block() + * are owned by the chain; do not FLAC__metadata_object_delete() them. + * In the same way, blocks passed to FLAC__metadata_iterator_set_block() + * become owned by the chain and they will be deleted when the chain is + * deleted. + * + * \{ + */ + +struct FLAC__Metadata_Chain; +/** The opaque structure definition for the level 2 chain type. + */ +typedef struct FLAC__Metadata_Chain FLAC__Metadata_Chain; + +struct FLAC__Metadata_Iterator; +/** The opaque structure definition for the level 2 iterator type. + */ +typedef struct FLAC__Metadata_Iterator FLAC__Metadata_Iterator; + +typedef enum { + FLAC__METADATA_CHAIN_STATUS_OK = 0, + /**< The chain is in the normal OK state */ + + FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE, + /**< The chain could not open the target file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE, + /**< The chain could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE, + /**< The chain tried to write to a file that was not writable */ + + FLAC__METADATA_CHAIN_STATUS_BAD_METADATA, + /**< The chain encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_CHAIN_STATUS_READ_ERROR, + /**< The chain encountered an error while reading the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR, + /**< The chain encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR, + /**< The chain encountered an error while writing the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR, + /**< The chain encountered an error renaming the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR, + /**< The chain encountered an error removing the temporary file */ + + FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR, + /**< The caller violated an assertion or an unexpected error occurred */ + + FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS, + /**< One or more of the required callbacks was NULL */ + + FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH, + /**< FLAC__metadata_chain_write() was called on a chain read by + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * or + * FLAC__metadata_chain_write_with_callbacks()/FLAC__metadata_chain_write_with_callbacks_and_tempfile() + * was called on a chain read by + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Matching read/write methods must always be used. */ + + FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL + /**< FLAC__metadata_chain_write_with_callbacks() was called when the + * chain write requires a tempfile; use + * FLAC__metadata_chain_write_with_callbacks_and_tempfile() instead. + * Or, FLAC__metadata_chain_write_with_callbacks_and_tempfile() was + * called when the chain write does not require a tempfile; use + * FLAC__metadata_chain_write_with_callbacks() instead. + * Always check FLAC__metadata_chain_check_if_tempfile_needed() + * before writing via callbacks. */ + +} FLAC__Metadata_ChainStatus; + +/** Maps a FLAC__Metadata_ChainStatus to a C string. + * + * Using a FLAC__Metadata_ChainStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_ChainStatusString[]; + +/*********** FLAC__Metadata_Chain ***********/ + +/** Create a new chain instance. + * + * \retval FLAC__Metadata_Chain* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void); + +/** Free a chain instance. Deletes the object pointed to by \a chain. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain); + +/** Get the current status of the chain. Call this after a function + * returns \c false to get the reason for the error. Also resets the + * status to FLAC__METADATA_CHAIN_STATUS_OK. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__Metadata_ChainStatus + * The current status of the chain. + */ +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain); + +/** Read all metadata from a FLAC file into the chain. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read(void *context, FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from an Ogg FLAC file into the chain. + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the Ogg FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(void *context, FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from a FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(void *context, FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Read all metadata from an Ogg FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the Ogg FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(void *context, FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Checks if writing the given chain would require the use of a + * temporary file, or if it could be written in place. + * + * Under certain conditions, padding can be utilized so that writing + * edited metadata back to the FLAC file does not require rewriting the + * entire file. If rewriting is required, then a temporary workfile is + * required. When writing metadata using callbacks, you must check + * this function to know whether to call + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_and_tempfile(). When + * writing with FLAC__metadata_chain_write(), the temporary file is + * handled internally. + * + * \param chain A pointer to an existing chain. + * \param use_padding + * Whether or not padding will be allowed to be used + * during the write. The value of \a use_padding given + * here must match the value later passed to + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_with_tempfile(). + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if writing the current chain would require a tempfile, or + * \c false if metadata can be written in place. + */ +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding); + +/** Write all metadata out to the FLAC file. This function tries to be as + * efficient as possible; how the metadata is actually written is shown by + * the following: + * + * If the current chain is the same size as the existing metadata, the new + * data is written in place. + * + * If the current chain is longer than the existing metadata, and + * \a use_padding is \c true, and the last block is a PADDING block of + * sufficient length, the function will truncate the final padding block + * so that the overall size of the metadata is the same as the existing + * metadata, and then just rewrite the metadata. Otherwise, if not all of + * the above conditions are met, the entire FLAC file must be rewritten. + * If you want to use padding this way it is a good idea to call + * FLAC__metadata_chain_sort_padding() first so that you have the maximum + * amount of padding to work with, unless you need to preserve ordering + * of the PADDING blocks for some reason. + * + * If the current chain is shorter than the existing metadata, and + * \a use_padding is \c true, and the final block is a PADDING block, the padding + * is extended to make the overall size the same as the existing data. If + * \a use_padding is \c true and the last block is not a PADDING block, a new + * PADDING block is added to the end of the new data to make it the same + * size as the existing data (if possible, see the note to + * FLAC__metadata_simple_iterator_set_block() about the four byte limit) + * and the new data is written in place. If none of the above apply or + * \a use_padding is \c false, the entire FLAC file is rewritten. + * + * If \a preserve_file_stats is \c true, the owner and modification time will + * be preserved even if the FLAC file is written. + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(), not + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(). + * + * \param chain A pointer to an existing chain. + * \param use_padding See above. + * \param preserve_file_stats See above. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * The \a handle must be open for updating and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r+" (or \c "r+b" + * for Windows). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c false. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O. The mandatory + * callbacks are \a write and \a seek. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * This version of the write-with-callbacks function must be used when + * FLAC__metadata_chain_check_if_tempfile_needed() returns true. In + * this function, you must supply an I/O handle corresponding to the + * FLAC file to edit, and a temporary handle to which the new FLAC + * file will be written. It is the caller's job to move this temporary + * FLAC file on top of the original FLAC file to complete the metadata + * edit. + * + * The \a handle must be open for reading and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * The \a temp_handle must be open for writing. The + * equivalent minimum stdio fopen() file mode is \c "w" (or \c "wb" + * for Windows). It should be an empty stream, or at least positioned + * at the start-of-file (in which case it is the caller's duty to + * truncate it on return). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c true. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the original FLAC stream to read. + * The handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O on \a handle. + * The mandatory callbacks are \a read, \a seek, and + * \a eof. + * \param temp_handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param temp_callbacks + * A set of callbacks to use for I/O on temp_handle. + * The only mandatory callback is \a write. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks); + +/** Merge adjacent PADDING blocks into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain); + +/** This function will move all PADDING blocks to the end on the metadata, + * then merge them into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain); + + +/*********** FLAC__Metadata_Iterator ***********/ + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_Iterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given chain. + * + * \param iterator A pointer to an existing iterator. + * \param chain A pointer to an existing and initialized (read) chain. + * \assert + * \code iterator != NULL \endcode + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator); + +/** Get the type of the metadata block at the current position. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator); + +/** Get the metadata block at the current position. You can modify + * the block in place but must write the chain before the changes + * are reflected to the FLAC file. You do not need to call + * FLAC__metadata_iterator_set_block() to reflect the changes; + * the pointer returned by FLAC__metadata_iterator_get_block() + * points directly into the chain. + * + * \warning + * Do not call FLAC__metadata_object_delete() on the returned object; + * to delete a block use FLAC__metadata_iterator_delete_block(). + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator); + +/** Set the metadata block at the current position, replacing the existing + * block. The new block passed in becomes owned by the chain and it will be + * deleted when the chain is deleted. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Removes the current block from the chain. If \a replace_with_padding is + * \c true, the block will instead be replaced with a padding block of equal + * size. You can not delete the STREAMINFO block. The iterator will be + * left pointing to the block before the one just "deleted", even if + * \a replace_with_padding is \c true. + * + * \param iterator A pointer to an existing initialized iterator. + * \param replace_with_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, + * otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding); + +/** Insert a new block before the current block. You cannot insert a block + * before the first STREAMINFO block. You cannot insert a STREAMINFO block + * as there can be only one, the one that already exists at the head when you + * read in a chain. The chain takes ownership of the new block and it will be + * deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Insert a new block after the current block. You cannot insert a STREAMINFO + * block as there can be only one, the one that already exists at the head when + * you read in a chain. The chain takes ownership of the new block and it will + * be deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/* \} */ + + +/** \defgroup flac_metadata_object FLAC/metadata.h: metadata object methods + * \ingroup flac_metadata + * + * \brief + * This module contains methods for manipulating FLAC metadata objects. + * + * Since many are variable length we have to be careful about the memory + * management. We decree that all pointers to data in the object are + * owned by the object and memory-managed by the object. + * + * Use the FLAC__metadata_object_new() and FLAC__metadata_object_delete() + * functions to create all instances. When using the + * FLAC__metadata_object_set_*() functions to set pointers to data, set + * \a copy to \c true to have the function make it's own copy of the data, or + * to \c false to give the object ownership of your data. In the latter case + * your pointer must be freeable by free() and will be free()d when the object + * is FLAC__metadata_object_delete()d. It is legal to pass a null pointer as + * the data pointer to a FLAC__metadata_object_set_*() function as long as + * the length argument is 0 and the \a copy argument is \c false. + * + * The FLAC__metadata_object_new() and FLAC__metadata_object_clone() function + * will return \c NULL in the case of a memory allocation error, otherwise a new + * object. The FLAC__metadata_object_set_*() functions return \c false in the + * case of a memory allocation error. + * + * We don't have the convenience of C++ here, so note that the library relies + * on you to keep the types straight. In other words, if you pass, for + * example, a FLAC__StreamMetadata* that represents a STREAMINFO block to + * FLAC__metadata_object_application_set_data(), you will get an assertion + * failure. + * + * For convenience the FLAC__metadata_object_vorbiscomment_*() functions + * maintain a trailing NUL on each Vorbis comment entry. This is not counted + * toward the length or stored in the stream, but it can make working with plain + * comments (those that don't contain embedded-NULs in the value) easier. + * Entries passed into these functions have trailing NULs added if missing, and + * returned entries are guaranteed to have a trailing NUL. + * + * The FLAC__metadata_object_vorbiscomment_*() functions that take a Vorbis + * comment entry/name/value will first validate that it complies with the Vorbis + * comment specification and return false if it does not. + * + * There is no need to recalculate the length field on metadata blocks you + * have modified. They will be calculated automatically before they are + * written back to a file. + * + * \{ + */ + + +/** Create a new metadata object instance of the given type. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0, + * with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have + * the vendor string set (but zero comments). + * + * Do not pass in a value greater than or equal to + * \a FLAC__METADATA_TYPE_UNDEFINED unless you really know what you're + * doing. + * + * \param type Type of object to create + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory or the type code is + * greater than FLAC__MAX_METADATA_TYPE_CODE, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type); + +/** Create a copy of an existing metadata object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new block and + * is responsible for freeing it with FLAC__metadata_object_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object); + +/** Free a metadata object. Deletes the object pointed to by \a object. + * + * The delete is a "deep" delete, i.e. dynamically allocated data within the + * object is also deleted. + * + * \param object A pointer to an existing object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object); + +/** Compares two metadata objects. + * + * The compare is "deep", i.e. dynamically allocated data within the + * object is also compared. + * + * \param block1 A pointer to an existing object. + * \param block2 A pointer to an existing object. + * \assert + * \code block1 != NULL \endcode + * \code block2 != NULL \endcode + * \retval FLAC__bool + * \c true if objects are identical, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2); + +/** Sets the application data of an APPLICATION block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. The existing data will be freed if this + * function is successful, otherwise the original data will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing APPLICATION object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_APPLICATION \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy); + +/** Resize the seekpoint array. + * + * If the size shrinks, elements will truncated; if it grows, new placeholder + * points will be added to the end. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param new_num_points The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code (object->data.seek_table.points == NULL && object->data.seek_table.num_points == 0) || + * (object->data.seek_table.points != NULL && object->data.seek_table.num_points > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points); + +/** Set a seekpoint in a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + */ +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Insert a seekpoint into a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points >= point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Delete a seekpoint from a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num); + +/** Check a seektable to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object); + +/** Append a number of placeholder points to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num); + +/** Append a specific seek point template to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_number The sample number of the seek point template. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number); + +/** Append specific seek point templates to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_numbers An array of sample numbers for the seek points. + * \param num The number of seek point templates to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced approximately + * \a total_samples / \a num samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param samples The number of samples apart to space the placeholder + * points. The first point will be at sample \c 0, the + * second at sample \a samples, then 2*\a samples, and + * so on. As long as \a samples and \a total_samples + * are greater than \c 0, there will always be at least + * one seekpoint at sample \c 0. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced + * \a samples samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code samples > 0 \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples); + +/** Sort a seek table's seek points according to the format specification, + * removing duplicates. + * + * \param object A pointer to a seek table to be sorted. + * \param compact If \c false, behaves like FLAC__format_seektable_sort(). + * If \c true, duplicates are deleted and the seek table is + * shrunk appropriately; the number of placeholder points + * present in the seek table will be the same after the call + * as before. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact); + +/** Sets the vendor string in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The entry to set the vendor string to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Resize the comment array. + * + * If the size shrinks, elements will truncated; if it grows, new empty + * fields will be added to the end. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param new_num_comments The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (object->data.vorbis_comment.comments == NULL && object->data.vorbis_comment.num_comments == 0) || + * (object->data.vorbis_comment.comments != NULL && object->data.vorbis_comment.num_comments > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments); + +/** Sets a comment in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num Index into comment array to set. + * \param entry The entry to set the comment to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code comment_num < object->data.vorbis_comment.num_comments \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Insert a comment in a VORBIS_COMMENT block at the given index. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index at which to insert the comment. The comments + * at and after \a comment_num move right one position. + * To append a comment to the end, set \a comment_num to + * \c object->data.vorbis_comment.num_comments . + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments >= comment_num \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Appends a comment to a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Replaces comments in a VORBIS_COMMENT block with a new one. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * Depending on the the value of \a all, either all or just the first comment + * whose field name(s) match the given entry's name will be replaced by the + * given entry. If no comments match, \a entry will simply be appended. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param all If \c true, all comments whose field name matches + * \a entry's field name will be removed, and \a entry will + * be inserted at the position of the first matching + * comment. If \c false, only the first comment whose + * field name matches \a entry's field name will be + * replaced with \a entry. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy); + +/** Delete a comment in a VORBIS_COMMENT block at the given index. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index of the comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments > comment_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); + +/** Creates a Vorbis comment entry from NUL-terminated name and value strings. + * + * On return, the filled-in \a entry->entry pointer will point to malloc()ed + * memory and shall be owned by the caller. For convenience the entry will + * have a terminating NUL. + * + * \param entry A pointer to a Vorbis comment entry. The entry's + * \c entry pointer should not point to allocated + * memory as it will be overwritten. + * \param field_name The field name in ASCII, \c NUL terminated. + * \param field_value The field value in UTF-8, \c NUL terminated. + * \assert + * \code entry != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, or if \a field_name or \a field_value does + * not comply with the Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value); + +/** Splits a Vorbis comment entry into NUL-terminated name and value strings. + * + * The returned pointers to name and value will be allocated by malloc() + * and shall be owned by the caller. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The address of where the returned pointer to the + * field name will be stored. + * \param field_value The address of where the returned pointer to the + * field value will be stored. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \code memchr(entry.entry, '=', entry.length) != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value); + +/** Check if the given Vorbis comment entry's field name matches the given + * field name. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The field name to check. + * \param field_name_length The length of \a field_name, not including the + * terminating \c NUL. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \retval FLAC__bool + * \c true if the field names match, else \c false + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length); + +/** Find a Vorbis comment with the given field name. + * + * The search begins at entry number \a offset; use an offset of 0 to + * search from the beginning of the comment array. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param offset The offset into the comment array from where to start + * the search. + * \param field_name The field name of the comment to find. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code field_name != NULL \endcode + * \retval int + * The offset in the comment array of the first comment whose field + * name matches \a field_name, or \c -1 if no match was found. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name); + +/** Remove first Vorbis comment matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * \c 1 for one matching entry deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Remove all Vorbis comments matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comments to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * else the number of matching entries deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Create a new CUESHEET track instance. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0. + * + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void); + +/** Create a copy of an existing CUESHEET track object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new object and + * is responsible for freeing it with + * FLAC__metadata_object_cuesheet_track_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object); + +/** Delete a CUESHEET track object + * + * \param object A pointer to an existing CUESHEET track object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object); + +/** Resize a track's index point array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * indices will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param new_num_indices The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code (object->data.cue_sheet.tracks[track_num].indices == NULL && object->data.cue_sheet.tracks[track_num].num_indices == 0) || + * (object->data.cue_sheet.tracks[track_num].indices != NULL && object->data.cue_sheet.tracks[track_num].num_indices > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices); + +/** Insert an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \param index The index point to insert. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index); + +/** Insert a blank index point in a CUESHEET track at the given index. + * + * A blank index point is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Delete an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * modify. NOTE: this is not necessarily the same + * as the track's \a number field. + * \param index_num The index into the track's index array of the index + * to delete. NOTE: this is not necessarily the same + * as the index's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Resize the track array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * tracks will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param new_num_tracks The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code (object->data.cue_sheet.tracks == NULL && object->data.cue_sheet.num_tracks == 0) || + * (object->data.cue_sheet.tracks != NULL && object->data.cue_sheet.num_tracks > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks); + +/** Sets a track in a CUESHEET block. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num Index into track array to set. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param track The track to set the track to. You may safely pass in + * a const pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code track_num < object->data.cue_sheet.num_tracks \endcode + * \code (track->indices != NULL && track->num_indices > 0) || + * (track->indices == NULL && track->num_indices == 0) + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a track in a CUESHEET block at the given index. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \param track The track to insert. You may safely pass in a const + * pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a blank track in a CUESHEET block at the given index. + * + * A blank track is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Delete a track in a CUESHEET block at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * delete. NOTE: this is not necessarily the same + * as the track's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param object A pointer to an existing CUESHEET object. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation); + +/** Calculate and return the CDDB/freedb ID for a cue sheet. The function + * assumes the cue sheet corresponds to a CD; the result is undefined + * if the cuesheet's is_cd bit is not set. + * + * \param object A pointer to an existing CUESHEET object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__uint32 + * The unsigned integer representation of the CDDB/freedb ID + */ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object); + +/** Sets the MIME type of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a mime_type if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param mime_type A pointer to the MIME type string. The string must be + * ASCII characters 0x20-0x7e, NUL-terminated. No validation + * is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (mime_type != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy); + +/** Sets the description of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a description if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param description A pointer to the description string. The string must be + * valid UTF-8, NUL-terminated. No validation is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (description != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy); + +/** Sets the picture data of a PICTURE block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. Also sets the \a data_length field of the + * metadata object to what is passed in as the \a length parameter. The + * existing data will be freed if this function is successful, otherwise the + * original data and data_length will remain if \a copy is \c true and + * malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy); + +/** Check a PICTURE block to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param object A pointer to existing PICTURE block to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \retval FLAC__bool + * \c false if PICTURE block is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/ordinals.h b/lib/flac-1.2.1/include/FLAC/ordinals.h new file mode 100755 index 0000000..a7a5cd9 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/ordinals.h @@ -0,0 +1,80 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ORDINALS_H +#define FLAC__ORDINALS_H + +#if !(defined(_MSC_VER) || defined(__BORLANDC__) || defined(__EMX__)) +#include +#endif + +typedef signed char FLAC__int8; +typedef unsigned char FLAC__uint8; + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int16 FLAC__int16; +typedef __int32 FLAC__int32; +typedef __int64 FLAC__int64; +typedef unsigned __int16 FLAC__uint16; +typedef unsigned __int32 FLAC__uint32; +typedef unsigned __int64 FLAC__uint64; +#elif defined(__EMX__) +typedef short FLAC__int16; +typedef long FLAC__int32; +typedef long long FLAC__int64; +typedef unsigned short FLAC__uint16; +typedef unsigned long FLAC__uint32; +typedef unsigned long long FLAC__uint64; +#else +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; +#endif + +typedef int FLAC__bool; + +typedef FLAC__uint8 FLAC__byte; + +#ifdef true +#undef true +#endif +#ifdef false +#undef false +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/stream_decoder.h b/lib/flac-1.2.1/include/FLAC/stream_decoder.h new file mode 100755 index 0000000..8a5bb5b --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/stream_decoder.h @@ -0,0 +1,1565 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_DECODER_H +#define FLAC__STREAM_DECODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_decoder.h + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * See the detailed documentation in the + * \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces + * \ingroup flac + * + * \brief + * This module describes the decoder layers provided by libFLAC. + * + * The stream decoder can be used to decode complete streams either from + * the client via callbacks, or directly from a file, depending on how + * it is initialized. When decoding via callbacks, the client provides + * callbacks for reading FLAC data and writing decoded samples, and + * handling metadata and errors. If the client also supplies seek-related + * callback, the decoder function for sample-accurate seeking within the + * FLAC input is also available. When decoding from a file, the client + * needs only supply a filename or open \c FILE* and write/metadata/error + * callbacks; the rest of the callbacks are supplied internally. For more + * info see the \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface + * \ingroup flac_decoder + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * The stream decoder can decode native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this decoder is as follows: + * - The program creates an instance of a decoder using + * FLAC__stream_decoder_new(). + * - The program overrides the default settings using + * FLAC__stream_decoder_set_*() functions. + * - The program initializes the instance to validate the settings and + * prepare for decoding using + * - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file() for native FLAC, + * - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE() + * or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC + * - The program calls the FLAC__stream_decoder_process_*() functions + * to decode data, which subsequently calls the callbacks. + * - The program finishes the decoding with FLAC__stream_decoder_finish(), + * which flushes the input and output and resets the decoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_decoder_delete(). + * + * In more detail, the program will create a new instance by calling + * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*() + * functions to override the default decoder options, and call + * one of the FLAC__stream_decoder_init_*() functions. + * + * There are three initialization functions for native FLAC, one for + * setting up the decoder to decode FLAC data from the client via + * callbacks, and two for decoding directly from a FLAC file. + * + * For decoding via callbacks, use FLAC__stream_decoder_init_stream(). + * You must also supply several callbacks for handling I/O. Some (like + * seeking) are optional, depending on the capabilities of the input. + * + * For decoding directly from a file, use FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file(). Then you must only supply an open + * \c FILE* or filename and fewer callbacks; the decoder will handle + * the other callbacks internally. + * + * There are three similarly-named init functions for decoding from Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * Once the decoder is initialized, your program will call one of several + * functions to start the decoding process: + * + * - FLAC__stream_decoder_process_single() - Tells the decoder to process at + * most one metadata block or audio frame and return, calling either the + * metadata callback or write callback, respectively, once. If the decoder + * loses sync it will return with only the error callback being called. + * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder + * to process the stream from the current location and stop upon reaching + * the first audio frame. The client will get one metadata, write, or error + * callback per metadata block, audio frame, or sync error, respectively. + * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder + * to process the stream from the current location until the read callback + * returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or + * FLAC__STREAM_DECODER_READ_STATUS_ABORT. The client will get one metadata, + * write, or error callback per metadata block, audio frame, or sync error, + * respectively. + * + * When the decoder has finished decoding (normally or through an abort), + * the instance is finished by calling FLAC__stream_decoder_finish(), which + * ensures the decoder is in the correct state and frees memory. Then the + * instance may be deleted with FLAC__stream_decoder_delete() or initialized + * again to decode another stream. + * + * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method. + * At any point after the stream decoder has been initialized, the client can + * call this function to seek to an exact sample within the stream. + * Subsequently, the first time the write callback is called it will be + * passed a (possibly partial) block starting at that sample. + * + * If the client cannot seek via the callback interface provided, but still + * has another way of seeking, it can flush the decoder using + * FLAC__stream_decoder_flush() and start feeding data from the new position + * through the read callback. + * + * The stream decoder also provides MD5 signature checking. If this is + * turned on before initialization, FLAC__stream_decoder_finish() will + * report when the decoded MD5 signature does not match the one stored + * in the STREAMINFO block. MD5 checking is automatically turned off + * (until the next FLAC__stream_decoder_reset()) if there is no signature + * in the STREAMINFO block or when a seek is attempted. + * + * The FLAC__stream_decoder_set_metadata_*() functions deserve special + * attention. By default, the decoder only calls the metadata_callback for + * the STREAMINFO block. These functions allow you to tell the decoder + * explicitly which blocks to parse and return via the metadata_callback + * and/or which to skip. Use a FLAC__stream_decoder_set_metadata_respond_all(), + * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(), + * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify + * which blocks to return. Remember that metadata blocks can potentially + * be big (for example, cover art) so filtering out the ones you don't + * use can reduce the memory requirements of the decoder. Also note the + * special forms FLAC__stream_decoder_set_metadata_respond_application(id) + * and FLAC__stream_decoder_set_metadata_ignore_application(id) for + * filtering APPLICATION blocks based on the application ID. + * + * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but + * they still can legally be filtered from the metadata_callback. + * + * \note + * The "set" functions may only be called when the decoder is in the + * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after + * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but + * before FLAC__stream_decoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_decoder_finish() resets all settings to the constructor + * defaults, including the callbacks. + * + * \{ + */ + + +/** State values for a FLAC__StreamDecoder + * + * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state(). + */ +typedef enum { + + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + /**< The decoder is ready to search for metadata. */ + + FLAC__STREAM_DECODER_READ_METADATA, + /**< The decoder is ready to or is in the process of reading metadata. */ + + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + /**< The decoder is ready to or is in the process of searching for the + * frame sync code. + */ + + FLAC__STREAM_DECODER_READ_FRAME, + /**< The decoder is ready to or is in the process of reading a frame. */ + + FLAC__STREAM_DECODER_END_OF_STREAM, + /**< The decoder has reached the end of the stream. */ + + FLAC__STREAM_DECODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_DECODER_SEEK_ERROR, + /**< An error occurred while seeking. The decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + */ + + FLAC__STREAM_DECODER_ABORTED, + /**< The decoder was aborted by the read callback. */ + + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. The decoder is in an invalid + * state and can no longer be used. + */ + + FLAC__STREAM_DECODER_UNINITIALIZED + /**< The decoder is in the uninitialized state; one of the + * FLAC__stream_decoder_init_*() functions must be called before samples + * can be processed. + */ + +} FLAC__StreamDecoderState; + +/** Maps a FLAC__StreamDecoderState to a C string. + * + * Using a FLAC__StreamDecoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderStateString[]; + + +/** Possible return values for the FLAC__stream_decoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. */ + + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + /**< fopen() failed in FLAC__stream_decoder_init_file() or + * FLAC__stream_decoder_init_ogg_file(). */ + + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_decoder_init_*() was called when the decoder was + * already initialized, usually because + * FLAC__stream_decoder_finish() was not called. + */ + +} FLAC__StreamDecoderInitStatus; + +/** Maps a FLAC__StreamDecoderInitStatus to a C string. + * + * Using a FLAC__StreamDecoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[]; + + +/** Return values for the FLAC__StreamDecoder read callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted while at the end of the stream. Note that + * the client must only return this value when the read callback was + * called when already at the end of the stream. Otherwise, if the read + * itself moves to the end of the stream, the client should still return + * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on + * the next read callback it should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count + * of \c 0. + */ + + FLAC__STREAM_DECODER_READ_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderReadStatus; + +/** Maps a FLAC__StreamDecoderReadStatus to a C string. + * + * Using a FLAC__StreamDecoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[]; + + +/** Return values for the FLAC__StreamDecoder seek callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + /**< The seek was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamDecoderSeekStatus; + +/** Maps a FLAC__StreamDecoderSeekStatus to a C string. + * + * Using a FLAC__StreamDecoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamDecoder tell callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_TELL_STATUS_OK, + /**< The tell was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support telling the position. */ + +} FLAC__StreamDecoderTellStatus; + +/** Maps a FLAC__StreamDecoderTellStatus to a C string. + * + * Using a FLAC__StreamDecoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[]; + + +/** Return values for the FLAC__StreamDecoder length callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + /**< The length call was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + /**< Client does not support reporting the length. */ + +} FLAC__StreamDecoderLengthStatus; + +/** Maps a FLAC__StreamDecoderLengthStatus to a C string. + * + * Using a FLAC__StreamDecoderLengthStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[]; + + +/** Return values for the FLAC__StreamDecoder write callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + /**< The write was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderWriteStatus; + +/** Maps a FLAC__StreamDecoderWriteStatus to a C string. + * + * Using a FLAC__StreamDecoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; + + +/** Possible values passed back to the FLAC__StreamDecoder error callback. + * \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch- + * all. The rest could be caused by bad sync (false synchronization on + * data that is not the start of a frame) or corrupted data. The error + * itself is the decoder's best guess at what happened assuming a correct + * sync. For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER + * could be caused by a correct sync on the start of a frame, but some + * data in the frame header was corrupted. Or it could be the result of + * syncing on a point the stream that looked like the starting of a frame + * but was not. \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + * could be because the decoder encountered a valid frame made by a future + * version of the encoder which it cannot parse, or because of a false + * sync making it appear as though an encountered frame was generated by + * a future encoder. + */ +typedef enum { + + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + /**< An error in the stream caused the decoder to lose synchronization. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + /**< The decoder encountered a corrupted frame header. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + /**< The frame's data did not match the CRC in the footer. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + /**< The decoder encountered reserved fields in use in the stream. */ + +} FLAC__StreamDecoderErrorStatus; + +/** Maps a FLAC__StreamDecoderErrorStatus to a C string. + * + * Using a FLAC__StreamDecoderErrorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamDecoder + * + ***********************************************************************/ + +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +/** The opaque structure definition for the stream decoder type. + * See the \link flac_stream_decoder stream decoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs more input data. The address of the + * buffer to be filled is supplied, along with the number of bytes the + * buffer can hold. The callback may choose to supply less data and + * modify the byte count but must be careful not to overflow the buffer. + * The callback then returns a status code chosen from + * FLAC__StreamDecoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be decoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderReadStatus + * The callee's return status. Note that the callback should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if + * zero bytes were read and there is no more data to be read. + */ +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to seek the input stream. The decoder + * will pass the absolute byte offset to seek to, 0 meaning the + * beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the current position of the + * stream. The callback should return the byte offset from the + * beginning of the stream. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_DECODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset A pointer to storage for the current offset + * from the beginning of the stream. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the length callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the total length of the stream + * in bytes. + * + * Here is an example of a length callback for stdio streams: + * \code + * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * struct stat filestats; + * + * if(file == stdin) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + * else if(fstat(fileno(file), &filestats) != 0) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + * else { + * *stream_length = (FLAC__uint64)filestats.st_size; + * return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param stream_length A pointer to storage for the length of the stream + * in bytes. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderLengthStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + +/** Signature for the EOF callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to know if the end of the stream has + * been reached. + * + * Here is an example of a EOF callback for stdio streams: + * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) + * \code + * { + * FILE *file = ((MyClientData*)client_data)->file; + * return feof(file)? true : false; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__bool + * \c true if the currently at the end of the stream, else \c false. + */ +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * single audio frame. The decoder will pass the frame metadata as well + * as an array of pointers (one for each channel) pointing to the + * decoded audio. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param frame The description of the decoded frame. See + * FLAC__Frame. + * \param buffer An array of pointers to decoded channels of data. + * Each pointer will point to an array of signed + * samples of length \a frame->header.blocksize. + * Channels will be ordered according to the FLAC + * specification; see the documentation for the + * frame header. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * metadata block. In a valid FLAC file there will always be one + * \c STREAMINFO block, followed by zero or more other metadata blocks. + * These will be supplied by the decoder in the same order as they + * appear in the stream and always before the first audio frame (i.e. + * write callback). The metadata block that is passed in must not be + * modified, and it doesn't live beyond the callback, so you should make + * a copy of it with FLAC__metadata_object_clone() if you will need it + * elsewhere. Since metadata blocks can potentially be large, by + * default the decoder only calls the metadata callback for the + * \c STREAMINFO block; you can instruct the decoder to pass or filter + * other blocks with FLAC__stream_decoder_set_metadata_*() calls. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param metadata The decoded metadata block. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the error callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called whenever an error occurs during + * decoding. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param status The error encountered by the decoder. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream decoder instance. The instance is created with + * default settings; see the individual FLAC__stream_decoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamDecoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void); + +/** Free a decoder instance. Deletes the object pointed to by \a decoder. + * + * \param decoder A pointer to an existing decoder. + * \assert + * \code decoder != NULL \endcode + */ +FLAC_API void FLAC__stream_decoder_delete(void *context, FLAC__StreamDecoder *decoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream within the Ogg container. + * The default behavior is to use the serial number of the first Ogg + * page. Setting a serial number here will explicitly specify which + * stream is to be decoded. + * + * \note + * This does not need to be set for native FLAC decoding. + * + * \default \c use serial number of first page + * \param decoder A decoder instance to set. + * \param serial_number See above. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); + +/** Set the "MD5 signature checking" flag. If \c true, the decoder will + * compute the MD5 signature of the unencoded audio data while decoding + * and compare it to the signature from the STREAMINFO block, if it + * exists, during FLAC__stream_decoder_finish(). + * + * MD5 signature checking will be turned off (until the next + * FLAC__stream_decoder_reset()) if there is no signature in the + * STREAMINFO block or when a seek is attempted. + * + * Clients that do not use the MD5 check should leave this off to speed + * up decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param value Flag value (see above). + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); + +/** Direct the decoder to pass on all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to pass on all APPLICATION metadata blocks of the + * given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to pass on all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); + +/** Direct the decoder to filter out all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to filter out all APPLICATION metadata blocks of + * the given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to filter out all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +/** Get the current decoder state. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The current decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); + +/** Get the current decoder state as a C string. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval const char * + * The decoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); + +/** Get the "MD5 signature checking" flag. + * This is the value of the setting, not whether or not the decoder is + * currently checking the MD5 (remember, it can be turned off automatically + * by a seek). When the decoder is reset the flag will be restored to the + * value returned by this function. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); + +/** Get the total number of samples in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the \c STREAMINFO block. A value of \c 0 means "unknown". + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); + +/** Get the current number of channels in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); + +/** Get the current channel assignment in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__ChannelAssignment + * See above. + */ +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); + +/** Get the current sample resolution in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); + +/** Get the current sample rate in Hz of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); + +/** Get the current blocksize of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); + +/** Returns the decoder's current read position within the stream. + * The position is the byte offset from the start of the stream. + * Bytes before this position have been fully decoded. Note that + * there may still be undecoded bytes in the decoder's read FIFO. + * The returned position is correct even after a seek. + * + * \warning This function currently only works for native FLAC, + * not Ogg FLAC streams. + * + * \param decoder A decoder instance to query. + * \param position Address at which to return the desired position. + * \assert + * \code decoder != NULL \endcode + * \code position != NULL \endcode + * \retval FLAC__bool + * \c true if successful, \c false if the stream is not native FLAC, + * or there was an error from the 'tell' callback or it returned + * \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(void *context, const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +/** Initialize the decoder instance to decode native FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * native FLAC stream. I/O is performed via callbacks to the client. + * For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + void *context, + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * FLAC stream in an Ogg container. I/O is performed via callbacks to the + * client. For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + void *context, + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain native FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + void *context, + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain Ogg FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + void *context, + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * native FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + void *context, + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + void *context, + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Finish the decoding process. + * Flushes the decoding buffer, releases resources, resets the decoder + * settings to their defaults, and returns the decoder state to + * FLAC__STREAM_DECODER_UNINITIALIZED. + * + * In the event of a prematurely-terminated decode, it is not strictly + * necessary to call this immediately before FLAC__stream_decoder_delete() + * but it is good practice to match every FLAC__stream_decoder_init_*() + * with a FLAC__stream_decoder_finish(). + * + * \param decoder An uninitialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if MD5 checking is on AND a STREAMINFO block was available + * AND the MD5 signature in the STREAMINFO block was non-zero AND the + * signature does not match the one computed by the decoder; else + * \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_finish(void *context, FLAC__StreamDecoder *decoder); + +/** Flush the stream input. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC. This will also turn + * off MD5 checking. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation + * error occurs (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); + +/** Reset the decoding process. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA. This is similar to + * FLAC__stream_decoder_finish() except that the settings are + * preserved; there is no need to call FLAC__stream_decoder_init_*() + * before decoding again. MD5 checking will be restored to its original + * setting. + * + * If the decoder is seekable, or was initialized with + * FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(), + * the decoder will also attempt to seek to the beginning of the file. + * If this rewind fails, this function will return \c false. It follows + * that FLAC__stream_decoder_reset() cannot be used when decoding from + * \c stdin. + * + * If the decoder was initialized with FLAC__stream_encoder_init*_stream() + * and is not seekable (i.e. no seek callback was provided or the seek + * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it + * is the duty of the client to start feeding data from the beginning of + * the stream on the next FLAC__stream_decoder_process() or + * FLAC__stream_decoder_process_interleaved() call. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation occurs + * (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error + * occurs (the state will be unchanged). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); + +/** Decode one metadata block or audio frame. + * This version instructs the decoder to decode a either a single metadata + * block or a single frame and stop, unless the callbacks return a fatal + * error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * Depending on what was decoded, the metadata or write callback will be + * called with the decoded metadata block or audio frame. + * + * Unless there is a fatal read error or end of stream, this function + * will return once one whole frame is decoded. In other words, if the + * stream is not synchronized or points to a corrupt frame header, the + * decoder will continue to try and resync until it gets to a valid + * frame, then decode one frame, then return. If the decoder points to + * a frame whose frame CRC in the frame footer does not match the + * computed frame CRC, this function will issue a + * FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the + * error callback, and return, having decoded one complete, although + * corrupt, frame. (Such corrupted frames are sent as silence of the + * correct length to the write callback.) + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(void *context, FLAC__StreamDecoder *decoder); + +/** Decode until the end of the metadata. + * This version instructs the decoder to decode from the current position + * and continue until all the metadata has been read, or until the + * callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block is decoded, the metadata callback will be called + * with the decoded metadata. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(void *context, FLAC__StreamDecoder *decoder); + +/** Decode until the end of the stream. + * This version instructs the decoder to decode from the current position + * and continue until the end of stream (the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the + * callbacks return a fatal error. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block and frame is decoded, the metadata or write + * callback will be called with the decoded metadata or frame. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(void *context, FLAC__StreamDecoder *decoder); + +/** Skip one audio frame. + * This version instructs the decoder to 'skip' a single frame and stop, + * unless the callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * The decoding flow is the same as what occurs when + * FLAC__stream_decoder_process_single() is called to process an audio + * frame, except that this function does not decode the parsed data into + * PCM or call the write callback. The integrity of the frame is still + * checked the same way as in the other process functions. + * + * This function will return once one whole frame is skipped, in the + * same way that FLAC__stream_decoder_process_single() will return once + * one whole frame is decoded. + * + * This function can be used in more quickly determining FLAC frame + * boundaries when decoding of the actual data is not needed, for + * example when an application is separating a FLAC stream into frames + * for editing or storing in a container. To do this, the application + * can use FLAC__stream_decoder_skip_single_frame() to quickly advance + * to the next frame, then use + * FLAC__stream_decoder_get_decode_position() to find the new frame + * boundary. + * + * This function should only be called when the stream has advanced + * past all the metadata, otherwise it will return \c false. + * + * \param decoder An initialized decoder instance not in a metadata + * state. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), or if the decoder + * is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or + * FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(void *context, FLAC__StreamDecoder *decoder); + +/** Flush the input and seek to an absolute sample. + * Decoding will resume at the given sample. Note that because of + * this, the next write callback may contain a partial block. The + * client must support seeking the input or this function will fail + * and return \c false. Furthermore, if the decoder state is + * \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + * + * \param decoder A decoder instance. + * \param sample The target sample number to seek to. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(void *context, FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/FLAC/stream_encoder.h b/lib/flac-1.2.1/include/FLAC/stream_encoder.h new file mode 100755 index 0000000..dbbbb23 --- /dev/null +++ b/lib/flac-1.2.1/include/FLAC/stream_encoder.h @@ -0,0 +1,1768 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_ENCODER_H +#define FLAC__STREAM_ENCODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" +#include "stream_decoder.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_encoder.h + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * See the detailed documentation in the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_encoder FLAC/ \*_encoder.h: encoder interfaces + * \ingroup flac + * + * \brief + * This module describes the encoder layers provided by libFLAC. + * + * The stream encoder can be used to encode complete streams either to the + * client via callbacks, or directly to a file, depending on how it is + * initialized. When encoding via callbacks, the client provides a write + * callback which will be called whenever FLAC data is ready to be written. + * If the client also supplies a seek callback, the encoder will also + * automatically handle the writing back of metadata discovered while + * encoding, like stream info, seek points offsets, etc. When encoding to + * a file, the client needs only supply a filename or open \c FILE* and an + * optional progress callback for periodic notification of progress; the + * write and seek callbacks are supplied internally. For more info see the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_stream_encoder FLAC/stream_encoder.h: stream encoder interface + * \ingroup flac_encoder + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * The stream encoder can encode to native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this encoder is as follows: + * - The program creates an instance of an encoder using + * FLAC__stream_encoder_new(). + * - The program overrides the default settings using + * FLAC__stream_encoder_set_*() functions. At a minimum, the following + * functions should be called: + * - FLAC__stream_encoder_set_channels() + * - FLAC__stream_encoder_set_bits_per_sample() + * - FLAC__stream_encoder_set_sample_rate() + * - FLAC__stream_encoder_set_ogg_serial_number() (if encoding to Ogg FLAC) + * - FLAC__stream_encoder_set_total_samples_estimate() (if known) + * - If the application wants to control the compression level or set its own + * metadata, then the following should also be called: + * - FLAC__stream_encoder_set_compression_level() + * - FLAC__stream_encoder_set_verify() + * - FLAC__stream_encoder_set_metadata() + * - The rest of the set functions should only be called if the client needs + * exact control over how the audio is compressed; thorough understanding + * of the FLAC format is necessary to achieve good results. + * - The program initializes the instance to validate the settings and + * prepare for encoding using + * - FLAC__stream_encoder_init_stream() or FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file() for native FLAC + * - FLAC__stream_encoder_init_ogg_stream() or FLAC__stream_encoder_init_ogg_FILE() + * or FLAC__stream_encoder_init_ogg_file() for Ogg FLAC + * - The program calls FLAC__stream_encoder_process() or + * FLAC__stream_encoder_process_interleaved() to encode data, which + * subsequently calls the callbacks when there is encoder data ready + * to be written. + * - The program finishes the encoding with FLAC__stream_encoder_finish(), + * which causes the encoder to encode any data still in its input pipe, + * update the metadata with the final encoding statistics if output + * seeking is possible, and finally reset the encoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_encoder_delete(). + * + * In more detail, the stream encoder functions similarly to the + * \link flac_stream_decoder stream decoder \endlink, but has fewer + * callbacks and more options. Typically the client will create a new + * instance by calling FLAC__stream_encoder_new(), then set the necessary + * parameters with FLAC__stream_encoder_set_*(), and initialize it by + * calling one of the FLAC__stream_encoder_init_*() functions. + * + * Unlike the decoders, the stream encoder has many options that can + * affect the speed and compression ratio. When setting these parameters + * you should have some basic knowledge of the format (see the + * user-level documentation + * or the formal description). The + * FLAC__stream_encoder_set_*() functions themselves do not validate the + * values as many are interdependent. The FLAC__stream_encoder_init_*() + * functions will do this, so make sure to pay attention to the state + * returned by FLAC__stream_encoder_init_*() to make sure that it is + * FLAC__STREAM_ENCODER_INIT_STATUS_OK. Any parameters that are not set + * before FLAC__stream_encoder_init_*() will take on the defaults from + * the constructor. + * + * There are three initialization functions for native FLAC, one for + * setting up the encoder to encode FLAC data to the client via + * callbacks, and two for encoding directly to a file. + * + * For encoding via callbacks, use FLAC__stream_encoder_init_stream(). + * You must also supply a write callback which will be called anytime + * there is raw encoded data to write. If the client can seek the output + * it is best to also supply seek and tell callbacks, as this allows the + * encoder to go back after encoding is finished to write back + * information that was collected while encoding, like seek point offsets, + * frame sizes, etc. + * + * For encoding directly to a file, use FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file(). Then you must only supply a + * filename or open \c FILE*; the encoder will handle all the callbacks + * internally. You may also supply a progress callback for periodic + * notification of the encoding progress. + * + * There are three similarly-named init functions for encoding to Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * The call to FLAC__stream_encoder_init_*() currently will also immediately + * call the write callback several times, once with the \c fLaC signature, + * and once for each encoded metadata block. Note that for Ogg FLAC + * encoding you will usually get at least twice the number of callbacks than + * with native FLAC, one for the Ogg page header and one for the page body. + * + * After initializing the instance, the client may feed audio data to the + * encoder in one of two ways: + * + * - Channel separate, through FLAC__stream_encoder_process() - The client + * will pass an array of pointers to buffers, one for each channel, to + * the encoder, each of the same length. The samples need not be + * block-aligned, but each channel should have the same number of samples. + * - Channel interleaved, through + * FLAC__stream_encoder_process_interleaved() - The client will pass a single + * pointer to data that is channel-interleaved (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * Again, the samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 and + * the last value channelN_sampleM. + * + * Note that for either process call, each sample in the buffers should be a + * signed integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the resolution + * is 16 bits per sample, the samples should all be in the range [-32768,32767]. + * + * When the client is finished encoding data, it calls + * FLAC__stream_encoder_finish(), which causes the encoder to encode any + * data still in its input pipe, and call the metadata callback with the + * final encoding statistics. Then the instance may be deleted with + * FLAC__stream_encoder_delete() or initialized again to encode another + * stream. + * + * For programs that write their own metadata, but that do not know the + * actual metadata until after encoding, it is advantageous to instruct + * the encoder to write a PADDING block of the correct size, so that + * instead of rewriting the whole stream after encoding, the program can + * just overwrite the PADDING block. If only the maximum size of the + * metadata is known, the program can write a slightly larger padding + * block, then split it after encoding. + * + * Make sure you understand how lengths are calculated. All FLAC metadata + * blocks have a 4 byte header which contains the type and length. This + * length does not include the 4 bytes of the header. See the format page + * for the specification of metadata blocks and their lengths. + * + * \note + * If you are writing the FLAC data to a file via callbacks, make sure it + * is open for update (e.g. mode "w+" for stdio streams). This is because + * after the first encoding pass, the encoder will try to seek back to the + * beginning of the stream, to the STREAMINFO block, to write some data + * there. (If using FLAC__stream_encoder_init*_file() or + * FLAC__stream_encoder_init*_FILE(), the file is managed internally.) + * + * \note + * The "set" functions may only be called when the encoder is in the + * state FLAC__STREAM_ENCODER_UNINITIALIZED, i.e. after + * FLAC__stream_encoder_new() or FLAC__stream_encoder_finish(), but + * before FLAC__stream_encoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_encoder_finish() resets all settings to the constructor + * defaults. + * + * \{ + */ + + +/** State values for a FLAC__StreamEncoder. + * + * The encoder's state can be obtained by calling FLAC__stream_encoder_get_state(). + * + * If the encoder gets into any other state besides \c FLAC__STREAM_ENCODER_OK + * or \c FLAC__STREAM_ENCODER_UNINITIALIZED, it becomes invalid for encoding and + * must be deleted with FLAC__stream_encoder_delete(). + */ +typedef enum { + + FLAC__STREAM_ENCODER_OK = 0, + /**< The encoder is in the normal OK state and samples can be processed. */ + + FLAC__STREAM_ENCODER_UNINITIALIZED, + /**< The encoder is in the uninitialized state; one of the + * FLAC__stream_encoder_init_*() functions must be called before samples + * can be processed. + */ + + FLAC__STREAM_ENCODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR, + /**< An error occurred in the underlying verify stream decoder; + * check FLAC__stream_encoder_get_verify_decoder_state(). + */ + + FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA, + /**< The verify decoder detected a mismatch between the original + * audio signal and the decoded audio signal. + */ + + FLAC__STREAM_ENCODER_CLIENT_ERROR, + /**< One of the callbacks returned a fatal error. */ + + FLAC__STREAM_ENCODER_IO_ERROR, + /**< An I/O error occurred while opening/reading/writing a file. + * Check \c errno. + */ + + FLAC__STREAM_ENCODER_FRAMING_ERROR, + /**< An error occurred while writing the stream; usually, the + * write_callback returned an error. + */ + + FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR + /**< Memory allocation failed. */ + +} FLAC__StreamEncoderState; + +/** Maps a FLAC__StreamEncoderState to a C string. + * + * Using a FLAC__StreamEncoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderStateString[]; + + +/** Possible return values for the FLAC__stream_encoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR, + /**< General failure to set up encoder; call FLAC__stream_encoder_get_state() for cause. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS, + /**< The encoder has an invalid setting for number of channels. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, + /**< The encoder has an invalid setting for bits-per-sample. + * FLAC supports 4-32 bps but the reference encoder currently supports + * only up to 24 bps. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, + /**< The encoder has an invalid setting for the input sample rate. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE, + /**< The encoder has an invalid setting for the block size. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER, + /**< The encoder has an invalid setting for the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION, + /**< The encoder has an invalid setting for the precision of the quantized linear predictor coefficients. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER, + /**< The specified block size is less than the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, + /**< The encoder is bound to the Subset but other settings violate it. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, + /**< The metadata input to the encoder is invalid, in one of the following ways: + * - FLAC__stream_encoder_set_metadata() was called with a null pointer but a block count > 0 + * - One of the metadata blocks contains an undefined type + * - It contains an illegal CUESHEET as checked by FLAC__format_cuesheet_is_legal() + * - It contains an illegal SEEKTABLE as checked by FLAC__format_seektable_is_legal() + * - It contains more than one SEEKTABLE block or more than one VORBIS_COMMENT block + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_encoder_init_*() was called when the encoder was + * already initialized, usually because + * FLAC__stream_encoder_finish() was not called. + */ + +} FLAC__StreamEncoderInitStatus; + +/** Maps a FLAC__StreamEncoderInitStatus to a C string. + * + * Using a FLAC__StreamEncoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderInitStatusString[]; + + +/** Return values for the FLAC__StreamEncoder read callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted at the end of the stream. */ + + FLAC__STREAM_ENCODER_READ_STATUS_ABORT, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED + /**< Client does not support reading back from the output. */ + +} FLAC__StreamEncoderReadStatus; + +/** Maps a FLAC__StreamEncoderReadStatus to a C string. + * + * Using a FLAC__StreamEncoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderReadStatusString[]; + + +/** Return values for the FLAC__StreamEncoder write callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0, + /**< The write was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR + /**< An unrecoverable error occurred. The encoder will return from the process call. */ + +} FLAC__StreamEncoderWriteStatus; + +/** Maps a FLAC__StreamEncoderWriteStatus to a C string. + * + * Using a FLAC__StreamEncoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[]; + + +/** Return values for the FLAC__StreamEncoder seek callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_SEEK_STATUS_OK, + /**< The seek was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderSeekStatus; + +/** Maps a FLAC__StreamEncoderSeekStatus to a C string. + * + * Using a FLAC__StreamEncoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamEncoder tell callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_TELL_STATUS_OK, + /**< The tell was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderTellStatus; + +/** Maps a FLAC__StreamEncoderTellStatus to a C string. + * + * Using a FLAC__StreamEncoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderTellStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamEncoder + * + ***********************************************************************/ + +struct FLAC__StreamEncoderProtected; +struct FLAC__StreamEncoderPrivate; +/** The opaque structure definition for the stream encoder type. + * See the \link flac_stream_encoder stream encoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamEncoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamEncoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamEncoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init_ogg_stream() if seeking is supported. + * The supplied function will be called when the encoder needs to read back + * encoded data. This happens during the metadata callback, when the encoder + * has to read, modify, and rewrite the metadata (e.g. seekpoints) gathered + * while encoding. The address of the buffer to be filled is supplied, along + * with the number of bytes the buffer can hold. The callback may choose to + * supply less data and modify the byte count but must be careful not to + * overflow the buffer. The callback then returns a status code chosen from + * FLAC__StreamEncoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamEncoderReadStatus read_cb(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be encoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_set_client_data(). + * \retval FLAC__StreamEncoderReadStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * by the encoder anytime there is raw encoded data ready to write. It may + * include metadata mixed with encoded audio frames and the data is not + * guaranteed to be aligned on frame or metadata block boundaries. + * + * The only duty of the callback is to write out the \a bytes worth of data + * in \a buffer to the current position in the output stream. The arguments + * \a samples and \a current_frame are purely informational. If \a samples + * is greater than \c 0, then \a current_frame will hold the current frame + * number that is being written; otherwise it indicates that the write + * callback is being called to write metadata. + * + * \note + * Unlike when writing to native FLAC, when writing to Ogg FLAC the + * write callback will be called twice when writing each audio + * frame; once for the page header, and once for the page body. + * When writing the page header, the \a samples argument to the + * write callback will be \c 0. + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer An array of encoded data of length \a bytes. + * \param bytes The byte length of \a buffer. + * \param samples The number of samples encoded by \a buffer. + * \c 0 has a special meaning; see above. + * \param current_frame The number of the current frame being encoded. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to seek the output stream. The encoder will pass + * the absolute byte offset to seek to, 0 meaning the beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamEncoderSeekStatus seek_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderSeekStatus (*FLAC__StreamEncoderSeekCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to know the current position of the output stream. + * + * \warning + * The callback must return the true current byte offset of the output to + * which the encoder is writing. If you are buffering the output, make + * sure and take this into account. If you are writing directly to a + * FILE* from your write callback, ftell() is sufficient. If you are + * writing directly to a file descriptor from your write callback, you + * can use lseek(fd, SEEK_CUR, 0). The encoder may later seek back to + * these points to rewrite metadata after encoding. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamEncoderTellStatus tell_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The address at which to store the current + * position of the output. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderTellStatus (*FLAC__StreamEncoderTellCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * once at the end of encoding with the populated STREAMINFO structure. This + * is so the client can seek back to the beginning of the file and write the + * STREAMINFO block with the correct statistics after encoding (like + * minimum/maximum frame size and total samples). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param metadata The final populated STREAMINFO block. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the progress callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE(). + * The supplied function will be called when the encoder has finished + * writing a frame. The \c total_frames_estimate argument to the + * callback will be based on the value from + * FLAC__stream_encoder_set_total_samples_estimate(). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param bytes_written Bytes written so far. + * \param samples_written Samples written so far. + * \param frames_written Frames written so far. + * \param total_frames_estimate The estimate of the total number of + * frames to be written. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream encoder instance. The instance is created with + * default settings; see the individual FLAC__stream_encoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamEncoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void); + +/** Free an encoder instance. Deletes the object pointed to by \a encoder. + * + * \param encoder A pointer to an existing encoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream to use in the Ogg container. + * + * \note + * This does not need to be set for native FLAC encoding. + * + * \note + * It is recommended to set a serial number explicitly as the default of '0' + * may collide with other streams. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param serial_number See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long serial_number); + +/** Set the "verify" flag. If \c true, the encoder will verify it's own + * encoded output by feeding it through an internal decoder and comparing + * the original signal against the decoded signal. If a mismatch occurs, + * the process call will return \c false. Note that this will slow the + * encoding process by the extra time required for decoding and comparison. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the Subset flag. If \c true, + * the encoder will comply with the Subset and will check the + * settings during FLAC__stream_encoder_init_*() to see if all settings + * comply. If \c false, the settings may take advantage of the full + * range that the format allows. + * + * Make sure you know what it entails before setting this to \c false. + * + * \default \c true + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the number of channels to be encoded. + * + * \default \c 2 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample resolution of the input to be encoded. + * + * \warning + * Do not feed the encoder data that is wider than the value you + * set here or you will generate an invalid stream. + * + * \default \c 16 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample rate (in Hz) of the input to be encoded. + * + * \default \c 44100 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the compression level + * + * The compression level is roughly proportional to the amount of effort + * the encoder expends to compress the file. A higher level usually + * means more computation but higher compression. The default level is + * suitable for most applications. + * + * Currently the levels range from \c 0 (fastest, least compression) to + * \c 8 (slowest, most compression). A value larger than \c 8 will be + * treated as \c 8. + * + * This function automatically calls the following other \c _set_ + * functions with appropriate values, so the client does not need to + * unless it specifically wants to override them: + * - FLAC__stream_encoder_set_do_mid_side_stereo() + * - FLAC__stream_encoder_set_loose_mid_side_stereo() + * - FLAC__stream_encoder_set_apodization() + * - FLAC__stream_encoder_set_max_lpc_order() + * - FLAC__stream_encoder_set_qlp_coeff_precision() + * - FLAC__stream_encoder_set_do_qlp_coeff_prec_search() + * - FLAC__stream_encoder_set_do_escape_coding() + * - FLAC__stream_encoder_set_do_exhaustive_model_search() + * - FLAC__stream_encoder_set_min_residual_partition_order() + * - FLAC__stream_encoder_set_max_residual_partition_order() + * - FLAC__stream_encoder_set_rice_parameter_search_dist() + * + * The actual values set for each level are: + * + * + * + * + * + * + * + * + * + * + * + * + *
level + * do mid-side stereo + * loose mid-side stereo + * apodization + * max lpc order + * qlp coeff precision + * qlp coeff prec search + * escape coding + * exhaustive model search + * min residual partition order + * max residual partition order + * rice parameter search dist + *
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5) 8 0 false false false 0 6 0
7 true false tukey(0.5) 8 0 false false true 0 6 0
8 true false tukey(0.5) 12 0 false false true 0 6 0
+ * + * \default \c 5 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the blocksize to use while encoding. + * + * The number of samples to use per frame. Use \c 0 to let the encoder + * estimate a blocksize; this is usually best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set to \c true to enable mid-side encoding on stereo input. The + * number of channels must be 2 for this to have any effect. Set to + * \c false to use only independent channel coding. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set to \c true to enable adaptive switching between mid-side and + * left-right encoding on stereo input. Set to \c false to use + * exhaustive searching. Setting this to \c true requires + * FLAC__stream_encoder_set_do_mid_side_stereo() to also be set to + * \c true in order to have any effect. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Sets the apodization function(s) the encoder will use when windowing + * audio data for LPC analysis. + * + * The \a specification is a plain ASCII string which specifies exactly + * which functions to use. There may be more than one (up to 32), + * separated by \c ';' characters. Some functions take one or more + * comma-separated arguments in parentheses. + * + * The available functions are \c bartlett, \c bartlett_hann, + * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, + * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, + * \c rectangle, \c triangle, \c tukey(P), \c welch. + * + * For \c gauss(STDDEV), STDDEV specifies the standard deviation + * (0blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the maximum partition order to search when coding the residual. + * This is used in tandem with + * FLAC__stream_encoder_set_min_residual_partition_order(). + * + * The partition order determines the context size in the residual. + * The context size will be approximately blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Deprecated. Setting this value has no effect. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set an estimate of the total samples that will be encoded. + * This is merely an estimate and may be set to \c 0 if unknown. + * This value will be written to the STREAMINFO block before encoding, + * and can remove the need for the caller to rewrite the value later + * if the value is known before encoding. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value); + +/** Set the metadata blocks to be emitted to the stream before encoding. + * A value of \c NULL, \c 0 implies no metadata; otherwise, supply an + * array of pointers to metadata blocks. The array is non-const since + * the encoder may need to change the \a is_last flag inside them, and + * in some cases update seek point offsets. Otherwise, the encoder will + * not modify or free the blocks. It is up to the caller to free the + * metadata blocks after encoding finishes. + * + * \note + * The encoder stores only copies of the pointers in the \a metadata array; + * the metadata blocks themselves must survive at least until after + * FLAC__stream_encoder_finish() returns. Do not free the blocks until then. + * + * \note + * The STREAMINFO block is always written and no STREAMINFO block may + * occur in the supplied array. + * + * \note + * By default the encoder does not create a SEEKTABLE. If one is supplied + * in the \a metadata array, but the client has specified that it does not + * support seeking, then the SEEKTABLE will be written verbatim. However + * by itself this is not very useful as the client will not know the stream + * offsets for the seekpoints ahead of time. In order to get a proper + * seektable the client must support seeking. See next note. + * + * \note + * SEEKTABLE blocks are handled specially. Since you will not know + * the values for the seek point stream offsets, you should pass in + * a SEEKTABLE 'template', that is, a SEEKTABLE object with the + * required sample numbers (or placeholder points), with \c 0 for the + * \a frame_samples and \a stream_offset fields for each point. If the + * client has specified that it supports seeking by providing a seek + * callback to FLAC__stream_encoder_init_stream() or both seek AND read + * callback to FLAC__stream_encoder_init_ogg_stream() (or by using + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE()), + * then while it is encoding the encoder will fill the stream offsets in + * for you and when encoding is finished, it will seek back and write the + * real values into the SEEKTABLE block in the stream. There are helper + * routines for manipulating seektable template blocks; see metadata.h: + * FLAC__metadata_object_seektable_template_*(). If the client does + * not support seeking, the SEEKTABLE will have inaccurate offsets which + * will slow down or remove the ability to seek in the FLAC stream. + * + * \note + * The encoder instance \b will modify the first \c SEEKTABLE block + * as it transforms the template to a valid seektable while encoding, + * but it is still up to the caller to free all metadata blocks after + * encoding. + * + * \note + * A VORBIS_COMMENT block may be supplied. The vendor string in it + * will be ignored. libFLAC will use it's own vendor string. libFLAC + * will not modify the passed-in VORBIS_COMMENT's vendor string, it + * will simply write it's own into the stream. If no VORBIS_COMMENT + * block is present in the \a metadata array, libFLAC will write an + * empty one, containing only the vendor string. + * + * \note The Ogg FLAC mapping requires that the VORBIS_COMMENT block be + * the second metadata block of the stream. The encoder already supplies + * the STREAMINFO block automatically. If \a metadata does not contain a + * VORBIS_COMMENT block, the encoder will supply that too. Otherwise, if + * \a metadata does contain a VORBIS_COMMENT block and it is not the + * first, the init function will reorder \a metadata by moving the + * VORBIS_COMMENT block to the front; the relative ordering of the other + * blocks will remain as they were. + * + * \note The Ogg FLAC mapping limits the number of metadata blocks per + * stream to \c 65535. If \a num_blocks exceeds this the function will + * return \c false. + * + * \default \c NULL, 0 + * \param encoder An encoder instance to set. + * \param metadata See above. + * \param num_blocks See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + * \c false if the encoder is already initialized, or if + * \a num_blocks > 65535 if encoding to Ogg FLAC, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks); + +/** Get the current encoder state. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamEncoderState + * The current encoder state. + */ +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder); + +/** Get the state of the verify stream decoder. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The verify stream decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder); + +/** Get the current encoder state as a C string. + * This version automatically resolves + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR by getting the + * verify decoder's state. + * + * \param encoder A encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval const char * + * The encoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder); + +/** Get relevant values about the nature of a verify decoder error. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. The arguments should + * be addresses in which the stats will be returned, or NULL if value + * is not desired. + * + * \param encoder An encoder instance to query. + * \param absolute_sample The absolute sample number of the mismatch. + * \param frame_number The number of the frame in which the mismatch occurred. + * \param channel The channel in which the mismatch occurred. + * \param sample The number of the sample (relative to the frame) in + * which the mismatch occurred. + * \param expected The expected value for the sample in question. + * \param got The actual value returned by the decoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got); + +/** Get the "verify" flag. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * See FLAC__stream_encoder_set_verify(). + */ +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); + +/** Get the frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of pointers to each channel's signal. + * \param samples The number of samples in one channel. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples); + +/** Submit data for encoding. + * This version allows you to supply the input data where the channels + * are interleaved into a single array (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * The samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 + * and the last value channelN_sampleM. Each sample should be a signed + * integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the + * resolution is 16 bits per sample, the samples should all be in the + * range [-32768,32767]. + * + * For applications where channel order is important, channels must + * follow the order as described in the + * frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of channel-interleaved data (see above). + * \param samples The number of samples in one channel, the same as for + * FLAC__stream_encoder_process(). For example, if + * encoding two channels, \c 1000 \a samples corresponds + * to a \a buffer of 2000 values. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/share/alloc.h b/lib/flac-1.2.1/include/share/alloc.h new file mode 100755 index 0000000..812aa69 --- /dev/null +++ b/lib/flac-1.2.1/include/share/alloc.h @@ -0,0 +1,212 @@ +/* alloc - Convenience routines for safely allocating memory + * Copyright (C) 2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FLAC__SHARE__ALLOC_H +#define FLAC__SHARE__ALLOC_H + +#if HAVE_CONFIG_H +# include +#endif + +/* WATCHOUT: for c++ you may have to #define __STDC_LIMIT_MACROS 1 real early + * before #including this file, otherwise SIZE_MAX might not be defined + */ + +#include /* for SIZE_MAX */ +#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__ +#include /* for SIZE_MAX in case limits.h didn't get it */ +#endif +#include /* for size_t, malloc(), etc */ + +#ifndef SIZE_MAX +# ifndef SIZE_T_MAX +# ifdef _MSC_VER +# define SIZE_T_MAX UINT_MAX +# else +# error +# endif +# endif +# define SIZE_MAX SIZE_T_MAX +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* avoid malloc()ing 0 bytes, see: + * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 +*/ +static FLaC__INLINE void *safe_malloc_(size_t size) +{ + /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(!size) + size++; + return malloc(size); +} + +static FLaC__INLINE void *safe_calloc_(size_t nmemb, size_t size) +{ + if(!nmemb || !size) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + return calloc(nmemb, size); +} + +/*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */ + +static FLaC__INLINE void *safe_malloc_add_2op_(size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) + return 0; + return safe_malloc_(size2); +} + +static FLaC__INLINE void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return safe_malloc_(size3); +} + +static FLaC__INLINE void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return safe_malloc_(size4); +} + +static FLaC__INLINE void *safe_malloc_mul_2op_(size_t size1, size_t size2) +#if 0 +needs support for cases where sizeof(size_t) != 4 +{ + /* could be faster #ifdef'ing off SIZEOF_SIZE_T */ + if(sizeof(size_t) == 4) { + if ((double)size1 * (double)size2 < 4294967296.0) + return malloc(size1*size2); + } + return 0; +} +#else +/* better? */ +{ + if(!size1 || !size2) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} +#endif + +static FLaC__INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2 || !size3) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + size1 *= size2; + if(size1 > SIZE_MAX / size3) + return 0; + return malloc(size1*size3); +} + +/* size1*size2 + size3 */ +static FLaC__INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2) + return safe_malloc_(size3); + if(size1 > SIZE_MAX / size2) + return 0; + return safe_malloc_add_2op_(size1*size2, size3); +} + +/* size1 * (size2 + size3) */ +static FLaC__INLINE void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_malloc_mul_2op_(size1, size2); +} + +static FLaC__INLINE void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) + return 0; + return realloc(ptr, size2); +} + +static FLaC__INLINE void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return realloc(ptr, size3); +} + +static FLaC__INLINE void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return realloc(ptr, size4); +} + +static FLaC__INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +{ + if(!size1 || !size2) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + if(size1 > SIZE_MAX / size2) + return 0; + return realloc(ptr, size1*size2); +} + +/* size1 * (size2 + size3) */ +static FLaC__INLINE void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_realloc_mul_2op_(ptr, size1, size2); +} + +#endif diff --git a/lib/flac-1.2.1/include/share/getopt.h b/lib/flac-1.2.1/include/share/getopt.h new file mode 100755 index 0000000..1b314b2 --- /dev/null +++ b/lib/flac-1.2.1/include/share/getopt.h @@ -0,0 +1,184 @@ +/* + NOTE: + I cannot get the vanilla getopt code to work (i.e. compile only what + is needed and not duplicate symbols found in the standard library) + on all the platforms that FLAC supports. In particular the gating + of code with the ELIDE_CODE #define is not accurate enough on systems + that are POSIX but not glibc. If someone has a patch that works on + GNU/Linux, Darwin, AND Solaris please submit it on the project page: + http://sourceforge.net/projects/flac + + In the meantime I have munged the global symbols and removed gates + around code, while at the same time trying to touch the original as + little as possible. +*/ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef SHARE__GETOPT_H +#define SHARE__GETOPT_H + +/*[JEC] was:#ifndef __need_getopt*/ +/*[JEC] was:# define _GETOPT_H 1*/ +/*[JEC] was:#endif*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `share__getopt' to the caller. + When `share__getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *share__optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `share__getopt'. + + On entry to `share__getopt', zero means this is the first call; initialize. + + When `share__getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `share__optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int share__optind; + +/* Callers store zero here to inhibit the error message `share__getopt' prints + for unrecognized options. */ + +extern int share__opterr; + +/* Set to an option character which was unrecognized. */ + +extern int share__optopt; + +/*[JEC] was:#ifndef __need_getopt */ +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to share__getopt_long or share__getopt_long_only is a vector + of `struct share__option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + share__no_argument (or 0) if the option does not take an argument, + share__required_argument (or 1) if the option requires an argument, + share__optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `share__optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `share__getopt' + returns the contents of the `val' field. */ + +struct share__option +{ +# if defined __STDC__ && __STDC__ + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct share__option'. */ + +# define share__no_argument 0 +# define share__required_argument 1 +# define share__optional_argument 2 +/*[JEC] was:#endif*/ /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `share__optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `share__optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `share__getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `share__getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `share__getopt'. */ + +/*[JEC] was:#if defined __STDC__ && __STDC__*/ +/*[JEC] was:# ifdef __GNU_LIBRARY__*/ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int share__getopt (int argc, char *const *argv, const char *shortopts); +/*[JEC] was:# else*/ /* not __GNU_LIBRARY__ */ +/*[JEC] was:extern int getopt ();*/ +/*[JEC] was:# endif*/ /* __GNU_LIBRARY__ */ + +/*[JEC] was:# ifndef __need_getopt*/ +extern int share__getopt_long (int argc, char *const *argv, const char *shortopts, + const struct share__option *longopts, int *longind); +extern int share__getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct share__option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int share___getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct share__option *longopts, int *longind, + int long_only); +/*[JEC] was:# endif*/ +/*[JEC] was:#else*/ /* not __STDC__ */ +/*[JEC] was:extern int getopt ();*/ +/*[JEC] was:# ifndef __need_getopt*/ +/*[JEC] was:extern int getopt_long ();*/ +/*[JEC] was:extern int getopt_long_only ();*/ + +/*[JEC] was:extern int _getopt_internal ();*/ +/*[JEC] was:# endif*/ +/*[JEC] was:#endif*/ /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +/*[JEC] was:#undef __need_getopt*/ + +#endif /* getopt.h */ diff --git a/lib/flac-1.2.1/include/share/grabbag.h b/lib/flac-1.2.1/include/share/grabbag.h new file mode 100755 index 0000000..42c6998 --- /dev/null +++ b/lib/flac-1.2.1/include/share/grabbag.h @@ -0,0 +1,29 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SHARE__GRABBAG_H +#define SHARE__GRABBAG_H + +/* These can't be included by themselves, only from within grabbag.h */ +#include "grabbag/cuesheet.h" +#include "grabbag/file.h" +#include "grabbag/picture.h" +#include "grabbag/replaygain.h" +#include "grabbag/seektable.h" + +#endif diff --git a/lib/flac-1.2.1/include/share/grabbag/cuesheet.h b/lib/flac-1.2.1/include/share/grabbag/cuesheet.h new file mode 100755 index 0000000..698d49b --- /dev/null +++ b/lib/flac-1.2.1/include/share/grabbag/cuesheet.h @@ -0,0 +1,42 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__CUESHEET_H +#define GRABBAG__CUESHEET_H + +#include +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames); +void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames); + +FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset); + +void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/share/grabbag/file.h b/lib/flac-1.2.1/include/share/grabbag/file.h new file mode 100755 index 0000000..c8b149f --- /dev/null +++ b/lib/flac-1.2.1/include/share/grabbag/file.h @@ -0,0 +1,63 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Convenience routines for manipulating files */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABAG__FILE_H +#define GRABAG__FILE_H + +/* needed because of off_t */ +#if HAVE_CONFIG_H +# include +#endif + +#include /* for off_t */ +#include /* for FILE */ +#include "FLAC/ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void grabbag__file_copy_metadata(const char *srcpath, const char *destpath); +off_t grabbag__file_get_filesize(const char *srcpath); +const char *grabbag__file_get_basename(const char *srcpath); + +/* read_only == false means "make file writable by user" + * read_only == true means "make file read-only for everyone" + */ +FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only); + +/* returns true iff stat() succeeds for both files and they have the same device and inode. */ +/* on windows, uses GetFileInformationByHandle() to compare */ +FLAC__bool grabbag__file_are_same(const char *f1, const char *f2); + +/* attempts to make writable before unlinking */ +FLAC__bool grabbag__file_remove_file(const char *filename); + +/* these will forcibly set stdin/stdout to binary mode (for OSes that require it) */ +FILE *grabbag__file_get_binary_stdin(void); +FILE *grabbag__file_get_binary_stdout(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/share/grabbag/picture.h b/lib/flac-1.2.1/include/share/grabbag/picture.h new file mode 100755 index 0000000..e7f4278 --- /dev/null +++ b/lib/flac-1.2.1/include/share/grabbag/picture.h @@ -0,0 +1,46 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__PICTURE_H +#define GRABBAG__PICTURE_H + +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* spec should be of the form "[TYPE]|MIME_TYPE|[DESCRIPTION]|[WIDTHxHEIGHTxDEPTH[/COLORS]]|FILE", e.g. + * "|image/jpeg|||cover.jpg" + * "4|image/jpeg||300x300x24|backcover.jpg" + * "|image/png|description|300x300x24/71|cover.png" + * "-->|image/gif||300x300x24/71|http://blah.blah.blah/cover.gif" + * + * empty type means default to FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER + * empty resolution spec means to get from the file (cannot get used with "-->" linked images) + * spec and error_message must not be NULL + */ +FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/share/grabbag/replaygain.h b/lib/flac-1.2.1/include/share/grabbag/replaygain.h new file mode 100755 index 0000000..ea8c935 --- /dev/null +++ b/lib/flac-1.2.1/include/share/grabbag/replaygain.h @@ -0,0 +1,72 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This wraps the replaygain_analysis lib, which is LGPL. This wrapper + * allows analysis of different input resolutions by automatically + * scaling the input signal + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__REPLAYGAIN_H +#define GRABBAG__REPLAYGAIN_H + +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED; + +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS; /* = "REPLAYGAIN_REFERENCE_LOUDNESS" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN; /* = "REPLAYGAIN_TRACK_GAIN" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK; /* = "REPLAYGAIN_TRACK_PEAK" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN; /* = "REPLAYGAIN_ALBUM_GAIN" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK; /* = "REPLAYGAIN_ALBUM_PEAK" */ + +FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency); + +FLAC__bool grabbag__replaygain_init(unsigned sample_frequency); + +/* 'bps' must be valid for FLAC, i.e. >=4 and <= 32 */ +FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples); + +void grabbag__replaygain_get_album(float *gain, float *peak); +void grabbag__replaygain_get_title(float *gain, float *peak); + +/* These three functions return an error string on error, or NULL if successful */ +const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata *block); +const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_reference(const char *filename, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime); + +FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *reference, double *gain, double *peak); +double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/share/grabbag/seektable.h b/lib/flac-1.2.1/include/share/grabbag/seektable.h new file mode 100755 index 0000000..8010fc9 --- /dev/null +++ b/lib/flac-1.2.1/include/share/grabbag/seektable.h @@ -0,0 +1,38 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Convenience routines for working with seek tables */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABAG__SEEKTABLE_H +#define GRABAG__SEEKTABLE_H + +#include "FLAC/format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +FLAC__bool grabbag__seektable_convert_specification_to_template(const char *spec, FLAC__bool only_explicit_placeholders, FLAC__uint64 total_samples_to_encode, unsigned sample_rate, FLAC__StreamMetadata *seektable_template, FLAC__bool *spec_has_real_points); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/flac-1.2.1/include/share/replaygain_analysis.h b/lib/flac-1.2.1/include/share/replaygain_analysis.h new file mode 100755 index 0000000..ad146d3 --- /dev/null +++ b/lib/flac-1.2.1/include/share/replaygain_analysis.h @@ -0,0 +1,59 @@ +/* + * ReplayGainAnalysis - analyzes input samples and give the recommended dB change + * Copyright (C) 2001 David Robinson and Glen Sawyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * concept and filter values by David Robinson (David@Robinson.org) + * -- blame him if you think the idea is flawed + * coding by Glen Sawyer (glensawyer@hotmail.com) 442 N 700 E, Provo, UT 84606 USA + * -- blame him if you think this runs too slowly, or the coding is otherwise flawed + * minor cosmetic tweaks to integrate with FLAC by Josh Coalson + * + * For an explanation of the concepts and the basic algorithms involved, go to: + * http://www.replaygain.org/ + */ + +#ifndef GAIN_ANALYSIS_H +#define GAIN_ANALYSIS_H + +#include + +#define GAIN_NOT_ENOUGH_SAMPLES -24601 +#define GAIN_ANALYSIS_ERROR 0 +#define GAIN_ANALYSIS_OK 1 + +#define INIT_GAIN_ANALYSIS_ERROR 0 +#define INIT_GAIN_ANALYSIS_OK 1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef float Float_t; /* Type used for filtering */ + +extern Float_t ReplayGainReferenceLoudness; /* in dB SPL, currently == 89.0 */ + +int InitGainAnalysis ( long samplefreq ); +int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ); +int ResetSampleFrequency ( long samplefreq ); +Float_t GetTitleGain ( void ); +Float_t GetAlbumGain ( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* GAIN_ANALYSIS_H */ diff --git a/lib/flac-1.2.1/include/share/replaygain_synthesis.h b/lib/flac-1.2.1/include/share/replaygain_synthesis.h new file mode 100755 index 0000000..ca1ae64 --- /dev/null +++ b/lib/flac-1.2.1/include/share/replaygain_synthesis.h @@ -0,0 +1,51 @@ +/* replaygain_synthesis - Routines for applying ReplayGain to a signal + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H +#define FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H + +#include /* for size_t */ +#include "FLAC/ordinals.h" + +#define FLAC_SHARE__MAX_SUPPORTED_CHANNELS 2 + +typedef enum { + NOISE_SHAPING_NONE = 0, + NOISE_SHAPING_LOW = 1, + NOISE_SHAPING_MEDIUM = 2, + NOISE_SHAPING_HIGH = 3 +} NoiseShaping; + +typedef struct { + const float* FilterCoeff; + FLAC__uint64 Mask; + double Add; + float Dither; + float ErrorHistory [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16]; /* 16th order Noise shaping */ + float DitherHistory [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16]; + int LastRandomNumber [FLAC_SHARE__MAX_SUPPORTED_CHANNELS]; + unsigned LastHistoryIndex; + NoiseShaping ShapingType; +} DitherContext; + +void FLAC__replaygain_synthesis__init_dither_context(DitherContext *dither, int bits, int shapingtype); + +/* scale = (float) pow(10., (double)replaygain * 0.05); */ +size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context); + +#endif diff --git a/lib/flac-1.2.1/include/share/utf8.h b/lib/flac-1.2.1/include/share/utf8.h new file mode 100755 index 0000000..7d6650d --- /dev/null +++ b/lib/flac-1.2.1/include/share/utf8.h @@ -0,0 +1,25 @@ +#ifndef SHARE__UTF8_H +#define SHARE__UTF8_H + +/* + * Convert a string between UTF-8 and the locale's charset. + * Invalid bytes are replaced by '#', and characters that are + * not available in the target encoding are replaced by '?'. + * + * If the locale's charset is not set explicitly then it is + * obtained using nl_langinfo(CODESET), where available, the + * environment variable CHARSET, or assumed to be US-ASCII. + * + * Return value of conversion functions: + * + * -1 : memory allocation failed + * 0 : data was converted exactly + * 1 : valid data was converted approximately (using '?') + * 2 : input was invalid (but still converted, using '#') + * 3 : unknown encoding (but still converted, using '?') + */ + +int utf8_encode(const char *from, char **to); +int utf8_decode(const char *from, char **to); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/bitmath.c b/lib/flac-1.2.1/src/libFLAC/bitmath.c new file mode 100755 index 0000000..27e25f0 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/bitmath.c @@ -0,0 +1,149 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/bitmath.h" +#include "FLAC/assert.h" + +/* An example of what FLAC__bitmath_ilog2() computes: + * + * ilog2( 0) = assertion failure + * ilog2( 1) = 0 + * ilog2( 2) = 1 + * ilog2( 3) = 1 + * ilog2( 4) = 2 + * ilog2( 5) = 2 + * ilog2( 6) = 2 + * ilog2( 7) = 2 + * ilog2( 8) = 3 + * ilog2( 9) = 3 + * ilog2(10) = 3 + * ilog2(11) = 3 + * ilog2(12) = 3 + * ilog2(13) = 3 + * ilog2(14) = 3 + * ilog2(15) = 3 + * ilog2(16) = 4 + * ilog2(17) = 4 + * ilog2(18) = 4 + */ +unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) +{ + unsigned l = 0; + FLAC__ASSERT(v > 0); + while(v >>= 1) + l++; + return l; +} + +unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +{ + unsigned l = 0; + FLAC__ASSERT(v > 0); + while(v >>= 1) + l++; + return l; +} + +/* An example of what FLAC__bitmath_silog2() computes: + * + * silog2(-10) = 5 + * silog2(- 9) = 5 + * silog2(- 8) = 4 + * silog2(- 7) = 4 + * silog2(- 6) = 4 + * silog2(- 5) = 4 + * silog2(- 4) = 3 + * silog2(- 3) = 3 + * silog2(- 2) = 2 + * silog2(- 1) = 2 + * silog2( 0) = 0 + * silog2( 1) = 2 + * silog2( 2) = 3 + * silog2( 3) = 3 + * silog2( 4) = 4 + * silog2( 5) = 4 + * silog2( 6) = 4 + * silog2( 7) = 4 + * silog2( 8) = 5 + * silog2( 9) = 5 + * silog2( 10) = 5 + */ +unsigned FLAC__bitmath_silog2(int v) +{ + while(1) { + if(v == 0) { + return 0; + } + else if(v > 0) { + unsigned l = 0; + while(v) { + l++; + v >>= 1; + } + return l+1; + } + else if(v == -1) { + return 2; + } + else { + v++; + v = -v; + } + } +} + +unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v) +{ + while(1) { + if(v == 0) { + return 0; + } + else if(v > 0) { + unsigned l = 0; + while(v) { + l++; + v >>= 1; + } + return l+1; + } + else if(v == -1) { + return 2; + } + else { + v++; + v = -v; + } + } +} diff --git a/lib/flac-1.2.1/src/libFLAC/bitreader.c b/lib/flac-1.2.1/src/libFLAC/bitreader.c new file mode 100755 index 0000000..92ae294 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/bitreader.c @@ -0,0 +1,1378 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for FMOD_memcpy(), FMOD_memset() */ +#ifdef _XENON +#include +#elif defined _MSC_VER +#include /* for ntohl() */ +#elif defined FLAC__SYS_DARWIN +#include /* for ntohl() */ +#elif defined __MINGW32__ +#include /* for ntohl() */ +#else +#include /* for ntohl() */ +#endif +#include "private/bitmath.h" +#include "private/bitreader.h" +#include "private/crc.h" +#include "FLAC/assert.h" + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS below to match */ +/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */ +/* also, some sections currently only have fast versions for 4 or 8 bytes per word */ +typedef FLAC__uint32 brword; +#define FLAC__BYTES_PER_WORD 4 +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_XENON) +#define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ntohl(x) +#endif +#endif +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) ( \ + (word) <= 0xffff ? \ + ( (word) <= 0xff? byte_to_unary_table[word] + 24 : byte_to_unary_table[(word) >> 8] + 16 ) : \ + ( (word) <= 0xffffff? byte_to_unary_table[word >> 16] + 8 : byte_to_unary_table[(word) >> 24] ) \ +) +/* this alternate might be slightly faster on some systems/compilers: */ +#define COUNT_ZERO_MSBS2(word) ( (word) <= 0xff ? byte_to_unary_table[word] + 24 : ((word) <= 0xffff ? byte_to_unary_table[(word) >> 8] + 16 : ((word) <= 0xffffff ? byte_to_unary_table[(word) >> 16] + 8 : byte_to_unary_table[(word) >> 24])) ) + + +/* + * This should be at least twice as large as the largest number of words + * required to represent any 'number' (in any encoding) you are going to + * read. With FLAC this is on the order of maybe a few hundred bits. + * If the buffer is smaller than that, the decoder won't be able to read + * in a whole number that is in a variable length encoding (e.g. Rice). + * But to be practical it should be at least 1K bytes. + * + * Increase this number to decrease the number of read callbacks, at the + * expense of using more memory. Or decrease for the reverse effect, + * keeping in mind the limit from the first paragraph. The optimal size + * also depends on the CPU cache size and other factors; some twiddling + * may be necessary to squeeze out the best performance. + */ +static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ + +static const unsigned char byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* WATCHOUT: assembly routines rely on the order in which these fields are declared */ +struct FLAC__BitReader { + /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ + /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ + brword *buffer; + unsigned capacity; /* in words */ + unsigned words; /* # of completed words in buffer */ + unsigned bytes; /* # of bytes in incomplete word at buffer[words] */ + unsigned consumed_words; /* #words ... */ + unsigned consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ + unsigned read_crc16; /* the running frame CRC */ + unsigned crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + FLAC__BitReaderReadCallback read_callback; + void *client_data; + FLAC__CPUInfo cpu_info; +}; + +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_XENON) +/* OPT: an MSVC built-in would be better */ +static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) +{ + x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF); + return (x>>16) | (x<<16); +} +static void local_swap32_block_(FLAC__uint32 *start, FLAC__uint32 len) +{ + __asm { + mov edx, start + mov ecx, len + test ecx, ecx +loop1: + jz done1 + mov eax, [edx] + bswap eax + mov [edx], eax + add edx, 4 + dec ecx + jmp short loop1 +done1: + } +} +#endif + +static FLaC__INLINE void crc16_update_word_(FLAC__BitReader *br, brword word) +{ + register unsigned crc = br->read_crc16; +#if FLAC__BYTES_PER_WORD == 4 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 24), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 24: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#elif FLAC__BYTES_PER_WORD == 8 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 56), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 48) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 40) & 0xff), crc); + case 24: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 32) & 0xff), crc); + case 32: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 24) & 0xff), crc); + case 40: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 48: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 56: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#else + for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8) + crc = FLAC__CRC16_UPDATE((unsigned)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc); + br->read_crc16 = crc; +#endif + br->crc16_align = 0; +} + +/* would be static except it needs to be called by asm routines */ +FLAC__bool bitreader_read_from_client_(void *context, FLAC__BitReader *br) +{ + unsigned start, end; + size_t bytes; + FLAC__byte *target; + + /* first shift the unconsumed buffer data toward the front as much as possible */ + if(br->consumed_words > 0) { + start = br->consumed_words; + end = br->words + (br->bytes? 1:0); + memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start)); + + br->words -= start; + br->consumed_words = 0; + } + + /* + * set the target for reading, taking into account word alignment and endianness + */ + bytes = (br->capacity - br->words) * FLAC__BYTES_PER_WORD - br->bytes; + if(bytes == 0) + return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY */ + target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes; + + /* before reading, if the existing reader looks like this (say brword is 32 bits wide) + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 (partial tail word is left-justified) + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown layed out as bytes sequentially in memory) + * buffer[LE]: 44 33 22 11 ?? ?? ?? 55 (?? being don't-care) + * ^^-------target, bytes=3 + * on LE machines, have to byteswap the odd tail word so nothing is + * overwritten: + */ +#if WORDS_BIGENDIAN +#else + if(br->bytes) + br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? + * buffer[LE]: 44 33 22 11 55 ?? ?? ?? + * ^^-------target, bytes=3 + */ + + /* read in the data; note that the callback may return a smaller number of bytes */ + if(!br->read_callback(context, target, &bytes, br->client_data)) + return false; + + /* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 55 66 77 88 99 AA BB CC DD EE FF ?? + * now have to byteswap on LE machines: + */ +#if WORDS_BIGENDIAN +#else + end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + (unsigned int)bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; +# if defined(_MSC_VER) && (FLAC__BYTES_PER_WORD == 4) && !defined(_WIN64) && !defined(_XENON) + if(br->cpu_info.type == FLAC__CPUINFO_TYPE_IA32 && br->cpu_info.data.ia32.bswap) { + start = br->words; + local_swap32_block_(br->buffer + start, end - start); + } + else +# endif + for(start = br->words; start < end; start++) + br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD + * finally we'll update the reader values: + */ + end = br->words*FLAC__BYTES_PER_WORD + br->bytes + (unsigned int)bytes; + br->words = end / FLAC__BYTES_PER_WORD; + br->bytes = end % FLAC__BYTES_PER_WORD; + + return true; +} + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitReader *FLAC__bitreader_new(void) +{ + FLAC__BitReader *br = (FLAC__BitReader*)calloc(1, sizeof(FLAC__BitReader)); + + /* calloc() implies: + FMOD_memset(br, 0, sizeof(FLAC__BitReader)); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; + */ + return br; +} + +void FLAC__bitreader_delete(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + FLAC__bitreader_free(br); + free(br); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitreader_init(void *context, FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd) +{ + FLAC__ASSERT(0 != br); + + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; + br->buffer = (brword*)malloc(sizeof(brword) * br->capacity); + if(br->buffer == 0) + return false; + br->read_callback = rcb; + br->client_data = cd; + br->cpu_info = cpu; + + return true; +} + +void FLAC__bitreader_free(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + if(0 != br->buffer) + free(br->buffer); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; +} + +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br) +{ + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + return true; +} + +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) +{ + unsigned i, j; + if(br == 0) { + fprintf(out, "bitreader is NULL\n"); + } + else { + fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits); + + for(i = 0; i < br->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(br->bytes > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < br->bytes*8; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & (1 << (br->bytes*8-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + + br->read_crc16 = (unsigned)seed; + br->crc16_align = br->consumed_bits; +} + +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + FLAC__ASSERT(br->crc16_align <= br->consumed_bits); + + /* CRC any tail bytes in a partially-consumed word */ + if(br->consumed_bits) { + const brword tail = br->buffer[br->consumed_words]; + for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) + br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); + } + return br->read_crc16; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) +{ + return ((br->consumed_bits & 7) == 0); +} + +FLaC__INLINE unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +{ + return 8 - (br->consumed_bits & 7); +} + +FLaC__INLINE unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +{ + return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(void *context, FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + FLAC__ASSERT(bits <= 32); + FLAC__ASSERT((br->capacity*FLAC__BITS_PER_WORD) * 2 >= bits); + FLAC__ASSERT(br->consumed_words <= br->words); + + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + if(bits == 0) { /* OPT: investigate if this can ever happen, maybe change to assertion */ + *val = 0; + return true; + } + + while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) { + if(!bitreader_read_from_client_(context, br)) + return false; + } + if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - br->consumed_bits; + const brword word = br->buffer[br->consumed_words]; + if(bits < n) { + *val = (word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits); + br->consumed_bits += bits; + return true; + } + *val = word & (FLAC__WORD_ALL_ONES >> br->consumed_bits); + bits -= n; + crc16_update_word_(br, word); + br->consumed_words++; + br->consumed_bits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + *val <<= bits; + *val |= (br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + } + return true; + } + else { + const brword word = br->buffer[br->consumed_words]; + if(bits < FLAC__BITS_PER_WORD) { + *val = word >> (FLAC__BITS_PER_WORD-bits); + br->consumed_bits = bits; + return true; + } + /* at this point 'bits' must be == FLAC__BITS_PER_WORD; because of previous assertions, it can't be larger */ + *val = word; + crc16_update_word_(br, word); + br->consumed_words++; + return true; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(br->consumed_bits + bits <= br->bytes*8); + *val = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits); + br->consumed_bits += bits; + return true; + } + else { + *val = br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits); + br->consumed_bits += bits; + return true; + } + } +} + +FLAC__bool FLAC__bitreader_read_raw_int32(void *context, FLAC__BitReader *br, FLAC__int32 *val, unsigned bits) +{ + /* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */ + if(!FLAC__bitreader_read_raw_uint32(context, br, (FLAC__uint32*)val, bits)) + return false; + /* sign-extend: */ + *val <<= (32-bits); + *val >>= (32-bits); + return true; +} + +FLAC__bool FLAC__bitreader_read_raw_uint64(void *context, FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits) +{ + FLAC__uint32 hi, lo; + + if(bits > 32) { + if(!FLAC__bitreader_read_raw_uint32(context, br, &hi, bits-32)) + return false; + if(!FLAC__bitreader_read_raw_uint32(context, br, &lo, 32)) + return false; + *val = hi; + *val <<= 32; + *val |= lo; + } + else { + if(!FLAC__bitreader_read_raw_uint32(context, br, &lo, bits)) + return false; + *val = lo; + } + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_uint32_little_endian(void *context, FLAC__BitReader *br, FLAC__uint32 *val) +{ + FLAC__uint32 x8, x32 = 0; + + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitreader_read_raw_uint32(context, br, &x32, 8)) + return false; + + if(!FLAC__bitreader_read_raw_uint32(context, br, &x8, 8)) + return false; + x32 |= (x8 << 8); + + if(!FLAC__bitreader_read_raw_uint32(context, br, &x8, 8)) + return false; + x32 |= (x8 << 16); + + if(!FLAC__bitreader_read_raw_uint32(context, br, &x8, 8)) + return false; + x32 |= (x8 << 24); + + *val = x32; + return true; +} + +FLAC__bool FLAC__bitreader_skip_bits_no_crc(void *context, FLAC__BitReader *br, unsigned bits) +{ + /* + * OPT: a faster implementation is possible but probably not that useful + * since this is only called a couple of times in the metadata readers. + */ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + if(bits > 0) { + const unsigned n = br->consumed_bits & 7; + unsigned m; + FLAC__uint32 x; + + if(n != 0) { + m = min(8-n, bits); + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, m)) + return false; + bits -= m; + } + m = bits / 8; + if(m > 0) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(context, br, m)) + return false; + bits %= 8; + } + if(bits > 0) { + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, bits)) + return false; + } + } + + return true; +} + +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(void *context, FLAC__BitReader *br, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: skip over partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: skip whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + br->consumed_words++; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(context, br)) + return false; + } + /* step 3: skip any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(void *context, FLAC__BitReader *br, FLAC__byte *val, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: read from partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: read whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + const brword word = br->buffer[br->consumed_words++]; +#if FLAC__BYTES_PER_WORD == 4 + val[0] = (FLAC__byte)(word >> 24); + val[1] = (FLAC__byte)(word >> 16); + val[2] = (FLAC__byte)(word >> 8); + val[3] = (FLAC__byte)word; +#elif FLAC__BYTES_PER_WORD == 8 + val[0] = (FLAC__byte)(word >> 56); + val[1] = (FLAC__byte)(word >> 48); + val[2] = (FLAC__byte)(word >> 40); + val[3] = (FLAC__byte)(word >> 32); + val[4] = (FLAC__byte)(word >> 24); + val[5] = (FLAC__byte)(word >> 16); + val[6] = (FLAC__byte)(word >> 8); + val[7] = (FLAC__byte)word; +#else + for(x = 0; x < FLAC__BYTES_PER_WORD; x++) + val[x] = (FLAC__byte)(word >> (8*(FLAC__BYTES_PER_WORD-x-1))); +#endif + val += FLAC__BYTES_PER_WORD; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(context, br)) + return false; + } + /* step 3: read any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(void *context, FLAC__BitReader *br, unsigned *val) +#if 0 /* slow but readable version */ +{ + unsigned bit; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + if(bit) + break; + else + *val++; + } + return true; +} +#else +{ + unsigned i; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[br->consumed_words] << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + } + return true; + } + else { + *val += FLAC__BITS_PER_WORD - br->consumed_bits; + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + return true; + } + else { + *val += end - br->consumed_bits; + br->consumed_bits += end; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + if(!bitreader_read_from_client_(context, br)) + return false; + } +} +#endif + +FLAC__bool FLAC__bitreader_read_rice_signed(void *context, FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned uval; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(parameter <= 31); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(context, br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(context, br, &lsbs, parameter)) + return false; + + /* compose the value */ + uval = (msbs << parameter) | lsbs; + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +/* this is by far the most heavily used reader call. it ain't pretty but it's fast */ +/* a lot of the logic is copied, then adapted, from FLAC__bitreader_read_unary_unsigned() and FLAC__bitreader_read_raw_uint32() */ +FLAC__bool FLAC__bitreader_read_rice_signed_block(void *context, FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) +/* OPT: possibly faster version for use with MSVC */ +#ifdef _MSC_VER +{ + unsigned i; + unsigned uval = 0; + unsigned bits; /* the # of binary LSBs left to read to finish a rice codeword */ + + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitwriter functions that use them, and before returning */ + register unsigned cwords; + register unsigned cbits; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more that 2 words, so we don't have to loop to read it */ + + if(nvals == 0) + return true; + + cbits = br->consumed_bits; + cwords = br->consumed_words; + + while(1) { + + /* read unary part */ + while(1) { + while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[cwords] << cbits; + if(b) { +#if 0 /* slower, probably due to bad register allocation... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 + __asm { + bsr eax, b + not eax + and eax, 31 + mov i, eax + } +#else + i = COUNT_ZERO_MSBS(b); +#endif + uval += i; + bits = parameter; + i++; + cbits += i; + if(cbits == FLAC__BITS_PER_WORD) { + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + } + goto break1; + } + else { + uval += FLAC__BITS_PER_WORD - cbits; + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[cwords] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << cbits; + if(b) { + i = COUNT_ZERO_MSBS(b); + uval += i; + bits = parameter; + i++; + cbits += i; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + goto break1; + } + else { + uval += end - cbits; + cbits += end; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(context, br)) + return false; + cwords = br->consumed_words; + } +break1: + /* read binary part */ + FLAC__ASSERT(cwords <= br->words); + + if(bits) { + while((br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits < bits) { + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(context, br)) + return false; + cwords = br->consumed_words; + } + if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - cbits; + const brword word = br->buffer[cwords]; + if(bits < n) { + uval <<= bits; + uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-bits); + cbits += bits; + goto break2; + } + uval <<= n; + uval |= word & (FLAC__WORD_ALL_ONES >> cbits); + bits -= n; + crc16_update_word_(br, word); + cwords++; + cbits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + uval <<= bits; + uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits)); + cbits = bits; + } + goto break2; + } + else { + FLAC__ASSERT(bits < FLAC__BITS_PER_WORD); + uval <<= bits; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); + cbits = bits; + goto break2; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + uval <<= bits; + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(cbits + bits <= br->bytes*8); + uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-bits); + cbits += bits; + goto break2; + } + else { + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); + cbits += bits; + goto break2; + } + } + } +break2: + /* compose the value */ + *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + + /* are we done? */ + --nvals; + if(nvals == 0) { + br->consumed_bits = cbits; + br->consumed_words = cwords; + return true; + } + + uval = 0; + ++vals; + + } +} +#else +{ + unsigned i; + unsigned uval = 0; + + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitwriter functions that use them, and before returning */ + register unsigned cwords; + register unsigned cbits; + unsigned ucbits; /* keep track of the number of unconsumed bits in the buffer */ + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it */ + + if(nvals == 0) + return true; + + cbits = br->consumed_bits; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + + while(1) { + + /* read unary part */ + while(1) { + while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[cwords] << cbits; + if(b) { +#if 0 /* is not discernably faster... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 && defined __GNUC__ + asm volatile ( + "bsrl %1, %0;" + "notl %0;" + "andl $31, %0;" + : "=r"(i) + : "r"(b) + ); +#else + i = COUNT_ZERO_MSBS(b); +#endif + uval += i; + cbits += i; + cbits++; /* skip over stop bit */ + if(cbits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(cbits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + } + goto break1; + } + else { + uval += FLAC__BITS_PER_WORD - cbits; + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[cwords] & ~(FLAC__WORD_ALL_ONES >> end)) << cbits; + if(b) { + i = COUNT_ZERO_MSBS(b); + uval += i; + cbits += i; + cbits++; /* skip over stop bit */ + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + goto break1; + } + else { + uval += end - cbits; + cbits += end; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(context, br)) + return false; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + uval; + /* + uval to offset our count by the # of unary bits already + * consumed before the read, because we will add these back + * in all at once at break1 + */ + } +break1: + ucbits -= uval; + ucbits--; /* account for stop bit */ + + /* read binary part */ + FLAC__ASSERT(cwords <= br->words); + + if(parameter) { + while(ucbits < parameter) { + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(context, br)) + return false; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + } + if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + if(cbits) { + /* this also works when consumed_bits==0, it's just slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - cbits; + const brword word = br->buffer[cwords]; + if(parameter < n) { + uval <<= parameter; + uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-parameter); + cbits += parameter; + } + else { + uval <<= n; + uval |= word & (FLAC__WORD_ALL_ONES >> cbits); + crc16_update_word_(br, word); + cwords++; + cbits = parameter - n; + if(cbits) { /* parameter > n, i.e. if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + uval <<= cbits; + uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits)); + } + } + } + else { + cbits = parameter; + uval <<= parameter; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'parameter' + * bits available to read, which makes this case simpler. + */ + uval <<= parameter; + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(cbits + parameter <= br->bytes*8); + uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-parameter); + cbits += parameter; + } + else { + cbits = parameter; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + } + } + } + + ucbits -= parameter; + + /* compose the value */ + *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + + /* are we done? */ + --nvals; + if(nvals == 0) { + br->consumed_bits = cbits; + br->consumed_words = cwords; + return true; + } + + uval = 0; + ++vals; + + } +} +#endif + +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned bit, uval, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + uval = msbs * parameter + lsbs; + } + + /* unfold unsigned to signed */ + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter) +{ + FLAC__uint32 lsbs, msbs = 0; + unsigned bit, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + *val = msbs * parameter + lsbs; + } + + return true; +} +#endif /* UNUSED */ + +/* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint32(void *context, FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint32 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else { + *val = 0xffffffff; + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = 0xffffffff; + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint64(void *context, FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint64 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */ + v = 0; + i = 6; + } + else { + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(context, br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} diff --git a/lib/flac-1.2.1/src/libFLAC/cpu.c b/lib/flac-1.2.1/src/libFLAC/cpu.c new file mode 100755 index 0000000..b04807c --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/cpu.c @@ -0,0 +1,418 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" +#include +#include + +#if defined FLAC__CPU_IA32 +# include +#elif defined FLAC__CPU_PPC +# if !defined FLAC__NO_ASM +# if defined FLAC__SYS_DARWIN +# include +# include +# include +# include +# include +# ifndef CPU_SUBTYPE_POWERPC_970 +# define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) +# endif +# else /* FLAC__SYS_DARWIN */ + +# include +# include + +static sigjmp_buf jmpbuf; +static volatile sig_atomic_t canjump = 0; + +static void sigill_handler (int sig) +{ + if (!canjump) { + signal (sig, SIG_DFL); + raise (sig); + } + canjump = 0; + siglongjmp (jmpbuf, 1); +} +# endif /* FLAC__SYS_DARWIN */ +# endif /* FLAC__NO_ASM */ +#endif /* FLAC__CPU_PPC */ + +#if defined (__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#include +#include +#endif + +#if defined(__APPLE__) +/* how to get sysctlbyname()? */ +#endif + +/* these are flags in EDX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; +/* these are flags in ECX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; +/* these are flags in EDX of CPUID AX=80000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; + + +/* + * Extra stuff needed for detection of OS support for SSE on IA-32 + */ +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS +# if defined(__linux__) +/* + * If the OS doesn't support SSE, we will get here with a SIGILL. We + * modify the return address to jump over the offending SSE instruction + * and also the operation following it that indicates the instruction + * executed successfully. In this way we use no global variables and + * stay thread-safe. + * + * 3 + 3 + 6: + * 3 bytes for "xorps xmm0,xmm0" + * 3 bytes for estimate of how long the follwing "inc var" instruction is + * 6 bytes extra in case our estimate is wrong + * 12 bytes puts us in the NOP "landing zone" + */ +# undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */ +# ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR + static void sigill_handler_sse_os(int signal, struct sigcontext sc) + { + (void)signal; + sc.eip += 3 + 3 + 6; + } +# else +# include + static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc) + { + (void)signal, (void)si; + ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6; + } +# endif +# elif defined(_MSC_VER) +# include +# undef USE_TRY_CATCH_FLAVOR /* #define this to use the try/catch method for catching illegal opcode exception */ +# ifdef USE_TRY_CATCH_FLAVOR +# else + LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep) + { + if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { + ep->ContextRecord->Eip += 3 + 3 + 6; + return EXCEPTION_CONTINUE_EXECUTION; + } + return EXCEPTION_CONTINUE_SEARCH; + } +# endif +# endif +#endif + + +void FLAC__cpu_info(FLAC__CPUInfo *info) +{ +/* + * IA32-specific + */ +#ifdef FLAC__CPU_IA32 + info->type = FLAC__CPUINFO_TYPE_IA32; +#if !defined FLAC__NO_ASM && defined FLAC__HAS_NASM + info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ + info->data.ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false; + info->data.ia32.bswap = info->data.ia32.cpuid; /* CPUID => BSWAP since it came after */ + info->data.ia32.cmov = false; + info->data.ia32.mmx = false; + info->data.ia32.fxsr = false; + info->data.ia32.sse = false; + info->data.ia32.sse2 = false; + info->data.ia32.sse3 = false; + info->data.ia32.ssse3 = false; + info->data.ia32._3dnow = false; + info->data.ia32.ext3dnow = false; + info->data.ia32.extmmx = false; + if(info->data.ia32.cpuid) { + /* http://www.sandpile.org/ia32/cpuid.htm */ + FLAC__uint32 flags_edx, flags_ecx; + FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); + info->data.ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; + info->data.ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; + info->data.ia32.fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; + info->data.ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; + info->data.ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; + info->data.ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; + info->data.ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; + +#ifdef FLAC__USE_3DNOW + flags_edx = FLAC__cpu_info_extended_amd_asm_ia32(); + info->data.ia32._3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW )? true : false; + info->data.ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false; + info->data.ia32.extmmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX )? true : false; +#else + info->data.ia32._3dnow = info->data.ia32.ext3dnow = info->data.ia32.extmmx = false; +#endif + +#ifdef DEBUG_VERBOSE + fprintf(stderr, "CPU info (IA-32):\n"); + fprintf(stderr, " CPUID ...... %c\n", info->data.ia32.cpuid ? 'Y' : 'n'); + fprintf(stderr, " BSWAP ...... %c\n", info->data.ia32.bswap ? 'Y' : 'n'); + fprintf(stderr, " CMOV ....... %c\n", info->data.ia32.cmov ? 'Y' : 'n'); + fprintf(stderr, " MMX ........ %c\n", info->data.ia32.mmx ? 'Y' : 'n'); + fprintf(stderr, " FXSR ....... %c\n", info->data.ia32.fxsr ? 'Y' : 'n'); + fprintf(stderr, " SSE ........ %c\n", info->data.ia32.sse ? 'Y' : 'n'); + fprintf(stderr, " SSE2 ....... %c\n", info->data.ia32.sse2 ? 'Y' : 'n'); + fprintf(stderr, " SSE3 ....... %c\n", info->data.ia32.sse3 ? 'Y' : 'n'); + fprintf(stderr, " SSSE3 ...... %c\n", info->data.ia32.ssse3 ? 'Y' : 'n'); + fprintf(stderr, " 3DNow! ..... %c\n", info->data.ia32._3dnow ? 'Y' : 'n'); + fprintf(stderr, " 3DNow!-ext . %c\n", info->data.ia32.ext3dnow? 'Y' : 'n'); + fprintf(stderr, " 3DNow!-MMX . %c\n", info->data.ia32.extmmx ? 'Y' : 'n'); +#endif + + /* + * now have to check for OS support of SSE/SSE2 + */ + if(info->data.ia32.fxsr || info->data.ia32.sse || info->data.ia32.sse2) { +#if defined FLAC__NO_SSE_OS + /* assume user knows better than us; turn it off */ + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined FLAC__SSE_OS + /* assume user knows better than us; leave as detected above */ +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__) + int sse = 0; + size_t len; + /* at least one of these must work: */ + len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); + len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined(__NetBSD__) || defined (__OpenBSD__) +# if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__) + int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; + size_t len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + else { /* double-check SSE2 */ + mib[1] = CPU_SSE2; + len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + } +# else + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +# endif +#elif defined(__linux__) + int sse = 0; + struct sigaction sigill_save; +#ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR + if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR) +#else + struct sigaction sigill_sse; + sigill_sse.sa_sigaction = sigill_handler_sse_os; + __sigemptyset(&sigill_sse.sa_mask); + sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ + if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) +#endif + { + /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ + /* see sigill_handler_sse_os() for an explanation of the following: */ + asm volatile ( + "xorl %0,%0\n\t" /* for some reason, still need to do this to clear 'sse' var */ + "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ + "incl %0\n\t" /* SIGILL handler will jump over this */ + /* landing zone */ + "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ + "nop\n\t" + "nop" /* SIGILL jump lands here if "inc" is 1 byte */ + : "=r"(sse) + : "r"(sse) + ); + + sigaction(SIGILL, &sigill_save, NULL); + } + + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined(_MSC_VER) +# ifdef USE_TRY_CATCH_FLAVOR + _try { + __asm { +# if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ + _emit 0x0F + _emit 0x57 + _emit 0xC0 +# else + xorps xmm0,xmm0 +# endif + } + } + _except(EXCEPTION_EXECUTE_HANDLER) { + if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + } +# else + int sse = 0; + LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os); + /* see GCC version above for explanation */ + /* http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx */ + /* http://www.codeproject.com/cpp/gccasm.asp */ + /* http://www.hick.org/~mmiller/msvc_inline_asm.html */ + __asm { +# if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ + _emit 0x0F + _emit 0x57 + _emit 0xC0 +# else + xorps xmm0,xmm0 +# endif + inc sse + nop + nop + nop + nop + nop + nop + nop + nop + nop + } + SetUnhandledExceptionFilter(save); + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +# endif +#else + /* no way to test, disable to be safe */ + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#endif +#ifdef DEBUG_VERBOSE + fprintf(stderr, " SSE OS sup . %c\n", info->data.ia32.sse ? 'Y' : 'n'); +#endif + + } + } +#else + info->use_asm = false; +#endif + +/* + * PPC-specific + */ +#elif defined FLAC__CPU_PPC + info->type = FLAC__CPUINFO_TYPE_PPC; +# if !defined FLAC__NO_ASM + info->use_asm = true; +# ifdef FLAC__USE_ALTIVEC +# if defined FLAC__SYS_DARWIN + { + int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT }; + size_t len = sizeof(val); + info->data.ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val); + } + { + host_basic_info_data_t hostInfo; + mach_msg_type_number_t infoCount; + + infoCount = HOST_BASIC_INFO_COUNT; + host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); + + info->data.ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970); + } +# else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */ + { + /* no Darwin, do it the brute-force way */ + /* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */ + info->data.ppc.altivec = 0; + info->data.ppc.ppc64 = 0; + + signal (SIGILL, sigill_handler); + canjump = 0; + if (!sigsetjmp (jmpbuf, 1)) { + canjump = 1; + + asm volatile ( + "mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : + : "r" (-1) + ); + + info->data.ppc.altivec = 1; + } + canjump = 0; + if (!sigsetjmp (jmpbuf, 1)) { + int x = 0; + canjump = 1; + + /* PPC64 hardware implements the cntlzd instruction */ + asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) ); + + info->data.ppc.ppc64 = 1; + } + signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */ + } +# endif +# else /* !FLAC__USE_ALTIVEC */ + info->data.ppc.altivec = 0; + info->data.ppc.ppc64 = 0; +# endif +# else + info->use_asm = false; +# endif + +/* + * unknown CPI + */ +#else + info->type = FLAC__CPUINFO_TYPE_UNKNOWN; + info->use_asm = false; +#endif +} diff --git a/lib/flac-1.2.1/src/libFLAC/crc.c b/lib/flac-1.2.1/src/libFLAC/crc.c new file mode 100755 index 0000000..463ab65 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/crc.c @@ -0,0 +1,142 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/crc.h" + +/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ + +FLAC__byte const FLAC__crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ + +unsigned FLAC__crc16_table[256] = { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 +}; + + +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc) +{ + *crc = FLAC__crc8_table[*crc ^ data]; +} + +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc) +{ + while(len--) + *crc = FLAC__crc8_table[*crc ^ *data++]; +} + +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) +{ + FLAC__uint8 crc = 0; + + while(len--) + crc = FLAC__crc8_table[crc ^ *data++]; + + return crc; +} + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) +{ + unsigned crc = 0; + + while(len--) + crc = ((crc<<8) ^ FLAC__crc16_table[(crc>>8) ^ *data++]) & 0xffff; + + return crc; +} diff --git a/lib/flac-1.2.1/src/libFLAC/fixed.c b/lib/flac-1.2.1/src/libFLAC/fixed.c new file mode 100755 index 0000000..f93e59b --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/fixed.c @@ -0,0 +1,436 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "../../../../../src/fmod_types.h" +#include "private/bitmath.h" +#include "private/fixed.h" +#include "FLAC/assert.h" + +#ifndef M_LN2 +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_LN2 0.69314718055994530942 +#endif + +#ifdef min +#undef min +#endif +#define min(x,y) ((x) < (y)? (x) : (y)) + +#ifdef local_abs +#undef local_abs +#endif +#define local_abs(x) ((unsigned)((x)<0? -(x) : (x))) + +#ifdef FLAC__INTEGER_ONLY_LIBRARY +/* rbps stands for residual bits per sample + * + * (ln(2) * err) + * rbps = log (-----------) + * 2 ( n ) + */ +static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} + +static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2_wide(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + /* total_error_* are 64-bits to avoid overflow when encoding + * erratic signals when the bits-per-sample and blocksize are + * large. + */ + FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with MSVC you have to spoon feed it the casting */ + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#endif +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_wide_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_wide_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_wide_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + FMOD_memcpy(residual, data, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - (data[i-1] << 1) + data[i-2]; +#else + residual[i] = data[i] - 2*data[i-1] + data[i-2]; +#endif + break; + case 3: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) - data[i-3]; +#else + residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; +#endif + break; + case 4: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - ((data[i-1]+data[i-3])<<2) + ((data[i-2]<<2) + (data[i-2]<<1)) + data[i-4]; +#else + residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; +#endif + break; + default: + FLAC__ASSERT(0); + } +} + +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + FMOD_memcpy(data, residual, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + (data[i-1]<<1) - data[i-2]; +#else + data[i] = residual[i] + 2*data[i-1] - data[i-2]; +#endif + break; + case 3: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) + data[i-3]; +#else + data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; +#endif + break; + case 4: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + ((data[i-1]+data[i-3])<<2) - ((data[i-2]<<2) + (data[i-2]<<1)) - data[i-4]; +#else + data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; +#endif + break; + default: + FLAC__ASSERT(0); + } +} diff --git a/lib/flac-1.2.1/src/libFLAC/float.c b/lib/flac-1.2.1/src/libFLAC/float.c new file mode 100755 index 0000000..8e19280 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/float.c @@ -0,0 +1,308 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" + +#include "private/float.h" + +#ifdef FLAC__INTEGER_ONLY_LIBRARY + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +const FLAC__fixedpoint FLAC__FP_ZERO = 0; +const FLAC__fixedpoint FLAC__FP_ONE_HALF = 0x00008000; +const FLAC__fixedpoint FLAC__FP_ONE = 0x00010000; +const FLAC__fixedpoint FLAC__FP_LN2 = 45426; +const FLAC__fixedpoint FLAC__FP_E = 178145; + +/* Lookup tables for Knuth's logarithm algorithm */ +#define LOG2_LOOKUP_PRECISION 16 +static const FLAC__uint32 log2_lookup[][LOG2_LOOKUP_PRECISION] = { + { + /* + * 0 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000001, + /* lg(4/3) = */ 0x00000000, + /* lg(8/7) = */ 0x00000000, + /* lg(16/15) = */ 0x00000000, + /* lg(32/31) = */ 0x00000000, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 4 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000010, + /* lg(4/3) = */ 0x00000007, + /* lg(8/7) = */ 0x00000003, + /* lg(16/15) = */ 0x00000001, + /* lg(32/31) = */ 0x00000001, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 8 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000100, + /* lg(4/3) = */ 0x0000006a, + /* lg(8/7) = */ 0x00000031, + /* lg(16/15) = */ 0x00000018, + /* lg(32/31) = */ 0x0000000c, + /* lg(64/63) = */ 0x00000006, + /* lg(128/127) = */ 0x00000003, + /* lg(256/255) = */ 0x00000001, + /* lg(512/511) = */ 0x00000001, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 12 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00001000, + /* lg(4/3) = */ 0x000006a4, + /* lg(8/7) = */ 0x00000315, + /* lg(16/15) = */ 0x0000017d, + /* lg(32/31) = */ 0x000000bc, + /* lg(64/63) = */ 0x0000005d, + /* lg(128/127) = */ 0x0000002e, + /* lg(256/255) = */ 0x00000017, + /* lg(512/511) = */ 0x0000000c, + /* lg(1024/1023) = */ 0x00000006, + /* lg(2048/2047) = */ 0x00000003, + /* lg(4096/4095) = */ 0x00000001, + /* lg(8192/8191) = */ 0x00000001, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 16 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00010000, + /* lg(4/3) = */ 0x00006a40, + /* lg(8/7) = */ 0x00003151, + /* lg(16/15) = */ 0x000017d6, + /* lg(32/31) = */ 0x00000bba, + /* lg(64/63) = */ 0x000005d1, + /* lg(128/127) = */ 0x000002e6, + /* lg(256/255) = */ 0x00000172, + /* lg(512/511) = */ 0x000000b9, + /* lg(1024/1023) = */ 0x0000005c, + /* lg(2048/2047) = */ 0x0000002e, + /* lg(4096/4095) = */ 0x00000017, + /* lg(8192/8191) = */ 0x0000000c, + /* lg(16384/16383) = */ 0x00000006, + /* lg(32768/32767) = */ 0x00000003 + }, + { + /* + * 20 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00100000, + /* lg(4/3) = */ 0x0006a3fe, + /* lg(8/7) = */ 0x00031513, + /* lg(16/15) = */ 0x00017d60, + /* lg(32/31) = */ 0x0000bb9d, + /* lg(64/63) = */ 0x00005d10, + /* lg(128/127) = */ 0x00002e59, + /* lg(256/255) = */ 0x00001721, + /* lg(512/511) = */ 0x00000b8e, + /* lg(1024/1023) = */ 0x000005c6, + /* lg(2048/2047) = */ 0x000002e3, + /* lg(4096/4095) = */ 0x00000171, + /* lg(8192/8191) = */ 0x000000b9, + /* lg(16384/16383) = */ 0x0000005c, + /* lg(32768/32767) = */ 0x0000002e + }, + { + /* + * 24 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x01000000, + /* lg(4/3) = */ 0x006a3fe6, + /* lg(8/7) = */ 0x00315130, + /* lg(16/15) = */ 0x0017d605, + /* lg(32/31) = */ 0x000bb9ca, + /* lg(64/63) = */ 0x0005d0fc, + /* lg(128/127) = */ 0x0002e58f, + /* lg(256/255) = */ 0x0001720e, + /* lg(512/511) = */ 0x0000b8d8, + /* lg(1024/1023) = */ 0x00005c61, + /* lg(2048/2047) = */ 0x00002e2d, + /* lg(4096/4095) = */ 0x00001716, + /* lg(8192/8191) = */ 0x00000b8b, + /* lg(16384/16383) = */ 0x000005c5, + /* lg(32768/32767) = */ 0x000002e3 + }, + { + /* + * 28 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x10000000, + /* lg(4/3) = */ 0x06a3fe5c, + /* lg(8/7) = */ 0x03151301, + /* lg(16/15) = */ 0x017d6049, + /* lg(32/31) = */ 0x00bb9ca6, + /* lg(64/63) = */ 0x005d0fba, + /* lg(128/127) = */ 0x002e58f7, + /* lg(256/255) = */ 0x001720da, + /* lg(512/511) = */ 0x000b8d87, + /* lg(1024/1023) = */ 0x0005c60b, + /* lg(2048/2047) = */ 0x0002e2d7, + /* lg(4096/4095) = */ 0x00017160, + /* lg(8192/8191) = */ 0x0000b8ad, + /* lg(16384/16383) = */ 0x00005c56, + /* lg(32768/32767) = */ 0x00002e2b + } +}; + +#if 0 +static const FLAC__uint64 log2_lookup_wide[] = { + { + /* + * 32 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x100000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c6), + /* lg(8/7) = */ FLAC__U64L(0x31513015), + /* lg(16/15) = */ FLAC__U64L(0x17d60497), + /* lg(32/31) = */ FLAC__U64L(0x0bb9ca65), + /* lg(64/63) = */ FLAC__U64L(0x05d0fba2), + /* lg(128/127) = */ FLAC__U64L(0x02e58f74), + /* lg(256/255) = */ FLAC__U64L(0x01720d9c), + /* lg(512/511) = */ FLAC__U64L(0x00b8d875), + /* lg(1024/1023) = */ FLAC__U64L(0x005c60aa), + /* lg(2048/2047) = */ FLAC__U64L(0x002e2d72), + /* lg(4096/4095) = */ FLAC__U64L(0x00171600), + /* lg(8192/8191) = */ FLAC__U64L(0x000b8ad2), + /* lg(16384/16383) = */ FLAC__U64L(0x0005c55d), + /* lg(32768/32767) = */ FLAC__U64L(0x0002e2ac) + }, + { + /* + * 48 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x1000000000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c60429), + /* lg(8/7) = */ FLAC__U64L(0x315130157f7a), + /* lg(16/15) = */ FLAC__U64L(0x17d60496cfbb), + /* lg(32/31) = */ FLAC__U64L(0xbb9ca64ecac), + /* lg(64/63) = */ FLAC__U64L(0x5d0fba187cd), + /* lg(128/127) = */ FLAC__U64L(0x2e58f7441ee), + /* lg(256/255) = */ FLAC__U64L(0x1720d9c06a8), + /* lg(512/511) = */ FLAC__U64L(0xb8d8752173), + /* lg(1024/1023) = */ FLAC__U64L(0x5c60aa252e), + /* lg(2048/2047) = */ FLAC__U64L(0x2e2d71b0d8), + /* lg(4096/4095) = */ FLAC__U64L(0x1716001719), + /* lg(8192/8191) = */ FLAC__U64L(0xb8ad1de1b), + /* lg(16384/16383) = */ FLAC__U64L(0x5c55d640d), + /* lg(32768/32767) = */ FLAC__U64L(0x2e2abcf52) + } +}; +#endif + +FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision) +{ + const FLAC__uint32 ONE = (1u << fracbits); + const FLAC__uint32 *table = log2_lookup[fracbits >> 2]; + + FLAC__ASSERT(fracbits < 32); + FLAC__ASSERT((fracbits & 0x3) == 0); + + if(x < ONE) + return 0; + + if(precision > LOG2_LOOKUP_PRECISION) + precision = LOG2_LOOKUP_PRECISION; + + /* Knuth's algorithm for computing logarithms, optimized for base-2 with lookup tables */ + { + FLAC__uint32 y = 0; + FLAC__uint32 z = x >> 1, k = 1; + while (x > ONE && k < precision) { + if (x - z >= ONE) { + x -= z; + z = x >> k; + y += table[k]; + } + else { + z >>= 1; + k++; + } + } + return y; + } +} + +#endif /* defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/lib/flac-1.2.1/src/libFLAC/format.c b/lib/flac-1.2.1/src/libFLAC/format.c new file mode 100755 index 0000000..c07d9eb --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/format.c @@ -0,0 +1,598 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include /* for qsort() */ +#include /* for FMOD_memset() */ +#include "../../../src/fmod_types.h" +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/format.h" + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +#ifdef min +#undef min +#endif +#define min(a,b) ((a)<(b)?(a):(b)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +#ifndef VERSION + #define VERSION "1.2.1" +#endif + +/* VERSION should come from configure */ +FLAC_API const char *FLAC__VERSION_STRING = VERSION; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINW32__ +/* yet one more hack because of MSVC6: */ +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC 1.2.1 20070917"; +#else +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20070917"; +#endif + +FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; +FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; +FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ + +FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); + +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe; +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1< FLAC__MAX_SAMPLE_RATE) { + return false; + } + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) +{ + if( + !FLAC__format_sample_rate_is_valid(sample_rate) || + ( + sample_rate >= (1u << 16) && + !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) + ) + ) { + return false; + } + else + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i; + FLAC__uint64 prev_sample_number = 0; + FLAC__bool got_prev = false; + + FLAC__ASSERT(0 != seek_table); + + for(i = 0; i < seek_table->num_points; i++) { + if(got_prev) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].sample_number <= prev_sample_number + ) + return false; + } + prev_sample_number = seek_table->points[i].sample_number; + got_prev = true; + } + + return true; +} + +/* used as the sort predicate for qsort() */ +static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +{ + /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ + if(l->sample_number == r->sample_number) + return 0; + else if(l->sample_number < r->sample_number) + return -1; + else + return 1; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i, j; + FLAC__bool first; + + FLAC__ASSERT(0 != seek_table); + + /* sort the seekpoints */ + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); + + /* uniquify the seekpoints */ + first = true; + for(i = j = 0; i < seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { + if(!first) { + if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) + continue; + } + } + first = false; + seek_table->points[j++] = seek_table->points[i]; + } + + for(i = j; i < seek_table->num_points; i++) { + seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + + return j; +} + +/* + * also disallows non-shortest-form encodings, c.f. + * http://www.unicode.org/versions/corrigendum1.html + * and a more clear explanation at the end of this section: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + */ +static FLaC__INLINE unsigned utf8len_(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((utf8[0] & 0x80) == 0) { + return 1; + } + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) { + if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */ + return 0; + return 2; + } + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) { + if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */ + return 0; + /* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */ + if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */ + return 0; + if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */ + return 0; + return 3; + } + else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) { + if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */ + return 0; + return 4; + } + else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) { + if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */ + return 0; + return 5; + } + else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) { + if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */ + return 0; + return 6; + } + else { + return 0; + } +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name) +{ + char c; + for(c = *name; c; c = *(++name)) + if(c < 0x20 || c == 0x3d || c > 0x7d) + return false; + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length) +{ + if(length == (unsigned)(-1)) { + while(*value) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + } + else { + const FLAC__byte *end = value + length; + while(value < end) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + if(value != end) + return false; + } + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length) +{ + const FLAC__byte *s, *end; + + for(s = entry, end = s + length; s < end && *s != '='; s++) { + if(*s < 0x20 || *s > 0x7D) + return false; + } + if(s == end) + return false; + + s++; /* skip '=' */ + + while(s < end) { + unsigned n = utf8len_(s); + if(n == 0) + return false; + s += n; + } + if(s != end) + return false; + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) +{ + unsigned i, j; + + if(check_cd_da_subset) { + if(cue_sheet->lead_in < 2 * 44100) { + if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds"; + return false; + } + if(cue_sheet->lead_in % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"; + return false; + } + } + + if(cue_sheet->num_tracks == 0) { + if(violation) *violation = "cue sheet must have at least one track (the lead-out)"; + return false; + } + + if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) { + if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)"; + return false; + } + + for(i = 0; i < cue_sheet->num_tracks; i++) { + if(cue_sheet->tracks[i].number == 0) { + if(violation) *violation = "cue sheet may not have a track number 0"; + return false; + } + + if(check_cd_da_subset) { + if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) { + if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170"; + return false; + } + } + + if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) { + if(violation) { + if(i == cue_sheet->num_tracks-1) /* the lead-out track... */ + *violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples"; + else + *violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples"; + } + return false; + } + + if(i < cue_sheet->num_tracks - 1) { + if(cue_sheet->tracks[i].num_indices == 0) { + if(violation) *violation = "cue sheet track must have at least one index point"; + return false; + } + + if(cue_sheet->tracks[i].indices[0].number > 1) { + if(violation) *violation = "cue sheet track's first index number must be 0 or 1"; + return false; + } + } + + for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) { + if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples"; + return false; + } + + if(j > 0) { + if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) { + if(violation) *violation = "cue sheet track index numbers must increase by 1"; + return false; + } + } + } + } + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation) +{ + char *p; + FLAC__byte *b; + + for(p = picture->mime_type; *p; p++) { + if(*p < 0x20 || *p > 0x7e) { + if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)"; + return false; + } + } + + for(b = picture->description; *b; ) { + unsigned n = utf8len_(b); + if(n == 0) { + if(violation) *violation = "description string must be valid UTF-8"; + return false; + } + b += n; + } + + return true; +} + +/* + * These routines are private to libFLAC + */ +unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) +{ + return + FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( + FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize), + blocksize, + predictor_order + ); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize) +{ + unsigned max_rice_partition_order = 0; + while(!(blocksize & 1)) { + max_rice_partition_order++; + blocksize >>= 1; + } + return min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) +{ + unsigned max_rice_partition_order = limit; + + while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) + max_rice_partition_order--; + + FLAC__ASSERT( + (max_rice_partition_order == 0 && blocksize >= predictor_order) || + (max_rice_partition_order > 0 && blocksize >> max_rice_partition_order > predictor_order) + ); + + return max_rice_partition_order; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + object->parameters = 0; + object->raw_bits = 0; + object->capacity_by_order = 0; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + if(0 != object->parameters) + free(object->parameters); + if(0 != object->raw_bits) + free(object->raw_bits); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); +} + +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order) +{ + FLAC__ASSERT(0 != object); + + FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); + + if(object->capacity_by_order < max_partition_order) { + if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned) * (unsigned int)(1 << max_partition_order)))) + return false; + if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned) * (unsigned int)(1 << max_partition_order)))) + return false; + FMOD_memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); + object->capacity_by_order = max_partition_order; + } + + return true; +} diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/all.h b/lib/flac-1.2.1/src/libFLAC/include/private/all.h new file mode 100755 index 0000000..304c471 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/all.h @@ -0,0 +1,49 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__ALL_H +#define FLAC__PRIVATE__ALL_H + +#include "bitmath.h" +#include "bitreader.h" +#include "bitwriter.h" +#include "cpu.h" +#include "crc.h" +#include "fixed.h" +#include "float.h" +#include "format.h" +#include "lpc.h" +#include "md5.h" +#include "memory.h" +#include "metadata.h" +#include "stream_encoder_framing.h" + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/bitmath.h b/lib/flac-1.2.1/src/libFLAC/include/private/bitmath.h new file mode 100755 index 0000000..87fa0fa --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/bitmath.h @@ -0,0 +1,42 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITMATH_H +#define FLAC__PRIVATE__BITMATH_H + +#include "FLAC/ordinals.h" + +unsigned FLAC__bitmath_ilog2(FLAC__uint32 v); +unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v); +unsigned FLAC__bitmath_silog2(int v); +unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/bitreader.h b/lib/flac-1.2.1/src/libFLAC/include/private/bitreader.h new file mode 100755 index 0000000..1fc4ea2 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/bitreader.h @@ -0,0 +1,99 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITREADER_H +#define FLAC__PRIVATE__BITREADER_H + +#include /* for FILE */ +#include "FLAC/ordinals.h" +#include "cpu.h" + +/* + * opaque structure definition + */ +struct FLAC__BitReader; +typedef struct FLAC__BitReader FLAC__BitReader; + +typedef FLAC__bool (*FLAC__BitReaderReadCallback)(void *context, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitReader *FLAC__bitreader_new(void); +void FLAC__bitreader_delete(FLAC__BitReader *br); +FLAC__bool FLAC__bitreader_init(void *context, FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd); +void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); + +/* + * CRC functions + */ +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed); +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); + +/* + * info functions + */ +FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); + +/* + * read functions + */ + +FLAC__bool FLAC__bitreader_read_raw_uint32(void *context, FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_int32(void *context, FLAC__BitReader *br, FLAC__int32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_uint64(void *context, FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_uint32_little_endian(void *context, FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ +FLAC__bool FLAC__bitreader_skip_bits_no_crc(void *context, FLAC__BitReader *br, unsigned bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(void *context, FLAC__BitReader *br, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(void *context, FLAC__BitReader *br, FLAC__byte *val, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_unary_unsigned(void *context, FLAC__BitReader *br, unsigned *val); +FLAC__bool FLAC__bitreader_read_rice_signed(void *context, FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_rice_signed_block(void *context, FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +FLAC__bool FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap(void *context, FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +# endif +# endif +#endif +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(void *context, FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_golomb_unsigned(void *context, FLAC__BitReader *br, unsigned *val, unsigned parameter); +#endif +FLAC__bool FLAC__bitreader_read_utf8_uint32(void *context, FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint64(void *context, FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); + +FLAC__bool bitreader_read_from_client_(void *context, FLAC__BitReader *br); +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/bitwriter.h b/lib/flac-1.2.1/src/libFLAC/include/private/bitwriter.h new file mode 100755 index 0000000..aa5c4f7 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/bitwriter.h @@ -0,0 +1,103 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITWRITER_H +#define FLAC__PRIVATE__BITWRITER_H + +#include /* for FILE */ +#include "FLAC/ordinals.h" + +/* + * opaque structure definition + */ +struct FLAC__BitWriter; +typedef struct FLAC__BitWriter FLAC__BitWriter; + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitWriter *FLAC__bitwriter_new(void); +void FLAC__bitwriter_delete(FLAC__BitWriter *bw); +FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw); +void FLAC__bitwriter_free(FLAC__BitWriter *bw); /* does not 'free(buffer)' */ +void FLAC__bitwriter_clear(FLAC__BitWriter *bw); +void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out); + +/* + * CRC functions + * + * non-const *bw because they have to cal FLAC__bitwriter_get_buffer() + */ +FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc); +FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc); + +/* + * info functions + */ +FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw); +unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw); /* can be called anytime, returns total # of bits unconsumed */ + +/* + * direct buffer access + * + * there may be no calls on the bitwriter between get and release. + * the bitwriter continues to own the returned buffer. + * before get, bitwriter MUST be byte aligned: check with FLAC__bitwriter_is_byte_aligned() + */ +FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes); +void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw); + +/* + * write functions + */ +FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); /*only for bits=32*/ +FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val); +unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter); +#if 0 /* UNUSED */ +unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter); +unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned val, unsigned parameter); +#endif +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter); +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned val, unsigned parameter); +#endif +FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val); +FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val); +FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/cpu.h b/lib/flac-1.2.1/src/libFLAC/include/private/cpu.h new file mode 100755 index 0000000..651bb22 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/cpu.h @@ -0,0 +1,88 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CPU_H +#define FLAC__PRIVATE__CPU_H + +#include "FLAC/ordinals.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef enum { + FLAC__CPUINFO_TYPE_IA32, + FLAC__CPUINFO_TYPE_PPC, + FLAC__CPUINFO_TYPE_UNKNOWN +} FLAC__CPUInfo_Type; + +typedef struct { + FLAC__bool cpuid; + FLAC__bool bswap; + FLAC__bool cmov; + FLAC__bool mmx; + FLAC__bool fxsr; + FLAC__bool sse; + FLAC__bool sse2; + FLAC__bool sse3; + FLAC__bool ssse3; + FLAC__bool _3dnow; + FLAC__bool ext3dnow; + FLAC__bool extmmx; +} FLAC__CPUInfo_IA32; + +typedef struct { + FLAC__bool altivec; + FLAC__bool ppc64; +} FLAC__CPUInfo_PPC; + +typedef struct { + FLAC__bool use_asm; + FLAC__CPUInfo_Type type; + union { + FLAC__CPUInfo_IA32 ia32; + FLAC__CPUInfo_PPC ppc; + } data; +} FLAC__CPUInfo; + +void FLAC__cpu_info(FLAC__CPUInfo *info); + +#ifndef FLAC__NO_ASM +#ifdef FLAC__CPU_IA32 +#ifdef FLAC__HAS_NASM +FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); +void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); +FLAC__uint32 FLAC__cpu_info_extended_amd_asm_ia32(void); +#endif +#endif +#endif + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/crc.h b/lib/flac-1.2.1/src/libFLAC/include/private/crc.h new file mode 100755 index 0000000..0b67fb4 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/crc.h @@ -0,0 +1,61 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CRC_H +#define FLAC__PRIVATE__CRC_H + +#include "FLAC/ordinals.h" + +/* 8 bit CRC generator, MSB shifted first +** polynomial = x^8 + x^2 + x^1 + x^0 +** init = 0 +*/ +extern FLAC__byte const FLAC__crc8_table[256]; +#define FLAC__CRC8_UPDATE(data, crc) (crc) = FLAC__crc8_table[(crc) ^ (data)]; +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc); +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc); +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); + +/* 16 bit CRC generator, MSB shifted first +** polynomial = x^16 + x^15 + x^2 + x^0 +** init = 0 +*/ +extern unsigned FLAC__crc16_table[256]; + +#define FLAC__CRC16_UPDATE(data, crc) (((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)])) +/* this alternate may be faster on some systems/compilers */ +#if 0 +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) +#endif + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/fixed.h b/lib/flac-1.2.1/src/libFLAC/include/private/fixed.h new file mode 100755 index 0000000..6656b79 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/fixed.h @@ -0,0 +1,97 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FIXED_H +#define FLAC__PRIVATE__FIXED_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +/* + * FLAC__fixed_compute_best_predictor() + * -------------------------------------------------------------------- + * Compute the best fixed predictor and the expected bits-per-sample + * of the residual signal for each order. The _wide() version uses + * 64-bit integers which is statistically necessary when bits-per- + * sample + log2(blocksize) > 30 + * + * IN data[0,data_len-1] + * IN data_len + * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif +# endif +# endif +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#endif + +/* + * FLAC__fixed_compute_residual() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]); + +/* + * FLAC__fixed_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/float.h b/lib/flac-1.2.1/src/libFLAC/include/private/float.h new file mode 100755 index 0000000..73313f6 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/float.h @@ -0,0 +1,97 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FLOAT_H +#define FLAC__PRIVATE__FLOAT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "FLAC/ordinals.h" + +/* + * These typedefs make it easier to ensure that integer versions of + * the library really only contain integer operations. All the code + * in libFLAC should use FLAC__float and FLAC__double in place of + * float and double, and be protected by checks of the macro + * FLAC__INTEGER_ONLY_LIBRARY. + * + * FLAC__real is the basic floating point type used in LPC analysis. + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +typedef double FLAC__double; +typedef float FLAC__float; +/* + * WATCHOUT: changing FLAC__real will change the signatures of many + * functions that have assembly language equivalents and break them. + */ +typedef float FLAC__real; +#else +/* + * The convention for FLAC__fixedpoint is to use the upper 16 bits + * for the integer part and lower 16 bits for the fractional part. + */ +typedef FLAC__int32 FLAC__fixedpoint; +extern const FLAC__fixedpoint FLAC__FP_ZERO; +extern const FLAC__fixedpoint FLAC__FP_ONE_HALF; +extern const FLAC__fixedpoint FLAC__FP_ONE; +extern const FLAC__fixedpoint FLAC__FP_LN2; +extern const FLAC__fixedpoint FLAC__FP_E; + +#define FLAC__fixedpoint_trunc(x) ((x)>>16) + +#define FLAC__fixedpoint_mul(x, y) ( (FLAC__fixedpoint) ( ((FLAC__int64)(x)*(FLAC__int64)(y)) >> 16 ) ) + +#define FLAC__fixedpoint_div(x, y) ( (FLAC__fixedpoint) ( ( ((FLAC__int64)(x)<<32) / (FLAC__int64)(y) ) >> 16 ) ) + +/* + * FLAC__fixedpoint_log2() + * -------------------------------------------------------------------- + * Returns the base-2 logarithm of the fixed-point number 'x' using an + * algorithm by Knuth for x >= 1.0 + * + * 'fracbits' is the number of fractional bits of 'x'. 'fracbits' must + * be < 32 and evenly divisible by 4 (0 is OK but not very precise). + * + * 'precision' roughly limits the number of iterations that are done; + * use (unsigned)(-1) for maximum precision. + * + * If 'x' is less than one -- that is, x < (1< +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_window_data() + * -------------------------------------------------------------------- + * Applies the given window to the data. + * OPT: asm implementation + * + * IN in[0,data_len-1] + * IN window[0,data_len-1] + * OUT out[0,lag-1] + * IN data_len + */ +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len); + +/* + * FLAC__lpc_compute_autocorrelation() + * -------------------------------------------------------------------- + * Compute the autocorrelation for lags between 0 and lag-1. + * Assumes data[] outside of [0,data_len-1] == 0. + * Asserts that lag > 0. + * + * IN data[0,data_len-1] + * IN data_len + * IN 0 < lag <= data_len + * OUT autoc[0,lag-1] + */ +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +# endif +# endif +#endif + +/* + * FLAC__lpc_compute_lp_coefficients() + * -------------------------------------------------------------------- + * Computes LP coefficients for orders 1..max_order. + * Do not call if autoc[0] == 0.0. This means the signal is zero + * and there is no point in calculating a predictor. + * + * IN autoc[0,max_order] autocorrelation values + * IN 0 < max_order <= FLAC__MAX_LPC_ORDER max LP order to compute + * OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order + * *** IMPORTANT: + * *** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched + * OUT error[0,max_order-1] error for each order (more + * specifically, the variance of + * the error signal times # of + * samples in the signal) + * + * Example: if max_order is 9, the LP coefficients for order 9 will be + * in lp_coeff[8][0,8], the LP coefficients for order 8 will be + * in lp_coeff[7][0,7], etc. + */ +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]); + +/* + * FLAC__lpc_quantize_coefficients() + * -------------------------------------------------------------------- + * Quantizes the LP coefficients. NOTE: precision + bits_per_sample + * must be less than 32 (sizeof(FLAC__int32)*8). + * + * IN lp_coeff[0,order-1] LP coefficients + * IN order LP order + * IN FLAC__MIN_QLP_COEFF_PRECISION < precision + * desired precision (in bits, including sign + * bit) of largest coefficient + * OUT qlp_coeff[0,order-1] quantized coefficients + * OUT shift # of bits to shift right to get approximated + * LP coefficients. NOTE: could be negative. + * RETURN 0 => quantization OK + * 1 => coefficients require too much shifting for *shift to + * fit in the LPC subframe header. 'shift' is unset. + * 2 => coefficients are all zero, which is bad. 'shift' is + * unset. + */ +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift); + +/* + * FLAC__lpc_compute_residual_from_qlp_coefficients() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +/* + * FLAC__lpc_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif /* FLAC__HAS_NASM */ +# elif defined FLAC__CPU_PPC +void FLAC__lpc_restore_signal_asm_ppc_altivec_16(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif/* FLAC__CPU_IA32 || FLAC__CPU_PPC */ +#endif /* FLAC__NO_ASM */ + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_compute_expected_bits_per_residual_sample() + * -------------------------------------------------------------------- + * Compute the expected number of bits per residual signal sample + * based on the LP error (which is related to the residual variance). + * + * IN lpc_error >= 0.0 error returned from calculating LP coefficients + * IN total_samples > 0 # of samples in residual signal + * RETURN expected bits per sample + */ +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples); +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale); + +/* + * FLAC__lpc_compute_best_order() + * -------------------------------------------------------------------- + * Compute the best order from the array of signal errors returned + * during coefficient computation. + * + * IN lpc_error[0,max_order-1] >= 0.0 error returned from calculating LP coefficients + * IN max_order > 0 max LP order + * IN total_samples > 0 # of samples in residual signal + * IN overhead_bits_per_order # of bits overhead for each increased LP order + * (includes warmup sample size and quantized LP coefficient) + * RETURN [1,max_order] best order + */ +unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/md5.h b/lib/flac-1.2.1/src/libFLAC/include/private/md5.h new file mode 100755 index 0000000..e5f675a --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/md5.h @@ -0,0 +1,44 @@ +#ifndef FLAC__PRIVATE__MD5_H +#define FLAC__PRIVATE__MD5_H + +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain, with no warranty. + */ + +#include "FLAC/ordinals.h" + +typedef struct { + FLAC__uint32 in[16]; + FLAC__uint32 buf[4]; + FLAC__uint32 bytes[2]; + FLAC__byte *internal_buf; + size_t capacity; +} FLAC__MD5Context; + +void FLAC__MD5Init(FLAC__MD5Context *context); +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); + +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/memory.h b/lib/flac-1.2.1/src/libFLAC/include/private/memory.h new file mode 100755 index 0000000..4d374d1 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/memory.h @@ -0,0 +1,57 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__MEMORY_H +#define FLAC__PRIVATE__MEMORY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include /* for size_t */ + +#include "../../../src/fmod_types.h" +#include "private/float.h" +#include "FLAC/ordinals.h" /* for FLAC__bool */ + +/* Returns the unaligned address returned by malloc. + * Use free() on this address to deallocate. + */ +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); +FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); +#endif + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/metadata.h b/lib/flac-1.2.1/src/libFLAC/include/private/metadata.h new file mode 100755 index 0000000..b5268c9 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/metadata.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__METADATA_H +#define FLAC__PRIVATE__METADATA_H + +#include "FLAC/metadata.h" + +/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not + * be a consistent state (e.g. PICTURE) or equivalent to the initial + * state after FLAC__metadata_object_new() + */ +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object); + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/ogg_decoder_aspect.h b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_decoder_aspect.h new file mode 100755 index 0000000..facbd42 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_decoder_aspect.h @@ -0,0 +1,79 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_DECODER_ASPECT_H +#define FLAC__PRIVATE__OGG_DECODER_ASPECT_H + +#include + +#include "FLAC/ordinals.h" +#include "FLAC/stream_decoder.h" /* for FLAC__StreamDecoderReadStatus */ + +typedef struct FLAC__OggDecoderAspect { + /* these are storage for values that can be set through the API */ + FLAC__bool use_first_serial_number; + long serial_number; + + /* these are for internal state related to Ogg decoding */ + ogg_stream_state stream_state; + ogg_sync_state sync_state; + unsigned version_major, version_minor; + FLAC__bool need_serial_number; + FLAC__bool end_of_stream; + FLAC__bool have_working_page; /* only if true will the following vars be valid */ + ogg_page working_page; + FLAC__bool have_working_packet; /* only if true will the following vars be valid */ + ogg_packet working_packet; /* as we work through the packet we will move working_packet.packet forward and working_packet.bytes down */ +} FLAC__OggDecoderAspect; + +void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value); +void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect); +FLAC__bool FLAC__ogg_decoder_aspect_init(void *context, FLAC__OggDecoderAspect *aspect); +void FLAC__ogg_decoder_aspect_finish(void *context, FLAC__OggDecoderAspect *aspect); +void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect); +void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect); + +typedef enum { + FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK = 0, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR +} FLAC__OggDecoderAspectReadStatus; + +typedef FLAC__OggDecoderAspectReadStatus (*FLAC__OggDecoderAspectReadCallbackProxy)(const void *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(void *context, FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/ogg_encoder_aspect.h b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_encoder_aspect.h new file mode 100755 index 0000000..290da07 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_encoder_aspect.h @@ -0,0 +1,62 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_ENCODER_ASPECT_H +#define FLAC__PRIVATE__OGG_ENCODER_ASPECT_H + +#include + +#include "FLAC/ordinals.h" +#include "FLAC/stream_encoder.h" /* for FLAC__StreamEncoderWriteStatus */ + +typedef struct FLAC__OggEncoderAspect { + /* these are storage for values that can be set through the API */ + long serial_number; + unsigned num_metadata; + + /* these are for internal state related to Ogg encoding */ + ogg_stream_state stream_state; + ogg_page page; + FLAC__bool seen_magic; /* true if we've seen the fLaC magic in the write callback yet */ + FLAC__bool is_first_packet; + FLAC__uint64 samples_written; +} FLAC__OggEncoderAspect; + +void FLAC__ogg_encoder_aspect_set_serial_number(FLAC__OggEncoderAspect *aspect, long value); +FLAC__bool FLAC__ogg_encoder_aspect_set_num_metadata(FLAC__OggEncoderAspect *aspect, unsigned value); +void FLAC__ogg_encoder_aspect_set_defaults(FLAC__OggEncoderAspect *aspect); +FLAC__bool FLAC__ogg_encoder_aspect_init(FLAC__OggEncoderAspect *aspect); +void FLAC__ogg_encoder_aspect_finish(FLAC__OggEncoderAspect *aspect); + +typedef FLAC__StreamEncoderWriteStatus (*FLAC__OggEncoderAspectWriteCallbackProxy)(const void *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + +FLAC__StreamEncoderWriteStatus FLAC__ogg_encoder_aspect_write_callback_wrapper(FLAC__OggEncoderAspect *aspect, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, FLAC__bool is_last_block, FLAC__OggEncoderAspectWriteCallbackProxy write_callback, void *encoder, void *client_data); +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/ogg_helper.h b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_helper.h new file mode 100755 index 0000000..389be18 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_helper.h @@ -0,0 +1,43 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_HELPER_H +#define FLAC__PRIVATE__OGG_HELPER_H + +#include +#include "FLAC/stream_encoder.h" /* for FLAC__StreamEncoder */ + +void simple_ogg_page__init(ogg_page *page); +void simple_ogg_page__clear(ogg_page *page); +FLAC__bool simple_ogg_page__get_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderReadCallback read_callback, void *client_data); +FLAC__bool simple_ogg_page__set_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderWriteCallback write_callback, void *client_data); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/ogg_mapping.h b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_mapping.h new file mode 100755 index 0000000..07dd8b2 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/ogg_mapping.h @@ -0,0 +1,63 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_MAPPING_H +#define FLAC__PRIVATE__OGG_MAPPING_H + +#include "FLAC/ordinals.h" + +/** The length of the 'FLAC' magic in bytes. */ +#define FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH (1u) + +extern const unsigned FLAC__OGG_MAPPING_PACKET_TYPE_LEN; /* = 8 bits */ + +extern const FLAC__byte FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE; /* = 0x7f */ + +/** The length of the 'FLAC' magic in bytes. */ +#define FLAC__OGG_MAPPING_MAGIC_LENGTH (4u) + +extern const FLAC__byte * const FLAC__OGG_MAPPING_MAGIC; /* = "FLAC" */ + +extern const unsigned FLAC__OGG_MAPPING_VERSION_MAJOR_LEN; /* = 8 bits */ +extern const unsigned FLAC__OGG_MAPPING_VERSION_MINOR_LEN; /* = 8 bits */ + +/** The length of the Ogg FLAC mapping major version number in bytes. */ +#define FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH (1u) + +/** The length of the Ogg FLAC mapping minor version number in bytes. */ +#define FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH (1u) + +extern const unsigned FLAC__OGG_MAPPING_NUM_HEADERS_LEN; /* = 16 bits */ + +/** The length of the #-of-header-packets number bytes. */ +#define FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH (2u) + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/stream_encoder_framing.h b/lib/flac-1.2.1/src/libFLAC/include/private/stream_encoder_framing.h new file mode 100755 index 0000000..4865c16 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/stream_encoder_framing.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H +#define FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H + +#include "FLAC/format.h" +#include "bitwriter.h" + +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw); +FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/private/window.h b/lib/flac-1.2.1/src/libFLAC/include/private/window.h new file mode 100755 index 0000000..01e0fc4 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/private/window.h @@ -0,0 +1,71 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__WINDOW_H +#define FLAC__PRIVATE__WINDOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__window_*() + * -------------------------------------------------------------------- + * Calculates window coefficients according to different apodization + * functions. + * + * OUT window[0,L-1] + * IN L (number of points in window) + */ +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */ +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/protected/all.h b/lib/flac-1.2.1/src/libFLAC/include/protected/all.h new file mode 100755 index 0000000..2921092 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/protected/all.h @@ -0,0 +1,38 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__ALL_H +#define FLAC__PROTECTED__ALL_H + +#include "stream_decoder.h" +#include "stream_encoder.h" + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/protected/stream_decoder.h b/lib/flac-1.2.1/src/libFLAC/include/protected/stream_decoder.h new file mode 100755 index 0000000..9108ca7 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/protected/stream_decoder.h @@ -0,0 +1,58 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__STREAM_DECODER_H +#define FLAC__PROTECTED__STREAM_DECODER_H + +#include "FLAC/stream_decoder.h" +#if FLAC__HAS_OGG +#include "private/ogg_decoder_aspect.h" +#endif + +typedef struct FLAC__StreamDecoderProtected { + FLAC__StreamDecoderState state; + unsigned channels; + FLAC__ChannelAssignment channel_assignment; + unsigned bits_per_sample; + unsigned sample_rate; /* in Hz */ + unsigned blocksize; /* in samples (per channel) */ + FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ +#if FLAC__HAS_OGG + FLAC__OggDecoderAspect ogg_decoder_aspect; +#endif +} FLAC__StreamDecoderProtected; + +/* + * return the number of input bytes consumed + */ +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/include/protected/stream_encoder.h b/lib/flac-1.2.1/src/libFLAC/include/protected/stream_encoder.h new file mode 100755 index 0000000..4101ee5 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/include/protected/stream_encoder.h @@ -0,0 +1,110 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__STREAM_ENCODER_H +#define FLAC__PROTECTED__STREAM_ENCODER_H + +#include "FLAC/stream_encoder.h" +#if FLAC__HAS_OGG +#include "private/ogg_encoder_aspect.h" +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#include "private/float.h" + +#define FLAC__MAX_APODIZATION_FUNCTIONS 32 + +typedef enum { + FLAC__APODIZATION_BARTLETT, + FLAC__APODIZATION_BARTLETT_HANN, + FLAC__APODIZATION_BLACKMAN, + FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE, + FLAC__APODIZATION_CONNES, + FLAC__APODIZATION_FLATTOP, + FLAC__APODIZATION_GAUSS, + FLAC__APODIZATION_HAMMING, + FLAC__APODIZATION_HANN, + FLAC__APODIZATION_KAISER_BESSEL, + FLAC__APODIZATION_NUTTALL, + FLAC__APODIZATION_RECTANGLE, + FLAC__APODIZATION_TRIANGLE, + FLAC__APODIZATION_TUKEY, + FLAC__APODIZATION_WELCH +} FLAC__ApodizationFunction; + +typedef struct { + FLAC__ApodizationFunction type; + union { + struct { + FLAC__real stddev; + } gauss; + struct { + FLAC__real p; + } tukey; + } parameters; +} FLAC__ApodizationSpecification; + +#endif // #ifndef FLAC__INTEGER_ONLY_LIBRARY + +typedef struct FLAC__StreamEncoderProtected { + FLAC__StreamEncoderState state; + FLAC__bool verify; + FLAC__bool streamable_subset; + FLAC__bool do_md5; + FLAC__bool do_mid_side_stereo; + FLAC__bool loose_mid_side_stereo; + unsigned channels; + unsigned bits_per_sample; + unsigned sample_rate; + unsigned blocksize; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + unsigned num_apodizations; + FLAC__ApodizationSpecification apodizations[FLAC__MAX_APODIZATION_FUNCTIONS]; +#endif + unsigned max_lpc_order; + unsigned qlp_coeff_precision; + FLAC__bool do_qlp_coeff_prec_search; + FLAC__bool do_exhaustive_model_search; + FLAC__bool do_escape_coding; + unsigned min_residual_partition_order; + unsigned max_residual_partition_order; + unsigned rice_parameter_search_dist; + FLAC__uint64 total_samples_estimate; + FLAC__StreamMetadata **metadata; + unsigned num_metadata_blocks; + FLAC__uint64 streaminfo_offset, seektable_offset, audio_offset; +#if FLAC__HAS_OGG + FLAC__OggEncoderAspect ogg_encoder_aspect; +#endif +} FLAC__StreamEncoderProtected; + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/lpc.c b/lib/flac-1.2.1/src/libFLAC/lpc.c new file mode 100755 index 0000000..a6df8d4 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/lpc.c @@ -0,0 +1,1377 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/bitmath.h" +#include "private/lpc.h" +#if defined DEBUG_VERBOSE || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE +#include +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#ifndef M_LN2 +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_LN2 0.69314718055994530942 +#endif + +/* OPT: #undef'ing this may improve the speed on some architectures */ +#define FLAC__LPC_UNROLLED_FILTER_LOOPS + + +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) +{ + unsigned i; + for(i = 0; i < data_len; i++) + out[i] = in[i] * window[i]; +} + +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + /* a readable, but slower, version */ +#if 0 + FLAC__real d; + unsigned i; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + /* + * Technically we should subtract the mean first like so: + * for(i = 0; i < data_len; i++) + * data[i] -= mean; + * but it appears not to make enough of a difference to matter, and + * most signals are already closely centered around zero + */ + while(lag--) { + for(i = lag, d = 0.0; i < data_len; i++) + d += data[i] * data[i - lag]; + autoc[lag] = d; + } +#endif + + /* + * this version tends to run faster because of better data locality + * ('data_len' is usually much larger than 'lag') + */ + FLAC__real d; + unsigned sample, coeff; + const unsigned limit = data_len - lag; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] = 0.0; + for(sample = 0; sample <= limit; sample++) { + d = data[sample]; + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + for(; sample < data_len; sample++) { + d = data[sample]; + for(coeff = 0; coeff < data_len - sample; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } +} + +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]) +{ + unsigned i, j; + FLAC__double r, err, ref[FLAC__MAX_LPC_ORDER], lpc[FLAC__MAX_LPC_ORDER]; + + FLAC__ASSERT(0 != max_order); + FLAC__ASSERT(0 < *max_order); + FLAC__ASSERT(*max_order <= FLAC__MAX_LPC_ORDER); + FLAC__ASSERT(autoc[0] != 0.0); + + err = autoc[0]; + + for(i = 0; i < *max_order; i++) { + /* Sum up this iteration's reflection coefficient. */ + r = -autoc[i+1]; + for(j = 0; j < i; j++) + r -= lpc[j] * autoc[i-j]; + ref[i] = (r/=err); + + /* Update LPC coefficients and total error. */ + lpc[i]=r; + for(j = 0; j < (i>>1); j++) { + FLAC__double tmp = lpc[j]; + lpc[j] += r * lpc[i-1-j]; + lpc[i-1-j] += r * tmp; + } + if(i & 1) + lpc[j] += lpc[j] * r; + + err *= (1.0 - r * r); + + /* save this order */ + for(j = 0; j <= i; j++) + lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */ + error[i] = err; + + /* see SF bug #1601812 http://sourceforge.net/tracker/index.php?func=detail&aid=1601812&group_id=13478&atid=113478 */ + if(err == 0.0) { + *max_order = i+1; + return; + } + } +} + +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift) +{ + unsigned i; + FLAC__double cmax; + FLAC__int32 qmax, qmin; + + FLAC__ASSERT(precision > 0); + FLAC__ASSERT(precision >= FLAC__MIN_QLP_COEFF_PRECISION); + + /* drop one bit for the sign; from here on out we consider only |lp_coeff[i]| */ + precision--; + qmax = 1 << precision; + qmin = -qmax; + qmax--; + + /* calc cmax = max( |lp_coeff[i]| ) */ + cmax = 0.0; + for(i = 0; i < order; i++) { + const FLAC__double d = fabs(lp_coeff[i]); + if(d > cmax) + cmax = d; + } + + if(cmax <= 0.0) { + /* => coefficients are all 0, which means our constant-detect didn't work */ + return 2; + } + else { + const int max_shiftlimit = (1 << (FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN-1)) - 1; + const int min_shiftlimit = -max_shiftlimit - 1; + int log2cmax; + + (void)frexp(cmax, &log2cmax); + log2cmax--; + *shift = (int)precision - log2cmax - 1; + + if(*shift > max_shiftlimit) + *shift = max_shiftlimit; + else if(*shift < min_shiftlimit) + return 1; + } + + if(*shift >= 0) { + FLAC__double error = 0.0; + FLAC__int32 q; + for(i = 0; i < order; i++) { + error += lp_coeff[i] * (1 << *shift); +#if 1 /* unfortunately lround() is C99 */ + if(error >= 0.0) + q = (FLAC__int32)(error + 0.5); + else + q = (FLAC__int32)(error - 0.5); +#else + q = lround(error); +#endif +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + } + /* negative shift is very rare but due to design flaw, negative shift is + * a NOP in the decoder, so it must be handled specially by scaling down + * coeffs + */ + else { + const int nshift = -(*shift); + FLAC__double error = 0.0; + FLAC__int32 q; +#ifdef DEBUG_VERBOSE + fprintf(stderr,"FLAC__lpc_quantize_coefficients: negative shift=%d order=%u cmax=%f\n", *shift, order, cmax); +#endif + for(i = 0; i < order; i++) { + error += lp_coeff[i] / (1 << nshift); +#if 1 /* unfortunately lround() is C99 */ + if(error >= 0.0) + q = (FLAC__int32)(error + 0.5); + else + q = (FLAC__int32)(error - 0.5); +#else + q = lround(error); +#endif +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + *shift = 0; + } + + return 0; +} + +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#if defined _MSC_VER + if(sumo > 2147483647I64 || sumo < -2147483648I64) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); +#else + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); +#endif + } + *(residual++) = *(data++) - (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { +#if defined _MSC_VER + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); +#else + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); +#endif + break; + } + if(FLAC__bitmath_silog2_wide((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { +#if defined _MSC_VER + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%I64d, residual=%I64d\n", i, *data, sum >> lp_quantization, (FLAC__int64)(*data) - (sum >> lp_quantization)); +#else + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%lld, residual=%lld\n", i, *data, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*data) - (sum >> lp_quantization))); +#endif + break; + } + *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#if defined _MSC_VER + if(sumo > 2147483647I64 || sumo < -2147483648I64) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); +#else + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); +#endif + } + *(data++) = *(r++) + (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + data[i] = residual[i] + (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { +#ifdef _MSC_VER + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); +#else + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); +#endif + break; + } + if(FLAC__bitmath_silog2_wide((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { +#ifdef _MSC_VER + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%I64d, data=%I64d\n", i, *r, sum >> lp_quantization, (FLAC__int64)(*r) + (sum >> lp_quantization)); +#else + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%lld, data=%lld\n", i, *r, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*r) + (sum >> lp_quantization))); +#endif + break; + } + *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples) +{ + FLAC__double error_scale; + + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + + return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale); +} + +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale) +{ + if(lpc_error > 0.0) { + FLAC__double bps = (FLAC__double)0.5 * log(error_scale * lpc_error) / M_LN2; + if(bps >= 0.0) + return bps; + else + return 0.0; + } + else if(lpc_error < 0.0) { /* error should not be negative but can happen due to inadequate floating-point resolution */ + return 1e32; + } + else { + return 0.0; + } +} + +unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order) +{ + unsigned order, index, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + FLAC__double bits, best_bits, error_scale; + + FLAC__ASSERT(max_order > 0); + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + + best_index = 0; + best_bits = (unsigned)(-1); + + for(index = 0, order = 1; index < max_order; index++, order++) { + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[index], error_scale) * (FLAC__double)(total_samples - order) + (FLAC__double)(order * overhead_bits_per_order); + if(bits < best_bits) { + best_index = index; + best_bits = bits; + } + } + + return best_index+1; /* +1 since index of lpc_error[] is order-1 */ +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/lib/flac-1.2.1/src/libFLAC/md5.c b/lib/flac-1.2.1/src/libFLAC/md5.c new file mode 100755 index 0000000..0a46422 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/md5.c @@ -0,0 +1,424 @@ +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for FMOD_memcpy() */ +#include "../../../src/fmod_types.h" +#include "private/md5.h" +#include "share/alloc.h" + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain. + */ + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) +{ + register FLAC__uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#if WORDS_BIGENDIAN +//@@@@@@ OPT: use bswap/intrinsics +static void byteSwap(FLAC__uint32 *buf, unsigned words) +{ + register FLAC__uint32 x; + do { + x = *buf; + x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); + *buf++ = (x >> 16) | (x << 16); + } while (--words); +} +static void byteSwapX16(FLAC__uint32 *buf) +{ + register FLAC__uint32 x; + + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf = (x >> 16) | (x << 16); +} +#else +#define byteSwap(buf, words) +#define byteSwapX16(buf) +#endif + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsigned len) +{ + FLAC__uint32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + FMOD_memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + FMOD_memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + FMOD_memcpy(ctx->in, buf, 64); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + FMOD_memcpy(ctx->in, buf, len); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void FLAC__MD5Init(FLAC__MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; + + ctx->internal_buf = 0; + ctx->capacity = 0; +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + FLAC__byte *p = (FLAC__byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + FMOD_memset(p, 0, count + 8); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + p = (FLAC__byte *)ctx->in; + count = 56; + } + FMOD_memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + FLAC__MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + FMOD_memcpy(digest, ctx->buf, 16); + FMOD_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + if(0 != ctx->internal_buf) { + free(ctx->internal_buf); + ctx->internal_buf = 0; + ctx->capacity = 0; + } +} + +/* + * Convert the incoming audio signal to a byte stream + */ +static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + unsigned channel, sample; + register FLAC__int32 a_word; + register FLAC__byte *buf_ = buf; + +#if WORDS_BIGENDIAN +#else + if(channels == 2 && bytes_per_sample == 2) { + FLAC__int16 *buf1_ = ((FLAC__int16*)buf_) + 1; + FMOD_memcpy(buf_, signal[0], sizeof(FLAC__int32) * samples); + for(sample = 0; sample < samples; sample++, buf1_+=2) + *buf1_ = (FLAC__int16)signal[1][sample]; + } + else if(channels == 1 && bytes_per_sample == 2) { + FLAC__int16 *buf1_ = (FLAC__int16*)buf_; + for(sample = 0; sample < samples; sample++) + *buf1_++ = (FLAC__int16)signal[0][sample]; + } + else +#endif + if(bytes_per_sample == 2) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else if(bytes_per_sample == 3) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else if(bytes_per_sample == 1) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else { /* bytes_per_sample == 4, maybe optimize more later */ + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } +} + +/* + * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. + */ +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; + + /* overflow check */ + if((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) + return false; + if((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) + return false; + + if(ctx->capacity < bytes_needed) { + FLAC__byte *tmp = (FLAC__byte*)realloc(ctx->internal_buf, bytes_needed); + if(0 == tmp) { + free(ctx->internal_buf); + if(0 == (ctx->internal_buf = (FLAC__byte*)safe_malloc_(bytes_needed))) + return false; + } + ctx->internal_buf = tmp; + ctx->capacity = bytes_needed; + } + + format_input_(ctx->internal_buf, signal, channels, samples, bytes_per_sample); + + FLAC__MD5Update(ctx, ctx->internal_buf, bytes_needed); + + return true; +} diff --git a/lib/flac-1.2.1/src/libFLAC/memory.c b/lib/flac-1.2.1/src/libFLAC/memory.c new file mode 100755 index 0000000..e76a721 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/memory.c @@ -0,0 +1,231 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/memory.h" +#include "FLAC/assert.h" +#include "share/alloc.h" + +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) +{ + void *x; + + FLAC__ASSERT(0 != aligned_address); + +#ifdef FLAC__ALIGN_MALLOC_DATA + /* align on 32-byte (256-bit) boundary */ + x = safe_malloc_add_2op_(bytes, /*+*/31); +#ifdef SIZEOF_VOIDP +#if SIZEOF_VOIDP == 4 + /* could do *aligned_address = x + ((unsigned) (32 - (((unsigned)x) & 31))) & 31; */ + *aligned_address = (void*)(((unsigned)x + 31) & -32); +#elif SIZEOF_VOIDP == 8 + *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); +#else +# error Unsupported sizeof(void*) +#endif +#else + /* there's got to be a better way to do this right for all archs */ + if(sizeof(void*) == sizeof(unsigned)) + *aligned_address = (void*)(((unsigned)x + 31) & -32); + else if(sizeof(void*) == sizeof(FLAC__uint64)) + *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); + else + return 0; +#endif +#else + x = safe_malloc_(bytes); + *aligned_address = x; +#endif + return x; +} + +FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) +{ + FLAC__int32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__int32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + +#if __WORDSIZE != 64 + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; +#endif + + pu = (FLAC__int32*)FLAC__memory_alloc_aligned(sizeof(*pu) * (size_t)elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) +{ + FLAC__uint32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + +#if __WORDSIZE != 64 + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; +#endif + + pu = (FLAC__uint32*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +{ + FLAC__uint64 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint64 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + +#if __WORDSIZE != 64 + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; +#endif + + pu = (FLAC__uint64*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) +{ + unsigned *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + unsigned *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + +#if __WORDSIZE != 64 + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; +#endif + + pu = (unsigned*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) +{ + FLAC__real *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__real *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + +#if __WORDSIZE != 64 + if((size_t)elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; +#endif + + pu = (FLAC__real*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#endif diff --git a/lib/flac-1.2.1/src/libFLAC/metadata_iterators.c b/lib/flac-1.2.1/src/libFLAC/metadata_iterators.c new file mode 100755 index 0000000..9dd81e8 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/metadata_iterators.c @@ -0,0 +1,3366 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined __BORLANDC__ +#include /* for utime() */ +#else +#include /* for utime() */ +#endif +#include /* for chmod() */ +#include /* for off_t */ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#include /* for chown(), unlink() */ +#endif +#include /* for stat(), maybe chmod() */ + +#include "private/metadata.h" + +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" +#include "share/alloc.h" + +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) +#ifdef min +#undef min +#endif +#define min(a,b) ((a)<(b)?(a):(b)) + + +/**************************************************************************** + * + * Local function declarations + * + ***************************************************************************/ + +static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes); +static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes); +static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes); +static FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes); +static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes); +static FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes); + +static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block); +static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, unsigned *length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned block_length); + +static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block); +static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, unsigned block_length); +static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned block_length); +static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block); +static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block); +static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block); +static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block); +static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned block_length); + +static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, unsigned padding_length, FLAC__bool padding_is_last); +static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append); + +static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator); + +static unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb); +static unsigned seek_to_first_metadata_block_(FILE *f); + +static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append); +static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, off_t fixup_is_last_flag_offset, FLAC__bool backup); + +static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status); + +static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static void cleanup_tempfile_(FILE **tempfile, char **tempfilename); + +static FLAC__bool get_file_stats_(const char *filename, struct stat *stats); +static void set_file_stats_(const char *filename, struct stat *stats); + +static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence); +static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle); + +static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status); + + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +/**************************************************************************** + * + * Level 0 implementation + * + ***************************************************************************/ + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +typedef struct { + FLAC__bool got_error; + FLAC__StreamMetadata *object; +} level0_client_data; + +static FLAC__StreamMetadata *get_one_metadata_block_(void *context, const char *filename, FLAC__MetadataType type) +{ + level0_client_data cd; + FLAC__StreamDecoder *decoder; + + FLAC__ASSERT(0 != filename); + + cd.got_error = false; + cd.object = 0; + + decoder = FLAC__stream_decoder_new(); + + if(0 == decoder) + return 0; + + FLAC__stream_decoder_set_md5_checking(decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, type); + + if(FLAC__stream_decoder_init_file(context, decoder, filename, write_callback_, metadata_callback_, error_callback_, &cd) != FLAC__STREAM_DECODER_INIT_STATUS_OK || cd.got_error) { + (void)FLAC__stream_decoder_finish(context, decoder); + FLAC__stream_decoder_delete(context, decoder); + return 0; + } + + if(!FLAC__stream_decoder_process_until_end_of_metadata(context, decoder) || cd.got_error) { + (void)FLAC__stream_decoder_finish(context, decoder); + FLAC__stream_decoder_delete(context, decoder); + if(0 != cd.object) + FLAC__metadata_object_delete(cd.object); + return 0; + } + + (void)FLAC__stream_decoder_finish(context, decoder); + FLAC__stream_decoder_delete(context, decoder); + + return cd.object; +} + +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(void *context, const char *filename, FLAC__StreamMetadata *streaminfo) +{ + FLAC__StreamMetadata *object; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != streaminfo); + + object = get_one_metadata_block_(context, filename, FLAC__METADATA_TYPE_STREAMINFO); + + if (object) { + /* can just copy the contents since STREAMINFO has no internal structure */ + *streaminfo = *object; + FLAC__metadata_object_delete(object); + return true; + } + else { + return false; + } +} + +FLAC_API FLAC__bool FLAC__metadata_get_tags(void *context, const char *filename, FLAC__StreamMetadata **tags) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tags); + + *tags = get_one_metadata_block_(context, filename, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + return 0 != *tags; +} + +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(void *context, const char *filename, FLAC__StreamMetadata **cuesheet) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != cuesheet); + + *cuesheet = get_one_metadata_block_(context, filename, FLAC__METADATA_TYPE_CUESHEET); + + return 0 != *cuesheet; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + /* + * we assume we only get here when the one metadata block we were + * looking for was passed to us + */ + if(!cd->got_error && 0 == cd->object) { + if(0 == (cd->object = FLAC__metadata_object_clone(metadata))) + cd->got_error = true; + } +} + +void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + cd->got_error = true; +} + +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors) +{ + FLAC__Metadata_SimpleIterator *it; + FLAC__uint64 max_area_seen = 0; + FLAC__uint64 max_depth_seen = 0; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != picture); + + *picture = 0; + + it = FLAC__metadata_simple_iterator_new(); + if(0 == it) + return false; + if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { + FLAC__metadata_simple_iterator_delete(it); + return false; + } + do { + if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) { + FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it); + FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height; + /* check constraints */ + if( + (type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) && + (mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) && + (description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) && + obj->data.picture.width <= max_width && + obj->data.picture.height <= max_height && + obj->data.picture.depth <= max_depth && + obj->data.picture.colors <= max_colors && + (area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen)) + ) { + if(*picture) + FLAC__metadata_object_delete(*picture); + *picture = obj; + max_area_seen = area; + max_depth_seen = obj->data.picture.depth; + } + else { + FLAC__metadata_object_delete(obj); + } + } + } while(FLAC__metadata_simple_iterator_next(it)); + + FLAC__metadata_simple_iterator_delete(it); + + return (0 != *picture); +} + + +/**************************************************************************** + * + * Level 1 implementation + * + ***************************************************************************/ + +#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4) +/* 1 for initial offset, +4 for our own personal use */ + +struct FLAC__Metadata_SimpleIterator { + FILE *file; + char *filename, *tempfile_path_prefix; + struct stat stats; + FLAC__bool has_stats; + FLAC__bool is_writable; + FLAC__Metadata_SimpleIteratorStatus status; + off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH]; + off_t first_offset; /* this is the offset to the STREAMINFO block */ + unsigned depth; + /* this is the metadata block header of the current block we are pointing to: */ + FLAC__bool is_last; + FLAC__MetadataType type; + unsigned length; +}; + +FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = { + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR" +}; + + +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void) +{ + FLAC__Metadata_SimpleIterator *iterator = (FLAC__Metadata_SimpleIterator*)calloc(1, sizeof(FLAC__Metadata_SimpleIterator)); + + if(0 != iterator) { + iterator->file = 0; + iterator->filename = 0; + iterator->tempfile_path_prefix = 0; + iterator->has_stats = false; + iterator->is_writable = false; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + iterator->first_offset = iterator->offset[0] = -1; + iterator->depth = 0; + } + + return iterator; +} + +static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 != iterator->file) { + fclose(iterator->file); + iterator->file = 0; + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + } + if(0 != iterator->filename) { + free(iterator->filename); + iterator->filename = 0; + } + if(0 != iterator->tempfile_path_prefix) { + free(iterator->tempfile_path_prefix); + iterator->tempfile_path_prefix = 0; + } +} + +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + simple_iterator_free_guts_(iterator); + free(iterator); +} + +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__Metadata_SimpleIteratorStatus status; + + FLAC__ASSERT(0 != iterator); + + status = iterator->status; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return status; +} + +static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only) +{ + unsigned ret; + + FLAC__ASSERT(0 != iterator); + + if(read_only || 0 == (iterator->file = fopen(iterator->filename, "r+b"))) { + iterator->is_writable = false; + if(read_only || errno == EACCES) { + if(0 == (iterator->file = fopen(iterator->filename, "rb"))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->is_writable = true; + } + + ret = seek_to_first_metadata_block_(iterator->file); + switch(ret) { + case 0: + iterator->depth = 0; + iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file); + return read_metadata_block_header_(iterator); + case 1: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + case 2: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + case 3: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE; + return false; + default: + FLAC__ASSERT(0); + return false; + } +} + +#if 0 +@@@ If we decide to finish implementing this, put this comment back in metadata.h +/* + * The 'tempfile_path_prefix' allows you to specify a directory where + * tempfiles should go. Remember that if your metadata edits cause the + * FLAC file to grow, the entire file will have to be rewritten. If + * 'tempfile_path_prefix' is NULL, the temp file will be written in the + * same directory as the original FLAC file. This makes replacing the + * original with the tempfile fast but requires extra space in the same + * partition for the tempfile. If space is a problem, you can pass a + * directory name belonging to a different partition in + * 'tempfile_path_prefix'. Note that you should use the forward slash + * '/' as the directory separator. A trailing slash is not needed; it + * will be added automatically. + */ +FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats, const char *tempfile_path_prefix); +#endif + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats) +{ + const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'rename(...)' for what it will take to finish implementing this */ + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != filename); + + simple_iterator_free_guts_(iterator); + + if(!read_only && preserve_file_stats) + iterator->has_stats = get_file_stats_(filename, &iterator->stats); + + if(0 == (iterator->filename = strdup(filename))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + return simple_iterator_prime_input_(iterator, read_only); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->is_writable; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(iterator->is_last) + return false; + + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + iterator->offset[iterator->depth] = ftello(iterator->file); + + return read_metadata_block_header_(iterator); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator) +{ + off_t this_offset; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(iterator->offset[iterator->depth] == iterator->first_offset) + return false; + + if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = iterator->first_offset; + if(!read_metadata_block_header_(iterator)) + return false; + + /* we ignore any error from ftello() and catch it in fseeko() */ + while(ftello(iterator->file) + (off_t)iterator->length < iterator->offset[iterator->depth]) { + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = ftello(iterator->file); + if(!read_metadata_block_header_(iterator)) + return false; + } + + iterator->offset[iterator->depth] = this_offset; + + return true; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->is_last; +} + +/*@@@@add to tests*/ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->offset[iterator->depth]; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->type; +} + +/*@@@@add to tests*/ +FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->length; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != id); + + if(iterator->type != FLAC__METADATA_TYPE_APPLICATION) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(fread(id, 1, id_bytes, iterator->file) != id_bytes) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + /* back up */ + if(0 != fseeko(iterator->file, -((int)id_bytes), SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return true; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__StreamMetadata *block = FLAC__metadata_object_new(iterator->type); + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(0 != block) { + block->is_last = iterator->is_last; + block->length = iterator->length; + + if(!read_metadata_block_data_(iterator, block)) { + FLAC__metadata_object_delete(block); + return 0; + } + + /* back up to the beginning of the block data to stay consistent */ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + FLAC__metadata_object_delete(block); + return 0; + } + } + else + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + return block; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + FLAC__ASSERT_DECLARATION(off_t debug_target_offset = iterator->offset[iterator->depth];) + FLAC__bool ret; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != block); + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) { + if(iterator->type != block->type) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + } + + block->is_last = iterator->is_last; + + if(iterator->length == block->length) + return write_metadata_block_stationary_(iterator, block); + else if(iterator->length > block->length) { + if(use_padding && iterator->length >= FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) { + ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - FLAC__STREAM_METADATA_HEADER_LENGTH - block->length, block->is_last); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else /* iterator->length < block->length */ { + unsigned padding_leftover = 0; + FLAC__bool padding_is_last = false; + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + const unsigned extra_padding_bytes_required = block->length - iterator->length; + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length == extra_padding_bytes_required) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < extra_padding_bytes_required) + use_padding = false; + else { + padding_leftover = FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length - extra_padding_bytes_required; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH); + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + unsigned padding_leftover = 0; + FLAC__bool padding_is_last = false; + + FLAC__ASSERT_DECLARATION(off_t debug_target_offset = iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length;) + FLAC__bool ret; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != block); + + if(!iterator->is_writable) + return false; + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + block->is_last = iterator->is_last; + + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(iterator->length == block->length) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) + use_padding = false; + else { + padding_leftover = iterator->length - block->length; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + /* move to the next block, which is suitable padding */ + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH); + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/true); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding) +{ + FLAC__ASSERT_DECLARATION(off_t debug_target_offset = iterator->offset[iterator->depth];) + FLAC__bool ret; + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(use_padding) { + FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + if(0 == padding) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + padding->length = iterator->length; + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) { + FLAC__metadata_object_delete(padding); + return false; + } + FLAC__metadata_object_delete(padding); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return false; + FLAC__ASSERT(iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) + (off_t)iterator->length == debug_target_offset); + return true; + } + else { + ret = rewrite_whole_file_(iterator, 0, /*append=*/false); + FLAC__ASSERT(iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) + (off_t)iterator->length == debug_target_offset); + return ret; + } +} + + + +/**************************************************************************** + * + * Level 2 implementation + * + ***************************************************************************/ + + +typedef struct FLAC__Metadata_Node { + FLAC__StreamMetadata *data; + struct FLAC__Metadata_Node *prev, *next; +} FLAC__Metadata_Node; + +struct FLAC__Metadata_Chain { + char *filename; /* will be NULL if using callbacks */ + FLAC__bool is_ogg; + FLAC__Metadata_Node *head; + FLAC__Metadata_Node *tail; + unsigned nodes; + FLAC__Metadata_ChainStatus status; + off_t first_offset, last_offset; + /* + * This is the length of the chain initially read from the FLAC file. + * it is used to compare against the current length to decide whether + * or not the whole file has to be rewritten. + */ + off_t initial_length; + /* @@@ hacky, these are currently only needed by ogg reader */ + FLAC__IOHandle handle; + FLAC__IOCallback_Read read_cb; +}; + +struct FLAC__Metadata_Iterator { + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Node *current; +}; + +FLAC_API const char * const FLAC__Metadata_ChainStatusString[] = { + "FLAC__METADATA_CHAIN_STATUS_OK", + "FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE", + "FLAC__METADATA_CHAIN_STATUS_BAD_METADATA", + "FLAC__METADATA_CHAIN_STATUS_READ_ERROR", + "FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR", + "FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR", + "FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS", + "FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", + "FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL" +}; + + +static FLAC__Metadata_Node *node_new_(void) +{ + return (FLAC__Metadata_Node*)calloc(1, sizeof(FLAC__Metadata_Node)); +} + +static void node_delete_(FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + if(0 != node->data) + FLAC__metadata_object_delete(node->data); + free(node); +} + +static void chain_init_(FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != chain); + + chain->filename = 0; + chain->is_ogg = false; + chain->head = chain->tail = 0; + chain->nodes = 0; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + chain->initial_length = 0; + chain->read_cb = 0; +} + +static void chain_clear_(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *next; + + FLAC__ASSERT(0 != chain); + + for(node = chain->head; node; ) { + next = node->next; + node_delete_(node); + node = next; + } + + if(0 != chain->filename) + free(chain->filename); + + chain_init_(chain); +} + +static void chain_append_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + + node->next = node->prev = 0; + node->data->is_last = true; + if(0 != chain->tail) + chain->tail->data->is_last = false; + + if(0 == chain->head) + chain->head = node; + else { + FLAC__ASSERT(0 != chain->tail); + chain->tail->next = node; + node->prev = chain->tail; + } + chain->tail = node; + chain->nodes++; +} + +static void chain_remove_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != node); + + if(node == chain->head) + chain->head = node->next; + else + node->prev->next = node->next; + + if(node == chain->tail) + chain->tail = node->prev; + else + node->next->prev = node->prev; + + if(0 != chain->tail) + chain->tail->data->is_last = true; + + chain->nodes--; +} + +static void chain_delete_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + chain_remove_node_(chain, node); + node_delete_(node); +} + +static off_t chain_calculate_length_(FLAC__Metadata_Chain *chain) +{ + const FLAC__Metadata_Node *node; + off_t length = 0; + for(node = chain->head; node; node = node->next) + length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + return length; +} + +static void iterator_insert_node_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->chain); + FLAC__ASSERT(0 != iterator->chain->head); + FLAC__ASSERT(0 != iterator->chain->tail); + + node->data->is_last = false; + + node->prev = iterator->current->prev; + node->next = iterator->current; + + if(0 == node->prev) + iterator->chain->head = node; + else + node->prev->next = node; + + iterator->current->prev = node; + + iterator->chain->nodes++; +} + +static void iterator_insert_node_after_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->chain); + FLAC__ASSERT(0 != iterator->chain->head); + FLAC__ASSERT(0 != iterator->chain->tail); + + iterator->current->data->is_last = false; + + node->prev = iterator->current; + node->next = iterator->current->next; + + if(0 == node->next) + iterator->chain->tail = node; + else + node->next->prev = node; + + node->prev->next = node; + + iterator->chain->tail->data->is_last = true; + + iterator->chain->nodes++; +} + +/* return true iff node and node->next are both padding */ +static FLAC__bool chain_merge_adjacent_padding_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) { + const unsigned growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length; + node->data->length += growth; + + chain_delete_node_(chain, node->next); + return true; + } + else + return false; +} + +/* Returns the new length of the chain, or 0 if there was an error. */ +/* WATCHOUT: This can get called multiple times before a write, so + * it should still work when this happens. + */ +/* WATCHOUT: Make sure to also update the logic in + * FLAC__metadata_chain_check_if_tempfile_needed() if the logic here changes. + */ +static off_t chain_prepare_for_write_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + off_t current_length = chain_calculate_length_(chain); + + if(use_padding) { + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + const off_t delta = chain->initial_length - current_length; + chain->tail->data->length += delta; + current_length += delta; + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) { + FLAC__StreamMetadata *padding; + FLAC__Metadata_Node *node; + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + padding->length = chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length); + if(0 == (node = node_new_())) { + FLAC__metadata_object_delete(padding); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + node->data = padding; + chain_append_node_(chain, node); + current_length = chain_calculate_length_(chain); + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const off_t delta = current_length - chain->initial_length; + if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((off_t)chain->tail->data->length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) { + chain_delete_node_(chain, chain->tail); + current_length = chain_calculate_length_(chain); + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((off_t)chain->tail->data->length >= delta) { + chain->tail->data->length -= delta; + current_length -= delta; + FLAC__ASSERT(current_length == chain->initial_length); + } + } + } + } + + return current_length; +} + +static FLAC__bool chain_read_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Tell tell_cb) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + + /* we assume we're already at the beginning of the file */ + + switch(seek_to_first_metadata_block_cb_(handle, read_cb, seek_cb)) { + case 0: + break; + case 1: + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + case 2: + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + case 3: + chain->status = FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + return false; + default: + FLAC__ASSERT(0); + return false; + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->first_offset = (off_t)pos; + } + + { + FLAC__bool is_last; + FLAC__MetadataType type; + unsigned length; + + do { + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + if(!read_metadata_block_header_cb_(handle, read_cb, &is_last, &type, &length)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + + node->data = FLAC__metadata_object_new(type); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + node->data->is_last = is_last; + node->data->length = length; + + chain->status = get_equivalent_status_(read_metadata_block_data_cb_(handle, read_cb, seek_cb, node->data)); + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + node_delete_(node); + return false; + } + chain_append_node_(chain, node); + } while(!is_last); + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->last_offset = (off_t)pos; + } + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__StreamDecoderReadStatus chain_read_ogg_read_cb_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder; + if(*bytes > 0 && chain->status == FLAC__METADATA_CHAIN_STATUS_OK) { + *bytes = chain->read_cb(buffer, sizeof(FLAC__byte), *bytes, chain->handle); + if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; +} + +static FLAC__StreamDecoderWriteStatus chain_read_ogg_write_cb_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void chain_read_ogg_metadata_cb_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + FLAC__Metadata_Node *node; + + (void)decoder; + + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + node->data = FLAC__metadata_object_clone(metadata); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + chain_append_node_(chain, node); +} + +static void chain_read_ogg_error_cb_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder, (void)status; + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ +} + +static FLAC__bool chain_read_ogg_cb_(void *context, FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb) +{ + FLAC__StreamDecoder *decoder; + + FLAC__ASSERT(0 != chain); + + /* we assume we're already at the beginning of the file */ + + chain->handle = handle; + chain->read_cb = read_cb; + if(0 == (decoder = FLAC__stream_decoder_new())) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + FLAC__stream_decoder_set_metadata_respond_all(decoder); + if(FLAC__stream_decoder_init_ogg_stream(context, decoder, chain_read_ogg_read_cb_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, chain_read_ogg_write_cb_, chain_read_ogg_metadata_cb_, chain_read_ogg_error_cb_, chain) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + FLAC__stream_decoder_delete(context, decoder); + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + return false; + } + + chain->first_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + if(!FLAC__stream_decoder_process_until_end_of_metadata(context, decoder)) + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + FLAC__stream_decoder_delete(context, decoder); + return false; + } + + FLAC__stream_decoder_delete(context, decoder); + + chain->last_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->head); + + if(0 != seek_cb(handle, chain->first_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_(FLAC__Metadata_Chain *chain) +{ + FILE *file; + FLAC__bool ret; + + FLAC__ASSERT(0 != chain->filename); + + if(0 == (file = fopen(chain->filename, "r+b"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* chain_rewrite_metadata_in_place_cb_() sets chain->status for us */ + ret = chain_rewrite_metadata_in_place_cb_(chain, (FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, fseek_wrapper_); + + fclose(file); + + return ret; +} + +static FLAC__bool chain_rewrite_file_(FLAC__Metadata_Chain *chain, const char *tempfile_path_prefix) +{ + FILE *f, *tempfile; + char *tempfilename; + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->filename); + FLAC__ASSERT(0 != chain->head); + + /* copy the file prefix (data up to first metadata block */ + if(0 == (f = fopen(chain->filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) { + chain->status = get_equivalent_status_(status); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + return false; + } + if(!write_metadata_block_data_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + return false; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != fseeko(f, chain->last_offset, SEEK_SET)) { + cleanup_tempfile_(&tempfile, &tempfilename); + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) { + cleanup_tempfile_(&tempfile, &tempfilename); + chain->status = get_equivalent_status_(status); + return false; + } + + /* move the tempfile on top of the original */ + (void)fclose(f); + if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status)) + return false; + + return true; +} + +/* assumes 'handle' is already at beginning of file */ +static FLAC__bool chain_rewrite_file_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb) +{ + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 == chain->filename); + FLAC__ASSERT(0 != chain->head); + + /* copy the file prefix (data up to first metadata block */ + if(!copy_n_bytes_from_file_cb_(handle, read_cb, temp_handle, temp_write_cb, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != seek_cb(handle, chain->last_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_cb_(handle, read_cb, eof_cb, temp_handle, temp_write_cb, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + return true; +} + +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)calloc(1, sizeof(FLAC__Metadata_Chain)); + + if(0 != chain) + chain_init_(chain); + + return chain; +} + +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != chain); + + chain_clear_(chain); + + free(chain); +} + +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_ChainStatus status; + + FLAC__ASSERT(0 != chain); + + status = chain->status; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return status; +} + +static FLAC__bool chain_read_(void *context, FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool is_ogg) +{ + FILE *file; + FLAC__bool ret; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != filename); + + chain_clear_(chain); + + if(0 == (chain->filename = strdup(filename))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + chain->is_ogg = is_ogg; + + if(0 == (file = fopen(filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(context, chain, file, (FLAC__IOCallback_Read)fread) : + chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_) + ; + + fclose(file); + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read(void *context, FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(context, chain, filename, /*is_ogg=*/false); +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(void *context, FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(context, chain, filename, /*is_ogg=*/true); +} + +static FLAC__bool chain_read_with_callbacks_(void *context, FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__bool is_ogg) +{ + FLAC__bool ret; + + FLAC__ASSERT(0 != chain); + + chain_clear_(chain); + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.tell) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + chain->is_ogg = is_ogg; + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(context, chain, handle, callbacks.read) : + chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell) + ; + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(void *context,FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(context, chain, handle, callbacks, /*is_ogg=*/false); +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(void *context, FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(context, chain, handle, callbacks, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + /* This does all the same checks that are in chain_prepare_for_write_() + * but doesn't actually alter the chain. Make sure to update the logic + * here if chain_prepare_for_write_() changes. + */ + const off_t current_length = chain_calculate_length_(chain); + + FLAC__ASSERT(0 != chain); + + if(use_padding) { + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) + return false; + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) + return false; + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const off_t delta = current_length - chain->initial_length; + if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((off_t)chain->tail->data->length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) + return false; + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((off_t)chain->tail->data->length >= delta) + return false; + } + } + } + + return (current_length != chain->initial_length); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats) +{ + struct stat stats; + const char *tempfile_path_prefix = 0; + off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 == chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + if(preserve_file_stats) + get_file_stats_(chain->filename, &stats); + + if(current_length == chain->initial_length) { + if(!chain_rewrite_metadata_in_place_(chain)) + return false; + } + else { + if(!chain_rewrite_file_(chain, tempfile_path_prefix)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + } + + if(preserve_file_stats) + set_file_stats_(chain->filename, &stats); + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.write || 0 == callbacks.seek) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + FLAC__ASSERT(current_length == chain->initial_length); + + return chain_rewrite_metadata_in_place_cb_(chain, handle, callbacks.write, callbacks.seek); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks) +{ + off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.eof) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + if (0 == temp_callbacks.write) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (!FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + FLAC__ASSERT(current_length != chain->initial_length); + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + if(!chain_rewrite_file_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.eof, temp_handle, temp_callbacks.write)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + + return true; +} + +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + + for(node = chain->head; node; ) { + if(!chain_merge_adjacent_padding_(chain, node)) + node = node->next; + } +} + +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *save; + unsigned i; + + FLAC__ASSERT(0 != chain); + + /* + * Don't try and be too smart... this simple algo is good enough for + * the small number of nodes that we deal with. + */ + for(i = 0, node = chain->head; i < chain->nodes; i++) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + save = node->next; + chain_remove_node_(chain, node); + chain_append_node_(chain, node); + node = save; + } + else { + node = node->next; + } + } + + FLAC__metadata_chain_merge_padding(chain); +} + + +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void) +{ + FLAC__Metadata_Iterator *iterator = (FLAC__Metadata_Iterator*)calloc(1, sizeof(FLAC__Metadata_Iterator)); + + /* calloc() implies: + iterator->current = 0; + iterator->chain = 0; + */ + + return iterator; +} + +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + free(iterator); +} + +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->head); + + iterator->chain = chain; + iterator->current = chain->head; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 == iterator->current || 0 == iterator->current->next) + return false; + + iterator->current = iterator->current->next; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 == iterator->current || 0 == iterator->current->prev) + return false; + + iterator->current = iterator->current->prev; + return true; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->current->data); + + return iterator->current->data->type; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + + return iterator->current->data; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != block); + return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block); +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding) +{ + FLAC__Metadata_Node *save; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + + if(0 == iterator->current->prev) { + FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO); + return false; + } + + save = iterator->current->prev; + + if(replace_with_padding) { + FLAC__metadata_object_delete_data(iterator->current->data); + iterator->current->data->type = FLAC__METADATA_TYPE_PADDING; + } + else { + chain_delete_node_(iterator->chain, iterator->current); + } + + iterator->current = save; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != block); + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == iterator->current->prev) { + FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO); + return false; + } + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_(iterator, node); + iterator->current = node; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != block); + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_after_(iterator, node); + iterator->current = node; + return true; +} + + +/**************************************************************************** + * + * Local function definitions + * + ***************************************************************************/ + +void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + for(i = 0; i < bytes; i++) { + *(b++) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint32 ret = 0; + unsigned i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*b++); + + return ret; +} + +FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint32 ret = 0; + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*--b); + + return ret; +} + +FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint64 ret = 0; + unsigned i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint64)(*b++); + + return ret; +} + +FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(!read_metadata_block_header_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, &iterator->is_last, &iterator->type, &iterator->length)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + return true; +} + +FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + iterator->status = read_metadata_block_data_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, block); + + return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK); +} + +FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, unsigned *length) +{ + FLAC__byte raw_header[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + if(read_cb(raw_header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + *is_last = raw_header[0] & 0x80? true : false; + *type = (FLAC__MetadataType)(raw_header[0] & 0x7f); + *length = unpack_uint32_(raw_header + 1, 3); + + /* Note that we don't check: + * if(iterator->type >= FLAC__METADATA_TYPE_UNDEFINED) + * we just will read in an opaque block + */ + + return true; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block) +{ + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return read_metadata_block_data_streaminfo_cb_(handle, read_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return read_metadata_block_data_padding_cb_(handle, seek_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return read_metadata_block_data_application_cb_(handle, read_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return read_metadata_block_data_seektable_cb_(handle, read_cb, &block->data.seek_table, block->length); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return read_metadata_block_data_vorbis_comment_cb_(handle, read_cb, &block->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return read_metadata_block_data_cuesheet_cb_(handle, read_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return read_metadata_block_data_picture_cb_(handle, read_cb, &block->data.picture); + default: + return read_metadata_block_data_unknown_cb_(handle, read_cb, &block->data.unknown, block->length); + } +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b; + + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + b = buffer; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level unpacker and use the + * _STREAMINFO_ constants. + */ + block->min_blocksize = unpack_uint32_(b, 2); b += 2; + block->max_blocksize = unpack_uint32_(b, 2); b += 2; + block->min_framesize = unpack_uint32_(b, 3); b += 3; + block->max_framesize = unpack_uint32_(b, 3); b += 3; + block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((unsigned)(b[2] & 0xf0) >> 4); + block->channels = (unsigned)((b[2] & 0x0e) >> 1) + 1; + block->bits_per_sample = ((((unsigned)(b[2] & 0x01)) << 4) | (((unsigned)(b[3] & 0xf0)) >> 4)) + 1; + block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4); + memcpy(block->md5sum, b+8, 16); + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, unsigned block_length) +{ + (void)block; /* nothing to do; we don't care about reading the padding bytes */ + + if(0 != seek_cb(handle, block_length, SEEK_CUR)) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(read_cb(block->id, 1, id_bytes, handle) != id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + if(block_length < id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + block_length -= id_bytes; + + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = (FLAC__byte*)malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned block_length) +{ + unsigned i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + FLAC__ASSERT(block_length % FLAC__STREAM_METADATA_SEEKPOINT_LENGTH == 0); + + block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + if(block->num_points == 0) + block->points = 0; + else if(0 == (block->points = (FLAC__StreamMetadata_SeekPoint*)safe_malloc_mul_2op_(block->num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_points; i++) { + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + /* some MAGIC NUMBERs here */ + block->points[i].sample_number = unpack_uint64_(buffer, 8); + block->points[i].stream_offset = unpack_uint64_(buffer+8, 8); + block->points[i].frame_samples = unpack_uint32_(buffer+16, 2); + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry) +{ + const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8 == sizeof(buffer)); + + if(read_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + entry->length = unpack_uint32_little_endian_(buffer, entry_length_len); + + if(0 != entry->entry) + free(entry->entry); + + if(entry->length == 0) { + entry->entry = 0; + } + else { + if(0 == (entry->entry = (FLAC__byte*)safe_malloc_add_2op_(entry->length, /*+*/1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(entry->entry, 1, entry->length, handle) != entry->length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + entry->entry[entry->length] = '\0'; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment *block) +{ + unsigned i; + FLAC__Metadata_SimpleIteratorStatus status; + const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8 == sizeof(buffer)); + + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, &(block->vendor_string)))) + return status; + + if(read_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len); + + if(block->num_comments == 0) { + block->comments = 0; + } + else if(0 == (block->comments = (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(block->num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_comments; i++) { + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, block->comments + i))) + return status; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track) +{ + unsigned i, len; + FLAC__byte buffer[32]; /* asserted below that this is big enough */ + + FLAC__ASSERT(sizeof(buffer) >= sizeof(FLAC__uint64)); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->offset = unpack_uint64_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->number = (FLAC__byte)unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(read_cb(track->isrc, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN == 1); + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN == 1); + track->type = buffer[0] >> 7; + track->pre_emphasis = (buffer[0] >> 6) & 1; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->num_indices = (FLAC__byte)unpack_uint32_(buffer, len); + + if(track->num_indices == 0) { + track->indices = 0; + } + else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)calloc(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < track->num_indices; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].offset = unpack_uint64_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].number = (FLAC__byte)unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block) +{ + unsigned i, len; + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[1024]; /* MSVC needs a constant expression so we put a magic number and assert */ + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)/8 <= sizeof(buffer)); + FLAC__ASSERT(sizeof(FLAC__uint64) <= sizeof(buffer)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(read_cb(block->media_catalog_number, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->lead_in = unpack_uint64_(buffer, len); + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->is_cd = buffer[0]&0x80? true : false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_tracks = unpack_uint32_(buffer, len); + + if(block->num_tracks == 0) { + block->tracks = 0; + } + else if(0 == (block->tracks = (FLAC__StreamMetadata_CueSheet_Track*)calloc(block->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_tracks; i++) { + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_cuesheet_track_cb_(handle, read_cb, block->tracks + i))) + return status; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len) +{ + FLAC__byte buffer[sizeof(FLAC__uint32)]; + + FLAC__ASSERT(0 != data); + FLAC__ASSERT(length_len%8 == 0); + + length_len /= 8; /* convert to bytes */ + + FLAC__ASSERT(sizeof(buffer) >= length_len); + + if(read_cb(buffer, 1, length_len, handle) != length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + *length = unpack_uint32_(buffer, length_len); + + if(0 != *data) + free(*data); + + if(0 == (*data = (FLAC__byte*)safe_malloc_add_2op_(*length, /*+*/1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(*length > 0) { + if(read_cb(*data, 1, *length, handle) != *length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + (*data)[*length] = '\0'; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block) +{ + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[4]; /* asserted below that this is big enough */ + FLAC__uint32 len; + + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_TYPE_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->type = (FLAC__StreamMetadata_Picture_Type)unpack_uint32_(buffer, len); + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, (FLAC__byte**)(&(block->mime_type)), &len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->description), &len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->width = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->height = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->depth = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_COLORS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->colors = unpack_uint32_(buffer, len); + + /* for convenience we use read_metadata_block_data_picture_cstring_cb_() even though it adds an extra terminating NUL we don't use */ + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->data), &(block->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned block_length) +{ + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = (FLAC__byte*)malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != status); + + if(!write_metadata_block_header_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != status); + + if (write_metadata_block_data_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return true; + } + else { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } +} + +FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + FLAC__ASSERT(block->length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); + + buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type; + pack_uint32_(block->length, buffer + 1, 3); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != block); + + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return write_metadata_block_data_streaminfo_cb_(handle, write_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return write_metadata_block_data_padding_cb_(handle, write_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return write_metadata_block_data_application_cb_(handle, write_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return write_metadata_block_data_seektable_cb_(handle, write_cb, &block->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return write_metadata_block_data_vorbis_comment_cb_(handle, write_cb, &block->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return write_metadata_block_data_cuesheet_cb_(handle, write_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return write_metadata_block_data_picture_cb_(handle, write_cb, &block->data.picture); + default: + return write_metadata_block_data_unknown_cb_(handle, write_cb, &block->data.unknown, block->length); + } +} + +FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; + const unsigned channels1 = block->channels - 1; + const unsigned bps1 = block->bits_per_sample - 1; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level packer and use the + * _STREAMINFO_ constants. + */ + pack_uint32_(block->min_blocksize, buffer, 2); + pack_uint32_(block->max_blocksize, buffer+2, 2); + pack_uint32_(block->min_framesize, buffer+4, 3); + pack_uint32_(block->max_framesize, buffer+7, 3); + buffer[10] = (block->sample_rate >> 12) & 0xff; + buffer[11] = (block->sample_rate >> 4) & 0xff; + buffer[12] = ((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4); + buffer[13] = (FLAC__byte)(((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f)); + pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4); + memcpy(buffer+18, block->md5sum, 16); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, unsigned block_length) +{ + unsigned i, n = block_length; + FLAC__byte buffer[1024]; + + (void)block; + + memset(buffer, 0, 1024); + + for(i = 0; i < n/1024; i++) + if(write_cb(buffer, 1, 1024, handle) != 1024) + return false; + + n %= 1024; + + if(write_cb(buffer, 1, n, handle) != n) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned block_length) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(write_cb(block->id, 1, id_bytes, handle) != id_bytes) + return false; + + block_length -= id_bytes; + + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block) +{ + unsigned i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + for(i = 0; i < block->num_points; i++) { + /* some MAGIC NUMBERs here */ + pack_uint64_(block->points[i].sample_number, buffer, 8); + pack_uint64_(block->points[i].stream_offset, buffer+8, 8); + pack_uint32_(block->points[i].frame_samples, buffer+16, 2); + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block) +{ + unsigned i; + const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(max(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN, FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8 == sizeof(buffer)); + + pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->vendor_string.entry, 1, block->vendor_string.length, handle) != block->vendor_string.length) + return false; + + pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len); + if(write_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return false; + + for(i = 0; i < block->num_comments; i++) { + pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->comments[i].entry, 1, block->comments[i].length, handle) != block->comments[i].length) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block) +{ + unsigned i, j, len; + FLAC__byte buffer[1024]; /* asserted below that this is big enough */ + + FLAC__ASSERT(sizeof(buffer) >= sizeof(FLAC__uint64)); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN/8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(write_cb(block->media_catalog_number, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + pack_uint64_(block->lead_in, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + memset(buffer, 0, len); + if(block->is_cd) + buffer[0] |= 0x80; + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + pack_uint32_(block->num_tracks, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(i = 0; i < block->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = block->tracks + i; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + pack_uint64_(track->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + pack_uint32_(track->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(write_cb(track->isrc, 1, len, handle) != len) + return false; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + memset(buffer, 0, len); + buffer[0] = (track->type << 7) | (track->pre_emphasis << 6); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + pack_uint32_(track->num_indices, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *index = track->indices + j; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + pack_uint64_(index->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + pack_uint32_(index->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + memset(buffer, 0, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + } + } + + return true; +} + +FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block) +{ + unsigned len; + size_t slen; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_TYPE_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_COLORS_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN%8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8); + + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8; + pack_uint32_(block->type, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8; + slen = strlen(block->mime_type); + pack_uint32_((unsigned int)slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->mime_type, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8; + slen = strlen((const char *)block->description); + pack_uint32_((unsigned int)slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->description, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8; + pack_uint32_(block->width, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8; + pack_uint32_(block->height, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8; + pack_uint32_(block->depth, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8; + pack_uint32_(block->colors, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8; + pack_uint32_(block->data_length, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->data, 1, block->data_length, handle) != block->data_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned block_length) +{ + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block) +{ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, unsigned padding_length, FLAC__bool padding_is_last) +{ + FLAC__StreamMetadata *padding; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + block->is_last = false; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + padding->is_last = padding_is_last; + padding->length = padding_length; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + FLAC__metadata_object_delete(padding); + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append) +{ + FILE *tempfile; + char *tempfilename; + int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */ + off_t fixup_is_last_flag_offset = -1; + + FLAC__ASSERT(0 != block || append == false); + + if(iterator->is_last) { + if(append) { + fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + } + else if(0 == block) { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_prev(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + if(!simple_iterator_pop_(iterator)) + return false; + } + } + + if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append)) + return false; + + if(0 != block) { + if(!write_metadata_block_header_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + + if(!write_metadata_block_data_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + } + + if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0)) + return false; + + if(append) + return FLAC__metadata_simple_iterator_next(iterator); + + return true; +} + +void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(iterator->depth+1 < SIMPLE_ITERATOR_MAX_PUSH_DEPTH); + iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth]; + iterator->depth++; +} + +FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(iterator->depth > 0); + iterator->depth--; + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +/* return meanings: + * 0: ok + * 1: read error + * 2: seek error + * 3: not a FLAC file + */ +unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__byte buffer[4]; + size_t n; + unsigned i; + + FLAC__ASSERT(FLAC__STREAM_SYNC_LENGTH == sizeof(buffer)); + + /* skip any id3v2 tag */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + else if(0 == memcmp(buffer, "ID3", 3)) { + unsigned tag_length = 0; + + /* skip to the tag length */ + if(seek_cb(handle, 2, SEEK_CUR) < 0) + return 2; + + /* read the length */ + for(i = 0; i < 4; i++) { + if(read_cb(buffer, 1, 1, handle) < 1 || buffer[0] & 0x80) + return 1; + tag_length <<= 7; + tag_length |= (buffer[0] & 0x7f); + } + + /* skip the rest of the tag */ + if(seek_cb(handle, tag_length, SEEK_CUR) < 0) + return 2; + + /* read the stream sync code */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + } + + /* check for the fLaC signature */ + if(0 == memcmp(FLAC__STREAM_SYNC_STRING, buffer, FLAC__STREAM_SYNC_LENGTH)) + return 0; + else + return 3; +} + +unsigned seek_to_first_metadata_block_(FILE *f) +{ + return seek_to_first_metadata_block_cb_((FLAC__IOHandle)f, (FLAC__IOCallback_Read)fread, fseek_wrapper_); +} + +FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append) +{ + const off_t offset_end = append? iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length : iterator->offset[iterator->depth]; + + if(0 != fseeko(iterator->file, 0, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + return true; +} + +FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, off_t fixup_is_last_flag_offset, FLAC__bool backup) +{ + off_t save_offset = iterator->offset[iterator->depth]; + FLAC__ASSERT(0 != *tempfile); + + if(0 != fseeko(iterator->file, save_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + if(fixup_is_last_code != 0) { + /* + * if code == 1, it means a block was appended to the end so + * we have to clear the is_last flag of the previous block + * if code == -1, it means the last block was deleted so + * we have to set the is_last flag of the previous block + */ + /* MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */ + FLAC__byte x; + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(fread(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(fixup_is_last_code > 0) { + FLAC__ASSERT(x & 0x80); + x &= 0x7f; + } + else { + FLAC__ASSERT(!(x & 0x80)); + x |= 0x80; + } + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(local__fwrite(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + (void)fclose(iterator->file); + + if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status)) + return false; + + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + + if(!simple_iterator_prime_input_(iterator, !iterator->is_writable)) + return false; + if(backup) { + while(iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length < save_offset) + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + return true; + } + else { + /* move the iterator to it's original block faster by faking a push, then doing a pop_ */ + FLAC__ASSERT(iterator->depth == 0); + iterator->offset[0] = save_offset; + iterator->depth++; + return simple_iterator_pop_(iterator); + } +} + +FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + FLAC__ASSERT(bytes >= 0); + while(bytes > 0) { + n = min(sizeof(buffer), (size_t)bytes); + if(fread(buffer, 1, n, file) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= (off_t)n; + } + + return true; +} + +FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + FLAC__ASSERT(bytes >= 0); + while(bytes > 0) { + n = min(sizeof(buffer), (size_t)bytes); + if(read_cb(buffer, 1, n, handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= (off_t)n; + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!feof(file)) { + n = fread(buffer, 1, sizeof(buffer), file); + if(n == 0 && !feof(file)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!eof_cb(handle)) { + n = read_cb(buffer, 1, sizeof(buffer), handle); + if(n == 0 && !eof_cb(handle)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + static const char *tempfile_suffix = ".metadata_edit"; + if(0 == tempfile_path_prefix) { + if(0 == (*tempfilename = (char*)safe_malloc_add_3op_(strlen(filename), /*+*/strlen(tempfile_suffix), /*+*/1))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + strcpy(*tempfilename, filename); + strcat(*tempfilename, tempfile_suffix); + } + else { + const char *p = strrchr(filename, '/'); + if(0 == p) + p = filename; + else + p++; + + if(0 == (*tempfilename = (char*)safe_malloc_add_4op_(strlen(tempfile_path_prefix), /*+*/strlen(p), /*+*/strlen(tempfile_suffix), /*+*/2))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + strcpy(*tempfilename, tempfile_path_prefix); + strcat(*tempfilename, "/"); + strcat(*tempfilename, p); + strcat(*tempfilename, tempfile_suffix); + } + + if(0 == (*tempfile = fopen(*tempfilename, "w+b"))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + + return true; +} + +FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tempfile); + FLAC__ASSERT(0 != *tempfile); + FLAC__ASSERT(0 != tempfilename); + FLAC__ASSERT(0 != *tempfilename); + FLAC__ASSERT(0 != status); + + (void)fclose(*tempfile); + *tempfile = 0; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__ + /* on some flavors of windows, rename() will fail if the destination already exists */ + if(unlink(filename) < 0) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR; + return false; + } +#endif + + /*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just rename(): */ + if(0 != rename(*tempfilename, filename)) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR; + return false; + } + + cleanup_tempfile_(tempfile, tempfilename); + + return true; +} + +void cleanup_tempfile_(FILE **tempfile, char **tempfilename) +{ + if(0 != *tempfile) { + (void)fclose(*tempfile); + *tempfile = 0; + } + + if(0 != *tempfilename) { + (void)unlink(*tempfilename); + free(*tempfilename); + *tempfilename = 0; + } +} + +FLAC__bool get_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + return (0 == stat(filename, stats)); +} + +void set_file_stats_(const char *filename, struct stat *stats) +{ + struct utimbuf srctime; +#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__ && !defined __EMX__ + int success; +#endif + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + + srctime.actime = stats->st_atime; + srctime.modtime = stats->st_mtime; + (void)chmod(filename, stats->st_mode); + (void)utime(filename, &srctime); +#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__ && !defined __EMX__ + success = chown(filename, stats->st_uid, -1); + success = chown(filename, -1, stats->st_gid); +#endif +} + +int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence) +{ + return fseeko((FILE*)handle, (off_t)offset, whence); +} + +FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle) +{ + return ftello((FILE*)handle); +} + +FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status) +{ + switch(status) { + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK: + return FLAC__METADATA_CHAIN_STATUS_OK; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT: + return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE: + return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE: + return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE: + return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA: + return FLAC__METADATA_CHAIN_STATUS_BAD_METADATA; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR: + return FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR: + return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR: + return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR: + default: + return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + } +} diff --git a/lib/flac-1.2.1/src/libFLAC/metadata_object.c b/lib/flac-1.2.1/src/libFLAC/metadata_object.c new file mode 100755 index 0000000..5848a24 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/metadata_object.c @@ -0,0 +1,1829 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "private/metadata.h" + +#include "FLAC/assert.h" +#include "share/alloc.h" + + +/**************************************************************************** + * + * Local routines + * + ***************************************************************************/ + +/* copy bytes: + * from = NULL && bytes = 0 + * to <- NULL + * from != NULL && bytes > 0 + * to <- copy of from + * else ASSERT + * malloc error leaves 'to' unchanged + */ +static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes) +{ + FLAC__ASSERT(0 != to); + if(bytes > 0 && 0 != from) { + FLAC__byte *x; + if(0 == (x = (FLAC__byte*)safe_malloc_(bytes))) + return false; + memcpy(x, from, bytes); + *to = x; + } + else { + FLAC__ASSERT(0 == from); + FLAC__ASSERT(bytes == 0); + *to = 0; + } + return true; +} + +#if 0 /* UNUSED */ +/* like copy_bytes_(), but free()s the original '*to' if the copy succeeds and the original '*to' is non-NULL */ +static FLAC__bool free_copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes) +{ + FLAC__byte *copy; + FLAC__ASSERT(0 != to); + if(copy_bytes_(©, from, bytes)) { + if(*to) + free(*to); + *to = copy; + return true; + } + else + return false; +} +#endif + +/* reallocate entry to 1 byte larger and add a terminating NUL */ +/* realloc() failure leaves entry unchanged */ +static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, unsigned length) +{ + FLAC__byte *x = (FLAC__byte*)safe_realloc_add_2op_(*entry, length, /*+*/1); + if(0 != x) { + x[length] = '\0'; + *entry = x; + return true; + } + else + return false; +} + +/* copies the NUL-terminated C-string 'from' to '*to', leaving '*to' + * unchanged if malloc fails, free()ing the original '*to' if it + * succeeds and the original '*to' was not NULL + */ +static FLAC__bool copy_cstring_(char **to, const char *from) +{ + char *copy = strdup(from); + FLAC__ASSERT(to); + if(copy) { + if(*to) + free(*to); + *to = copy; + return true; + } + else + return false; +} + +static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from) +{ + to->length = from->length; + if(0 == from->entry) { + FLAC__ASSERT(from->length == 0); + to->entry = 0; + } + else { + FLAC__byte *x; + FLAC__ASSERT(from->length > 0); + if(0 == (x = (FLAC__byte*)safe_malloc_add_2op_(from->length, /*+*/1))) + return false; + memcpy(x, from->entry, from->length); + x[from->length] = '\0'; + to->entry = x; + } + return true; +} + +static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from) +{ + memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track)); + if(0 == from->indices) { + FLAC__ASSERT(from->num_indices == 0); + } + else { + FLAC__StreamMetadata_CueSheet_Index *x; + FLAC__ASSERT(from->num_indices > 0); + if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)safe_malloc_mul_2op_(from->num_indices, /*times*/sizeof(FLAC__StreamMetadata_CueSheet_Index)))) + return false; + memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)); + to->indices = x; + } + return true; +} + +static void seektable_calculate_length_(FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; +} + +static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points) +{ + FLAC__StreamMetadata_SeekPoint *object_array; + + FLAC__ASSERT(num_points > 0); + + object_array = (FLAC__StreamMetadata_SeekPoint*)safe_malloc_mul_2op_(num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)); + + if(0 != object_array) { + unsigned i; + for(i = 0; i < num_points; i++) { + object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object_array[i].stream_offset = 0; + object_array[i].frame_samples = 0; + } + } + + return object_array; +} + +static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object) +{ + unsigned i; + + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8; + object->length += object->data.vorbis_comment.vendor_string.length; + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8; + for(i = 0; i < object->data.vorbis_comment.num_comments; i++) { + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8); + object->length += object->data.vorbis_comment.comments[i].length; + } +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments) +{ + FLAC__ASSERT(num_comments > 0); + + return (FLAC__StreamMetadata_VorbisComment_Entry*)safe_calloc_(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); +} + +static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments) +{ + unsigned i; + + FLAC__ASSERT(0 != object_array && num_comments > 0); + + for(i = 0; i < num_comments; i++) + if(0 != object_array[i].entry) + free(object_array[i].entry); + + if(0 != object_array) + free(object_array); +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments) +{ + FLAC__StreamMetadata_VorbisComment_Entry *return_array; + + FLAC__ASSERT(0 != object_array); + FLAC__ASSERT(num_comments > 0); + + return_array = vorbiscomment_entry_array_new_(num_comments); + + if(0 != return_array) { + unsigned i; + + for(i = 0; i < num_comments; i++) { + if(!copy_vcentry_(return_array+i, object_array+i)) { + vorbiscomment_entry_array_delete_(return_array, num_comments); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy) +{ + FLAC__byte *save; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(0 != dest); + FLAC__ASSERT(0 != src); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0)); + + save = dest->entry; + + if(0 != src->entry && src->length > 0) { + if(copy) { + /* do the copy first so that if we fail we leave the dest object untouched */ + if(!copy_vcentry_(dest, src)) + return false; + } + else { + /* we have to make sure that the string we're taking over is null-terminated */ + + /* + * Stripping the const from src->entry is OK since we're taking + * ownership of the pointer. This is a hack around a deficiency + * in the API where the same function is used for 'copy' and + * 'own', but the source entry is a const pointer. If we were + * precise, the 'own' flavor would be a separate function with a + * non-const source pointer. But it's not, so we hack away. + */ + if(!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length)) + return false; + *dest = *src; + } + } + else { + /* the src is null */ + *dest = *src; + } + + if(0 != save) + free(save); + + vorbiscomment_calculate_length_(object); + return true; +} + +static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length) +{ + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(0 != field_name); + + for(i = offset; i < object->data.vorbis_comment.num_comments; i++) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) + return (int)i; + } + + return -1; +} + +static void cuesheet_calculate_length_(FLAC__StreamMetadata *object) +{ + unsigned i; + + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + object->length = ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8; + + object->length += object->data.cue_sheet.num_tracks * ( + FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN + ) / 8; + + for(i = 0; i < object->data.cue_sheet.num_tracks; i++) { + object->length += object->data.cue_sheet.tracks[i].num_indices * ( + FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN + ) / 8; + } +} + +static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices) +{ + FLAC__ASSERT(num_indices > 0); + + return (FLAC__StreamMetadata_CueSheet_Index*)safe_calloc_(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks) +{ + FLAC__ASSERT(num_tracks > 0); + + return (FLAC__StreamMetadata_CueSheet_Track*)safe_calloc_(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks) +{ + unsigned i; + + FLAC__ASSERT(0 != object_array && num_tracks > 0); + + for(i = 0; i < num_tracks; i++) { + if(0 != object_array[i].indices) { + FLAC__ASSERT(object_array[i].num_indices > 0); + free(object_array[i].indices); + } + } + + if(0 != object_array) + free(object_array); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks) +{ + FLAC__StreamMetadata_CueSheet_Track *return_array; + + FLAC__ASSERT(0 != object_array); + FLAC__ASSERT(num_tracks > 0); + + return_array = cuesheet_track_array_new_(num_tracks); + + if(0 != return_array) { + unsigned i; + + for(i = 0; i < num_tracks; i++) { + if(!copy_track_(return_array+i, object_array+i)) { + cuesheet_track_array_delete_(return_array, num_tracks); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet_Index *save; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(0 != dest); + FLAC__ASSERT(0 != src); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0)); + + save = dest->indices; + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_track_(dest, src)) + return false; + } + else { + *dest = *src; + } + + if(0 != save) + free(save); + + cuesheet_calculate_length_(object); + return true; +} + + +/**************************************************************************** + * + * Metadata object routines + * + ***************************************************************************/ + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type) +{ + FLAC__StreamMetadata *object; + + if(type > FLAC__MAX_METADATA_TYPE_CODE) + return 0; + + object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata)); + if(0 != object) { + object->is_last = false; + object->type = type; + switch(type) { + case FLAC__METADATA_TYPE_STREAMINFO: + object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + break; + case FLAC__METADATA_TYPE_PADDING: + /* calloc() took care of this for us: + object->length = 0; + */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + /* calloc() took care of this for us: + object->data.application.data = 0; + */ + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + /* calloc() took care of this for us: + object->length = 0; + object->data.seek_table.num_points = 0; + object->data.seek_table.points = 0; + */ + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING); + if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) { + free(object); + return 0; + } + vorbiscomment_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_CUESHEET: + cuesheet_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_PICTURE: + object->length = ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */ + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */ + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN + + 0 /* no data */ + ) / 8; + object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER; + object->data.picture.mime_type = 0; + object->data.picture.description = 0; + /* calloc() took care of this for us: + object->data.picture.width = 0; + object->data.picture.height = 0; + object->data.picture.depth = 0; + object->data.picture.colors = 0; + object->data.picture.data_length = 0; + object->data.picture.data = 0; + */ + /* now initialize mime_type and description with empty strings to make things easier on the client */ + if(!copy_cstring_(&object->data.picture.mime_type, "")) { + free(object); + return 0; + } + if(!copy_cstring_((char**)(&object->data.picture.description), "")) { + if(object->data.picture.mime_type) + free(object->data.picture.mime_type); + free(object); + return 0; + } + break; + default: + /* calloc() took care of this for us: + object->length = 0; + object->data.unknown.data = 0; + */ + break; + } + } + + return object; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object) +{ + FLAC__StreamMetadata *to; + + FLAC__ASSERT(0 != object); + + if(0 != (to = FLAC__metadata_object_new(object->type))) { + to->is_last = object->is_last; + to->type = object->type; + to->length = object->length; + switch(to->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo)); + break; + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(to->length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) { /* underflow check */ + FLAC__metadata_object_delete(to); + return 0; + } + memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8); + if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + to->data.seek_table.num_points = object->data.seek_table.num_points; +#if __WORDSIZE != 64 + if(to->data.seek_table.num_points > SIZE_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) { /* overflow check */ + FLAC__metadata_object_delete(to); + return 0; + } +#endif + if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != to->data.vorbis_comment.vendor_string.entry) { + free(to->data.vorbis_comment.vendor_string.entry); + to->data.vorbis_comment.vendor_string.entry = 0; + } + if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) { + FLAC__metadata_object_delete(to); + return 0; + } + if(object->data.vorbis_comment.num_comments == 0) { + FLAC__ASSERT(0 == object->data.vorbis_comment.comments); + to->data.vorbis_comment.comments = 0; + } + else { + FLAC__ASSERT(0 != object->data.vorbis_comment.comments); + to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + if(0 == to->data.vorbis_comment.comments) { + FLAC__metadata_object_delete(to); + return 0; + } + } + to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments; + break; + case FLAC__METADATA_TYPE_CUESHEET: + memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet)); + if(object->data.cue_sheet.num_tracks == 0) { + FLAC__ASSERT(0 == object->data.cue_sheet.tracks); + } + else { + FLAC__ASSERT(0 != object->data.cue_sheet.tracks); + to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + if(0 == to->data.cue_sheet.tracks) { + FLAC__metadata_object_delete(to); + return 0; + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + to->data.picture.type = object->data.picture.type; + if(!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) { + FLAC__metadata_object_delete(to); + return 0; + } + if(!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) { + FLAC__metadata_object_delete(to); + return 0; + } + to->data.picture.width = object->data.picture.width; + to->data.picture.height = object->data.picture.height; + to->data.picture.depth = object->data.picture.depth; + to->data.picture.colors = object->data.picture.colors; + to->data.picture.data_length = object->data.picture.data_length; + if(!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + default: + if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + } + } + + return to; +} + +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(0 != object); + + switch(object->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != object->data.application.data) { + free(object->data.application.data); + object->data.application.data = 0; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + if(0 != object->data.seek_table.points) { + free(object->data.seek_table.points); + object->data.seek_table.points = 0; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != object->data.vorbis_comment.vendor_string.entry) { + free(object->data.vorbis_comment.vendor_string.entry); + object->data.vorbis_comment.vendor_string.entry = 0; + } + if(0 != object->data.vorbis_comment.comments) { + FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0); + vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + } + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(0 != object->data.cue_sheet.tracks) { + FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0); + cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + } + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != object->data.picture.mime_type) { + free(object->data.picture.mime_type); + object->data.picture.mime_type = 0; + } + if(0 != object->data.picture.description) { + free(object->data.picture.description); + object->data.picture.description = 0; + } + if(0 != object->data.picture.data) { + free(object->data.picture.data); + object->data.picture.data = 0; + } + break; + default: + if(0 != object->data.unknown.data) { + free(object->data.unknown.data); + object->data.unknown.data = 0; + } + break; + } +} + +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object) +{ + FLAC__metadata_object_delete_data(object); + free(object); +} + +static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2) +{ + if(block1->min_blocksize != block2->min_blocksize) + return false; + if(block1->max_blocksize != block2->max_blocksize) + return false; + if(block1->min_framesize != block2->min_framesize) + return false; + if(block1->max_framesize != block2->max_framesize) + return false; + if(block1->sample_rate != block2->sample_rate) + return false; + if(block1->channels != block2->channels) + return false; + if(block1->bits_per_sample != block2->bits_per_sample) + return false; + if(block1->total_samples != block2->total_samples) + return false; + if(0 != memcmp(block1->md5sum, block2->md5sum, 16)) + return false; + return true; +} + +static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length) +{ + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + FLAC__ASSERT(block_length >= sizeof(block1->id)); + + if(0 != memcmp(block1->id, block2->id, sizeof(block1->id))) + return false; + if(0 != block1->data && 0 != block2->data) + return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id)); + else + return block1->data == block2->data; +} + +static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2) +{ + unsigned i; + + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + + if(block1->num_points != block2->num_points) + return false; + + if(0 != block1->points && 0 != block2->points) { + for(i = 0; i < block1->num_points; i++) { + if(block1->points[i].sample_number != block2->points[i].sample_number) + return false; + if(block1->points[i].stream_offset != block2->points[i].stream_offset) + return false; + if(block1->points[i].frame_samples != block2->points[i].frame_samples) + return false; + } + return true; + } + else + return block1->points == block2->points; +} + +static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2) +{ + unsigned i; + + if(block1->vendor_string.length != block2->vendor_string.length) + return false; + + if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) { + if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length)) + return false; + } + else if(block1->vendor_string.entry != block2->vendor_string.entry) + return false; + + if(block1->num_comments != block2->num_comments) + return false; + + for(i = 0; i < block1->num_comments; i++) { + if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) { + if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length)) + return false; + } + else if(block1->comments[i].entry != block2->comments[i].entry) + return false; + } + return true; +} + +static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2) +{ + unsigned i, j; + + if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number)) + return false; + + if(block1->lead_in != block2->lead_in) + return false; + + if(block1->is_cd != block2->is_cd) + return false; + + if(block1->num_tracks != block2->num_tracks) + return false; + + if(0 != block1->tracks && 0 != block2->tracks) { + FLAC__ASSERT(block1->num_tracks > 0); + for(i = 0; i < block1->num_tracks; i++) { + if(block1->tracks[i].offset != block2->tracks[i].offset) + return false; + if(block1->tracks[i].number != block2->tracks[i].number) + return false; + if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc))) + return false; + if(block1->tracks[i].type != block2->tracks[i].type) + return false; + if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis) + return false; + if(block1->tracks[i].num_indices != block2->tracks[i].num_indices) + return false; + if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) { + FLAC__ASSERT(block1->tracks[i].num_indices > 0); + for(j = 0; j < block1->tracks[i].num_indices; j++) { + if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset) + return false; + if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number) + return false; + } + } + else if(block1->tracks[i].indices != block2->tracks[i].indices) + return false; + } + } + else if(block1->tracks != block2->tracks) + return false; + return true; +} + +static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2) +{ + if(block1->type != block2->type) + return false; + if(block1->mime_type != block2->mime_type && (0 == block1->mime_type || 0 == block2->mime_type || strcmp(block1->mime_type, block2->mime_type))) + return false; + if(block1->description != block2->description && (0 == block1->description || 0 == block2->description || strcmp((const char *)block1->description, (const char *)block2->description))) + return false; + if(block1->width != block2->width) + return false; + if(block1->height != block2->height) + return false; + if(block1->depth != block2->depth) + return false; + if(block1->colors != block2->colors) + return false; + if(block1->data_length != block2->data_length) + return false; + if(block1->data != block2->data && (0 == block1->data || 0 == block2->data || memcmp(block1->data, block2->data, block1->data_length))) + return false; + return true; +} + +static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length) +{ + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + + if(0 != block1->data && 0 != block2->data) + return 0 == memcmp(block1->data, block2->data, block_length); + else + return block1->data == block2->data; +} + +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2) +{ + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + + if(block1->type != block2->type) { + return false; + } + if(block1->is_last != block2->is_last) { + return false; + } + if(block1->length != block2->length) { + return false; + } + switch(block1->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return true; /* we don't compare the padding guts */ + case FLAC__METADATA_TYPE_APPLICATION: + return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return compare_block_data_picture_(&block1->data.picture, &block2->data.picture); + default: + return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy) +{ + FLAC__byte *save; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION); + FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false)); + + save = object->data.application.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_bytes_(&object->data.application.data, data, length)) + return false; + } + else { + object->data.application.data = data; + } + + if(0 != save) + free(save); + + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if(0 == object->data.seek_table.points) { + FLAC__ASSERT(object->data.seek_table.num_points == 0); + if(0 == new_num_points) + return true; + else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points))) + return false; + } + else { + const size_t old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + const size_t new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + + /* overflow check */ +#if __WORDSIZE != 64 + if((size_t)new_num_points > SIZE_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) + return false; +#endif + + FLAC__ASSERT(object->data.seek_table.num_points > 0); + + if(new_size == 0) { + free(object->data.seek_table.points); + object->data.seek_table.points = 0; + } + else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size))) + return false; + + /* if growing, set new elements to placeholders */ + if(new_size > old_size) { + unsigned i; + for(i = object->data.seek_table.num_points; i < new_num_points; i++) { + object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object->data.seek_table.points[i].stream_offset = 0; + object->data.seek_table.points[i].frame_samples = 0; + } + } + } + + object->data.seek_table.num_points = new_num_points; + + seektable_calculate_length_(object); + return true; +} + +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num < object->data.seek_table.num_points); + + object->data.seek_table.points[point_num] = point; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point) +{ + int i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num <= object->data.seek_table.num_points); + + if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1)) + return false; + + /* move all points >= point_num forward one space */ + for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--) + object->data.seek_table.points[i] = object->data.seek_table.points[i-1]; + + FLAC__metadata_object_seektable_set_point(object, point_num, point); + seektable_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num) +{ + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num < object->data.seek_table.num_points); + + /* move all points > point_num backward one space */ + for(i = point_num; i < object->data.seek_table.num_points-1; i++) + object->data.seek_table.points[i] = object->data.seek_table.points[i+1]; + + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + return FLAC__format_seektable_is_legal(&object->data.seek_table); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if(num > 0) + /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */ + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num); + else + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number) +{ + FLAC__StreamMetadata_SeekTable *seek_table; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + seek_table = &object->data.seek_table; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1)) + return false; + + seek_table->points[seek_table->num_points - 1].sample_number = sample_number; + seek_table->points[seek_table->num_points - 1].stream_offset = 0; + seek_table->points[seek_table->num_points - 1].frame_samples = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(0 != sample_numbers || num == 0); + + if(num > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for(j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = sample_numbers[j]; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(total_samples > 0); + + if(num > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for(j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(samples > 0); + FLAC__ASSERT(total_samples > 0); + + if(samples > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + FLAC__uint64 num, sample; + + num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */ + /* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */ + if(total_samples % samples == 0) + num--; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (unsigned)num)) + return false; + + sample = 0; + for(j = 0; j < num; i++, j++, sample += samples) { + seek_table->points[i].sample_number = sample; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact) +{ + unsigned unique; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + unique = FLAC__format_seektable_sort(&object->data.seek_table); + + return !compact || FLAC__metadata_object_seektable_resize_points(object, unique); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + if(!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if(0 == object->data.vorbis_comment.comments) { + FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0); + if(0 == new_num_comments) + return true; + else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments))) + return false; + } + else { + const size_t old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + const size_t new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + + /* overflow check */ +#if __WORDSIZE != 64 + if((size_t)new_num_comments > SIZE_MAX / sizeof(FLAC__StreamMetadata_VorbisComment_Entry)) + return false; +#endif + + FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0); + + /* if shrinking, free the truncated entries */ + if(new_num_comments < object->data.vorbis_comment.num_comments) { + unsigned i; + for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++) + if(0 != object->data.vorbis_comment.comments[i].entry) + free(object->data.vorbis_comment.comments[i].entry); + } + + if(new_size == 0) { + free(object->data.vorbis_comment.comments); + object->data.vorbis_comment.comments = 0; + } + else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size))) + return false; + + /* if growing, zero all the length/pointers of new elements */ + if(new_size > old_size) + memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size); + } + + object->data.vorbis_comment.num_comments = new_num_comments; + + vorbiscomment_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + vc = &object->data.vorbis_comment; + + if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1)) + return false; + + /* move all comments >= comment_num forward one space */ + memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num)); + vc->comments[comment_num].length = 0; + vc->comments[comment_num].entry = 0; + + return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + int i; + size_t field_name_length; + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + + FLAC__ASSERT(0 != eq); + + if(0 == eq) + return false; /* double protection */ + + field_name_length = (unsigned int)(eq-entry.entry); + + if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) { + unsigned index = (unsigned)i; + if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy)) + return false; + if(all && (index+1 < object->data.vorbis_comment.num_comments)) { + for(i = vorbiscomment_find_entry_from_(object, index+1, (const char *)entry.entry, field_name_length); i >= 0; ) { + if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i)) + return false; + if((unsigned)i < object->data.vorbis_comment.num_comments) + i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length); + else + i = -1; + } + } + return true; + } + else + return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments); + + vc = &object->data.vorbis_comment; + + /* free the comment at comment_num */ + if(0 != vc->comments[comment_num].entry) + free(vc->comments[comment_num].entry); + + /* move all comments > comment_num backward one space */ + memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1)); + vc->comments[vc->num_comments-1].length = 0; + vc->comments[vc->num_comments-1].entry = 0; + + return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value) +{ + FLAC__ASSERT(0 != entry); + FLAC__ASSERT(0 != field_name); + FLAC__ASSERT(0 != field_value); + + if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name)) + return false; + if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1))) + return false; + + { + const size_t nn = strlen(field_name); + const size_t nv = strlen(field_value); + entry->length = (unsigned int)nn + 1 /*=*/ + (unsigned int)nv; + if(0 == (entry->entry = (FLAC__byte*)safe_malloc_add_4op_(nn, /*+*/1, /*+*/nv, /*+*/1))) + return false; + memcpy(entry->entry, field_name, nn); + entry->entry[nn] = '='; + memcpy(entry->entry+nn+1, field_value, nv); + entry->entry[entry->length] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + FLAC__ASSERT(0 != field_name); + FLAC__ASSERT(0 != field_value); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + const size_t nn = eq-entry.entry; + const size_t nv = entry.length-nn-1; /* -1 for the '=' */ + FLAC__ASSERT(0 != eq); + if(0 == eq) + return false; /* double protection */ + if(0 == (*field_name = (char*)safe_malloc_add_2op_(nn, /*+*/1))) + return false; + if(0 == (*field_value = (char*)safe_malloc_add_2op_(nv, /*+*/1))) { + free(*field_name); + return false; + } + memcpy(*field_name, entry.entry, nn); + memcpy(*field_value, entry.entry+nn+1, nv); + (*field_name)[nn] = '\0'; + (*field_value)[nv] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__ +#define FLAC__STRNCASECMP strnicmp +#else +#define FLAC__STRNCASECMP strncasecmp +#endif + return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length)); +#undef FLAC__STRNCASECMP + } +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name) +{ + FLAC__ASSERT(0 != field_name); + + return vorbiscomment_find_entry_from_(object, offset, field_name, (unsigned int)strlen(field_name)); +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + const unsigned field_name_length = (unsigned int)strlen(field_name); + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + for(i = 0; i < object->data.vorbis_comment.num_comments; i++) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i)) + return -1; + else + return 1; + } + } + + return 0; +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + FLAC__bool ok = true; + unsigned matching = 0; + const unsigned field_name_length = (unsigned int)strlen(field_name); + int i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + /* must delete from end to start otherwise it will interfere with our iteration */ + for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + matching++; + ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i); + } + } + + return ok? (int)matching : -1; +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void) +{ + return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__StreamMetadata_CueSheet_Track *to; + + FLAC__ASSERT(0 != object); + + if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) { + if(!copy_track_(to, object)) { + FLAC__metadata_object_cuesheet_track_delete(to); + return 0; + } + } + + return to; +} + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__ASSERT(0 != object); + + if(0 != object->indices) { + FLAC__ASSERT(object->num_indices > 0); + free(object->indices); + } +} + +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__metadata_object_cuesheet_track_delete_data(object); + free(object); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + track = &object->data.cue_sheet.tracks[track_num]; + + if(0 == track->indices) { + FLAC__ASSERT(track->num_indices == 0); + if(0 == new_num_indices) + return true; + else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices))) + return false; + } + else { + const size_t old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + const size_t new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + + /* overflow check */ +#if __WORDSIZE != 64 + if((size_t)new_num_indices > SIZE_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Index)) + return false; +#endif + + FLAC__ASSERT(track->num_indices > 0); + + if(new_size == 0) { + free(track->indices); + track->indices = 0; + } + else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size))) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if(new_size > old_size) + memset(track->indices + track->num_indices, 0, new_size - old_size); + } + + track->num_indices = new_num_indices; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices); + + track = &object->data.cue_sheet.tracks[track_num]; + + if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1)) + return false; + + /* move all indices >= index_num forward one space */ + memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num)); + + track->indices[index_num] = index; + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num) +{ + FLAC__StreamMetadata_CueSheet_Index index; + memset(&index, 0, sizeof(index)); + return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices); + + track = &object->data.cue_sheet.tracks[track_num]; + + /* move all indices > index_num backward one space */ + memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1)); + + FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1); + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + if(0 == object->data.cue_sheet.tracks) { + FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0); + if(0 == new_num_tracks) + return true; + else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks))) + return false; + } + else { + const size_t old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + const size_t new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + + /* overflow check */ +#if __WORDSIZE != 64 + if((size_t)new_num_tracks > SIZE_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Track)) + return false; +#endif + + FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0); + + /* if shrinking, free the truncated entries */ + if(new_num_tracks < object->data.cue_sheet.num_tracks) { + unsigned i; + for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++) + if(0 != object->data.cue_sheet.tracks[i].indices) + free(object->data.cue_sheet.tracks[i].indices); + } + + if(new_size == 0) { + free(object->data.cue_sheet.tracks); + object->data.cue_sheet.tracks = 0; + } + else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size))) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if(new_size > old_size) + memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size); + } + + object->data.cue_sheet.num_tracks = new_num_tracks; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks); + + cs = &object->data.cue_sheet; + + if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1)) + return false; + + /* move all tracks >= track_num forward one space */ + memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num)); + cs->tracks[track_num].num_indices = 0; + cs->tracks[track_num].indices = 0; + + return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num) +{ + FLAC__StreamMetadata_CueSheet_Track track; + memset(&track, 0, sizeof(track)); + return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num) +{ + FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + cs = &object->data.cue_sheet; + + /* free the track at track_num */ + if(0 != cs->tracks[track_num].indices) + free(cs->tracks[track_num].indices); + + /* move all tracks > track_num backward one space */ + memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1)); + cs->tracks[cs->num_tracks-1].num_indices = 0; + cs->tracks[cs->num_tracks-1].indices = 0; + + return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation); +} + +static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track) +{ + if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1) + return 0; + else if (cs->tracks[track].indices[0].number == 1) + return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in; + else if (cs->tracks[track].num_indices < 2) + return 0; + else if (cs->tracks[track].indices[1].number == 1) + return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in; + else + return 0; +} + +static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x) +{ + FLAC__uint32 n = 0; + while (x) { + n += (x%10); + x /= 10; + } + return n; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object) +{ + const FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + cs = &object->data.cue_sheet; + + if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */ + return 0; + + { + FLAC__uint32 i, length, sum = 0; + for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */ + sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100)); + length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100); + + return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy) +{ + char *old; + size_t old_length, new_length; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT(0 != mime_type); + + old = object->data.picture.mime_type; + old_length = old? strlen(old) : 0; + new_length = strlen(mime_type); + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(new_length >= SIZE_MAX) /* overflow check */ + return false; + if(!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, (unsigned int)new_length+1)) + return false; + } + else { + object->data.picture.mime_type = mime_type; + } + + if(0 != old) + free(old); + + object->length -= (unsigned int)old_length; + object->length += (unsigned int)new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy) +{ + FLAC__byte *old; + size_t old_length, new_length; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT(0 != description); + + old = object->data.picture.description; + old_length = old? strlen((const char *)old) : 0; + new_length = strlen((const char *)description); + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(new_length >= SIZE_MAX) /* overflow check */ + return false; + if(!copy_bytes_(&object->data.picture.description, description, (unsigned int)new_length+1)) + return false; + } + else { + object->data.picture.description = description; + } + + if(0 != old) + free(old); + + object->length -= (unsigned int)old_length; + object->length += (unsigned int)new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy) +{ + FLAC__byte *old; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false)); + + old = object->data.picture.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_bytes_(&object->data.picture.data, data, length)) + return false; + } + else { + object->data.picture.data = data; + } + + if(0 != old) + free(old); + + object->length -= object->data.picture.data_length; + object->data.picture.data_length = length; + object->length += length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + + return FLAC__format_picture_is_legal(&object->data.picture, violation); +} diff --git a/lib/flac-1.2.1/src/libFLAC/stream_decoder.c b/lib/flac-1.2.1/src/libFLAC/stream_decoder.c new file mode 100755 index 0000000..09767d2 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/stream_decoder.c @@ -0,0 +1,3407 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for _setmode() */ +#include /* for _O_BINARY */ +#endif +#if defined __CYGWIN__ || defined __EMX__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include +#include /* for malloc() */ +#include /* for FMOD_memset/FMOD_memcpy() */ +#include /* for stat() */ +#include /* for off_t */ +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#endif +#include "FLAC/assert.h" +#include "share/alloc.h" +#include "protected/stream_decoder.h" +#include "private/bitreader.h" +#include "private/bitmath.h" +#include "private/cpu.h" +#include "private/crc.h" +#include "private/fixed.h" +#include "private/format.h" +#include "private/lpc.h" +#include "private/md5.h" +#include "private/memory.h" + +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + + +/* technically this should be in an "export.c" but this is convenient enough */ +FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = +#if FLAC__HAS_OGG + 1 +#else + 0 +#endif +; + + +/*********************************************************************** + * + * Private static data + * + ***********************************************************************/ + +static FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_(FLAC__StreamDecoder *decoder); +static FILE *get_binary_stdin_(void); +static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); +static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); +static FLAC__bool find_metadata_(void *context, FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_(void *context, FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_streaminfo_(void *context, FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_seektable_(void *context, FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_vorbiscomment_(void *context, FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj); +static FLAC__bool read_metadata_cuesheet_(void *context, FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); +static FLAC__bool read_metadata_picture_(void *context, FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); +static FLAC__bool skip_id3v2_tag_(void *context, FLAC__StreamDecoder *decoder); +static FLAC__bool frame_sync_(void *context, FLAC__StreamDecoder *decoder); +static FLAC__bool read_frame_(void *context, FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode); +static FLAC__bool read_frame_header_(void *context, FLAC__StreamDecoder *decoder); +static FLAC__bool read_subframe_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_constant_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_fixed_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_lpc_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_verbatim_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_residual_partitioned_rice_(void *context, FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended); +static FLAC__bool read_zero_padding_(void *context, FLAC__StreamDecoder *decoder); +static FLAC__bool read_callback_(void *context, FLAC__byte buffer[], size_t *bytes, void *client_data); +#if FLAC__HAS_OGG +static FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(void *context, const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes); +static FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +#endif +static FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +static void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status); +static FLAC__bool seek_to_absolute_sample_(void *context, FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#if FLAC__HAS_OGG +static FLAC__bool seek_to_absolute_sample_ogg_(void *context, FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#endif +static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamDecoderPrivate { +#if FLAC__HAS_OGG + FLAC__bool is_ogg; +#endif + FLAC__StreamDecoderReadCallback read_callback; + FLAC__StreamDecoderSeekCallback seek_callback; + FLAC__StreamDecoderTellCallback tell_callback; + FLAC__StreamDecoderLengthCallback length_callback; + FLAC__StreamDecoderEofCallback eof_callback; + FLAC__StreamDecoderWriteCallback write_callback; + FLAC__StreamDecoderMetadataCallback metadata_callback; + FLAC__StreamDecoderErrorCallback error_callback; + /* generic 32-bit datapath: */ + void (*local_lpc_restore_signal)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* generic 64-bit datapath: */ + void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ + void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit), AND order <= 8: */ + void (*local_lpc_restore_signal_16bit_order8)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + FLAC__bool (*local_bitreader_read_rice_signed_block)(void *context, FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); + void *client_data; + FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ + FLAC__BitReader *input; + FLAC__int32 *output[FLAC__MAX_CHANNELS]; + FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS]; + unsigned output_capacity, output_channels; + FLAC__uint32 fixed_block_size, next_fixed_block_size; + FLAC__uint64 samples_decoded; + FLAC__bool has_stream_info, has_seek_table; + FLAC__StreamMetadata stream_info; + FLAC__StreamMetadata seek_table; + FLAC__bool metadata_filter[128]; /* MAGIC number 128 == total number of metadata block types == 1 << 7 */ + FLAC__byte *metadata_filter_ids; + size_t metadata_filter_ids_count, metadata_filter_ids_capacity; /* units for both are IDs, not bytes */ + FLAC__Frame frame; + FLAC__bool cached; /* true if there is a byte in lookahead */ + FLAC__CPUInfo cpuinfo; + FLAC__byte header_warmup[2]; /* contains the sync code and reserved bits */ + FLAC__byte lookahead; /* temp storage when we need to look ahead one byte in the stream */ + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *residual_unaligned[FLAC__MAX_CHANNELS]; + FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */ + FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */ + FLAC__bool is_seeking; + FLAC__MD5Context md5context; + FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ + /* (the rest of these are only used for seeking) */ + FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */ + FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */ + FLAC__uint64 target_sample; + unsigned unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ +#if FLAC__HAS_OGG + FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */ +#endif +} FLAC__StreamDecoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamDecoderStateString[] = { + "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA", + "FLAC__STREAM_DECODER_READ_METADATA", + "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC", + "FLAC__STREAM_DECODER_READ_FRAME", + "FLAC__STREAM_DECODER_END_OF_STREAM", + "FLAC__STREAM_DECODER_OGG_ERROR", + "FLAC__STREAM_DECODER_SEEK_ERROR", + "FLAC__STREAM_DECODER_ABORTED", + "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_UNINITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderInitStatusString[] = { + "FLAC__STREAM_DECODER_INIT_STATUS_OK", + "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE", + "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderReadStatusString[] = { + "FLAC__STREAM_DECODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_DECODER_READ_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[] = { + "FLAC__STREAM_DECODER_SEEK_STATUS_OK", + "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderTellStatusString[] = { + "FLAC__STREAM_DECODER_TELL_STATUS_OK", + "FLAC__STREAM_DECODER_TELL_STATUS_ERROR", + "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[] = { + "FLAC__STREAM_DECODER_LENGTH_STATUS_OK", + "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR", + "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[] = { + "FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { + "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", + "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", + "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", + "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" +}; + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) +{ + FLAC__StreamDecoder *decoder; + unsigned i; + + FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ + + decoder = (FLAC__StreamDecoder*)calloc(1, sizeof(FLAC__StreamDecoder)); + if(decoder == 0) { + return 0; + } + + decoder->protected_ = (FLAC__StreamDecoderProtected*)calloc(1, sizeof(FLAC__StreamDecoderProtected)); + if(decoder->protected_ == 0) { + free(decoder); + return 0; + } + + decoder->private_ = (FLAC__StreamDecoderPrivate*)calloc(1, sizeof(FLAC__StreamDecoderPrivate)); + if(decoder->private_ == 0) { + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->input = FLAC__bitreader_new(); + if(decoder->private_->input == 0) { + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->metadata_filter_ids_capacity = 16; + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { + FLAC__bitreader_delete(decoder->private_->input); + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + decoder->private_->output[i] = 0; + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + decoder->private_->has_seek_table = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]); + + decoder->private_->file = 0; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return decoder; +} + +FLAC_API void FLAC__stream_decoder_delete(void *context, FLAC__StreamDecoder *decoder) +{ + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->private_->input); + + (void)FLAC__stream_decoder_finish(context, decoder); + + if(0 != decoder->private_->metadata_filter_ids) + free(decoder->private_->metadata_filter_ids); + + FLAC__bitreader_delete(decoder->private_->input); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&decoder->private_->partitioned_rice_contents[i]); + + free(decoder->private_); + free(decoder->protected_); + free(decoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamDecoderInitStatus init_stream_internal_( + void *context, + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + +#if !FLAC__HAS_OGG + if(is_ogg) + return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER; +#endif + + if( + 0 == read_callback || + 0 == write_callback || + 0 == error_callback || + (seek_callback && (0 == tell_callback || 0 == length_callback || 0 == eof_callback)) + ) + return FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + +#if FLAC__HAS_OGG + decoder->private_->is_ogg = is_ogg; + if(is_ogg && !FLAC__ogg_decoder_aspect_init(context, &decoder->protected_->ogg_decoder_aspect)) + return decoder->protected_->state = FLAC__STREAM_DECODER_OGG_ERROR; +#endif + + /* + * get the CPU info and set the function pointers + */ + FLAC__cpu_info(&decoder->private_->cpuinfo); + /* first default to the non-asm routines */ + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal; + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block; + /* now override with asm where appropriate */ +#ifndef FLAC__NO_ASM + if(decoder->private_->cpuinfo.use_asm) { +#ifdef FLAC__CPU_IA32 + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); +#ifdef FLAC__HAS_NASM +#if 1 /*@@@@@@ OPT: not clearly faster, needs more testing */ + if(decoder->private_->cpuinfo.data.ia32.bswap) + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap; +#endif + if(decoder->private_->cpuinfo.data.ia32.mmx) { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32_mmx; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32_mmx; + } + else { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32; + } +#endif +#elif defined FLAC__CPU_PPC + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_PPC); + if(decoder->private_->cpuinfo.data.ppc.altivec) { + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ppc_altivec_16; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8; + } +#endif + } +#endif + + /* from here on, errors are fatal */ + + if(!FLAC__bitreader_init(context, decoder->private_->input, decoder->private_->cpuinfo, read_callback_, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + decoder->private_->read_callback = read_callback; + decoder->private_->seek_callback = seek_callback; + decoder->private_->tell_callback = tell_callback; + decoder->private_->length_callback = length_callback; + decoder->private_->eof_callback = eof_callback; + decoder->private_->write_callback = write_callback; + decoder->private_->metadata_callback = metadata_callback; + decoder->private_->error_callback = error_callback; + decoder->private_->client_data = client_data; + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + decoder->private_->samples_decoded = 0; + decoder->private_->has_stream_info = false; + decoder->private_->cached = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + decoder->private_->is_seeking = false; + + decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */ + if(!FLAC__stream_decoder_reset(decoder)) { + /* above call sets the state for us */ + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + return FLAC__STREAM_DECODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + void *context, + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + context, + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + void *context, + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + context, + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamDecoderInitStatus init_FILE_internal_( + void *context, + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != file); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdin) + file = get_binary_stdin_(); /* just to be safe */ + + decoder->private_->file = file; + + return init_stream_internal_( + context, + decoder, + file_read_callback_, + decoder->private_->file == stdin? 0: file_seek_callback_, + decoder->private_->file == stdin? 0: file_tell_callback_, + decoder->private_->file == stdin? 0: file_length_callback_, + file_eof_callback_, + write_callback, + metadata_callback, + error_callback, + client_data, + is_ogg + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + void *context, + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(context, decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + void *context, + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(context, decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamDecoderInitStatus init_file_internal_( + void *context, + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + FLAC__ASSERT(0 != decoder); + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned. + */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + file = filename? fopen(filename, "rb") : stdin; + + if(0 == file) + return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; + + return init_FILE_internal_(context, decoder, file, write_callback, metadata_callback, error_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + void *context, + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(context, decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + void *context, + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(context, decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_decoder_finish(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__bool md5_failed = false; + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return true; + + /* see the comment in FLAC__seekable_stream_decoder_reset() as to why we + * always call FLAC__MD5Final() + */ + FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); + + if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + } + FLAC__bitreader_free(decoder->private_->input); + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_finish(context, &decoder->protected_->ogg_decoder_aspect); +#endif + + if(0 != decoder->private_->file) { + if(decoder->private_->file != stdin) + fclose(decoder->private_->file); + decoder->private_->file = 0; + } + + if(decoder->private_->do_md5_checking) { + if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16)) + md5_failed = true; + } + decoder->private_->is_seeking = false; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return !md5_failed; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + /* can't check decoder->private_->is_ogg since that's not set until init time */ + FLAC__ogg_decoder_aspect_set_serial_number(&decoder->protected_->ogg_decoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->protected_->md5_checking = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = true; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + FMOD_memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder) +{ + unsigned i; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++) + decoder->private_->metadata_filter[i] = true; + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = false; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + FMOD_memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + FMOD_memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->state; +} + +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder) +{ + return FLAC__StreamDecoderStateString[decoder->protected_->state]; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->md5_checking; +} + +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0; +} + +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channels; +} + +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channel_assignment; +} + +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->bits_per_sample; +} + +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->sample_rate; +} + +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(void *context, const FLAC__StreamDecoder *decoder, FLAC__uint64 *position) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != position); + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + return false; +#endif + if(0 == decoder->private_->tell_callback) + return false; + if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) + return false; + /* should never happen since all FLAC frames and metadata blocks are byte aligned, but check just in case */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) + return false; + FLAC__ASSERT(*position >= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder)); + *position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder); + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + decoder->private_->samples_decoded = 0; + decoder->private_->do_md5_checking = false; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_flush(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(!FLAC__bitreader_clear(decoder->private_->input)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + +#if FLAC__HAS_OGG + /*@@@ could go in !internal_reset_hack block below */ + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_reset(&decoder->protected_->ogg_decoder_aspect); +#endif + + /* Rewind if necessary. If FLAC__stream_decoder_init() is calling us, + * (internal_reset_hack) don't try to rewind since we are already at + * the beginning of the stream and don't want to fail if the input is + * not seekable. + */ + if(!decoder->private_->internal_reset_hack) { + if(decoder->private_->file == stdin) + return false; /* can't rewind stdin, reset fails */ + if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) + return false; /* seekable and seek fails, reset fails */ + } + else + decoder->private_->internal_reset_hack = false; + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + + decoder->private_->has_stream_info = false; + if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + } + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + /* + * This goes in reset() and not flush() because according to the spec, a + * fixed-blocksize stream must stay that way through the whole stream. + */ + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + + /* We initialize the FLAC__MD5Context even though we may never use it. This + * is because md5 checking may be turned on to start and then turned off if + * a seek occurs. So we init the context here and finalize it in + * FLAC__stream_decoder_finish() to make sure things are always cleaned up + * properly. + */ + FLAC__MD5Init(&decoder->private_->md5context); + + decoder->private_->first_frame_offset = 0; + decoder->private_->unparseable_frame_count = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(context, decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(context, decoder)) + return false; /* above function sets the status for us */ + else + return true; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(context, decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(context, decoder, &got_a_frame, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(context, decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(context, decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__bool dummy; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(context, decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(context, decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(context, decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(context, decoder, &dummy, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + return false; /* above function sets the status for us */ + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(context, decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(context, decoder, &got_a_frame, /*do_full_decode=*/false)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(void *context, FLAC__StreamDecoder *decoder, FLAC__uint64 sample) +{ + FLAC__uint64 length; + + FLAC__ASSERT(0 != decoder); + + if( + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME && + decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM + ) + return false; + + if(0 == decoder->private_->seek_callback) + return false; + + FLAC__ASSERT(decoder->private_->seek_callback); + FLAC__ASSERT(decoder->private_->tell_callback); + FLAC__ASSERT(decoder->private_->length_callback); + FLAC__ASSERT(decoder->private_->eof_callback); + + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) + return false; + + decoder->private_->is_seeking = true; + + /* turn off md5 checking if a seek is attempted */ + decoder->private_->do_md5_checking = false; + + /* get the file length (currently our algorithm needs to know the length so it's also an error to get FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) */ + if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) { + decoder->private_->is_seeking = false; + return false; + } + + /* if we haven't finished processing the metadata yet, do that so we have the STREAMINFO, SEEK_TABLE, and first_frame_offset */ + if( + decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA || + decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA + ) { + if(!FLAC__stream_decoder_process_until_end_of_metadata(context, decoder)) { + /* above call sets the state for us */ + decoder->private_->is_seeking = false; + return false; + } + /* check this again in case we didn't know total_samples the first time */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->private_->is_seeking = false; + return false; + } + } + + { + const FLAC__bool ok = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + seek_to_absolute_sample_ogg_(context, decoder, length, sample) : +#endif + seek_to_absolute_sample_(context, decoder, length, sample) + ; + decoder->private_->is_seeking = false; + return ok; + } +} + +/*********************************************************************** + * + * Protected class methods + * + ***********************************************************************/ + +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + FLAC__ASSERT(!(FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) & 7)); + return FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) / 8; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_(FLAC__StreamDecoder *decoder) +{ +#if FLAC__HAS_OGG + decoder->private_->is_ogg = false; +#endif + decoder->private_->read_callback = 0; + decoder->private_->seek_callback = 0; + decoder->private_->tell_callback = 0; + decoder->private_->length_callback = 0; + decoder->private_->eof_callback = 0; + decoder->private_->write_callback = 0; + decoder->private_->metadata_callback = 0; + decoder->private_->error_callback = 0; + decoder->private_->client_data = 0; + + FMOD_memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] = true; + decoder->private_->metadata_filter_ids_count = 0; + + decoder->protected_->md5_checking = false; + +#if FLAC__HAS_OGG + FLAC__ogg_decoder_aspect_set_defaults(&decoder->protected_->ogg_decoder_aspect); +#endif +} + +/* + * This will forcibly set stdin to binary mode (for OSes that require it) + */ +FILE *get_binary_stdin_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdin), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdin), O_BINARY); +#endif + + return stdin; +} + +FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) +{ + unsigned i; + FLAC__int32 *tmp; + + if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels) + return true; + + /* simply using realloc() is not practical because the number of channels may change mid-stream */ + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + + for(i = 0; i < channels; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + tmp = (FLAC__int32*)safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/); + if(tmp == 0) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + FMOD_memset(tmp, 0, sizeof(FLAC__int32)*4); + decoder->private_->output[i] = tmp + 4; + + /* WATCHOUT: + * minimum of quadword alignment for PPC vector optimizations is REQUIRED: + */ + if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + + decoder->private_->output_capacity = size; + decoder->private_->output_channels = channels; + + return true; +} + +FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) +{ + size_t i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + + for(i = 0; i < decoder->private_->metadata_filter_ids_count; i++) + if(0 == memcmp(decoder->private_->metadata_filter_ids + i * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))) + return true; + + return false; +} + +FLAC__bool find_metadata_(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, id; + FLAC__bool first = true; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + for(i = id = 0; i < 4; ) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == FLAC__STREAM_SYNC_STRING[i]) { + first = true; + i++; + id = 0; + continue; + } + if(x == ID3V2_TAG_[id]) { + id++; + i = 0; + if(id == 3) { + if(!skip_id3v2_tag_(context, decoder)) + return false; /* skip_id3v2_tag_ sets the state for us */ + } + continue; + } + id = 0; + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + i = 0; + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA; + return true; +} + +FLAC__bool read_metadata_(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__bool is_last; + FLAC__uint32 i, x, type, length; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; /* read_callback_ sets the state for us */ + is_last = x? true : false; + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &length, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(type == FLAC__METADATA_TYPE_STREAMINFO) { + if(!read_metadata_streaminfo_(context, decoder, is_last, length)) + return false; + + decoder->private_->has_stream_info = true; + if(0 == memcmp(decoder->private_->stream_info.data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + decoder->private_->do_md5_checking = false; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data); + } + else if(type == FLAC__METADATA_TYPE_SEEKTABLE) { + if(!read_metadata_seektable_(context, decoder, is_last, length)) + return false; + + decoder->private_->has_seek_table = true; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + } + else { + FLAC__bool skip_it = !decoder->private_->metadata_filter[type]; + unsigned real_length = length; + FLAC__StreamMetadata block; + + block.is_last = is_last; + block.type = (FLAC__MetadataType)type; + block.length = length; + + if(type == FLAC__METADATA_TYPE_APPLICATION) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, block.data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(real_length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) { /* underflow check */ + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;/*@@@@@@ maybe wrong error? need to resync?*/ + return false; + } + + real_length -= FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8; + + if(decoder->private_->metadata_filter_ids_count > 0 && has_id_filtered_(decoder, block.data.application.id)) + skip_it = !skip_it; + } + + if(skip_it) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(context, decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else { + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + /* skip the padding bytes */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(context, decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + /* remember, we read the ID already */ + if(real_length > 0) { + if(0 == (block.data.application.data = (FLAC__byte*)malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, block.data.application.data, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else + block.data.application.data = 0; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!read_metadata_vorbiscomment_(context, decoder, &block.data.vorbis_comment)) + return false; + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(!read_metadata_cuesheet_(context, decoder, &block.data.cue_sheet)) + return false; + break; + case FLAC__METADATA_TYPE_PICTURE: + if(!read_metadata_picture_(context, decoder, &block.data.picture)) + return false; + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + break; + default: + if(real_length > 0) { + if(0 == (block.data.unknown.data = (FLAC__byte*)malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, block.data.unknown.data, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else + block.data.unknown.data = 0; + break; + } + if(!decoder->private_->is_seeking && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); + + /* now we have to free any malloc()ed data in the block */ + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != block.data.application.data) + free(block.data.application.data); + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != block.data.vorbis_comment.vendor_string.entry) + free(block.data.vorbis_comment.vendor_string.entry); + if(block.data.vorbis_comment.num_comments > 0) + for(i = 0; i < block.data.vorbis_comment.num_comments; i++) + if(0 != block.data.vorbis_comment.comments[i].entry) + free(block.data.vorbis_comment.comments[i].entry); + if(0 != block.data.vorbis_comment.comments) + free(block.data.vorbis_comment.comments); + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(block.data.cue_sheet.num_tracks > 0) + for(i = 0; i < block.data.cue_sheet.num_tracks; i++) + if(0 != block.data.cue_sheet.tracks[i].indices) + free(block.data.cue_sheet.tracks[i].indices); + if(0 != block.data.cue_sheet.tracks) + free(block.data.cue_sheet.tracks); + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != block.data.picture.mime_type) + free(block.data.picture.mime_type); + if(0 != block.data.picture.description) + free(block.data.picture.description); + if(0 != block.data.picture.data) + free(block.data.picture.data); + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + default: + if(0 != block.data.unknown.data) + free(block.data.unknown.data); + break; + } + } + } + + if(is_last) { + /* if this fails, it's OK, it's just a hint for the seek routine */ + if(!FLAC__stream_decoder_get_decode_position(context, decoder, &decoder->private_->first_frame_offset)) + decoder->private_->first_frame_offset = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + + return true; +} + +FLAC__bool read_metadata_streaminfo_(void *context, FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 x; + unsigned bits, used_bits = 0; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->stream_info.type = FLAC__METADATA_TYPE_STREAMINFO; + decoder->private_->stream_info.is_last = is_last; + decoder->private_->stream_info.length = length; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, bits)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.sample_rate = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.channels = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; + if(!FLAC__bitreader_read_raw_uint64(context, decoder->private_->input, &decoder->private_->stream_info.data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + used_bits += bits; + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, decoder->private_->stream_info.data.stream_info.md5sum, 16)) + return false; /* read_callback_ sets the state for us */ + used_bits += 16*8; + + /* skip the rest of the block */ + FLAC__ASSERT(used_bits % 8 == 0); + length -= (used_bits / 8); + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(context, decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + + return true; +} + +FLAC__bool read_metadata_seektable_(void *context, FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 i, x; + FLAC__uint64 xx; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->seek_table.type = FLAC__METADATA_TYPE_SEEKTABLE; + decoder->private_->seek_table.is_last = is_last; + decoder->private_->seek_table.length = length; + + decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + /* use realloc since we may pass through here several times (e.g. after seeking) */ + if(0 == (decoder->private_->seek_table.data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)safe_realloc_mul_2op_(decoder->private_->seek_table.data.seek_table.points, decoder->private_->seek_table.data.seek_table.num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < decoder->private_->seek_table.data.seek_table.num_points; i++) { + if(!FLAC__bitreader_read_raw_uint64(context, decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].sample_number = xx; + + if(!FLAC__bitreader_read_raw_uint64(context, decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].stream_offset = xx; + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x; + } + length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH); + /* if there is a partial point left, skip over it */ + if(length > 0) { + /*@@@ do a send_error_to_client_() here? there's an argument for either way */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(context, decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_vorbiscomment_(void *context, FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj) +{ + FLAC__uint32 i; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read vendor string */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(context, decoder->private_->input, &obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + if(obj->vendor_string.length > 0) { + if(0 == (obj->vendor_string.entry = (FLAC__byte*)safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + } + else + obj->vendor_string.entry = 0; + + /* read num comments */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(context, decoder->private_->input, &obj->num_comments)) + return false; /* read_callback_ sets the state for us */ + + /* read comments */ + if(obj->num_comments > 0) { + if(0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*)safe_malloc_mul_2op_(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_comments; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(context, decoder->private_->input, &obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + if(obj->comments[i].length > 0) { + if(0 == (obj->comments[i].entry = (FLAC__byte*)safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + else + obj->comments[i].entry = 0; + } + } + else { + obj->comments = 0; + } + + return true; +} + +FLAC__bool read_metadata_cuesheet_(void *context, FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj) +{ + FLAC__uint32 i, j, x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + FMOD_memset(obj, 0, sizeof(FLAC__StreamMetadata_CueSheet)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, (FLAC__byte*)obj->media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint64(context, decoder->private_->input, &obj->lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->is_cd = x? true : false; + + if(!FLAC__bitreader_skip_bits_no_crc(context, decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->num_tracks = x; + + if(obj->num_tracks > 0) { + if(0 == (obj->tracks = (FLAC__StreamMetadata_CueSheet_Track*)safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = &obj->tracks[i]; + if(!FLAC__bitreader_read_raw_uint64(context, decoder->private_->input, &track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + track->number = (FLAC__byte)x; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, (FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + track->type = x; + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; /* read_callback_ sets the state for us */ + track->pre_emphasis = x; + + if(!FLAC__bitreader_skip_bits_no_crc(context, decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; /* read_callback_ sets the state for us */ + track->num_indices = (FLAC__byte)x; + + if(track->num_indices > 0) { + if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *index = &track->indices[j]; + if(!FLAC__bitreader_read_raw_uint64(context, decoder->private_->input, &index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + index->number = (FLAC__byte)x; + + if(!FLAC__bitreader_skip_bits_no_crc(context, decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + } + } + } + } + + return true; +} + +FLAC__bool read_metadata_picture_(void *context, FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj) +{ + FLAC__uint32 x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read type */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->type = x; + + /* read MIME type */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->mime_type = (char*)safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, (FLAC__byte*)obj->mime_type, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->mime_type[x] = '\0'; + + /* read description */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->description = (FLAC__byte*)safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, obj->description, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->description[x] = '\0'; + + /* read width */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &obj->width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read height */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &obj->height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read depth */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &obj->depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read colors */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &obj->colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read data */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->data = (FLAC__byte*)safe_malloc_(obj->data_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(obj->data_length > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(context, decoder->private_->input, obj->data, obj->data_length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool skip_id3v2_tag_(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, skip; + + /* skip the version and flags bytes */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 24)) + return false; /* read_callback_ sets the state for us */ + /* get the size (in bytes) to skip */ + skip = 0; + for(i = 0; i < 4; i++) { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + skip <<= 7; + skip |= (x & 0x7f); + } + /* skip the rest of the tag */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(context, decoder->private_->input, skip)) + return false; /* read_callback_ sets the state for us */ + return true; +} + +FLAC__bool frame_sync_(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__bool first = true; + + /* If we know the total number of samples in the stream, stop if we've read that many. */ + /* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0) { + if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return true; + } + } + + /* make sure we're byte aligned */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + } + + while(1) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + return true; +} + +FLAC__bool read_frame_(void *context, FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode) +{ + unsigned channel; + unsigned i; + FLAC__int32 mid, side; + unsigned frame_crc; /* the one we calculate from the input stream */ + FLAC__uint32 x; + + *got_a_frame = false; + + /* init the CRC */ + frame_crc = 0; + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[0], frame_crc); + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[1], frame_crc); + FLAC__bitreader_reset_read_crc16(decoder->private_->input, (FLAC__uint16)frame_crc); + + if(!read_frame_header_(context, decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */ + return true; + if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels)) + return false; + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + /* + * first figure the correct bits-per-sample of the subframe + */ + unsigned bps = decoder->private_->frame.header.bits_per_sample; + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* no adjustment needed */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 0) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + default: + FLAC__ASSERT(0); + } + /* + * now read it + */ + if(!read_subframe_(context, decoder, channel, bps, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + if(!read_zero_padding_(context, decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption (i.e. "zero bits" were not all zeroes) */ + return true; + + /* + * Read the frame CRC-16 from the footer and check + */ + frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) + return false; /* read_callback_ sets the state for us */ + if(frame_crc == x) { + if(do_full_decode) { + /* Undo any special channel coding */ + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* do nothing */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[0][i] += decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { +#if 1 + mid = decoder->private_->output[0][i]; + side = decoder->private_->output[1][i]; + mid <<= 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + side) >> 1; + decoder->private_->output[1][i] = (mid - side) >> 1; +#else + /* OPT: without 'side' temp variable */ + mid = (decoder->private_->output[0][i] << 1) | (decoder->private_->output[1][i] & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + decoder->private_->output[1][i]) >> 1; + decoder->private_->output[1][i] = (mid - decoder->private_->output[1][i]) >> 1; +#endif + } + break; + default: + FLAC__ASSERT(0); + break; + } + } + } + else { + /* Bad frame, emit error and zero the output signal */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); + if(do_full_decode) { + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + FMOD_memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + } + } + } + + *got_a_frame = true; + + /* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */ + if(decoder->private_->next_fixed_block_size) + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size; + + /* put the latest values into the public section of the decoder instance */ + decoder->protected_->channels = decoder->private_->frame.header.channels; + decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; + decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; + decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; + decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; + + FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; + + /* write it */ + if(do_full_decode) { + if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) + return false; + } + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; +} + +FLAC__bool read_frame_header_(void *context, FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__uint64 xx; + unsigned i, blocksize_hint = 0, sample_rate_hint = 0; + FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */ + unsigned raw_header_len; + FLAC__bool is_unparseable = false; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* init the raw header with the saved bits from synchronization */ + raw_header[0] = decoder->private_->header_warmup[0]; + raw_header[1] = decoder->private_->header_warmup[1]; + raw_header_len = 2; + + /* check to make sure that reserved bit is 0 */ + if(raw_header[1] & 0x02) /* MAGIC NUMBER */ + is_unparseable = true; + + /* + * Note that along the way as we read the header, we look for a sync + * code inside. If we find one it would indicate that our original + * sync was bad since there cannot be a sync code in a valid header. + * + * Three kinds of things can go wrong when reading the frame header: + * 1) We may have sync'ed incorrectly and not landed on a frame header. + * If we don't find a sync code, it can end up looking like we read + * a valid but unparseable header, until getting to the frame header + * CRC. Even then we could get a false positive on the CRC. + * 2) We may have sync'ed correctly but on an unparseable frame (from a + * future encoder). + * 3) We may be on a damaged frame which appears valid but unparseable. + * + * For all these reasons, we try and read a complete frame header as + * long as it seems valid, even if unparseable, up until the frame + * header CRC. + */ + + /* + * read in the raw header as bytes so we can CRC it, and parse it on the way + */ + for(i = 0; i < 2; i++) { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + /* if we get here it means our original sync was erroneous since the sync code cannot appear in the header */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + raw_header[raw_header_len++] = (FLAC__byte)x; + } + + switch(x = raw_header[2] >> 4) { + case 0: + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.blocksize = 192; + break; + case 2: + case 3: + case 4: + case 5: + decoder->private_->frame.header.blocksize = 576 << (x-2); + break; + case 6: + case 7: + blocksize_hint = x; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + decoder->private_->frame.header.blocksize = 256 << (x-8); + break; + default: + FLAC__ASSERT(0); + break; + } + + switch(x = raw_header[2] & 0x0f) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.sample_rate = decoder->private_->stream_info.data.stream_info.sample_rate; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.sample_rate = 88200; + break; + case 2: + decoder->private_->frame.header.sample_rate = 176400; + break; + case 3: + decoder->private_->frame.header.sample_rate = 192000; + break; + case 4: + decoder->private_->frame.header.sample_rate = 8000; + break; + case 5: + decoder->private_->frame.header.sample_rate = 16000; + break; + case 6: + decoder->private_->frame.header.sample_rate = 22050; + break; + case 7: + decoder->private_->frame.header.sample_rate = 24000; + break; + case 8: + decoder->private_->frame.header.sample_rate = 32000; + break; + case 9: + decoder->private_->frame.header.sample_rate = 44100; + break; + case 10: + decoder->private_->frame.header.sample_rate = 48000; + break; + case 11: + decoder->private_->frame.header.sample_rate = 96000; + break; + case 12: + case 13: + case 14: + sample_rate_hint = x; + break; + case 15: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + default: + FLAC__ASSERT(0); + } + + x = (unsigned)(raw_header[3] >> 4); + if(x & 8) { + decoder->private_->frame.header.channels = 2; + switch(x & 7) { + case 0: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE; + break; + case 1: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE; + break; + case 2: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE; + break; + default: + is_unparseable = true; + break; + } + } + else { + decoder->private_->frame.header.channels = (unsigned)x + 1; + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + } + + switch(x = (unsigned)(raw_header[3] & 0x0e) >> 1) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.bits_per_sample = 8; + break; + case 2: + decoder->private_->frame.header.bits_per_sample = 12; + break; + case 4: + decoder->private_->frame.header.bits_per_sample = 16; + break; + case 5: + decoder->private_->frame.header.bits_per_sample = 20; + break; + case 6: + decoder->private_->frame.header.bits_per_sample = 24; + break; + case 3: + case 7: + is_unparseable = true; + break; + default: + FLAC__ASSERT(0); + break; + } + + /* check to make sure that reserved bit is 0 */ + if(raw_header[3] & 0x01) /* MAGIC NUMBER */ + is_unparseable = true; + + /* read the frame's starting sample number (or frame number as the case may be) */ + if( + raw_header[1] & 0x01 || + /*@@@ this clause is a concession to the old way of doing variable blocksize; the only known implementation is flake and can probably be removed without inconveniencing anyone */ + (decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize != decoder->private_->stream_info.data.stream_info.max_blocksize) + ) { /* variable blocksize */ + if(!FLAC__bitreader_read_utf8_uint64(context, decoder->private_->input, &xx, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(xx == FLAC__U64L(0xffffffffffffffff)) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + decoder->private_->frame.header.number.sample_number = xx; + } + else { /* fixed blocksize */ + if(!FLAC__bitreader_read_utf8_uint32(context, decoder->private_->input, &x, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xffffffff) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; + decoder->private_->frame.header.number.frame_number = x; + } + + if(blocksize_hint) { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(blocksize_hint == 7) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + decoder->private_->frame.header.blocksize = x+1; + } + + if(sample_rate_hint) { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(sample_rate_hint != 12) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + if(sample_rate_hint == 12) + decoder->private_->frame.header.sample_rate = x*1000; + else if(sample_rate_hint == 13) + decoder->private_->frame.header.sample_rate = x; + else + decoder->private_->frame.header.sample_rate = x*10; + } + + /* read the CRC-8 byte */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + crc8 = (FLAC__byte)x; + + if(FLAC__crc8(raw_header, raw_header_len) != crc8) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* calculate the sample number from the frame number if needed */ + decoder->private_->next_fixed_block_size = 0; + if(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { + x = decoder->private_->frame.header.number.frame_number; + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + if(decoder->private_->fixed_block_size) + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->fixed_block_size * (FLAC__uint64)x; + else if(decoder->private_->has_stream_info) { + if(decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize) { + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->stream_info.data.stream_info.min_blocksize * (FLAC__uint64)x; + decoder->private_->next_fixed_block_size = decoder->private_->stream_info.data.stream_info.max_blocksize; + } + else + is_unparseable = true; + } + else if(x == 0) { + decoder->private_->frame.header.number.sample_number = 0; + decoder->private_->next_fixed_block_size = decoder->private_->frame.header.blocksize; + } + else { + /* can only get here if the stream has invalid frame numbering and no STREAMINFO, so assume it's not the last (possibly short) frame */ + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->frame.header.blocksize * (FLAC__uint64)x; + } + } + + if(is_unparseable) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + return true; +} + +FLAC__bool read_subframe_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__uint32 x; + FLAC__bool wasted_bits; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &x, 8)) /* MAGIC NUMBER */ + return false; /* read_callback_ sets the state for us */ + + wasted_bits = (x & 1); + x &= 0xfe; + + if(wasted_bits) { + unsigned u; + if(!FLAC__bitreader_read_unary_unsigned(context, decoder->private_->input, &u)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->frame.subframes[channel].wasted_bits = u+1; + bps -= decoder->private_->frame.subframes[channel].wasted_bits; + } + else + decoder->private_->frame.subframes[channel].wasted_bits = 0; + + /* + * Lots of magic numbers here + */ + if(x & 0x80) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x == 0) { + if(!read_subframe_constant_(context, decoder, channel, bps, do_full_decode)) + return false; + } + else if(x == 2) { + if(!read_subframe_verbatim_(context, decoder, channel, bps, do_full_decode)) + return false; + } + else if(x < 16) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x <= 24) { + if(!read_subframe_fixed_(context, decoder, channel, bps, (x>>1)&7, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + else if(x < 64) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else { + if(!read_subframe_lpc_(context, decoder, channel, bps, ((x>>1)&31)+1, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + + if(wasted_bits && do_full_decode) { + x = decoder->private_->frame.subframes[channel].wasted_bits; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[channel][i] <<= x; + } + + return true; +} + +FLAC__bool read_subframe_constant_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant; + FLAC__int32 x; + unsigned i; + FLAC__int32 *output = decoder->private_->output[channel]; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; + + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + + subframe->value = x; + + /* decode the subframe */ + if(do_full_decode) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } + + return true; +} + +FLAC__bool read_subframe_fixed_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(context, decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + FMOD_memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_lpc_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read qlp coeff precision */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &u32, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; /* read_callback_ sets the state for us */ + if(u32 == (1u << FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN) - 1) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->qlp_coeff_precision = u32+1; + + /* read qlp shift */ + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->quantization_level = i32; + + /* read quantized lp coefficiencts */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &i32, subframe->qlp_coeff_precision)) + return false; /* read_callback_ sets the state for us */ + subframe->qlp_coeff[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(context, decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + FMOD_memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + /*@@@@@@ technically not pessimistic enough, should be more like + if( (FLAC__uint64)order * ((((FLAC__uint64)1)<qlp_coeff_precision)-1) < (((FLAC__uint64)-1) << 32) ) + */ + if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(bps <= 16 && subframe->qlp_coeff_precision <= 16) { + if(order <= 8) + decoder->private_->local_lpc_restore_signal_16bit_order8(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + else + decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_verbatim_(void *context, FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim; + FLAC__int32 x, *residual = decoder->private_->residual[channel]; + unsigned i; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data = residual; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + residual[i] = x; + } + + /* decode the subframe */ + if(do_full_decode) + FMOD_memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + + return true; +} + +FLAC__bool read_residual_partitioned_rice_(void *context, FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended) +{ + FLAC__uint32 rice_parameter; + int i; + unsigned partition, sample, u; + const unsigned partitions = 1u << partition_order; + const unsigned partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order; + const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + /* sanity checks */ + if(partition_order == 0) { + if(decoder->private_->frame.header.blocksize < predictor_order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } + else { + if(partition_samples < predictor_order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } + + if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + sample = 0; + for(partition = 0; partition < partitions; partition++) { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &rice_parameter, plen)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->parameters[partition] = rice_parameter; + if(rice_parameter < pesc) { + partitioned_rice_contents->raw_bits[partition] = 0; + u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; + if(!decoder->private_->local_bitreader_read_rice_signed_block(context, decoder->private_->input, residual + sample, u, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + sample += u; + } + else { + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->raw_bits[partition] = rice_parameter; + for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { + if(!FLAC__bitreader_read_raw_int32(context, decoder->private_->input, &i, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + residual[sample] = i; + } + } + } + + return true; +} + +FLAC__bool read_zero_padding_(void *context, FLAC__StreamDecoder *decoder) +{ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + FLAC__uint32 zero = 0; + if(!FLAC__bitreader_read_raw_uint32(context, decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + if(zero != 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + } + return true; +} + +FLAC__bool read_callback_(void *context, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder *)client_data; + + if( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) { + *bytes = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else if(*bytes > 0) { + /* While seeking, it is possible for our seek to land in the + * middle of audio data that looks exactly like a frame header + * from a future version of an encoder. When that happens, our + * error callback will get an + * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its + * unparseable_frame_count. But there is a remote possibility + * that it is properly synced at such a "future-codec frame", + * so to make sure, we wait to see many "unparseable" errors in + * a row before bailing out. + */ + if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else { + const FLAC__StreamDecoderReadStatus status = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + read_callback_ogg_aspect_(context, decoder, buffer, bytes) : +#endif + decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data) + ; + if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else if(*bytes == 0) { + if( + status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM || + ( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) + ) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else + return true; + } + else + return true; + } + } + else { + /* abort to avoid a deadlock */ + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + /* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around + * for Ogg FLAC. This is because the ogg decoder aspect can lose sync + * and at the same time hit the end of the stream (for example, seeking + * to a point that is after the beginning of the last Ogg page). There + * is no way to report an Ogg sync loss through the callbacks (see note + * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0. + * So to keep the decoder from stopping at this point we gate the call + * to the eof_callback and let the Ogg decoder aspect set the + * end-of-stream state when it is needed. + */ +} + +#if FLAC__HAS_OGG +FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(void *context, const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes) +{ + switch(FLAC__ogg_decoder_aspect_read_callback_wrapper(context, &decoder->protected_->ogg_decoder_aspect, buffer, bytes, read_callback_proxy_, decoder, decoder->private_->client_data)) { + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + /* we don't really have a way to handle lost sync via read + * callback so we'll let it pass and let the underlying + * FLAC decoder catch the error + */ + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + default: + FLAC__ASSERT(0); + /* double protection */ + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder*)void_decoder; + + switch(decoder->private_->read_callback(decoder, buffer, bytes, client_data)) { + case FLAC__STREAM_DECODER_READ_STATUS_CONTINUE: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; + case FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; + case FLAC__STREAM_DECODER_READ_STATUS_ABORT: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + default: + /* double protection: */ + FLAC__ASSERT(0); + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + } +} +#endif + +FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if(decoder->private_->is_seeking) { + FLAC__uint64 this_frame_sample = frame->header.number.sample_number; + FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; + FLAC__uint64 target_sample = decoder->private_->target_sample; + + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + +#if FLAC__HAS_OGG + decoder->private_->got_a_frame = true; +#endif + decoder->private_->last_frame = *frame; /* save the frame */ + if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ + unsigned delta = (unsigned)(target_sample - this_frame_sample); + /* kick out of seek mode */ + decoder->private_->is_seeking = false; + /* shift out the samples before target_sample */ + if(delta > 0) { + unsigned channel; + const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; + for(channel = 0; channel < frame->header.channels; channel++) + newbuffer[channel] = buffer[channel] + delta; + decoder->private_->last_frame.header.blocksize -= delta; + decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data); + } + else { + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } + } + else { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + } + else { + /* + * If we never got STREAMINFO, turn off MD5 checking to save + * cycles since we don't have a sum to compare to anyway + */ + if(!decoder->private_->has_stream_info) + decoder->private_->do_md5_checking = false; + if(decoder->private_->do_md5_checking) { + if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } +} + +void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) +{ + if(!decoder->private_->is_seeking) + decoder->private_->error_callback(decoder, status, decoder->private_->client_data); + else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM) + decoder->private_->unparseable_frame_count++; +} + +FLAC__bool seek_to_absolute_sample_(void *context, FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample; + FLAC__int64 pos = -1; + int i; + unsigned approx_bytes_per_frame; + FLAC__bool first_seek = true; + const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); + const unsigned min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + const unsigned max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; + const unsigned max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; + const unsigned min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; + /* take these from the current frame in case they've changed mid-stream */ + unsigned channels = FLAC__stream_decoder_get_channels(decoder); + unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; + + /* use values from stream info if we didn't decode a frame */ + if(channels == 0) + channels = decoder->private_->stream_info.data.stream_info.channels; + if(bps == 0) + bps = decoder->private_->stream_info.data.stream_info.bits_per_sample; + + /* we are just guessing here */ + if(max_framesize > 0) + approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1; + /* + * Check if it's a known fixed-blocksize stream. Note that though + * the spec doesn't allow zeroes in the STREAMINFO block, we may + * never get a STREAMINFO block when decoding so the value of + * min_blocksize might be zero. + */ + else if(min_blocksize == max_blocksize && min_blocksize > 0) { + /* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */ + approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64; + } + else + approx_bytes_per_frame = 4096 * channels * bps/8 + 64; + + /* + * First, we set an upper and lower bound on where in the + * stream we will search. For now we assume the worst case + * scenario, which is our best guess at the beginning of + * the first frame and end of the stream. + */ + lower_bound = first_frame_offset; + lower_bound_sample = 0; + upper_bound = stream_length; + upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/; + + /* + * Now we refine the bounds if we have a seektable with + * suitable points. Note that according to the spec they + * must be ordered by ascending sample number. + * + * Note: to protect against invalid seek tables we will ignore points + * that have frame_samples==0 or sample_number>=total_samples + */ + if(seek_table) { + FLAC__uint64 new_lower_bound = lower_bound; + FLAC__uint64 new_upper_bound = upper_bound; + FLAC__uint64 new_lower_bound_sample = lower_bound_sample; + FLAC__uint64 new_upper_bound_sample = upper_bound_sample; + + /* find the closest seek point <= target_sample, if it exists */ + for(i = (int)seek_table->num_points - 1; i >= 0; i--) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number <= target_sample + ) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + new_lower_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_lower_bound_sample = seek_table->points[i].sample_number; + } + + /* find the closest seek point > target_sample, if it exists */ + for(i = 0; i < (int)seek_table->num_points; i++) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number > target_sample + ) + break; + } + if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */ + new_upper_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_upper_bound_sample = seek_table->points[i].sample_number; + } + /* final protection against unsorted seek tables; keep original values if bogus */ + if(new_upper_bound >= new_lower_bound) { + lower_bound = new_lower_bound; + upper_bound = new_upper_bound; + lower_bound_sample = new_lower_bound_sample; + upper_bound_sample = new_upper_bound_sample; + } + } + + FLAC__ASSERT(upper_bound_sample >= lower_bound_sample); + /* there are 2 insidious ways that the following equality occurs, which + * we need to fix: + * 1) total_samples is 0 (unknown) and target_sample is 0 + * 2) total_samples is 0 (unknown) and target_sample happens to be + * exactly equal to the last seek point in the seek table; this + * means there is no seek point above it, and upper_bound_samples + * remains equal to the estimate (of target_samples) we made above + * in either case it does not hurt to move upper_bound_sample up by 1 + */ + if(upper_bound_sample == lower_bound_sample) + upper_bound_sample++; + + decoder->private_->target_sample = target_sample; + while(1) { + /* check if the bounds are still ok */ + if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with VC++ you have to spoon feed it the casting */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(FLAC__int64)(target_sample - lower_bound_sample) / (FLAC__double)(FLAC__int64)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(FLAC__int64)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#else + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(target_sample - lower_bound_sample) / (FLAC__double)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#endif +#else + /* a little less accurate: */ + if(upper_bound - lower_bound < 0xffffffff) + pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame; +#endif + if(pos >= (FLAC__int64)upper_bound) + pos = (FLAC__int64)upper_bound - 1; + if(pos < (FLAC__int64)lower_bound) + pos = (FLAC__int64)lower_bound; + if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + /* Now we need to get a frame. First we need to reset our + * unparseable_frame_count; if we get too many unparseable + * frames in a row, the read callback will return + * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing + * FLAC__stream_decoder_process_single() to return false. + */ + decoder->private_->unparseable_frame_count = 0; + if(!FLAC__stream_decoder_process_single(context, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our write callback will change the state when it gets to the target frame */ + /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */ +#if 0 + /*@@@@@@ used to be the following; not clear if the check for end of stream is needed anymore */ + if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) + break; +#endif + if(!decoder->private_->is_seeking) + break; + + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + + if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) { + if (pos == (FLAC__int64)lower_bound) { + /* can't move back any more than the first frame, something is fatally wrong */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our last move backwards wasn't big enough, try again */ + approx_bytes_per_frame = approx_bytes_per_frame? approx_bytes_per_frame * 2 : 16; + continue; + } + /* allow one seek over upper bound, so we can get a correct upper_bound_sample for streams with unknown total_samples */ + first_seek = false; + + /* make sure we are not seeking in corrupted stream */ + if (this_frame_sample < lower_bound_sample) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + + /* we need to narrow the search */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; +/*@@@@@@ what will decode position be if at end of stream? */ + if(!FLAC__stream_decoder_get_decode_position(context, decoder, &upper_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (upper_bound - pos) / 3 + 16); + } + else { /* target_sample >= this_frame_sample + this frame's blocksize */ + lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(context, decoder, &lower_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (lower_bound - pos) / 3 + 16); + } + } + + return true; +} + +#if FLAC__HAS_OGG +FLAC__bool seek_to_absolute_sample_ogg_(void *context, FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 left_pos = 0, right_pos = stream_length; + FLAC__uint64 left_sample = 0, right_sample = FLAC__stream_decoder_get_total_samples(decoder); + FLAC__uint64 this_frame_sample = (FLAC__uint64)0 - 1; + FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ + FLAC__bool did_a_seek; + unsigned iteration = 0; + + /* In the first iterations, we will calculate the target byte position + * by the distance from the target sample to left_sample and + * right_sample (let's call it "proportional search"). After that, we + * will switch to binary search. + */ + unsigned BINARY_SEARCH_AFTER_ITERATION = 2; + + /* We will switch to a linear search once our current sample is less + * than this number of samples ahead of the target sample + */ + static const FLAC__uint64 LINEAR_SEARCH_WITHIN_SAMPLES = FLAC__MAX_BLOCK_SIZE * 2; + + /* If the total number of samples is unknown, use a large value, and + * force binary search immediately. + */ + if(right_sample == 0) { + right_sample = (FLAC__uint64)(-1); + BINARY_SEARCH_AFTER_ITERATION = 0; + } + + decoder->private_->target_sample = target_sample; + for( ; ; iteration++) { + if (iteration == 0 || this_frame_sample > target_sample || target_sample - this_frame_sample > LINEAR_SEARCH_WITHIN_SAMPLES) { + if (iteration >= BINARY_SEARCH_AFTER_ITERATION) { + pos = (right_pos + left_pos) / 2; + } + else { +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with MSVC you have to spoon feed it the casting */ + pos = (FLAC__uint64)((FLAC__double)(FLAC__int64)(target_sample - left_sample) / (FLAC__double)(FLAC__int64)(right_sample - left_sample) * (FLAC__double)(FLAC__int64)(right_pos - left_pos)); +#else + pos = (FLAC__uint64)((FLAC__double)(target_sample - left_sample) / (FLAC__double)(right_sample - left_sample) * (FLAC__double)(right_pos - left_pos)); +#endif +#else + /* a little less accurate: */ + if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) + pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)); + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16)); +#endif + /* @@@ TODO: might want to limit pos to some distance + * before EOF, to make sure we land before the last frame, + * thereby getting a this_frame_sample and so having a better + * estimate. + */ + } + + /* physical seek */ + if(decoder->private_->seek_callback((FLAC__StreamDecoder*)decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + did_a_seek = true; + } + else + did_a_seek = false; + + decoder->private_->got_a_frame = false; + if(!FLAC__stream_decoder_process_single(context, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!decoder->private_->got_a_frame) { + if(did_a_seek) { + /* this can happen if we seek to a point after the last frame; we drop + * to binary search right away in this case to avoid any wasted + * iterations of proportional search. + */ + right_pos = pos; + BINARY_SEARCH_AFTER_ITERATION = 0; + } + else { + /* this can probably only happen if total_samples is unknown and the + * target_sample is past the end of the stream + */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + } + /* our write callback will change the state when it gets to the target frame */ + else if(!decoder->private_->is_seeking) { + break; + } + else { + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + + if (did_a_seek) { + if (this_frame_sample <= target_sample) { + /* The 'equal' case should not happen, since + * FLAC__stream_decoder_process_single() + * should recognize that it has hit the + * target sample and we would exit through + * the 'break' above. + */ + FLAC__ASSERT(this_frame_sample != target_sample); + + left_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (left_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + left_pos = pos; + } + else if(this_frame_sample > target_sample) { + right_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (right_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + right_pos = pos; + } + } + } + } + + return true; +} +#endif + +FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file); + if(ferror(decoder->private_->file)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ +} + +FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + else if(fseeko(decoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + off_t pos; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + else if((pos = ftello(decoder->private_->file)) < 0) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ +#if defined _MSC_VER || defined __MINGW32__ + struct _stat filestats; +#else + struct stat filestats; +#endif + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; +#if defined _MSC_VER || defined __MINGW32__ + else if(_fstat(_fileno(decoder->private_->file), &filestats) != 0) +#else + else if(fstat(fileno(decoder->private_->file), &filestats) != 0) +#endif + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + else { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) +{ + (void)client_data; + + return feof(decoder->private_->file)? true : false; +} diff --git a/lib/flac-1.2.1/src/libFLAC/window.c b/lib/flac-1.2.1/src/libFLAC/window.c new file mode 100755 index 0000000..cd689c5 --- /dev/null +++ b/lib/flac-1.2.1/src/libFLAC/window.c @@ -0,0 +1,225 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/window.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#ifndef M_PI +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_PI 3.14159265358979323846 +#endif + + +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + if (L & 1) { + for (n = 0; n <= N/2; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } + else { + for (n = 0; n <= L/2-1; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * (N-n) / (float)N; + } +} + +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N+0.5f) + 0.38f * cos(2.0f * M_PI * ((float)n/(float)N+0.5f))); +} + +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); +} + +/* 4-term -92dB side-lobe */ +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n <= N; n++) + window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + double k = ((double)n - N2) / N2; + k = 1.0f - k * k; + window[n] = (FLAC__real)(k * k); + } +} + +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(1.0f - 1.93f * cos(2.0f * M_PI * n / N) + 1.29f * cos(4.0f * M_PI * n / N) - 0.388f * cos(6.0f * M_PI * n / N) + 0.0322f * cos(8.0f * M_PI * n / N)); +} + +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / (stddev * N2); + window[n] = (FLAC__real)exp(-0.5f * k * k); + } +} + +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); +} + +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = 1.0f; +} + +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + if (L & 1) { + for (n = 1; n <= L+1/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = - (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } + else { + for (n = 1; n <= L/2; n++) + window[n-1] = 2.0f * n / (float)L; + for (; n <= L; n++) + window[n-1] = ((float)(2 * (L - n)) + 1.0f) / (float)L; + } +} + +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p) +{ + if (p <= 0.0) + FLAC__window_rectangle(window, L); + else if (p >= 1.0) + FLAC__window_hann(window, L); + else { + const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; + FLAC__int32 n; + /* start with rectangle... */ + FLAC__window_rectangle(window, L); + /* ...replace ends with hann */ + if (Np > 0) { + for (n = 0; n <= Np; n++) { + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); + window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); + } + } + } +} + +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / N2; + window[n] = (FLAC__real)(1.0f - k * k); + } +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/lib/freeverb/allpass.cpp b/lib/freeverb/allpass.cpp new file mode 100755 index 0000000..850337e --- /dev/null +++ b/lib/freeverb/allpass.cpp @@ -0,0 +1,36 @@ +// Allpass filter implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "allpass.h" + +allpass::allpass() +{ + bufidx = 0; +} + +void allpass::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void allpass::mute() +{ + for (int i=0; i=bufsize) bufidx = 0; + + return output; +} + +#endif//_allpass + +//ends diff --git a/lib/freeverb/comb.cpp b/lib/freeverb/comb.cpp new file mode 100755 index 0000000..62be706 --- /dev/null +++ b/lib/freeverb/comb.cpp @@ -0,0 +1,48 @@ +// Comb filter implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "comb.h" + +comb::comb() +{ + filterstore = 0; + bufidx = 0; +} + +void comb::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void comb::mute() +{ + for (int i=0; i=bufsize) bufidx = 0; + + return output; +} + +#endif //_comb_ + +//ends diff --git a/lib/freeverb/denormals.h b/lib/freeverb/denormals.h new file mode 100755 index 0000000..f871412 --- /dev/null +++ b/lib/freeverb/denormals.h @@ -0,0 +1,15 @@ +// Macro for killing denormalled numbers +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// Based on IS_DENORMAL macro by Jon Watte +// This code is public domain + +#ifndef _denormals_ +#define _denormals_ + +#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + +#endif//_denormals_ + +//ends diff --git a/lib/freeverb/revmodel.cpp b/lib/freeverb/revmodel.cpp new file mode 100755 index 0000000..21eef45 --- /dev/null +++ b/lib/freeverb/revmodel.cpp @@ -0,0 +1,309 @@ +// Reverb model implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "revmodel.h" + +revmodel::revmodel() +{ + // Tie the components to their buffers + combL[0].setbuffer(bufcombL1,combtuningL1); + combR[0].setbuffer(bufcombR1,combtuningR1); + combL[1].setbuffer(bufcombL2,combtuningL2); + combR[1].setbuffer(bufcombR2,combtuningR2); + combL[2].setbuffer(bufcombL3,combtuningL3); + combR[2].setbuffer(bufcombR3,combtuningR3); + combL[3].setbuffer(bufcombL4,combtuningL4); + combR[3].setbuffer(bufcombR4,combtuningR4); + combL[4].setbuffer(bufcombL5,combtuningL5); + combR[4].setbuffer(bufcombR5,combtuningR5); + combL[5].setbuffer(bufcombL6,combtuningL6); + combR[5].setbuffer(bufcombR6,combtuningR6); + combL[6].setbuffer(bufcombL7,combtuningL7); + combR[6].setbuffer(bufcombR7,combtuningR7); + combL[7].setbuffer(bufcombL8,combtuningL8); + combR[7].setbuffer(bufcombR8,combtuningR8); + allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); + allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); + allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); + allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); + allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); + allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); + allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); + allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); + + // Set default values + allpassL[0].setfeedback(0.5f); + allpassR[0].setfeedback(0.5f); + allpassL[1].setfeedback(0.5f); + allpassR[1].setfeedback(0.5f); + allpassL[2].setfeedback(0.5f); + allpassR[2].setfeedback(0.5f); + allpassL[3].setfeedback(0.5f); + allpassR[3].setfeedback(0.5f); + setwet(initialwet); + setroomsize(initialroom); + setdry(initialdry); + setdamp(initialdamp); + setwidth(initialwidth); + setmode(initialmode); + + // Buffer will be full of rubbish - so we MUST mute them + mute(); +} + +void revmodel::mute() +{ + int i; + + if (getmode() >= freezemode) + return; + + for (i=0;i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(i=0; i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(i=0; i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(i=0; i= freezemode) + { + roomsize1 = 1; + damp1 = 0; + gain = muted; + } + else + { + roomsize1 = roomsize; + damp1 = damp; + gain = fixedgain; + } + + for(i=0; i= freezemode) + return 1; + else + return 0; +} + +//ends diff --git a/lib/freeverb/revmodel.h b/lib/freeverb/revmodel.h new file mode 100755 index 0000000..9556631 --- /dev/null +++ b/lib/freeverb/revmodel.h @@ -0,0 +1,87 @@ +// Reverb model declaration +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#ifndef _revmodel_ +#define _revmodel_ + +#include "comb.h" +#include "allpass.h" +#include "tuning.h" + +class revmodel +{ +public: + revmodel(); + void mute(); + void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip, unsigned short speakermask); + void setroomsize(float value); + float getroomsize(); + void setdamp(float value); + float getdamp(); + void setwet(float value); + float getwet(); + void setdry(float value); + float getdry(); + void setwidth(float value); + float getwidth(); + void setmode(float value); + float getmode(); +private: + void update(); +private: + float gain; + float roomsize,roomsize1; + float damp,damp1; + float wet,wet1,wet2; + float dry; + float width; + float mode; + + // The following are all declared inline + // to remove the need for dynamic allocation + // with its subsequent error-checking messiness + + // Comb filters + comb combL[numcombs]; + comb combR[numcombs]; + + // Allpass filters + allpass allpassL[numallpasses]; + allpass allpassR[numallpasses]; + + // Buffers for the combs + float bufcombL1[combtuningL1]; + float bufcombR1[combtuningR1]; + float bufcombL2[combtuningL2]; + float bufcombR2[combtuningR2]; + float bufcombL3[combtuningL3]; + float bufcombR3[combtuningR3]; + float bufcombL4[combtuningL4]; + float bufcombR4[combtuningR4]; + float bufcombL5[combtuningL5]; + float bufcombR5[combtuningR5]; + float bufcombL6[combtuningL6]; + float bufcombR6[combtuningR6]; + float bufcombL7[combtuningL7]; + float bufcombR7[combtuningR7]; + float bufcombL8[combtuningL8]; + float bufcombR8[combtuningR8]; + + // Buffers for the allpasses + float bufallpassL1[allpasstuningL1]; + float bufallpassR1[allpasstuningR1]; + float bufallpassL2[allpasstuningL2]; + float bufallpassR2[allpasstuningR2]; + float bufallpassL3[allpasstuningL3]; + float bufallpassR3[allpasstuningR3]; + float bufallpassL4[allpasstuningL4]; + float bufallpassR4[allpasstuningR4]; +}; + +#endif//_revmodel_ + +//ends diff --git a/lib/freeverb/tuning.h b/lib/freeverb/tuning.h new file mode 100755 index 0000000..baaa9ce --- /dev/null +++ b/lib/freeverb/tuning.h @@ -0,0 +1,60 @@ +// Reverb model tuning values +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#ifndef _tuning_ +#define _tuning_ + +const int numcombs = 8; +const int numallpasses = 4; +const float muted = 0; +const float fixedgain = 0.015f; +const float scalewet = 3; +const float scaledry = 2; +const float scaledamp = 0.4f; +const float scaleroom = 0.28f; +const float offsetroom = 0.7f; +const float initialroom = 0.5f; +const float initialdamp = 0.5f; +const float initialwet = 1/scalewet; +const float initialdry = 0; +const float initialwidth = 1; +const float initialmode = 0; +const float freezemode = 0.5f; +const int stereospread = 23; + +// These values assume 44.1KHz sample rate +// they will probably be OK for 48KHz sample rate +// but would need scaling for 96KHz (or other) sample rates. +// The values were obtained by listening tests. +const int combtuningL1 = 1116; +const int combtuningR1 = 1116+stereospread; +const int combtuningL2 = 1188; +const int combtuningR2 = 1188+stereospread; +const int combtuningL3 = 1277; +const int combtuningR3 = 1277+stereospread; +const int combtuningL4 = 1356; +const int combtuningR4 = 1356+stereospread; +const int combtuningL5 = 1422; +const int combtuningR5 = 1422+stereospread; +const int combtuningL6 = 1491; +const int combtuningR6 = 1491+stereospread; +const int combtuningL7 = 1557; +const int combtuningR7 = 1557+stereospread; +const int combtuningL8 = 1617; +const int combtuningR8 = 1617+stereospread; +const int allpasstuningL1 = 556; +const int allpasstuningR1 = 556+stereospread; +const int allpasstuningL2 = 441; +const int allpasstuningR2 = 441+stereospread; +const int allpasstuningL3 = 341; +const int allpasstuningR3 = 341+stereospread; +const int allpasstuningL4 = 225; +const int allpasstuningR4 = 225+stereospread; + +#endif//_tuning_ + +//ends + diff --git a/lib/libcelt/_kiss_fft_guts.h b/lib/libcelt/_kiss_fft_guts.h new file mode 100755 index 0000000..a497683 --- /dev/null +++ b/lib/libcelt/_kiss_fft_guts.h @@ -0,0 +1,245 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KISS_FFT_GUTS_H +#define KISS_FFT_GUTS_H + +#define MIN(a,b) ((a)<(b) ? (a):(b)) +#define MAX(a,b) ((a)>(b) ? (a):(b)) + +/* kiss_fft.h + defines kiss_fft_scalar as either short or a float type + and defines + typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ +#include "kiss_fft.h" + +#define MAXFACTORS 32 +/* e.g. an fft of length 128 has 4 factors + as far as kissfft is concerned + 4*4*4*2 + */ + +struct kiss_fft_state{ + int nfft; +#ifndef FIXED_POINT + kiss_fft_scalar scale; +#endif + int factors[2*MAXFACTORS]; + int *bitrev; + kiss_twiddle_cpx twiddles[1]; +}; + +/* + Explanation of macros dealing with complex math: + + C_MUL(m,a,b) : m = a*b + C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise + C_SUB( res, a,b) : res = a - b + C_SUBFROM( res , a) : res -= a + C_ADDTO( res , a) : res += a + * */ +#ifdef FIXED_POINT +#include "arch.h" + +#ifdef DOUBLE_PRECISION + +# define FRACBITS 31 +# define SAMPPROD celt_int64_t +#define SAMP_MAX 2147483647 +#ifdef MIXED_PRECISION +#define TWID_MAX 32767 +#define TRIG_UPSCALE 1 +#else +#define TRIG_UPSCALE 65536 +#define TWID_MAX 2147483647 +#endif +#define EXT32(a) (a) + +#else /* DOUBLE_PRECISION */ + +# define FRACBITS 15 +# define SAMPPROD celt_int32_t +#define SAMP_MAX 32767 +#define TRIG_UPSCALE 1 +#define EXT32(a) EXTEND32(a) + +#endif /* !DOUBLE_PRECISION */ + +#define SAMP_MIN -SAMP_MAX + +#if defined(CHECK_OVERFLOW) +# define CHECK_OVERFLOW_OP(a,op,b) \ + if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ + fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } +#endif + +# define smul(a,b) ( (SAMPPROD)(a)*(b) ) +# define sround( x ) (kiss_fft_scalar)( ( (x) + ((SAMPPROD)1<<(FRACBITS-1)) ) >> FRACBITS ) + +#ifdef MIXED_PRECISION + +# define S_MUL(a,b) MULT16_32_Q15(b, a) + +# define C_MUL(m,a,b) \ + do{ (m).r = SUB32(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \ + (m).i = ADD32(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)); }while(0) + +# define C_MULC(m,a,b) \ + do{ (m).r = ADD32(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \ + (m).i = SUB32(S_MUL((a).i,(b).r) , S_MUL((a).r,(b).i)); }while(0) + +# define C_MUL4(m,a,b) \ + do{ (m).r = SHR(SUB32(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)),2); \ + (m).i = SHR(ADD32(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)),2); }while(0) + +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r = S_MUL( (c).r , s ) ;\ + (c).i = S_MUL( (c).i , s ) ; }while(0) + +# define DIVSCALAR(x,k) \ + (x) = S_MUL( x, (TWID_MAX-((k)>>1))/(k)+1 ) + +# define C_FIXDIV(c,div) \ + do { DIVSCALAR( (c).r , div); \ + DIVSCALAR( (c).i , div); }while (0) + +#define C_ADD( res, a,b)\ + do {(res).r=ADD32((a).r,(b).r); (res).i=ADD32((a).i,(b).i); \ + }while(0) +#define C_SUB( res, a,b)\ + do {(res).r=SUB32((a).r,(b).r); (res).i=SUB32((a).i,(b).i); \ + }while(0) +#define C_ADDTO( res , a)\ + do {(res).r = ADD32((res).r, (a).r); (res).i = ADD32((res).i,(a).i);\ + }while(0) + +#define C_SUBFROM( res , a)\ + do {(res).r = ADD32((res).r,(a).r); (res).i = SUB32((res).i,(a).i); \ + }while(0) + +#else /* MIXED_PRECISION */ +# define sround4( x ) (kiss_fft_scalar)( ( (x) + ((SAMPPROD)1<<(FRACBITS-1)) ) >> (FRACBITS+2) ) + +# define S_MUL(a,b) sround( smul(a,b) ) + +# define C_MUL(m,a,b) \ + do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ + (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) +# define C_MULC(m,a,b) \ + do{ (m).r = sround( smul((a).r,(b).r) + smul((a).i,(b).i) ); \ + (m).i = sround( smul((a).i,(b).r) - smul((a).r,(b).i) ); }while(0) + +# define C_MUL4(m,a,b) \ + do{ (m).r = sround4( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ + (m).i = sround4( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) + +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r = sround( smul( (c).r , s ) ) ;\ + (c).i = sround( smul( (c).i , s ) ) ; }while(0) + +# define DIVSCALAR(x,k) \ + (x) = sround( smul( x, SAMP_MAX/k ) ) + +# define C_FIXDIV(c,div) \ + do { DIVSCALAR( (c).r , div); \ + DIVSCALAR( (c).i , div); }while (0) + +#endif /* !MIXED_PRECISION */ + + + +#else /* not FIXED_POINT*/ + +#define EXT32(a) (a) + +# define S_MUL(a,b) ( (a)*(b) ) +#define C_MUL(m,a,b) \ + do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ + (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) +#define C_MULC(m,a,b) \ + do{ (m).r = (a).r*(b).r + (a).i*(b).i;\ + (m).i = (a).i*(b).r - (a).r*(b).i; }while(0) + +#define C_MUL4(m,a,b) C_MUL(m,a,b) + +# define C_FIXDIV(c,div) /* NOOP */ +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r *= (s);\ + (c).i *= (s); }while(0) +#endif + + + +#ifndef CHECK_OVERFLOW_OP +# define CHECK_OVERFLOW_OP(a,op,b) /* noop */ +#endif + +#ifndef C_ADD +#define C_ADD( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,+,(b).r)\ + CHECK_OVERFLOW_OP((a).i,+,(b).i)\ + (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ + }while(0) +#define C_SUB( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,-,(b).r)\ + CHECK_OVERFLOW_OP((a).i,-,(b).i)\ + (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ + }while(0) +#define C_ADDTO( res , a)\ + do { \ + CHECK_OVERFLOW_OP((res).r,+,(a).r)\ + CHECK_OVERFLOW_OP((res).i,+,(a).i)\ + (res).r += (a).r; (res).i += (a).i;\ + }while(0) + +#define C_SUBFROM( res , a)\ + do {\ + CHECK_OVERFLOW_OP((res).r,-,(a).r)\ + CHECK_OVERFLOW_OP((res).i,-,(a).i)\ + (res).r -= (a).r; (res).i -= (a).i; \ + }while(0) +#endif /* C_ADD defined */ + +#ifdef FIXED_POINT +/*# define KISS_FFT_COS(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase)))) +# define KISS_FFT_SIN(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))*/ +# define KISS_FFT_COS(phase) floor(.5+TWID_MAX*cos (phase)) +# define KISS_FFT_SIN(phase) floor(.5+TWID_MAX*sin (phase)) +# define HALF_OF(x) ((x)>>1) +#elif defined(USE_SIMD) +# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) +# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) +# define HALF_OF(x) ((x)*_mm_set1_ps(.5)) +#else +# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) +# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) +# define HALF_OF(x) ((x)*.5) +#endif + +#define kf_cexp(x,phase) \ + do{ \ + (x)->r = KISS_FFT_COS(phase);\ + (x)->i = KISS_FFT_SIN(phase);\ + }while(0) + +#define kf_cexp2(x,phase) \ + do{ \ + (x)->r = TRIG_UPSCALE*celt_cos_norm((phase));\ + (x)->i = TRIG_UPSCALE*celt_cos_norm((phase)-32768);\ +}while(0) + + +#endif /* KISS_FFT_GUTS_H */ diff --git a/lib/libcelt/arch.h b/lib/libcelt/arch.h new file mode 100755 index 0000000..0a06f21 --- /dev/null +++ b/lib/libcelt/arch.h @@ -0,0 +1,252 @@ +/* Copyright (C) 2003-2008 Jean-Marc Valin */ +/** + @file arch.h + @brief Various architecture definitions for CELT +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef ARCH_H +#define ARCH_H + +#include "celt_types.h" + +#define CELT_SIG_SCALE 32768. + +#define celt_fatal(str) _celt_fatal(str, __FILE__, __LINE__); +#ifdef ENABLE_ASSERTIONS +#define celt_assert(cond) {if (!(cond)) {celt_fatal("assertion failed: " #cond);}} +#define celt_assert2(cond, message) {if (!(cond)) {celt_fatal("assertion failed: " #cond "\n" message);}} +#else +#define celt_assert(cond) +#define celt_assert2(cond, message) +#endif + +#define IMUL32(a,b) ((a)*(b)) +#define UMUL32(a,b) ((celt_int32_t)(a)*(celt_int32_t)(b)) +#define UMUL16_16(a,b) ((celt_int32_t)(a)*(celt_int32_t)(b)) + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */ +#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */ +#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 16-bit value. */ +#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */ +#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 32-bit value. */ +#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */ +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */ +#define UADD32(a,b) ((a)+(b)) +#define USUB32(a,b) ((a)-(b)) + +#define PRINT_MIPS(file) + +#ifdef FIXED_POINT + +typedef celt_int16_t celt_word16_t; +typedef celt_int32_t celt_word32_t; + +typedef celt_word32_t celt_sig_t; +typedef celt_word16_t celt_norm_t; +typedef celt_word32_t celt_ener_t; +typedef celt_word16_t celt_pgain_t; +typedef celt_word32_t celt_mask_t; + +#define Q15ONE 32767 +#define Q30ONE 1073741823 + +#define SIG_SHIFT 12 + +#define NORM_SCALING 16384 +#define NORM_SCALING_1 (1.f/16384.f) +#define NORM_SHIFT 14 + +#define ENER_SCALING 16384.f +#define ENER_SCALING_1 (1.f/16384.f) +#define ENER_SHIFT 14 + +#define PGAIN_SCALING 32768.f +#define PGAIN_SCALING_1 (1.f/32768.f) +#define PGAIN_SHIFT 15 + +#define DB_SCALING 256.f +#define DB_SCALING_1 (1.f/256.f) + +#define EPSILON 1 +#define VERY_SMALL 0 +#define VERY_LARGE32 ((celt_word32_t)2147483647) +#define VERY_LARGE16 ((celt_word16_t)32767) +#define Q15_ONE ((celt_word16_t)32767) +#define Q15_ONE_1 (1.f/32768.f) + +#define SCALEIN(a) (a) +#define SCALEOUT(a) (a) + +#ifdef FIXED_DEBUG +#include "fixed_debug.h" +#else + +#include "fixed_generic.h" + +#ifdef ARM5E_ASM +#include "fixed_arm5e.h" +#elif defined (ARM4_ASM) +#include "fixed_arm4.h" +#elif defined (BFIN_ASM) +#include "fixed_bfin.h" +#elif defined (TI_C5X_ASM) +#include "fixed_c5x.h" +#elif defined (TI_C6X_ASM) +#include "fixed_c6x.h" +#endif + +#endif + + +#else /* FIXED_POINT */ + +typedef float celt_word16_t; +typedef float celt_word32_t; + +typedef float celt_sig_t; +typedef float celt_norm_t; +typedef float celt_ener_t; +typedef float celt_pgain_t; +typedef float celt_mask_t; + +#define Q15ONE 1.0f +#define Q30ONE 1.0f + +#define NORM_SCALING 1.f +#define NORM_SCALING_1 1.f +#define ENER_SCALING 1.f +#define ENER_SCALING_1 1.f +#define PGAIN_SCALING 1.f +#define PGAIN_SCALING_1 1.f + +#define DB_SCALING 1.f +#define DB_SCALING_1 1.f + +#define EPSILON 1e-15f +#define VERY_SMALL 1e-15f +#define VERY_LARGE32 1e15f +#define VERY_LARGE16 1e15f +#define Q15_ONE ((celt_word16_t)1.f) +#define Q15_ONE_1 ((celt_word16_t)1.f) + +#define QCONST16(x,bits) (x) +#define QCONST32(x,bits) (x) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) (x) +#define EXTEND32(x) (x) +#define SHR16(a,shift) (a) +#define SHL16(a,shift) (a) +#define SHR32(a,shift) (a) +#define SHL32(a,shift) (a) +#define PSHR16(a,shift) (a) +#define PSHR32(a,shift) (a) +#define VSHR32(a,shift) (a) +#define SATURATE16(x,a) (x) +#define SATURATE32(x,a) (x) + +#define PSHR(a,shift) (a) +#define SHR(a,shift) (a) +#define SHL(a,shift) (a) +#define SATURATE(x,a) (x) + +#define ROUND16(a,shift) (a) +#define HALF32(x) (.5f*(x)) + +#define ADD16(a,b) ((a)+(b)) +#define SUB16(a,b) ((a)-(b)) +#define ADD32(a,b) ((a)+(b)) +#define SUB32(a,b) ((a)-(b)) +#define MULT16_16_16(a,b) ((a)*(b)) +#define MULT16_16(a,b) ((celt_word32_t)(a)*(celt_word32_t)(b)) +#define MAC16_16(c,a,b) ((c)+(celt_word32_t)(a)*(celt_word32_t)(b)) + +#define MULT16_32_Q11(a,b) ((a)*(b)) +#define MULT16_32_Q13(a,b) ((a)*(b)) +#define MULT16_32_Q14(a,b) ((a)*(b)) +#define MULT16_32_Q15(a,b) ((a)*(b)) +#define MULT16_32_Q16(a,b) ((a)*(b)) +#define MULT16_32_P15(a,b) ((a)*(b)) + +#define MULT32_32_Q31(a,b) ((a)*(b)) + +#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) + +#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_P13(c,a,b) ((c)+(a)*(b)) +#define MULT16_16_Q11_32(a,b) ((a)*(b)) +#define MULT16_16_Q13(a,b) ((a)*(b)) +#define MULT16_16_Q14(a,b) ((a)*(b)) +#define MULT16_16_Q15(a,b) ((a)*(b)) +#define MULT16_16_P15(a,b) ((a)*(b)) +#define MULT16_16_P13(a,b) ((a)*(b)) +#define MULT16_16_P14(a,b) ((a)*(b)) + +#define DIV32_16(a,b) (((celt_word32_t)(a))/(celt_word16_t)(b)) +#define PDIV32_16(a,b) (((celt_word32_t)(a))/(celt_word16_t)(b)) +#define DIV32(a,b) (((celt_word32_t)(a))/(celt_word32_t)(b)) +#define PDIV32(a,b) (((celt_word32_t)(a))/(celt_word32_t)(b)) + +#define SCALEIN(a) ((a)*CELT_SIG_SCALE) +#define SCALEOUT(a) ((a)*(1/CELT_SIG_SCALE)) + +#endif /* !FIXED_POINT */ + + +#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + +/* 2 on TI C5x DSP */ +#define BYTES_PER_CHAR 2 +#define BITS_PER_CHAR 16 +#define LOG2_BITS_PER_CHAR 4 + +#else /* CONFIG_TI_C54X */ + +#define BYTES_PER_CHAR 1 +#define BITS_PER_CHAR 8 +#define LOG2_BITS_PER_CHAR 3 + +#endif /* !CONFIG_TI_C54X */ + +#ifndef GLOBAL_STACK_SIZE +#ifdef FIXED_POINT +#define GLOBAL_STACK_SIZE 100000 +#else +#define GLOBAL_STACK_SIZE 100000 +#endif +#endif + +#endif /* ARCH_H */ diff --git a/lib/libcelt/bands.c b/lib/libcelt/bands.c new file mode 100755 index 0000000..d359bbe --- /dev/null +++ b/lib/libcelt/bands.c @@ -0,0 +1,949 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO + (C) 2008-2009 Gregory Maxwell */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "bands.h" +#include "modes.h" +#include "vq.h" +#include "cwrs.h" +#include "stack_alloc.h" +#include "os_support.h" +#include "mathops.h" +#include "rate.h" + +const celt_word16_t sqrtC_1[2] = {QCONST16(1.f, 14), QCONST16(1.414214f, 14)}; + +#ifdef FIXED_POINT +/* Compute the amplitude (sqrt energy) in each of the bands */ +void compute_band_energies(const CELTMode *m, const celt_sig_t *X, celt_ener_t *bank) +{ + int i, c, N; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + N = FRAMESIZE(m); + for (c=0;cnbEBands;i++) + { + int j; + celt_word32_t maxval=0; + celt_word32_t sum = 0; + + j=eBands[i]; do { + maxval = MAX32(maxval, X[j+c*N]); + maxval = MAX32(maxval, -X[j+c*N]); + } while (++j 0) + { + int shift = celt_ilog2(maxval)-10; + j=eBands[i]; do { + sum = MAC16_16(sum, EXTRACT16(VSHR32(X[j+c*N],shift)), + EXTRACT16(VSHR32(X[j+c*N],shift))); + } while (++jnbEBands] = EPSILON+VSHR32(EXTEND32(celt_sqrt(sum)),-shift); + } else { + bank[i+c*m->nbEBands] = EPSILON; + } + /*printf ("%f ", bank[i+c*m->nbEBands]);*/ + } + } + /*printf ("\n");*/ +} + +/* Normalise each band such that the energy is one. */ +void normalise_bands(const CELTMode *m, const celt_sig_t * celt_restrict freq, celt_norm_t * celt_restrict X, const celt_ener_t *bank) +{ + int i, c, N; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + N = FRAMESIZE(m); + for (c=0;cnbEBands])-13; + E = VSHR32(bank[i+c*m->nbEBands], shift); + g = EXTRACT16(celt_rcp(SHL32(E,3))); + j=eBands[i]; do { + X[j*C+c] = MULT16_16_Q15(VSHR32(freq[j+c*N],shift-1),g); + } while (++jnbEBands); + } +} + +#else /* FIXED_POINT */ +/* Compute the amplitude (sqrt energy) in each of the bands */ +void compute_band_energies(const CELTMode *m, const celt_sig_t *X, celt_ener_t *bank) +{ + int i, c, N; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + N = FRAMESIZE(m); + for (c=0;cnbEBands;i++) + { + int j; + celt_word32_t sum = 1e-10; + for (j=eBands[i];jnbEBands] = sqrt(sum); + /*printf ("%f ", bank[i+c*m->nbEBands]);*/ + } + } + /*printf ("\n");*/ +} + +#ifdef EXP_PSY +void compute_noise_energies(const CELTMode *m, const celt_sig_t *X, const celt_word16_t *tonality, celt_ener_t *bank) +{ + int i, c, N; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + N = FRAMESIZE(m); + for (c=0;cnbEBands;i++) + { + int j; + celt_word32_t sum = 1e-10; + for (j=eBands[i];jnbEBands] = sqrt(sum); + /*printf ("%f ", bank[i+c*m->nbEBands]);*/ + } + } + /*printf ("\n");*/ +} +#endif + +/* Normalise each band such that the energy is one. */ +void normalise_bands(const CELTMode *m, const celt_sig_t * celt_restrict freq, celt_norm_t * celt_restrict X, const celt_ener_t *bank) +{ + int i, c, N; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + N = FRAMESIZE(m); + for (c=0;cnbEBands;i++) + { + int j; + celt_word16_t g = 1.f/(1e-10+bank[i+c*m->nbEBands]); + for (j=eBands[i];jeBands; + const int C = CHANNELS(m); + for (c=0;cnbEBands); + } +} +#endif /* DISABLE_STEREO */ + +/* De-normalise the energy to produce the synthesis from the unit-energy bands */ +void denormalise_bands(const CELTMode *m, const celt_norm_t * celt_restrict X, celt_sig_t * celt_restrict freq, const celt_ener_t *bank) +{ + int i, c, N; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + N = FRAMESIZE(m); + if (C>2) + celt_fatal("denormalise_bands() not implemented for >2 channels"); + for (c=0;cnbEBands;i++) + { + int j; + celt_word32_t g = SHR32(bank[i+c*m->nbEBands],1); + j=eBands[i]; do { + freq[j+c*N] = SHL32(MULT16_32_Q15(X[j*C+c], g),2); + } while (++jnbEBands];inbEBands+1];i++) + freq[i+c*N] = 0; + } +} + + +/* Compute the best gain for each "pitch band" */ +int compute_pitch_gain(const CELTMode *m, const celt_norm_t *X, const celt_norm_t *P, celt_pgain_t *gains) +{ + int i; + int gain_sum = 0; + const celt_int16_t *pBands = m->pBands; + const int C = CHANNELS(m); + + for (i=0;inbPBands;i++) + { + celt_word32_t Sxy=0, Sxx=0; + int j; + /* We know we're not going to overflow because Sxx can't be more than 1 (Q28) */ + for (j=C*pBands[i];j Sxx) + Sxy = Sxx; + /* We need to be a bit conservative (multiply gain by 0.9), otherwise the + residual doesn't quantise well */ + Sxy = MULT16_32_Q15(QCONST16(.99f, 15), Sxy); + /* gain = Sxy/Sxx */ + gains[i] = EXTRACT16(celt_div(Sxy,ADD32(SHR32(Sxx, PGAIN_SHIFT),EPSILON))); + if (gains[i]>QCONST16(.5,15)) + gain_sum++; + } + return gain_sum > 5; +} + +#ifndef DISABLE_STEREO + +static void stereo_band_mix(const CELTMode *m, celt_norm_t *X, const celt_ener_t *bank, int stereo_mode, int bandID, int dir) +{ + int i = bandID; + const celt_int16_t *eBands = m->eBands; + const int C = CHANNELS(m); + int j; + celt_word16_t a1, a2; + if (stereo_mode==0) + { + /* Do mid-side when not doing intensity stereo */ + a1 = QCONST16(.70711f,14); + a2 = dir*QCONST16(.70711f,14); + } else { + celt_word16_t left, right; + celt_word16_t norm; +#ifdef FIXED_POINT + int shift = celt_zlog2(MAX32(bank[i], bank[i+m->nbEBands]))-13; +#endif + left = VSHR32(bank[i],shift); + right = VSHR32(bank[i+m->nbEBands],shift); + norm = EPSILON + celt_sqrt(EPSILON+MULT16_16(left,left)+MULT16_16(right,right)); + a1 = DIV32_16(SHL32(EXTEND32(left),14),norm); + a2 = dir*DIV32_16(SHL32(EXTEND32(right),14),norm); + } + for (j=eBands[i];j>1;i++) + { + x[i<<1] = tmp[i]; + x[(i<<1)+1] = tmp[i+(N>>1)]; + } + RESTORE_STACK; +} + +void deinterleave(celt_norm_t *x, int N) +{ + int i; + VARDECL(celt_norm_t, tmp); + SAVE_STACK; + ALLOC(tmp, N, celt_norm_t); + + for (i=0;i>1;i++) + { + x[i] = tmp[i<<1]; + x[i+(N>>1)] = tmp[(i<<1)+1]; + } + RESTORE_STACK; +} + +#endif /* DISABLE_STEREO */ + +int folding_decision(const CELTMode *m, celt_norm_t *X, celt_word16_t *average, int *last_decision) +{ + int i; + int NR=0; + celt_word32_t ratio = EPSILON; + const celt_int16_t * celt_restrict eBands = m->eBands; + for (i=0;inbEBands;i++) + { + int j, N; + int max_i=0; + celt_word16_t max_val=EPSILON; + celt_word32_t floor_ener=EPSILON; + celt_norm_t * celt_restrict x = X+eBands[i]; + N = eBands[i+1]-eBands[i]; + for (j=0;jmax_val) + { + max_val = ABS16(x[j]); + max_i = j; + } + } +#if 0 + for (j=0;j2) + floor_ener += x[j]*x[j]; + } +#else + floor_ener = QCONST32(1.,28)-MULT16_16(max_val,max_val); + if (max_i < N-1) + floor_ener -= MULT16_16(x[max_i+1], x[max_i+1]); + if (max_i < N-2) + floor_ener -= MULT16_16(x[max_i+2], x[max_i+2]); + if (max_i > 0) + floor_ener -= MULT16_16(x[max_i-1], x[max_i-1]); + if (max_i > 1) + floor_ener -= MULT16_16(x[max_i-2], x[max_i-2]); + floor_ener = MAX32(floor_ener, EPSILON); +#endif + if (N>7 && eBands[i] >= m->pitchEnd) + { + celt_word16_t r; + celt_word16_t den = celt_sqrt(floor_ener); + den = MAX32(QCONST16(.02, 15), den); + r = DIV32_16(SHL32(EXTEND32(max_val),8),den); + ratio = ADD32(ratio, EXTEND32(r)); + NR++; + } + } + if (NR>0) + ratio = DIV32_16(ratio, NR); + ratio = ADD32(HALF32(ratio), HALF32(*average)); + if (!*last_decision) + { + *last_decision = (ratio < QCONST16(1.8,8)); + } else { + *last_decision = (ratio < QCONST16(3.,8)); + } + *average = EXTRACT16(ratio); + return *last_decision; +} + +#ifdef FMOD_CELT_ENCODER + +/* Quantisation of the residual */ +void quant_bands(const CELTMode *m, celt_norm_t * celt_restrict X, celt_norm_t *P, celt_mask_t *W, int pitch_used, celt_pgain_t *pgains, const celt_ener_t *bandE, int *pulses, int shortBlocks, int fold, int total_bits, ec_enc *enc) +{ + int i, j, remaining_bits, balance; + const celt_int16_t * celt_restrict eBands = m->eBands; + celt_norm_t * celt_restrict norm; + VARDECL(celt_norm_t, _norm); const celt_int16_t *pBands = m->pBands; + int pband=-1; + int B; + SAVE_STACK; + + B = shortBlocks ? m->nbShortMdcts : 1; + ALLOC(_norm, eBands[m->nbEBands+1], celt_norm_t); + norm = _norm; + + balance = 0; + for (i=0;inbEBands;i++) + { + int tell; + int N; + int q; + celt_word16_t n; + const celt_int16_t * const *BPbits; + + int curr_balance, curr_bits; + + N = eBands[i+1]-eBands[i]; + BPbits = m->bits; + + tell = ec_enc_tell(enc, 4); + if (i != 0) + balance -= tell; + remaining_bits = (total_bits<nbEBands-i); + if (curr_balance > 3) + curr_balance = 3; + curr_balance = balance / curr_balance; + q = bits2pulses(m, BPbits[i], N, pulses[i]+curr_balance); + curr_bits = pulses2bits(BPbits[i], N, q); + remaining_bits -= curr_bits; + while (remaining_bits < 0 && q > 0) + { + remaining_bits += curr_bits; + q--; + curr_bits = pulses2bits(BPbits[i], N, q); + remaining_bits -= curr_bits; + } + balance += pulses[i] + tell; + + n = SHL16(celt_sqrt(eBands[i+1]-eBands[i]),11); + + /* If pitch is in use and this eBand begins a pitch band, encode the pitch gain flag */ + if (pitch_used && eBands[i]< m->pitchEnd && eBands[i] == pBands[pband+1]) + { + int enabled = 1; + pband++; + if (remaining_bits >= 1< QCONST16(.5,15); + ec_enc_bits(enc, enabled, 1); + balance += 1<= m->pitchEnd && fold) || q<=0) + { + intra_fold(m, X+eBands[i], eBands[i+1]-eBands[i], &q, norm, P+eBands[i], eBands[i], B); + } else if (pitch_used && eBands[i] < m->pitchEnd) { + for (j=eBands[i];j 0) + { + alg_quant(X+eBands[i], W+eBands[i], eBands[i+1]-eBands[i], q, P+eBands[i], enc); + } else { + for (j=eBands[i];jeBands; + celt_norm_t * celt_restrict norm; + VARDECL(celt_norm_t, _norm); + const int C = CHANNELS(m); + const celt_int16_t *pBands = m->pBands; + int pband=-1; + int B; + celt_word16_t mid, side; + SAVE_STACK; + + B = shortBlocks ? m->nbShortMdcts : 1; + ALLOC(_norm, C*eBands[m->nbEBands+1], celt_norm_t); + norm = _norm; + + balance = 0; + for (i=0;inbEBands;i++) + { + int c; + int tell; + int q1, q2; + celt_word16_t n; + const celt_int16_t * const *BPbits; + int b, qb; + int N; + int curr_balance, curr_bits; + int imid, iside, itheta; + int mbits, sbits, delta; + int qalloc; + + BPbits = m->bits; + + N = eBands[i+1]-eBands[i]; + tell = ec_enc_tell(enc, 4); + if (i != 0) + balance -= tell; + remaining_bits = (total_bits<nbEBands-i); + if (curr_balance > 3) + curr_balance = 3; + curr_balance = balance / curr_balance; + b = IMIN(remaining_bits+1,pulses[i]+curr_balance); + if (b<0) + b = 0; + + qb = (b-2*(N-1)*(40-log2_frac(N,4)))/(32*(N-1)); + if (qb > (b>>BITRES)-1) + qb = (b>>BITRES)-1; + if (qb<0) + qb = 0; + if (qb>14) + qb = 14; + + stereo_band_mix(m, X, bandE, qb==0, i, 1); + + mid = renormalise_vector(X+C*eBands[i], Q15ONE, N, C); + side = renormalise_vector(X+C*eBands[i]+1, Q15ONE, N, C); +#ifdef FIXED_POINT + itheta = MULT16_16_Q15(QCONST16(0.63662,15),celt_atan2p(side, mid)); +#else + itheta = floor(.5+16384*0.63662*atan2(side,mid)); +#endif + qalloc = log2_frac((1<>1))>>shift; + ec_enc_uint(enc, itheta, (1<>2; + } + mbits = (b-qalloc/2-delta)/2; + if (mbits > b-qalloc) + mbits = b-qalloc; + if (mbits<0) + mbits=0; + sbits = b-qalloc-mbits; + q1 = bits2pulses(m, BPbits[i], N, mbits); + q2 = bits2pulses(m, BPbits[i], N, sbits); + curr_bits = pulses2bits(BPbits[i], N, q1)+pulses2bits(BPbits[i], N, q2)+qalloc; + remaining_bits -= curr_bits; + while (remaining_bits < 0 && (q1 > 0 || q2 > 0)) + { + remaining_bits += curr_bits; + if (q1>q2) + { + q1--; + curr_bits = pulses2bits(BPbits[i], N, q1)+pulses2bits(BPbits[i], N, q2)+qalloc; + } else { + q2--; + curr_bits = pulses2bits(BPbits[i], N, q1)+pulses2bits(BPbits[i], N, q2)+qalloc; + } + remaining_bits -= curr_bits; + } + balance += pulses[i] + tell; + + n = SHL16(celt_sqrt((eBands[i+1]-eBands[i])),11); + + /* If pitch is in use and this eBand begins a pitch band, encode the pitch gain flag */ + if (pitch_used && eBands[i]< m->pitchEnd && eBands[i] == pBands[pband+1]) + { + int enabled = 1; + pband++; + if (remaining_bits >= 1< QCONST16(.5,15); + ec_enc_bits(enc, enabled, 1); + balance += 1<= m->pitchEnd && fold) || (q1+q2)<=0) + { + int K[2] = {q1, q2}; + intra_fold(m, X+C*eBands[i], eBands[i+1]-eBands[i], K, norm, P+C*eBands[i], eBands[i], B); + deinterleave(P+C*eBands[i], C*N); + } else if (pitch_used && eBands[i] < m->pitchEnd) { + stereo_band_mix(m, P, bandE, qb==0, i, 1); + renormalise_vector(P+C*eBands[i], Q15ONE, N, C); + renormalise_vector(P+C*eBands[i]+1, Q15ONE, N, C); + deinterleave(P+C*eBands[i], C*N); + for (j=C*eBands[i];j 0) + alg_quant(X+C*eBands[i], W+C*eBands[i], N, q1, P+C*eBands[i], enc); + else + for (j=C*eBands[i];j 0) + alg_quant(X+C*eBands[i]+N, W+C*eBands[i], N, q2, P+C*eBands[i]+N, enc); + else + for (j=C*eBands[i]+N;jeBands; + celt_norm_t * celt_restrict norm; + VARDECL(celt_norm_t, _norm); + const celt_int16_t *pBands = m->pBands; + int pband=-1; + int B; + SAVE_STACK; + + B = shortBlocks ? m->nbShortMdcts : 1; + ALLOC(_norm, eBands[m->nbEBands+1], celt_norm_t); + norm = _norm; + + balance = 0; + for (i=0;inbEBands;i++) + { + int tell; + int N; + int q; + celt_word16_t n; + const celt_int16_t * const *BPbits; + + int curr_balance, curr_bits; + + N = eBands[i+1]-eBands[i]; + BPbits = m->bits; + + tell = ec_dec_tell(dec, 4); + if (i != 0) + balance -= tell; + remaining_bits = (total_bits<nbEBands-i); + if (curr_balance > 3) + curr_balance = 3; + curr_balance = balance / curr_balance; + q = bits2pulses(m, BPbits[i], N, pulses[i]+curr_balance); + curr_bits = pulses2bits(BPbits[i], N, q); + remaining_bits -= curr_bits; + while (remaining_bits < 0 && q > 0) + { + remaining_bits += curr_bits; + q--; + curr_bits = pulses2bits(BPbits[i], N, q); + remaining_bits -= curr_bits; + } + balance += pulses[i] + tell; + + n = SHL16(celt_sqrt(eBands[i+1]-eBands[i]),11); + + /* If pitch is in use and this eBand begins a pitch band, encode the pitch gain flag */ + if (pitch_used && eBands[i] < m->pitchEnd && eBands[i] == pBands[pband+1]) + { + int enabled = 1; + pband++; + if (remaining_bits >= 1<= m->pitchEnd && fold) || q<=0) + { + intra_fold(m, X+eBands[i], eBands[i+1]-eBands[i], &q, norm, P+eBands[i], eBands[i], B); + } else if (pitch_used && eBands[i] < m->pitchEnd) { + for (j=eBands[i];j 0) + { + alg_unquant(X+eBands[i], eBands[i+1]-eBands[i], q, P+eBands[i], dec); + } else { + for (j=eBands[i];jeBands; + celt_norm_t * celt_restrict norm; + VARDECL(celt_norm_t, _norm); + const int C = CHANNELS(m); + const celt_int16_t *pBands = m->pBands; + int pband=-1; + int B; + celt_word16_t mid, side; + SAVE_STACK; + + B = shortBlocks ? m->nbShortMdcts : 1; + ALLOC(_norm, C*eBands[m->nbEBands+1], celt_norm_t); + norm = _norm; + + balance = 0; + for (i=0;inbEBands;i++) + { + int c; + int tell; + int q1, q2; + celt_word16_t n; + const celt_int16_t * const *BPbits; + int b, qb; + int N; + int curr_balance, curr_bits; + int imid, iside, itheta; + int mbits, sbits, delta; + int qalloc; + + BPbits = m->bits; + + N = eBands[i+1]-eBands[i]; + tell = ec_dec_tell(dec, 4); + if (i != 0) + balance -= tell; + remaining_bits = (total_bits<nbEBands-i); + if (curr_balance > 3) + curr_balance = 3; + curr_balance = balance / curr_balance; + b = IMIN(remaining_bits+1,pulses[i]+curr_balance); + if (b<0) + b = 0; + + qb = (b-2*(N-1)*(40-log2_frac(N,4)))/(32*(N-1)); + if (qb > (b>>BITRES)-1) + qb = (b>>BITRES)-1; + if (qb>14) + qb = 14; + if (qb<0) + qb = 0; + qalloc = log2_frac((1<>2; + } + mbits = (b-qalloc/2-delta)/2; + if (mbits > b-qalloc) + mbits = b-qalloc; + if (mbits<0) + mbits=0; + sbits = b-qalloc-mbits; + q1 = bits2pulses(m, BPbits[i], N, mbits); + q2 = bits2pulses(m, BPbits[i], N, sbits); + curr_bits = pulses2bits(BPbits[i], N, q1)+pulses2bits(BPbits[i], N, q2)+qalloc; + remaining_bits -= curr_bits; + while (remaining_bits < 0 && (q1 > 0 || q2 > 0)) + { + remaining_bits += curr_bits; + if (q1>q2) + { + q1--; + curr_bits = pulses2bits(BPbits[i], N, q1)+pulses2bits(BPbits[i], N, q2)+qalloc; + } else { + q2--; + curr_bits = pulses2bits(BPbits[i], N, q1)+pulses2bits(BPbits[i], N, q2)+qalloc; + } + remaining_bits -= curr_bits; + } + balance += pulses[i] + tell; + + n = SHL16(celt_sqrt((eBands[i+1]-eBands[i])),11); + + /* If pitch is in use and this eBand begins a pitch band, encode the pitch gain flag */ + if (pitch_used && eBands[i]< m->pitchEnd && eBands[i] == pBands[pband+1]) + { + int enabled = 1; + pband++; + if (remaining_bits >= 1<= m->pitchEnd && fold) || (q1+q2)<=0) + { + int K[2] = {q1, q2}; + intra_fold(m, X+C*eBands[i], eBands[i+1]-eBands[i], K, norm, P+C*eBands[i], eBands[i], B); + deinterleave(P+C*eBands[i], C*N); + } else if (pitch_used && eBands[i] < m->pitchEnd) { + stereo_band_mix(m, P, bandE, qb==0, i, 1); + renormalise_vector(P+C*eBands[i], Q15ONE, N, C); + renormalise_vector(P+C*eBands[i]+1, Q15ONE, N, C); + deinterleave(P+C*eBands[i], C*N); + for (j=C*eBands[i];j 0) + alg_unquant(X+C*eBands[i], N, q1, P+C*eBands[i], dec); + else + for (j=C*eBands[i];j 0) + alg_unquant(X+C*eBands[i]+N, N, q2, P+C*eBands[i]+N, dec); + else + for (j=C*eBands[i]+N;j +#include "celt.h" +#include "pitch.h" +#include "kiss_fftr.h" +#include "bands.h" +#include "modes.h" +#include "entcode.h" +#include "quant_bands.h" +#include "psy.h" +#include "rate.h" +#include "stack_alloc.h" +#include "mathops.h" +#include "float_cast.h" +#include + +static const celt_word16_t preemph = QCONST16(0.8f,15); + +#ifdef FIXED_POINT +static const celt_word16_t transientWindow[16] = { + 279, 1106, 2454, 4276, 6510, 9081, 11900, 14872, + 17896, 20868, 23687, 26258, 28492, 30314, 31662, 32489}; +#else +static const float transientWindow[16] = { + 0.0085135, 0.0337639, 0.0748914, 0.1304955, + 0.1986827, 0.2771308, 0.3631685, 0.4538658, + 0.5461342, 0.6368315, 0.7228692, 0.8013173, + 0.8695045, 0.9251086, 0.9662361, 0.9914865}; +#endif + +#define ENCODERVALID 0x4c434554 +#define ENCODERPARTIAL 0x5445434c +#define ENCODERFREED 0x4c004500 + +/** Encoder state + @brief Encoder state + */ +struct CELTEncoder { + celt_uint32_t marker; + const CELTMode *mode; /**< Mode used by the encoder */ + int frame_size; + int block_size; + int overlap; + int channels; + + int pitch_enabled; /* Complexity level is allowed to use pitch */ + int pitch_permitted; /* Use of the LTP is permitted by the user */ + int pitch_available; /* Amount of pitch buffer available */ + int force_intra; + int delayedIntra; + celt_word16_t tonal_average; + int fold_decision; + + int VBR_rate; /* Target number of 16th bits per frame */ + celt_word16_t * celt_restrict preemph_memE; + celt_sig_t * celt_restrict preemph_memD; + + celt_sig_t *in_mem; + celt_sig_t *out_mem; + + celt_word16_t *oldBandE; +#ifdef EXP_PSY + celt_word16_t *psy_mem; + struct PsyDecay psy; +#endif +}; + +#ifdef FMOD_CELT_ENCODER + +int check_encoder(const CELTEncoder *st) +{ + if (st==NULL) + { + celt_warning("NULL passed as an encoder structure"); + return CELT_INVALID_STATE; + } + if (st->marker == ENCODERVALID) + return CELT_OK; + if (st->marker == ENCODERFREED) + celt_warning("Referencing an encoder that has already been freed"); + else + celt_warning("This is not a valid CELT encoder structure"); + return CELT_INVALID_STATE; +} + +CELTEncoder *celt_encoder_create(const CELTMode *mode) +{ + int N, C; + CELTEncoder *st; + + if (check_mode(mode) != CELT_OK) + return NULL; + + N = mode->mdctSize; + C = mode->nbChannels; + st = celt_alloc(sizeof(CELTEncoder)); + + if (st==NULL) + return NULL; + st->marker = ENCODERPARTIAL; + st->mode = mode; + st->frame_size = N; + st->block_size = N; + st->overlap = mode->overlap; + + st->VBR_rate = 0; + st->pitch_enabled = 1; + st->pitch_permitted = 1; + st->pitch_available = 1; + st->force_intra = 0; + st->delayedIntra = 1; + st->tonal_average = QCONST16(1.,8); + st->fold_decision = 1; + + st->in_mem = celt_alloc(st->overlap*C*sizeof(celt_sig_t)); + st->out_mem = celt_alloc((MAX_PERIOD+st->overlap)*C*sizeof(celt_sig_t)); + + st->oldBandE = (celt_word16_t*)celt_alloc(C*mode->nbEBands*sizeof(celt_word16_t)); + + st->preemph_memE = (celt_word16_t*)celt_alloc(C*sizeof(celt_word16_t)); + st->preemph_memD = (celt_sig_t*)celt_alloc(C*sizeof(celt_sig_t)); + +#ifdef EXP_PSY + st->psy_mem = celt_alloc(MAX_PERIOD*sizeof(celt_word16_t)); + psydecay_init(&st->psy, MAX_PERIOD/2, st->mode->Fs); +#endif + + if ((st->in_mem!=NULL) && (st->out_mem!=NULL) && (st->oldBandE!=NULL) +#ifdef EXP_PSY + && (st->psy_mem!=NULL) +#endif + && (st->preemph_memE!=NULL) && (st->preemph_memD!=NULL)) + { + st->marker = ENCODERVALID; + return st; + } + /* If the setup fails for some reason deallocate it. */ + celt_encoder_destroy(st); + return NULL; +} + +void celt_encoder_destroy(CELTEncoder *st) +{ + if (st == NULL) + { + celt_warning("NULL passed to celt_encoder_destroy"); + return; + } + + if (st->marker == ENCODERFREED) + { + celt_warning("Freeing an encoder which has already been freed"); + return; + } + + if (st->marker != ENCODERVALID && st->marker != ENCODERPARTIAL) + { + celt_warning("This is not a valid CELT encoder structure"); + return; + } + /*Check_mode is non-fatal here because we can still free + the encoder memory even if the mode is bad, although calling + the free functions in this order is a violation of the API.*/ + check_mode(st->mode); + + celt_free(st->in_mem); + celt_free(st->out_mem); + + celt_free(st->oldBandE); + + celt_free(st->preemph_memE); + celt_free(st->preemph_memD); + +#ifdef EXP_PSY + celt_free (st->psy_mem); + psydecay_clear(&st->psy); +#endif + st->marker = ENCODERFREED; + + celt_free(st); +} + +#endif + +static FMOD_INLINE celt_int16_t FLOAT2INT16(float x) +{ + x = x*CELT_SIG_SCALE; + x = MAX32(x, -32768); + x = MIN32(x, 32767); + return (celt_int16_t)float2int(x); +} + +static FMOD_INLINE celt_word16_t SIG2WORD16(celt_sig_t x) +{ +#ifdef FIXED_POINT + x = PSHR32(x, SIG_SHIFT); + x = MAX32(x, -32768); + x = MIN32(x, 32767); + return EXTRACT16(x); +#else + return (celt_word16_t)x; +#endif +} + +static int transient_analysis(celt_word32_t *in, int len, int C, int *transient_time, int *transient_shift) +{ + int c, i, n; + celt_word32_t ratio; + VARDECL(celt_word32_t, begin); + SAVE_STACK; + ALLOC(begin, len, celt_word32_t); + for (i=0;i 1000) + ratio = 1000; + ratio *= ratio; + + if (ratio > 2048) + *transient_shift = 3; + else + *transient_shift = 0; + + *transient_time = n; + + RESTORE_STACK; + return ratio > 20; +} + +/** Apply window and compute the MDCT for all sub-frames and + all channels in a frame */ +static void compute_mdcts(const CELTMode *mode, int shortBlocks, celt_sig_t * celt_restrict in, celt_sig_t * celt_restrict out) +{ + const int C = CHANNELS(mode); + if (C==1 && !shortBlocks) + { + const mdct_lookup *lookup = MDCT(mode); + const int overlap = OVERLAP(mode); + mdct_forward(lookup, in, out, mode->window, overlap); + } else if (!shortBlocks) { + const mdct_lookup *lookup = MDCT(mode); + const int overlap = OVERLAP(mode); + const int N = FRAMESIZE(mode); + int c; + VARDECL(celt_word32_t, x); + VARDECL(celt_word32_t, tmp); + SAVE_STACK; + ALLOC(x, N+overlap, celt_word32_t); + ALLOC(tmp, N, celt_word32_t); + for (c=0;cwindow, overlap); + /* Interleaving the sub-frames */ + for (j=0;jshortMdct; + const int overlap = mode->overlap; + const int N = mode->shortMdctSize; + int b, c; + VARDECL(celt_word32_t, x); + VARDECL(celt_word32_t, tmp); + SAVE_STACK; + ALLOC(x, N+overlap, celt_word32_t); + ALLOC(tmp, N, celt_word32_t); + for (c=0;cnbShortMdcts; + for (b=0;bwindow, overlap); + /* Interleaving the sub-frames */ + for (j=0;j>1; + for (c=0;cwindow, overlap); + } else if (!shortBlocks) { + const mdct_lookup *lookup = MDCT(mode); + VARDECL(celt_word32_t, x); + VARDECL(celt_word32_t, tmp); + SAVE_STACK; + ALLOC(x, 2*N, celt_word32_t); + ALLOC(tmp, N, celt_word32_t); + /* De-interleaving the sub-frames */ + for (j=0;jwindow, overlap); + celt_assert(transient_shift == 0); + /* The first and last part would need to be set to zero if we actually + wanted to use them. */ + for (j=0;jshortMdctSize; + const int B = mode->nbShortMdcts; + const mdct_lookup *lookup = &mode->shortMdct; + VARDECL(celt_word32_t, x); + VARDECL(celt_word32_t, tmp); + SAVE_STACK; + ALLOC(x, 2*N, celt_word32_t); + ALLOC(tmp, N, celt_word32_t); + /* Prevents problems from the imdct doing the overlap-add */ + CELT_MEMSET(x+N4, 0, N2); + for (b=0;bwindow, overlap); + } + if (transient_shift > 0) + { +#ifdef FIXED_POINT + for (j=0;j<16;j++) + x[N4+transient_time+j-16] = MULT16_32_Q15(SHR16(Q15_ONE-transientWindow[j],transient_shift)+transientWindow[j], SHL32(x[N4+transient_time+j-16],transient_shift)); + for (j=transient_time;jmode); + int mdct_weight_shift = 0; + int mdct_weight_pos=0; + SAVE_STACK; + + if (check_encoder(st) != CELT_OK) + return CELT_INVALID_STATE; + + if (check_mode(st->mode) != CELT_OK) + return CELT_INVALID_MODE; + + if (nbCompressedBytes<0 || pcm==NULL) + return CELT_BAD_ARG; + + /* The memset is important for now in case the encoder doesn't + fill up all the bytes */ + CELT_MEMSET(compressed, 0, nbCompressedBytes); + ec_byte_writeinit_buffer(&buf, compressed, nbCompressedBytes); + ec_enc_init(&enc,&buf); + + N = st->block_size; + N4 = (N-st->overlap)>>1; + ALLOC(in, 2*C*N-2*C*N4, celt_sig_t); + + CELT_COPY(in, st->in_mem, C*st->overlap); + for (c=0;coverlap+c; + for (i=0;ipreemph_memE[c]),3)); + st->preemph_memE[c] = SCALEIN(*pcmp); + inp += C; + pcmp += C; + } + } + CELT_COPY(st->in_mem, in+C*(2*N-2*N4-st->overlap), C*st->overlap); + + /* Transient handling */ + transient_time = -1; + transient_shift = 0; + shortBlocks = 0; + + if (st->mode->nbShortMdcts > 1 && transient_analysis(in, N+st->overlap, C, &transient_time, &transient_shift)) + { +#ifndef FIXED_POINT + float gain_1; +#endif + /* Apply the inverse shaping window */ + if (transient_shift) + { +#ifdef FIXED_POINT + for (c=0;coverlap;i++) + in[C*i+c] = SHR32(in[C*i+c], transient_shift); +#else + for (c=0;coverlap;i++) + in[C*i+c] *= gain_1; +#endif + } + shortBlocks = 1; + has_fold = 1; + } + + ALLOC(freq, C*N, celt_sig_t); /**< Interleaved signal MDCTs */ + ALLOC(bandE,st->mode->nbEBands*C, celt_ener_t); + ALLOC(bandLogE,st->mode->nbEBands*C, celt_word16_t); + /* Compute MDCTs */ + compute_mdcts(st->mode, shortBlocks, in, freq); + + if (shortBlocks && !transient_shift) + { + celt_word32_t sum[8]={1,1,1,1,1,1,1,1}; + int m; + for (c=0;cmode->nbShortMdcts) + tmp += ABS32(freq[i]); + sum[m++] += tmp; + } while (mmode->nbShortMdcts); + } + m=0; +#ifdef FIXED_POINT + do { + if (SHR32(sum[m+1],3) > sum[m]) + { + mdct_weight_shift=2; + mdct_weight_pos = m; + } else if (SHR32(sum[m+1],1) > sum[m] && mdct_weight_shift < 2) + { + mdct_weight_shift=1; + mdct_weight_pos = m; + } + m++; + } while (mmode->nbShortMdcts-1); + if (mdct_weight_shift) + { + for (c=0;cmode->nbShortMdcts;m++) + for (i=m+c*N;i<(c+1)*N;i+=st->mode->nbShortMdcts) + freq[i] = SHR32(freq[i],mdct_weight_shift); + } +#else + do { + if (sum[m+1] > 8*sum[m]) + { + mdct_weight_shift=2; + mdct_weight_pos = m; + } else if (sum[m+1] > 2*sum[m] && mdct_weight_shift < 2) + { + mdct_weight_shift=1; + mdct_weight_pos = m; + } + m++; + } while (mmode->nbShortMdcts-1); + if (mdct_weight_shift) + { + for (c=0;cmode->nbShortMdcts;m++) + for (i=m+c*N;i<(c+1)*N;i+=st->mode->nbShortMdcts) + freq[i] = (1./(1<mode, freq, bandE); + for (i=0;imode->nbEBands*C;i++) + bandLogE[i] = amp2Log(bandE[i]); + + /* Don't use intra energy when we're operating at low bit-rate */ + intra_ener = st->force_intra || (st->delayedIntra && nbCompressedBytes > st->mode->nbEBands); + if (shortBlocks || intra_decision(bandLogE, st->oldBandE, st->mode->nbEBands)) + st->delayedIntra = 1; + else + st->delayedIntra = 0; + + /* Pitch analysis: we do it early to save on the peak stack space */ + /* Don't use pitch if there isn't enough data available yet, + or if we're using shortBlocks */ + has_pitch = st->pitch_enabled && st->pitch_permitted && (N <= 512) + && (st->pitch_available >= MAX_PERIOD) && (!shortBlocks) + && !intra_ener; +#ifdef EXP_PSY + ALLOC(tonality, MAX_PERIOD/4, celt_word16_t); + { + VARDECL(celt_word16_t, X); + ALLOC(X, MAX_PERIOD/2, celt_word16_t); + find_spectral_pitch(st->mode, st->mode->fft, &st->mode->psy, in, st->out_mem, st->mode->window, X, 2*N-2*N4, MAX_PERIOD-(2*N-2*N4), &pitch_index); + compute_tonality(st->mode, X, st->psy_mem, MAX_PERIOD, tonality, MAX_PERIOD/4); + } +#else + if (has_pitch) + { + find_spectral_pitch(st->mode, st->mode->fft, &st->mode->psy, in, st->out_mem, st->mode->window, NULL, 2*N-2*N4, MAX_PERIOD-(2*N-2*N4), &pitch_index); + } +#endif + +#ifdef EXP_PSY + ALLOC(mask, N, celt_sig_t); + compute_mdct_masking(&st->psy, freq, tonality, st->psy_mem, mask, C*N); + /*for (i=0;i<256;i++) + printf ("%f %f %f ", freq[i], tonality[i], mask[i]); + printf ("\n");*/ +#endif + + /* Deferred allocation after find_spectral_pitch() to reduce + the peak memory usage */ + ALLOC(X, C*N, celt_norm_t); /**< Interleaved normalised MDCTs */ + ALLOC(P, C*N, celt_norm_t); /**< Interleaved normalised pitch MDCTs*/ + ALLOC(gains,st->mode->nbPBands, celt_pgain_t); + + + /* Band normalisation */ + normalise_bands(st->mode, freq, X, bandE); + if (!shortBlocks && !folding_decision(st->mode, X, &st->tonal_average, &st->fold_decision)) + has_fold = 0; +#ifdef EXP_PSY + ALLOC(bandN,C*st->mode->nbEBands, celt_ener_t); + ALLOC(bandM,st->mode->nbEBands, celt_ener_t); + compute_noise_energies(st->mode, freq, tonality, bandN); + + /*for (i=0;imode->nbEBands;i++) + printf ("%f ", (.1+bandN[i])/(.1+bandE[i])); + printf ("\n");*/ + has_fold = 0; + for (i=st->mode->nbPBands;imode->nbEBands;i++) + if (bandN[i] < .4*bandE[i]) + has_fold++; + /*printf ("%d\n", has_fold);*/ + if (has_fold>=2) + has_fold = 0; + else + has_fold = 1; + for (i=0;imode, mask, bandM); + /*for (i=0;imode->nbEBands;i++) + printf ("%f %f ", bandE[i], bandM[i]); + printf ("\n");*/ +#endif + + /* Compute MDCTs of the pitch part */ + if (has_pitch) + { + celt_word32_t curr_power, pitch_power=0; + /* Normalise the pitch vector as well (discard the energies) */ + VARDECL(celt_ener_t, bandEp); + + compute_mdcts(st->mode, 0, st->out_mem+pitch_index*C, freq); + ALLOC(bandEp, st->mode->nbEBands*st->mode->nbChannels, celt_ener_t); + compute_band_energies(st->mode, freq, bandEp); + normalise_bands(st->mode, freq, P, bandEp); + pitch_power = bandEp[0]+bandEp[1]+bandEp[2]; + curr_power = bandE[0]+bandE[1]+bandE[2]; + if (C>1) + { + pitch_power += bandEp[0+st->mode->nbEBands]+bandEp[1+st->mode->nbEBands]+bandEp[2+st->mode->nbEBands]; + curr_power += bandE[0+st->mode->nbEBands]+bandE[1+st->mode->nbEBands]+bandE[2+st->mode->nbEBands]; + } + /* Check if we can safely use the pitch (i.e. effective gain + isn't too high) */ + if ((MULT16_32_Q15(QCONST16(.1f, 15),curr_power) + QCONST32(10.f,ENER_SHIFT) < pitch_power)) + { + /* Pitch prediction */ + has_pitch = compute_pitch_gain(st->mode, X, P, gains); + } else { + has_pitch = 0; + } + } + + encode_flags(&enc, intra_ener, has_pitch, shortBlocks, has_fold); + if (has_pitch) + { + ec_enc_uint(&enc, pitch_index, MAX_PERIOD-(2*N-2*N4)); + } else { + for (i=0;imode->nbPBands;i++) + gains[i] = 0; + for (i=0;ioverlap); + } else { + ec_enc_bits(&enc, mdct_weight_shift, 2); + if (mdct_weight_shift && st->mode->nbShortMdcts!=2) + ec_enc_uint(&enc, mdct_weight_pos, st->mode->nbShortMdcts-1); + } + } + +#ifdef STDIN_TUNING2 + static int fine_quant[30]; + static int pulses[30]; + static int init=0; + if (!init) + { + for (i=0;imode->nbEBands;i++) + scanf("%d ", &fine_quant[i]); + for (i=0;imode->nbEBands;i++) + scanf("%d ", &pulses[i]); + init = 1; + } +#else + ALLOC(fine_quant, st->mode->nbEBands, int); + ALLOC(pulses, st->mode->nbEBands, int); +#endif + + /* Bit allocation */ + ALLOC(error, C*st->mode->nbEBands, celt_word16_t); + coarse_needed = quant_coarse_energy(st->mode, bandLogE, st->oldBandE, nbCompressedBytes*8/3, intra_ener, st->mode->prob, error, &enc); + coarse_needed = ((coarse_needed*3-1)>>3)+1; + + /* Variable bitrate */ + if (st->VBR_rate>0) + { + /* The target rate in 16th bits per frame */ + int target=st->VBR_rate; + + /* Shortblocks get a large boost in bitrate, but since they + are uncommon long blocks are not greatly effected */ + if (shortBlocks) + target*=2; + else if (st->mode->nbShortMdcts > 1) + target-=(target+14)/28; + + /* The average energy is removed from the target and the actual + energy added*/ + target=target-588+ec_enc_tell(&enc, 4); + + /* In VBR mode the frame size must not be reduced so much that it would result in the coarse energy busting its budget */ + target=IMAX(coarse_needed,(target+64)/128); + nbCompressedBytes=IMIN(nbCompressedBytes,target); + } + + ALLOC(offsets, st->mode->nbEBands, int); + ALLOC(fine_priority, st->mode->nbEBands, int); + + for (i=0;imode->nbEBands;i++) + offsets[i] = 0; + bits = nbCompressedBytes*8 - ec_enc_tell(&enc, 0) - 1; + if (has_pitch) + bits -= st->mode->nbPBands; +#ifndef STDIN_TUNING + compute_allocation(st->mode, offsets, bits, pulses, fine_quant, fine_priority); +#endif + + quant_fine_energy(st->mode, bandE, st->oldBandE, error, fine_quant, &enc); + + /* Residual quantisation */ + if (C==1) + quant_bands(st->mode, X, P, NULL, has_pitch, gains, bandE, pulses, shortBlocks, has_fold, nbCompressedBytes*8, &enc); +#ifndef DISABLE_STEREO + else + quant_bands_stereo(st->mode, X, P, NULL, has_pitch, gains, bandE, pulses, shortBlocks, has_fold, nbCompressedBytes*8, &enc); +#endif + + quant_energy_finalise(st->mode, bandE, st->oldBandE, error, fine_quant, fine_priority, nbCompressedBytes*8-ec_enc_tell(&enc, 0), &enc); + + /* Re-synthesis of the coded audio if required */ + if (st->pitch_available>0 || optional_synthesis!=NULL) + { + if (st->pitch_available>0 && st->pitch_availablepitch_available+=st->frame_size; + + /* Synthesis */ + denormalise_bands(st->mode, X, freq, bandE); + + + CELT_MOVE(st->out_mem, st->out_mem+C*N, C*(MAX_PERIOD+st->overlap-N)); + + if (mdct_weight_shift) + { + int m; + for (c=0;cmode->nbShortMdcts;m++) + for (i=m+c*N;i<(c+1)*N;i+=st->mode->nbShortMdcts) +#ifdef FIXED_POINT + freq[i] = SHL32(freq[i], mdct_weight_shift); +#else + freq[i] = (1<mode, shortBlocks, freq, transient_time, transient_shift, st->out_mem); + /* De-emphasis and put everything back at the right place + in the synthesis history */ + if (optional_synthesis != NULL) { + for (c=0;cout_mem[C*(MAX_PERIOD-N)+C*j+c], + preemph,st->preemph_memD[c]); + st->preemph_memD[c] = tmp; + optional_synthesis[C*j+c] = SCALEOUT(SIG2WORD16(tmp)); + } + } + } + } + + ec_enc_done(&enc); + + RESTORE_STACK; + return nbCompressedBytes; +} + +#ifdef FIXED_POINT +#ifndef DISABLE_FLOAT_API +int celt_encode_float(CELTEncoder * celt_restrict st, const float * pcm, float * optional_synthesis, unsigned char *compressed, int nbCompressedBytes) +{ + int j, ret, C, N; + VARDECL(celt_int16_t, in); + + if (check_encoder(st) != CELT_OK) + return CELT_INVALID_STATE; + + if (check_mode(st->mode) != CELT_OK) + return CELT_INVALID_MODE; + + if (pcm==NULL) + return CELT_BAD_ARG; + + SAVE_STACK; + C = CHANNELS(st->mode); + N = st->block_size; + ALLOC(in, C*N, celt_int16_t); + + for (j=0;jmode) != CELT_OK) + return CELT_INVALID_MODE; + + if (pcm==NULL) + return CELT_BAD_ARG; + + { + SAVE_STACK; + C=CHANNELS(st->mode); + N=st->block_size; + ALLOC(in, C*N, celt_sig_t); + for (j=0;jmode) != CELT_OK)) + goto bad_mode; + switch (request) + { + case CELT_GET_MODE_REQUEST: + { + const CELTMode ** value = va_arg(ap, const CELTMode**); + if (value==0) + goto bad_arg; + *value=st->mode; + } + break; + case CELT_SET_COMPLEXITY_REQUEST: + { + int value = va_arg(ap, celt_int32_t); + if (value<0 || value>10) + goto bad_arg; + if (value<=2) { + st->pitch_enabled = 0; + st->pitch_available = 0; + } else { + st->pitch_enabled = 1; + if (st->pitch_available<1) + st->pitch_available = 1; + } + } + break; + case CELT_SET_PREDICTION_REQUEST: + { + int value = va_arg(ap, celt_int32_t); + if (value<0 || value>2) + goto bad_arg; + if (value==0) + { + st->force_intra = 1; + st->pitch_permitted = 0; + } else if (value==1) { + st->force_intra = 0; + st->pitch_permitted = 0; + } else { + st->force_intra = 0; + st->pitch_permitted = 1; + } + } + break; + case CELT_SET_VBR_RATE_REQUEST: + { + int value = va_arg(ap, celt_int32_t); + if (value<0) + goto bad_arg; + if (value>3072000) + value = 3072000; + st->VBR_rate = ((st->mode->Fs<<3)+(st->block_size>>1))/st->block_size; + st->VBR_rate = ((value<<7)+(st->VBR_rate>>1))/st->VBR_rate; + } + break; + case CELT_RESET_STATE: + { + const CELTMode *mode = st->mode; + int C = mode->nbChannels; + + if (st->pitch_available > 0) st->pitch_available = 1; + + CELT_MEMSET(st->in_mem, 0, st->overlap*C); + CELT_MEMSET(st->out_mem, 0, (MAX_PERIOD+st->overlap)*C); + + CELT_MEMSET(st->oldBandE, 0, C*mode->nbEBands); + + CELT_MEMSET(st->preemph_memE, 0, C); + CELT_MEMSET(st->preemph_memD, 0, C); + st->delayedIntra = 1; + } + break; + default: + goto bad_request; + } + va_end(ap); + return CELT_OK; +bad_mode: + va_end(ap); + return CELT_INVALID_MODE; +bad_arg: + va_end(ap); + return CELT_BAD_ARG; +bad_request: + va_end(ap); + return CELT_UNIMPLEMENTED; +} + +#endif + +/**********************************************************************/ +/* */ +/* DECODER */ +/* */ +/**********************************************************************/ +#ifdef NEW_PLC +#define DECODE_BUFFER_SIZE 2048 +#else +#define DECODE_BUFFER_SIZE MAX_PERIOD +#endif + +#define DECODERVALID 0x4c434454 +#define DECODERPARTIAL 0x5444434c +#define DECODERFREED 0x4c004400 + +/** Decoder state + @brief Decoder state + */ +struct CELTDecoder { + celt_uint32_t marker; + const CELTMode *mode; + int frame_size; + int block_size; + int overlap; + + ec_byte_buffer buf; + ec_enc enc; + + celt_sig_t * celt_restrict preemph_memD; + + celt_sig_t *out_mem; + celt_sig_t *decode_mem; + + celt_word16_t *oldBandE; + + int last_pitch_index; +}; + +int check_decoder(const CELTDecoder *st) +{ + if (st==NULL) + { + celt_warning("NULL passed a decoder structure"); + return CELT_INVALID_STATE; + } + if (st->marker == DECODERVALID) + return CELT_OK; + if (st->marker == DECODERFREED) + celt_warning("Referencing a decoder that has already been freed"); + else + celt_warning("This is not a valid CELT decoder structure"); + return CELT_INVALID_STATE; +} + +CELTDecoder *celt_decoder_create(const CELTMode *mode) +{ + int N, C; + CELTDecoder *st; + + if (check_mode(mode) != CELT_OK) + return NULL; + + N = mode->mdctSize; + C = CHANNELS(mode); + st = celt_alloc(sizeof(CELTDecoder)); + + if (st==NULL) + return NULL; + + st->marker = DECODERPARTIAL; + st->mode = mode; + st->frame_size = N; + st->block_size = N; + st->overlap = mode->overlap; + + st->decode_mem = celt_alloc((DECODE_BUFFER_SIZE+st->overlap)*C*sizeof(celt_sig_t)); + st->out_mem = st->decode_mem+DECODE_BUFFER_SIZE-MAX_PERIOD; + + st->oldBandE = (celt_word16_t*)celt_alloc(C*mode->nbEBands*sizeof(celt_word16_t)); + + st->preemph_memD = (celt_sig_t*)celt_alloc(C*sizeof(celt_sig_t)); + + st->last_pitch_index = 0; + + if ((st->decode_mem!=NULL) && (st->out_mem!=NULL) && (st->oldBandE!=NULL) && + (st->preemph_memD!=NULL)) + { + st->marker = DECODERVALID; + return st; + } + /* If the setup fails for some reason deallocate it. */ + celt_decoder_destroy(st); + return NULL; +} + +// CPS +#if 1 +CELTDecoder *fmod_celt_decoder_create_only(char *membuffer) +{ + /* + Get rid of the allocs + */ + + int C = 2; // cps - 2 channels maximum + CELTDecoder *st; + + // st = celt_alloc(sizeof(CELTDecoder)); // 32bit = 76 bytes 64bit = 136 bytes + st = (CELTDecoder *)membuffer; // CPS + + if (st==NULL) + return NULL; + + st->marker = DECODERPARTIAL; + st->overlap = 128; // CPS + + // st->decode_mem = celt_alloc((DECODE_BUFFER_SIZE+st->overlap)*C*sizeof(celt_sig_t)); // 5120 bytes + st->decode_mem = (celt_sig_t *)(membuffer + sizeof(CELTDecoder)); // CPS + + st->out_mem = st->decode_mem+DECODE_BUFFER_SIZE-MAX_PERIOD; + + // st->oldBandE = (celt_word16_t*)celt_alloc(C* /*FMOD_CELT_MAX_EBANDS*/ 24 *sizeof(celt_word16_t)); // 192 bytes + st->oldBandE = (celt_word16_t *)(membuffer + sizeof(CELTDecoder) + 5120); // CPS + + // st->preemph_memD = (celt_sig_t*)celt_alloc(C*sizeof(celt_sig_t)); // 8 bytes + st->preemph_memD = (celt_sig_t *)(membuffer + sizeof(CELTDecoder) + 5120 + 192); + + st->last_pitch_index = 0; + + if ((st->decode_mem!=NULL) && (st->out_mem!=NULL) && (st->oldBandE!=NULL) && + (st->preemph_memD!=NULL)) + { + st->marker = DECODERVALID; + return st; + } + /* If the setup fails for some reason deallocate it. */ + celt_decoder_destroy(st); + return NULL; +} + +// CPS +void fmod_celt_decoder_setmode(CELTDecoder *st, CELTMode *mode) +{ + int N, C; + if (check_mode(mode) != CELT_OK) + return; + + N = mode->mdctSize; + C = CHANNELS(mode); + + st->mode = mode; + st->frame_size = N; + st->block_size = N; + st->overlap = mode->overlap; + + + /* + Extra shit that isn't in static modes yet + */ + /* + mdct_init(&mode->mdct, 2*mode->mdctSize); + mode->fft = pitch_state_alloc(MAX_PERIOD); + + mode->shortMdctSize = mode->mdctSize/mode->nbShortMdcts; + mdct_init(&mode->shortMdct, 2*mode->shortMdctSize); + mode->shortWindow = mode->window; + mode->prob = quant_prob_alloc(mode); + */ +} +#endif + + +void celt_decoder_destroy(CELTDecoder *st) +{ + if (st == NULL) + { + celt_warning("NULL passed to celt_decoder_destroy"); + return; + } + + if (st->marker == DECODERFREED) + { + celt_warning("Freeing a decoder which has already been freed"); + return; + } + + if (st->marker != DECODERVALID && st->marker != DECODERPARTIAL) + { + celt_warning("This is not a valid CELT decoder structure"); + return; + } + + /*Check_mode is non-fatal here because we can still free + the encoder memory even if the mode is bad, although calling + the free functions in this order is a violation of the API.*/ + check_mode(st->mode); + + celt_free(st->decode_mem); + celt_free(st->oldBandE); + celt_free(st->preemph_memD); + + st->marker = DECODERFREED; + + celt_free(st); +} + +/** Handles lost packets by just copying past data with the same + offset as the last + pitch period */ +#ifdef NEW_PLC +#include "plc.c" +#else +static void celt_decode_lost(CELTDecoder * celt_restrict st, celt_word16_t * celt_restrict pcm) +{ + int c, N; + int pitch_index; + int i, len; + VARDECL(celt_sig_t, freq); + const int C = CHANNELS(st->mode); + int offset; + SAVE_STACK; + N = st->block_size; + ALLOC(freq,C*N, celt_sig_t); /**< Interleaved signal MDCTs */ + + len = N+st->mode->overlap; +#if 0 + pitch_index = st->last_pitch_index; + + /* Use the pitch MDCT as the "guessed" signal */ + compute_mdcts(st->mode, st->mode->window, st->out_mem+pitch_index*C, freq); + +#else + find_spectral_pitch(st->mode, st->mode->fft, &st->mode->psy, st->out_mem+MAX_PERIOD-len, st->out_mem, st->mode->window, NULL, len, MAX_PERIOD-len-100, &pitch_index); + pitch_index = MAX_PERIOD-len-pitch_index; + offset = MAX_PERIOD-pitch_index; + while (offset+len >= MAX_PERIOD) + offset -= pitch_index; + compute_mdcts(st->mode, 0, st->out_mem+offset*C, freq); + for (i=0;iout_mem, st->out_mem+C*N, C*(MAX_PERIOD+st->mode->overlap-N)); + /* Compute inverse MDCTs */ + compute_inv_mdcts(st->mode, 0, freq, -1, 0, st->out_mem); + + for (c=0;cout_mem[C*(MAX_PERIOD-N)+C*j+c], + preemph,st->preemph_memD[c]); + st->preemph_memD[c] = tmp; + pcm[C*j+c] = SCALEOUT(SIG2WORD16(tmp)); + } + } + RESTORE_STACK; +} +#endif + +#ifdef FIXED_POINT +int celt_decode(CELTDecoder * celt_restrict st, const unsigned char *data, int len, celt_int16_t * celt_restrict pcm) +{ +#else +int celt_decode_float(CELTDecoder * celt_restrict st, const unsigned char *data, int len, celt_sig_t * celt_restrict pcm) +{ +#endif + int i, c, N, N4; + int has_pitch, has_fold; + int pitch_index; + int bits; + ec_dec dec; + ec_byte_buffer buf; + VARDECL(celt_sig_t, freq); + VARDECL(celt_norm_t, X); + VARDECL(celt_norm_t, P); + VARDECL(celt_ener_t, bandE); + VARDECL(celt_pgain_t, gains); + VARDECL(int, fine_quant); + VARDECL(int, pulses); + VARDECL(int, offsets); + VARDECL(int, fine_priority); + + int shortBlocks; + int intra_ener; + int transient_time; + int transient_shift; + int mdct_weight_shift=0; + const int C = CHANNELS(st->mode); + int mdct_weight_pos=0; + SAVE_STACK; + + if (check_decoder(st) != CELT_OK) + return CELT_INVALID_STATE; + + if (check_mode(st->mode) != CELT_OK) + return CELT_INVALID_MODE; + + if (pcm==NULL) + return CELT_BAD_ARG; + + N = st->block_size; + N4 = (N-st->overlap)>>1; + + ALLOC(freq, C*N, celt_sig_t); /**< Interleaved signal MDCTs */ + ALLOC(X, C*N, celt_norm_t); /**< Interleaved normalised MDCTs */ + ALLOC(P, C*N, celt_norm_t); /**< Interleaved normalised pitch MDCTs*/ + ALLOC(bandE, st->mode->nbEBands*C, celt_ener_t); + ALLOC(gains, st->mode->nbPBands, celt_pgain_t); + + if (data == NULL) + { + celt_decode_lost(st, pcm); + RESTORE_STACK; + return 0; + } + if (len<0) { + RESTORE_STACK; + return CELT_BAD_ARG; + } + + ec_byte_readinit(&buf,(unsigned char*)data,len); + ec_dec_init(&dec,&buf); + + decode_flags(&dec, &intra_ener, &has_pitch, &shortBlocks, &has_fold); + if (shortBlocks) + { + transient_shift = ec_dec_bits(&dec, 2); + if (transient_shift == 3) + { + transient_time = ec_dec_uint(&dec, N+st->mode->overlap); + } else { + mdct_weight_shift = transient_shift; + if (mdct_weight_shift && st->mode->nbShortMdcts>2) + mdct_weight_pos = ec_dec_uint(&dec, st->mode->nbShortMdcts-1); + transient_shift = 0; + transient_time = 0; + } + } else { + transient_time = -1; + transient_shift = 0; + } + + if (has_pitch) + { + pitch_index = ec_dec_uint(&dec, MAX_PERIOD-(2*N-2*N4)); + st->last_pitch_index = pitch_index; + } else { + pitch_index = 0; + for (i=0;imode->nbPBands;i++) + gains[i] = 0; + } + + ALLOC(fine_quant, st->mode->nbEBands, int); + /* Get band energies */ + unquant_coarse_energy(st->mode, bandE, st->oldBandE, len*8/3, intra_ener, st->mode->prob, &dec); + + ALLOC(pulses, st->mode->nbEBands, int); + ALLOC(offsets, st->mode->nbEBands, int); + ALLOC(fine_priority, st->mode->nbEBands, int); + + for (i=0;imode->nbEBands;i++) + offsets[i] = 0; + + bits = len*8 - ec_dec_tell(&dec, 0) - 1; + if (has_pitch) + bits -= st->mode->nbPBands; + compute_allocation(st->mode, offsets, bits, pulses, fine_quant, fine_priority); + /*bits = ec_dec_tell(&dec, 0); + compute_fine_allocation(st->mode, fine_quant, (20*C+len*8/5-(ec_dec_tell(&dec, 0)-bits))/C);*/ + + unquant_fine_energy(st->mode, bandE, st->oldBandE, fine_quant, &dec); + + if (has_pitch) + { + VARDECL(celt_ener_t, bandEp); + + /* Pitch MDCT */ + compute_mdcts(st->mode, 0, st->out_mem+pitch_index*C, freq); + ALLOC(bandEp, st->mode->nbEBands*C, celt_ener_t); + compute_band_energies(st->mode, freq, bandEp); + normalise_bands(st->mode, freq, P, bandEp); + /* Apply pitch gains */ + } else { + for (i=0;imode, X, P, has_pitch, gains, bandE, pulses, shortBlocks, has_fold, len*8, &dec); +#ifndef DISABLE_STEREO + else + unquant_bands_stereo(st->mode, X, P, has_pitch, gains, bandE, pulses, shortBlocks, has_fold, len*8, &dec); +#endif + unquant_energy_finalise(st->mode, bandE, st->oldBandE, fine_quant, fine_priority, len*8-ec_dec_tell(&dec, 0), &dec); + + /* Synthesis */ + denormalise_bands(st->mode, X, freq, bandE); + + CELT_MOVE(st->decode_mem, st->decode_mem+C*N, C*(DECODE_BUFFER_SIZE+st->overlap-N)); + if (mdct_weight_shift) + { + int m; + for (c=0;cmode->nbShortMdcts;m++) + for (i=m+c*N;i<(c+1)*N;i+=st->mode->nbShortMdcts) +#ifdef FIXED_POINT + freq[i] = SHL32(freq[i], mdct_weight_shift); +#else + freq[i] = (1<mode, shortBlocks, freq, transient_time, transient_shift, st->out_mem); + + for (c=0;cout_mem[C*(MAX_PERIOD-N)+C*j+c], + preemph,st->preemph_memD[c]); + st->preemph_memD[c] = tmp; + pcm[C*j+c] = SCALEOUT(SIG2WORD16(tmp)); + } + } + + RESTORE_STACK; + return 0; +} + +#ifdef FIXED_POINT +#ifndef DISABLE_FLOAT_API +int celt_decode_float(CELTDecoder * celt_restrict st, const unsigned char *data, int len, float * celt_restrict pcm) +{ + int j, ret, C, N; + VARDECL(celt_int16_t, out); + + if (check_decoder(st) != CELT_OK) + return CELT_INVALID_STATE; + + if (check_mode(st->mode) != CELT_OK) + return CELT_INVALID_MODE; + + if (pcm==NULL) + return CELT_BAD_ARG; + + SAVE_STACK; + C = CHANNELS(st->mode); + N = st->block_size; + + ALLOC(out, C*N, celt_int16_t); + ret=celt_decode(st, data, len, out); + for (j=0;jmode) != CELT_OK) + return CELT_INVALID_MODE; + + if (pcm==NULL) + return CELT_BAD_ARG; + + { + SAVE_STACK; + C = CHANNELS(st->mode); + N = st->block_size; + ALLOC(out, C*N, celt_sig_t); + + ret=celt_decode_float(st, data, len, out); + + for (j=0;jmode) != CELT_OK)) + goto bad_mode; + switch (request) + { + case CELT_GET_MODE_REQUEST: + { + const CELTMode ** value = va_arg(ap, const CELTMode**); + if (value==0) + goto bad_arg; + *value=st->mode; + } + break; + case CELT_RESET_STATE: + { + const CELTMode *mode = st->mode; + int C = mode->nbChannels; + + CELT_MEMSET(st->decode_mem, 0, (DECODE_BUFFER_SIZE+st->overlap)*C); + CELT_MEMSET(st->oldBandE, 0, C*mode->nbEBands); + + CELT_MEMSET(st->preemph_memD, 0, C); + + st->last_pitch_index = 0; + } + break; + default: + goto bad_request; + } + va_end(ap); + return CELT_OK; +bad_mode: + va_end(ap); + return CELT_INVALID_MODE; +bad_arg: + va_end(ap); + return CELT_BAD_ARG; +bad_request: + va_end(ap); + return CELT_UNIMPLEMENTED; +} diff --git a/lib/libcelt/celt.h b/lib/libcelt/celt.h new file mode 100755 index 0000000..4a1ef90 --- /dev/null +++ b/lib/libcelt/celt.h @@ -0,0 +1,284 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO + (C) 2008 Gregory Maxwell */ +/** + @file celt.h + @brief Contains all the functions for encoding and decoding audio + */ + +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CELT_H +#define CELT_H + +#include "celt_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) && defined(CELT_BUILD) +#define EXPORT __attribute__ ((visibility ("default"))) +#elif defined(WIN32) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +#define _celt_check_int(x) (((void)((x) == (celt_int32_t)0)), (celt_int32_t)(x)) +#define _celt_check_mode_ptr_ptr(ptr) ((ptr) + ((ptr) - (CELTMode**)(ptr))) + +/* Error codes */ +/** No error */ +#define CELT_OK 0 +/** An (or more) invalid argument (e.g. out of range) */ +#define CELT_BAD_ARG -1 +/** The mode struct passed is invalid */ +#define CELT_INVALID_MODE -2 +/** An internal error was detected */ +#define CELT_INTERNAL_ERROR -3 +/** The data passed (e.g. compressed data to decoder) is corrupted */ +#define CELT_CORRUPTED_DATA -4 +/** Invalid/unsupported request number */ +#define CELT_UNIMPLEMENTED -5 +/** An encoder or decoder structure is invalid or already freed */ +#define CELT_INVALID_STATE -6 + +/* Requests */ +#define CELT_GET_MODE_REQUEST 1 +/** Get the CELTMode used by an encoder or decoder */ +#define CELT_GET_MODE(x) CELT_GET_MODE_REQUEST, _celt_check_mode_ptr_ptr(x) +#define CELT_SET_COMPLEXITY_REQUEST 2 +/** Controls the complexity from 0-10 (int) */ +#define CELT_SET_COMPLEXITY(x) CELT_SET_COMPLEXITY_REQUEST, _celt_check_int(x) +#define CELT_SET_PREDICTION_REQUEST 4 +/** Controls the use of interframe prediction. + 0=Independent frames + 1=Short term interframe prediction allowed + 2=Long term prediction allowed + */ +#define CELT_SET_PREDICTION(x) CELT_SET_PREDICTION_REQUEST, _celt_check_int(x) +#define CELT_SET_VBR_RATE_REQUEST 6 +/** Set the target VBR rate in bits per second(int); 0=CBR (default) */ +#define CELT_SET_VBR_RATE(x) CELT_SET_VBR_RATE_REQUEST, _celt_check_int(x) +/** Reset the encoder/decoder memories to zero*/ +#define CELT_RESET_STATE_REQUEST 8 +#define CELT_RESET_STATE CELT_RESET_STATE_REQUEST + +/** GET the frame size used in the current mode */ +#define CELT_GET_FRAME_SIZE 1000 +/** GET the lookahead used in the current mode */ +#define CELT_GET_LOOKAHEAD 1001 +/** GET the number of channels used in the current mode */ +#define CELT_GET_NB_CHANNELS 1002 +/** GET the sample rate used in the current mode */ +#define CELT_GET_SAMPLE_RATE 1003 + +/** GET the bit-stream version for compatibility check */ +#define CELT_GET_BITSTREAM_VERSION 2000 + + +/** Contains the state of an encoder. One encoder state is needed + for each stream. It is initialised once at the beginning of the + stream. Do *not* re-initialise the state for every frame. + @brief Encoder state + */ +typedef struct CELTEncoder CELTEncoder; + +/** State of the decoder. One decoder state is needed for each stream. + It is initialised once at the beginning of the stream. Do *not* + re-initialise the state for every frame */ +typedef struct CELTDecoder CELTDecoder; + +/** The mode contains all the information necessary to create an + encoder. Both the encoder and decoder need to be initialised + with exactly the same mode, otherwise the quality will be very + bad */ +typedef struct CELTMode CELTMode; + + +/** \defgroup codec Encoding and decoding */ +/* @{ */ + +/* Mode calls */ + +/** Creates a new mode struct. This will be passed to an encoder or + decoder. The mode MUST NOT BE DESTROYED until the encoders and + decoders that use it are destroyed as well. + @param Fs Sampling rate (32000 to 96000 Hz) + @param channels Number of channels + @param frame_size Number of samples (per channel) to encode in each + packet (even values; 64 - 512) + @param error Returned error code (if NULL, no error will be returned) + @return A newly created mode +*/ +EXPORT CELTMode *celt_mode_create(celt_int32_t Fs, int channels, int frame_size, int *error); + +/** Destroys a mode struct. Only call this after all encoders and + decoders using this mode are destroyed as well. + @param mode Mode to be destroyed +*/ +EXPORT void celt_mode_destroy(CELTMode *mode); + +/** Query information from a mode */ +EXPORT int celt_mode_info(const CELTMode *mode, int request, celt_int32_t *value); + +/* Encoder stuff */ + + +/** Creates a new encoder state. Each stream needs its own encoder + state (can't be shared across simultaneous streams). + @param mode Contains all the information about the characteristics of + * the stream (must be the same characteristics as used for the + * decoder) + @return Newly created encoder state. +*/ +EXPORT CELTEncoder *celt_encoder_create(const CELTMode *mode); + +/** Destroys a an encoder state. + @param st Encoder state to be destroyed + */ +EXPORT void celt_encoder_destroy(CELTEncoder *st); + +/** Encodes a frame of audio. + @param st Encoder state + @param pcm PCM audio in float format, with a normal range of ±1.0. + * Samples with a range beyond ±1.0 are supported but will + * be clipped by decoders using the integer API and should + * only be used if it is known that the far end supports + * extended dynmaic range. There must be exactly + * frame_size samples per channel. + @param optional_synthesis If not NULL, the encoder copies the audio signal that + * the decoder would decode. It is the same as calling the + * decoder on the compressed data, just faster. + * This may alias pcm. + @param compressed The compressed data is written here. This may not alias pcm or + * optional_synthesis. + @param nbCompressedBytes Maximum number of bytes to use for compressing the frame + * (can change from one frame to another) + @return Number of bytes written to "compressed". Will be the same as + * "nbCompressedBytes" unless the stream is VBR and will never be larger. + * If negative, an error has occurred (see error codes). It is IMPORTANT that + * the length returned be somehow transmitted to the decoder. Otherwise, no + * decoding is possible. +*/ +EXPORT int celt_encode_float(CELTEncoder *st, const float *pcm, float *optional_synthesis, unsigned char *compressed, int nbCompressedBytes); + +/** Encodes a frame of audio. + @param st Encoder state + @param pcm PCM audio in signed 16-bit format (native endian). There must be + * exactly frame_size samples per channel. + @param optional_synthesis If not NULL, the encoder copies the audio signal that + * the decoder would decode. It is the same as calling the + * decoder on the compressed data, just faster. + * This may alias pcm. + @param compressed The compressed data is written here. This may not alias pcm or + * optional_synthesis. + @param nbCompressedBytes Maximum number of bytes to use for compressing the frame + * (can change from one frame to another) + @return Number of bytes written to "compressed". Will be the same as + * "nbCompressedBytes" unless the stream is VBR and will never be larger. + * If negative, an error has occurred (see error codes). It is IMPORTANT that + * the length returned be somehow transmitted to the decoder. Otherwise, no + * decoding is possible. + */ +EXPORT int celt_encode(CELTEncoder *st, const celt_int16_t *pcm, celt_int16_t *optional_synthesis, unsigned char *compressed, int nbCompressedBytes); + +/** Query and set encoder parameters + @param st Encoder state + @param request Parameter to change or query + @param value Pointer to a 32-bit int value + @return Error code +*/ +EXPORT int celt_encoder_ctl(CELTEncoder * st, int request, ...); + +/* Decoder stuff */ + + +/** Creates a new decoder state. Each stream needs its own decoder state (can't + be shared across simultaneous streams). + @param mode Contains all the information about the characteristics of the + stream (must be the same characteristics as used for the encoder) + @return Newly created decoder state. + */ +EXPORT CELTDecoder *celt_decoder_create(const CELTMode *mode); + +/** CPS - Create CELTDecoder with no mode stuff set. celt_decoder_setmode + must be called before use. (FMOD) +*/ +EXPORT CELTDecoder *fmod_celt_decoder_create_only(char *membuffer); + +/** CPS - Set the decoder mode after it has been created. +*/ +EXPORT void fmod_celt_decoder_setmode(CELTDecoder *st, CELTMode *mode); + +/** Destroys a a decoder state. + @param st Decoder state to be destroyed + */ +EXPORT void celt_decoder_destroy(CELTDecoder *st); + +/** Decodes a frame of audio. + @param st Decoder state + @param data Compressed data produced by an encoder + @param len Number of bytes to read from "data". This MUST be exactly the number + of bytes returned by the encoder. Using a larger value WILL NOT WORK. + @param pcm One frame (frame_size samples per channel) of decoded PCM will be + returned here in float format. + @return Error code. + */ +EXPORT int celt_decode_float(CELTDecoder * celt_restrict st, const unsigned char *data, int len, float * celt_restrict pcm); + +/** Decodes a frame of audio. + @param st Decoder state + @param data Compressed data produced by an encoder + @param len Number of bytes to read from "data". This MUST be exactly the number + of bytes returned by the encoder. Using a larger value WILL NOT WORK. + @param pcm One frame (frame_size samples per channel) of decoded PCM will be + returned here in 16-bit PCM format (native endian). + @return Error code. + */ +EXPORT int celt_decode(CELTDecoder * celt_restrict st, const unsigned char *data, int len, celt_int16_t * celt_restrict pcm); + +/** Query and set decoder parameters + @param st Decoder state + @param request Parameter to change or query + @param value Pointer to a 32-bit int value + @return Error code + */ +EXPORT int celt_decoder_ctl(CELTDecoder * celt_restrict st, int request, ...); + + +/* @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /*CELT_H */ diff --git a/lib/libcelt/celt_header.h b/lib/libcelt/celt_header.h new file mode 100755 index 0000000..78a528e --- /dev/null +++ b/lib/libcelt/celt_header.h @@ -0,0 +1,69 @@ +/* (C) 2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CELT_HEADER_H +#define CELT_HEADER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "celt.h" +#include "celt_types.h" + +/** Header data to be used for Ogg files (or possibly other encapsulation) + @brief Header data + */ +typedef struct { + char codec_id[8]; /**< MUST be "CELT " (four spaces) */ + char codec_version[20]; /**< Version used (as string) */ + celt_int32_t version_id; /**< Version id (negative for until stream is frozen) */ + celt_int32_t header_size; /**< Size of this header */ + celt_int32_t sample_rate; /**< Sampling rate of the original audio */ + celt_int32_t nb_channels; /**< Number of channels */ + celt_int32_t frame_size; /**< Samples per frame (per channel) */ + celt_int32_t overlap; /**< Overlapping samples (per channel) */ + celt_int32_t bytes_per_packet; /**< Number of bytes per compressed packet (0 if unknown) */ + celt_int32_t extra_headers; /**< Number of additional headers that follow this header */ +} CELTHeader; + +/** Creates a basic header struct */ +EXPORT int celt_header_init(CELTHeader *header, const CELTMode *m); + +EXPORT int celt_header_to_packet(const CELTHeader *header, unsigned char *packet, celt_uint32_t size); + +EXPORT int celt_header_from_packet(const unsigned char *packet, celt_uint32_t size, CELTHeader *header); + +#ifdef __cplusplus +} +#endif + +#endif /* CELT_HEADER_H */ diff --git a/lib/libcelt/celt_types.h b/lib/libcelt/celt_types.h new file mode 100755 index 0000000..fda63ef --- /dev/null +++ b/lib/libcelt/celt_types.h @@ -0,0 +1,178 @@ +/* celt_types.h taken from libogg */ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h 7524 2004-08-11 04:20:36Z conrad $ + + ********************************************************************/ +/** + @file celt_types.h + @brief CELT types +*/ +#ifndef _CELT_TYPES_H +#define _CELT_TYPES_H + +#include "../../src/fmod_types.h" + +/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */ +#if (defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H)) +#include + + typedef int16_t celt_int16_t; + typedef uint16_t celt_uint16_t; + typedef int32_t celt_int32_t; + typedef uint32_t celt_uint32_t; + typedef int64_t celt_int64_t; + typedef uint64_t celt_uint64_t; + + #define celt_restrict __restrict__ + +#elif defined(_WIN32) + +# if defined(__CYGWIN__) +# include <_G_config.h> + typedef _G_int32_t celt_int32_t; + typedef _G_uint32_t celt_uint32_t; + typedef _G_int16_t celt_int16_t; + typedef _G_uint16_t celt_uint16_t; + typedef _G_int64_t celt_int64_t; + typedef _G_uint64_t celt_uint64_t; +# elif defined(__MINGW32__) + typedef short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef long long celt_int64_t; + typedef unsigned long long celt_uint64_t; +# elif defined(__MWERKS__) + typedef int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef long long celt_int64_t; + typedef unsigned long long celt_uint64_t; +# else + /* MSVC/Borland */ + typedef __int32 celt_int32_t; + typedef unsigned __int32 celt_uint32_t; + typedef __int16 celt_int16_t; + typedef unsigned __int16 celt_uint16_t; + typedef __int64 celt_int64_t; + typedef unsigned __int64 celt_uint64_t; + + #define celt_restrict __restrict +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 celt_int16_t; + typedef UInt16 celt_uint16_t; + typedef SInt32 celt_int32_t; + typedef UInt32 celt_uint32_t; + typedef SInt64 celt_int64_t; + typedef UInt64 celt_uint64_t; + +#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ + +# include + typedef int16_t celt_int16_t; + typedef u_int16_t celt_uint16_t; + typedef int32_t celt_int32_t; + typedef u_int32_t celt_uint32_t; + typedef int64_t celt_int64_t; + typedef u_int64_t celt_uint64_t; + + #define celt_restrict __restrict__ + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t celt_int16_t; + typedef u_int16_t celt_uint16_t; + typedef int32_t celt_int32_t; + typedef u_int32_t celt_uint32_t; + typedef int64_t celt_int64_t; + typedef u_int64_t celt_uint64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef long long celt_int64_t; + typedef unsigned long long celt_uint64_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short celt_int16_t; + typedef int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef long long celt_int64_t; + typedef unsigned long long celt_uint64_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef int celt_int32_t; + typedef unsigned celt_uint32_t; + typedef short celt_int16_t; + typedef long celt_int64_t; + typedef unsigned long celt_uint64_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef signed int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef long long int celt_int64_t; + typedef unsigned long long int celt_uint64_t; + +#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + + typedef short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef long celt_int32_t; + typedef unsigned long celt_uint32_t; + typedef long long celt_int64_t; + typedef unsigned long long celt_uint64_t; + +#elif defined(CONFIG_TI_C6X) + + typedef short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef long long int celt_int64_t; + typedef unsigned long long int celt_uint64_t; + +#else + + /* Give up, take a reasonable guess */ + typedef short celt_int16_t; + typedef unsigned short celt_uint16_t; + typedef int celt_int32_t; + typedef unsigned int celt_uint32_t; + typedef long long celt_int64_t; + typedef unsigned long long celt_uint64_t; + + #define celt_restrict __restrict__ +#endif + +#endif /* _CELT_TYPES_H */ diff --git a/lib/libcelt/cwrs.c b/lib/libcelt/cwrs.c new file mode 100755 index 0000000..9a3f329 --- /dev/null +++ b/lib/libcelt/cwrs.c @@ -0,0 +1,923 @@ +/* (C) 2007-2008 Timothy B. Terriberry + (C) 2008 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "os_support.h" +#include +#include +#include "cwrs.h" +#include "mathops.h" +#include "arch.h" + +/*Guaranteed to return a conservatively large estimate of the binary logarithm + with frac bits of fractional precision. + Tested for all possible 32-bit inputs with frac=4, where the maximum + overestimation is 0.06254243 bits.*/ +int log2_frac(ec_uint32 val, int frac) +{ + int l; + l=EC_ILOG(val); + if(val&val-1){ + /*This is (val>>l-16), but guaranteed to round up, even if adding a bias + before the shift would cause overflow (e.g., for 0xFFFFxxxx).*/ + if(l>16)val=(val>>l-16)+((val&(1<>l-16); + else val<<=16-l; + l=l-1<>16); + l+=b<>b; + val=val*val+0x7FFF>>15; + } + while(frac-->0); + /*If val is not exactly 0x8000, then we have to round up the remainder.*/ + return l+(val>0x8000); + } + /*Exact powers of two require no rounding.*/ + else return l-1<0); + shift=EC_ILOG(_d^_d-1); + celt_assert(_d<=256); + inv=INV_TABLE[_d-1>>shift]; + shift--; + one=1<>shift)-(_c>>shift)+ + (_a*(_b&mask)+one-(_c&mask)>>shift)-1)*inv&MASK32; +} + +/*Compute floor(sqrt(_val)) with exact arithmetic. + This has been tested on all possible 32-bit inputs.*/ +static unsigned isqrt32(celt_uint32_t _val){ + unsigned b; + unsigned g; + int bshift; + /*Uses the second method from + http://www.azillionmonkeys.com/qed/sqroot.html + The main idea is to search for the largest binary digit b such that + (g+b)*(g+b) <= _val, and add it to the solution g.*/ + g=0; + bshift=EC_ILOG(_val)-1>>1; + b=1U<>=1; + bshift--; + } + while(bshift>=0); + return g; +} + +#if 0 +/*Compute floor(sqrt(_val)) with exact arithmetic. + This has been tested on all possible 36-bit inputs.*/ +static celt_uint32_t isqrt36(celt_uint64_t _val){ + celt_uint32_t val32; + celt_uint32_t b; + celt_uint32_t g; + int bshift; + g=0; + b=0x20000; + for(bshift=18;bshift-->13;){ + celt_uint64_t t; + t=((celt_uint64_t)g<<1)+b<>=1; + } + val32=(celt_uint32_t)_val; + for(;bshift>=0;bshift--){ + celt_uint32_t t; + t=(g<<1)+b<>=1; + } + return g; +} +#endif + +/*Although derived separately, the pulse vector coding scheme is equivalent to + a Pyramid Vector Quantizer \cite{Fis86}. + Some additional notes about an early version appear at + http://people.xiph.org/~tterribe/notes/cwrs.html, but the codebook ordering + and the definitions of some terms have evolved since that was written. + + The conversion from a pulse vector to an integer index (encoding) and back + (decoding) is governed by two related functions, V(N,K) and U(N,K). + + V(N,K) = the number of combinations, with replacement, of N items, taken K + at a time, when a sign bit is added to each item taken at least once (i.e., + the number of N-dimensional unit pulse vectors with K pulses). + One way to compute this is via + V(N,K) = K>0 ? sum(k=1...K,2**k*choose(N,k)*choose(K-1,k-1)) : 1, + where choose() is the binomial function. + A table of values for N<10 and K<10 looks like: + V[10][10] = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + {1, 4, 8, 12, 16, 20, 24, 28, 32, 36}, + {1, 6, 18, 38, 66, 102, 146, 198, 258, 326}, + {1, 8, 32, 88, 192, 360, 608, 952, 1408, 1992}, + {1, 10, 50, 170, 450, 1002, 1970, 3530, 5890, 9290}, + {1, 12, 72, 292, 912, 2364, 5336, 10836, 20256, 35436}, + {1, 14, 98, 462, 1666, 4942, 12642, 28814, 59906, 115598}, + {1, 16, 128, 688, 2816, 9424, 27008, 68464, 157184, 332688}, + {1, 18, 162, 978, 4482, 16722, 53154, 148626, 374274, 864146} + }; + + U(N,K) = the number of such combinations wherein N-1 objects are taken at + most K-1 at a time. + This is given by + U(N,K) = sum(k=0...K-1,V(N-1,k)) + = K>0 ? (V(N-1,K-1) + V(N,K-1))/2 : 0. + The latter expression also makes clear that U(N,K) is half the number of such + combinations wherein the first object is taken at least once. + Although it may not be clear from either of these definitions, U(N,K) is the + natural function to work with when enumerating the pulse vector codebooks, + not V(N,K). + U(N,K) is not well-defined for N=0, but with the extension + U(0,K) = K>0 ? 0 : 1, + the function becomes symmetric: U(N,K) = U(K,N), with a similar table: + U[10][10] = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 3, 5, 7, 9, 11, 13, 15, 17}, + {0, 1, 5, 13, 25, 41, 61, 85, 113, 145}, + {0, 1, 7, 25, 63, 129, 231, 377, 575, 833}, + {0, 1, 9, 41, 129, 321, 681, 1289, 2241, 3649}, + {0, 1, 11, 61, 231, 681, 1683, 3653, 7183, 13073}, + {0, 1, 13, 85, 377, 1289, 3653, 8989, 19825, 40081}, + {0, 1, 15, 113, 575, 2241, 7183, 19825, 48639, 108545}, + {0, 1, 17, 145, 833, 3649, 13073, 40081, 108545, 265729} + }; + + With this extension, V(N,K) may be written in terms of U(N,K): + V(N,K) = U(N,K) + U(N,K+1) + for all N>=0, K>=0. + Thus U(N,K+1) represents the number of combinations where the first element + is positive or zero, and U(N,K) represents the number of combinations where + it is negative. + With a large enough table of U(N,K) values, we could write O(N) encoding + and O(min(N*log(K),N+K)) decoding routines, but such a table would be + prohibitively large for small embedded devices (K may be as large as 32767 + for small N, and N may be as large as 200). + + Both functions obey the same recurrence relation: + V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1), + U(N,K) = U(N-1,K) + U(N,K-1) + U(N-1,K-1), + for all N>0, K>0, with different initial conditions at N=0 or K=0. + This allows us to construct a row of one of the tables above given the + previous row or the next row. + Thus we can derive O(NK) encoding and decoding routines with O(K) memory + using only addition and subtraction. + + When encoding, we build up from the U(2,K) row and work our way forwards. + When decoding, we need to start at the U(N,K) row and work our way backwards, + which requires a means of computing U(N,K). + U(N,K) may be computed from two previous values with the same N: + U(N,K) = ((2*N-1)*U(N,K-1) - U(N,K-2))/(K-1) + U(N,K-2) + for all N>1, and since U(N,K) is symmetric, a similar relation holds for two + previous values with the same K: + U(N,K>1) = ((2*K-1)*U(N-1,K) - U(N-2,K))/(N-1) + U(N-2,K) + for all K>1. + This allows us to construct an arbitrary row of the U(N,K) table by starting + with the first two values, which are constants. + This saves roughly 2/3 the work in our O(NK) decoding routine, but costs O(K) + multiplications. + Similar relations can be derived for V(N,K), but are not used here. + + For N>0 and K>0, U(N,K) and V(N,K) take on the form of an (N-1)-degree + polynomial for fixed N. + The first few are + U(1,K) = 1, + U(2,K) = 2*K-1, + U(3,K) = (2*K-2)*K+1, + U(4,K) = (((4*K-6)*K+8)*K-3)/3, + U(5,K) = ((((2*K-4)*K+10)*K-8)*K+3)/3, + and + V(1,K) = 2, + V(2,K) = 4*K, + V(3,K) = 4*K*K+2, + V(4,K) = 8*(K*K+2)*K/3, + V(5,K) = ((4*K*K+20)*K*K+6)/3, + for all K>0. + This allows us to derive O(N) encoding and O(N*log(K)) decoding routines for + small N (and indeed decoding is also O(N) for N<3). + + @ARTICLE{Fis86, + author="Thomas R. Fischer", + title="A Pyramid Vector Quantizer", + journal="IEEE Transactions on Information Theory", + volume="IT-32", + number=4, + pages="568--583", + month=Jul, + year=1986 + }*/ + +/*Determines if V(N,K) fits in a 32-bit unsigned integer. + N and K are themselves limited to 15 bits.*/ +int fits_in32(int _n, int _k) +{ + static const celt_int16_t maxN[15] = { + 32767, 32767, 32767, 1476, 283, 109, 60, 40, + 29, 24, 20, 18, 16, 14, 13}; + static const celt_int16_t maxK[15] = { + 32767, 32767, 32767, 32767, 1172, 238, 95, 53, + 36, 27, 22, 18, 16, 15, 13}; + if (_n>=14) + { + if (_k>=14) + return 0; + else + return _n <= maxN[_k]; + } else { + return _k <= maxK[_n]; + } +} + +/*Compute U(1,_k).*/ +static FMOD_INLINE unsigned ucwrs1(int _k){ + return _k?1:0; +} + +/*Compute V(1,_k).*/ +static FMOD_INLINE unsigned ncwrs1(int _k){ + return _k?2:1; +} + +/*Compute U(2,_k). + Note that this may be called with _k=32768 (maxK[2]+1).*/ +static FMOD_INLINE unsigned ucwrs2(unsigned _k){ + return _k?_k+(_k-1):0; +} + +/*Compute V(2,_k).*/ +static FMOD_INLINE celt_uint32_t ncwrs2(int _k){ + return _k?4*(celt_uint32_t)_k:1; +} + +/*Compute U(3,_k). + Note that this may be called with _k=32768 (maxK[3]+1).*/ +static FMOD_INLINE celt_uint32_t ucwrs3(unsigned _k){ + return _k?(2*(celt_uint32_t)_k-2)*_k+1:0; +} + +/*Compute V(3,_k).*/ +static FMOD_INLINE celt_uint32_t ncwrs3(int _k){ + return _k?2*(2*(unsigned)_k*(celt_uint32_t)_k+1):1; +} + +/*Compute U(4,_k).*/ +static FMOD_INLINE celt_uint32_t ucwrs4(int _k){ + return _k?imusdiv32odd(2*_k,(2*_k-3)*(celt_uint32_t)_k+4,3,1):0; +} + +/*Compute V(4,_k).*/ +static FMOD_INLINE celt_uint32_t ncwrs4(int _k){ + return _k?((_k*(celt_uint32_t)_k+2)*_k)/3<<3:1; +} + +/*Compute U(5,_k).*/ +static FMOD_INLINE celt_uint32_t ucwrs5(int _k){ + return _k?(((((_k-2)*(unsigned)_k+5)*(celt_uint32_t)_k-4)*_k)/3<<1)+1:0; +} + +/*Compute V(5,_k).*/ +static FMOD_INLINE celt_uint32_t ncwrs5(int _k){ + return _k?(((_k*(unsigned)_k+5)*(celt_uint32_t)_k*_k)/3<<2)+2:1; +} + +/*Computes the next row/column of any recurrence that obeys the relation + u[i][j]=u[i-1][j]+u[i][j-1]+u[i-1][j-1]. + _ui0 is the base case for the new row/column.*/ +static FMOD_INLINE void unext(celt_uint32_t *_ui,unsigned _len,celt_uint32_t _ui0){ + celt_uint32_t ui1; + unsigned j; + /*This do-while will overrun the array if we don't have storage for at least + 2 values.*/ + j=1; do { + ui1=UADD32(UADD32(_ui[j],_ui[j-1]),_ui0); + _ui[j-1]=_ui0; + _ui0=ui1; + } while (++j<_len); + _ui[j-1]=_ui0; +} + +/*Computes the previous row/column of any recurrence that obeys the relation + u[i-1][j]=u[i][j]-u[i][j-1]-u[i-1][j-1]. + _ui0 is the base case for the new row/column.*/ +static FMOD_INLINE void uprev(celt_uint32_t *_ui,unsigned _n,celt_uint32_t _ui0){ + celt_uint32_t ui1; + unsigned j; + /*This do-while will overrun the array if we don't have storage for at least + 2 values.*/ + j=1; do { + ui1=USUB32(USUB32(_ui[j],_ui[j-1]),_ui0); + _ui[j-1]=_ui0; + _ui0=ui1; + } while (++j<_n); + _ui[j-1]=_ui0; +} + +/*Compute V(_n,_k), as well as U(_n,0..._k+1). + _u: On exit, _u[i] contains U(_n,i) for i in [0..._k+1].*/ +static celt_uint32_t ncwrs_urow(unsigned _n,unsigned _k,celt_uint32_t *_u){ + celt_uint32_t um2; + unsigned len; + unsigned k; + len=_k+2; + /*We require storage at least 3 values (e.g., _k>0).*/ + celt_assert(len>=3); + _u[0]=0; + _u[1]=um2=1; + if(_n<=6 || _k>255){ + /*If _n==0, _u[0] should be 1 and the rest should be 0.*/ + /*If _n==1, _u[i] should be 1 for i>1.*/ + celt_assert(_n>=2); + /*If _k==0, the following do-while loop will overflow the buffer.*/ + celt_assert(_k>0); + k=2; + do _u[k]=(k<<1)-1; + while(++k=len)break; + _u[k]=um1=imusdiv32odd(n2m1,um2,um1,k-1>>1)+um1; + } + } + return _u[_k]+_u[_k+1]; +} + + +/*Returns the _i'th combination of _k elements (at most 32767) chosen from a + set of size 1 with associated sign bits. + _y: Returns the vector of pulses.*/ +static FMOD_INLINE void cwrsi1(int _k,celt_uint32_t _i,int *_y){ + int s; + s=-(int)_i; + _y[0]=_k+s^s; +} + +/*Returns the _i'th combination of _k elements (at most 32767) chosen from a + set of size 2 with associated sign bits. + _y: Returns the vector of pulses.*/ +static FMOD_INLINE void cwrsi2(int _k,celt_uint32_t _i,int *_y){ + celt_uint32_t p; + int s; + int yj; + p=ucwrs2(_k+1U); + s=-(_i>=p); + _i-=p&s; + yj=_k; + _k=_i+1>>1; + p=ucwrs2(_k); + _i-=p; + yj-=_k; + _y[0]=yj+s^s; + cwrsi1(_k,_i,_y+1); +} + +/*Returns the _i'th combination of _k elements (at most 32767) chosen from a + set of size 3 with associated sign bits. + _y: Returns the vector of pulses.*/ +static void cwrsi3(int _k,celt_uint32_t _i,int *_y){ + celt_uint32_t p; + int s; + int yj; + p=ucwrs3(_k+1U); + s=-(_i>=p); + _i-=p&s; + yj=_k; + /*Finds the maximum _k such that ucwrs3(_k)<=_i (tested for all + _i<2147418113=U(3,32768)).*/ + _k=_i>0?isqrt32(2*_i-1)+1>>1:0; + p=ucwrs3(_k); + _i-=p; + yj-=_k; + _y[0]=yj+s^s; + cwrsi2(_k,_i,_y+1); +} + +/*Returns the _i'th combination of _k elements (at most 1172) chosen from a set + of size 4 with associated sign bits. + _y: Returns the vector of pulses.*/ +static void cwrsi4(int _k,celt_uint32_t _i,int *_y){ + celt_uint32_t p; + int s; + int yj; + int kl; + int kr; + p=ucwrs4(_k+1); + s=-(_i>=p); + _i-=p&s; + yj=_k; + /*We could solve a cubic for k here, but the form of the direct solution does + not lend itself well to exact integer arithmetic. + Instead we do a binary search on U(4,K).*/ + kl=0; + kr=_k; + for(;;){ + _k=kl+kr>>1; + p=ucwrs4(_k); + if(p<_i){ + if(_k>=kr)break; + kl=_k+1; + } + else if(p>_i)kr=_k-1; + else break; + } + _i-=p; + yj-=_k; + _y[0]=yj+s^s; + cwrsi3(_k,_i,_y+1); +} + +/*Returns the _i'th combination of _k elements (at most 238) chosen from a set + of size 5 with associated sign bits. + _y: Returns the vector of pulses.*/ +static void cwrsi5(int _k,celt_uint32_t _i,int *_y){ + celt_uint32_t p; + int s; + int yj; + p=ucwrs5(_k+1); + s=-(_i>=p); + _i-=p&s; + yj=_k; +#if 0 + /*Finds the maximum _k such that ucwrs5(_k)<=_i (tested for all + _i<2157192969=U(5,239)).*/ + if(_i>=0x2AAAAAA9UL)_k=isqrt32(2*isqrt36(10+6*(celt_uint64_t)_i)-7)+1>>1; + else _k=_i>0?isqrt32(2*(celt_uint32_t)isqrt32(10+6*_i)-7)+1>>1:0; + p=ucwrs5(_k); +#else + /* A binary search on U(5,K) avoids the need for 64-bit arithmetic */ + { + int kl=0; + int kr=_k; + for(;;){ + _k=kl+kr>>1; + p=ucwrs5(_k); + if(p<_i){ + if(_k>=kr)break; + kl=_k+1; + } + else if(p>_i)kr=_k-1; + else break; + } + } +#endif + _i-=p; + yj-=_k; + _y[0]=yj+s^s; + cwrsi4(_k,_i,_y+1); +} + +/*Returns the _i'th combination of _k elements chosen from a set of size _n + with associated sign bits. + _y: Returns the vector of pulses. + _u: Must contain entries [0..._k+1] of row _n of U() on input. + Its contents will be destructively modified.*/ +static void cwrsi(int _n,int _k,celt_uint32_t _i,int *_y,celt_uint32_t *_u){ + int j; + celt_assert(_n>0); + j=0; + do{ + celt_uint32_t p; + int s; + int yj; + p=_u[_k+1]; + s=-(_i>=p); + _i-=p&s; + yj=_k; + p=_u[_k]; + while(p>_i)p=_u[--_k]; + _i-=p; + yj-=_k; + _y[j]=yj+s^s; + uprev(_u,_k+2,0); + } + while(++j<_n); +} + + +/*Returns the index of the given combination of K elements chosen from a set + of size 1 with associated sign bits. + _y: The vector of pulses, whose sum of absolute values is K. + _k: Returns K.*/ +static FMOD_INLINE celt_uint32_t icwrs1(const int *_y,int *_k){ + *_k=abs(_y[0]); + return _y[0]<0; +} + +/*Returns the index of the given combination of K elements chosen from a set + of size 2 with associated sign bits. + _y: The vector of pulses, whose sum of absolute values is K. + _k: Returns K.*/ +static FMOD_INLINE celt_uint32_t icwrs2(const int *_y,int *_k){ + celt_uint32_t i; + int k; + i=icwrs1(_y+1,&k); + i+=ucwrs2(k); + k+=abs(_y[0]); + if(_y[0]<0)i+=ucwrs2(k+1U); + *_k=k; + return i; +} + +/*Returns the index of the given combination of K elements chosen from a set + of size 3 with associated sign bits. + _y: The vector of pulses, whose sum of absolute values is K. + _k: Returns K.*/ +static FMOD_INLINE celt_uint32_t icwrs3(const int *_y,int *_k){ + celt_uint32_t i; + int k; + i=icwrs2(_y+1,&k); + i+=ucwrs3(k); + k+=abs(_y[0]); + if(_y[0]<0)i+=ucwrs3(k+1U); + *_k=k; + return i; +} + +/*Returns the index of the given combination of K elements chosen from a set + of size 4 with associated sign bits. + _y: The vector of pulses, whose sum of absolute values is K. + _k: Returns K.*/ +static FMOD_INLINE celt_uint32_t icwrs4(const int *_y,int *_k){ + celt_uint32_t i; + int k; + i=icwrs3(_y+1,&k); + i+=ucwrs4(k); + k+=abs(_y[0]); + if(_y[0]<0)i+=ucwrs4(k+1); + *_k=k; + return i; +} + +/*Returns the index of the given combination of K elements chosen from a set + of size 5 with associated sign bits. + _y: The vector of pulses, whose sum of absolute values is K. + _k: Returns K.*/ +static FMOD_INLINE celt_uint32_t icwrs5(const int *_y,int *_k){ + celt_uint32_t i; + int k; + i=icwrs4(_y+1,&k); + i+=ucwrs5(k); + k+=abs(_y[0]); + if(_y[0]<0)i+=ucwrs5(k+1); + *_k=k; + return i; +} + +/*Returns the index of the given combination of K elements chosen from a set + of size _n with associated sign bits. + _y: The vector of pulses, whose sum of absolute values must be _k. + _nc: Returns V(_n,_k).*/ +celt_uint32_t icwrs(int _n,int _k,celt_uint32_t *_nc,const int *_y, + celt_uint32_t *_u){ + celt_uint32_t i; + int j; + int k; + /*We can't unroll the first two iterations of the loop unless _n>=2.*/ + celt_assert(_n>=2); + _u[0]=0; + for(k=1;k<=_k+1;k++)_u[k]=(k<<1)-1; + i=icwrs1(_y+_n-1,&k); + j=_n-2; + i+=_u[k]; + k+=abs(_y[j]); + if(_y[j]<0)i+=_u[k+1]; + while(j-->0){ + unext(_u,_k+2,0); + i+=_u[k]; + k+=abs(_y[j]); + if(_y[j]<0)i+=_u[k+1]; + } + *_nc=_u[k]+_u[k+1]; + return i; +} + + +/*Computes get_required_bits when splitting is required. + _left_bits and _right_bits must contain the required bits for the left and + right sides of the split, respectively (which themselves may require + splitting).*/ +static void get_required_split_bits(celt_int16_t *_bits, + const celt_int16_t *_left_bits,const celt_int16_t *_right_bits, + int _n,int _maxk,int _frac){ + int k; + for(k=_maxk;k-->0;){ + /*If we've reached a k where everything fits in 32 bits, evaluate the + remaining required bits directly.*/ + if(fits_in32(_n,k)){ + get_required_bits(_bits,_n,k+1,_frac); + break; + } + else{ + int worst_bits; + int i; + /*Due to potentially recursive splitting, it's difficult to derive an + analytic expression for the location of the worst-case split index. + We simply check them all.*/ + worst_bits=0; + for(i=0;i<=k;i++){ + int split_bits; + split_bits=_left_bits[i]+_right_bits[k-i]; + if(split_bits>worst_bits)worst_bits=split_bits; + } + _bits[k]=log2_frac(k+1,_frac)+worst_bits; + } + } +} + +/*Computes get_required_bits for a pair of N values. + _n1 and _n2 must either be equal or two consecutive integers. + Returns the buffer used to store the required bits for _n2, which is either + _bits1 if _n1==_n2 or _bits2 if _n1+1==_n2.*/ +static celt_int16_t *get_required_bits_pair(celt_int16_t *_bits1, + celt_int16_t *_bits2,celt_int16_t *_tmp,int _n1,int _n2,int _maxk,int _frac){ + celt_int16_t *tmp2; + /*If we only need a single set of required bits...*/ + if(_n1==_n2){ + /*Stop recursing if everything fits.*/ + if(fits_in32(_n1,_maxk-1))get_required_bits(_bits1,_n1,_maxk,_frac); + else{ + _tmp=get_required_bits_pair(_bits2,_tmp,_bits1, + _n1>>1,_n1+1>>1,_maxk,_frac); + get_required_split_bits(_bits1,_bits2,_tmp,_n1,_maxk,_frac); + } + return _bits1; + } + /*Otherwise we need two distinct sets...*/ + celt_assert(_n1+1==_n2); + /*Stop recursing if everything fits.*/ + if(fits_in32(_n2,_maxk-1)){ + get_required_bits(_bits1,_n1,_maxk,_frac); + get_required_bits(_bits2,_n2,_maxk,_frac); + } + /*Otherwise choose an evaluation order that doesn't require extra buffers.*/ + else if(_n1&1){ + /*This special case isn't really needed, but can save some work.*/ + if(fits_in32(_n1,_maxk-1)){ + tmp2=get_required_bits_pair(_tmp,_bits1,_bits2, + _n2>>1,_n2>>1,_maxk,_frac); + get_required_split_bits(_bits2,_tmp,tmp2,_n2,_maxk,_frac); + get_required_bits(_bits1,_n1,_maxk,_frac); + } + else{ + _tmp=get_required_bits_pair(_bits2,_tmp,_bits1, + _n1>>1,_n1+1>>1,_maxk,_frac); + get_required_split_bits(_bits1,_bits2,_tmp,_n1,_maxk,_frac); + get_required_split_bits(_bits2,_tmp,_tmp,_n2,_maxk,_frac); + } + } + else{ + /*There's no need to special case _n1 fitting by itself, since _n2 requires + us to recurse for both values anyway.*/ + tmp2=get_required_bits_pair(_tmp,_bits1,_bits2, + _n2>>1,_n2+1>>1,_maxk,_frac); + get_required_split_bits(_bits2,_tmp,tmp2,_n2,_maxk,_frac); + get_required_split_bits(_bits1,_tmp,_tmp,_n1,_maxk,_frac); + } + return _bits2; +} + +void get_required_bits(celt_int16_t *_bits,int _n,int _maxk,int _frac){ + int k; + /*_maxk==0 => there's nothing to do.*/ + celt_assert(_maxk>0); + if(fits_in32(_n,_maxk-1)){ + _bits[0]=0; + if(_maxk>1){ + VARDECL(celt_uint32_t,u); + SAVE_STACK; + ALLOC(u,_maxk+1U,celt_uint32_t); + ncwrs_urow(_n,_maxk-1,u); + for(k=1;k<_maxk;k++)_bits[k]=log2_frac(u[k]+u[k+1],_frac); + RESTORE_STACK; + } + } + else{ + VARDECL(celt_int16_t,n1bits); + VARDECL(celt_int16_t,n2bits_buf); + celt_int16_t *n2bits; + SAVE_STACK; + ALLOC(n1bits,_maxk,celt_int16_t); + ALLOC(n2bits_buf,_maxk,celt_int16_t); + n2bits=get_required_bits_pair(n1bits,n2bits_buf,_bits, + _n>>1,_n+1>>1,_maxk,_frac); + get_required_split_bits(_bits,n1bits,n2bits,_n,_maxk,_frac); + RESTORE_STACK; + } +} + +#ifdef FMOD_CELT_ENCODER + +static FMOD_INLINE void encode_pulses32(int _n,int _k,const int *_y,ec_enc *_enc){ + celt_uint32_t i; + switch(_n){ + case 1:{ + i=icwrs1(_y,&_k); + celt_assert(ncwrs1(_k)==2); + ec_enc_bits(_enc,i,1); + }break; + case 2:{ + i=icwrs2(_y,&_k); + ec_enc_uint(_enc,i,ncwrs2(_k)); + }break; + case 3:{ + i=icwrs3(_y,&_k); + ec_enc_uint(_enc,i,ncwrs3(_k)); + }break; + case 4:{ + i=icwrs4(_y,&_k); + ec_enc_uint(_enc,i,ncwrs4(_k)); + }break; + case 5:{ + i=icwrs5(_y,&_k); + ec_enc_uint(_enc,i,ncwrs5(_k)); + }break; + default:{ + VARDECL(celt_uint32_t,u); + celt_uint32_t nc; + SAVE_STACK; + ALLOC(u,_k+2U,celt_uint32_t); + i=icwrs(_n,_k,&nc,_y,u); + ec_enc_uint(_enc,i,nc); + RESTORE_STACK; + }break; + } +} + +void encode_pulses(int *_y, int N, int K, ec_enc *enc) +{ + if (K==0) { + } else if(fits_in32(N,K)) + { + encode_pulses32(N, K, _y, enc); + } else { + int i; + int count=0; + int split; + split = (N+1)/2; + for (i=0;i +#include +#if !defined(_ecintrin_H) +# define _ecintrin_H (1) + +/*Some specific platforms may have optimized intrinsic or inline assembly + versions of these functions which can substantially improve performance. + We define macros for them to allow easy incorporation of these non-ANSI + features.*/ + +/*Note that we do not provide a macro for abs(), because it is provided as a + library function, which we assume is translated into an intrinsic to avoid + the function call overhead and then implemented in the smartest way for the + target platform. + With modern gcc (4.x), this is true: it uses cmov instructions if the + architecture supports it and branchless bit-twiddling if it does not (the + speed difference between the two approaches is not measurable). + Interestingly, the bit-twiddling method was patented in 2000 (US 6,073,150) + by Sun Microsystems, despite prior art dating back to at least 1996: + http://web.archive.org/web/19961201174141/www.x86.org/ftp/articles/pentopt/PENTOPT.TXT + On gcc 3.x, however, our assumption is not true, as abs() is translated to a + conditional jump, which is horrible on deeply piplined architectures (e.g., + all consumer architectures for the past decade or more) when the sign cannot + be reliably predicted.*/ + +/*Modern gcc (4.x) can compile the naive versions of min and max with cmov if + given an appropriate architecture, but the branchless bit-twiddling versions + are just as fast, and do not require any special target architecture. + Earlier gcc versions (3.x) compiled both code to the same assembly + instructions, because of the way they represented ((_b)>(_a)) internally.*/ +#define EC_MAXI(_a,_b) ((_a)-((_a)-(_b)&-((_b)>(_a)))) +#define EC_MINI(_a,_b) ((_a)+((_b)-(_a)&-((_b)<(_a)))) +/*This has a chance of compiling branchless, and is just as fast as the + bit-twiddling method, which is slightly less portable, since it relies on a + sign-extended rightshift, which is not guaranteed by ANSI (but present on + every relevant platform).*/ +#define EC_SIGNI(_a) (((_a)>0)-((_a)<0)) +/*Slightly more portable than relying on a sign-extended right-shift (which is + not guaranteed by ANSI), and just as fast, since gcc (3.x and 4.x both) + compile it into the right-shift anyway.*/ +#define EC_SIGNMASK(_a) (-((_a)<0)) +/*Clamps an integer into the given range. + If _a>_c, then the lower bound _a is respected over the upper bound _c (this + behavior is required to meet our documented API behavior). + _a: The lower bound. + _b: The value to clamp. + _c: The upper boud.*/ +#define EC_CLAMPI(_a,_b,_c) (EC_MAXI(_a,EC_MINI(_b,_c))) + + +/*Count leading zeros. + This macro should only be used for implementing ec_ilog(), if it is defined. + All other code should use EC_ILOG() instead.*/ +#ifdef __GNUC_PREREQ +#if __GNUC_PREREQ(3,4) +# if INT_MAX>=2147483647 +# define EC_CLZ0 sizeof(unsigned)*CHAR_BIT +# define EC_CLZ(_x) (__builtin_clz(_x)) +# elif LONG_MAX>=2147483647L +# define EC_CLZ0 sizeof(unsigned long)*CHAR_BIT +# define EC_CLZ(_x) (__builtin_clzl(_x)) +# endif +#endif +#endif + +#if defined(EC_CLZ) +/*Note that __builtin_clz is not defined when _x==0, according to the gcc + documentation (and that of the BSR instruction that implements it on x86). + The majority of the time we can never pass it zero. + When we need to, it can be special cased.*/ +# define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x)) +#elif defined(ENABLE_TI_DSPLIB) +#include "dsplib.h" +#define EC_ILOG(x) (31 - _lnorm(x)) +#else +# define EC_ILOG(_x) (ec_ilog(_x)) +#endif + +#ifdef __GNUC_PREREQ +#if __GNUC_PREREQ(3,4) +# if INT_MAX>=9223372036854775807 +# define EC_CLZ64_0 sizeof(unsigned)*CHAR_BIT +# define EC_CLZ64(_x) (__builtin_clz(_x)) +# elif LONG_MAX>=9223372036854775807L +# define EC_CLZ64_0 sizeof(unsigned long)*CHAR_BIT +# define EC_CLZ64(_x) (__builtin_clzl(_x)) +# elif LLONG_MAX>=9223372036854775807LL +# define EC_CLZ64_0 sizeof(unsigned long long)*CHAR_BIT +# define EC_CLZ64(_x) (__builtin_clzll(_x)) +# endif +#endif +#endif + +#if defined(EC_CLZ64) +/*Note that __builtin_clz is not defined when _x==0, according to the gcc + documentation (and that of the BSR instruction that implements it on x86). + The majority of the time we can never pass it zero. + When we need to, it can be special cased.*/ +# define EC_ILOG64(_x) (EC_CLZ64_0-EC_CLZ64(_x)) +#else +# define EC_ILOG64(_x) (ec_ilog64(_x)) +#endif + +#endif diff --git a/lib/libcelt/entcode.c b/lib/libcelt/entcode.c new file mode 100755 index 0000000..ecedadf --- /dev/null +++ b/lib/libcelt/entcode.c @@ -0,0 +1,70 @@ +/* (C) 2001-2008 Timothy B. Terriberry +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "entcode.h" + + + + + + + +int ec_ilog(ec_uint32 _v){ +#if defined(EC_CLZ) + return EC_CLZ0-EC_CLZ(_v); +#else + /*On a Pentium M, this branchless version tested as the fastest on + 1,000,000,000 random 32-bit integers, edging out a similar version with + branches, and a 256-entry LUT version.*/ + int ret; + int m; + ret=!!_v; + m=!!(_v&0xFFFF0000)<<4; + _v>>=m; + ret|=m; + m=!!(_v&0xFF00)<<3; + _v>>=m; + ret|=m; + m=!!(_v&0xF0)<<2; + _v>>=m; + ret|=m; + m=!!(_v&0xC)<<1; + _v>>=m; + ret|=m; + ret+=!!(_v&0x2); + return ret; +#endif +} + diff --git a/lib/libcelt/entcode.h b/lib/libcelt/entcode.h new file mode 100755 index 0000000..7601d00 --- /dev/null +++ b/lib/libcelt/entcode.h @@ -0,0 +1,95 @@ +/* (C) 2001-2008 Timothy B. Terriberry + (C) 2008 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "celt_types.h" + +#if !defined(_entcode_H) +# define _entcode_H (1) +# include +# include "ecintrin.h" + + + +typedef celt_int32_t ec_int32; +typedef celt_uint32_t ec_uint32; +typedef celt_uint64_t ec_uint64; +typedef struct ec_byte_buffer ec_byte_buffer; + + + +/*The number of bits to code at a time when coding bits directly.*/ +# define EC_UNIT_BITS (8) +/*The mask for the given bits.*/ +# define EC_UNIT_MASK ((1U<ptr=_b->buf; +} + +static FMOD_INLINE long ec_byte_bytes(ec_byte_buffer *_b){ + return _b->ptr-_b->buf; +} + +static FMOD_INLINE unsigned char *ec_byte_get_buffer(ec_byte_buffer *_b){ + return _b->buf; +} + +int ec_ilog(ec_uint32 _v); +int ec_ilog64(ec_uint64 _v); + +#endif diff --git a/lib/libcelt/entdec.c b/lib/libcelt/entdec.c new file mode 100755 index 0000000..fb39f00 --- /dev/null +++ b/lib/libcelt/entdec.c @@ -0,0 +1,165 @@ +/* (C) 2001-2008 Timothy B. Terriberry + (C) 2008 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "entdec.h" +#include "os_support.h" +#include "arch.h" + + +void ec_byte_readinit(ec_byte_buffer *_b,unsigned char *_buf,long _bytes){ + _b->buf=_b->ptr=_buf; + _b->storage=_bytes; +} + +int ec_byte_look1(ec_byte_buffer *_b){ + ptrdiff_t endbyte; + endbyte=_b->ptr-_b->buf; + if(endbyte>=_b->storage)return -1; + else return _b->ptr[0]; +} + +int ec_byte_look4(ec_byte_buffer *_b,ec_uint32 *_val){ + ptrdiff_t endbyte; + endbyte=_b->ptr-_b->buf; + if(endbyte+4>_b->storage){ + if(endbyte<_b->storage){ + *_val=_b->ptr[0]; + endbyte++; + if(endbyte<_b->storage){ + *_val|=(ec_uint32)_b->ptr[1]<<8; + endbyte++; + if(endbyte<_b->storage)*_val|=(ec_uint32)_b->ptr[2]<<16; + } + } + return -1; + } + else{ + *_val=_b->ptr[0]; + *_val|=(ec_uint32)_b->ptr[1]<<8; + *_val|=(ec_uint32)_b->ptr[2]<<16; + *_val|=(ec_uint32)_b->ptr[3]<<24; + } + return 0; +} + +void ec_byte_adv1(ec_byte_buffer *_b){ + _b->ptr++; +} + +void ec_byte_adv4(ec_byte_buffer *_b){ + _b->ptr+=4; +} + +int ec_byte_read1(ec_byte_buffer *_b){ + ptrdiff_t endbyte; + endbyte=_b->ptr-_b->buf; + if(endbyte>=_b->storage)return -1; + else return *(_b->ptr++); +} + +int ec_byte_read4(ec_byte_buffer *_b,ec_uint32 *_val){ + unsigned char *end; + end=_b->buf+_b->storage; + if(_b->ptr+4>end){ + if(_b->ptrptr++); + if(_b->ptrptr++)<<8; + if(_b->ptrptr++)<<16; + } + } + return -1; + } + else{ + *_val=(*_b->ptr++); + *_val|=(ec_uint32)*(_b->ptr++)<<8; + *_val|=(ec_uint32)*(_b->ptr++)<<16; + *_val|=(ec_uint32)*(_b->ptr++)<<24; + } + return 0; +} + + + +ec_uint32 ec_dec_bits(ec_dec *_this,int _ftb){ + ec_uint32 t; + unsigned s; + unsigned ft; + t=0; + while(_ftb>EC_UNIT_BITS){ + s=ec_decode_bin(_this,EC_UNIT_BITS); + ec_dec_update(_this,s,s+1,EC_UNIT_MASK+1); + t=t<1); + _ft--; + ftb=EC_ILOG(_ft); + if(ftb>EC_UNIT_BITS){ + ftb-=EC_UNIT_BITS; + ft=(unsigned)(_ft>>ftb)+1; + s=ec_decode(_this,ft); + ec_dec_update(_this,s,s+1,ft); + t=t<_ft) + { + celt_notify("uint decode error"); + t = _ft; + } + return t; + } else { + _ft++; + s=ec_decode(_this,(unsigned)_ft); + ec_dec_update(_this,s,s+1,(unsigned)_ft); + t=t<ptr=_b->buf=_buf; + _b->storage=_size; + _b->resizable = 0; +} + +void ec_byte_writeinit(ec_byte_buffer *_b){ + _b->ptr=_b->buf=celt_alloc(EC_BUFFER_INCREMENT*sizeof(char)); + _b->storage=EC_BUFFER_INCREMENT; + _b->resizable = 1; +} + +void ec_byte_writetrunc(ec_byte_buffer *_b,long _bytes){ + _b->ptr=_b->buf+_bytes; +} + +void ec_byte_write1(ec_byte_buffer *_b,unsigned _value){ + ptrdiff_t endbyte; + endbyte=_b->ptr-_b->buf; + if(endbyte>=_b->storage){ + if (_b->resizable){ + _b->buf=celt_realloc(_b->buf,(_b->storage+EC_BUFFER_INCREMENT)*sizeof(char)); + _b->storage+=EC_BUFFER_INCREMENT; + _b->ptr=_b->buf+endbyte; + } else { + celt_fatal("range encoder overflow\n"); + } + } + *(_b->ptr++)=(unsigned char)_value; +} + +void ec_byte_write4(ec_byte_buffer *_b,ec_uint32 _value){ + ptrdiff_t endbyte; + endbyte=_b->ptr-_b->buf; + if(endbyte+4>_b->storage){ + if (_b->resizable){ + _b->buf=celt_realloc(_b->buf,(_b->storage+EC_BUFFER_INCREMENT)*sizeof(char)); + _b->storage+=EC_BUFFER_INCREMENT; + _b->ptr=_b->buf+endbyte; + } else { + celt_fatal("range encoder overflow\n"); + } + } + *(_b->ptr++)=(unsigned char)_value; + _value>>=8; + *(_b->ptr++)=(unsigned char)_value; + _value>>=8; + *(_b->ptr++)=(unsigned char)_value; + _value>>=8; + *(_b->ptr++)=(unsigned char)_value; +} + +void ec_byte_writecopy(ec_byte_buffer *_b,void *_source,long _bytes){ + ptrdiff_t endbyte; + endbyte=_b->ptr-_b->buf; + if(endbyte+_bytes>_b->storage){ + if (_b->resizable){ + _b->storage=endbyte+_bytes+EC_BUFFER_INCREMENT; + _b->buf=celt_realloc(_b->buf,_b->storage*sizeof(char)); + _b->ptr=_b->buf+endbyte; + } else { + celt_fatal("range encoder overflow\n"); + } + } + memmove(_b->ptr,_source,_bytes); + _b->ptr+=_bytes; +} + +void ec_byte_writeclear(ec_byte_buffer *_b){ + celt_free(_b->buf); +} + + + +void ec_enc_bits(ec_enc *_this,ec_uint32 _fl,int _ftb){ + unsigned fl; + unsigned ft; + while(_ftb>EC_UNIT_BITS){ + _ftb-=EC_UNIT_BITS; + fl=(unsigned)(_fl>>_ftb)&EC_UNIT_MASK; + ec_encode_bin(_this,fl,fl+1,EC_UNIT_BITS); + } + ft=1<<_ftb; + fl=(unsigned)_fl&ft-1; + ec_encode_bin(_this,fl,fl+1,_ftb); +} + +void ec_enc_uint(ec_enc *_this,ec_uint32 _fl,ec_uint32 _ft){ + unsigned ft; + unsigned fl; + int ftb; + /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ + celt_assert(_ft>1); + _ft--; + ftb=EC_ILOG(_ft); + if(ftb>EC_UNIT_BITS){ + ftb-=EC_UNIT_BITS; + ft=(_ft>>ftb)+1; + fl=(unsigned)(_fl>>ftb); + ec_encode(_this,fl,fl+1,ft); + ec_enc_bits(_this,_fl,ftb); + } else { + ec_encode(_this,_fl,_fl+1,_ft+1); + } +} + +#endif \ No newline at end of file diff --git a/lib/libcelt/entenc.h b/lib/libcelt/entenc.h new file mode 100755 index 0000000..4918ed4 --- /dev/null +++ b/lib/libcelt/entenc.h @@ -0,0 +1,116 @@ +/* (C) 2001-2008 Timothy B. Terriberry + (C) 2008 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if !defined(_entenc_H) +# define _entenc_H (1) +# include +# include "entcode.h" + + + +typedef struct ec_enc ec_enc; + + + +/*The entropy encoder.*/ +struct ec_enc{ + /*Buffered output.*/ + ec_byte_buffer *buf; + /*A buffered output symbol, awaiting carry propagation.*/ + int rem; + /*Number of extra carry propagating symbols.*/ + size_t ext; + /*The number of values in the current range.*/ + ec_uint32 rng; + /*The low end of the current range (inclusive).*/ + ec_uint32 low; +}; + + +/*Initializes the encoder. + _buf: The buffer to store output bytes in. + This must have already been initialized for writing and reset.*/ +void ec_enc_init(ec_enc *_this,ec_byte_buffer *_buf); +/*Encodes a symbol given its frequency information. + The frequency information must be discernable by the decoder, assuming it + has read only the previous symbols from the stream. + It is allowable to change the frequency information, or even the entire + source alphabet, so long as the decoder can tell from the context of the + previously encoded information that it is supposed to do so as well. + _fl: The cumulative frequency of all symbols that come before the one to be + encoded. + _fh: The cumulative frequency of all symbols up to and including the one to + be encoded. + Together with _fl, this defines the range [_fl,_fh) in which the + decoded value will fall. + _ft: The sum of the frequencies of all the symbols*/ +void ec_encode(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _ft); +void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned bits); +/*Encodes a sequence of raw bits in the stream. + _fl: The bits to encode. + _ftb: The number of bits to encode. + This must be at least one, and no more than 32.*/ +void ec_enc_bits(ec_enc *_this,ec_uint32 _fl,int _ftb); +/*Encodes a sequence of raw bits in the stream. + _fl: The bits to encode. + _ftb: The number of bits to encode. + This must be at least one, and no more than 64.*/ +void ec_enc_bits64(ec_enc *_this,ec_uint64 _fl,int _ftb); +/*Encodes a raw unsigned integer in the stream. + _fl: The integer to encode. + _ft: The number of integers that can be encoded (one more than the max). + This must be at least one, and no more than 2**32-1.*/ +void ec_enc_uint(ec_enc *_this,ec_uint32 _fl,ec_uint32 _ft); +/*Encodes a raw unsigned integer in the stream. + _fl: The integer to encode. + _ft: The number of integers that can be encoded (one more than the max). + This must be at least one, and no more than 2**64-1.*/ +void ec_enc_uint64(ec_enc *_this,ec_uint64 _fl,ec_uint64 _ft); + +/*Returns the number of bits "used" by the encoded symbols so far. + The actual number of bits may be larger, due to rounding to whole bytes, or + smaller, due to trailing zeros that can be stripped, so this is not an + estimate of the true packet size. + This same number can be computed by the decoder, and is suitable for making + coding decisions. + _b: The number of extra bits of precision to include. + At most 16 will be accurate. + Return: The number of bits scaled by 2**_b. + This will always be slightly larger than the exact value (e.g., all + rounding error is in the positive direction).*/ +long ec_enc_tell(ec_enc *_this,int _b); + +/*Indicates that there are no more symbols to encode. + All reamining output bytes are flushed to the output buffer. + ec_enc_init() must be called before the encoder can be used again.*/ +void ec_enc_done(ec_enc *_this); + +#endif diff --git a/lib/libcelt/fixed_c5x.h b/lib/libcelt/fixed_c5x.h new file mode 100755 index 0000000..de7a217 --- /dev/null +++ b/lib/libcelt/fixed_c5x.h @@ -0,0 +1,93 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** + @file fixed_c5x.h + @brief Fixed-point operations for the TI C5x DSP family +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FIXED_C5X_H +#define FIXED_C5X_H + +#include "dsplib.h" + +#undef IMUL32 +static inline long IMUL32(long i, long j) +{ + long ac0, ac1; + ac0 = _lmpy(i>>16,j); + ac1 = ac0 + _lmpy(i,j>>16); + return _lmpyu(i,j) + (ac1<<16); +} + +#undef MAX16 +#define MAX16(a,b) _max(a,b) + +#undef MIN16 +#define MIN16(a,b) _min(a,b) + +#undef MAX32 +#define MAX32(a,b) _lmax(a,b) + +#undef MIN32 +#define MIN32(a,b) _lmin(a,b) + +#undef VSHR32 +#define VSHR32(a, shift) _lshl(a,-(shift)) + +#undef MULT16_16_Q15 +#define MULT16_16_Q15(a,b) (_smpy(a,b)) + +#undef MULT16_16SU +#define MULT16_16SU(a,b) _lmpysu(a,b) + +#undef MULT_16_16 +#define MULT_16_16(a,b) _lmpy(a,b) + +/* FIXME: This is technically incorrect and is bound to cause problems. Is there any cleaner solution? */ +#undef MULT16_32_Q15 +#define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),(b)),15)) + + +#define celt_ilog2(x) (30 - _lnorm(x)) +#define OVERRIDE_CELT_ILOG2 + +#define celt_maxabs16(x, len) MAX16(maxval((DATA *)x, len),-minval((DATA *)x, len)) +#define OVERRIDE_CELT_MAXABS16 + +#define OVERRIDE_FIND_MAX16 +static inline int find_max16(celt_word16_t *x, int len) +{ + DATA max_corr16 = -VERY_LARGE16; + DATA pitch16 = 0; + maxvec((DATA *)x, len, &max_corr16, &pitch16); + return pitch16; +} + +#endif /* FIXED_C5X_H */ diff --git a/lib/libcelt/fixed_c6x.h b/lib/libcelt/fixed_c6x.h new file mode 100755 index 0000000..a2bcdbd --- /dev/null +++ b/lib/libcelt/fixed_c6x.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2008 CSIRO */ +/** + @file fixed_c6x.h + @brief Fixed-point operations for the TI C6x DSP family +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FIXED_C6X_H +#define FIXED_C6X_H + +#undef MULT16_16SU +#define MULT16_16SU(a,b) _mpysu(a,b) + +#undef MULT_16_16 +#define MULT_16_16(a,b) _mpy(a,b) + +#define celt_ilog2(x) (30 - _norm(x)) +#define OVERRIDE_CELT_ILOG2 + +#undef MULT16_32_Q15 +#define MULT16_32_Q15(a,b) ADD32(SHL(_mpylh(a,b),1), SHR(_mpsu(a,b),15) + +#if 0 +#include "dsplib.h" + +#undef MAX16 +#define MAX16(a,b) _max(a,b) + +#undef MIN16 +#define MIN16(a,b) _min(a,b) + +#undef MAX32 +#define MAX32(a,b) _lmax(a,b) + +#undef MIN32 +#define MIN32(a,b) _lmin(a,b) + +#undef VSHR32 +#define VSHR32(a, shift) _lshl(a,-(shift)) + +#undef MULT16_16_Q15 +#define MULT16_16_Q15(a,b) (_smpy(a,b)) + +#define celt_maxabs16(x, len) MAX16(maxval((DATA *)x, len),-minval((DATA *)x, len)) +#define OVERRIDE_CELT_MAXABS16 + +#define OVERRIDE_FIND_MAX16 +static inline int find_max16(celt_word16_t *x, int len) +{ + DATA max_corr16 = -VERY_LARGE16; + DATA pitch16 = 0; + maxvec((DATA *)x, len, &max_corr16, &pitch16); + return pitch16; +} +#endif + +#endif /* FIXED_C6X_H */ diff --git a/lib/libcelt/fixed_generic.h b/lib/libcelt/fixed_generic.h new file mode 100755 index 0000000..a273c62 --- /dev/null +++ b/lib/libcelt/fixed_generic.h @@ -0,0 +1,159 @@ +/* Copyright (C) 2003-2008 Jean-Marc Valin, CSIRO */ +/** + @file fixed_generic.h + @brief Generic fixed-point operations +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FIXED_GENERIC_H +#define FIXED_GENERIC_H + +/** Multiply a 16-bit signed value by a 16-bit unsigned value. The result is a 32-bit signed value */ +#define MULT16_16SU(a,b) ((celt_word32_t)(celt_word16_t)(a)*(celt_word32_t)(celt_uint16_t)(b)) + +/** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ +#define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) + +/** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ +#define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),((b)&0x0000ffff)),15)) + +/** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ +#define MULT32_32_Q31(a,b) ADD32(ADD32(SHL(MULT16_16(SHR((a),16),SHR((b),16)),1), SHR(MULT16_16SU(SHR((a),16),((b)&0x0000ffff)),15)), SHR(MULT16_16SU(SHR((b),16),((a)&0x0000ffff)),15)) + +/** 32x32 multiplication, followed by a 32-bit shift right. Results fits in 32 bits */ +#define MULT32_32_Q32(a,b) ADD32(ADD32(MULT16_16(SHR((a),16),SHR((b),16)), SHR(MULT16_16SU(SHR((a),16),((b)&0x0000ffff)),16)), SHR(MULT16_16SU(SHR((b),16),((a)&0x0000ffff)),16)) + +/** Compile-time conversion of float constant to 16-bit value */ +#define QCONST16(x,bits) ((celt_word16_t)(.5+(x)*(((celt_word32_t)1)<<(bits)))) +/** Compile-time conversion of float constant to 32-bit value */ +#define QCONST32(x,bits) ((celt_word32_t)(.5+(x)*(((celt_word32_t)1)<<(bits)))) + +/** Negate a 16-bit value */ +#define NEG16(x) (-(x)) +/** Negate a 32-bit value */ +#define NEG32(x) (-(x)) + +/** Change a 32-bit value into a 16-bit value. The value is assumed to fit in 16-bit, otherwise the result is undefined */ +#define EXTRACT16(x) ((celt_word16_t)(x)) +/** Change a 16-bit value into a 32-bit value */ +#define EXTEND32(x) ((celt_word32_t)(x)) + +/** Arithmetic shift-right of a 16-bit value */ +#define SHR16(a,shift) ((a) >> (shift)) +/** Arithmetic shift-left of a 16-bit value */ +#define SHL16(a,shift) ((a) << (shift)) +/** Arithmetic shift-right of a 32-bit value */ +#define SHR32(a,shift) ((a) >> (shift)) +/** Arithmetic shift-left of a 32-bit value */ +#define SHL32(a,shift) ((celt_word32_t)(a) << (shift)) + +/** 16-bit arithmetic shift right with rounding-to-nearest instead of rounding down */ +#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift)) +/** 32-bit arithmetic shift right with rounding-to-nearest instead of rounding down */ +#define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift)) +/** 32-bit arithmetic shift right where the argument can be negative */ +#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) + +/** Saturates 16-bit value to +/- a */ +#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) +/** Saturates 32-bit value to +/- a */ +#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + +/** "RAW" macros, should not be used outside of this header file */ +#define SHR(a,shift) ((a) >> (shift)) +#define SHL(a,shift) ((celt_word32_t)(a) << (shift)) +#define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift)) +#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + +/** Shift by a and round-to-neareast 32-bit value. Result is a 16-bit value */ +#define ROUND16(x,a) (EXTRACT16(PSHR32((x),(a)))) +/** Divide by two */ +#define HALF32(x) (SHR32(x,1)) + +/** Add two 16-bit values */ +#define ADD16(a,b) ((celt_word16_t)((celt_word16_t)(a)+(celt_word16_t)(b))) +/** Subtract two 16-bit values */ +#define SUB16(a,b) ((celt_word16_t)(a)-(celt_word16_t)(b)) +/** Add two 32-bit values */ +#define ADD32(a,b) ((celt_word32_t)(a)+(celt_word32_t)(b)) +/** Subtract two 32-bit values */ +#define SUB32(a,b) ((celt_word32_t)(a)-(celt_word32_t)(b)) + + +/** 16x16 multiplication where the result fits in 16 bits */ +#define MULT16_16_16(a,b) ((((celt_word16_t)(a))*((celt_word16_t)(b)))) + +/* (celt_word32_t)(celt_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */ +/** 16x16 multiplication where the result fits in 32 bits */ +#define MULT16_16(a,b) (((celt_word32_t)(celt_word16_t)(a))*((celt_word32_t)(celt_word16_t)(b))) + +/** 16x16 multiply-add where the result fits in 32 bits */ +#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) +/** 16x32 multiplication, followed by a 12-bit shift right. Results fits in 32 bits */ +#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12)) +/** 16x32 multiplication, followed by a 13-bit shift right. Results fits in 32 bits */ +#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13)) +/** 16x32 multiplication, followed by a 14-bit shift right. Results fits in 32 bits */ +#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14)) + +/** 16x32 multiplication, followed by an 11-bit shift right. Results fits in 32 bits */ +#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)) +/** 16x32 multiply-add, followed by an 11-bit shift right. Results fits in 32 bits */ +#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))) + +/** 16x32 multiplication, followed by a 15-bit shift right (round-to-nearest). Results fits in 32 bits */ +#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15)) +/** 16x32 multiply-add, followed by a 15-bit shift right. Results fits in 32 bits */ +#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) + + +#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11))) +#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13))) +#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13))) + +#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) +#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) +#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) +#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) + +#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) +#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) +#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) + +/** Divide a 32-bit value by a 16-bit value. Result fits in 16 bits */ +#define DIV32_16(a,b) ((celt_word16_t)(((celt_word32_t)(a))/((celt_word16_t)(b)))) +/** Divide a 32-bit value by a 16-bit value and round to nearest. Result fits in 16 bits */ +#define PDIV32_16(a,b) ((celt_word16_t)(((celt_word32_t)(a)+((celt_word16_t)(b)>>1))/((celt_word16_t)(b)))) +/** Divide a 32-bit value by a 32-bit value. Result fits in 32 bits */ +#define DIV32(a,b) (((celt_word32_t)(a))/((celt_word32_t)(b))) +/** Divide a 32-bit value by a 32-bit value and round to nearest. Result fits in 32 bits */ +#define PDIV32(a,b) (((celt_word32_t)(a)+((celt_word16_t)(b)>>1))/((celt_word32_t)(b))) + +#endif diff --git a/lib/libcelt/float_cast.h b/lib/libcelt/float_cast.h new file mode 100755 index 0000000..ee5984a --- /dev/null +++ b/lib/libcelt/float_cast.h @@ -0,0 +1,114 @@ +/* +** Copyright (C) 2001 Erik de Castro Lopo +** +** Permission to use, copy, modify, distribute, and sell this file for any +** purpose is hereby granted without fee, provided that the above copyright +** and this permission notice appear in all copies. No representations are +** made about the suitability of this software for any purpose. It is +** provided "as is" without express or implied warranty. +*/ + +/* Version 1.1 */ + +#ifndef FLOAT_CAST_H +#define FLOAT_CAST_H + +/*============================================================================ +** On Intel Pentium processors (especially PIII and probably P4), converting +** from float to int is very slow. To meet the C specs, the code produced by +** most C compilers targeting Pentium needs to change the FPU rounding mode +** before the float to int conversion is performed. +** +** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It +** is this flushing of the pipeline which is so slow. +** +** Fortunately the ISO C99 specifications define the functions lrint, lrintf, +** llrint and llrintf which fix this problem as a side effect. +** +** On Unix-like systems, the configure process should have detected the +** presence of these functions. If they weren't found we have to replace them +** here with a standard C cast. +*/ + +/* +** The C99 prototypes for lrint and lrintf are as follows: +** +** long int lrintf (float x) ; +** long int lrint (double x) ; +*/ + +/* The presence of the required functions are detected during the configure +** process and the values HAVE_LRINT and HAVE_LRINTF are set accordingly in +** the config.h file. +*/ + +/* No configure script, define manually */ +#if defined(__MACH__) + #define HAVE_LRINTF 1 +#endif + +#if (HAVE_LRINTF) +/*#if 0*/ + + /* These defines enable functionality introduced with the 1999 ISO C + ** standard. They must be defined before the inclusion of math.h to + ** engage them. If optimisation is enabled, these functions will be + ** inlined. With optimisation switched off, you have to link in the + ** maths library using -lm. + */ + + #define _ISOC9X_SOURCE 1 + #define _ISOC99_SOURCE 1 + + #define __USE_ISOC9X 1 + #define __USE_ISOC99 1 + + #include + #define float2int(x) lrintf(x) + +#elif (defined(HAVE_LRINT)) + +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 + +#define __USE_ISOC9X 1 +#define __USE_ISOC99 1 + +#include +#define float2int(x) lrint(x) + +#elif (defined (WIN32) || defined (_WIN32)) && !defined(_XENON) + + #include + + /* Win32 doesn't seem to have these functions. + ** Therefore implement inline versions of these functions here. + */ + + __inline long int + float2int (float flt) + { int intgr; + + _asm + { fld flt + fistp intgr + } ; + + return intgr ; + } + +#else + +#ifdef __GNUC__ /* supported by gcc, but not by all other compilers*/ + #warning "Don't have the functions lrint() and lrintf ()." + #warning "Replacing these functions with a standard C cast." +#endif /* __GNUC__ */ + + #include + + #define float2int(flt) ((int)(floor(.5+flt))) + +#endif + + +#endif /* FLOAT_CAST_H */ diff --git a/lib/libcelt/header.c b/lib/libcelt/header.c new file mode 100755 index 0000000..7950583 --- /dev/null +++ b/lib/libcelt/header.c @@ -0,0 +1,140 @@ +/* (C) 2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "celt_header.h" +#include "os_support.h" +#include "modes.h" + +/*typedef struct { + char codec_id[8]; + char codec_version[20]; + celt_int32_t version_id; + celt_int32_t header_size; + celt_int32_t mode; + celt_int32_t sample_rate; + celt_int32_t nb_channels; + celt_int32_t bytes_per_packet; + celt_int32_t extra_headers; +} CELTHeader;*/ + +static celt_uint32_t +_le_32 (celt_uint32_t i) +{ + celt_uint32_t ret=i; +#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) ) + ret = (i>>24); + ret += (i>>8) & 0x0000ff00; + ret += (i<<8) & 0x00ff0000; + ret += (i<<24); +#endif + return ret; +} + +int celt_header_init(CELTHeader *header, const CELTMode *m) +{ + + if (check_mode(m) != CELT_OK) + return CELT_INVALID_MODE; + if (header==NULL) + return CELT_BAD_ARG; + + CELT_COPY(header->codec_id, "CELT ", 8); + CELT_COPY(header->codec_version, "experimental ", 20); + + celt_mode_info(m, CELT_GET_BITSTREAM_VERSION, &header->version_id); + header->header_size = 56; + header->sample_rate = m->Fs; + header->nb_channels = m->nbChannels; + header->frame_size = m->mdctSize; + header->overlap = m->overlap; + header->bytes_per_packet = -1; + header->extra_headers = 0; + return CELT_OK; +} + +int celt_header_to_packet(const CELTHeader *header, unsigned char *packet, celt_uint32_t size) +{ + celt_int32_t * h; + + if ((size < 56) || (header==NULL) || (packet==NULL)) + return CELT_BAD_ARG; /* FAIL */ + + CELT_MEMSET(packet, 0, sizeof(*header)); + /* FIXME: Do it in an alignment-safe manner */ + + /* Copy ident and version */ + CELT_COPY(packet, (unsigned char*)header, 28); + + /* Copy the int32 fields */ + h = (celt_int32_t*)(packet+28); + *h++ = _le_32 (header->version_id); + *h++ = _le_32 (header->header_size); + *h++ = _le_32 (header->sample_rate); + *h++ = _le_32 (header->nb_channels); + *h++ = _le_32 (header->frame_size); + *h++ = _le_32 (header->overlap); + *h++ = _le_32 (header->bytes_per_packet); + *h = _le_32 (header->extra_headers); + + return sizeof(*header); +} + +int celt_header_from_packet(const unsigned char *packet, celt_uint32_t size, CELTHeader *header) +{ + celt_int32_t * h; + + if ((size < 56) || (header==NULL) || (packet==NULL)) + return CELT_BAD_ARG; /* FAIL */ + + CELT_MEMSET((unsigned char*)header, 0, sizeof(*header)); + /* FIXME: Do it in an alignment-safe manner */ + + /* Copy ident and version */ + CELT_COPY((unsigned char*)header, packet, 28); + + /* Copy the int32 fields */ + h = (celt_int32_t*)(packet+28); + header->version_id = _le_32(*h++); + header->header_size = _le_32(*h++); + header->sample_rate = _le_32(*h++); + header->nb_channels = _le_32(*h++); + header->frame_size = _le_32(*h++); + header->overlap = _le_32(*h++); + header->bytes_per_packet = _le_32(*h++); + header->extra_headers = _le_32(*h); + + return sizeof(*header); +} + diff --git a/lib/libcelt/kfft_double.h b/lib/libcelt/kfft_double.h new file mode 100755 index 0000000..8826c6f --- /dev/null +++ b/lib/libcelt/kfft_double.h @@ -0,0 +1,68 @@ +/* (C) 2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KFFT_DOUBLE_H +#define KFFT_DOUBLE_H + +#ifdef ENABLE_TI_DSPLIB + +#include "dsplib.h" +#include "_kiss_fft_guts.h" + +#define cpx32_fft_alloc(length) NULL +#define cpx32_fft_free(state) + +#define cpx32_fft(state, X, Y, nx)\ + (\ + cfft32_SCALE(X,nx),\ + cbrev32(X,Y,nx)\ + ) + +#define cpx32_ifft(state, X, Y, nx) \ + (\ + cifft32_NOSCALE(X,nx),\ + cbrev32(X,Y,nx)\ + ) + + +#else /* ENABLE_TI_DSPLIB */ + +#include "kiss_fft.h" +#include "_kiss_fft_guts.h" + +#define cpx32_fft_alloc(length) kiss_fft_alloc(length, 0, 0); +#define cpx32_fft_free(state) kiss_fft_free(state) +#define cpx32_fft(state, X, Y, nx) kiss_fft(state,(const kiss_fft_cpx *)(X), (kiss_fft_cpx *)(Y)) +#define cpx32_ifft(state, X, Y, nx) kiss_ifft(state,(const kiss_fft_cpx *)(X), (kiss_fft_cpx *)(Y)) + +#endif /* !ENABLE_TI_DSPLIB */ + +#endif /* KFFT_DOUBLE_H */ diff --git a/lib/libcelt/kfft_single.c b/lib/libcelt/kfft_single.c new file mode 100755 index 0000000..e72c046 --- /dev/null +++ b/lib/libcelt/kfft_single.c @@ -0,0 +1,44 @@ +/* (C) 2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef FIXED_POINT + +#include "kfft_single.h" + +#define SKIP_CONFIG_H +#include "kiss_fft.c" +#include "kiss_fftr.c" + +#endif diff --git a/lib/libcelt/kfft_single.h b/lib/libcelt/kfft_single.h new file mode 100755 index 0000000..e97e446 --- /dev/null +++ b/lib/libcelt/kfft_single.h @@ -0,0 +1,84 @@ +/* (C) 2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KFFT_SINGLE_H +#define KFFT_SINGLE_H + +#ifdef ENABLE_TI_DSPLIB + +#include "dsplib.h" + +#define real16_fft_alloc(length) NULL +#define real16_fft_free(state) +#define BITREV(state, i) (i) + +#define real16_fft_inplace(state, X, nx)\ + (\ + cfft_SCALE(X,nx/2),\ + cbrev(X,X,nx/2),\ + unpack(X,nx)\ + ) + +#define real16_ifft(state, X, Y, nx) \ + (\ + unpacki(X, nx),\ + cifft_NOSCALE(X,nx/2),\ + cbrev(X,Y,nx/2)\ + ) + + +#else /* ENABLE_TI_DSPLIB */ + +#ifdef FIXED_POINT + +#ifdef DOUBLE_PRECISION +#undef DOUBLE_PRECISION +#endif + +#ifdef MIXED_PRECISION +#undef MIXED_PRECISION +#endif + +#endif /* FIXED_POINT */ + +#include "kiss_fft.h" +#include "kiss_fftr.h" +#include "_kiss_fft_guts.h" + +#define real16_fft_alloc(length) kiss_fftr_alloc_celt_single(length, 0, 0); +#define real16_fft_free(state) kiss_fft_free(state) +#define real16_fft_inplace(state, X, nx) kiss_fftr_inplace(state,X) +#define BITREV(state, i) ((state)->substate->bitrev[i]) +#define real16_ifft(state, X, Y, nx) kiss_fftri(state,X, Y) + +#endif /* !ENABLE_TI_DSPLIB */ + +#endif /* KFFT_SINGLE_H */ diff --git a/lib/libcelt/kiss_fft.c b/lib/libcelt/kiss_fft.c new file mode 100755 index 0000000..2d5e9a6 --- /dev/null +++ b/lib/libcelt/kiss_fft.c @@ -0,0 +1,701 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding +Lots of modifications by JMV +Copyright (c) 2005-2007, Jean-Marc Valin +Copyright (c) 2008, Jean-Marc Valin, CSIRO + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SKIP_CONFIG_H +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +#endif + +#include "_kiss_fft_guts.h" +#include "arch.h" +#include "os_support.h" +#include "mathops.h" +#include "stack_alloc.h" + +/* The guts header contains all the multiplication and addition macros that are defined for + complex numbers. It also delares the kf_ internal functions. +*/ + +static void kf_bfly2( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int N, + int mm + ) +{ + kiss_fft_cpx * Fout2; + kiss_twiddle_cpx * tw1; + int i,j; + kiss_fft_cpx * Fout_beg = Fout; + for (i=0;itwiddles; + for(j=0;jr = SHR(Fout->r, 1);Fout->i = SHR(Fout->i, 1); + Fout2->r = SHR(Fout2->r, 1);Fout2->i = SHR(Fout2->i, 1); + C_MUL (t, *Fout2 , *tw1); + tw1 += fstride; + C_SUB( *Fout2 , *Fout , t ); + C_ADDTO( *Fout , t ); + ++Fout2; + ++Fout; + } + } +} + +static void ki_bfly2( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int N, + int mm + ) +{ + kiss_fft_cpx * Fout2; + kiss_twiddle_cpx * tw1; + kiss_fft_cpx t; + int i,j; + kiss_fft_cpx * Fout_beg = Fout; + for (i=0;itwiddles; + for(j=0;jtwiddles; + for (j=0;jr = PSHR(Fout->r, 2); + Fout->i = PSHR(Fout->i, 2); + C_SUB( scratch[5] , *Fout, scratch[1] ); + C_ADDTO(*Fout, scratch[1]); + C_ADD( scratch[3] , scratch[0] , scratch[2] ); + C_SUB( scratch[4] , scratch[0] , scratch[2] ); + Fout[m2].r = PSHR(Fout[m2].r, 2); + Fout[m2].i = PSHR(Fout[m2].i, 2); + C_SUB( Fout[m2], *Fout, scratch[3] ); + tw1 += fstride; + tw2 += fstride*2; + tw3 += fstride*3; + C_ADDTO( *Fout , scratch[3] ); + + Fout[m].r = scratch[5].r + scratch[4].i; + Fout[m].i = scratch[5].i - scratch[4].r; + Fout[m3].r = scratch[5].r - scratch[4].i; + Fout[m3].i = scratch[5].i + scratch[4].r; + ++Fout; + } + } +} + +static void ki_bfly4( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int N, + int mm + ) +{ + kiss_twiddle_cpx *tw1,*tw2,*tw3; + kiss_fft_cpx scratch[6]; + const size_t m2=2*m; + const size_t m3=3*m; + int i, j; + + kiss_fft_cpx * Fout_beg = Fout; + for (i=0;itwiddles; + for (j=0;jtwiddles[fstride*m]; + + tw1=tw2=st->twiddles; + do{ + C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); + + C_MUL(scratch[1],Fout[m] , *tw1); + C_MUL(scratch[2],Fout[m2] , *tw2); + + C_ADD(scratch[3],scratch[1],scratch[2]); + C_SUB(scratch[0],scratch[1],scratch[2]); + tw1 += fstride; + tw2 += fstride*2; + + Fout[m].r = Fout->r - HALF_OF(scratch[3].r); + Fout[m].i = Fout->i - HALF_OF(scratch[3].i); + + C_MULBYSCALAR( scratch[0] , epi3.i ); + + C_ADDTO(*Fout,scratch[3]); + + Fout[m2].r = Fout[m].r + scratch[0].i; + Fout[m2].i = Fout[m].i - scratch[0].r; + + Fout[m].r -= scratch[0].i; + Fout[m].i += scratch[0].r; + + ++Fout; + }while(--k); +} + +static void ki_bfly3( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + size_t m + ) +{ + size_t k=m; + const size_t m2 = 2*m; + kiss_twiddle_cpx *tw1,*tw2; + kiss_fft_cpx scratch[5]; + kiss_twiddle_cpx epi3; + epi3 = st->twiddles[fstride*m]; + + tw1=tw2=st->twiddles; + do{ + + C_MULC(scratch[1],Fout[m] , *tw1); + C_MULC(scratch[2],Fout[m2] , *tw2); + + C_ADD(scratch[3],scratch[1],scratch[2]); + C_SUB(scratch[0],scratch[1],scratch[2]); + tw1 += fstride; + tw2 += fstride*2; + + Fout[m].r = Fout->r - HALF_OF(scratch[3].r); + Fout[m].i = Fout->i - HALF_OF(scratch[3].i); + + C_MULBYSCALAR( scratch[0] , -epi3.i ); + + C_ADDTO(*Fout,scratch[3]); + + Fout[m2].r = Fout[m].r + scratch[0].i; + Fout[m2].i = Fout[m].i - scratch[0].r; + + Fout[m].r -= scratch[0].i; + Fout[m].i += scratch[0].r; + + ++Fout; + }while(--k); +} + + +static void kf_bfly5( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; + int u; + kiss_fft_cpx scratch[13]; + kiss_twiddle_cpx * twiddles = st->twiddles; + kiss_twiddle_cpx *tw; + kiss_twiddle_cpx ya,yb; + ya = twiddles[fstride*m]; + yb = twiddles[fstride*2*m]; + + Fout0=Fout; + Fout1=Fout0+m; + Fout2=Fout0+2*m; + Fout3=Fout0+3*m; + Fout4=Fout0+4*m; + + tw=st->twiddles; + for ( u=0; ur += scratch[7].r + scratch[8].r; + Fout0->i += scratch[7].i + scratch[8].i; + + scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); + scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); + + scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); + scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); + + C_SUB(*Fout1,scratch[5],scratch[6]); + C_ADD(*Fout4,scratch[5],scratch[6]); + + scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); + scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); + scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); + scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); + + C_ADD(*Fout2,scratch[11],scratch[12]); + C_SUB(*Fout3,scratch[11],scratch[12]); + + ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; + } +} + +static void ki_bfly5( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; + int u; + kiss_fft_cpx scratch[13]; + kiss_twiddle_cpx * twiddles = st->twiddles; + kiss_twiddle_cpx *tw; + kiss_twiddle_cpx ya,yb; + ya = twiddles[fstride*m]; + yb = twiddles[fstride*2*m]; + + Fout0=Fout; + Fout1=Fout0+m; + Fout2=Fout0+2*m; + Fout3=Fout0+3*m; + Fout4=Fout0+4*m; + + tw=st->twiddles; + for ( u=0; ur += scratch[7].r + scratch[8].r; + Fout0->i += scratch[7].i + scratch[8].i; + + scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); + scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); + + scratch[6].r = -S_MUL(scratch[10].i,ya.i) - S_MUL(scratch[9].i,yb.i); + scratch[6].i = S_MUL(scratch[10].r,ya.i) + S_MUL(scratch[9].r,yb.i); + + C_SUB(*Fout1,scratch[5],scratch[6]); + C_ADD(*Fout4,scratch[5],scratch[6]); + + scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); + scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); + scratch[12].r = S_MUL(scratch[10].i,yb.i) - S_MUL(scratch[9].i,ya.i); + scratch[12].i = -S_MUL(scratch[10].r,yb.i) + S_MUL(scratch[9].r,ya.i); + + C_ADD(*Fout2,scratch[11],scratch[12]); + C_SUB(*Fout3,scratch[11],scratch[12]); + + ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; + } +} + +/* perform the butterfly for one stage of a mixed radix FFT */ +static void kf_bfly_generic( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int p + ) +{ + int u,k,q1,q; + kiss_twiddle_cpx * twiddles = st->twiddles; + kiss_fft_cpx t; + VARDECL(kiss_fft_cpx, scratchbuf); + int Norig = st->nfft; + ALLOC(scratchbuf, p, kiss_fft_cpx); + + for ( u=0; u=Norig) twidx-=Norig; + C_MUL(t,scratchbuf[q] , twiddles[twidx] ); + C_ADDTO( Fout[ k ] ,t); + } + k += m; + } + } +} + +static void ki_bfly_generic( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int p + ) +{ + int u,k,q1,q; + kiss_twiddle_cpx * twiddles = st->twiddles; + kiss_fft_cpx t; + VARDECL(kiss_fft_cpx, scratchbuf); + int Norig = st->nfft; + ALLOC(scratchbuf, p, kiss_fft_cpx); + + for ( u=0; u=Norig) twidx-=Norig; + C_MULC(t,scratchbuf[q] , twiddles[twidx] ); + C_ADDTO( Fout[ k ] ,t); + } + k += m; + } + } +} +#endif + +static +void compute_bitrev_table( + int Fout, + int *f, + const size_t fstride, + int in_stride, + int * factors, + const kiss_fft_cfg st + ) +{ + const int p=*factors++; /* the radix */ + const int m=*factors++; /* stage's fft length/p */ + + /*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/ + if (m==1) + { + int j; + for (j=0;j32000 || (celt_int32_t)p*(celt_int32_t)p > n) + p = n; /* no more factors, skip to end */ + } + n /= p; + *facbuf++ = p; + *facbuf++ = n; + } while (n > 1); +} +/* + * + * User-callable function to allocate all necessary storage space for the fft. + * + * The return value is a contiguous block of memory, allocated with malloc. As such, + * It can be freed with free(), rather than a kiss_fft-specific function. + * */ +kiss_fft_cfg kiss_fft_alloc(int nfft,void * mem,size_t * lenmem ) +{ + kiss_fft_cfg st=NULL; + size_t memneeded = sizeof(struct kiss_fft_state) + + sizeof(kiss_twiddle_cpx)*(nfft-1) + sizeof(int)*nfft; /* twiddle factors*/ + + if ( lenmem==NULL ) { + st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); + }else{ + if (mem != NULL && *lenmem >= memneeded) + st = (kiss_fft_cfg)mem; + *lenmem = memneeded; + } + if (st) { + int i; + st->nfft=nfft; +#ifndef FIXED_POINT + st->scale = 1./nfft; +#endif +#if defined(FIXED_POINT) && (!defined(DOUBLE_PRECISION) || defined(MIXED_PRECISION)) + for (i=0;itwiddles+i, DIV32(SHL32(phase,17),nfft)); + } +#else + for (i=0;itwiddles+i, phase ); + } +#endif + kf_factor(nfft,st->factors); + + /* bitrev */ + st->bitrev = (int*)((char*)st + memneeded - sizeof(int)*nfft); + compute_bitrev_table(0, st->bitrev, 1,1, st->factors,st); + } + return st; +} + + + + +void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) +{ + if (fin == fout) + { + celt_fatal("In-place FFT not supported"); + } else { + /* Bit-reverse the input */ + int i; + for (i=0;infft;i++) + { + fout[st->bitrev[i]] = fin[i]; +#ifndef FIXED_POINT + fout[st->bitrev[i]].r *= st->scale; + fout[st->bitrev[i]].i *= st->scale; +#endif + } + kf_work( fout, fin, 1,in_stride, st->factors,st, 1, in_stride, 1); + } +} + +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) +{ + kiss_fft_stride(cfg,fin,fout,1); +} + +void kiss_ifft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) +{ + if (fin == fout) + { + celt_fatal("In-place FFT not supported"); + } else { + /* Bit-reverse the input */ + int i; + for (i=0;infft;i++) + fout[st->bitrev[i]] = fin[i]; + ki_work( fout, fin, 1,in_stride, st->factors,st, 1, in_stride, 1); + } +} + +void kiss_ifft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) +{ + kiss_ifft_stride(cfg,fin,fout,1); +} + diff --git a/lib/libcelt/kiss_fft.h b/lib/libcelt/kiss_fft.h new file mode 100755 index 0000000..f35e87e --- /dev/null +++ b/lib/libcelt/kiss_fft.h @@ -0,0 +1,159 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding +Lots of modifications by JMV +Copyright (c) 2005-2007, Jean-Marc Valin +Copyright (c) 2008, Jean-Marc Valin, CSIRO + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef KISS_FFT_H +#define KISS_FFT_H + +#include +#include +#include "arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ATTENTION! + If you would like a : + -- a utility that will handle the caching of fft objects + -- real-only (no imaginary time component ) FFT + -- a multi-dimensional FFT + -- a command-line utility to perform ffts + -- a command-line utility to perform fast-convolution filtering + + Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c + in the tools/ directory. +*/ + +#ifdef USE_SIMD +# include +# define kiss_fft_scalar __m128 +#define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes) +#else +#define KISS_FFT_MALLOC celt_alloc +#endif + + +#ifdef FIXED_POINT +#include "arch.h" +#ifdef DOUBLE_PRECISION +# define kiss_fft_scalar celt_int32_t +# define kiss_twiddle_scalar celt_int32_t +# define KF_SUFFIX _celt_double +#else +# define kiss_fft_scalar celt_int16_t +# define kiss_twiddle_scalar celt_int16_t +# define KF_SUFFIX _celt_single +#endif +#else +# ifndef kiss_fft_scalar +/* default is float */ +# define kiss_fft_scalar float +# define kiss_twiddle_scalar float +# define KF_SUFFIX _celt_single +# endif +#endif + + +/* This adds a suffix to all the kiss_fft functions so we + can easily link with more than one copy of the fft */ +#define CAT_SUFFIX(a,b) a ## b +#define SUF(a,b) CAT_SUFFIX(a, b) + +#define kiss_fft_alloc SUF(kiss_fft_alloc,KF_SUFFIX) +#define kf_work SUF(kf_work,KF_SUFFIX) +#define ki_work SUF(ki_work,KF_SUFFIX) +#define kiss_fft SUF(kiss_fft,KF_SUFFIX) +#define kiss_ifft SUF(kiss_ifft,KF_SUFFIX) +#define kiss_fft_stride SUF(kiss_fft_stride,KF_SUFFIX) +#define kiss_ifft_stride SUF(kiss_ifft_stride,KF_SUFFIX) + + +typedef struct { + kiss_fft_scalar r; + kiss_fft_scalar i; +}kiss_fft_cpx; + +typedef struct { + kiss_twiddle_scalar r; + kiss_twiddle_scalar i; +}kiss_twiddle_cpx; + +typedef struct kiss_fft_state* kiss_fft_cfg; + +/** + * kiss_fft_alloc + * + * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. + * + * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); + * + * The return value from fft_alloc is a cfg buffer used internally + * by the fft routine or NULL. + * + * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. + * The returned value should be free()d when done to avoid memory leaks. + * + * The state can be placed in a user supplied buffer 'mem': + * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, + * then the function places the cfg in mem and the size used in *lenmem + * and returns mem. + * + * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), + * then the function returns NULL and places the minimum cfg + * buffer size in *lenmem. + * */ + +kiss_fft_cfg kiss_fft_alloc(int nfft,void * mem,size_t * lenmem); + +void kf_work(kiss_fft_cpx * Fout,const kiss_fft_cpx * f,const size_t fstride, + int in_stride,int * factors,const kiss_fft_cfg st,int N,int s2,int m2); + +/** Internal function. Can be useful when you want to do the bit-reversing yourself */ +void ki_work(kiss_fft_cpx * Fout, const kiss_fft_cpx * f, const size_t fstride, + int in_stride,int * factors,const kiss_fft_cfg st,int N,int s2,int m2); + +/** + * kiss_fft(cfg,in_out_buf) + * + * Perform an FFT on a complex input buffer. + * for a forward FFT, + * fin should be f[0] , f[1] , ... ,f[nfft-1] + * fout will be F[0] , F[1] , ... ,F[nfft-1] + * Note that each element is complex and can be accessed like + f[k].r and f[k].i + * */ +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); +void kiss_ifft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); + +/** + A more generic version of the above function. It reads its input from every Nth sample. + * */ +void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); +void kiss_ifft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); + +/** If kiss_fft_alloc allocated a buffer, it is one contiguous + buffer and can be simply free()d when no longer needed*/ +#define kiss_fft_free celt_free + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libcelt/kiss_fftr.c b/lib/libcelt/kiss_fftr.c new file mode 100755 index 0000000..ee8d7bb --- /dev/null +++ b/lib/libcelt/kiss_fftr.c @@ -0,0 +1,165 @@ +/* +Original version: +Copyright (c) 2003-2004, Mark Borgerding +Followed by heavy modifications: +Copyright (c) 2007-2008, Jean-Marc Valin + + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SKIP_CONFIG_H +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +#endif + +#include "os_support.h" +#include "mathops.h" +#include "kiss_fftr.h" +#include "_kiss_fft_guts.h" + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,void * mem,size_t * lenmem) +{ + int i; + int twiddle_size; + kiss_fftr_cfg st = NULL; + size_t subsize, memneeded; + + if (nfft & 1) { + celt_warning("Real FFT optimization must be even.\n"); + return NULL; + } + nfft >>= 1; + twiddle_size = nfft/2+1; + kiss_fft_alloc (nfft, NULL, &subsize); + memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_twiddle_cpx)*twiddle_size; + + if (lenmem == NULL) { + st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); + } else { + if (*lenmem >= memneeded) + st = (kiss_fftr_cfg) mem; + *lenmem = memneeded; + } + if (!st) + return NULL; + + st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ + st->super_twiddles = (kiss_twiddle_cpx*) (((char *) st->substate) + subsize); + kiss_fft_alloc(nfft, st->substate, &subsize); +#ifndef FIXED_POINT + st->substate->scale *= .5; +#endif + +#if defined (FIXED_POINT) && (!defined(DOUBLE_PRECISION) || defined(MIXED_PRECISION)) + for (i=0;i>1); + kf_cexp2(st->super_twiddles+i, DIV32(SHL32(phase,16),nfft)); + } +#else + for (i=0;isuper_twiddles+i, phase ); + } +#endif + return st; +} + +void kiss_fftr_twiddles(kiss_fftr_cfg st,kiss_fft_scalar *freqdata) +{ + /* input buffer timedata is stored row-wise */ + int k,ncfft; + kiss_fft_cpx f2k,f1k,tdc,tw; + + ncfft = st->substate->nfft; + + /* The real part of the DC element of the frequency spectrum in st->tmpbuf + * contains the sum of the even-numbered elements of the input time sequence + * The imag part is the sum of the odd-numbered elements + * + * The sum of tdc.r and tdc.i is the sum of the input time sequence. + * yielding DC of input time sequence + * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... + * yielding Nyquist bin of input time sequence + */ + + tdc.r = freqdata[0]; + tdc.i = freqdata[1]; + C_FIXDIV(tdc,2); + CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); + CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); + freqdata[0] = tdc.r + tdc.i; + freqdata[1] = tdc.r - tdc.i; + + for ( k=1;k <= ncfft/2 ; ++k ) + { + f2k.r = SHR32(SUB32(EXT32(freqdata[2*k]), EXT32(freqdata[2*(ncfft-k)])),1); + f2k.i = PSHR32(ADD32(EXT32(freqdata[2*k+1]), EXT32(freqdata[2*(ncfft-k)+1])),1); + + f1k.r = SHR32(ADD32(EXT32(freqdata[2*k]), EXT32(freqdata[2*(ncfft-k)])),1); + f1k.i = SHR32(SUB32(EXT32(freqdata[2*k+1]), EXT32(freqdata[2*(ncfft-k)+1])),1); + + C_MULC( tw , f2k , st->super_twiddles[k]); + + freqdata[2*k] = HALF_OF(f1k.r + tw.r); + freqdata[2*k+1] = HALF_OF(f1k.i + tw.i); + freqdata[2*(ncfft-k)] = HALF_OF(f1k.r - tw.r); + freqdata[2*(ncfft-k)+1] = HALF_OF(tw.i - f1k.i); + + } +} + +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_scalar *freqdata) +{ + /*perform the parallel fft of two real signals packed in real,imag*/ + kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, (kiss_fft_cpx *)freqdata ); + + kiss_fftr_twiddles(st,freqdata); +} + +void kiss_fftr_inplace(kiss_fftr_cfg st, kiss_fft_scalar *X) +{ + kf_work((kiss_fft_cpx*)X, NULL, 1,1, st->substate->factors,st->substate, 1, 1, 1); + kiss_fftr_twiddles(st,X); +} + +void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_scalar *freqdata,kiss_fft_scalar *timedata) +{ + /* input buffer timedata is stored row-wise */ + int k, ncfft; + + ncfft = st->substate->nfft; + + timedata[2*st->substate->bitrev[0]] = freqdata[0] + freqdata[1]; + timedata[2*st->substate->bitrev[0]+1] = freqdata[0] - freqdata[1]; + for (k = 1; k <= ncfft / 2; ++k) { + kiss_fft_cpx fk, fnkc, fek, fok, tmp; + int k1, k2; + k1 = st->substate->bitrev[k]; + k2 = st->substate->bitrev[ncfft-k]; + fk.r = freqdata[2*k]; + fk.i = freqdata[2*k+1]; + fnkc.r = freqdata[2*(ncfft-k)]; + fnkc.i = -freqdata[2*(ncfft-k)+1]; + + C_ADD (fek, fk, fnkc); + C_SUB (tmp, fk, fnkc); + C_MUL (fok, tmp, st->super_twiddles[k]); + timedata[2*k1] = fek.r + fok.r; + timedata[2*k1+1] = fek.i + fok.i; + timedata[2*k2] = fek.r - fok.r; + timedata[2*k2+1] = fok.i - fek.i; + } + ki_work((kiss_fft_cpx*)timedata, NULL, 1,1, st->substate->factors,st->substate, 1, 1, 1); +} diff --git a/lib/libcelt/kiss_fftr.h b/lib/libcelt/kiss_fftr.h new file mode 100755 index 0000000..8d42115 --- /dev/null +++ b/lib/libcelt/kiss_fftr.h @@ -0,0 +1,82 @@ +/* +Original version: +Copyright (c) 2003-2004, Mark Borgerding +Followed by heavy modifications: +Copyright (c) 2007-2008, Jean-Marc Valin + + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define kiss_fftr_alloc SUF(kiss_fftr_alloc,KF_SUFFIX) +#define kiss_fftr_inplace SUF(kiss_fftr_inplace,KF_SUFFIX) +#define kiss_fftr_alloc SUF(kiss_fftr_alloc,KF_SUFFIX) +#define kiss_fftr_twiddles SUF(kiss_fftr_twiddles,KF_SUFFIX) +#define kiss_fftr SUF(kiss_fftr,KF_SUFFIX) +#define kiss_fftri SUF(kiss_fftri,KF_SUFFIX) + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +struct kiss_fftr_state{ + kiss_fft_cfg substate; + kiss_twiddle_cpx * super_twiddles; +#ifdef USE_SIMD + long pad; +#endif + }; + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points, packed into nfft scalar points +*/ +void kiss_fftr_twiddles(kiss_fftr_cfg st,kiss_fft_scalar *freqdata); + +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_scalar *freqdata); +void kiss_fftr_inplace(kiss_fftr_cfg st, kiss_fft_scalar *X); + +void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_scalar *freqdata, kiss_fft_scalar *timedata); + +/* + input freqdata has nfft/2+1 complex points, packed into nfft scalar points + output timedata has nfft scalar points +*/ + +#define kiss_fftr_free speex_free + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/libcelt/laplace.c b/lib/libcelt/laplace.c new file mode 100755 index 0000000..a04d3db --- /dev/null +++ b/lib/libcelt/laplace.c @@ -0,0 +1,141 @@ +/* (C) 2007 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "laplace.h" + +int ec_laplace_get_start_freq(int decay) +{ + int fs = (((ec_uint32)32768)*(16384-decay))/(16384+decay); + /* Making fs even so we're sure that all the range is used for +/- values */ + fs -= (fs&1); + return fs; +} + +#ifdef FMOD_CELT_ENCODER + +void ec_laplace_encode_start(ec_enc *enc, int *value, int decay, int fs) +{ + int i; + int fl; + unsigned int ft; + int s = 0; + int val = *value; + if (val < 0) + { + s = 1; + val = -val; + } + ft = 32768; + fl = -fs; + for (i=0;i>14; + if (fs == 0) + { + if (fl+2 <= ft) + { + fs = 1; + } else { + fs = tmp_s; + fl = tmp_l; + if (s) + *value = -i; + else + *value = i; + break; + } + } + } + if (fl < 0) + fl = 0; + if (s) + fl += fs; + ec_encode(enc, fl, fl+fs, ft); +} + +void ec_laplace_encode(ec_enc *enc, int *value, int decay) +{ + int fs = ec_laplace_get_start_freq(decay); + ec_laplace_encode_start(enc, value, decay, fs); +} + +#endif + +int ec_laplace_decode_start(ec_dec *dec, int decay, int fs) +{ + int val=0; + int fl, fh, fm; + unsigned int ft; + fl = 0; + ft = 32768; + fh = fs; + fm = ec_decode(dec, ft); + while (fm >= fh && fs != 0) + { + fl = fh; + fs = (fs*(ec_int32)decay)>>14; + if (fs == 0 && fh+2 <= ft) + { + fs = 1; + } + fh += fs*2; + val++; + } + if (fl>0) + { + if (fm >= fl+fs) + { + val = -val; + fl += fs; + } else { + fh -= fs; + } + } + /* Preventing an infinite loop in case something screws up in the decoding */ + if (fl==fh) + fl--; + ec_dec_update(dec, fl, fh, ft); + return val; +} + +int ec_laplace_decode(ec_dec *dec, int decay) +{ + int fs = ec_laplace_get_start_freq(decay); + return ec_laplace_decode_start(dec, decay, fs); +} diff --git a/lib/libcelt/laplace.h b/lib/libcelt/laplace.h new file mode 100755 index 0000000..34ae905 --- /dev/null +++ b/lib/libcelt/laplace.h @@ -0,0 +1,55 @@ +/* (C) 2007 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "entenc.h" +#include "entdec.h" + +int ec_laplace_get_start_freq(int decay); + +/** Encode a value that is assumed to be the realisation of a + Laplace-distributed random process + @param enc Entropy encoder state + @param value Value to encode + @param decay Probability of the value +/- 1, multiplied by 16384 +*/ +void ec_laplace_encode(ec_enc *enc, int *value, int decay); + +void ec_laplace_encode_start(ec_enc *enc, int *value, int decay, int fs); + +/** Decode a value that is assumed to be the realisation of a + Laplace-distributed random process + @param dec Entropy decoder state + @param decay Probability of the value +/- 1, multiplied by 16384 + @return Value decoded + */ +int ec_laplace_decode(ec_dec *dec, int decay); + +int ec_laplace_decode_start(ec_dec *dec, int decay, int fs); diff --git a/lib/libcelt/mathops.h b/lib/libcelt/mathops.h new file mode 100755 index 0000000..1f47a82 --- /dev/null +++ b/lib/libcelt/mathops.h @@ -0,0 +1,371 @@ +/* Copyright (C) 2002-2008 Jean-Marc Valin */ +/** + @file mathops.h + @brief Various math functions +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MATHOPS_H +#define MATHOPS_H + +#include "arch.h" +#include "entcode.h" +#include "os_support.h" + +#ifndef OVERRIDE_CELT_ILOG2 +/** Integer log in base2. Undefined for zero and negative numbers */ +static FMOD_INLINE celt_int16_t celt_ilog2(celt_word32_t x) +{ + celt_assert2(x>0, "celt_ilog2() only defined for strictly positive numbers"); + return EC_ILOG(x)-1; +} +#endif + +#ifndef OVERRIDE_FIND_MAX16 +static FMOD_INLINE int find_max16(celt_word16_t *x, int len) +{ + celt_word16_t max_corr=-VERY_LARGE16; + int i, id = 0; + for (i=0;i max_corr) + { + id = i; + max_corr = x[i]; + } + } + return id; +} +#endif + +#ifndef OVERRIDE_FIND_MAX32 +static FMOD_INLINE int find_max32(celt_word32_t *x, int len) +{ + celt_word32_t max_corr=-VERY_LARGE32; + int i, id = 0; + for (i=0;i max_corr) + { + id = i; + max_corr = x[i]; + } + } + return id; +} +#endif + +#define FRAC_MUL16(a,b) ((16384+((celt_int32_t)(celt_int16_t)(a)*(celt_int16_t)(b)))>>15) +static FMOD_INLINE celt_int16_t bitexact_cos(celt_int16_t x) +{ + celt_int32_t tmp; + celt_int16_t x2; + tmp = (4096+((celt_int32_t)(x)*(x)))>>13; + if (tmp > 32767) + tmp = 32767; + x2 = tmp; + x2 = (32767-x2) + FRAC_MUL16(x2, (-7651 + FRAC_MUL16(x2, (8277 + FRAC_MUL16(-626, x2))))); + if (x2 > 32766) + x2 = 32766; + return 1+x2; +} + + +#ifndef FIXED_POINT + +#define celt_sqrt(x) ((float)sqrt(x)) +#define celt_psqrt(x) ((float)sqrt(x)) +#define celt_rsqrt(x) (1.f/celt_sqrt(x)) +#define celt_acos acos +#define celt_exp exp +#define celt_cos_norm(x) (cos((.5f*M_PI)*(x))) +#define celt_atan atan +#define celt_rcp(x) (1.f/(x)) +#define celt_div(a,b) ((a)/(b)) + +#ifdef FLOAT_APPROX + +/* Note: This assumes radix-2 floating point with the exponent at bits 23..30 and an offset of 127 + denorm, +/- inf and NaN are *not* handled */ + +/** Base-2 log approximation (log2(x)). */ +static inline float celt_log2(float x) +{ + int integer; + float frac; + union { + float f; + celt_uint32_t i; + } in; + in.f = x; + integer = (in.i>>23)-127; + in.i -= integer<<23; + frac = in.f - 1.5; + /* -0.41446 0.96093 -0.33981 0.15600 */ + frac = -0.41446 + frac*(0.96093 + frac*(-0.33981 + frac*0.15600)); + return 1+integer+frac; +} + +/** Base-2 exponential approximation (2^x). */ +static inline float celt_exp2(float x) +{ + int integer; + float frac; + union { + float f; + celt_uint32_t i; + } res; + integer = floor(x); + if (integer < -50) + return 0; + frac = x-integer; + /* K0 = 1, K1 = log(2), K2 = 3-4*log(2), K3 = 3*log(2) - 2 */ + res.f = 1.f + frac * (0.696147f + frac * (0.224411f + 0.079442f*frac)); + res.i = (res.i + (integer<<23)) & 0x7fffffff; + return res.f; +} + +#else +#define celt_log2(x) (1.442695040888963387*log(x)) +#define celt_exp2(x) (exp(0.6931471805599453094*(x))) +#endif + +#endif + + + +#ifdef FIXED_POINT + +#include "os_support.h" + +#ifndef OVERRIDE_CELT_MAXABS16 +static inline celt_word16_t celt_maxabs16(celt_word16_t *x, int len) +{ + int i; + celt_word16_t maxval = 0; + for (i=0;i>1; + x = VSHR32(x, (k-7)<<1); + /* Range of n is [-16384,32767] */ + n = x-32768; + rt = ADD16(C[0], MULT16_16_Q15(n, ADD16(C[1], MULT16_16_Q15(n, ADD16(C[2], + MULT16_16_Q15(n, ADD16(C[3], MULT16_16_Q15(n, (C[4]))))))))); + rt = VSHR32(rt,k); + return rt; +} + +/** Sqrt approximation (QX input, QX/2 output) */ +static inline celt_word32_t celt_sqrt(celt_word32_t x) +{ + int k; + celt_word16_t n; + celt_word32_t rt; + const celt_word16_t C[5] = {23174, 11584, -3011, 1570, -557}; + if (x==0) + return 0; + k = (celt_ilog2(x)>>1)-7; + x = VSHR32(x, (k<<1)); + n = x-32768; + rt = ADD16(C[0], MULT16_16_Q15(n, ADD16(C[1], MULT16_16_Q15(n, ADD16(C[2], + MULT16_16_Q15(n, ADD16(C[3], MULT16_16_Q15(n, (C[4]))))))))); + rt = VSHR32(rt,7-k); + return rt; +} + +/** Sqrt approximation (QX input, QX/2 output) that assumes that the input is + strictly positive */ +static inline celt_word32_t celt_psqrt(celt_word32_t x) +{ + int k; + celt_word16_t n; + celt_word32_t rt; + const celt_word16_t C[5] = {23174, 11584, -3011, 1570, -557}; + k = (celt_ilog2(x)>>1)-7; + x = VSHR32(x, (k<<1)); + n = x-32768; + rt = ADD16(C[0], MULT16_16_Q15(n, ADD16(C[1], MULT16_16_Q15(n, ADD16(C[2], + MULT16_16_Q15(n, ADD16(C[3], MULT16_16_Q15(n, (C[4]))))))))); + rt = VSHR32(rt,7-k); + return rt; +} + +#define L1 32767 +#define L2 -7651 +#define L3 8277 +#define L4 -626 + +static inline celt_word16_t _celt_cos_pi_2(celt_word16_t x) +{ + celt_word16_t x2; + + x2 = MULT16_16_P15(x,x); + return ADD16(1,MIN16(32766,ADD32(SUB16(L1,x2), MULT16_16_P15(x2, ADD32(L2, MULT16_16_P15(x2, ADD32(L3, MULT16_16_P15(L4, x2 + )))))))); +} + +#undef L1 +#undef L2 +#undef L3 +#undef L4 + +static inline celt_word16_t celt_cos_norm(celt_word32_t x) +{ + x = x&0x0001ffff; + if (x>SHL32(EXTEND32(1), 16)) + x = SUB32(SHL32(EXTEND32(1), 17),x); + if (x&0x00007fff) + { + if (x14) + return 0x7f000000; + else if (integer < -15) + return 0; + frac = SHL16(x-SHL16(integer,11),3); + frac = ADD16(D0, MULT16_16_Q14(frac, ADD16(D1, MULT16_16_Q14(frac, ADD16(D2 , MULT16_16_Q14(D3,frac)))))); + return VSHR32(EXTEND32(frac), -integer-2); +} + +/** Reciprocal approximation (Q15 input, Q16 output) */ +static inline celt_word32_t celt_rcp(celt_word32_t x) +{ + int i; + celt_word16_t n, frac; + const celt_word16_t C[5] = {21848, -7251, 2403, -934, 327}; + celt_assert2(x>0, "celt_rcp() only defined for positive values"); + i = celt_ilog2(x); + n = VSHR32(x,i-16)-SHL32(EXTEND32(3),15); + frac = ADD16(C[0], MULT16_16_Q15(n, ADD16(C[1], MULT16_16_Q15(n, ADD16(C[2], + MULT16_16_Q15(n, ADD16(C[3], MULT16_16_Q15(n, (C[4]))))))))); + return VSHR32(EXTEND32(frac),i-16); +} + +#define celt_div(a,b) MULT32_32_Q31((celt_word32_t)(a),celt_rcp(b)) + + +#define M1 32767 +#define M2 -21 +#define M3 -11943 +#define M4 4936 + +static inline celt_word16_t celt_atan01(celt_word16_t x) +{ + return MULT16_16_P15(x, ADD32(M1, MULT16_16_P15(x, ADD32(M2, MULT16_16_P15(x, ADD32(M3, MULT16_16_P15(M4, x))))))); +} + +#undef M1 +#undef M2 +#undef M3 +#undef M4 + +static inline celt_word16_t celt_atan2p(celt_word16_t y, celt_word16_t x) +{ + if (y < x) + { + celt_word32_t arg; + arg = celt_div(SHL32(EXTEND32(y),15),x); + if (arg >= 32767) + arg = 32767; + return SHR16(celt_atan01(EXTRACT16(arg)),1); + } else { + celt_word32_t arg; + arg = celt_div(SHL32(EXTEND32(x),15),y); + if (arg >= 32767) + arg = 32767; + return 25736-SHR16(celt_atan01(EXTRACT16(arg)),1); + } +} + +#endif /* FIXED_POINT */ + + +#endif /* MATHOPS_H */ diff --git a/lib/libcelt/mdct.c b/lib/libcelt/mdct.c new file mode 100755 index 0000000..d75b734 --- /dev/null +++ b/lib/libcelt/mdct.c @@ -0,0 +1,295 @@ +/* (C) 2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* This is a simple MDCT implementation that uses a N/4 complex FFT + to do most of the work. It should be relatively straightforward to + plug in pretty much and FFT here. + + This replaces the Vorbis FFT (and uses the exact same API), which + was a bit too messy and that was ending up duplicating code + (might as well use the same FFT everywhere). + + The algorithm is similar to (and inspired from) Fabrice Bellard's + MDCT implementation in FFMPEG, but has differences in signs, ordering + and scaling in many places. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mdct.h" +#include "kfft_double.h" +#include +#include "os_support.h" +#include "mathops.h" +#include "stack_alloc.h" + +#ifndef M_PI +#define M_PI 3.141592653 +#endif + +void mdct_init(mdct_lookup *l,int N) +{ + int i; + int N2; + l->n = N; + N2 = N>>1; + l->kfft = cpx32_fft_alloc(N>>2); + if (l->kfft==NULL) + return; + l->trig = (kiss_twiddle_scalar*)celt_alloc(N2*sizeof(kiss_twiddle_scalar)); + if (l->trig==NULL) + return; + /* We have enough points that sine isn't necessary */ +#if defined(FIXED_POINT) +#if defined(DOUBLE_PRECISION) & !defined(MIXED_PRECISION) + for (i=0;itrig[i] = SAMP_MAX*cos(2*M_PI*(i+1./8.)/N); +#else + for (i=0;itrig[i] = TRIG_UPSCALE*celt_cos_norm(DIV32(ADD32(SHL32(EXTEND32(i),17),16386),N)); +#endif +#else + for (i=0;itrig[i] = cos(2*M_PI*(i+1./8.)/N); +#endif +} + +void mdct_clear(mdct_lookup *l) +{ + cpx32_fft_free(l->kfft); + celt_free(l->trig); +} + +void mdct_forward(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * celt_restrict out, const celt_word16_t *window, int overlap) +{ + int i; + int N, N2, N4; + VARDECL(kiss_fft_scalar, f); + SAVE_STACK; + N = l->n; + N2 = N>>1; + N4 = N>>2; + ALLOC(f, N2, kiss_fft_scalar); + + /* Consider the input to be compused of four blocks: [a, b, c, d] */ + /* Window, shuffle, fold */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + const kiss_fft_scalar * celt_restrict xp1 = in+(overlap>>1); + const kiss_fft_scalar * celt_restrict xp2 = in+N2-1+(overlap>>1); + kiss_fft_scalar * celt_restrict yp = out; + const celt_word16_t * celt_restrict wp1 = window+(overlap>>1); + const celt_word16_t * celt_restrict wp2 = window+(overlap>>1)-1; + for(i=0;i<(overlap>>2);i++) + { + /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ + *yp++ = MULT16_32_Q15(*wp2, xp1[N2]) + MULT16_32_Q15(*wp1,*xp2); + *yp++ = MULT16_32_Q15(*wp1, *xp1) - MULT16_32_Q15(*wp2, xp2[-N2]); + xp1+=2; + xp2-=2; + wp1+=2; + wp2-=2; + } + wp1 = window; + wp2 = window+overlap-1; + for(;i>2);i++) + { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + *yp++ = *xp2; + *yp++ = *xp1; + xp1+=2; + xp2-=2; + } + for(;itrig[0]; + for(i=0;ikfft, out, f, N4); + + /* Post-rotate */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + const kiss_fft_scalar * celt_restrict fp = f; + kiss_fft_scalar * celt_restrict yp1 = out; + kiss_fft_scalar * celt_restrict yp2 = out+N2-1; + kiss_fft_scalar *t = &l->trig[0]; + /* Temp pointers to make it really clear to the compiler what we're doing */ + for(i=0;in; + N2 = N>>1; + N4 = N>>2; + ALLOC(f, N2, kiss_fft_scalar); + ALLOC(f2, N2, kiss_fft_scalar); + + /* Pre-rotate */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + const kiss_fft_scalar * celt_restrict xp1 = in; + const kiss_fft_scalar * celt_restrict xp2 = in+N2-1; + kiss_fft_scalar * celt_restrict yp = f2; + kiss_fft_scalar *t = &l->trig[0]; + for(i=0;ikfft, f2, f, N4); + + /* Post-rotate */ + { + kiss_fft_scalar * celt_restrict fp = f; + kiss_fft_scalar *t = &l->trig[0]; + + for(i=0;i>EC_SYM_BITS) +/*Code for which propagating carries are possible.*/ +# define EC_CODE_CARRY (((ec_uint32)EC_SYM_MAX)<mdctSize; + break; + case CELT_GET_LOOKAHEAD: + *value = mode->overlap; + break; + case CELT_GET_NB_CHANNELS: + *value = mode->nbChannels; + break; + case CELT_GET_BITSTREAM_VERSION: + *value = CELT_BITSTREAM_VERSION; + break; + case CELT_GET_SAMPLE_RATE: + *value = mode->Fs; + break; + default: + return CELT_UNIMPLEMENTED; + } + return CELT_OK; +} + +#ifndef STATIC_MODES + +#define PBANDS 8 + +#ifdef STDIN_TUNING +int MIN_BINS; +#else +#define MIN_BINS 3 +#endif + +/* Defining 25 critical bands for the full 0-20 kHz audio bandwidth + Taken from http://ccrma.stanford.edu/~jos/bbt/Bark_Frequency_Scale.html */ +#define BARK_BANDS 25 +static const celt_int16_t bark_freq[BARK_BANDS+1] = { + 0, 100, 200, 300, 400, + 510, 630, 770, 920, 1080, + 1270, 1480, 1720, 2000, 2320, + 2700, 3150, 3700, 4400, 5300, + 6400, 7700, 9500, 12000, 15500, + 20000}; + +static const celt_int16_t pitch_freq[PBANDS+1] ={0, 345, 689, 1034, 1378, 2067, 3273, 5340, 6374}; + +/* This allocation table is per critical band. When creating a mode, the bits get added together + into the codec bands, which are sometimes larger than one critical band at low frequency */ + +#ifdef STDIN_TUNING +int BITALLOC_SIZE; +int *band_allocation; +#else +#define BITALLOC_SIZE 12 +static const int band_allocation[BARK_BANDS*BITALLOC_SIZE] = + /* 0 100 200 300 400 510 630 770 920 1k 1.2 1.5 1.7 2k 2.3 2.7 3.1 3.7 4.4 5.3 6.4 7.7 9.5 12k 15k */ + { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*0*/ + 2, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*1*/ + 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 5, 7, 7, 7, 5, 4, 0, 0, 0, 0, 0, 0, /*2*/ + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 5, 6, 8, 8, 8, 6, 5, 4, 0, 0, 0, 0, 0, /*3*/ + 3, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 6, 7, 9, 9, 9, 7, 6, 5, 5, 5, 0, 0, 0, /*4*/ + 3, 3, 3, 4, 4, 5, 6, 6, 6, 6, 6, 7, 7, 9, 10, 10, 10, 9, 6, 5, 5, 5, 5, 1, 0, /*5*/ + 4, 3, 3, 4, 6, 7, 7, 7, 7, 7, 8, 9, 9, 9, 11, 10, 10, 9, 9, 8, 11, 10, 10, 1, 0, /*6*/ + 5, 5, 5, 6, 7, 7, 7, 7, 8, 8, 9, 10, 10, 12, 12, 11, 11, 17, 12, 15, 15, 20, 18, 10, 1, /*7*/ + 6, 7, 7, 7, 8, 8, 8, 8, 9, 10, 11, 12, 14, 17, 18, 21, 22, 27, 29, 39, 37, 38, 40, 35, 1, /*8*/ + 7, 7, 7, 8, 8, 8, 10, 10, 10, 13, 14, 18, 20, 24, 28, 32, 32, 35, 38, 38, 42, 50, 59, 54, 31, /*9*/ + 8, 8, 8, 8, 8, 9, 10, 12, 14, 20, 22, 25, 28, 30, 35, 42, 46, 50, 55, 60, 62, 62, 72, 82, 62, /*10*/ + 9, 9, 9, 10, 12, 13, 15, 18, 22, 30, 32, 35, 40, 45, 55, 62, 66, 70, 85, 90, 92, 92, 92,102, 92, /*11*/ + }; +#endif + +static celt_int16_t *compute_ebands(celt_int32_t Fs, int frame_size, int *nbEBands) +{ + celt_int16_t *eBands; + int i, res, min_width, lin, low, high, nBark; + res = (Fs+frame_size)/(2*frame_size); + min_width = MIN_BINS*res; + + /* Find the number of critical bands supported by our sampling rate */ + for (nBark=1;nBark= Fs) + break; + + /* Find where the linear part ends (i.e. where the spacing is more than min_width */ + for (lin=0;lin= min_width) + break; + + low = ((bark_freq[lin]/res)+(MIN_BINS-1))/MIN_BINS; + high = nBark-lin; + *nbEBands = low+high; + eBands = celt_alloc(sizeof(celt_int16_t)*(*nbEBands+2)); + + if (eBands==NULL) + return NULL; + + /* Linear spacing (min_width) */ + for (i=0;i eBands[*nbEBands+1]) + eBands[*nbEBands] = eBands[*nbEBands+1]; + + /* FIXME: Remove last band if too small */ + return eBands; +} + +static void compute_pbands(CELTMode *mode, int res) +{ + int i; + celt_int16_t *pBands; + pBands=celt_alloc(sizeof(celt_int16_t)*(PBANDS+2)); + mode->pBands = pBands; + if (pBands==NULL) + return; + mode->nbPBands = PBANDS; + for (i=0;ieBands[i]) + pBands[i] = mode->eBands[i]; + } + pBands[PBANDS+1] = mode->eBands[mode->nbEBands+1]; + for (i=1;inbPBands+1;i++) + { + int j; + for (j=0;jnbEBands;j++) + if (mode->eBands[j] <= pBands[i] && mode->eBands[j+1] > pBands[i]) + break; + if (mode->eBands[j] != pBands[i]) + { + if (pBands[i]-mode->eBands[j] < mode->eBands[j+1]-pBands[i] && + mode->eBands[j] != pBands[i-1]) + pBands[i] = mode->eBands[j]; + else + pBands[i] = mode->eBands[j+1]; + } + } + mode->pitchEnd = pBands[PBANDS]; +} + +static void compute_allocation_table(CELTMode *mode, int res) +{ + int i, j, nBark; + celt_int16_t *allocVectors; + const int C = CHANNELS(mode); + + /* Find the number of critical bands supported by our sampling rate */ + for (nBark=1;nBark= mode->Fs) + break; + + mode->nbAllocVectors = BITALLOC_SIZE; + allocVectors = celt_alloc(sizeof(celt_int16_t)*(BITALLOC_SIZE*mode->nbEBands)); + if (allocVectors==NULL) + return; + /* Compute per-codec-band allocation from per-critical-band matrix */ + for (i=0;ieBands[eband+1]*res; + alloc = band_allocation[i*BARK_BANDS+j]; + alloc = alloc*C*mode->mdctSize; + if (edge < bark_freq[j+1]) + { + int num, den; + num = alloc * (edge-bark_freq[j]); + den = bark_freq[j+1]-bark_freq[j]; + low = (num+den/2)/den; + allocVectors[i*mode->nbEBands+eband] = (current+low+128)/256; + current=0; + eband++; + current += alloc-low; + } else { + current += alloc; + } + } + allocVectors[i*mode->nbEBands+eband] = (current+128)/256; + } + mode->allocVectors = allocVectors; +} + +#endif /* STATIC_MODES */ + +CELTMode *celt_mode_create(celt_int32_t Fs, int channels, int frame_size, int *error) +{ + int i; +#ifdef STDIN_TUNING + scanf("%d ", &MIN_BINS); + scanf("%d ", &BITALLOC_SIZE); + band_allocation = celt_alloc(sizeof(int)*BARK_BANDS*BITALLOC_SIZE); + for (i=0;iFs && + channels == static_mode_list[i]->nbChannels && + frame_size == static_mode_list[i]->mdctSize) + { + m = static_mode_list[i]; + break; + } + } + if (m == NULL) + { + celt_warning("Mode not included as part of the static modes"); + if (error) + *error = CELT_BAD_ARG; + return NULL; + } + mode = (CELTMode*)celt_alloc(sizeof(CELTMode)); + if (mode==NULL) + goto failure; + CELT_COPY(mode, m, 1); + mode->marker_start = MODEPARTIAL; +#else + int res; + CELTMode *mode=NULL; + celt_word16_t *window; + ALLOC_STACK; +#if !defined(VAR_ARRAYS) && !defined(USE_ALLOCA) + if (global_stack==NULL) + { + celt_free(global_stack); + goto failure; + } +#endif + + /* The good thing here is that permutation of the arguments will automatically be invalid */ + + if (Fs < 32000 || Fs > 96000) + { + celt_warning("Sampling rate must be between 32 kHz and 96 kHz"); + if (error) + *error = CELT_BAD_ARG; + return NULL; + } + if (channels < 0 || channels > 2) + { + celt_warning("Only mono and stereo supported"); + if (error) + *error = CELT_BAD_ARG; + return NULL; + } + if (frame_size < 64 || frame_size > 1024 || frame_size%2!=0) + { + celt_warning("Only even frame sizes from 64 to 1024 are supported"); + if (error) + *error = CELT_BAD_ARG; + return NULL; + } + res = (Fs+frame_size)/(2*frame_size); + + mode = celt_alloc(sizeof(CELTMode)); + if (mode==NULL) + goto failure; + mode->marker_start = MODEPARTIAL; + mode->Fs = Fs; + mode->mdctSize = frame_size; + mode->nbChannels = channels; + mode->eBands = compute_ebands(Fs, frame_size, &mode->nbEBands); + if (mode->eBands==NULL) + goto failure; + compute_pbands(mode, res); + if (mode->pBands==NULL) + goto failure; + mode->ePredCoef = QCONST16(.8f,15); + + if (frame_size > 640 && (frame_size%16)==0) + { + mode->nbShortMdcts = 8; + } else if (frame_size > 384 && (frame_size%8)==0) + { + mode->nbShortMdcts = 4; + } else if (frame_size > 384 && (frame_size%10)==0) + { + mode->nbShortMdcts = 5; + } else if (frame_size > 256 && (frame_size%6)==0) + { + mode->nbShortMdcts = 3; + } else if (frame_size > 256 && (frame_size%8)==0) + { + mode->nbShortMdcts = 4; + } else if (frame_size > 64 && (frame_size%4)==0) + { + mode->nbShortMdcts = 2; + } else if (frame_size > 128 && (frame_size%6)==0) + { + mode->nbShortMdcts = 3; + } else + { + mode->nbShortMdcts = 1; + } + + /* Overlap must be divisible by 4 */ + if (mode->nbShortMdcts > 1) + mode->overlap = ((frame_size/mode->nbShortMdcts)>>2)<<2; + else + mode->overlap = (frame_size>>3)<<2; + + compute_allocation_table(mode, res); + if (mode->allocVectors==NULL) + goto failure; + + window = (celt_word16_t*)celt_alloc(mode->overlap*sizeof(celt_word16_t)); + if (window==NULL) + goto failure; + +#ifndef FIXED_POINT + for (i=0;ioverlap;i++) + window[i] = Q15ONE*sin(.5*M_PI* sin(.5*M_PI*(i+.5)/mode->overlap) * sin(.5*M_PI*(i+.5)/mode->overlap)); +#else + for (i=0;ioverlap;i++) + window[i] = MIN32(32767,32768.*sin(.5*M_PI* sin(.5*M_PI*(i+.5)/mode->overlap) * sin(.5*M_PI*(i+.5)/mode->overlap))); +#endif + mode->window = window; + + mode->bits = (const celt_int16_t **)compute_alloc_cache(mode, 1); + if (mode->bits==NULL) + goto failure; + +#ifndef SHORTCUTS + psydecay_init(&mode->psy, MAX_PERIOD/2, mode->Fs); + if (mode->psy.decayR==NULL) + goto failure; +#endif + +#endif /* !STATIC_MODES */ + +#ifdef DISABLE_STEREO + if (channels > 1) + { + celt_warning("Stereo support was disable from this build"); + if (error) + *error = CELT_BAD_ARG; + return NULL; + } +#endif + + mdct_init(&mode->mdct, 2*mode->mdctSize); + mode->fft = pitch_state_alloc(MAX_PERIOD); + + mode->shortMdctSize = mode->mdctSize/mode->nbShortMdcts; + mdct_init(&mode->shortMdct, 2*mode->shortMdctSize); + mode->shortWindow = mode->window; + mode->prob = quant_prob_alloc(mode); + if ((mode->mdct.trig==NULL) || (mode->mdct.kfft==NULL) || (mode->fft==NULL) || + (mode->shortMdct.trig==NULL) || (mode->shortMdct.kfft==NULL) || (mode->prob==NULL)) + goto failure; + + mode->marker_start = MODEVALID; + mode->marker_end = MODEVALID; + if (error) + *error = CELT_OK; + return mode; +failure: + if (error) + *error = CELT_INVALID_MODE; + if (mode!=NULL) + celt_mode_destroy(mode); + return NULL; +} + +void celt_mode_destroy(CELTMode *mode) +{ + int i; + const celt_int16_t *prevPtr = NULL; + if (mode == NULL) + { + celt_warning("NULL passed to celt_mode_destroy"); + return; + } + + if (mode->marker_start == MODEFREED || mode->marker_end == MODEFREED) + { + celt_warning("Freeing a mode which has already been freed"); + return; + } + + if (mode->marker_start != MODEVALID && mode->marker_start != MODEPARTIAL) + { + celt_warning("This is not a valid CELT mode structure"); + return; + } + mode->marker_start = MODEFREED; +#ifndef STATIC_MODES + if (mode->bits!=NULL) + { + for (i=0;inbEBands;i++) + { + if (mode->bits[i] != prevPtr) + { + prevPtr = mode->bits[i]; + celt_free((int*)mode->bits[i]); + } + } + } + celt_free((int**)mode->bits); + celt_free((int*)mode->eBands); + celt_free((int*)mode->pBands); + celt_free((int*)mode->allocVectors); + + celt_free((celt_word16_t*)mode->window); + +#ifndef SHORTCUTS + psydecay_clear(&mode->psy); +#endif +#endif + mdct_clear(&mode->mdct); + mdct_clear(&mode->shortMdct); + pitch_state_free(mode->fft); + quant_prob_free(mode->prob); + mode->marker_end = MODEFREED; + celt_free((CELTMode *)mode); +} + +int check_mode(const CELTMode *mode) +{ + if (mode==NULL) + return CELT_INVALID_MODE; + if (mode->marker_start == MODEVALID && mode->marker_end == MODEVALID) + return CELT_OK; + if (mode->marker_start == MODEFREED || mode->marker_end == MODEFREED) + celt_warning("Using a mode that has already been freed"); + else + celt_warning("This is not a valid CELT mode"); + return CELT_INVALID_MODE; +} diff --git a/lib/libcelt/modes.h b/lib/libcelt/modes.h new file mode 100755 index 0000000..5bfc265 --- /dev/null +++ b/lib/libcelt/modes.h @@ -0,0 +1,111 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MODES_H +#define MODES_H + +#include "celt_types.h" +#include "celt.h" +#include "arch.h" +#include "mdct.h" +#include "psy.h" +#include "pitch.h" + +#define CELT_BITSTREAM_VERSION 0x80000009 + +#ifdef STATIC_MODES +#include "static_modes.h" +#endif + +#define MAX_PERIOD 512 + +#ifndef CHANNELS +# ifdef DISABLE_STEREO +# define CHANNELS(mode) (1) +# else +# define CHANNELS(mode) ((mode)->nbChannels) +# endif +#endif + +#define MDCT(mode) (&(mode)->mdct) + +#ifndef OVERLAP +#define OVERLAP(mode) ((mode)->overlap) +#endif + +#ifndef FRAMESIZE +#define FRAMESIZE(mode) ((mode)->mdctSize) +#endif + +/** Mode definition (opaque) + @brief Mode definition + */ +struct CELTMode { + celt_uint32_t marker_start; + celt_int32_t Fs; + int overlap; + int mdctSize; + int nbChannels; + + int nbEBands; + int nbPBands; + int pitchEnd; + + const celt_int16_t *eBands; /**< Definition for each "pseudo-critical band" */ + const celt_int16_t *pBands; /**< Definition of the bands used for the pitch */ + + celt_word16_t ePredCoef;/**< Prediction coefficient for the energy encoding */ + + int nbAllocVectors; /**< Number of lines in the matrix below */ + const celt_int16_t *allocVectors; /**< Number of bits in each band for several rates */ + + const celt_int16_t * const *bits; /**< Cache for pulses->bits mapping in each band */ + + /* Stuff that could go in the {en,de}coder, but we save space this way */ + mdct_lookup mdct; + kiss_fftr_cfg fft; + + const celt_word16_t *window; + + int nbShortMdcts; + int shortMdctSize; + mdct_lookup shortMdct; + const celt_word16_t *shortWindow; + + struct PsyDecay psy; + + int *prob; + celt_uint32_t marker_end; +}; + +int check_mode(const CELTMode *mode); + +#endif diff --git a/lib/libcelt/os_support.h b/lib/libcelt/os_support.h new file mode 100755 index 0000000..5ed3c58 --- /dev/null +++ b/lib/libcelt/os_support.h @@ -0,0 +1,178 @@ +/* Copyright (C) 2007 Jean-Marc Valin + + File: os_support.h + This is the (tiny) OS abstraction layer. Aside from math.h, this is the + only place where system headers are allowed. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef OS_SUPPORT_H +#define OS_SUPPORT_H + +#ifdef CUSTOM_SUPPORT +# include "custom_support.h" +#endif + +#include "../../src/fmod_types.h" +#include "../../src/fmod_memory.h" + +#include +#include +#include + + + +/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, celt_realloc and celt_free + NOTE: celt_alloc needs to CLEAR THE MEMORY */ +#ifndef OVERRIDE_CELT_ALLOC +static FMOD_INLINE void *celt_alloc (int size) +{ + /* WARNING: this is not equivalent to malloc(). If you want to use malloc() + or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise + you will experience strange bugs */ + +#ifdef FMOD_CELT_ENCODER + return calloc(1, size); +#else + return FMOD_Memory_Calloc(size); +#endif +} +#endif + +/** Same as celt_alloc, except that the area is only needed inside a Speex call (might cause problem with wideband though) */ +#ifndef OVERRIDE_CELT_ALLOC_SCRATCH +static FMOD_INLINE void *celt_alloc_scratch (int size) +{ + /* Scratch space doesn't need to be cleared */ + +#ifdef FMOD_CELT_ENCODER + return calloc(1, size); +#else + return FMOD_Memory_Calloc(size); +#endif +} +#endif + +/** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, celt_alloc and celt_free */ +#ifndef OVERRIDE_CELT_REALLOC +static FMOD_INLINE void *celt_realloc (void *ptr, int size) +{ + +#ifdef FMOD_CELT_ENCODER + return realloc(ptr, size); +#else + return FMOD_Memory_ReAlloc(ptr, size); +#endif +} +#endif + +/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, celt_realloc and celt_alloc */ +#ifndef OVERRIDE_CELT_FREE +static FMOD_INLINE void celt_free (void *ptr) +{ +#ifdef FMOD_CELT_ENCODER + free(ptr); +#else + FMOD_Memory_Free(ptr); +#endif +} +#endif + +/** Same as celt_free, except that the area is only needed inside a Speex call (might cause problem with wideband though) */ +#ifndef OVERRIDE_CELT_FREE_SCRATCH +static FMOD_INLINE void celt_free_scratch (void *ptr) +{ +#ifdef FMOD_CELT_ENCODER + free(ptr); +#else + FMOD_Memory_Free(ptr); +#endif +} +#endif + +/** Copy n bytes of memory from src to dst. The 0* term provides compile-time type checking */ +#ifndef OVERRIDE_CELT_COPY +#define CELT_COPY(dst, src, n) (FMOD_memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) +#endif + +/** Copy n bytes of memory from src to dst, allowing overlapping regions. The 0* term + provides compile-time type checking */ +#ifndef OVERRIDE_CELT_MOVE +#define CELT_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) +#endif + +/** Set n bytes of memory to value of c, starting at address s */ +#ifndef OVERRIDE_CELT_MEMSET +#define CELT_MEMSET(dst, c, n) (FMOD_memset((dst), (c), (n)*sizeof(*(dst)))) +#endif + + +#ifndef OVERRIDE_CELT_FATAL +static FMOD_INLINE void _celt_fatal(const char *str, const char *file, int line) +{ + fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str); + abort(); +} +#endif + +#ifndef OVERRIDE_CELT_WARNING +static FMOD_INLINE void celt_warning(const char *str) +{ +#ifndef DISABLE_WARNINGS + fprintf (stderr, "warning: %s\n", str); +#endif +} +#endif + +#ifndef OVERRIDE_CELT_WARNING_INT +static FMOD_INLINE void celt_warning_int(const char *str, int val) +{ +#ifndef DISABLE_WARNINGS + fprintf (stderr, "warning: %s %d\n", str, val); +#endif +} +#endif + +#ifndef OVERRIDE_CELT_NOTIFY +static FMOD_INLINE void celt_notify(const char *str) +{ +#ifndef DISABLE_NOTIFICATIONS + fprintf (stderr, "notification: %s\n", str); +#endif +} +#endif + + + +/*#ifdef __GNUC__ +#pragma GCC poison printf sprintf +#pragma GCC poison malloc free realloc calloc +#endif*/ + +#endif /* OS_SUPPORT_H */ + diff --git a/lib/libcelt/pitch.c b/lib/libcelt/pitch.c new file mode 100755 index 0000000..8a1bee5 --- /dev/null +++ b/lib/libcelt/pitch.c @@ -0,0 +1,237 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO +*/ +/** + @file pitch.c + @brief Pitch analysis + */ + +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*#include "_kiss_fft_guts.h" +#include "kiss_fftr.h"*/ +#include "kfft_single.h" + +#include "pitch.h" +#include "psy.h" +#include "os_support.h" +#include "mathops.h" +#include "modes.h" +#include "stack_alloc.h" + +kiss_fftr_cfg pitch_state_alloc(int max_lag) +{ + return real16_fft_alloc(max_lag); +} + +void pitch_state_free(kiss_fftr_cfg st) +{ + real16_fft_free(st); +} + +#ifdef FIXED_POINT +static void normalise16(celt_word16_t *x, int len, celt_word16_t val) +{ + int i; + celt_word16_t maxabs; + maxabs = celt_maxabs16(x,len); + if (maxabs > val) + { + int shift = 0; + while (maxabs > val) + { + maxabs >>= 1; + shift++; + } + if (shift==0) + return; + i=0; + do{ + x[i] = SHR16(x[i], shift); + } while (++i>= 1; + while (maxabs < val) + { + val >>= 1; + shift++; + } + if (shift==0) + return; + i=0; + do{ + x[i] = SHL16(x[i], shift); + } while (++i>1; + L2 = len>>1; + ALLOC(_X, lag, celt_word16_t); + X = _X; +#ifndef SHORTCUTS + ALLOC(curve, n2, celt_mask_t); +#endif + CELT_MEMSET(X,0,lag); + /* Sum all channels of the current frame and copy into X in bit-reverse order */ + for (c=0;c>1;i++) + { + X[2*BITREV(fft,i)] = MULT16_16_Q15(wptr[0], X[2*BITREV(fft,i)]); + X[2*BITREV(fft,i)+1] = MULT16_16_Q15(wptr[1], X[2*BITREV(fft,i)+1]); + X[2*BITREV(fft,L2-i-1)] = MULT16_16_Q15(wptr[1], X[2*BITREV(fft,L2-i-1)]); + X[2*BITREV(fft,L2-i-1)+1] = MULT16_16_Q15(wptr[0], X[2*BITREV(fft,L2-i-1)+1]); + wptr += 2; + } + normalise16(X, lag, 8192); + /*for (i=0;i>1));*/ + n = 1+(8192>>(celt_ilog2(1+MULT16_16(Xr,Xr)+MULT16_16(Xi,Xi))>>1)); + /* Pre-multiply X by n, so we can keep everything in 16 bits */ + Xr = MULT16_16_16(n, Xr); + Xi = MULT16_16_16(n, Xi); +#else + n = celt_rsqrt(EPSILON+curve[i]); + /* Pre-multiply X by n, so we can keep everything in 16 bits */ + Xr = EXTRACT16(SHR32(MULT16_16(n, Xr),3)); + Xi = EXTRACT16(SHR32(MULT16_16(n, Xi),3)); +#endif + /* Cross-spectrum between X and conj(Y) */ + *Xptr++ = ADD16(MULT16_16_Q15(Xr, Yptr[0]), MULT16_16_Q15(Xi,Yptr[1])); + *Xptr++ = SUB16(MULT16_16_Q15(Xr, Yptr[1]), MULT16_16_Q15(Xi,Yptr[0])); + Yptr += 2; + } + /*printf ("\n");*/ + X[0] = X[1] = 0; + /*for (i=0;i +#include "os_support.h" +#include "arch.h" +#include "stack_alloc.h" +#include "mathops.h" + +/* The Vorbis freq<->Bark mapping */ +#define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n)) +#define fromBARK(z) (102.f*(z)-2.f*pow(z,2.f)+.4f*pow(z,3.f)+pow(1.46f,z)-1.f) + +#ifndef STATIC_MODES +/* Psychoacoustic spreading function. The idea here is compute a first order + recursive filter. The filter coefficient is frequency dependent and + chosen such that we have a -10dB/Bark slope on the right side and a -25dB/Bark + slope on the left side. */ +void psydecay_init(struct PsyDecay *decay, int len, celt_int32_t Fs) +{ + int i; + + celt_word16_t *decayR = (celt_word16_t*)celt_alloc(sizeof(celt_word16_t)*len); + decay->decayR = decayR; + if (decayR==NULL) + return; + + for (i=0;iBark function (see above) */ + deriv = (8.288e-8 * f)/(3.4225e-16 *f*f*f*f + 1) + .009694/(5.476e-7 *f*f + 1) + 1e-4; + /* Back to FFT bin units */ + deriv *= Fs*(1/(2.f*len)); + /* decay corresponding to -10dB/Bark */ + decayR[i] = Q15ONE*pow(.1f, deriv); + /* decay corresponding to -25dB/Bark */ + /*decay->decayL[i] = Q15ONE*pow(0.0031623f, deriv);*/ + /*printf ("%f %f\n", decayL[i], decayR[i]);*/ + } +} + +void psydecay_clear(struct PsyDecay *decay) +{ + celt_free((celt_word16_t *)decay->decayR); + /*celt_free(decay->decayL);*/ +} +#endif + +static void spreading_func(const struct PsyDecay *d, celt_word32_t * celt_restrict psd, int len) +{ + int i; + celt_word32_t mem; + /* Compute right slope (-10 dB/Bark) */ + mem=psd[0]; + for (i=0;idecayR[i],mem-psd[i]); + mem = psd[i]; + } + /* Compute left slope (-25 dB/Bark) */ + mem=psd[len-1]; + for (i=len-1;i>=0;i--) + { + /* Left side has around twice the slope as the right side, so we just + square the coef instead of storing two sets of decay coefs */ + celt_word16_t decayL = MULT16_16_Q15(d->decayR[i], d->decayR[i]); + /* psd = (1-decay)*psd + decay*mem */ + psd[i] = EPSILON + psd[i] + MULT16_32_Q15(decayL,mem-psd[i]); + mem = psd[i]; + } +#if 0 /* Prints signal and mask energy per critical band */ + for (i=0;i<25;i++) + { + int start,end; + int j; + celt_word32_t Esig=0, Emask=0; + start = (int)floor(fromBARK((float)i)*(2*len)/Fs); + if (start<0) + start = 0; + end = (int)ceil(fromBARK((float)(i+1))*(2*len)/Fs); + if (end<=start) + end = start+1; + if (end>len-1) + end = len-1; + for (j=start;j>1; + mask[0] = MULT16_16(X[0], X[0]); + for (i=1;i>2; + + mem2 = mem+2*N; + X[0] = 0; + X[1] = 0; + tonality[0] = 1; + for (i=1;i1) + tonality[i] = 1; + if (tonality[i]<.02) + tonality[i]=.02; + } +} +#endif diff --git a/lib/libcelt/psy.h b/lib/libcelt/psy.h new file mode 100755 index 0000000..3967e37 --- /dev/null +++ b/lib/libcelt/psy.h @@ -0,0 +1,56 @@ +/* (C) 2007 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef PSY_H +#define PSY_H + +#include "arch.h" +#include "celt.h" + +struct PsyDecay { + /*celt_word16_t *decayL;*/ + const celt_word16_t * celt_restrict decayR; +}; + +/** Pre-compute the decay of the psycho-acoustic spreading function */ +void psydecay_init(struct PsyDecay *decay, int len, celt_int32_t Fs); + +/** Free the memory allocated for the spreading function */ +void psydecay_clear(struct PsyDecay *decay); + +/** Compute the masking curve for an input (DFT) spectrum X */ +void compute_masking(const struct PsyDecay *decay, celt_word16_t *X, celt_mask_t * celt_restrict mask, int len); + +/** Compute the masking curve for an input (MDCT) spectrum X */ +void compute_mdct_masking(const struct PsyDecay *decay, celt_word32_t *X, celt_word16_t *tonality, celt_word16_t *long_window, celt_mask_t *mask, int len); + +void compute_tonality(const CELTMode *m, celt_word16_t * celt_restrict X, celt_word16_t * mem, int len, celt_word16_t *tonality, int mdct_size); + +#endif /* PSY_H */ diff --git a/lib/libcelt/quant_bands.c b/lib/libcelt/quant_bands.c new file mode 100755 index 0000000..945ea61 --- /dev/null +++ b/lib/libcelt/quant_bands.c @@ -0,0 +1,325 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "quant_bands.h" +#include "laplace.h" +#include +#include "os_support.h" +#include "arch.h" +#include "mathops.h" +#include "stack_alloc.h" + +#ifdef FIXED_POINT +const celt_word16_t eMeans[24] = {1920, -341, -512, -107, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +#else +const celt_word16_t eMeans[24] = {7.5f, -1.33f, -2.f, -0.42f, 0.17f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; +#endif + +int intra_decision(celt_word16_t *eBands, celt_word16_t *oldEBands, int len) +{ + int i; + celt_word32_t dist = 0; + for (i=0;i 2*len; +} + +int *quant_prob_alloc(const CELTMode *m) +{ + int i; + int *prob; + prob = celt_alloc(4*m->nbEBands*sizeof(int)); + if (prob==NULL) + return NULL; + for (i=0;inbEBands;i++) + { + prob[2*i] = 6000-i*200; + prob[2*i+1] = ec_laplace_get_start_freq(prob[2*i]); + } + for (i=0;inbEBands;i++) + { + prob[2*m->nbEBands+2*i] = 9000-i*240; + prob[2*m->nbEBands+2*i+1] = ec_laplace_get_start_freq(prob[2*m->nbEBands+2*i]); + } + return prob; +} + +void quant_prob_free(int *freq) +{ + celt_free(freq); +} + +#ifdef FMOD_CELT_ENCODER + +unsigned quant_coarse_energy(const CELTMode *m, celt_word16_t *eBands, celt_word16_t *oldEBands, int budget, int intra, int *prob, celt_word16_t *error, ec_enc *enc) +{ + int i, c; + unsigned bits; + unsigned bits_used = 0; + celt_word16_t prev[2] = {0,0}; + celt_word16_t coef = m->ePredCoef; + celt_word16_t beta; + const int C = CHANNELS(m); + + if (intra) + { + coef = 0; + prob += 2*m->nbEBands; + } + /* The .8 is a heuristic */ + beta = MULT16_16_Q15(QCONST16(.8f,15),coef); + + bits = ec_enc_tell(enc, 0); + /* Encode at a fixed coarse resolution */ + for (i=0;inbEBands;i++) + { + c=0; + do { + int qi; + celt_word16_t q; /* dB */ + celt_word16_t x; /* dB */ + celt_word16_t f; /* Q8 */ + celt_word16_t mean = MULT16_16_Q15(Q15ONE-coef,eMeans[i]); + x = eBands[i+c*m->nbEBands]; +#ifdef FIXED_POINT + f = x-mean -MULT16_16_Q15(coef,oldEBands[i+c*m->nbEBands])-prev[c]; + /* Rounding to nearest integer here is really important! */ + qi = (f+128)>>8; +#else + f = x-mean-coef*oldEBands[i+c*m->nbEBands]-prev[c]; + /* Rounding to nearest integer here is really important! */ + qi = (int)floor(.5f+f); +#endif + /* If we don't have enough bits to encode all the energy, just assume something safe. + We allow slightly busting the budget here */ + bits_used=ec_enc_tell(enc, 0) - bits; + if (bits_used > budget) + { + qi = -1; + error[i+c*m->nbEBands] = 128; + } else { + ec_laplace_encode_start(enc, &qi, prob[2*i], prob[2*i+1]); + error[i+c*m->nbEBands] = f - SHL16(qi,8); + } + q = qi*DB_SCALING; + + oldEBands[i+c*m->nbEBands] = MULT16_16_Q15(coef,oldEBands[i+c*m->nbEBands])+(mean+prev[c]+q); + prev[c] = mean+prev[c]+MULT16_16_Q15(Q15ONE-beta,q); + } while (++c < C); + } + return bits_used; +} + + +void quant_fine_energy(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, celt_word16_t *error, int *fine_quant, ec_enc *enc) +{ + int i, c; + const int C = CHANNELS(m); + + /* Encode finer resolution */ + for (i=0;inbEBands;i++) + { + celt_int16_t frac = 1<nbEBands]+QCONST16(.5f,8))>>(8-fine_quant[i]); +#else + q2 = (int)floor((error[i+c*m->nbEBands]+.5f)*frac); +#endif + if (q2 > frac-1) + q2 = frac-1; + ec_enc_bits(enc, q2, fine_quant[i]); +#ifdef FIXED_POINT + offset = SUB16(SHR16(SHL16(q2,8)+QCONST16(.5,8),fine_quant[i]),QCONST16(.5f,8)); +#else + offset = (q2+.5f)*(1<<(14-fine_quant[i]))*(1.f/16384) - .5f; +#endif + oldEBands[i+c*m->nbEBands] += offset; + error[i+c*m->nbEBands] -= offset; + eBands[i+c*m->nbEBands] = log2Amp(oldEBands[i+c*m->nbEBands]); + /*printf ("%f ", error[i] - offset);*/ + } while (++c < C); + } + for (i=0;inbEBands;i++) + eBands[i] = log2Amp(oldEBands[i]); +} + +void quant_energy_finalise(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, celt_word16_t *error, int *fine_quant, int *fine_priority, int bits_left, ec_enc *enc) +{ + int i, prio, c; + const int C = CHANNELS(m); + + /* Use up the remaining bits */ + for (prio=0;prio<2;prio++) + { + for (i=0;inbEBands && bits_left>=C ;i++) + { + if (fine_quant[i] >= 7 || fine_priority[i]!=prio) + continue; + c=0; + do { + int q2; + celt_word16_t offset; + q2 = error[i+c*m->nbEBands]<0 ? 0 : 1; + ec_enc_bits(enc, q2, 1); +#ifdef FIXED_POINT + offset = SHR16(SHL16(q2,8)-QCONST16(.5,8),fine_quant[i]+1); +#else + offset = (q2-.5f)*(1<<(14-fine_quant[i]-1))*(1.f/16384); +#endif + oldEBands[i+c*m->nbEBands] += offset; + bits_left--; + } while (++c < C); + } + } + for (i=0;inbEBands;i++) + { + eBands[i] = log2Amp(oldEBands[i]); + if (oldEBands[i] < -QCONST16(7.f,8)) + oldEBands[i] = -QCONST16(7.f,8); + } +} + +#endif + +void unquant_coarse_energy(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, int budget, int intra, int *prob, ec_dec *dec) +{ + int i, c; + unsigned bits; + celt_word16_t prev[2] = {0, 0}; + celt_word16_t coef = m->ePredCoef; + celt_word16_t beta; + const int C = CHANNELS(m); + + if (intra) + { + coef = 0; + prob += 2*m->nbEBands; + } + /* The .8 is a heuristic */ + beta = MULT16_16_Q15(QCONST16(.8f,15),coef); + + bits = ec_dec_tell(dec, 0); + /* Decode at a fixed coarse resolution */ + for (i=0;inbEBands;i++) + { + c=0; + do { + int qi; + celt_word16_t q; + celt_word16_t mean = MULT16_16_Q15(Q15ONE-coef,eMeans[i]); + /* If we didn't have enough bits to encode all the energy, just assume something safe. + We allow slightly busting the budget here */ + if (ec_dec_tell(dec, 0) - bits > budget) + qi = -1; + else + qi = ec_laplace_decode_start(dec, prob[2*i], prob[2*i+1]); + q = qi*DB_SCALING; + + oldEBands[i+c*m->nbEBands] = MULT16_16_Q15(coef,oldEBands[i+c*m->nbEBands])+(mean+prev[c]+q); + prev[c] = mean+prev[c]+MULT16_16_Q15(Q15ONE-beta,q); + } while (++c < C); + } +} + +void unquant_fine_energy(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, int *fine_quant, ec_dec *dec) +{ + int i, c; + const int C = CHANNELS(m); + /* Decode finer resolution */ + for (i=0;inbEBands;i++) + { + if (fine_quant[i] <= 0) + continue; + c=0; + do { + int q2; + celt_word16_t offset; + q2 = ec_dec_bits(dec, fine_quant[i]); +#ifdef FIXED_POINT + offset = SUB16(SHR16(SHL16(q2,8)+QCONST16(.5,8),fine_quant[i]),QCONST16(.5f,8)); +#else + offset = (q2+.5f)*(1<<(14-fine_quant[i]))*(1.f/16384) - .5f; +#endif + oldEBands[i+c*m->nbEBands] += offset; + } while (++c < C); + } + for (i=0;inbEBands;i++) + eBands[i] = log2Amp(oldEBands[i]); +} + +void unquant_energy_finalise(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, int *fine_quant, int *fine_priority, int bits_left, ec_dec *dec) +{ + int i, prio, c; + const int C = CHANNELS(m); + + /* Use up the remaining bits */ + for (prio=0;prio<2;prio++) + { + for (i=0;inbEBands && bits_left>=C ;i++) + { + if (fine_quant[i] >= 7 || fine_priority[i]!=prio) + continue; + c=0; + do { + int q2; + celt_word16_t offset; + q2 = ec_dec_bits(dec, 1); +#ifdef FIXED_POINT + offset = SHR16(SHL16(q2,8)-QCONST16(.5,8),fine_quant[i]+1); +#else + offset = (q2-.5f)*(1<<(14-fine_quant[i]-1))*(1.f/16384); +#endif + oldEBands[i+c*m->nbEBands] += offset; + bits_left--; + } while (++c < C); + } + } + for (i=0;inbEBands;i++) + { + eBands[i] = log2Amp(oldEBands[i]); + if (oldEBands[i] < -QCONST16(7.f,8)) + oldEBands[i] = -QCONST16(7.f,8); + } +} diff --git a/lib/libcelt/quant_bands.h b/lib/libcelt/quant_bands.h new file mode 100755 index 0000000..004a3cd --- /dev/null +++ b/lib/libcelt/quant_bands.h @@ -0,0 +1,70 @@ +/* (C) 2007 Jean-Marc Valin, CSIRO + */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef QUANT_BANDS +#define QUANT_BANDS + +#include "arch.h" +#include "modes.h" +#include "entenc.h" +#include "entdec.h" +#include "mathops.h" + +static FMOD_INLINE celt_word16_t amp2Log(celt_word32_t amp) +{ + return celt_log2(MAX32(QCONST32(.001f,14),SHL32(amp,2))); +} + +static FMOD_INLINE celt_word32_t log2Amp(celt_word16_t lg) +{ + return PSHR32(celt_exp2(SHL16(lg,3)),4); +} + +int *quant_prob_alloc(const CELTMode *m); +void quant_prob_free(int *freq); + +void compute_fine_allocation(const CELTMode *m, int *bits, int budget); + +int intra_decision(celt_word16_t *eBands, celt_word16_t *oldEBands, int len); + +unsigned quant_coarse_energy(const CELTMode *m, celt_word16_t *eBands, celt_word16_t *oldEBands, int budget, int intra, int *prob, celt_word16_t *error, ec_enc *enc); + +void quant_fine_energy(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, celt_word16_t *error, int *fine_quant, ec_enc *enc); + +void quant_energy_finalise(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, celt_word16_t *error, int *fine_quant, int *fine_priority, int bits_left, ec_enc *enc); + +void unquant_coarse_energy(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, int budget, int intra, int *prob, ec_dec *dec); + +void unquant_fine_energy(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, int *fine_quant, ec_dec *dec); + +void unquant_energy_finalise(const CELTMode *m, celt_ener_t *eBands, celt_word16_t *oldEBands, int *fine_quant, int *fine_priority, int bits_left, ec_dec *dec); + +#endif /* QUANT_BANDS */ diff --git a/lib/libcelt/rangedec.c b/lib/libcelt/rangedec.c new file mode 100755 index 0000000..f77d206 --- /dev/null +++ b/lib/libcelt/rangedec.c @@ -0,0 +1,226 @@ +/* (C) 2001-2008 Timothy B. Terriberry + (C) 2008 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "arch.h" +#include "entdec.h" +#include "mfrngcod.h" + + + +/*A range decoder. + This is an entropy decoder based upon \cite{Mar79}, which is itself a + rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}. + It is very similar to arithmetic encoding, except that encoding is done with + digits in any base, instead of with bits, and so it is faster when using + larger bases (i.e.: a byte). + The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$ + is the base, longer than the theoretical optimum, but to my knowledge there + is no published justification for this claim. + This only seems true when using near-infinite precision arithmetic so that + the process is carried out with no rounding errors. + + IBM (the author's employer) never sought to patent the idea, and to my + knowledge the algorithm is unencumbered by any patents, though its + performance is very competitive with proprietary arithmetic coding. + The two are based on very similar ideas, however. + An excellent description of implementation details is available at + http://www.arturocampos.com/ac_range.html + A recent work \cite{MNW98} which proposes several changes to arithmetic + encoding for efficiency actually re-discovers many of the principles + behind range encoding, and presents a good theoretical analysis of them. + + @PHDTHESIS{Pas76, + author="Richard Clark Pasco", + title="Source coding algorithms for fast data compression", + school="Dept. of Electrical Engineering, Stanford University", + address="Stanford, CA", + month=May, + year=1976 + } + @INPROCEEDINGS{Mar79, + author="Martin, G.N.N.", + title="Range encoding: an algorithm for removing redundancy from a digitised + message", + booktitle="Video & Data Recording Conference", + year=1979, + address="Southampton", + month=Jul + } + @ARTICLE{MNW98, + author="Alistair Moffat and Radford Neal and Ian H. Witten", + title="Arithmetic Coding Revisited", + journal="{ACM} Transactions on Information Systems", + year=1998, + volume=16, + number=3, + pages="256--294", + month=Jul, + URL="http://www.stanford.edu/class/ee398/handouts/papers/Moffat98ArithmCoding.pdf" + }*/ + + +/*Gets the next byte of input. + After all the bytes in the current packet have been consumed, and the extra + end code returned if needed, this function will continue to return zero each + time it is called. + Return: The next byte of input.*/ +static int ec_dec_in(ec_dec *_this){ + int ret; + ret=ec_byte_read1(_this->buf); + if(ret<0){ + ret=0; + /*Needed to keep oc_dec_tell() operating correctly.*/ + ec_byte_adv1(_this->buf); + } + return ret; +} + +/*Normalizes the contents of dif and rng so that rng lies entirely in the + high-order symbol.*/ +static FMOD_INLINE void ec_dec_normalize(ec_dec *_this){ + /*If the range is too small, rescale it and input some bits.*/ + while(_this->rng<=EC_CODE_BOT){ + int sym; + _this->rng<<=EC_SYM_BITS; + /*Use up the remaining bits from our last symbol.*/ + sym=_this->rem<rem=ec_dec_in(_this); + /*Take the rest of the bits we need from this new symbol.*/ + sym|=_this->rem>>EC_SYM_BITS-EC_CODE_EXTRA; + _this->dif=(_this->dif<dif>EC_CODE_TOP)_this->dif-=EC_CODE_TOP;*/ + _this->dif^=_this->dif&_this->dif-1&EC_CODE_TOP; + } +} + +void ec_dec_init(ec_dec *_this,ec_byte_buffer *_buf){ + _this->buf=_buf; + _this->rem=ec_dec_in(_this); + _this->rng=1U<dif=_this->rng-(_this->rem>>EC_SYM_BITS-EC_CODE_EXTRA); + /*Normalize the interval.*/ + ec_dec_normalize(_this); +} + + +unsigned ec_decode(ec_dec *_this,unsigned _ft){ + unsigned s; + _this->nrm=_this->rng/_ft; + s=(unsigned)((_this->dif-1)/_this->nrm); + return _ft-EC_MINI(s+1,_ft); +} + +unsigned ec_decode_bin(ec_dec *_this,unsigned bits){ + unsigned s; + ec_uint32 ft; + ft = (ec_uint32)1<nrm=_this->rng>>bits; + s=(unsigned)((_this->dif-1)/_this->nrm); + return ft-EC_MINI(s+1,ft); +} + +void ec_dec_update(ec_dec *_this,unsigned _fl,unsigned _fh,unsigned _ft){ + ec_uint32 s; + s=IMUL32(_this->nrm,(_ft-_fh)); + _this->dif-=s; + _this->rng=_fl>0?IMUL32(_this->nrm,(_fh-_fl)):_this->rng-s; + ec_dec_normalize(_this); +} + +long ec_dec_tell(ec_dec *_this,int _b){ + ec_uint32 r; + int l; + long nbits; + nbits=(ec_byte_bytes(_this->buf)-(EC_CODE_BITS+EC_SYM_BITS-1)/EC_SYM_BITS)* + EC_SYM_BITS; + /*To handle the non-integral number of bits still left in the encoder state, + we compute the number of bits of low that must be encoded to ensure that + the value is inside the range for any possible subsequent bits. + Note that this is subtly different than the actual value we would end the + stream with, which tries to make as many of the trailing bits zeros as + possible.*/ + nbits+=EC_CODE_BITS; + nbits<<=_b; + l=EC_ILOG(_this->rng); + r=_this->rng>>l-16; + while(_b-->0){ + int b; + r=r*r>>15; + b=(int)(r>>16); + l=l<<1|b; + r>>=b; + } + return nbits-l; +} + +#if 0 +int ec_dec_done(ec_dec *_this){ + unsigned low; + int ret; + /*Check to make sure we've used all the input bytes. + This ensures that no more ones would ever be inserted into the decoder.*/ + if(_this->buf->ptr-ec_byte_get_buffer(_this->buf)<= + ec_byte_bytes(_this->buf)){ + return 0; + } + /*We compute the smallest finitely odd fraction that fits inside the current + range, and write that to the stream. + This is guaranteed to yield the smallest possible encoding.*/ + /*TODO: Fix this line, as it is wrong. + It doesn't seem worth being able to make this check to do an extra + subtraction for every symbol decoded.*/ + low=/*What we want: _this->top-_this->rng; What we have:*/_this->dif + if(low){ + unsigned end; + end=EC_CODE_TOP; + /*Ensure that the next free end is in the range.*/ + if(end-low>=_this->rng){ + unsigned msk; + msk=EC_CODE_TOP-1; + do{ + msk>>=1; + end=(low+msk)&~msk|msk+1; + } + while(end-low>=_this->rng); + } + /*The remaining input should have been the next free end.*/ + return end-low!=_this->dif; + } + return 1; +} +#endif diff --git a/lib/libcelt/rangeenc.c b/lib/libcelt/rangeenc.c new file mode 100755 index 0000000..2c45ead --- /dev/null +++ b/lib/libcelt/rangeenc.c @@ -0,0 +1,196 @@ +/* (C) 2001-2008 Timothy B. Terriberry + (C) 2008 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef FMOD_CELT_ENCODER + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "arch.h" +#include "entenc.h" +#include "mfrngcod.h" + + + +/*A range encoder. + See rangedec.c and the references for implementation details + \cite{Mar79,MNW98}. + + @INPROCEEDINGS{Mar79, + author="Martin, G.N.N.", + title="Range encoding: an algorithm for removing redundancy from a digitised + message", + booktitle="Video \& Data Recording Conference", + year=1979, + address="Southampton", + month=Jul + } + @ARTICLE{MNW98, + author="Alistair Moffat and Radford Neal and Ian H. Witten", + title="Arithmetic Coding Revisited", + journal="{ACM} Transactions on Information Systems", + year=1998, + volume=16, + number=3, + pages="256--294", + month=Jul, + URL="http://www.stanford.edu/class/ee398/handouts/papers/Moffat98ArithmCoding.pdf" + }*/ + + + +/*Outputs a symbol, with a carry bit. + If there is a potential to propagate a carry over several symbols, they are + buffered until it can be determined whether or not an actual carry will + occur. + If the counter for the buffered symbols overflows, then the stream becomes + undecodable. + This gives a theoretical limit of a few billion symbols in a single packet on + 32-bit systems. + The alternative is to truncate the range in order to force a carry, but + requires similar carry tracking in the decoder, needlessly slowing it down.*/ +static void ec_enc_carry_out(ec_enc *_this,int _c){ + if(_c!=EC_SYM_MAX){ + /*No further carry propagation possible, flush buffer.*/ + int carry; + carry=_c>>EC_SYM_BITS; + /*Don't output a byte on the first write. + This compare should be taken care of by branch-prediction thereafter.*/ + if(_this->rem>=0)ec_byte_write1(_this->buf,_this->rem+carry); + if(_this->ext>0){ + unsigned sym; + sym=EC_SYM_MAX+carry&EC_SYM_MAX; + do ec_byte_write1(_this->buf,sym); + while(--(_this->ext)>0); + } + _this->rem=_c&EC_SYM_MAX; + } + else _this->ext++; +} + +static FMOD_INLINE void ec_enc_normalize(ec_enc *_this){ + /*If the range is too small, output some bits and rescale it.*/ + while(_this->rng<=EC_CODE_BOT){ + ec_enc_carry_out(_this,(int)(_this->low>>EC_CODE_SHIFT)); + /*Move the next-to-high-order symbol into the high-order position.*/ + _this->low=_this->low<rng<<=EC_SYM_BITS; + } +} + +void ec_enc_init(ec_enc *_this,ec_byte_buffer *_buf){ + _this->buf=_buf; + _this->rem=-1; + _this->ext=0; + _this->low=0; + _this->rng=EC_CODE_TOP; +} + +void ec_encode(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _ft){ + ec_uint32 r; + r=_this->rng/_ft; + if(_fl>0){ + _this->low+=_this->rng-IMUL32(r,(_ft-_fl)); + _this->rng=IMUL32(r,(_fh-_fl)); + } + else _this->rng-=IMUL32(r,(_ft-_fh)); + ec_enc_normalize(_this); +} + +void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned bits){ + ec_uint32 r, ft; + r=_this->rng>>bits; + ft = (ec_uint32)1<0){ + _this->low+=_this->rng-IMUL32(r,(ft-_fl)); + _this->rng=IMUL32(r,(_fh-_fl)); + } + else _this->rng-=IMUL32(r,(ft-_fh)); + ec_enc_normalize(_this); +} + +long ec_enc_tell(ec_enc *_this,int _b){ + ec_uint32 r; + int l; + long nbits; + nbits=(ec_byte_bytes(_this->buf)+(_this->rem>=0)+_this->ext)*EC_SYM_BITS; + /*To handle the non-integral number of bits still left in the encoder state, + we compute the number of bits of low that must be encoded to ensure that + the value is inside the range for any possible subsequent bits. + Note that this is subtly different than the actual value we would end the + stream with, which tries to make as many of the trailing bits zeros as + possible.*/ + nbits+=EC_CODE_BITS; + nbits<<=_b; + l=EC_ILOG(_this->rng); + r=_this->rng>>l-16; + while(_b-->0){ + int b; + r=r*r>>15; + b=(int)(r>>16); + l=l<<1|b; + r>>=b; + } + return nbits-l; +} + +void ec_enc_done(ec_enc *_this){ + /*We compute the integer in the current interval that has the largest number + of trailing zeros, and write that to the stream. + This is guaranteed to yield the smallest possible encoding.*/ + if(_this->low){ + ec_uint32 end; + end=EC_CODE_TOP; + /*Ensure that the end value is in the range.*/ + if(end-_this->low>=_this->rng){ + ec_uint32 msk; + msk=EC_CODE_TOP-1; + do{ + msk>>=1; + end=_this->low+msk&~msk|msk+1; + } + while(end-_this->low>=_this->rng); + } + /*The remaining output is the next free end.*/ + while(end){ + ec_enc_carry_out(_this,end>>EC_CODE_SHIFT); + end=end<rem>0||_this->ext>0){ + ec_enc_carry_out(_this,0); + _this->rem=-1; + } +} + +#endif /* FMOD_CELT_ENCODER */ \ No newline at end of file diff --git a/lib/libcelt/rate.c b/lib/libcelt/rate.c new file mode 100755 index 0000000..9248396 --- /dev/null +++ b/lib/libcelt/rate.c @@ -0,0 +1,212 @@ +/* (C) 2007-2009 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "modes.h" +#include "cwrs.h" +#include "arch.h" +#include "os_support.h" + +#include "entcode.h" +#include "rate.h" + + +#ifndef STATIC_MODES + +celt_int16_t **compute_alloc_cache(CELTMode *m, int C) +{ + int i, prevN; + int error = 0; + celt_int16_t **bits; + const celt_int16_t *eBands = m->eBands; + + bits = celt_alloc(m->nbEBands*sizeof(celt_int16_t*)); + if (bits==NULL) + return NULL; + + prevN = -1; + for (i=0;inbEBands;i++) + { + int N = C*(eBands[i+1]-eBands[i]); + if (N == prevN && eBands[i] < m->pitchEnd) + { + bits[i] = bits[i-1]; + } else { + bits[i] = celt_alloc(MAX_PULSES*sizeof(celt_int16_t)); + if (bits[i]!=NULL) { + get_required_bits(bits[i], N, MAX_PULSES, BITRES); + } else { + error=1; + } + prevN = N; + } + } + if (error) + { + const celt_int16_t *prevPtr = NULL; + if (bits!=NULL) + { + for (i=0;inbEBands;i++) + { + if (bits[i] != prevPtr) + { + prevPtr = bits[i]; + celt_free((int*)bits[i]); + } + } + free(bits); + bits=NULL; + } + } + return bits; +} + +#endif /* !STATIC_MODES */ + + + +static void interp_bits2pulses(const CELTMode *m, int *bits1, int *bits2, int total, int *bits, int *ebits, int *fine_priority, int len) +{ + int psum; + int lo, hi; + int j; + const int C = CHANNELS(m); + SAVE_STACK; + lo = 0; + hi = 1<>1; + psum = 0; + for (j=0;j (total<eBands[j+1]-m->eBands[j]; + d=C*N<= offset; + + /* Make sure not to bust */ + if (C*ebits[j] > (bits[j]>>BITRES)) + ebits[j] = bits[j]/C >> BITRES; + + if (ebits[j]>7) + ebits[j]=7; + /* The bits used for fine allocation can't be used for pulses */ + bits[j] -= C*ebits[j]<nbEBands; + ALLOC(bits1, len, int); + ALLOC(bits2, len, int); + + lo = 0; + hi = m->nbAllocVectors - 1; + while (hi-lo != 1) + { + int psum = 0; + int mid = (lo+hi) >> 1; + for (j=0;jallocVectors[mid*len+j] + offsets[j])< (total<allocVectors[lo*len+j] + offsets[j]; + bits2[j] = m->allocVectors[hi*len+j] + offsets[j]; + if (bits1[j] < 0) + bits1[j] = 0; + if (bits2[j] < 0) + bits2[j] = 0; + } + interp_bits2pulses(m, bits1, bits2, total, pulses, ebits, fine_priority, len); + RESTORE_STACK; +} + diff --git a/lib/libcelt/rate.h b/lib/libcelt/rate.h new file mode 100755 index 0000000..e21b999 --- /dev/null +++ b/lib/libcelt/rate.h @@ -0,0 +1,144 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RATE_H +#define RATE_H + +#define MAX_PULSES 128 +#define LOG_MAX_PULSES 7 + +#define BITRES 4 +#define BITROUND 8 +#define BITOVERFLOW 30000 + +#include "cwrs.h" + +static FMOD_INLINE int bits2pulses(const CELTMode *m, const celt_int16_t *cache, int N, int bits) +{ + int i; + int lo, hi; + lo = 0; + hi = MAX_PULSES-1; + +#if 0 /* Disabled until we can make that useful */ + /* Use of more than MAX_PULSES is disabled until we are able to cwrs that decently */ + if (bits > cache[MAX_PULSES-1] && N<=4) + { + /*int pulses; + pulses = 127; + while (16 + log2_frac(2*(pulses+1)*(pulses+1) + 1, 4) <= bits && pulses < 32767) + pulses++;*/ + lo = 127; + switch (N) + { + case 3: + hi = 1024; + for (i=0;i<10;i++) + { + int pulses = (lo+hi)>>1; + if (log2_frac(((UMUL16_16(pulses,pulses)>>1)+1)>>1, 4) > bits) + hi = pulses; + else + lo = pulses; + } + break; + case 4: + hi = 1024; + for (i=0;i<10;i++) + { + int pulses = (lo+hi)>>1; + if (log2_frac((UMUL32(UMUL16_16(pulses,pulses)+2,pulses))/3<<3, 4) > bits) + hi = pulses; + else + lo = pulses; + } + break; + } + return lo; + } +#endif + /* Instead of using the "bisection condition" we use a fixed number of + iterations because it should be faster */ + /*while (hi-lo != 1)*/ + for (i=0;i>1; + /* OPT: Make sure this is implemented with a conditional move */ + if (cache[mid] >= bits) + hi = mid; + else + lo = mid; + } + if (bits-cache[lo] <= cache[hi]-bits) + return lo; + else + return hi; +} + + +static FMOD_INLINE int pulses2bits(const celt_int16_t *cache, int N, int pulses) +{ +#if 0 /* Use of more than MAX_PULSES is disabled until we are able to cwrs that decently */ + if (pulses > 127) + { + int bits; + switch (N) + { + case 3: + bits = log2_frac(((UMUL16_16(pulses,pulses)>>1)+1)>>1, 4); + break; + case 4: + bits = log2_frac((UMUL32(UMUL16_16(pulses,pulses)+2,pulses))/3<<3, 4); + break; + } + /*printf ("%d <- %d\n", bits, pulses);*/ + return bits; + } +#endif + return cache[pulses]; +} + +/** Computes a cache of the pulses->bits mapping in each band */ +celt_int16_t **compute_alloc_cache(CELTMode *m, int C); + +/** Compute the pulse allocation, i.e. how many pulses will go in each + * band. + @param m mode + @param offsets Requested increase or decrease in the number of bits for + each band + @param total Number of bands + @param pulses Number of pulses per band (returned) + @return Total number of bits allocated +*/ +void compute_allocation(const CELTMode *m, int *offsets, int total, int *pulses, int *ebits, int *fine_priority); + + +#endif diff --git a/lib/libcelt/stack_alloc.h b/lib/libcelt/stack_alloc.h new file mode 100755 index 0000000..a7c4b42 --- /dev/null +++ b/lib/libcelt/stack_alloc.h @@ -0,0 +1,146 @@ +/* Copyright (C) 2002 Jean-Marc Valin */ +/** + @file stack_alloc.h + @brief Temporary memory allocation on stack +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef STACK_ALLOC_H +#define STACK_ALLOC_H + +#define USE_ALLOCA // CPS + +#ifdef USE_ALLOCA +# ifdef WIN32 +# include +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# include +# endif +# endif +#endif + +/** + * @def ALIGN(stack, size) + * + * Aligns the stack to a 'size' boundary + * + * @param stack Stack + * @param size New size boundary + */ + +/** + * @def PUSH(stack, size, type) + * + * Allocates 'size' elements of type 'type' on the stack + * + * @param stack Stack + * @param size Number of elements + * @param type Type of element + */ + +/** + * @def VARDECL(var) + * + * Declare variable on stack + * + * @param var Variable to declare + */ + +/** + * @def ALLOC(var, size, type) + * + * Allocate 'size' elements of 'type' on stack + * + * @param var Name of variable to allocate + * @param size Number of elements + * @param type Type of element + */ + + +#if defined(VAR_ARRAYS) + +#define VARDECL(type, var) +#define ALLOC(var, size, type) type var[size] +#define SAVE_STACK +#define RESTORE_STACK +#define ALLOC_STACK + +#elif defined(USE_ALLOCA) + +#define VARDECL(type, var) type *var +#define ALLOC(var, size, type) var = ((type*)alloca(sizeof(type)*(size))) +#define SAVE_STACK +#define RESTORE_STACK +#define ALLOC_STACK + +#else + +#ifdef CELT_C +char *global_stack=0; +#else +extern char *global_stack; +#endif /*CELT_C*/ + +#ifdef ENABLE_VALGRIND + +#include + +#ifdef CELT_C +char *global_stack_top=0; +#else +extern char *global_stack_top; +#endif /*CELT_C*/ + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) +#define PUSH(stack, size, type) (VALGRIND_MAKE_MEM_NOACCESS(stack, global_stack_top-stack),ALIGN((stack),sizeof(type)/sizeof(char)),VALGRIND_MAKE_MEM_UNDEFINED(stack, ((size)*sizeof(type)/sizeof(char))),(stack)+=(2*(size)*sizeof(type)/sizeof(char)),(type*)((stack)-(2*(size)*sizeof(type)/sizeof(char)))) +#define RESTORE_STACK ((global_stack = _saved_stack),VALGRIND_MAKE_MEM_NOACCESS(global_stack, global_stack_top-global_stack)) +#define ALLOC_STACK ((global_stack = (global_stack==0) ? ((global_stack_top=celt_alloc_scratch(GLOBAL_STACK_SIZE*2)+(GLOBAL_STACK_SIZE*2))-(GLOBAL_STACK_SIZE*2)) : global_stack),VALGRIND_MAKE_MEM_NOACCESS(global_stack, global_stack_top-global_stack)) + +#else + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) +#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)/sizeof(char)),(stack)+=(size)*(sizeof(type)/sizeof(char)),(type*)((stack)-(size)*(sizeof(type)/sizeof(char)))) +#define RESTORE_STACK (global_stack = _saved_stack) +#define ALLOC_STACK (global_stack = (global_stack==0) ? celt_alloc_scratch(GLOBAL_STACK_SIZE) : global_stack) + +#endif /*ENABLE_VALGRIND*/ + +#include "os_support.h" +#define VARDECL(type, var) type *var +#define ALLOC(var, size, type) var = PUSH(global_stack, size, type) +#define SAVE_STACK char *_saved_stack = global_stack; + +#endif /*VAR_ARRAYS*/ + + +#endif /*STACK_ALLOC_H*/ diff --git a/lib/libcelt/vq.c b/lib/libcelt/vq.c new file mode 100755 index 0000000..705e54c --- /dev/null +++ b/lib/libcelt/vq.c @@ -0,0 +1,393 @@ +/* (C) 2007-2008 Jean-Marc Valin, CSIRO +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mathops.h" +#include "cwrs.h" +#include "vq.h" +#include "arch.h" +#include "os_support.h" + +/** Takes the pitch vector and the decoded residual vector, computes the gain + that will give ||p+g*y||=1 and mixes the residual with the pitch. */ +static void mix_pitch_and_residual(int * celt_restrict iy, celt_norm_t * celt_restrict X, int N, int K, const celt_norm_t * celt_restrict P) +{ + int i; + celt_word32_t Ryp, Ryy, Rpp; + celt_word16_t ryp, ryy, rpp; + celt_word32_t g; + VARDECL(celt_norm_t, y); +#ifdef FIXED_POINT + int yshift; +#endif + SAVE_STACK; +#ifdef FIXED_POINT + yshift = 13-celt_ilog2(K); +#endif + ALLOC(y, N, celt_norm_t); + + Rpp = 0; + i=0; + do { + Rpp = MAC16_16(Rpp,P[i],P[i]); + y[i] = SHL16(iy[i],yshift); + } while (++i < N); + + Ryp = 0; + Ryy = 0; + /* If this doesn't generate a dual MAC (on supported archs), fire the compiler guy */ + i=0; + do { + Ryp = MAC16_16(Ryp, y[i], P[i]); + Ryy = MAC16_16(Ryy, y[i], y[i]); + } while (++i < N); + + ryp = ROUND16(Ryp,14); + ryy = ROUND16(Ryy,14); + rpp = ROUND16(Rpp,14); + /* g = (sqrt(Ryp^2 + Ryy - Rpp*Ryy)-Ryp)/Ryy */ + g = MULT16_32_Q15(celt_sqrt(MAC16_16(Ryy, ryp,ryp) - MULT16_16(ryy,rpp)) - ryp, + celt_rcp(SHR32(Ryy,9))); + + i=0; + do + X[i] = ADD16(P[i], ROUND16(MULT16_16(y[i], g),11)); + while (++i < N); + + RESTORE_STACK; +} + +#ifdef FMOD_CELT_ENCODER + +void alg_quant(celt_norm_t *X, celt_mask_t *W, int N, int K, celt_norm_t *P, ec_enc *enc) +{ + VARDECL(celt_norm_t, y); + VARDECL(int, iy); + VARDECL(celt_word16_t, signx); + int j, is; + celt_word16_t s; + int pulsesLeft; + celt_word32_t sum; + celt_word32_t xy, yy, yp; + celt_word16_t Rpp; + int N_1; /* Inverse of N, in Q14 format (even for float) */ +#ifdef FIXED_POINT + int yshift; +#endif + SAVE_STACK; + +#ifdef FIXED_POINT + yshift = 13-celt_ilog2(K); +#endif + + ALLOC(y, N, celt_norm_t); + ALLOC(iy, N, int); + ALLOC(signx, N, celt_word16_t); + N_1 = 512/N; + + sum = 0; + j=0; do { + X[j] -= P[j]; + if (X[j]>0) + signx[j]=1; + else { + signx[j]=-1; + X[j]=-X[j]; + P[j]=-P[j]; + } + iy[j] = 0; + y[j] = 0; + sum = MAC16_16(sum, P[j],P[j]); + } while (++j (N>>1)) + { + celt_word16_t rcp; + sum=0; + j=0; do { + sum += X[j]; + } while (++j=1, "Allocated too many pulses in the quick pass"); + + while (pulsesLeft > 1) + { + int pulsesAtOnce=1; + int best_id; + celt_word16_t magnitude; + celt_word32_t best_num = -VERY_LARGE16; + celt_word16_t best_den = 0; +#ifdef FIXED_POINT + int rshift; +#endif + /* Decide on how many pulses to find at once */ + pulsesAtOnce = (pulsesLeft*N_1)>>9; /* pulsesLeft/N */ + if (pulsesAtOnce<1) + pulsesAtOnce = 1; +#ifdef FIXED_POINT + rshift = yshift+1+celt_ilog2(K-pulsesLeft+pulsesAtOnce); +#endif + magnitude = SHL16(pulsesAtOnce, yshift); + + best_id = 0; + /* The squared magnitude term gets added anyway, so we might as well + add it outside the loop */ + yy = MAC16_16(yy, magnitude,magnitude); + /* Choose between fast and accurate strategy depending on where we are in the search */ + /* This should ensure that anything we can process will have a better score */ + j=0; + do { + celt_word16_t Rxy, Ryy; + /* Select sign based on X[j] alone */ + s = magnitude; + /* Temporary sums of the new pulse(s) */ + Rxy = EXTRACT16(SHR32(MAC16_16(xy, s,X[j]),rshift)); + /* We're multiplying y[j] by two so we don't have to do it here */ + Ryy = EXTRACT16(SHR32(MAC16_16(yy, s,y[j]),rshift)); + + /* Approximate score: we maximise Rxy/sqrt(Ryy) (we're guaranteed that + Rxy is positive because the sign is pre-computed) */ + Rxy = MULT16_16_Q15(Rxy,Rxy); + /* The idea is to check for num/den >= best_num/best_den, but that way + we can do it without any division */ + /* OPT: Make sure to use conditional moves here */ + if (MULT16_16(best_den, Rxy) > MULT16_16(Ryy, best_num)) + { + best_den = Ryy; + best_num = Rxy; + best_id = j; + } + } while (++j 0) + { + celt_word16_t g; + celt_word16_t best_num = -VERY_LARGE16; + celt_word16_t best_den = 0; + int best_id = 0; + celt_word16_t magnitude = SHL16(1, yshift); + + /* The squared magnitude term gets added anyway, so we might as well + add it outside the loop */ + yy = MAC16_16(yy, magnitude,magnitude); + j=0; + do { + celt_word16_t Rxy, Ryy, Ryp; + celt_word16_t num; + /* Select sign based on X[j] alone */ + s = magnitude; + /* Temporary sums of the new pulse(s) */ + Rxy = ROUND16(MAC16_16(xy, s,X[j]), 14); + /* We're multiplying y[j] by two so we don't have to do it here */ + Ryy = ROUND16(MAC16_16(yy, s,y[j]), 14); + Ryp = ROUND16(MAC16_16(yp, s,P[j]), 14); + + /* Compute the gain such that ||p + g*y|| = 1 + ...but instead, we compute g*Ryy to avoid dividing */ + g = celt_psqrt(MULT16_16(Ryp,Ryp) + MULT16_16(Ryy,QCONST16(1.f,14)-Rpp)) - Ryp; + /* Knowing that gain, what's the error: (x-g*y)^2 + (result is negated and we discard x^2 because it's constant) */ + /* score = 2*g*Rxy - g*g*Ryy;*/ +#ifdef FIXED_POINT + /* No need to multiply Rxy by 2 because we did it earlier */ + num = MULT16_16_Q15(ADD16(SUB16(Rxy,g),Rxy),g); +#else + num = g*(2*Rxy-g); +#endif + if (MULT16_16(best_den, num) > MULT16_16(Ryy, best_num)) + { + best_den = Ryy; + best_num = num; + best_id = j; + } + } while (++j C*N0/16) + offset = C*N0/16; + offset -= offset % (C*B); + if (offset < 0) + offset = 0; + //printf ("%d\n", offset); + id += offset; + }*/ + if (id+C*N>N0*C) + for (j=0;j + +//***************************************************************************// +//* Delay_INIT(...) *// +//***************************************************************************// +int Delay_INIT(unsigned int DelayAmount, + Delay_Struct * pPtr) +{ + int n; + + if(DelayAmount != NEURAL_FRAMESIZE) return UNSUPPORTED_FRAMESIZE; + + pPtr->Pntr = 0; + pPtr->DelayAmount = DelayAmount; + for(n = 0; n < NEURAL_FRAMESIZE+1; n++){ + pPtr->Buffer[n] = 0; + } + + return NRLSUR_OK; +} + + +//***************************************************************************// +//* Delay(...) *// +//***************************************************************************// +int Delay(float * PtrIn, + float * PtrOut, + unsigned int Framesize, + Delay_Struct * pPtr) +{ + int n, Pntr, DelayAmount; + float * Buffer; + + if(Framesize != NEURAL_FRAMESIZE) return UNSUPPORTED_FRAMESIZE; + + Buffer = pPtr->Buffer; + Pntr = pPtr->Pntr; + DelayAmount = pPtr->DelayAmount; + + for(n = 0; n < NEURAL_FRAMESIZE; n++){ + Buffer[Pntr] = PtrIn[n]; + Pntr = ( (Pntr+1) >= (DelayAmount+1) ) ? 0 : (Pntr+1); + PtrOut[n] = Buffer[Pntr]; + } + + pPtr->Pntr = Pntr; + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/FFT_Overlapped.c b/lib/neural_thx/FFT_Overlapped.c new file mode 100755 index 0000000..a623250 --- /dev/null +++ b/lib/neural_thx/FFT_Overlapped.c @@ -0,0 +1,197 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Surround *// +//* File : FFT_Overlapped.c *// +//* Description : A windowed and overlapped FFT and IFFT *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include + +//***************************************************************************// +//* FFT_Overlapped_INIT(...) *// +//***************************************************************************// +int FFT_Overlapped_INIT(FFT_Overlapped_Struct * pPersistent, + float * pTempBuffer0, //Size must be 2*Framesize + float * pTempBuffer1, //Size must be 2*Framesize + unsigned int Framesize) +{ + int n, j; + size_t memNeeded; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + pPersistent->pSineWin = (float*)GetSineWindow(); + + pPersistent->pReal = pTempBuffer0; + pPersistent->pImag = pTempBuffer1; + + for(n = 0; n < HALFFFTSIZE; n++){ + pPersistent->pOverlappedBuffer[n] = 0; + } + + // Let's do something that will speed everything up + memNeeded = FFT_MEM_CHUNK; + pPersistent->state = THX_kiss_fft_alloc(FFTSIZE,0,fft_buff,&memNeeded); + // Lets make some memory for our buffers + for(j = 0; j < FFTSIZE; j++) + { + pPersistent->pIn[j].r = 0.0f; + pPersistent->pIn[j].i = 0.0f; + pPersistent->pOut[j].r = 0.0f; + pPersistent->pOut[j].i = 0.0f; + } + + return NRLSUR_OK; +} + +//***************************************************************************// +//* FFT_Overlapped(...) *// +//***************************************************************************// +int FFT_Overlapped(float * PtrIn, + float * PtrOutReal, + float * PtrOutImag, + unsigned int Framesize, + FFT_Overlapped_Struct * pPersistent) +{ + unsigned int n; + float Win; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + // Put input values into every index as "real" data + for(n = 0; n < HALFFFTSIZE; n++){ + Win = pPersistent->pSineWin[n]; + pPersistent->pIn[n ].r = Win * pPersistent->pOverlappedBuffer[n]; + pPersistent->pIn[n ].i = 0.0f; + pPersistent->pIn[FFTSIZE-n-1].r = Win * PtrIn[HALFFFTSIZE-n-1]; + pPersistent->pIn[FFTSIZE-n-1].i = 0.0f; + //Store in pOverlappedBuffer for next call + pPersistent->pOverlappedBuffer[n] = PtrIn[n]; + } + + // FFT call + THX_kiss_fft(pPersistent->state, + pPersistent->pIn, + pPersistent->pOut); + + // Now parse through the complex output to separate the input signal + for(n = 0; n < HALFFFTSIZE; n++){ + PtrOutReal[n] = pPersistent->pOut[n].r; // Real output + PtrOutImag[n] = pPersistent->pOut[n].i; // Imag output + } + + return NRLSUR_OK; +} + +//***************************************************************************// +//* IFFT_Overlapped_INIT(...) *// +//***************************************************************************// +int IFFT_Overlapped_INIT(IFFT_Overlapped_Struct * pPersistent, + float * pTempBuffer0, //Size must be 2*Framesize + float * pTempBuffer1, //Size must be 2*Framesize + unsigned int Framesize) +{ + int n, j; + size_t memNeeded; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + pPersistent->pSineWin = (float*)GetSineWindow(); + + pPersistent->pReal = pTempBuffer0; + pPersistent->pImag = pTempBuffer1; + + for(n = 0; n < HALFFFTSIZE; n++){ + pPersistent->pOverlappedBuffer[n] = 0; + } + + // Let's do something that will speed everything up + memNeeded = FFT_MEM_CHUNK; + pPersistent->state = THX_kiss_fft_alloc(FFTSIZE,0,fft_buff,&memNeeded); + // Lets make some memory for our buffers + for(j = 0; j < FFTSIZE; j++) + { + pPersistent->pIn[j].r = 0.0f; + pPersistent->pIn[j].i = 0.0f; + pPersistent->pOut[j].r = 0.0f; + pPersistent->pOut[j].i = 0.0f; + } + + return NRLSUR_OK; +} + +//***************************************************************************// +//* IFFT_Overlapped(...) *// +//***************************************************************************// +int IFFT_Overlapped(float * PtrInReal, + float * PtrInImag, + float * PtrOut, + unsigned int Framesize, + IFFT_Overlapped_Struct * pPersistent) +{ + unsigned int n; + float Win; + float coeff; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + for(n = 0; n < HALFFFTSIZE; n++){ + pPersistent->pIn[n].r = PtrInReal[n]; //Real + pPersistent->pIn[n].i = PtrInImag[n]; //Imag + } + //Use the input buffers as temporary buffers + pPersistent->pIn[HALFFFTSIZE].r = 0; + pPersistent->pIn[HALFFFTSIZE].i = 0; + for(n = 0; n < HALFFFTSIZE-1; n++){ + pPersistent->pIn[FFTSIZE-n-1].r = pPersistent->pIn[n+1].r; + pPersistent->pIn[FFTSIZE-n-1].i = -pPersistent->pIn[n+1].i; + } + + // IFFT call + coeff=(float)FFTSIZE; + coeff=1/coeff; + + for(n=0;npIn[n].i = -pPersistent->pIn[n].i; + } + + THX_kiss_fft(pPersistent->state, + pPersistent->pIn, + pPersistent->pOut); + + for(n=0;n < (unsigned int)FFTSIZE;n++) + { + pPersistent->pIn[n].i = -coeff*pPersistent->pIn[n].i; + pPersistent->pIn[n].r = coeff*pPersistent->pIn[n].r; + } + + // Store all even indexes to output 0 and all odd indexes to output 1 + for(n = 0; n < HALFFFTSIZE; n++){ + Win = pPersistent->pSineWin[n]; + PtrOut[n] = Win * pPersistent->pIn[n].r + pPersistent->pOverlappedBuffer[n]; // Real output + } + for(n = 0; n < HALFFFTSIZE; n++){ + Win = pPersistent->pSineWin[n]; + pPersistent->pOverlappedBuffer[HALFFFTSIZE-n-1] = Win * pPersistent->pIn[FFTSIZE-n-1].r; + } + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/FFT_Overlapped_Stereo.c b/lib/neural_thx/FFT_Overlapped_Stereo.c new file mode 100755 index 0000000..852513f --- /dev/null +++ b/lib/neural_thx/FFT_Overlapped_Stereo.c @@ -0,0 +1,209 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Surround *// +//* File : FFT_Overlapped_Stereo.c *// +//* Description : A windowed and overlapped FFT and IFFT *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include + +//***************************************************************************// +//* FFT_Overlapped_Stereo_INIT(...) *// +//***************************************************************************// +int FFT_Overlapped_Stereo_INIT(FFT_Overlapped_Stereo_Struct * pPersistent, + float * pTempBuffer0, //Size must be 2*Framesize + float * pTempBuffer1, //Size must be 2*Framesize + unsigned int Framesize) +{ + int n, j; + size_t memNeeded; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + pPersistent->pSineWin = (float*)GetSineWindow(); + + pPersistent->pReal = pTempBuffer0; + pPersistent->pImag = pTempBuffer1; + + for(n = 0; n < HALFFFTSIZE; n++){ + pPersistent->pOverlappedBuffer0[n] = 0; + pPersistent->pOverlappedBuffer1[n] = 0; + } + + // Let's do something that will speed everything up + memNeeded = FFT_MEM_CHUNK; + pPersistent->state = THX_kiss_fft_alloc(FFTSIZE,0,fft_buff,&memNeeded); + // Lets make some memory for our buffers + for(j = 0; j < FFTSIZE; j++) + { + pPersistent->pIn[j].r = 0.0f; + pPersistent->pIn[j].i = 0.0f; + pPersistent->pOut[j].r = 0.0f; + pPersistent->pOut[j].i = 0.0f; + } + + return NRLSUR_OK; +} + +//***************************************************************************// +//* FFT_Overlapped_Stereo(...) *// +//***************************************************************************// +int FFT_Overlapped_Stereo(float * PtrIn0, + float * PtrOutReal0, + float * PtrOutImag0, + float * PtrIn1, + float * PtrOutReal1, + float * PtrOutImag1, + unsigned int Framesize, + FFT_Overlapped_Stereo_Struct * pPersistent) +{ + unsigned int n; + float Win; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + // Put input0 values into every even index as "real" data and input1 values into every odd index as "imag" data + for(n = 0; n < HALFFFTSIZE; n++){ + Win = pPersistent->pSineWin[n]; + pPersistent->pIn[n ].r = Win * pPersistent->pOverlappedBuffer0[n]; + pPersistent->pIn[n ].i = Win * pPersistent->pOverlappedBuffer1[n]; + pPersistent->pIn[FFTSIZE-n-1].r = Win * PtrIn0[HALFFFTSIZE-n-1]; + pPersistent->pIn[FFTSIZE-n-1].i = Win * PtrIn1[HALFFFTSIZE-n-1]; + //Store in pOverlappedBuffer for next call + pPersistent->pOverlappedBuffer0[n] = PtrIn0[n]; + pPersistent->pOverlappedBuffer1[n] = PtrIn1[n]; + } + + // FFT call + THX_kiss_fft(pPersistent->state, + pPersistent->pIn, + pPersistent->pOut); + + // Pull the information out ... + // Now parse through the complex output to separate the two input signals + PtrOutReal0[0] = pPersistent->pOut[0].r; //Real value of DC coefficient + PtrOutImag0[0] = 0; + PtrOutReal1[0] = pPersistent->pOut[0].i; //Imag value of DC coefficient + PtrOutImag1[0] = 0; + for(n = 1; n < HALFFFTSIZE; n++){ + PtrOutReal0[n] = (float)0.5 * ( pPersistent->pOut[n].r + pPersistent->pOut[FFTSIZE-n].r); // Real output + PtrOutImag0[n] = (float)0.5 * ( pPersistent->pOut[n].i - pPersistent->pOut[FFTSIZE-n].i); // Imag output + PtrOutReal1[n] = (float)0.5 * ( pPersistent->pOut[n].i + pPersistent->pOut[FFTSIZE-n].i); // Real output + PtrOutImag1[n] = (float)0.5 * (-pPersistent->pOut[n].r + pPersistent->pOut[FFTSIZE-n].r); // Imag output + } + + return NRLSUR_OK; +} + +//***************************************************************************// +//* IFFT_Overlapped_Stereo_INIT(...) *// +//***************************************************************************// +int IFFT_Overlapped_Stereo_INIT(IFFT_Overlapped_Stereo_Struct * pPersistent, + float * pTempBuffer0, //Size must be 2*Framesize + float * pTempBuffer1, //Size must be 2*Framesize + unsigned int Framesize) +{ + int n, j; + size_t memNeeded; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + pPersistent->pSineWin = (float*)GetSineWindow(); + + pPersistent->pReal = pTempBuffer0; + pPersistent->pImag = pTempBuffer1; + + for(n = 0; n < HALFFFTSIZE; n++){ + pPersistent->pOverlappedBuffer0[n] = 0; + pPersistent->pOverlappedBuffer1[n] = 0; + } + + // Let's do something that will speed everything up + memNeeded = FFT_MEM_CHUNK; + pPersistent->state = THX_kiss_fft_alloc(FFTSIZE,0,fft_buff,&memNeeded); + // Lets make some memory for our buffers + for(j = 0; j < FFTSIZE; j++) + { + pPersistent->pIn[j].r = 0.0f; + pPersistent->pIn[j].i = 0.0f; + pPersistent->pOut[j].r = 0.0f; + pPersistent->pOut[j].i = 0.0f; + } + + return NRLSUR_OK; +} + +//***************************************************************************// +//* IFFT_Overlapped_Stereo(...) *// +//***************************************************************************// +int IFFT_Overlapped_Stereo(float * PtrInReal0, + float * PtrInImag0, + float * PtrOut0, + float * PtrInReal1, + float * PtrInImag1, + float * PtrOut1, + unsigned int Framesize, + IFFT_Overlapped_Stereo_Struct * pPersistent) +{ + unsigned int n; + float Win; + float coeff; + + if(Framesize != HALFFFTSIZE) return UNSUPPORTED_FRAMESIZE; + + //Sum X1 + j*X2 + for(n = 0; n < HALFFFTSIZE; n++){ + pPersistent->pIn[n].r = PtrInReal0[n] - PtrInImag1[n]; //Real + pPersistent->pIn[n].i = -(PtrInImag0[n] + PtrInReal1[n]); //Imag + } + //Use the input buffers as temporary buffers + pPersistent->pReal[HALFFFTSIZE] = 0; + pPersistent->pImag[HALFFFTSIZE] = 0; + for(n = 0; n < HALFFFTSIZE-1; n++){ + pPersistent->pIn[FFTSIZE-n-1].r = PtrInReal0[n+1] + PtrInImag1[n+1]; + pPersistent->pIn[FFTSIZE-n-1].i = -(-PtrInImag0[n+1] + PtrInReal1[n+1]); + } + + // FFT call + THX_kiss_fft(pPersistent->state, + pPersistent->pIn, + pPersistent->pOut); + + coeff = (float)FFTSIZE; + coeff = 1/coeff; + for(n = 0; n < FFTSIZE; n++) + { + pPersistent->pOut[n].i = -coeff * pPersistent->pOut[n].i; + pPersistent->pOut[n].r = coeff * pPersistent->pOut[n].r; + } + + for(n = 0; n < HALFFFTSIZE; n++){ + Win = pPersistent->pSineWin[n]; + PtrOut0[n] = Win * pPersistent->pOut[n].r + pPersistent->pOverlappedBuffer0[n]; // Real output + PtrOut1[n] = Win * pPersistent->pOut[n].i + pPersistent->pOverlappedBuffer1[n]; // Real output + } + for(n = 0; n < HALFFFTSIZE; n++){ + Win = pPersistent->pSineWin[n]; + pPersistent->pOverlappedBuffer0[HALFFFTSIZE-n-1] = Win * pPersistent->pOut[FFTSIZE-n-1].r; + pPersistent->pOverlappedBuffer1[HALFFFTSIZE-n-1] = Win * pPersistent->pOut[FFTSIZE-n-1].i; + } + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/FastMathApprox.c b/lib/neural_thx/FastMathApprox.c new file mode 100755 index 0000000..4c36a87 --- /dev/null +++ b/lib/neural_thx/FastMathApprox.c @@ -0,0 +1,882 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : FastMathApprox.c *// +//* Description : Fast math approximations *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include +#include "fmod_types.h" + +//***************************************************************************// +//* FastMag(...) *// +//***************************************************************************// +#ifdef USE_MAG_APPROX +float FastMag(float r, float i) +{ + return FastSqrt(r*r + i*i); +} +#else +float FastMag(float r, float i) +{ + return (float)sqrtf(r*r + i*i); +} +#endif + +//***************************************************************************// +//* FastMag_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_MAG_APPROX +void FastMag_ARRAY(float * PtrInRe, + float * PtrInIm, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = PtrInRe[n]*PtrInRe[n] + PtrInIm[n]*PtrInIm[n]; + } + FastSqrt_ARRAY(PtrOut,PtrOut,Framesize); +} +#else +void FastMag_ARRAY(float * PtrInRe, + float * PtrInIm, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)sqrtf(PtrInRe[n]*PtrInRe[n] + PtrInIm[n]*PtrInIm[n]); + } +} +#endif + +//***************************************************************************// +//* FastPhase_INIT(...) *// +//***************************************************************************// +void FastPhase_INIT(FastPhase_Struct * pParams, + float * TempBuffer0, + float * TempBuffer1, + float * TempBuffer2) +{ + pParams->Quadrant = TempBuffer0; + pParams->Numerator = TempBuffer1; + pParams->Denominator = TempBuffer2; +} + +//***************************************************************************// +//* FastPhase(...) *// +//***************************************************************************// +#ifdef USE_PHASE_APPROX +float FastPhase(float r, float i) +{ + float temp, phase; + float Quadrant = 0, Numerator = 0, Denominator = 0; + + //Shift by 0 degrees + Quadrant = 0; + Numerator = i; + Denominator = r; + + if(i > r && i > -r){ + //Shift by 90 degrees + Quadrant = 1; + Numerator = -r; + Denominator = i; + } + if(r <= i && r <= -i){ + //Shift by 180 degrees + Quadrant = 2; + Numerator = i; + Denominator = r; + } + if(i <= r && i <= -r){ + //Shift by 270 degrees + Quadrant = 3; + Numerator = -r; + Denominator = i; + } + + if((float)fabs(Denominator) < (float)FLT_EPSILON) + Denominator = (float)FLT_EPSILON; + + Denominator = 1.0f / Denominator; + + Numerator *= Denominator; + + temp = Numerator * Numerator; + phase = 0.0208351f; + phase *= temp; + phase -= 0.085133f; + phase *= temp; + phase += 0.180141f; + phase *= temp; + phase -= 0.3302995f; + phase *= temp; + phase += 0.999866f; + phase *= Numerator; + + if(Quadrant == 1){ + phase += 0.5f * (float)PI; + } + if(Quadrant == 2){ + phase += (float)PI; + } + if(Quadrant == 3){ + phase -= 0.5f * (float)PI; + } + + if(phase > (float)PI) + phase -= 2.0f * (float)PI; + + return phase; +} +#else +float FastPhase(float r, float i) +{ + float phase, temp; + + //Take the reciprocal of the real part + temp = r; + if(temp <= (float)FLT_EPSILON && temp >= -(float)FLT_EPSILON) + temp = (float)FLT_EPSILON; + temp = 1.0f / temp; + + //Now take the arctan of the imag / real + temp = i * temp; + phase = (float)atanf(temp); //2 quadrant arctan + + //Make into a 4 quadrant arctan + if(r < 0 && i >= 0){ + phase += (float)PI; + } + if(r < 0 && i < 0){ + phase -= (float)PI; + } + + return phase; +} +#endif + +//***************************************************************************// +//* FastPhase_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_PHASE_APPROX +void FastPhase_ARRAY(float * PtrInRe, + float * PtrInIm, + float * PtrOutPhase, + unsigned int Framesize, + FastPhase_Struct * pParams) +{ + int n; + float temp; + + for(n = 0; n < (int)Framesize; n++){ + //Shift by 0 degrees + pParams->Quadrant[n] = 0; + pParams->Numerator[n] = PtrInIm[n]; + pParams->Denominator[n] = PtrInRe[n]; + + if(PtrInIm[n] > PtrInRe[n] && PtrInIm[n] > -PtrInRe[n]){ + //Shift by 90 degrees + pParams->Quadrant[n] = 1; + pParams->Numerator[n] = -PtrInRe[n]; + pParams->Denominator[n] = PtrInIm[n]; + } + if(PtrInRe[n] <= PtrInIm[n] && PtrInRe[n] <= -PtrInIm[n]){ + //Shift by 180 degrees + pParams->Quadrant[n] = 2; + pParams->Numerator[n] = PtrInIm[n]; + pParams->Denominator[n] = PtrInRe[n]; + } + if(PtrInIm[n] <= PtrInRe[n] && PtrInIm[n] <= -PtrInRe[n]){ + //Shift by 270 degrees + pParams->Quadrant[n] = 3; + pParams->Numerator[n] = -PtrInRe[n]; + pParams->Denominator[n] = PtrInIm[n]; + } + } + for(n = 0; n < (int)Framesize; n++){ + if((float)fabs(pParams->Denominator[n]) < (float)FLT_EPSILON) + pParams->Denominator[n] = (float)FLT_EPSILON; + } + +#ifdef PC_BUILD + for(n = 0; n < (int)Framesize; n++){ + pParams->Denominator[n] = 1.0f / pParams->Denominator[n]; + } +#endif +#ifdef DSP_BUILD + DSPF_sp_vecrecip(pParams->Denominator, pParams->Denominator, Framesize); +#endif + + for(n = 0; n < (int)Framesize; n++){ + pParams->Numerator[n] *= pParams->Denominator[n]; + } + + for(n = 0; n < (int)Framesize; n++){ + temp = pParams->Numerator[n] * pParams->Numerator[n]; + PtrOutPhase[n] = 0.0208351f; + PtrOutPhase[n] *= temp; + PtrOutPhase[n] -= 0.085133f; + PtrOutPhase[n] *= temp; + PtrOutPhase[n] += 0.180141f; + PtrOutPhase[n] *= temp; + PtrOutPhase[n] -= 0.3302995f; + PtrOutPhase[n] *= temp; + PtrOutPhase[n] += 0.999866f; + PtrOutPhase[n] *= pParams->Numerator[n]; + } + + for(n = 0; n < (int)Framesize; n++){ + if(pParams->Quadrant[n] == 1){ + PtrOutPhase[n] += 0.5f * (float)PI; + } + if(pParams->Quadrant[n] == 2){ + PtrOutPhase[n] += (float)PI; + } + if(pParams->Quadrant[n] == 3){ + PtrOutPhase[n] -= 0.5f * (float)PI; + } + } + for(n = 0; n < (int)Framesize; n++){ + if(PtrOutPhase[n] > (float)PI) + PtrOutPhase[n] -= 2.0f * (float)PI; + } +} +#else +void FastPhase_ARRAY(float * PtrInRe, + float * PtrInIm, + float * PtrOutPhase, + unsigned int Framesize, + FastPhase_Struct * pParams) +{ + int n; + float temp; + + //Do the hard part first: the PHASE + //Take the reciprocal of the real part + for(n = 0; n < (int)Framesize; n++){ + PtrOutPhase[n] = PtrInRe[n]; + if(PtrOutPhase[n] <= (float)FLT_EPSILON && PtrOutPhase[n] >= -(float)FLT_EPSILON){ + PtrOutPhase[n] = (float)FLT_EPSILON; + } + PtrOutPhase[n] = 1.0f / PtrOutPhase[n]; + } + //Now take the arctan of the imag / real + for(n = 0; n < (int)Framesize; n++){ + temp = PtrInIm[n] * PtrOutPhase[n]; + PtrOutPhase[n] = atanf(temp); //2 quadrant arctan + } + for(n = 0; n < (int)Framesize; n++){ + //Make into a 4 quadrant arctan + if(PtrInRe[n] < 0 && PtrInIm[n] >= 0){ + PtrOutPhase[n] += (float)PI; + } + if(PtrInRe[n] < 0 && PtrInIm[n] < 0){ + PtrOutPhase[n] -= (float)PI; + } + } +} +#endif + +//***************************************************************************// +//* FastSqrt(...) *// +//***************************************************************************// +#ifdef USE_SQRT_APPROX +float FastSqrt(float x) +{ + float xhalf = 0.5f*x; + int i = *(int*)&x; // get bits for floating value + i = 0x5f3759df - (i>>1); // gives initial guess y0 + x = *(float*)&i; // convert bits back to float + x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy + x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy + + x = 2.0f*xhalf*x; // convert InvSqrt into Sqrt + return x; +} +#else +float FastSqrt(float x) +{ + return (float)sqrtf(x); +} +#endif + +//***************************************************************************// +//* FastSqrt_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_SQRT_APPROX +void FastSqrt_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + float x, xhalf; + int n, i; + static float Temp[NEURAL_FRAMESIZE]; + + for(n = 0; n < (int)Framesize; n++){ + x = PtrIn[n]; + i = *(int*)&x; // get bits for floating value + i = 0x5f3759df - (i>>1); // gives initial guess y0 + Temp[n] = *(float*)&i; // convert bits back to float + } + for(n = 0; n < (int)Framesize; n++){ + x = Temp[n]; + xhalf = 0.5f*PtrIn[n]; + x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy + x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy + + x = 2.0f*xhalf*x; // convert InvSqrt into Sqrt + + PtrOut[n] = x; + } + + /* + for(n = 0; n < (int)Framesize; n++){ + x = LocalPtrIn[n]; + xhalf = 0.5f*x; + i = *(int*)&x; // get bits for floating value + i = 0x5f3759df - (i>>1); // gives initial guess y0 + x = *(float*)&i; // convert bits back to float + x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy + x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy + + x = 2.0f*xhalf*x; // convert InvSqrt into Sqrt + + LocalPtrOut[n] = x; + } + */ +} +#else +void FastSqrt_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)sqrtf(PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastLog2(...) *// +//***************************************************************************// +#ifdef USE_LOG_APPROX +float FastLog2(float x) +{ + ieee754_float32_t log2val, partial; + union { + ieee754_float32_t f; + int i; + } fi; + int mantisse; + + ieee754_float32_t * LocalLogTable = (ieee754_float32_t *)LogTable; + + if((1<>23) & 0xFF)-0x7f); + partial = (ieee754_float32_t)((mantisse & ((1<<(23-LOG2_SIZE_L2))-1))); + partial *= 0.00006103515625f; //1.0f/((1<<(23-LOG2_SIZE_L2))); + + + mantisse >>= (23-LOG2_SIZE_L2); + + // log2val += log_table[mantisse]; without interpolation the results are not good // + log2val += LocalLogTable[mantisse]*(1.0f-partial) + LocalLogTable[mantisse+1]*partial; + + return log2val; +} +#else +float FastLog2(float x) +{ + return 1.44269504088896f * logf(x); +} +#endif + +//***************************************************************************// +//* FastLog2_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_LOG_APPROX +void FastLog2_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + ieee754_float32_t log2val, partial; + union { + ieee754_float32_t f; + int i; + } fi; + int mantisse; + int n; + + ieee754_float32_t * LocalLogTable = (ieee754_float32_t *)LogTable; + + if((1<>23) & 0xFF)-0x7f); + partial = (ieee754_float32_t)((mantisse & ((1<<(23-LOG2_SIZE_L2))-1))); + partial *= 0.00006103515625f; //1.0f/((1<<(23-LOG2_SIZE_L2))); + + + mantisse >>= (23-LOG2_SIZE_L2); + + // log2val += log_table[mantisse]; without interpolation the results are not good // + log2val += LocalLogTable[mantisse]*(1.0f-partial) + LocalLogTable[mantisse+1]*partial; + + PtrOut[n] = log2val; + } +} +#else +void FastLog2_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + float norm = 1.44269504088896f; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = norm * logf(PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastLog10(...) *// +//***************************************************************************// +#ifdef USE_LOG_APPROX +float FastLog10(float x) +{ + float y = FastLog2(x); + y *= LOG2OVERLOG10; + return y; +} +#else +float FastLog10(float x) +{ + return (float)log10f(x); +} +#endif + +//***************************************************************************// +//* FastLog10_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_LOG_APPROX +void FastLog10_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + + FastLog2_ARRAY(PtrIn,PtrOut,Framesize); + + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] *= LOG2OVERLOG10; + } +} +#else +void FastLog10_ARRAY(float * restrict PtrIn, + float * restrict PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)log10f(PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastLogln(...) *// +//***************************************************************************// +#ifdef USE_LOG_APPROX +float FastLogln(float x) +{ + float y = FastLog2(x); + y *= LOG2; + return y; +} +#else +float FastLogln(float x) +{ + return (float)logf(x); +} +#endif + +//***************************************************************************// +//* FastLogln_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_LOG_APPROX +void FastLogln_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + + FastLog2_ARRAY(PtrIn,PtrOut,Framesize); + + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] *= LOG2; + } +} +#else +void FastLogln_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)logf(PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastPow(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +float FastPow(float x, float y) +{ + return FastPow2(y*FastLog2(x)); +} +#else +float FastPow(float x, float y) +{ + return (float)FMOD_POW(x,y); +} +#endif + +//***************************************************************************// +//* FastPow_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +void FastPow_ARRAY(float * PtrIn0, + float * PtrIn1, + float * PtrOut, + unsigned int Framesize) +{ + int n; + FastLog2_ARRAY(PtrIn0,PtrOut,Framesize); + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] *= PtrIn1[n]; + } + FastPow2_ARRAY(PtrOut,PtrOut,Framesize); + + //for(n = 0; n < (int)Framesize; n++){ + // PtrOut[n] = FastPow2(PtrIn1[n]*FastLog2(PtrIn0[n])); + //} +} +#else +void FastPow_ARRAY(float * PtrIn0, + float * PtrIn1, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)FMOD_POW(PtrIn0[n],PtrIn1[n]); + } +} +#endif + +//***************************************************************************// +//* FastPow2(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +float FastPow2(float i) +{ + float shift23=(1<<23); + float PowBodge=0.33971f; + float x; + float y=i-FMOD_FLOOR(i); + y=(y-y*y)*PowBodge; + + x=i+127-y; + x*= shift23; //pow(2,23); + *(int*)&x=(int)x; + return x; +} +#else +float FastPow2(float x) +{ + return (float)FMOD_POW(2.0f,x); +} +#endif + +//***************************************************************************// +//* FastPow2_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +void FastPow2_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + float shift23=(1<<23); + float i, PowBodge, x, y; + for(n = 0; n < (int)Framesize; n++){ + i = PtrIn[n]; + PowBodge=0.33971f; + x=(float)((i>0.0f)?(int)i:(int)(i-1.0f)); + y=i-x; //floorf(i); + y=(y-y*y)*PowBodge; + + PtrOut[n]=i+127-y; + } + for(n = 0; n < (int)Framesize; n++){ + x=PtrOut[n]; + x*=shift23; //pow(2,23); + *(int*)&x=(int)x; + PtrOut[n] = x; + } +} +#else +void FastPow2_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)FMOD_POW(2.0f,PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastPow10(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +float FastPow10(float x) +{ + return FastPow2(x * 3.32192809488736f); +} +#else +float FastPow10(float x) +{ + return (float)FMOD_POW(10.0f,x); +} +#endif + +//***************************************************************************// +//* FastPow10_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +void FastPow10_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = PtrIn[n] * 3.32192809488736f; + } + FastPow2_ARRAY(PtrOut,PtrOut,Framesize); +} +#else +void FastPow10_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)FMOD_POW(10.0f,PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastExp(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +float FastExp(float x) +{ + return FastPow2(x * 1.44269504064533f); +} +#else +float FastExp(float x) +{ + return (float)expf(x); +} +#endif + +//***************************************************************************// +//* FastExp_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_POW_APPROX +void FastExp_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = PtrIn[n] * 1.44269504064533f; + } + FastPow2_ARRAY(PtrOut,PtrOut,Framesize); +} +#else +void FastExp_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)expf(PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastSin(...) *// +//***************************************************************************// +#ifdef USE_SIN_APPROX +float FastSin(float x) +{ + float ASqr = x*x; + float Result = (float)-2.39e-08; + Result *= ASqr; + Result += (float)2.7526e-06; + Result *= ASqr; + Result -= (float)1.98409e-04; + Result *= ASqr; + Result += (float)8.3333315e-03; + Result *= ASqr; + Result -= (float)1.666666664e-01; + Result *= ASqr; + Result += (float)1.0; + Result *= x; + return Result; +} +#else +float FastSin(float x) +{ + return (float)sin(x); +} +#endif + +//***************************************************************************// +//* FastSin_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_SIN_APPROX +void FastSin_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + float ASqr, Result; + for(n = 0; n < (int)Framesize; n++){ + ASqr = PtrIn[n]*PtrIn[n]; + Result = (float)-2.39e-08; + Result *= ASqr; + Result += (float)2.7526e-06; + Result *= ASqr; + Result -= (float)1.98409e-04; + Result *= ASqr; + Result += (float)8.3333315e-03; + Result *= ASqr; + Result -= (float)1.666666664e-01; + Result *= ASqr; + Result += (float)1.0; + Result *= PtrIn[n]; + PtrOut[n] = Result; + } +} +#else +void FastSin_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)sin(PtrIn[n]); + } +} +#endif + +//***************************************************************************// +//* FastCos(...) *// +//***************************************************************************// +#ifdef USE_COS_APPROX +float FastCos(float x) +{ + float ASqr = x*x; + float Result = (float)-2.605e-07; + Result *= ASqr; + Result += (float)2.47609e-05; + Result *= ASqr; + Result -= (float)1.3888397e-03; + Result *= ASqr; + Result += (float)4.16666418e-02; + Result *= ASqr; + Result -= (float)4.999999963e-01; + Result *= ASqr; + Result += (float)1.0; + return Result; +} +#else +float FastCos(float x) +{ + return (float)cos(x); +} +#endif + +//***************************************************************************// +//* FastCos_ARRAY(...) *// +//***************************************************************************// +#ifdef USE_COS_APPROX +void FastCos_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + float ASqr, Result; + for(n = 0; n < (int)Framesize; n++){ + ASqr = PtrIn[n]*PtrIn[n]; + Result = (float)-2.605e-07; + Result *= ASqr; + Result += (float)2.47609e-05; + Result *= ASqr; + Result -= (float)1.3888397e-03; + Result *= ASqr; + Result += (float)4.16666418e-02; + Result *= ASqr; + Result -= (float)4.999999963e-01; + Result *= ASqr; + Result += (float)1.0; + PtrOut[n] = Result; + } +} +#else +void FastCos_ARRAY(float * PtrIn, + float * PtrOut, + unsigned int Framesize) +{ + int n; + for(n = 0; n < (int)Framesize; n++){ + PtrOut[n] = (float)cos(PtrIn[n]); + } +} +#endif diff --git a/lib/neural_thx/FreqDomain_PhaseShift.c b/lib/neural_thx/FreqDomain_PhaseShift.c new file mode 100755 index 0000000..9695091 --- /dev/null +++ b/lib/neural_thx/FreqDomain_PhaseShift.c @@ -0,0 +1,112 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : FreqDomain_PhaseShift.c *// +//* Description : Shift a frequency domain signal in phase *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include +#include "fmod_types.h" + +//***************************************************************************// +//* FreqDomain_PhaseShift_INIT(...) *// +//***************************************************************************// +//----- +//Phase shift by -90 degrees causes the output to lag the input by 90 degrees (similar to Hilbert Transform) +//Phase shift by +90 degrees causes the output to lead the input by 90 degrees +//----- +int FreqDomain_PhaseShift_INIT(float PhaseShift_Degrees, //Amount of phase shift (-90 degrees < x < 90 degrees) + unsigned int Framesize, + FreqDomain_PhaseShift_Struct * pPtr) +{ + float PhaseShift_Radians; + + pPtr->Prev_PhaseShift_Degrees = PhaseShift_Degrees; + if(PhaseShift_Degrees < -90.0f) PhaseShift_Degrees = -90.0f; + if(PhaseShift_Degrees > 90.0f) PhaseShift_Degrees = 90.0f; + PhaseShift_Radians = (float)PI * PhaseShift_Degrees / 180.0f; + pPtr->ShiftScalar_PositiveFreqs_Real = (float)FMOD_COS( PhaseShift_Radians ); + pPtr->ShiftScalar_PositiveFreqs_Imag = (float)FMOD_SIN( PhaseShift_Radians ); + + return NRLSUR_OK; +} + + +//***************************************************************************// +//* FreqDomain_PhaseShift(...) *// +//***************************************************************************// +int FreqDomain_PhaseShift(float * PtrInReal, + float * PtrInImag, + float * PtrOutReal, + float * PtrOutImag, + float PhaseShift_Degrees, //Amount of phase shift (-90 degrees < x < 90 degrees) + unsigned int Framesize, + unsigned int SampleRate, + FreqDomain_PhaseShift_Struct * pPtr) +{ + int n; + float PhaseShift_Radians, RealTemp, ImagTemp, RealShiftTemp, ImagShiftTemp; + const float *LowFreqSynthApFactors; + + if(Framesize < NUMLOWFREQBINS) return UNSUPPORTED_FRAMESIZE; + + //Use the tables appropriate with the samplerate + switch(SampleRate){ + case SAMPLERATE_32_0: + LowFreqSynthApFactors = LowFreqSynthApFactors_32k; + break; + case SAMPLERATE_44_1: + LowFreqSynthApFactors = LowFreqSynthApFactors_44k; + break; + case SAMPLERATE_48_0: + LowFreqSynthApFactors = LowFreqSynthApFactors_48k; + break; + default: + return UNSUPPORTED_PARAMETER; + } + + //If the amount of phase shift changes, re-calculate scaling constants + if(PhaseShift_Degrees != pPtr->Prev_PhaseShift_Degrees){ + pPtr->Prev_PhaseShift_Degrees = PhaseShift_Degrees; + if(PhaseShift_Degrees < -90.0f) PhaseShift_Degrees = -90.0f; + if(PhaseShift_Degrees > 90.0f) PhaseShift_Degrees = 90.0f; + PhaseShift_Radians = (float)PI * PhaseShift_Degrees / 180.0f; + pPtr->ShiftScalar_PositiveFreqs_Real = (float)FMOD_COS( PhaseShift_Radians ); + pPtr->ShiftScalar_PositiveFreqs_Imag = (float)FMOD_SIN( PhaseShift_Radians ); + } + + //Perform the phase shift which is a complex multiplication with pre-computed scalars + for(n = 0; n < NUMLOWFREQBINS; n++){ //Special handling of the low frequency bins since an ideal Hilbert transform is not possible + RealTemp = PtrInReal[n]; + ImagTemp = PtrInImag[n]; + RealShiftTemp = 1.0f * (1.0f - LowFreqSynthApFactors[n]) + pPtr->ShiftScalar_PositiveFreqs_Real * LowFreqSynthApFactors[n]; + ImagShiftTemp = 0.0f * (1.0f - LowFreqSynthApFactors[n]) + pPtr->ShiftScalar_PositiveFreqs_Imag * LowFreqSynthApFactors[n]; + PtrOutReal[n] = RealTemp * RealShiftTemp - ImagTemp * ImagShiftTemp; + PtrOutImag[n] = RealTemp * ImagShiftTemp + ImagTemp * RealShiftTemp; + } + for(n = NUMLOWFREQBINS; n < (int)Framesize; n++){ + RealTemp = PtrInReal[n]; + ImagTemp = PtrInImag[n]; + PtrOutReal[n] = RealTemp * pPtr->ShiftScalar_PositiveFreqs_Real - ImagTemp * pPtr->ShiftScalar_PositiveFreqs_Imag; + PtrOutImag[n] = RealTemp * pPtr->ShiftScalar_PositiveFreqs_Imag + ImagTemp * pPtr->ShiftScalar_PositiveFreqs_Real; + } + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/LR4_LP.c b/lib/neural_thx/LR4_LP.c new file mode 100755 index 0000000..debb6cc --- /dev/null +++ b/lib/neural_thx/LR4_LP.c @@ -0,0 +1,102 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : LR4_LP.c *// +//* Description : Linkwitz-Riley 4th order Low-Pass filter *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2005 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include "fmod_types.h" + +//***************************************************************************// +//* LR4_LP_INIT(...) *// +//***************************************************************************// +void LR4_LP_INIT(LR4_LP_Struct * pPtr) +{ + pPtr->PrevCutOff = -1.0f; + pPtr->PrevStage00 = 0.0f; + pPtr->PrevStage01 = 0.0f; + pPtr->PrevStage10 = 0.0f; + pPtr->PrevStage11 = 0.0f; + pPtr->b0 = 0.0f; + pPtr->b1 = 0.0f; + pPtr->b2 = 0.0f; + pPtr->a0 = 0.0f; + pPtr->a1 = 0.0f; + pPtr->PrevSampleRate = -1.0f; +} + + +//***************************************************************************// +//* LR4_LP(...) *// +//***************************************************************************// +void LR4_LP(float * PtrIn, + float * PtrOut, + float Cutoff, //in Hertz + unsigned int Framesize, + unsigned int SampleRate, + LR4_LP_Struct * pPtr) +{ + int n; + float midStage = 0.0f; // Local buffer + + //----- Note that this only works for fourth order LR filters -----// + + //----- Calculate the filter coefficients -----// + if( Cutoff != pPtr->PrevCutOff || (float)SampleRate != pPtr->PrevSampleRate ){ + //----- Pre-warp the digital cutoff frequency to an analog equivalent -----// + float omegac = (float)2.0 * FMOD_TAN( (float)PI*(Cutoff) / (float)SampleRate ); + float norm = (float)4.0 + omegac*(float)TWOPOWTHREEHALVES + omegac*omegac; + norm = (float)1.0 / norm; + pPtr->b0 = (omegac*omegac) * norm; + pPtr->b1 = (float)2.0*pPtr->b0; + pPtr->b2 = pPtr->b0; + pPtr->a0 = ((float)2.0*omegac*omegac - (float)8.0) * norm; + pPtr->a1 = ((float)4.0 - omegac*(float)TWOPOWTHREEHALVES + omegac*omegac) * norm; + pPtr->PrevCutOff = Cutoff; + pPtr->PrevSampleRate = (float)SampleRate; + } + + //------------------------------------------------------------------// + // Perform the filtering through a cascade of second order stages // + //------------------------------------------------------------------// + // Stage 1 + for(n = 0; n < (int)Framesize; n++){ + midStage = PtrIn[n] + - pPtr->PrevStage00 * pPtr->a0 + - pPtr->PrevStage01 * pPtr->a1; + PtrOut[n] = midStage * pPtr->b0 + + pPtr->PrevStage00 * pPtr->b1 + + pPtr->PrevStage01 * pPtr->b2; + pPtr->PrevStage01 = pPtr->PrevStage00; + pPtr->PrevStage00 = midStage; + } + + // Stage 2 + for(n = 0; n < (int)Framesize; n++){ + midStage = PtrOut[n] + - pPtr->PrevStage10 * pPtr->a0 + - pPtr->PrevStage11 * pPtr->a1; + PtrOut[n] = midStage * pPtr->b0 + + pPtr->PrevStage10 * pPtr->b1 + + pPtr->PrevStage11 * pPtr->b2; + pPtr->PrevStage11 = pPtr->PrevStage10; + pPtr->PrevStage10 = midStage; + } +} diff --git a/lib/neural_thx/Limiter.c b/lib/neural_thx/Limiter.c new file mode 100755 index 0000000..87f8438 --- /dev/null +++ b/lib/neural_thx/Limiter.c @@ -0,0 +1,110 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : Limiter.c *// +//* Description : Limiter *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include +#include +#include "fmod_types.h" + +//***************************************************************************// +//* Limiter_INIT(...) *// +//***************************************************************************// +int Limiter_INIT(float * TempBuffer, + Limiter_Struct * pPtr) +{ + pPtr->TempBuffer = TempBuffer; + PeakConverter_INIT(0.0f,0.0f,0.0f,0.0f,0,&pPtr->EnvSmoother); + return NRLSUR_OK; +} + +//***************************************************************************// +//* Compressor(...) *// +//***************************************************************************// +int Limiter(float * AudioIn, + float * AudioOut, + float MaxAudioValue, + float Knee, //in dB referenced to zero dB + float Ceiling, //in dB referenced to zero dB + float RiseTime, //in msec + float FallTime, //in msec + unsigned int Framesize, + unsigned int SampleRate, + Limiter_Struct * pPtr) +{ + int n; + float fTemp; + + if(Knee > Ceiling) Knee = Ceiling; + + CopyArray(AudioIn,pPtr->TempBuffer,Framesize); + + //First normalize audio + if(MaxAudioValue < (float)FLT_EPSILON) + MaxAudioValue = (float)FLT_EPSILON; + fTemp = 1.0f / MaxAudioValue; + ScaleArray(pPtr->TempBuffer,fTemp,Framesize); + + //First convert the audio to the dB domain + AbsValue(pPtr->TempBuffer,Framesize); + MinClip(pPtr->TempBuffer,(float)FLT_EPSILON,Framesize); + MaxClip(pPtr->TempBuffer,1.0f,Framesize); + FastLog10_ARRAY(pPtr->TempBuffer,pPtr->TempBuffer,Framesize); + ScaleArray(pPtr->TempBuffer,20.0f,Framesize); + + //Smooth the envelope + PeakConverter(pPtr->TempBuffer, + pPtr->TempBuffer, + RiseTime, //in msec + FallTime, //in msec + -60.0f, + 0.0f, + Framesize, + SampleRate, + &pPtr->EnvSmoother); + + //Apply the knee and ceiling parameters + for(n = 0; n < (int)Framesize; n++){ + fTemp = pPtr->TempBuffer[n]; + pPtr->TempBuffer[n] = 0.0f; + if(fTemp > Knee){ + pPtr->TempBuffer[n] = 0.5f * (Knee - fTemp); + fTemp = fTemp + pPtr->TempBuffer[n]; + } + if(fTemp > Ceiling){ + pPtr->TempBuffer[n] += Ceiling - fTemp; + fTemp = fTemp + pPtr->TempBuffer[n]; + } + } + + //Convert back to linear domain + MaxClip(pPtr->TempBuffer,0.0f,Framesize); + ScaleArray(pPtr->TempBuffer,0.05f,Framesize); + FastPow10_ARRAY(pPtr->TempBuffer,pPtr->TempBuffer,Framesize); + + //Limit the audio signal + Multiply2(AudioIn,pPtr->TempBuffer,AudioOut,Framesize); + Ceiling = FMOD_POW(10.0f,0.05f*Ceiling) * MaxAudioValue; + SaturateArray(AudioOut,Ceiling,Framesize); + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/Neural_THX_522_Encode.c b/lib/neural_thx/Neural_THX_522_Encode.c new file mode 100755 index 0000000..8147483 --- /dev/null +++ b/lib/neural_thx/Neural_THX_522_Encode.c @@ -0,0 +1,189 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio - THX *// +//* File : Neural_THX_522_Encode.c *// +//* Description : Neural Surround Downmix implementation *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include + +//***************************************************************************// +//* Neural_THX_522_Encode_INIT(...) *// +//***************************************************************************// +int Neural_THX_522_Encode_INIT(unsigned int Framesize, + unsigned int ChanConfig, + unsigned int SampleRate, + Neural_THX_522_Encode_Struct * pParams) +{ + //Forward and inverse overlapped FFT's + FFT_Overlapped_Stereo_INIT(&pParams->FFTSourceLR,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + FFT_Overlapped_INIT(&pParams->FFTSourceC,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + FFT_Overlapped_Stereo_INIT(&pParams->FFTSourceLsRs,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + IFFT_Overlapped_Stereo_INIT(&pParams->InvFFTDownmixLR,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + + //Initialize phase shifts + FreqDomain_PhaseShift_INIT(-22.5f,NEURAL_FRAMESIZE,&pParams->PhaseShift_L); + FreqDomain_PhaseShift_INIT(+22.5f,NEURAL_FRAMESIZE,&pParams->PhaseShift_R); + FreqDomain_PhaseShift_INIT(-90.0f,NEURAL_FRAMESIZE,&pParams->PhaseShift_Ls); + FreqDomain_PhaseShift_INIT(+90.0f,NEURAL_FRAMESIZE,&pParams->PhaseShift_Rs); + + LR4_LP_INIT(&pParams->LFE_LP); + + //Init final limiters + Limiter_INIT(pParams->TempBuffer0,&pParams->FinalLimiterL); + Limiter_INIT(pParams->TempBuffer0,&pParams->FinalLimiterR); + + return NRLSUR_OK; +} + + +//***************************************************************************// +//* Neural_THX_522_Encode(...) *// +//***************************************************************************// +int Neural_THX_522_Encode(float * PtrInL, + float * PtrInR, + float * PtrInC, + float * PtrInLFE, + float * PtrInLs, + float * PtrInRs, + float * PtrOutL, + float * PtrOutR, + THX_bool UseFinalLimiting, + float LFE_Cutoff, + unsigned int Framesize, + unsigned int ChanConfig, + unsigned int SampleRate, + Neural_THX_522_Encode_Struct * pParams) +{ + //Neural Surround Downmix + + //*********************************************************** + //***** Low-pass filter the LFE channel (if selected) ***** + //*********************************************************** + if(LFE_Cutoff > 40.0f && LFE_Cutoff < 200.0f){ + LR4_LP(PtrInLFE,PtrInLFE,LFE_Cutoff,NEURAL_FRAMESIZE,SampleRate,&pParams->LFE_LP); + } + + //First sum LFE channel into Center channel + Add2(PtrInC,PtrInLFE,PtrInC,NEURAL_FRAMESIZE); + + //**************************************************************************************************** + //***** Begin downmix by transforming all signals to frequency domain and performing phase shift ***** + //**************************************************************************************************** + //Convert all input channels to freq domain + //L-R channels + FFT_Overlapped_Stereo(PtrInL,pParams->Input_RealL,pParams->Input_ImagL, + PtrInR,pParams->Input_RealR,pParams->Input_ImagR, + NEURAL_FRAMESIZE,&pParams->FFTSourceLR); + //C channel + FFT_Overlapped(PtrInC,pParams->Input_RealC,pParams->Input_ImagC, + NEURAL_FRAMESIZE,&pParams->FFTSourceC); + //Ls-Rs channels + FFT_Overlapped_Stereo(PtrInLs,pParams->Input_RealLs,pParams->Input_ImagLs, + PtrInRs,pParams->Input_RealRs,pParams->Input_ImagRs, + NEURAL_FRAMESIZE,&pParams->FFTSourceLsRs); + + //Phase shift L channel + FreqDomain_PhaseShift(pParams->Input_RealL,pParams->Input_ImagL, + pParams->Input_RealL,pParams->Input_ImagL, + -22.5f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_L); + //Phase shift R channel + FreqDomain_PhaseShift(pParams->Input_RealR,pParams->Input_ImagR, + pParams->Input_RealR,pParams->Input_ImagR, + +22.5f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_R); + //Phase shift Ls channel + FreqDomain_PhaseShift(pParams->Input_RealLs,pParams->Input_ImagLs, + pParams->Input_RealLs,pParams->Input_ImagLs, + -90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Ls); + //Phase shift Rs channel + FreqDomain_PhaseShift(pParams->Input_RealRs,pParams->Input_ImagRs, + pParams->Input_RealRs,pParams->Input_ImagRs, + +90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Rs); + + + //Scale center and lfe channels by -3dB + ScaleArray(pParams->Input_RealC,(float)MINUS_3DB,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagC,(float)MINUS_3DB,NEURAL_FRAMESIZE); + + //Add center and lfe channels to left/right phase shifted channels + //L channel + Add2(pParams->Input_RealL,pParams->Input_RealC,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + Add2(pParams->Input_ImagL,pParams->Input_ImagC,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + //R channel + Add2(pParams->Input_RealR,pParams->Input_RealC,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + Add2(pParams->Input_ImagR,pParams->Input_ImagC,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + + //*********************************************************** + //***** Combine the front channels with the surrounds ***** + //*********************************************************** + ScaleArray(pParams->Input_RealLs,(float)CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagLs,(float)CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_RealRs,(float)CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagRs,(float)CROSSSHARENORM,NEURAL_FRAMESIZE); + + Add2(pParams->Downmix_RealL,pParams->Input_RealLs,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + Add2(pParams->Downmix_ImagL,pParams->Input_ImagLs,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + + Add2(pParams->Downmix_RealR,pParams->Input_RealRs,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + Add2(pParams->Downmix_ImagR,pParams->Input_ImagRs,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + CopyArray(pParams->Input_RealLs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_RealR,pParams->TempBuffer0,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + CopyArray(pParams->Input_ImagLs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_ImagR,pParams->TempBuffer0,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + CopyArray(pParams->Input_RealRs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_RealL,pParams->TempBuffer0,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + CopyArray(pParams->Input_ImagRs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_ImagL,pParams->TempBuffer0,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + + + //*********************************************************** + //***** Inverse filterbank back to time signals ***** + //*********************************************************** + IFFT_Overlapped_Stereo(pParams->Downmix_RealL,pParams->Downmix_ImagL,PtrOutL, + pParams->Downmix_RealR,pParams->Downmix_ImagR,PtrOutR, + NEURAL_FRAMESIZE,&pParams->InvFFTDownmixLR); + + if(UseFinalLimiting){ + //Perform final limiting on the output signals + Limiter(PtrOutL,PtrOutL, + (float)AUDIO_VAL_MAX, + -6.0f,-0.1f, + 0.0f,500.0f, + NEURAL_FRAMESIZE,SampleRate,&pParams->FinalLimiterL); + Limiter(PtrOutR,PtrOutR, + (float)AUDIO_VAL_MAX, + -6.0f,-0.1f, + 0.0f,500.0f, + NEURAL_FRAMESIZE,SampleRate,&pParams->FinalLimiterR); + } + + + //That's all folks + SaturateArray(PtrOutL,(float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutR,(float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/Neural_THX_722_Encode.c b/lib/neural_thx/Neural_THX_722_Encode.c new file mode 100755 index 0000000..4f81134 --- /dev/null +++ b/lib/neural_thx/Neural_THX_722_Encode.c @@ -0,0 +1,233 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio - THX *// +//* File : Neural_THX_722_Encode.c *// +//* Description : Neural Surround Downmix implementation *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include + +//***************************************************************************// +//* Neural_THX_722_Encode_INIT(...) *// +//***************************************************************************// +int Neural_THX_722_Encode_INIT(unsigned int Framesize, + unsigned int ChanConfig, + unsigned int SampleRate, + Neural_THX_722_Encode_Struct * pParams) +{ + //Forward and inverse overlapped FFT's + FFT_Overlapped_Stereo_INIT(&pParams->FFTSourceLR,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + FFT_Overlapped_INIT(&pParams->FFTSourceC,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + FFT_Overlapped_Stereo_INIT(&pParams->FFTSourceLsRs,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + FFT_Overlapped_Stereo_INIT(&pParams->FFTSourceLbRb,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + IFFT_Overlapped_Stereo_INIT(&pParams->InvFFTDownmixLR,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + + //Initialize phase shifts + FreqDomain_PhaseShift_INIT(-22.5f,NEURAL_FRAMESIZE,&pParams->PhaseShift_L); + FreqDomain_PhaseShift_INIT(+22.5f,NEURAL_FRAMESIZE,&pParams->PhaseShift_R); + FreqDomain_PhaseShift_INIT(-90.0f,NEURAL_FRAMESIZE,&pParams->PhaseShift_Ls); + FreqDomain_PhaseShift_INIT(+90.0f,NEURAL_FRAMESIZE,&pParams->PhaseShift_Rs); + + LR4_LP_INIT(&pParams->LFE_LP); + + //Init final limiters + Limiter_INIT(pParams->TempBuffer0,&pParams->FinalLimiterL); + Limiter_INIT(pParams->TempBuffer0,&pParams->FinalLimiterR); + + return NRLSUR_OK; +} + + +//***************************************************************************// +//* Neural_THX_722_Encode(...) *// +//***************************************************************************// +int Neural_THX_722_Encode(float * PtrInL, + float * PtrInR, + float * PtrInC, + float * PtrInLFE, + float * PtrInLs, + float * PtrInRs, + float * PtrInLb, + float * PtrInRb, + float * PtrOutL, + float * PtrOutR, + THX_bool UseFinalLimiting, + float LFE_Cutoff, + unsigned int Framesize, + unsigned int ChanConfig, + unsigned int SampleRate, + Neural_THX_722_Encode_Struct * pParams) +{ + //Neural Surround Downmix + + //*********************************************************** + //***** Low-pass filter the LFE channel (if selected) ***** + //*********************************************************** + if(LFE_Cutoff > 40.0f && LFE_Cutoff < 200.0f){ + LR4_LP(PtrInLFE,PtrInLFE,LFE_Cutoff,NEURAL_FRAMESIZE,SampleRate,&pParams->LFE_LP); + } + + //First sum LFE channel into Center channel + Add2(PtrInC,PtrInLFE,PtrInC,NEURAL_FRAMESIZE); + + //**************************************************************************************************** + //***** Begin downmix by transforming all signals to frequency domain and performing phase shift ***** + //**************************************************************************************************** + //Convert all input channels to freq domain + //L-R channels + FFT_Overlapped_Stereo(PtrInL,pParams->Input_RealL,pParams->Input_ImagL, + PtrInR,pParams->Input_RealR,pParams->Input_ImagR, + NEURAL_FRAMESIZE,&pParams->FFTSourceLR); + //C channel + FFT_Overlapped(PtrInC,pParams->Input_RealC,pParams->Input_ImagC, + NEURAL_FRAMESIZE,&pParams->FFTSourceC); + //Ls-Rs channels + FFT_Overlapped_Stereo(PtrInLs,pParams->Input_RealLs,pParams->Input_ImagLs, + PtrInRs,pParams->Input_RealRs,pParams->Input_ImagRs, + NEURAL_FRAMESIZE,&pParams->FFTSourceLsRs); + //Lb-Rb channels + FFT_Overlapped_Stereo(PtrInLb,pParams->Input_RealLb,pParams->Input_ImagLb, + PtrInRb,pParams->Input_RealRb,pParams->Input_ImagRb, + NEURAL_FRAMESIZE,&pParams->FFTSourceLbRb); + + //Phase shift L channel + FreqDomain_PhaseShift(pParams->Input_RealL,pParams->Input_ImagL, + pParams->Input_RealL,pParams->Input_ImagL, + -22.5f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_L); + //Phase shift R channel + FreqDomain_PhaseShift(pParams->Input_RealR,pParams->Input_ImagR, + pParams->Input_RealR,pParams->Input_ImagR, + +22.5f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_R); + //Phase shift Ls channel + FreqDomain_PhaseShift(pParams->Input_RealLs,pParams->Input_ImagLs, + pParams->Input_RealLs,pParams->Input_ImagLs, + -90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Ls); + //Phase shift Rs channel + FreqDomain_PhaseShift(pParams->Input_RealRs,pParams->Input_ImagRs, + pParams->Input_RealRs,pParams->Input_ImagRs, + +90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Rs); + //Phase shift Lb channel + FreqDomain_PhaseShift(pParams->Input_RealLb,pParams->Input_ImagLb, + pParams->Input_RealLb,pParams->Input_ImagLb, + -90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Ls); + //Phase shift Rb channel + FreqDomain_PhaseShift(pParams->Input_RealRb,pParams->Input_ImagRb, + pParams->Input_RealRb,pParams->Input_ImagRb, + +90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Rs); + + + + //Scale center and lfe channels by -3dB + ScaleArray(pParams->Input_RealC,(float)MINUS_3DB,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagC,(float)MINUS_3DB,NEURAL_FRAMESIZE); + + //Add center and lfe channels to left/right phase shifted channels + //L channel + Add2(pParams->Input_RealL,pParams->Input_RealC,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + Add2(pParams->Input_ImagL,pParams->Input_ImagC,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + //R channel + Add2(pParams->Input_RealR,pParams->Input_RealC,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + Add2(pParams->Input_ImagR,pParams->Input_ImagC,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + + //*********************************************************** + //***** Combine the front channels with the surrounds ***** + //*********************************************************** + ScaleArray(pParams->Input_RealLs,(float)SUR_CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagLs,(float)SUR_CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_RealRs,(float)SUR_CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagRs,(float)SUR_CROSSSHARENORM,NEURAL_FRAMESIZE); + //----- Back channels ----- + ScaleArray(pParams->Input_RealLb,(float)BACK_CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagLb,(float)BACK_CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_RealRb,(float)BACK_CROSSSHARENORM,NEURAL_FRAMESIZE); + ScaleArray(pParams->Input_ImagRb,(float)BACK_CROSSSHARENORM,NEURAL_FRAMESIZE); + + + //----- Surround channels ----- + Add2(pParams->Downmix_RealL,pParams->Input_RealLs,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + Add2(pParams->Downmix_ImagL,pParams->Input_ImagLs,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + + Add2(pParams->Downmix_RealR,pParams->Input_RealRs,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + Add2(pParams->Downmix_ImagR,pParams->Input_ImagRs,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + CopyArray(pParams->Input_RealLs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-SUR_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_RealR,pParams->TempBuffer0,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + CopyArray(pParams->Input_ImagLs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-SUR_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_ImagR,pParams->TempBuffer0,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + CopyArray(pParams->Input_RealRs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-SUR_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_RealL,pParams->TempBuffer0,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + CopyArray(pParams->Input_ImagRs,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-SUR_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_ImagL,pParams->TempBuffer0,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + + //----- Back channels ----- + Add2(pParams->Downmix_RealL,pParams->Input_RealLb,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + Add2(pParams->Downmix_ImagL,pParams->Input_ImagLb,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + + Add2(pParams->Downmix_RealR,pParams->Input_RealRb,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + Add2(pParams->Downmix_ImagR,pParams->Input_ImagRb,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + CopyArray(pParams->Input_RealLb,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-BACK_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_RealR,pParams->TempBuffer0,pParams->Downmix_RealR,NEURAL_FRAMESIZE); + CopyArray(pParams->Input_ImagLb,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-BACK_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_ImagR,pParams->TempBuffer0,pParams->Downmix_ImagR,NEURAL_FRAMESIZE); + + CopyArray(pParams->Input_RealRb,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-BACK_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_RealL,pParams->TempBuffer0,pParams->Downmix_RealL,NEURAL_FRAMESIZE); + CopyArray(pParams->Input_ImagRb,pParams->TempBuffer0,NEURAL_FRAMESIZE); + ScaleArray(pParams->TempBuffer0,-BACK_CROSSSHARECONSTANT,Framesize); + Add2(pParams->Downmix_ImagL,pParams->TempBuffer0,pParams->Downmix_ImagL,NEURAL_FRAMESIZE); + + + //*********************************************************** + //***** Inverse filterbank back to time signals ***** + //*********************************************************** + IFFT_Overlapped_Stereo(pParams->Downmix_RealL,pParams->Downmix_ImagL,PtrOutL, + pParams->Downmix_RealR,pParams->Downmix_ImagR,PtrOutR, + NEURAL_FRAMESIZE,&pParams->InvFFTDownmixLR); + + if(UseFinalLimiting){ + //Perform final limiting on the output signals + Limiter(PtrOutL,PtrOutL, + (float)AUDIO_VAL_MAX, + -6.0f,-0.1f, + 0.0f,500.0f, + NEURAL_FRAMESIZE,SampleRate,&pParams->FinalLimiterL); + Limiter(PtrOutR,PtrOutR, + (float)AUDIO_VAL_MAX, + -6.0f,-0.1f, + 0.0f,500.0f, + NEURAL_FRAMESIZE,SampleRate,&pParams->FinalLimiterR); + } + + + //That's all folks + SaturateArray(PtrOutL,(float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutR,(float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/Neural_THX_725_Encode.c b/lib/neural_thx/Neural_THX_725_Encode.c new file mode 100755 index 0000000..0221564 --- /dev/null +++ b/lib/neural_thx/Neural_THX_725_Encode.c @@ -0,0 +1,207 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio - THX *// +//* File : Neural_THX_725_Encode.c *// +//* Description : Downmixer for Neural - THX gaming mode from 7.1 to 5.1 *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include + +//***************************************************************************// +//* Neural_THX_725_Encode_INIT(...) *// +//***************************************************************************// +int Neural_THX_725_Encode_INIT(unsigned int Framesize, + unsigned int ChanConfig, + unsigned int SampleRate, + Neural_THX_725_Encode_Struct * pParams) +{ + //Initialize the FFT transforms + FFT_Overlapped_Stereo_INIT(&pParams->Transform_LsRs,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + FFT_Overlapped_Stereo_INIT(&pParams->Transform_LbRb,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + IFFT_Overlapped_Stereo_INIT(&pParams->InvTransform_LsRs,pParams->TempBuffer0,pParams->TempBuffer1,NEURAL_FRAMESIZE); + + //Initialize phase shifts + FreqDomain_PhaseShift_INIT(+90.0f,NEURAL_FRAMESIZE,&pParams->PhaseShift_Pos); + FreqDomain_PhaseShift_INIT(-90.0f,NEURAL_FRAMESIZE,&pParams->PhaseShift_Neg); + +#ifdef COMPENSATION_FRONT_DELAY + //Initialize delays + if(Delay_INIT(NEURAL_FRAMESIZE,&pParams->DelayChanL) < 0) return INIT_ERROR; + if(Delay_INIT(NEURAL_FRAMESIZE,&pParams->DelayChanR) < 0) return INIT_ERROR; + if(Delay_INIT(NEURAL_FRAMESIZE,&pParams->DelayChanC) < 0) return INIT_ERROR; + if(Delay_INIT(NEURAL_FRAMESIZE,&pParams->DelayChanLFE) < 0) return INIT_ERROR; +#endif + + //Init final limiters + Limiter_INIT(pParams->TempBuffer0,&pParams->FinalLimiterLs); + Limiter_INIT(pParams->TempBuffer0,&pParams->FinalLimiterRs); + + return NRLSUR_OK; +} + + +//***************************************************************************// +//* Neural_THX_725_Encode(...) *// +//***************************************************************************// +int Neural_THX_725_Encode(float * PtrInL, //7.1 In + float * PtrInR, + float * PtrInC, + float * PtrInLFE, + float * PtrInLs, + float * PtrInRs, + float * PtrInLb, + float * PtrInRb, + float * PtrOutL, //5.1 Out + float * PtrOutR, + float * PtrOutC, + float * PtrOutLFE, + float * PtrOutLs, + float * PtrOutRs, + THX_bool UseFinalLimiting, + unsigned int Framesize, + unsigned int ChanConfig, + unsigned int SampleRate, + Neural_THX_725_Encode_Struct * pParams) +{ + //Neural Audio - THX Gaming Downmix + //Downmix 7.1 format to 5.1 format + float * PhaseShift_RealLb_Pos = &pParams->TempBuffer0[0*NEURAL_FRAMESIZE]; + float * PhaseShift_ImagLb_Pos = &pParams->TempBuffer0[1*NEURAL_FRAMESIZE]; + float * PhaseShift_RealRb_Pos = &pParams->TempBuffer0[2*NEURAL_FRAMESIZE]; + float * PhaseShift_ImagRb_Pos = &pParams->TempBuffer0[3*NEURAL_FRAMESIZE]; + float * PhaseShift_RealLb_Neg = &pParams->TempBuffer1[0*NEURAL_FRAMESIZE]; + float * PhaseShift_ImagLb_Neg = &pParams->TempBuffer1[1*NEURAL_FRAMESIZE]; + float * PhaseShift_RealRb_Neg = &pParams->TempBuffer1[2*NEURAL_FRAMESIZE]; + float * PhaseShift_ImagRb_Neg = &pParams->TempBuffer1[3*NEURAL_FRAMESIZE]; + + //*********************************************************** + //***** Surround channel downmixing ***** + //*********************************************************** + //Convert all input channels to freq domain + FFT_Overlapped_Stereo(PtrInLs,pParams->Input_RealLs,pParams->Input_ImagLs, + PtrInRs,pParams->Input_RealRs,pParams->Input_ImagRs, + NEURAL_FRAMESIZE,&pParams->Transform_LsRs); + FFT_Overlapped_Stereo(PtrInLb,pParams->Input_RealLb,pParams->Input_ImagLb, + PtrInRb,pParams->Input_RealRb,pParams->Input_ImagRb, + NEURAL_FRAMESIZE,&pParams->Transform_LbRb); + + //Phase shift Lb channel + FreqDomain_PhaseShift(pParams->Input_RealLb,pParams->Input_ImagLb, + PhaseShift_RealLb_Pos,PhaseShift_ImagLb_Pos, + +90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Pos); + FreqDomain_PhaseShift(pParams->Input_RealLb,pParams->Input_ImagLb, + PhaseShift_RealLb_Neg,PhaseShift_ImagLb_Neg, + -90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Neg); + //Phase shift Rb channel + FreqDomain_PhaseShift(pParams->Input_RealRb,pParams->Input_ImagRb, + PhaseShift_RealRb_Pos,PhaseShift_ImagRb_Pos, + +90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Pos); + FreqDomain_PhaseShift(pParams->Input_RealRb,pParams->Input_ImagRb, + PhaseShift_RealRb_Neg,PhaseShift_ImagRb_Neg, + -90.0f,NEURAL_FRAMESIZE,SampleRate,&pParams->PhaseShift_Neg); + + //----- + //Generate Left Surround + //----- + //Real + Add2(pParams->Input_RealLs,PhaseShift_RealLb_Pos,pParams->Dmix_RealLs,NEURAL_FRAMESIZE); + ScaleArray(PhaseShift_RealRb_Pos,BACK_CROSS_SHARE,NEURAL_FRAMESIZE); + Add2(pParams->Dmix_RealLs,PhaseShift_RealRb_Pos,pParams->Dmix_RealLs,NEURAL_FRAMESIZE); + //Imag + Add2(pParams->Input_ImagLs,PhaseShift_ImagLb_Pos,pParams->Dmix_ImagLs,NEURAL_FRAMESIZE); + ScaleArray(PhaseShift_ImagRb_Pos,BACK_CROSS_SHARE,NEURAL_FRAMESIZE); + Add2(pParams->Dmix_ImagLs,PhaseShift_ImagRb_Pos,pParams->Dmix_ImagLs,NEURAL_FRAMESIZE); + + //----- + //Generate Right Surround + //----- + //Real + Add2(pParams->Input_RealRs,PhaseShift_RealRb_Neg,pParams->Dmix_RealRs,NEURAL_FRAMESIZE); + ScaleArray(PhaseShift_RealLb_Neg,BACK_CROSS_SHARE,NEURAL_FRAMESIZE); + Add2(pParams->Dmix_RealRs,PhaseShift_RealLb_Neg,pParams->Dmix_RealRs,NEURAL_FRAMESIZE); + //Imag + Add2(pParams->Input_ImagRs,PhaseShift_ImagRb_Neg,pParams->Dmix_ImagRs,NEURAL_FRAMESIZE); + ScaleArray(PhaseShift_ImagLb_Neg,BACK_CROSS_SHARE,NEURAL_FRAMESIZE); + Add2(pParams->Dmix_ImagRs,PhaseShift_ImagLb_Neg,pParams->Dmix_ImagRs,NEURAL_FRAMESIZE); + + + //*********************************************************** + //***** Convert downmixed channels back to time domain ***** + //*********************************************************** + IFFT_Overlapped_Stereo(pParams->Dmix_RealLs,pParams->Dmix_ImagLs,PtrOutLs, + pParams->Dmix_RealRs,pParams->Dmix_ImagRs,PtrOutRs, + NEURAL_FRAMESIZE,&pParams->InvTransform_LsRs); + + if(UseFinalLimiting){ + //Perform final limiting on the output signals + Limiter(PtrOutLs,PtrOutLs, + (float)AUDIO_VAL_MAX, + -6.0f,-0.1f, + 0.0f,500.0f, + NEURAL_FRAMESIZE,SampleRate,&pParams->FinalLimiterLs); + Limiter(PtrOutRs,PtrOutRs, + (float)AUDIO_VAL_MAX, + -6.0f,-0.1f, + 0.0f,500.0f, + NEURAL_FRAMESIZE,SampleRate,&pParams->FinalLimiterRs); + } + +#ifdef COMPENSATION_FRONT_DELAY + //*********************************************************** + //***** Front channel handling ***** + //*********************************************************** + //Delay Front channels to compensate for Surround downmixing + + //----- + //Left Front + //----- + Delay(PtrInL, PtrOutL,NEURAL_FRAMESIZE,&pParams->DelayChanL); + //----- + //Right Front + //----- + Delay(PtrInR, PtrOutR,NEURAL_FRAMESIZE,&pParams->DelayChanR); + //----- + //Center + //----- + Delay(PtrInC, PtrOutC,NEURAL_FRAMESIZE,&pParams->DelayChanC); + //----- + //LFE + //----- + Delay(PtrInLFE, PtrOutLFE,NEURAL_FRAMESIZE,&pParams->DelayChanLFE); +#else + //Else just copy the input buffer to output buffers with no delay + CopyArray(PtrInL, PtrOutL, NEURAL_FRAMESIZE); + CopyArray(PtrInR, PtrOutR, NEURAL_FRAMESIZE); + CopyArray(PtrInC, PtrOutC, NEURAL_FRAMESIZE); + CopyArray(PtrInLFE,PtrOutLFE,NEURAL_FRAMESIZE); +#endif + + //*********************************************************** + //***** That's all folks! ***** + //*********************************************************** + SaturateArray(PtrOutL, (float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutR, (float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutC, (float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutLFE,(float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutLs, (float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + SaturateArray(PtrOutRs, (float)AUDIO_VAL_MAX,NEURAL_FRAMESIZE); + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/Neural_THX_Encoders.h b/lib/neural_thx/Neural_THX_Encoders.h new file mode 100755 index 0000000..c118b99 --- /dev/null +++ b/lib/neural_thx/Neural_THX_Encoders.h @@ -0,0 +1,1115 @@ +////////////////////////////////////////////////////////////////////////// +// _____________ // +// _________________ ___________________ // +// _____/~\___________/\_ | // +// ______/\_/\_________/\____ | | | \ / // +// ______/\___/\_______/\______ | | | \ / // +// _____/\_____/\_____/\_______ | |----| / // +// ____/\_______/\___/\________ | | | / \ // +// __/\_________/\_/\________ | | | / \ // +// /\___________/~\_______ ___________________ // +// ___________________ // +// _____________ // +// // +// // +// Neural-THX (R) Surround Technology // +// // +// Copyright (c) 2008 Neural Audio & THX Ltd. // +// // +// Neural Surround is a trademark of Neural Audio Corporation. // +// THX is a trademark of THX Ltd., which may be registered in // +// some jurisdictions. // +// // +// All Rights Reserved. // +// // +// THX & Neural Confidential Information // +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Neural_THX_Encoders.h +// +// Author(s) : Mark Gugler (mgugler@thx.com) +// Jeff Thompson (jeff@neuralaudio.com) +// Aaron Warner +// +// Created On : 08/21/2007 +// +// Last Modified : 03/03/2008 +// +// Version : 1.61 +// +// References : Link to library that is built for the platform you +// are working on. +// +// Description : Encapsulates all header files needed by each encoder +// +// Revision History : 08/21/2007 Build and hope things start to work +// 08/23/2007 Add in 522 encoders and functionality +// 08/24/2007 Prune unnecessary if checks and clean up +// 09/04/2007 Clean up and double checking things +// 11/28/2007 Modify and strip out unneeded functions +// 12/14/2007 Plugging in new 256 framesize code +// 12/17/2007 Final polishing and tests, integrating +// kiss_fft back into the encoder. +// 12/19/2007 - 01/29/2008 Testing platform specific FFT +// implementations +// 02/15/2008 Received 722 encoder and integrating,testing +// and basic optimizing +// 02/25/2008 Took memory allocation out of the kiss_fft +// and made sure we were working... +// 08/01/2008 Updating to new Limiter functionality +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __NEURAL_THX_ENCODERS_H__ +#define __NEURAL_THX_ENCODERS_H__ + +#include "THX__kiss_fft_guts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== */ +/* */ +/* TEXAS INSTRUMENTS, INC. */ +/* */ +/* NAME */ +/* brev_table */ +/* */ +/* USAGE */ +/* This data is required to bit-reverse the output in the */ +/* sp_fftSPxSP routine. Paste this array onto your code */ +/* for use of the routine. */ +/* */ +/* ======================================================================== */ + +//#pragma DATA_SECTION(BRev, ".tables"); +static const unsigned char BRev[64] = { +0x0, 0x20, 0x10, 0x30, 0x8, 0x28, 0x18, 0x38, +0x4, 0x24, 0x14, 0x34, 0xc, 0x2c, 0x1c, 0x3c, +0x2, 0x22, 0x12, 0x32, 0xa, 0x2a, 0x1a, 0x3a, +0x6, 0x26, 0x16, 0x36, 0xe, 0x2e, 0x1e, 0x3e, +0x1, 0x21, 0x11, 0x31, 0x9, 0x29, 0x19, 0x39, +0x5, 0x25, 0x15, 0x35, 0xd, 0x2d, 0x1d, 0x3d, +0x3, 0x23, 0x13, 0x33, 0xb, 0x2b, 0x1b, 0x3b, +0x7, 0x27, 0x17, 0x37, 0xf, 0x2f, 0x1f, 0x3f +}; + +extern unsigned char * GetBrevTable(); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : Common.h *// +//* Description : Sets common parameters, options, and variables *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +//522 Build +#define NEURAL_522ENCODER_BUILD_MAJOR 03 +#define NEURAL_522ENCODER_BUILD_MINOR 00 +//725 Build +#define NEURAL_725ENCODER_BUILD_MAJOR 02 +#define NEURAL_725ENCODER_BUILD_MINOR 00 +//722 Build +#define NEURAL_722ENCODER_BUILD_MAJOR 01 +#define NEURAL_722ENCODER_BUILD_MINOR 00 + +//Only one build type should be defined +#define PC_BUILD + +//Only one cross-sharing amount should be defined +#define CROSSSHARE_8DB //CROSSSHARE_8DB should be defined + +//The audio block size used by the Neural encoder +#define NEURAL_FRAMESIZE 256 //Must be 256 for Downmix + +#ifdef PC_BUILD +#define AUDIO_VAL_MAX 2147483647 //32 bit audio +#define AUDIO_VAL_NORM 4.656612875246e-10f +#endif + +//Define this flag to perform compensation delay on the front channels +//Removing compensation delay saves some memory requirements +#define COMPENSATION_FRONT_DELAY + +//Define supported channel configurations +#define NEURAL_THX_5_2_GAMING 1 +#define NEURAL_THX_7_2_GAMING 2 +#define NEURAL_THX_6_5_GAMING 3 //Not currently supported +#define NEURAL_THX_7_5_GAMING 4 + +//Algorithm constants +#define BACK_CROSS_SHARE 0.35481338923357545843321870226449f + +//Define low frequency handling tables +#define NUMLOWFREQBINS 5 //Smooth the low frequency bins to compensate for small framesize +static const float LowFreqSynthApFactors_32k[] = { 0.0f, 0.22f, 0.47f, 0.58f, 0.71f }; +static const float LowFreqSmoothingFactors_32k[] = { 0.11f, 0.28f, 0.55f, 0.69f, 0.95f }; +static const float LowFreqSynthApFactors_44k[] = { 0.0f, 0.3f, 0.65f, 0.9f, 0.98f }; +static const float LowFreqSmoothingFactors_44k[] = { 0.08f, 0.15f, 0.3f, 0.5f, 0.8f }; +static const float LowFreqSynthApFactors_48k[] = { 0.0f, 0.33f, 0.7f, 0.98f, 0.999f }; +static const float LowFreqSmoothingFactors_48k[] = { 0.07f, 0.13f, 0.28f, 0.46f, 0.74f }; + +//Define supported sample rates +#define SAMPLERATE_32_0 32000 +#define SAMPLERATE_44_1 44100 +#define SAMPLERATE_48_0 48000 + +//Error codes +#define NRLSUR_OK 0 +#define BASE_ERROR -100 +#define INIT_ERROR BASE_ERROR-1 +#define UNSUPPORTED_SAMPLERATE BASE_ERROR-2 +#define UNSUPPORTED_CHANCONFIG BASE_ERROR-3 +#define UNSUPPORTED_FRAMESIZE BASE_ERROR-4 +#define UNSUPPORTED_PARAMETER BASE_ERROR-5 +#define UNKNOWN_ERROR BASE_ERROR-6 +#define NRLTHX_OK NRLSUR_OK + +//Some types used by Neural Encoder +#ifndef THX_bool +#define THX_bool unsigned char +#endif +#ifndef NULL +#define NULL 0 +#endif +#ifndef THX_true +#define THX_true 1 +#endif +#ifndef THX_false +#define THX_false 0 +#endif + +//***************************************************************************// +//* *// +//* Project : Neural Surround *// +//* File : VectorOperations.h *// +//* Description : Basic vector (array) operations *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2006 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +//---------------------------------------------------------------------------// +//- Function List -// +//---------------------------------------------------------------------------// +//- Add2(...) -// +//- Add2_x2(...) -// +//- Add2_x3(...) -// +//- Add2_x4(...) -// +//- Add3(...) -// +//- Add4(...) -// +//- Add5(...) -// +//- Subtract2(...) -// +//- Subtract2_x2(...) -// +//- Subtract2_x3(...) -// +//- Subtract2_x4(...) -// +//- Multiply2(...) -// +//- Multiply2_x2(...) -// +//- Multiply2_x3(...) -// +//- Multiply2_x4(...) -// +//- Multiply3(...) -// +//- Multiply4(...) -// +//- Multiply5(...) -// +//- ComplexMultiply2(...) -// +//- Divide2(...) -// +//- Divide2_x2(...) -// +//- Divide2_x3(...) -// +//- Divide2_x4(...) -// +//- CopyArray(...) -// +//- FillArray(...) -// +//- OffsetArray(...) -// +//- ScaleArray(...) -// +//- SquareArray(...) -// +//- AbsValue(...) -// +//- MaxClip(...) -// +//- MinClip(...) -// +//- SaturateArray(...) -// +//- ReverseArray(...) -// +//---------------------------------------------------------------------------// + +extern void Add2(float * PtrIn0,float * PtrIn1,float * PtrOut0,unsigned int Framesize); +extern void Add2_x2(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,unsigned int Framesize); +extern void Add2_x3(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,unsigned int Framesize); +extern void Add2_x4(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,float * PtrIn6,float * PtrIn7,float * PtrOut3,unsigned int Framesize); +extern void Add3(float * PtrIn0,float * PtrIn1,float * PtrIn2,float * PtrOut0,unsigned int Framesize); +extern void Add4(float * PtrIn0,float * PtrIn1,float * PtrIn2,float * PtrIn3,float * PtrOut0,unsigned int Framesize); +extern void Add5(float * PtrIn0,float * PtrIn1,float * PtrIn2,float * PtrIn3,float * PtrIn4,float * PtrOut0,unsigned int Framesize); +extern void Subtract2(float * PtrIn0,float * PtrIn1,float * PtrOut0,unsigned int Framesize); +extern void Subtract2_x2(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,unsigned int Framesize); +extern void Subtract2_x3(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,unsigned int Framesize); +extern void Subtract2_x4(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,float * PtrIn6,float * PtrIn7,float * PtrOut3,unsigned int Framesize); +extern void Multiply2(float * PtrIn0,float * PtrIn1,float * PtrOut0,unsigned int Framesize); +extern void Multiply2_x2(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,unsigned int Framesize); +extern void Multiply2_x3(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,unsigned int Framesize); +extern void Multiply2_x4(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,float * PtrIn6,float * PtrIn7,float * PtrOut3,unsigned int Framesize); +extern void Multiply3(float * PtrIn0,float * PtrIn1,float * PtrIn2,float * PtrOut0,unsigned int Framesize); +extern void Multiply4(float * PtrIn0,float * PtrIn1,float * PtrIn2,float * PtrIn3,float * PtrOut0,unsigned int Framesize); +extern void Multiply5(float * PtrIn0,float * PtrIn1,float * PtrIn2,float * PtrIn3,float * PtrIn4,float * PtrOut0,unsigned int Framesize); +extern void ComplexMultiply2(float * PtrInReal0,float * PtrInImag0,float * PtrInReal1,float * PtrInImag1,float * PtrOutReal,float * PtrOutImag,unsigned int Framesize); +extern void Divide2(float * PtrIn0,float * PtrIn1,float * PtrOut0,unsigned int Framesize); +extern void Divide2_x2(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,unsigned int Framesize); +extern void Divide2_x3(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,unsigned int Framesize); +extern void Divide2_x4(float * PtrIn0,float * PtrIn1,float * PtrOut0,float * PtrIn2,float * PtrIn3,float * PtrOut1,float * PtrIn4,float * PtrIn5,float * PtrOut2,float * PtrIn6,float * PtrIn7,float * PtrOut3,unsigned int Framesize); +extern void CopyArray(float * PtrIn,float * PtrOut,unsigned int Framesize); +extern void FillArray(float * PtrArray,float FillValue, unsigned int Framesize); +extern void OffsetArray(float * PtrArray,float OffsetValue,unsigned int Framesize); +extern void ScaleArray(float * PtrArray,float ScaleValue,unsigned int Framesize); +extern void SquareArray(float * PtrArray,unsigned int Framesize); +extern void AbsValue(float * PtrArray,unsigned int Framesize); +extern void MaxClip(float * PtrArray,float MaxValue,unsigned int Framesize); +extern void MinClip(float * PtrArray,float MinValue,unsigned int Framesize); +extern void SaturateArray(float * PtrArray,float SaturationValue,unsigned int Framesize); +extern void ReverseArray(float * PtrArray,unsigned int Framesize); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : FreqDomain_PhaseShift.h *// +//* Description : Shift a frequency domain signal in phase *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +#ifndef PI +#define PI 3.1415926535897932384626433832795 +#endif + +//***************************************************************************// +//* Phase Shift Struct *// +//***************************************************************************// +typedef struct +{ + float Prev_PhaseShift_Degrees; + float ShiftScalar_PositiveFreqs_Real; + float ShiftScalar_PositiveFreqs_Imag; + +} FreqDomain_PhaseShift_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// +//----- +//Phase shift by -90 degrees causes the output to lag the input by 90 degrees (similar to Hilbert Transform) +//Phase shift by +90 degrees causes the output to lead the input by 90 degrees +//----- +extern int FreqDomain_PhaseShift_INIT(float PhaseShift_Degrees,unsigned int Framesize,FreqDomain_PhaseShift_Struct * pPtr); +extern int FreqDomain_PhaseShift(float * PtrInReal,float * PtrInImag,float * PtrOutReal,float * PtrOutImag,float PhaseShift_Degrees, unsigned int Framesize,unsigned int SampleRate,FreqDomain_PhaseShift_Struct * pPtr); + +/////////////////////////////////////////////////////////////////////////////// +/// FFT //////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define swap(a,b) tempr=(a); (a)=(b); (b)=tempr; + +//Sine Window 512 coefficients +//Jeff Thompson, Neural Audio +//Generated from script: 8-7-2007 14:8.14 +static const float SineWindow[] = { + 0.003067956763f, 0.009203754782f, 0.015339206285f, 0.021474080275f, + 0.027608145779f, 0.033741171851f, 0.039872927588f, 0.046003182131f, + 0.052131704680f, 0.058258264500f, 0.064382630930f, 0.070504573390f, + 0.076623861392f, 0.082740264549f, 0.088853552583f, 0.094963495330f, + 0.101069862755f, 0.107172424957f, 0.113270952178f, 0.119365214811f, + 0.125454983412f, 0.131540028703f, 0.137620121586f, 0.143695033150f, + 0.149764534677f, 0.155828397654f, 0.161886393780f, 0.167938294975f, + 0.173983873387f, 0.180022901406f, 0.186055151663f, 0.192080397050f, + 0.198098410718f, 0.204108966093f, 0.210111836880f, 0.216106797076f, + 0.222093620973f, 0.228072083171f, 0.234041958584f, 0.240003022449f, + 0.245955050336f, 0.251897818154f, 0.257831102162f, 0.263754678975f, + 0.269668325573f, 0.275571819311f, 0.281464937926f, 0.287347459545f, + 0.293219162694f, 0.299079826308f, 0.304929229735f, 0.310767152750f, + 0.316593375556f, 0.322407678801f, 0.328209843579f, 0.333999651442f, + 0.339776884407f, 0.345541324964f, 0.351292756086f, 0.357030961233f, + 0.362755724367f, 0.368466829953f, 0.374164062971f, 0.379847208924f, + 0.385516053844f, 0.391170384302f, 0.396809987417f, 0.402434650859f, + 0.408044162865f, 0.413638312238f, 0.419216888363f, 0.424779681209f, + 0.430326481340f, 0.435857079922f, 0.441371268732f, 0.446868840162f, + 0.452349587234f, 0.457813303599f, 0.463259783552f, 0.468688822036f, + 0.474100214651f, 0.479493757660f, 0.484869248001f, 0.490226483288f, + 0.495565261826f, 0.500885382611f, 0.506186645345f, 0.511468850438f, + 0.516731799018f, 0.521975292937f, 0.527199134782f, 0.532403127877f, + 0.537587076296f, 0.542750784865f, 0.547894059173f, 0.553016705580f, + 0.558118531221f, 0.563199344014f, 0.568258952670f, 0.573297166698f, + 0.578313796412f, 0.583308652938f, 0.588281548223f, 0.593232295040f, + 0.598160706996f, 0.603066598540f, 0.607949784968f, 0.612810082429f, + 0.617647307938f, 0.622461279374f, 0.627251815495f, 0.632018735940f, + 0.636761861236f, 0.641481012809f, 0.646176012983f, 0.650846684996f, + 0.655492853000f, 0.660114342067f, 0.664710978203f, 0.669282588347f, + 0.673829000379f, 0.678350043130f, 0.682845546385f, 0.687315340892f, + 0.691759258364f, 0.696177131491f, 0.700568793943f, 0.704934080376f, + 0.709272826439f, 0.713584868781f, 0.717870045056f, 0.722128193929f, + 0.726359155084f, 0.730562769228f, 0.734738878096f, 0.738887324461f, + 0.743007952135f, 0.747100605980f, 0.751165131910f, 0.755201376897f, + 0.759209188978f, 0.763188417263f, 0.767138911936f, 0.771060524262f, + 0.774953106595f, 0.778816512381f, 0.782650596167f, 0.786455213599f, + 0.790230221437f, 0.793975477554f, 0.797690840943f, 0.801376171723f, + 0.805031331143f, 0.808656181588f, 0.812250586585f, 0.815814410807f, + 0.819347520077f, 0.822849781376f, 0.826321062846f, 0.829761233795f, + 0.833170164702f, 0.836547727224f, 0.839893794196f, 0.843208239642f, + 0.846490938774f, 0.849741768001f, 0.852960604930f, 0.856147328375f, + 0.859301818357f, 0.862423956111f, 0.865513624091f, 0.868570705971f, + 0.871595086656f, 0.874586652278f, 0.877545290207f, 0.880470889052f, + 0.883363338666f, 0.886222530149f, 0.889048355855f, 0.891840709392f, + 0.894599485631f, 0.897324580705f, 0.900015892016f, 0.902673318237f, + 0.905296759318f, 0.907886116488f, 0.910441292258f, 0.912962190428f, + 0.915448716088f, 0.917900775621f, 0.920318276709f, 0.922701128334f, + 0.925049240783f, 0.927362525650f, 0.929640895843f, 0.931884265582f, + 0.934092550404f, 0.936265667170f, 0.938403534063f, 0.940506070593f, + 0.942573197601f, 0.944604837261f, 0.946600913083f, 0.948561349916f, + 0.950486073949f, 0.952375012720f, 0.954228095109f, 0.956045251350f, + 0.957826413028f, 0.959571513082f, 0.961280485811f, 0.962953266874f, + 0.964589793290f, 0.966190003445f, 0.967753837093f, 0.969281235357f, + 0.970772140729f, 0.972226497079f, 0.973644249651f, 0.975025345067f, + 0.976369731330f, 0.977677357825f, 0.978948175319f, 0.980182135968f, + 0.981379193314f, 0.982539302287f, 0.983662419212f, 0.984748501802f, + 0.985797509168f, 0.986809401814f, 0.987784141645f, 0.988721691960f, + 0.989622017463f, 0.990485084256f, 0.991310859846f, 0.992099313142f, + 0.992850414460f, 0.993564135521f, 0.994240449453f, 0.994879330795f, + 0.995480755492f, 0.996044700901f, 0.996571145791f, 0.997060070339f, + 0.997511456140f, 0.997925286199f, 0.998301544934f, 0.998640218180f, + 0.998941293187f, 0.999204758618f, 0.999430604555f, 0.999618822495f, + 0.999769405351f, 0.999882347454f, 0.999957644552f, 0.999995293810f, +}; + +extern float * GetSineWindow(); + +//***************************************************************************// +//* *// +//* Project : Neural Surround *// +//* File : FFT_Overlapped_Stereo.h *// +//* Description : A windowed and overlapped FFT and IFFT *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +#if NEURAL_FRAMESIZE == 1024 +#define FFTSIZE 2048 +#define HALFFFTSIZE 1024 +#elif NEURAL_FRAMESIZE == 256 +#define FFTSIZE 512 +#define HALFFFTSIZE 256 +#endif + +#ifndef FFT_MEM_CHUNK +#define FFT_MEM_CHUNK 4360 // Amount of Memory the Kiss_FFT needs +static char fft_buff[FFT_MEM_CHUNK]; +#endif + +//***************************************************************************// +//* Forward FFT Structure *// +//***************************************************************************// +typedef struct +{ + float pOverlappedBuffer0[HALFFFTSIZE]; + float pOverlappedBuffer1[HALFFFTSIZE]; + float * pSineWin; + + float * pReal; //Size must be 2*Framesize + float * pImag; //Size must be 2*Framesize + + THX_kiss_fft_cfg state; + THX_kiss_fft_cpx pIn[FFTSIZE]; + THX_kiss_fft_cpx pOut[FFTSIZE]; +} FFT_Overlapped_Stereo_Struct; + +//***************************************************************************// +//* Inverse FFT Structure *// +//***************************************************************************// +typedef struct +{ + float pOverlappedBuffer0[HALFFFTSIZE]; + float pOverlappedBuffer1[HALFFFTSIZE]; + float * pSineWin; + + float * pReal; //Size must be 2*Framesize + float * pImag; //Size must be 2*Framesize + + THX_kiss_fft_cfg state; + THX_kiss_fft_cpx pIn[FFTSIZE]; + THX_kiss_fft_cpx pOut[FFTSIZE]; +} IFFT_Overlapped_Stereo_Struct; + + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int FFT_Overlapped_Stereo_INIT(FFT_Overlapped_Stereo_Struct * pPersistent,float * pTempBuffer0,float * pTempBuffer1,unsigned int Framesize); +extern int FFT_Overlapped_Stereo(float * PtrIn0,float * PtrOutReal0,float * PtrOutImag0,float * PtrIn1,float * PtrOutReal1,float * PtrOutImag1,unsigned int Framesize,FFT_Overlapped_Stereo_Struct * pPersistent); +extern int IFFT_Overlapped_Stereo_INIT(IFFT_Overlapped_Stereo_Struct * pPersistent,float * pTempBuffer0,float * pTempBuffer1,unsigned int Framesize); +extern int IFFT_Overlapped_Stereo(float * PtrInReal0,float * PtrInImag0,float * PtrOut0,float * PtrInReal1,float * PtrInImag1,float * PtrOut1,unsigned int Framesize,IFFT_Overlapped_Stereo_Struct * pPersistent); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : Delay.h *// +//* Description : Fixed delay *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +typedef struct +{ +//***************************************************************************// +//* Internal Persistent Variables *// +//***************************************************************************// + float Buffer[NEURAL_FRAMESIZE+1]; + unsigned int Pntr; + unsigned int DelayAmount; +} Delay_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int Delay_INIT(unsigned int DelayAmount,Delay_Struct * pPtr); +extern int Delay(float * PtrIn,float * PtrOut,unsigned int Framesize,Delay_Struct * pPtr); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : LR4_LP.h *// +//* Description : Linkwitz-Riley 4th order Low-Pass filter *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2005 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +#ifndef PI +#define PI 3.1415926535897932384626433832795 +#endif + +#define TWOPOWTHREEHALVES 2.8284271247461900976033774484194 + +typedef struct +{ +//***************************************************************************// +//* Internal Persistent Variables *// +//***************************************************************************// + float PrevCutOff; + float PrevStage00; + float PrevStage01; + float PrevStage10; + float PrevStage11; + float b0; + float b1; + float b2; + float a0; + float a1; + float PrevSampleRate; + +} LR4_LP_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern void LR4_LP_INIT(LR4_LP_Struct * pPtr); +extern void LR4_LP(float * PtrIn,float * PtrOut,float Cutoff,unsigned int Framesize,unsigned int SampleRate,LR4_LP_Struct * pPtr); + +//***************************************************************************// +//* *// +//* Project : Neural Surround *// +//* File : FFT_Overlapped.h *// +//* Description : A windowed and overlapped FFT and IFFT *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +#if NEURAL_FRAMESIZE == 1024 +#define FFTSIZE 2048 +#define HALFFFTSIZE 1024 +#elif NEURAL_FRAMESIZE == 256 +#define FFTSIZE 512 +#define HALFFFTSIZE 256 +#endif + +#ifndef FFT_MEM_CHUNK +#define FFT_MEM_CHUNK 4360 // Amount of Memory the Kiss_FFT needs +static char fft_buff[FFT_MEM_CHUNK]; +#endif + +//***************************************************************************// +//* Forward FFT Structure *// +//***************************************************************************// +typedef struct +{ + float pOverlappedBuffer[HALFFFTSIZE]; + float * pSineWin; + + float * pReal; //Size must be 2*Framesize + float * pImag; //Size must be 2*Framesize + + THX_kiss_fft_cfg state; + THX_kiss_fft_cpx pIn[FFTSIZE]; + THX_kiss_fft_cpx pOut[FFTSIZE]; +} FFT_Overlapped_Struct; + +//***************************************************************************// +//* Inverse FFT Structure *// +//***************************************************************************// +typedef struct +{ + float pOverlappedBuffer[HALFFFTSIZE]; + float * pSineWin; + + float * pReal; //Size must be 2*Framesize + float * pImag; //Size must be 2*Framesize + + THX_kiss_fft_cfg state; + THX_kiss_fft_cpx pIn[FFTSIZE]; + THX_kiss_fft_cpx pOut[FFTSIZE]; +} IFFT_Overlapped_Struct; + + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int FFT_Overlapped_INIT(FFT_Overlapped_Struct * pPersistent,float * pTempBuffer0,float * pTempBuffer1,unsigned int Framesize); +extern int FFT_Overlapped(float * PtrIn,float * PtrOutReal,float * PtrOutImag,unsigned int Framesize,FFT_Overlapped_Struct * pPersistent); +extern int IFFT_Overlapped_INIT(IFFT_Overlapped_Struct * pPersistent,float * pTempBuffer0, float * pTempBuffer1, unsigned int Framesize); +extern int IFFT_Overlapped(float * PtrInReal,float * PtrInImag,float * PtrOut,unsigned int Framesize,IFFT_Overlapped_Struct * pPersistent); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : FastMathApprox.h *// +//* Description : Fast math approximations *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// +#define USE_MAG_APPROX +#define USE_PHASE_APPROX +#define USE_SQRT_APPROX +#define USE_LOG_APPROX +#define USE_POW_APPROX +#define USE_SIN_APPROX +#define USE_COS_APPROX + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +#ifndef PI +#define PI 3.1415926535897932384626434 +#endif + +typedef float ieee754_float32_t; + +#define LOG2_SIZE 512 +#define LOG2_SIZE_L2 9 + +#define LOG2 0.69314718055994530942f +#define LOG10 2.30258509299404568402f +#define LOG2OVERLOG10 0.30102999566398119521f + +#define EXP_A 1512775.39519518569384f +#define EXP_C 45799//60801// + +static const float LogTable[] = { +0.000000000000f, 0.002815015614f, 0.005624548998f, 0.008428622037f, 0.011227255687f, 0.014020469971f, +0.016808288172f, 0.019590727985f, 0.022367812693f, 0.025139562786f, 0.027905996889f, 0.030667135492f, +0.033423002809f, 0.036173611879f, 0.038918990642f, 0.041659150273f, 0.044394120574f, 0.047123912722f, +0.049848549068f, 0.052568051964f, 0.055282436311f, 0.057991724461f, 0.060695931315f, 0.063395082951f, +0.066089190543f, 0.068778276443f, 0.071462363005f, 0.074141465127f, 0.076815597713f, 0.079484783113f, +0.082149043679f, 0.084808386862f, 0.087462842464f, 0.090112417936f, 0.092757143080f, 0.095397025347f, +0.098032079637f, 0.100662335753f, 0.103287808597f, 0.105908505619f, 0.108524456620f, 0.111135669053f, +0.113742165267f, 0.116343960166f, 0.118941076100f, 0.121533520520f, 0.124121308327f, 0.126704469323f, +0.129283010960f, 0.131856963038f, 0.134426325560f, 0.136991113424f, 0.139551356435f, 0.142107054591f, +0.144658237696f, 0.147204920650f, 0.149747118354f, 0.152284845710f, 0.154818102717f, 0.157346934080f, +0.159871339798f, 0.162391334772f, 0.164906933904f, 0.167418152094f, 0.169925004244f, 0.172427505255f, +0.174925684929f, 0.177419543266f, 0.179909095168f, 0.182394355536f, 0.184875339270f, 0.187352076173f, +0.189824566245f, 0.192292809486f, 0.194756850600f, 0.197216689587f, 0.199672341347f, 0.202123820782f, +0.204571142793f, 0.207014322281f, 0.209453359246f, 0.211888298392f, 0.214319124818f, 0.216745853424f, +0.219168514013f, 0.221587121487f, 0.224001675844f, 0.226412191987f, 0.228818684816f, 0.231221184134f, +0.233619675040f, 0.236014187336f, 0.238404735923f, 0.240791335702f, 0.243173986673f, 0.245552703738f, +0.247927516699f, 0.250298410654f, 0.252665430307f, 0.255028575659f, 0.257387846708f, 0.259743273258f, +0.262094855309f, 0.264442592859f, 0.266786545515f, 0.269126683474f, 0.271463036537f, 0.273795604706f, +0.276124417782f, 0.278449445963f, 0.280770778656f, 0.283088356256f, 0.285402208567f, 0.287712365389f, +0.290018856525f, 0.292321622372f, 0.294620752335f, 0.296916216612f, 0.299208015203f, 0.301496207714f, +0.303780734539f, 0.306061685085f, 0.308339029551f, 0.310612767935f, 0.312882959843f, 0.315149575472f, +0.317412614822f, 0.319672107697f, 0.321928083897f, 0.324180543423f, 0.326429486275f, 0.328674912453f, +0.330916881561f, 0.333155363798f, 0.335390359163f, 0.337621897459f, 0.339850008488f, 0.342074662447f, +0.344295918941f, 0.346513718367f, 0.348728150129f, 0.350939184427f, 0.353146821260f, 0.355351090431f, +0.357551991940f, 0.359749555588f, 0.361943781376f, 0.364134669304f, 0.366322219372f, 0.368506461382f, +0.370687395334f, 0.372865051031f, 0.375039428473f, 0.377210527658f, 0.379378378391f, 0.381542950869f, +0.383704304695f, 0.385862410069f, 0.388017296791f, 0.390168964863f, 0.392317414284f, 0.394462704659f, +0.396604776382f, 0.398743689060f, 0.400879442692f, 0.403012037277f, 0.405141472816f, 0.407267779112f, +0.409390926361f, 0.411510974169f, 0.413627922535f, 0.415741771460f, 0.417852520943f, 0.419960170984f, +0.422064751387f, 0.424166291952f, 0.426264762878f, 0.428360164165f, 0.430452555418f, 0.432541906834f, +0.434628218412f, 0.436711549759f, 0.438791841269f, 0.440869182348f, 0.442943483591f, 0.445014834404f, +0.447083234787f, 0.449148654938f, 0.451211124659f, 0.453270643950f, 0.455327212811f, 0.457380890846f, +0.459431618452f, 0.461479455233f, 0.463524371386f, 0.465566396713f, 0.467605561018f, 0.469641804695f, +0.471675217152f, 0.473705738783f, 0.475733429193f, 0.477758258581f, 0.479780256748f, 0.481799423695f, +0.483815789223f, 0.485829323530f, 0.487840026617f, 0.489847958088f, 0.491853088140f, 0.493855446577f, +0.495855033398f, 0.497851848602f, 0.499845892191f, 0.501837193966f, 0.503825724125f, 0.505811572075f, +0.507794618607f, 0.509774982929f, 0.511752665043f, 0.513727605343f, 0.515699863434f, 0.517669379711f, +0.519636273384f, 0.521600425243f, 0.523561954498f, 0.525520801544f, 0.527477025986f, 0.529430568218f, +0.531381487846f, 0.533329725266f, 0.535275399685f, 0.537218391895f, 0.539158821106f, 0.541096627712f, +0.543031811714f, 0.544964432716f, 0.546894431114f, 0.548821926117f, 0.550746798515f, 0.552669107914f, +0.554588854313f, 0.556506037712f, 0.558420717716f, 0.560332834721f, 0.562242448330f, 0.564149498940f, +0.566054046154f, 0.567956089973f, 0.569855630398f, 0.571752667427f, 0.573647201061f, 0.575539231300f, +0.577428817749f, 0.579315960407f, 0.581200599670f, 0.583082795143f, 0.584962487221f, 0.586839795113f, +0.588714659214f, 0.590587079525f, 0.592457056046f, 0.594324588776f, 0.596189737320f, 0.598052501678f, +0.599912822247f, 0.601770758629f, 0.603626370430f, 0.605479538441f, 0.607330322266f, 0.609178721905f, +0.611024796963f, 0.612868487835f, 0.614709854126f, 0.616548836231f, 0.618385493755f, 0.620219826698f, +0.622051835060f, 0.623881518841f, 0.625708818436f, 0.627533912659f, 0.629356622696f, 0.631177067757f, +0.632995188236f, 0.634811043739f, 0.636624634266f, 0.638435900211f, 0.640244960785f, 0.642051696777f, +0.643856167793f, 0.645658433437f, 0.647458434105f, 0.649256169796f, 0.651051700115f, 0.652844965458f, +0.654636025429f, 0.656424880028f, 0.658211469650f, 0.659995913506f, 0.661778092384f, 0.663558125496f, +0.665335893631f, 0.667111515999f, 0.668884992599f, 0.670656263828f, 0.672425329685f, 0.674192249775f, +0.675957024097f, 0.677719652653f, 0.679480075836f, 0.681238412857f, 0.682994604111f, 0.684748649597f, +0.686500549316f, 0.688250303268f, 0.689997971058f, 0.691743493080f, 0.693486928940f, 0.695228278637f, +0.696967542171f, 0.698704659939f, 0.700439691544f, 0.702172696590f, 0.703903555870f, 0.705632388592f, +0.707359135151f, 0.709083795547f, 0.710806429386f, 0.712526977062f, 0.714245498180f, 0.715961992741f, +0.717676401138f, 0.719388842583f, 0.721099197865f, 0.722807526588f, 0.724513828754f, 0.726218163967f, +0.727920472622f, 0.729620754719f, 0.731319010258f, 0.733015298843f, 0.734709620476f, 0.736401915550f, +0.738092243671f, 0.739780604839f, 0.741466999054f, 0.743151366711f, 0.744833827019f, 0.746514320374f, +0.748192846775f, 0.749869406223f, 0.751544058323f, 0.753216743469f, 0.754887521267f, 0.756556332111f, +0.758223235607f, 0.759888172150f, 0.761551260948f, 0.763212382793f, 0.764871597290f, 0.766528904438f, +0.768184304237f, 0.769837856293f, 0.771489441395f, 0.773139178753f, 0.774787068367f, 0.776433050632f, +0.778077125549f, 0.779719352722f, 0.781359732151f, 0.782998204231f, 0.784634828568f, 0.786269605160f, +0.787902534008f, 0.789533674717f, 0.791162908077f, 0.792790293694f, 0.794415891171f, 0.796039581299f, +0.797661542892f, 0.799281597137f, 0.800899922848f, 0.802516341209f, 0.804131031036f, 0.805743873119f, +0.807354927063f, 0.808964192867f, 0.810571610928f, 0.812177300453f, 0.813781201839f, 0.815383315086f, +0.816983640194f, 0.818582177162f, 0.820178985596f, 0.821774005890f, 0.823367238045f, 0.824958741665f, +0.826548516750f, 0.828136503696f, 0.829722762108f, 0.831307232380f, 0.832890033722f, 0.834471046925f, +0.836050331593f, 0.837627947330f, 0.839203774929f, 0.840777933598f, 0.842350363731f, 0.843921065331f, +0.845490038395f, 0.847057342529f, 0.848622918129f, 0.850186824799f, 0.851749062538f, 0.853309571743f, +0.854868412018f, 0.856425523758f, 0.857980966568f, 0.859534800053f, 0.861086905003f, 0.862637341022f, +0.864186167717f, 0.865733265877f, 0.867278754711f, 0.868822574615f, 0.870364725590f, 0.871905267239f, +0.873444139957f, 0.874981343746f, 0.876516938210f, 0.878050923347f, 0.879583239555f, 0.881113946438f, +0.882643043995f, 0.884170532227f, 0.885696351528f, 0.887220621109f, 0.888743221760f, 0.890264272690f, +0.891783714294f, 0.893301546574f, 0.894817769527f, 0.896332383156f, 0.897845447063f, 0.899356901646f, +0.900866806507f, 0.902375102043f, 0.903881847858f, 0.905386984348f, 0.906890571117f, 0.908392608166f, +0.909893095493f, 0.911391973495f, 0.912889361382f, 0.914385139942f, 0.915879368782f, 0.917372107506f, +0.918863236904f, 0.920352876186f, 0.921840965748f, 0.923327505589f, 0.924812495708f, 0.926295995712f, +0.927777945995f, 0.929258406162f, 0.930737316608f, 0.932214736938f, 0.933690667152f, 0.935165047646f, +0.936637938023f, 0.938109338284f, 0.939579188824f, 0.941047608852f, 0.942514479160f, 0.943979918957f, +0.945443809032f, 0.946906268597f, 0.948367238045f, 0.949826717377f, 0.951284706593f, 0.952741265297f, +0.954196333885f, 0.955649912357f, 0.957102060318f, 0.958552718163f, 0.960001945496f, 0.961449682713f, +0.962895989418f, 0.964340865612f, 0.965784311295f, 0.967226266861f, 0.968666791916f, 0.970105886459f, +0.971543550491f, 0.972979784012f, 0.974414587021f, 0.975847959518f, 0.977279901505f, 0.978710472584f, +0.980139553547f, 0.981567263603f, 0.982993602753f, 0.984418451786f, 0.985841929913f, 0.987264037132f, +0.988684713840f, 0.990103960037f, 0.991521835327f, 0.992938339710f, 0.994353413582f, 0.995767176151f, +0.997179508209f, 0.998590409756f, 1.000000000000f, +}; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +//----- Magnitude approximations ----- +extern float FastMag(float r, float i); //Good accuracy +extern void FastMag_ARRAY(float * PtrInRe,float * PtrInIm,float * PtrOut,unsigned int Framesize); + +//----- Fast phase approximations ----- +typedef struct +{ + float * Quadrant; + float * Numerator; + float * Denominator; + +} FastPhase_Struct; + +extern void FastPhase_INIT(FastPhase_Struct * pParams,float * TempBuffer0,float * TempBuffer1,float * TempBuffer2); +extern float FastPhase(float r, float i); //Good accuracy +extern void FastPhase_ARRAY(float * PtrInRe,float * PtrInIm,float * PtrOutPhase,unsigned int Framesize,FastPhase_Struct * pParams); +//----- Square root approximations ----- +extern float FastSqrt(float x); //Good accuracy +extern void FastSqrt_ARRAY(float * PtrIn,float * PtrOut,unsigned int Framesize); +//----- Log approximations ----- +extern float FastLog2(float x); //Good accuracy +extern void FastLog2_ARRAY(float * PtrIn,float * PtrOut,unsigned int Framesize); +extern float FastLog10(float x); //Good accuracy +extern void FastLog10_ARRAY(float * PtrIn, float * PtrOut,unsigned int Framesize); +extern float FastLogln(float x); //Good accuracy +extern void FastLogln_ARRAY(float * PtrIn, float * PtrOut,unsigned int Framesize); +//----- Pow approximations ----- +extern float FastPow(float x, float y); //Okay accuracy +extern void FastPow_ARRAY(float * PtrIn0,float * PtrIn1,float * PtrOut,unsigned int Framesize); +extern float FastPow2(float x); //Okay accuracy +extern void FastPow2_ARRAY(float * PtrIn,float * PtrOut,unsigned int Framesize); +extern float FastPow10(float x); //Okay accuracy +extern void FastPow10_ARRAY(float * PtrIn, float * PtrOut,unsigned int Framesize); +//----- Exponential approximations ----- +extern float FastExp(float x); //Okay accuracy +extern void FastExp_ARRAY(float * PtrIn,float * PtrOut,unsigned int Framesize); +//----- Trig approximations ----- +//Input must be between -PI <= x <= PI +extern float FastSin(float x); //Good accuracy +extern void FastSin_ARRAY(float * PtrIn,float * PtrOut,unsigned int Framesize); +//Input must be between -PI <= x <= PI +extern float FastCos(float x); //Good accuracy +extern void FastCos_ARRAY(float * PtrIn,float * PtrOut,unsigned int Framesize); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : PeakConverter.h *// +//* Description : Peak converter with rise time / fall time *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* Peak Converter Struct *// +//***************************************************************************// +typedef struct +{ + unsigned int SampleRate; + float RiseTime; + float FallTime; + float MinValue; + float MaxValue; + float PrevValue; + float RiseSlew; + float FallSlew; + +} PeakConverter_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// +extern int PeakConverter_INIT(float RiseTime,float FallTime,float MinValue,float MaxValue,unsigned int SampleRate,PeakConverter_Struct * pParams); +extern int PeakConverter(float * PtrIn,float * PtrOut,float RiseTime,float FallTime,float MinValue,float MaxValue,unsigned int Framesize,unsigned int SampleRate,PeakConverter_Struct * pParams); + +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : Limiter.h *// +//* Description : Limiter *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +typedef struct +{ + PeakConverter_Struct EnvSmoother; + float * TempBuffer; + +} Limiter_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int Limiter_INIT(float * TempBuffer,Limiter_Struct * pPtr); +extern int Limiter(float * AudioIn,float * AudioOut,float MaxAudioValue,float Knee, float Ceiling,float RiseTime,float FallTime,unsigned int Framesize,unsigned int SampleRate,Limiter_Struct * pPtr); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////// Neural Encoders +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//***************************************************************************// +//* *// +//* Project : Neural Audio - THX *// +//* File : Neural_THX_725_Encode.h *// +//* Description : Downmixer for Neural - THX gaming mode from 7.1 to 5.1 *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// + +//***************************************************************************// +//* Downmixer structure *// +//***************************************************************************// +typedef struct +{ + //FFT transforms for input/output channels + FFT_Overlapped_Stereo_Struct Transform_LsRs; + FFT_Overlapped_Stereo_Struct Transform_LbRb; + IFFT_Overlapped_Stereo_Struct InvTransform_LsRs; + + //Phase shift for back channels + FreqDomain_PhaseShift_Struct PhaseShift_Pos; + FreqDomain_PhaseShift_Struct PhaseShift_Neg; + +#ifdef COMPENSATION_FRONT_DELAY + //Delays for all other channels + Delay_Struct DelayChanL; + Delay_Struct DelayChanR; + Delay_Struct DelayChanC; + Delay_Struct DelayChanLFE; +#endif + + //Final limiters + Limiter_Struct FinalLimiterLs; + Limiter_Struct FinalLimiterRs; + + float Input_RealLs[NEURAL_FRAMESIZE]; + float Input_ImagLs[NEURAL_FRAMESIZE]; + float Input_RealRs[NEURAL_FRAMESIZE]; + float Input_ImagRs[NEURAL_FRAMESIZE]; + float Input_RealLb[NEURAL_FRAMESIZE]; + float Input_ImagLb[NEURAL_FRAMESIZE]; + float Input_RealRb[NEURAL_FRAMESIZE]; + float Input_ImagRb[NEURAL_FRAMESIZE]; + + float Dmix_RealLs[NEURAL_FRAMESIZE]; + float Dmix_ImagLs[NEURAL_FRAMESIZE]; + float Dmix_RealRs[NEURAL_FRAMESIZE]; + float Dmix_ImagRs[NEURAL_FRAMESIZE]; + + //Temporary buffers + float Placeholder; + float TempBuffer0[4*NEURAL_FRAMESIZE]; + float TempBuffer1[4*NEURAL_FRAMESIZE]; + +} Neural_THX_725_Encode_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int Neural_THX_725_Encode_INIT(unsigned int Framesize,unsigned int ChanConfig,unsigned int SampleRate,Neural_THX_725_Encode_Struct * pParams); +extern int Neural_THX_725_Encode(float * PtrInL,float * PtrInR,float * PtrInC,float * PtrInLFE,float * PtrInLs,float * PtrInRs,float * PtrInLb,float * PtrInRb,float * PtrOutL,float * PtrOutR,float * PtrOutC,float * PtrOutLFE,float * PtrOutLs,float * PtrOutRs,THX_bool UseFinalLimiting,unsigned int Framesize,unsigned int ChanConfig,unsigned int SampleRate,Neural_THX_725_Encode_Struct * pParams); + +//***************************************************************************// +//* *// +//* Project : Neural Audio - THX *// +//* File : Neural_THX_522_Encode.h *// +//* Description : Neural Surround Downmix implementation *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +//Algorithm constants for cross-sharing (CROSSSHARE_XDB defined in Common.h) +#ifdef CROSSSHARE_9DB +#define CROSSSHARECONSTANT 0.354813389233575f //-9dB +#define CROSSSHARENORM 0.942435265799133f //Use for -9dB +#endif +#ifdef CROSSSHARE_8DB +#define CROSSSHARECONSTANT 0.398107170553497f //-8dB +#define CROSSSHARENORM 0.929081864744324f //Use for -8dB +#endif +#ifdef CROSSSHARE_7DB +#define CROSSSHARECONSTANT 0.446683592150963f //-7dB +#define CROSSSHARENORM 0.913051186507875f //Use for -7dB +#endif +#ifdef CROSSSHARE_6DB +#define CROSSSHARECONSTANT 0.501187233627272f //-6dB +#define CROSSSHARENORM 0.894002232148723f //Use for -6dB +#endif + +#define MINUS_3DB 0.707945784384138f + +//***************************************************************************// +//* Encoder structure *// +//***************************************************************************// +typedef struct +{ + //Forward and inverse overlapped FFT's + FFT_Overlapped_Stereo_Struct FFTSourceLR; + FFT_Overlapped_Struct FFTSourceC; + FFT_Overlapped_Stereo_Struct FFTSourceLsRs; + IFFT_Overlapped_Stereo_Struct InvFFTDownmixLR; + + //Phase shifts on L, R, Ls, Rs + FreqDomain_PhaseShift_Struct PhaseShift_L; + FreqDomain_PhaseShift_Struct PhaseShift_R; + FreqDomain_PhaseShift_Struct PhaseShift_Ls; + FreqDomain_PhaseShift_Struct PhaseShift_Rs; + + //Low-pass of the LFE channel + LR4_LP_Struct LFE_LP; + + //Final limiters + Limiter_Struct FinalLimiterL; + Limiter_Struct FinalLimiterR; + + //Some temporary buffers + float Placeholder; + float TempBuffer0[4*NEURAL_FRAMESIZE]; + float TempBuffer1[4*NEURAL_FRAMESIZE]; + + //Working buffers + float Input_RealL[NEURAL_FRAMESIZE]; + float Input_ImagL[NEURAL_FRAMESIZE]; + float Input_RealR[NEURAL_FRAMESIZE]; + float Input_ImagR[NEURAL_FRAMESIZE]; + float Input_RealC[NEURAL_FRAMESIZE]; + float Input_ImagC[NEURAL_FRAMESIZE]; + float Input_RealLs[NEURAL_FRAMESIZE]; + float Input_ImagLs[NEURAL_FRAMESIZE]; + float Input_RealRs[NEURAL_FRAMESIZE]; + float Input_ImagRs[NEURAL_FRAMESIZE]; + + float Downmix_RealL[NEURAL_FRAMESIZE]; + float Downmix_ImagL[NEURAL_FRAMESIZE]; + float Downmix_RealR[NEURAL_FRAMESIZE]; + float Downmix_ImagR[NEURAL_FRAMESIZE]; + +} Neural_THX_522_Encode_Struct; + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int Neural_THX_522_Encode_INIT(unsigned int Framesize,unsigned int ChanConfig,unsigned int SampleRate,Neural_THX_522_Encode_Struct * pParams); +extern int Neural_THX_522_Encode(float * PtrInL,float * PtrInR,float * PtrInC,float * PtrInLFE,float * PtrInLs,float * PtrInRs,float * PtrOutL,float * PtrOutR,THX_bool UseFinalLimiting,float LFE_Cutoff,unsigned int Framesize,unsigned int ChanConfig,unsigned int SampleRate,Neural_THX_522_Encode_Struct * pParams); + +//***************************************************************************// +//* *// +//* Project : Neural Audio - THX *// +//* File : Neural_THX_722_Encode.h *// +//* Description : Neural Surround Downmix implementation *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2007 *// +//* *// +//***************************************************************************// + +//***************************************************************************// +//* DEFINITIONS *// +//***************************************************************************// +//Algorithm constants for cross-sharing (CROSSSHARE_XDB defined in Common.h) +#ifdef CROSSSHARE_9DB +#define SUR_CROSSSHARECONSTANT 0.354813389233575f //-9dB +#define SUR_CROSSSHARENORM 0.942435265799133f //Use for -9dB +#endif +#ifdef CROSSSHARE_8DB +#define SUR_CROSSSHARECONSTANT 0.398107170553497f //-8dB +#define SUR_CROSSSHARENORM 0.929081864744324f //Use for -8dB +#define BACK_CROSSSHARECONSTANT 0.575439937337157f //-4.8dB +#define BACK_CROSSSHARENORM 0.866741480219858f //Use for -4.8dB +#endif +#ifdef CROSSSHARE_7DB +#define SUR_CROSSSHARECONSTANT 0.446683592150963f //-7dB +#define SUR_CROSSSHARENORM 0.913051186507875f //Use for -7dB +#endif +#ifdef CROSSSHARE_6DB +#define SUR_CROSSSHARECONSTANT 0.501187233627272f //-6dB +#define SUR_CROSSSHARENORM 0.894002232148723f //Use for -6dB +#endif + +#define MINUS_3DB 0.707945784384138f + +//***************************************************************************// +//* Encoder structure *// +//***************************************************************************// +typedef struct +{ + //Forward and inverse overlapped FFT's + FFT_Overlapped_Stereo_Struct FFTSourceLR; + FFT_Overlapped_Struct FFTSourceC; + FFT_Overlapped_Stereo_Struct FFTSourceLsRs; + FFT_Overlapped_Stereo_Struct FFTSourceLbRb; + IFFT_Overlapped_Stereo_Struct InvFFTDownmixLR; + + //Phase shifts on L, R, Ls, Rs + FreqDomain_PhaseShift_Struct PhaseShift_L; + FreqDomain_PhaseShift_Struct PhaseShift_R; + FreqDomain_PhaseShift_Struct PhaseShift_Ls; + FreqDomain_PhaseShift_Struct PhaseShift_Rs; + + //Low-pass of the LFE channel + LR4_LP_Struct LFE_LP; + + //Final limiters + Limiter_Struct FinalLimiterL; + Limiter_Struct FinalLimiterR; + + //Some temporary buffers + float Placeholder; + float TempBuffer0[4*NEURAL_FRAMESIZE]; + float TempBuffer1[4*NEURAL_FRAMESIZE]; + + //Working buffers + float Input_RealL[NEURAL_FRAMESIZE]; + float Input_ImagL[NEURAL_FRAMESIZE]; + float Input_RealR[NEURAL_FRAMESIZE]; + float Input_ImagR[NEURAL_FRAMESIZE]; + float Input_RealC[NEURAL_FRAMESIZE]; + float Input_ImagC[NEURAL_FRAMESIZE]; + float Input_RealLs[NEURAL_FRAMESIZE]; + float Input_ImagLs[NEURAL_FRAMESIZE]; + float Input_RealRs[NEURAL_FRAMESIZE]; + float Input_ImagRs[NEURAL_FRAMESIZE]; + float Input_RealLb[NEURAL_FRAMESIZE]; + float Input_ImagLb[NEURAL_FRAMESIZE]; + float Input_RealRb[NEURAL_FRAMESIZE]; + float Input_ImagRb[NEURAL_FRAMESIZE]; + + float Downmix_RealL[NEURAL_FRAMESIZE]; + float Downmix_ImagL[NEURAL_FRAMESIZE]; + float Downmix_RealR[NEURAL_FRAMESIZE]; + float Downmix_ImagR[NEURAL_FRAMESIZE]; + +} Neural_THX_722_Encode_Struct; + + +//***************************************************************************// +//* FUNCTION PROTOTYPES *// +//***************************************************************************// + +extern int Neural_THX_722_Encode_INIT(unsigned int Framesize,unsigned int ChanConfig,unsigned int SampleRate,Neural_THX_722_Encode_Struct * pParams); +extern int Neural_THX_722_Encode(float * PtrInL,float * PtrInR,float * PtrInC,float * PtrInLFE,float * PtrInLs,float * PtrInRs,float * PtrInLb,float * PtrInRb,float * PtrOutL,float * PtrOutR,THX_bool UseFinalLimiting,float LFE_Cutoff,unsigned int Framesize,unsigned int ChanConfig,unsigned int SampleRate,Neural_THX_722_Encode_Struct * pParams); + +/////////////////////////////////////// +// Closing brace for extern "C" +/////////////////////////////////////// +#ifdef __cplusplus +} // Closing brace for extern "C" +#endif + +/////////////////////////////////////// +// end of file +/////////////////////////////////////// +#endif //__NEURAL_THX_ENCODERS_H__ + diff --git a/lib/neural_thx/Neural_THX_Global.h b/lib/neural_thx/Neural_THX_Global.h new file mode 100755 index 0000000..3326413 --- /dev/null +++ b/lib/neural_thx/Neural_THX_Global.h @@ -0,0 +1,311 @@ +////////////////////////////////////////////////////////////////////// +// ___________________ // +// | // +// | | | \ / // +// | | | \ / // +// | |----| / // +// | | | / \ // +// | | | / \ // +// ___________________ // +// // +// Neural-THX (R) Surround Technology // +// // +// Copyright (c) 2008 THX Ltd. // +// // +// THX is a trademark of THX Ltd., which may be registered // +// in some jurisdictions. // +// All Rights Reserved. // +// // +// THX Confidential Information // +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Neural_THX_Global.h +// +// Author(s) : Mark Gugler (mgugler@thx.com) +// +// Created On : 08/20/2007 +// +// Last Modified : 03/03/2008 +// +// Version : 1.61 +// +// References : +// +// Description : Encapsulates any enumerations, structures, and +// defines needed by the Neural-THX API +// +// Revision History : 08/20/2007 Build basic framework and add in new +// structs for the encoder +// 08/21/2007 Add in more comments and update some values +// based on what was received from Neural +// 08/22/2007 Add in a new structure to encapsulate +// parameter structures into one interface +// 08/23/2007 Finish adding functions to the new struct +// Add 522 parameters to settings struct +// 08/31/2007 Updating comments for clearly outlining +// what values represent +// 09/04/2007 Update comments and prepare for 722 +// encoder integration +// 02/15/2008 Got 722 encoder so integrating and testing +// 02/26/2008 Added some defines and stripped out any +// remaining memory allocation from the API +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef __NEURAL_THX_GLOBAL__ +#define __NEURAL_THX_GLOBAL__ + +#include "Neural_THX_Encoders.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Global Defines +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// NOTE:: Only comment out the encoders you won't use over the course of the game +// This was implemented to not have any memory allocation +// throughout the entire API. +#define USING_725 +#define USING_722 +// #define USING_522 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Definitions +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Neural_THX_Encoder_Params +// +// Created On : 08/22/2007 +// +// Last Modified : 02/28/2008 +// +// Description : Parameter structure encapsulating all other param structs +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct Neural_THX_Encoder_Params +{ +#ifdef USING_725 + Neural_THX_725_Encode_Struct t725; +#endif +#ifdef USING_522 + Neural_THX_522_Encode_Struct t522; +#endif +#ifdef USING_722 + Neural_THX_722_Encode_Struct t722; +#endif + + void * pParams; + Neural_THX_Encoder_Params() + { + pParams = NULL; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : Init + // + // Params : int - Channel Configuration to know which structure to use + // + // Return : void + // + // Purpose : Initialize the pointer needed by this encoder :) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Init(int nChanConfig) + { + // see which configuration we have :) + if(nChanConfig == NEURAL_THX_7_5_GAMING) + { +#ifdef USING_725 + pParams = &t725; +#endif + } + else if(nChanConfig == NEURAL_THX_5_2_GAMING) + { +#ifdef USING_522 + pParams = &t522; +#endif + } + else if(nChanConfig == NEURAL_THX_7_2_GAMING) + { +#ifdef USING_722 + pParams = &t722; +#endif + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : Release + // + // Params : void + // + // Return : void + // + // Purpose : Releases memory if any has been allocated through the init + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Release(void) + { + pParams = NULL; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : ValidStruct + // + // Params : void + // + // Return : bool - whether any of the pointers are valid or not + // + // Purpose : Checks to see if the init was successful + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ValidStruct(void) + { + if(pParams == NULL) + { + return false; + } + // return yes + return true; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Neural_THX_Encoder_Settings +// +// Created On : 08/20/2007 +// +// Last Modified : 02/28/2008 +// +// Description : Settings structure for the Neural-THX encoder +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct Neural_THX_Encoder_Settings +{ + // The Number of audio samples in each mono channel buffers passed to the + // encoder + // NOTE: The encoder currently only supports an audio frame size of NEURAL_FRAMESIZE + unsigned int nFramesize; + // A configuration value indicating that the encoder is to perform a Neural-THX + // compatible encoding. + unsigned int nChanConfig; + // The sample rate of the input/output audio. + // NOTE: The encoder currently supports sample rates of 32 kHz, 44.1 kHz, and + // 48 kHz. + unsigned int nSampleRate; + // Whether to use Final Limiting or not + // NOTE: Used in 522 & 722 encoder + THX_bool bUseFinalLimiting; + // LFE Cutoff when converting to 2 channels + // NOTE: Used in 522 & 722 encoder + float fLFECutOff; + + // Default Constructor Set to defaults + Neural_THX_Encoder_Settings() + { + nFramesize = NEURAL_FRAMESIZE; + nChanConfig = NEURAL_THX_7_5_GAMING; + nSampleRate = SAMPLERATE_44_1; + bUseFinalLimiting = true; + fLFECutOff = 0.0f; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Neural_THX_Channel_Format +// +// Created On : 08/20/2007 +// +// Last Modified : 02/28/2008 +// +// Description : Surround Setup structure +// +// m_fL[n] equals 1 Sample point for the channel +// while variable_name[n] =s a sample frame +// NOTE: The encoder uses 32 bit max for sample points +// which requires the user to compensate based on +// the bytes each sample point takes +// (i.e. bitsPerSample / 8 = nBytesPerSample rounded up) +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct Neural_THX_Channel_Format // double check if we need to support the framesize +{ + // Front + float m_fL[NEURAL_FRAMESIZE]; + float m_fC[NEURAL_FRAMESIZE]; + float m_fR[NEURAL_FRAMESIZE]; + // Sides/Surround & LFE + float m_fLs[NEURAL_FRAMESIZE]; + float m_fRs[NEURAL_FRAMESIZE]; + float m_fLFE[NEURAL_FRAMESIZE]; + // Back + float m_fLb[NEURAL_FRAMESIZE]; + float m_fRb[NEURAL_FRAMESIZE]; + + // Constructor set everything to NULL + Neural_THX_Channel_Format() + { + // Fill with silence + // CPS - FMOD_memset(this->m_fL,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fC,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fR,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fLs,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fRs,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fLFE,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fLb,0,sizeof(float) * NEURAL_FRAMESIZE); + // CPS - FMOD_memset(this->m_fRb,0,sizeof(float) * NEURAL_FRAMESIZE); + + //ZeroMemory(this->m_fL, sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fC, sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fR, sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fLs, sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fRs, sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fLFE,sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fLb, sizeof(float) * NEURAL_FRAMESIZE); + //ZeroMemory(this->m_fRb, sizeof(float) * NEURAL_FRAMESIZE); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + //// Name : Convert + //// + //// Params : void * - Source of channels + //// + //// Return : void + //// + //// Purpose : Used to provide conversion support on any platform + //// * converts any source into this format for use by + //// the Neural-THX encoder + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + //void Convert(void *pSource) + //{ + // // TODO : Use this function to transfer your channels into this + // // structure which will later be passed into the encoder + //} + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : Initialize + // + // Params : void * - Source of channels + // + // Return : void + // + // Purpose : Used to set all channels in one easy to use function + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + //void Initialize(float *fL = NULL, + // float *fC = NULL, + // float *fR = NULL, + // float *fLs = NULL, + // float *fRs = NULL, + // float *fLFE = NULL, + // float *fLb = NULL, + // float *fRb = NULL) + //{ + // // Copy over the information into the structure's variables + // if(fL) + // FMOD_memcpy(this->m_fL,fL,sizeof(float) * NEURAL_FRAMESIZE); + // if(fC) + // FMOD_memcpy(this->m_fC,fC,sizeof(float) * NEURAL_FRAMESIZE); + // if(fR) + // FMOD_memcpy(this->m_fR,fR,sizeof(float) * NEURAL_FRAMESIZE); + // if(fLs) + // FMOD_memcpy(this->m_fLs,fLs,sizeof(float) * NEURAL_FRAMESIZE); + // if(fRs) + // FMOD_memcpy(this->m_fRs,fRs,sizeof(float) * NEURAL_FRAMESIZE); + // if(fLFE) + // FMOD_memcpy(this->m_fLFE,fLFE,sizeof(float) * NEURAL_FRAMESIZE); + // if(fLb) + // FMOD_memcpy(this->m_fLb,fLb,sizeof(float) * NEURAL_FRAMESIZE); + // if(fRb) + // FMOD_memcpy(this->m_fRb,fRb,sizeof(float) * NEURAL_FRAMESIZE); + //} +}; + + +#endif // __NEURAL_THX_GLOBAL__ + diff --git a/lib/neural_thx/Neural_THX_Interface.cpp b/lib/neural_thx/Neural_THX_Interface.cpp new file mode 100755 index 0000000..0b615f3 --- /dev/null +++ b/lib/neural_thx/Neural_THX_Interface.cpp @@ -0,0 +1,261 @@ + +#include "Neural_THX_Interface.h" + + +NEURAL_THX_ENCODER::NEURAL_THX_ENCODER(void) +{ +} + +NEURAL_THX_ENCODER::~NEURAL_THX_ENCODER(void) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Init +// +// Params : Neural_THX_Encoder_Settings - settings structure defining +// global settings of encoder +// +// Return : int - Neural-THX error code +// +// Purpose : Used to set all channels in one easy to use function +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +int NEURAL_THX_ENCODER::Init( Neural_THX_Encoder_Settings &tSettings, Neural_THX_Encoder_Params &tParams ) +{ + // Get prepared to catch errors + int nErrorHandle = NRLTHX_OK; + + // Make sure the settings they passed us are valid + + // Check Configuration + if( tSettings.nChanConfig != NEURAL_THX_7_5_GAMING && + tSettings.nChanConfig != NEURAL_THX_6_5_GAMING && + tSettings.nChanConfig != NEURAL_THX_7_2_GAMING && + tSettings.nChanConfig != NEURAL_THX_5_2_GAMING) + { + return UNSUPPORTED_CHANCONFIG; + } + // Check Samplerate + if( tSettings.nSampleRate != SAMPLERATE_32_0 && + tSettings.nSampleRate != SAMPLERATE_44_1 && + tSettings.nSampleRate != SAMPLERATE_48_0 ) + { + return UNSUPPORTED_SAMPLERATE; + } + // Check Framesize + if( tSettings.nFramesize != NEURAL_FRAMESIZE) + { + return UNSUPPORTED_FRAMESIZE; + } + // Double check if we have a valid structure now + if(!tParams.ValidStruct()) + { + return UNSUPPORTED_PARAMETER; + } + + // Branch for Initialization of encoder that the user wants + switch(tSettings.nChanConfig) + { + case NEURAL_THX_7_5_GAMING: + { + // The 7 to 5 Encoder Init + nErrorHandle = Neural_THX_725_Encode_INIT( tSettings.nFramesize, + tSettings.nChanConfig, + tSettings.nSampleRate, + (Neural_THX_725_Encode_Struct *)(tParams.pParams)); + } + break; + case NEURAL_THX_5_2_GAMING: + { + // The 5 To 2 Encoder Init + nErrorHandle = Neural_THX_522_Encode_INIT( tSettings.nFramesize, + tSettings.nChanConfig, + tSettings.nSampleRate, + (Neural_THX_522_Encode_Struct *)(tParams.pParams)); + } + break; + case NEURAL_THX_6_5_GAMING: // !Supported Yet + { + // Temporary until it gets supported + return UNSUPPORTED_CHANCONFIG; + } + break; + case NEURAL_THX_7_2_GAMING: // !Supported Yet + { + nErrorHandle = Neural_THX_722_Encode_INIT( tSettings.nFramesize, + tSettings.nChanConfig, + tSettings.nSampleRate, + (Neural_THX_722_Encode_Struct *)(tParams.pParams)); + } + break; + + default: + { + return UNKNOWN_ERROR;// should never reach this ... should :) + } + break; + }; + // Double check if there was an error :) hopefully not + NRLTHX_ERRORCHECK(nErrorHandle); + // return the success (if it reaches here things went well most likely) + return nErrorHandle; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Encode +// +// Params : Neural_THX_Channel_Format - Channels in to down mix +// Neural_THX_Channel_Format - Channels out (results) +// Neural_THX_Encoder_Settings - settings structure defining +// global settings of encoder +// +// Return : int - Neural-THX error code +// +// Purpose : Used to pass the information to the right encoder +// packaged in the library +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +int NEURAL_THX_ENCODER::Encode( Neural_THX_Channel_Format &tChannelsIn, + Neural_THX_Channel_Format &tChannelsOut, + Neural_THX_Encoder_Settings &tSettings, + Neural_THX_Encoder_Params &tParams) +{ + // Get prepared to catch errors + int nErrorHandle = NRLTHX_OK; + // Make sure the settings they passed us are valid + + // Check Configuration + if( tSettings.nChanConfig != NEURAL_THX_7_5_GAMING && + tSettings.nChanConfig != NEURAL_THX_6_5_GAMING && + tSettings.nChanConfig != NEURAL_THX_7_2_GAMING && + tSettings.nChanConfig != NEURAL_THX_5_2_GAMING) + { + return UNSUPPORTED_CHANCONFIG; + } + // Check Samplerate + if( tSettings.nSampleRate != SAMPLERATE_32_0 && + tSettings.nSampleRate != SAMPLERATE_44_1 && + tSettings.nSampleRate != SAMPLERATE_48_0 ) + { + return UNSUPPORTED_SAMPLERATE; + } + // Check Framesize + if( tSettings.nFramesize != NEURAL_FRAMESIZE) + { + return UNSUPPORTED_FRAMESIZE; + } + // Check to see if we have a valid buffer + if(!tParams.ValidStruct()) + { + return UNSUPPORTED_PARAMETER; + } + + // Prepare the channels to be sent to the corresponding encoder + // NOTE: This section will be used if we support interleaved buffers + + // Branch - figure out which encoder the user wants by the settings struct + // Send off the information to the right encoder and let it + // spit back out the results the user wants + switch(tSettings.nChanConfig) + { + case NEURAL_THX_7_5_GAMING: + { + // The 7 To 5 Encoder + nErrorHandle = Neural_THX_725_Encode( tChannelsIn.m_fL, + tChannelsIn.m_fR, + tChannelsIn.m_fC, + tChannelsIn.m_fLFE, + tChannelsIn.m_fLs, + tChannelsIn.m_fRs, + tChannelsIn.m_fLb, + tChannelsIn.m_fRb, + tChannelsOut.m_fL, + tChannelsOut.m_fR, + tChannelsOut.m_fC, + tChannelsOut.m_fLFE, + tChannelsOut.m_fLs, + tChannelsOut.m_fRs, + tSettings.bUseFinalLimiting, + tSettings.nFramesize, + tSettings.nChanConfig, + tSettings.nSampleRate, + (Neural_THX_725_Encode_Struct *)(tParams.pParams)); + } + break; + case NEURAL_THX_5_2_GAMING: + { + // The 5 To 2 Encoder :) + nErrorHandle = Neural_THX_522_Encode( tChannelsIn.m_fL, + tChannelsIn.m_fR, + tChannelsIn.m_fC, + tChannelsIn.m_fLFE, + tChannelsIn.m_fLs, + tChannelsIn.m_fRs, + tChannelsOut.m_fL, + tChannelsOut.m_fR, + tSettings.bUseFinalLimiting, + tSettings.fLFECutOff, + tSettings.nFramesize, + tSettings.nChanConfig, + tSettings.nSampleRate, + (Neural_THX_522_Encode_Struct *)(tParams.pParams)); + } + break; + case NEURAL_THX_6_5_GAMING: // !Supported Yet + { + // Temporary until it gets supported + return UNSUPPORTED_CHANCONFIG; + } + break; + case NEURAL_THX_7_2_GAMING: // !Supported Yet + { + nErrorHandle = Neural_THX_722_Encode( tChannelsIn.m_fL, + tChannelsIn.m_fR, + tChannelsIn.m_fC, + tChannelsIn.m_fLFE, + tChannelsIn.m_fLs, + tChannelsIn.m_fRs, + tChannelsIn.m_fLb, + tChannelsIn.m_fRb, + tChannelsOut.m_fL, + tChannelsOut.m_fR, + tSettings.bUseFinalLimiting, + tSettings.fLFECutOff, + tSettings.nFramesize, + tSettings.nChanConfig, + tSettings.nSampleRate, + (Neural_THX_722_Encode_Struct *)(tParams.pParams)); + } + break; + + default: + { + return UNKNOWN_ERROR;// should never reach this ... should :) + } + break; + }; + + + // Double check if there was an error :) hopefully not + NRLTHX_ERRORCHECK(nErrorHandle); + // return the errors + return nErrorHandle; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Shutdown +// +// Params : void +// +// Return : int - Neural-THX error code +// +// Purpose : Used to unitialize structures and release any memory needed +// by the encoder +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +int NEURAL_THX_ENCODER::Shutdown(void) +{ + // Get prepared to catch errors + int nErrorHandle = NRLTHX_OK; + + // Return the errors + return nErrorHandle; +} diff --git a/lib/neural_thx/Neural_THX_Interface.h b/lib/neural_thx/Neural_THX_Interface.h new file mode 100755 index 0000000..71157c3 --- /dev/null +++ b/lib/neural_thx/Neural_THX_Interface.h @@ -0,0 +1,128 @@ +////////////////////////////////////////////////////////////////////// +// ___________________ // +// | // +// | | | \ / // +// | | | \ / // +// | |----| / // +// | | | / \ // +// | | | / \ // +// ___________________ // +// // +// Neural-THX (R) Surround Technology // +// // +// Copyright (c) 2008 THX Ltd. // +// // +// THX is a trademark of THX Ltd., which may be registered // +// in some jurisdictions. // +// All Rights Reserved. // +// // +// THX Confidential Information // +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : Neural_THX_Interface.h +// +// Author(s) : Mark Gugler (mgugler@thx.com) +// +// Created On : 08/20/2007 +// +// Last Modified : 03/03/2008 +// +// Version : 1.61 +// +// References : Linked to Neural_THX_Encoder.h & .lib +// +// Description : Interfaces the Encoder packaged in the library +// +// Revision History : 08/20/2007 Build basic framework and comment +// tasks as best as possible +// 08/21/2007 More comments and changes of format +// 08/22/2007 Library attempts to fix linking +// 08/23/2007 Skipping libray for now, filling in +// functionality of the functions below. +// Also added in a shutdown to release memory +// 08/28/2007 Adapt the structure of allocating memory +// to developer's feedback +// 09/04/2007 Update comments and prepare for 722 +// encoder integration +// 02/15/2008 Got 722 encoder so integrating and testing +// 02/26/2008 Making interface compatible with new param +// structure as we stripped out memory allocations +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef __NEURAL_THX_ENCODER__ +#define __NEURAL_THX_ENCODER__ + +#include "Neural_THX_Encoders.h" +#include "Neural_THX_Global.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define NRLTHX_FAILED(error) (error == NRLTHX_OK || error == NRLSUR_OK ? false : true) +#define NRLTHX_ERRORCHECK(error) if(NRLTHX_FAILED(error)){return error;} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Name : NEURAL_THX_ENCODER +// +// Created On : 08/20/2007 +// +// Last Modified : 08/23/2007 +// +// Description : Interface class used to encapsulate all the +// Neural-THX encoders into one object +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +class NEURAL_THX_ENCODER +{ +public: + NEURAL_THX_ENCODER(void); + virtual ~NEURAL_THX_ENCODER(void); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : Init + // + // Params : Neural_THX_Encoder_Settings - settings structure defining + // global settings of encoder + // Neural_THX_Encoder_Params - Parameters structure used by + // the encoder + // + // Return : int - Neural-THX error code + // + // Purpose : Used to initialize any needs by the encoder + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Init(Neural_THX_Encoder_Settings &tSettings,Neural_THX_Encoder_Params &tParams); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : Encode + // + // Params : Neural_THX_Channel_Format - Channels in to down mix + // Neural_THX_Channel_Format - Channels out (results) + // Neural_THX_Encoder_Settings - settings structure defining + // global settings of encoder + // Neural_THX_Encoder_Params - Parameters structure used by + // the encoder + // + // Return : int - Neural-THX error code + // + // Purpose : Used to pass the information to the right encoder + // packaged in the library + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Encode( Neural_THX_Channel_Format &tChannelsIn, + Neural_THX_Channel_Format &tChannelsOut, + Neural_THX_Encoder_Settings &tSettings, + Neural_THX_Encoder_Params &tParams); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Name : Shutdown + // + // Params : void + // + // Return : int - Neural-THX error code + // + // Purpose : Used to unitialize structures and release any memory needed + // by the encoder + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + int Shutdown(void); +}; + + + +#endif //__NEURAL_THX_ENCODER__ + diff --git a/lib/neural_thx/PeakConverter.c b/lib/neural_thx/PeakConverter.c new file mode 100755 index 0000000..21b086f --- /dev/null +++ b/lib/neural_thx/PeakConverter.c @@ -0,0 +1,114 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Audio *// +//* File : PeakConverter.c *// +//* Description : Peak converter with rise time / fall time *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2008 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include + +//***************************************************************************// +//* PeakConverter_INIT *// +//***************************************************************************// +int PeakConverter_INIT(float RiseTime, //in msec + float FallTime, //in msec + float MinValue, + float MaxValue, + unsigned int SampleRate, + PeakConverter_Struct * pParams) +{ + if(SampleRate == 0) + return UNSUPPORTED_SAMPLERATE; + if(MaxValue-MinValue <= 0.0f) + return UNSUPPORTED_PARAMETER; + + if(RiseTime > 0) + pParams->RiseSlew = (MaxValue-MinValue) / (RiseTime * 0.001f * (float)SampleRate); + else + pParams->RiseSlew = (MaxValue-MinValue); + pParams->RiseTime = RiseTime; + + if(FallTime > 0) + pParams->FallSlew = -(MaxValue-MinValue) / (FallTime * 0.001f * (float)SampleRate); + else + pParams->FallSlew = -(MaxValue-MinValue); + pParams->FallTime = FallTime; + + pParams->MinValue = MinValue; + pParams->MaxValue = MaxValue; + pParams->PrevValue = 0.0f; + pParams->SampleRate = SampleRate; + + return NRLSUR_OK; +} + +//***************************************************************************// +//* PeakConverter(...) *// +//***************************************************************************// +int PeakConverter(float * PtrIn, + float * PtrOut, + float RiseTime, //in msec + float FallTime, //in msec + float MinValue, + float MaxValue, + unsigned int Framesize, + unsigned int SampleRate, + PeakConverter_Struct * pParams) +{ + int n; + int RetVal; + float fTemp; + + if(SampleRate == 0) + return UNSUPPORTED_SAMPLERATE; + if(RiseTime < 0.0f) + RiseTime = 0.0f; + if(RiseTime > 5000.0f) + RiseTime = 5000.0f; + if(FallTime < 0.0f) + FallTime = 0.0f; + if(FallTime > 5000.0f) + FallTime = 5000.0f; + + //If parameters have changed, then re-initialize + if( RiseTime != pParams->RiseTime || + FallTime != pParams->FallTime || + MinValue != pParams->MinValue || + MaxValue != pParams->MaxValue || + SampleRate != pParams->SampleRate ) { + RetVal = PeakConverter_INIT(RiseTime,FallTime,MinValue,MaxValue,SampleRate,pParams); + if(RetVal != NRLSUR_OK) + return RetVal; + } + + //-----------------------------------------------------------// + // Perform peak converter processing // + //-----------------------------------------------------------// + for(n = 0; n < (int)Framesize; n++){ + fTemp = PtrIn[n] - pParams->PrevValue; + if(fTemp > pParams->RiseSlew) + fTemp = pParams->RiseSlew; + if(fTemp < pParams->FallSlew) + fTemp = pParams->FallSlew; + PtrOut[n] = pParams->PrevValue + fTemp; + pParams->PrevValue = PtrOut[n]; + } + + return NRLSUR_OK; +} diff --git a/lib/neural_thx/SineWin_512.c b/lib/neural_thx/SineWin_512.c new file mode 100755 index 0000000..14b0d81 --- /dev/null +++ b/lib/neural_thx/SineWin_512.c @@ -0,0 +1,5 @@ +#include "Neural_THX_Encoders.h" + +float * GetSineWindow(){ + return (float*)SineWindow; +} diff --git a/lib/neural_thx/THX__kiss_fft_guts.h b/lib/neural_thx/THX__kiss_fft_guts.h new file mode 100755 index 0000000..1405d0b --- /dev/null +++ b/lib/neural_thx/THX__kiss_fft_guts.h @@ -0,0 +1,150 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* kiss_fft.h + defines kiss_fft_scalar as either short or a float type + and defines + typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ +#include "THX_kiss_fft.h" +#include + +#define MAXFACTORS 32 +/* e.g. an fft of length 128 has 4 factors + as far as kissfft is concerned + 4*4*4*2 + */ + +struct THX_kiss_fft_state{ + int nfft; + int inverse; + int factors[2*MAXFACTORS]; + THX_kiss_fft_cpx twiddles[1]; +}; + +/* + Explanation of macros dealing with complex math: + + C_MUL(m,a,b) : m = a*b + C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise + C_SUB( res, a,b) : res = a - b + C_SUBFROM( res , a) : res -= a + C_ADDTO( res , a) : res += a + * */ +#ifdef FIXED_POINT +#if (FIXED_POINT==32) +# define FRACBITS 31 +# define SAMPPROD int64_t +#define SAMP_MAX 2147483647 +#else +# define FRACBITS 15 +# define SAMPPROD int32_t +#define SAMP_MAX 32767 +#endif + +#define SAMP_MIN -SAMP_MAX + +#if defined(CHECK_OVERFLOW) +# define CHECK_OVERFLOW_OP(a,op,b) \ + if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ + fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } +#endif + + +# define smul(a,b) ( (SAMPPROD)(a)*(b) ) +# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) + +# define S_MUL(a,b) sround( smul(a,b) ) + +# define C_MUL(m,a,b) \ + do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ + (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) + +# define DIVSCALAR(x,k) \ + (x) = sround( smul( x, SAMP_MAX/k ) ) + +# define C_FIXDIV(c,div) \ + do { DIVSCALAR( (c).r , div); \ + DIVSCALAR( (c).i , div); }while (0) + +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r = sround( smul( (c).r , s ) ) ;\ + (c).i = sround( smul( (c).i , s ) ) ; }while(0) + +#else /* not FIXED_POINT*/ + +# define S_MUL(a,b) ( (a)*(b) ) +#define C_MUL(m,a,b) \ + do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ + (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) +# define C_FIXDIV(c,div) /* NOOP */ +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r *= (s);\ + (c).i *= (s); }while(0) +#endif + +#ifndef CHECK_OVERFLOW_OP +# define CHECK_OVERFLOW_OP(a,op,b) /* noop */ +#endif + +#define C_ADD( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,+,(b).r)\ + CHECK_OVERFLOW_OP((a).i,+,(b).i)\ + (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ + }while(0) +#define C_SUB( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,-,(b).r)\ + CHECK_OVERFLOW_OP((a).i,-,(b).i)\ + (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ + }while(0) +#define C_ADDTO( res , a)\ + do { \ + CHECK_OVERFLOW_OP((res).r,+,(a).r)\ + CHECK_OVERFLOW_OP((res).i,+,(a).i)\ + (res).r += (a).r; (res).i += (a).i;\ + }while(0) + +#define C_SUBFROM( res , a)\ + do {\ + CHECK_OVERFLOW_OP((res).r,-,(a).r)\ + CHECK_OVERFLOW_OP((res).i,-,(a).i)\ + (res).r -= (a).r; (res).i -= (a).i; \ + }while(0) + + +#ifdef FIXED_POINT +# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase)) +# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase)) +# define HALF_OF(x) ((x)>>1) +#elif defined(USE_SIMD) +# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) +# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) +# define HALF_OF(x) ((x)*_mm_set1_ps(.5)) +#else +# define KISS_FFT_COS(phase) (THX_kiss_fft_scalar) cos(phase) +# define KISS_FFT_SIN(phase) (THX_kiss_fft_scalar) sin(phase) +# define HALF_OF(x) ((x)*.5) +#endif + +#define kf_cexp(x,phase) \ + do{ \ + (x)->r = KISS_FFT_COS(phase);\ + (x)->i = KISS_FFT_SIN(phase);\ + }while(0) + + +/* a debugging function */ +#define pcpx(c)\ + fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) diff --git a/lib/neural_thx/THX_kiss_fft.c b/lib/neural_thx/THX_kiss_fft.c new file mode 100755 index 0000000..8d7a555 --- /dev/null +++ b/lib/neural_thx/THX_kiss_fft.c @@ -0,0 +1,401 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "THX__kiss_fft_guts.h" +/* The guts header contains all the multiplication and addition macros that are defined for + fixed or floating point complex numbers. It also delares the kf_ internal functions. + */ + +//static THX_kiss_fft_cpx *scratchbuf=NULL; +//static size_t nscratchbuf=0; +static THX_kiss_fft_cpx scratchbuf[4]; +static size_t nscratchbuf=4; +//static THX_kiss_fft_cpx *tmpbuf=NULL; +//static size_t ntmpbuf=0; + +//#define CHECKBUF(buf,nbuf,n) \ +// do { \ +// if ( nbuf < (size_t)(n) ) {\ +// free(buf); \ +// buf = (THX_kiss_fft_cpx*)THX_KISS_FFT_MALLOC(sizeof(THX_kiss_fft_cpx)*(n)); \ +// nbuf = (size_t)(n); \ +// } \ +// }while(0) + + +static void THX_kf_bfly2( + THX_kiss_fft_cpx * Fout, + const size_t fstride, + const THX_kiss_fft_cfg st, + int m + ) +{ + THX_kiss_fft_cpx * Fout2; + THX_kiss_fft_cpx * tw1 = st->twiddles; + THX_kiss_fft_cpx t; + Fout2 = Fout + m; + do{ + C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); + + C_MUL (t, *Fout2 , *tw1); + tw1 += fstride; + C_SUB( *Fout2 , *Fout , t ); + C_ADDTO( *Fout , t ); + ++Fout2; + ++Fout; + }while (--m); +} + +static void THX_kf_bfly4( + THX_kiss_fft_cpx * Fout, + const size_t fstride, + const THX_kiss_fft_cfg st, + const size_t m + ) +{ + THX_kiss_fft_cpx *tw1,*tw2,*tw3; + THX_kiss_fft_cpx scratch[6]; + size_t k=m; + const size_t m2=2*m; + const size_t m3=3*m; + + tw3 = tw2 = tw1 = st->twiddles; + + do { + C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); + + C_MUL(scratch[0],Fout[m] , *tw1 ); + C_MUL(scratch[1],Fout[m2] , *tw2 ); + C_MUL(scratch[2],Fout[m3] , *tw3 ); + + C_SUB( scratch[5] , *Fout, scratch[1] ); + C_ADDTO(*Fout, scratch[1]); + C_ADD( scratch[3] , scratch[0] , scratch[2] ); + C_SUB( scratch[4] , scratch[0] , scratch[2] ); + C_SUB( Fout[m2], *Fout, scratch[3] ); + tw1 += fstride; + tw2 += fstride*2; + tw3 += fstride*3; + C_ADDTO( *Fout , scratch[3] ); + + if(st->inverse) { + Fout[m].r = scratch[5].r - scratch[4].i; + Fout[m].i = scratch[5].i + scratch[4].r; + Fout[m3].r = scratch[5].r + scratch[4].i; + Fout[m3].i = scratch[5].i - scratch[4].r; + }else{ + Fout[m].r = scratch[5].r + scratch[4].i; + Fout[m].i = scratch[5].i - scratch[4].r; + Fout[m3].r = scratch[5].r - scratch[4].i; + Fout[m3].i = scratch[5].i + scratch[4].r; + } + ++Fout; + }while(--k); +} + +static void THX_kf_bfly3( + THX_kiss_fft_cpx * Fout, + const size_t fstride, + const THX_kiss_fft_cfg st, + size_t m + ) +{ + size_t k=m; + const size_t m2 = 2*m; + THX_kiss_fft_cpx *tw1,*tw2; + THX_kiss_fft_cpx scratch[5]; + THX_kiss_fft_cpx epi3; + epi3 = st->twiddles[fstride*m]; + + tw1=tw2=st->twiddles; + + do{ + C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); + + C_MUL(scratch[1],Fout[m] , *tw1); + C_MUL(scratch[2],Fout[m2] , *tw2); + + C_ADD(scratch[3],scratch[1],scratch[2]); + C_SUB(scratch[0],scratch[1],scratch[2]); + tw1 += fstride; + tw2 += fstride*2; + + Fout[m].r = (float)(Fout->r - HALF_OF(scratch[3].r)); + Fout[m].i = (float)(Fout->i - HALF_OF(scratch[3].i)); + + C_MULBYSCALAR( scratch[0] , epi3.i ); + + C_ADDTO(*Fout,scratch[3]); + + Fout[m2].r = Fout[m].r + scratch[0].i; + Fout[m2].i = Fout[m].i - scratch[0].r; + + Fout[m].r -= scratch[0].i; + Fout[m].i += scratch[0].r; + + ++Fout; + }while(--k); +} + +static void THX_kf_bfly5( + THX_kiss_fft_cpx * Fout, + const size_t fstride, + const THX_kiss_fft_cfg st, + int m + ) +{ + THX_kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; + int u; + THX_kiss_fft_cpx scratch[13]; + THX_kiss_fft_cpx * twiddles = st->twiddles; + THX_kiss_fft_cpx *tw; + THX_kiss_fft_cpx ya,yb; + ya = twiddles[fstride*m]; + yb = twiddles[fstride*2*m]; + + Fout0=Fout; + Fout1=Fout0+m; + Fout2=Fout0+2*m; + Fout3=Fout0+3*m; + Fout4=Fout0+4*m; + + tw=st->twiddles; + for ( u=0; ur += scratch[7].r + scratch[8].r; + Fout0->i += scratch[7].i + scratch[8].i; + + scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); + scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); + + scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); + scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); + + C_SUB(*Fout1,scratch[5],scratch[6]); + C_ADD(*Fout4,scratch[5],scratch[6]); + + scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); + scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); + scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); + scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); + + C_ADD(*Fout2,scratch[11],scratch[12]); + C_SUB(*Fout3,scratch[11],scratch[12]); + + ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; + } +} + +/* perform the butterfly for one stage of a mixed radix FFT */ +static void THX_kf_bfly_generic( + THX_kiss_fft_cpx * Fout, + const size_t fstride, + const THX_kiss_fft_cfg st, + int m, + int p + ) +{ + int u,k,q1,q; + THX_kiss_fft_cpx * twiddles = st->twiddles; + THX_kiss_fft_cpx t; + int Norig = st->nfft; + + //CHECKBUF(scratchbuf,nscratchbuf,p); + + for ( u=0; u=Norig) twidx-=Norig; + C_MUL(t,scratchbuf[q] , twiddles[twidx] ); + C_ADDTO( Fout[ k ] ,t); + } + k += m; + } + } +} + +static +void THX_kf_work( + THX_kiss_fft_cpx * Fout, + const THX_kiss_fft_cpx * f, + const size_t fstride, + int in_stride, + int * factors, + const THX_kiss_fft_cfg st + ) +{ + THX_kiss_fft_cpx * Fout_beg=Fout; + const int p=*factors++; /* the radix */ + const int m=*factors++; /* stage's fft length/p */ + const THX_kiss_fft_cpx * Fout_end = Fout + p*m; + + if (m==1) { + do{ + *Fout = *f; + f += fstride*in_stride; + }while(++Fout != Fout_end ); + }else{ + do{ + THX_kf_work( Fout , f, fstride*p, in_stride, factors,st); + f += fstride*in_stride; + }while( (Fout += m) != Fout_end ); + } + + Fout=Fout_beg; + + switch (p) { + case 2: THX_kf_bfly2(Fout,fstride,st,m); break; + case 3: THX_kf_bfly3(Fout,fstride,st,m); break; + case 4: THX_kf_bfly4(Fout,fstride,st,m); break; + case 5: THX_kf_bfly5(Fout,fstride,st,m); break; + default: THX_kf_bfly_generic(Fout,fstride,st,m,p); break; + } +} + +/* facbuf is populated by p1,m1,p2,m2, ... + where + p[i] * m[i] = m[i-1] + m0 = n */ +static +void THX_kf_factor(int n,int * facbuf) +{ + int p=4; + double floor_sqrt; + floor_sqrt = floor( sqrt((double)n) ); + + /*factor out powers of 4, powers of 2, then any remaining primes */ + do { + while (n % p) { + switch (p) { + case 4: p = 2; break; + case 2: p = 3; break; + default: p += 2; break; + } + if (p > floor_sqrt) + p = n; /* no more factors, skip to end */ + } + n /= p; + *facbuf++ = p; + *facbuf++ = n; + } while (n > 1); +} + +/* + * + * User-callable function to allocate all necessary storage space for the fft. + * + * The return value is a contiguous block of memory, allocated with malloc. As such, + * It can be freed with free(), rather than a kiss_fft-specific function. + * */ +THX_kiss_fft_cfg THX_kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) +{ + THX_kiss_fft_cfg st=NULL; + size_t memneeded = sizeof(struct THX_kiss_fft_state) + + sizeof(THX_kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ + + //if ( lenmem==NULL ) { + // st = ( THX_kiss_fft_cfg)THX_KISS_FFT_MALLOC( memneeded ); + //}else{ + if (mem != NULL && *lenmem >= memneeded) + st = (THX_kiss_fft_cfg)mem; + *lenmem = memneeded; + //} + if (st) { + int i; + st->nfft=nfft; + st->inverse = inverse_fft; + + for (i=0;iinverse) + phase *= -1; + kf_cexp(st->twiddles+i, phase ); + } + + THX_kf_factor(nfft,st->factors); + } + return st; +} + + + + +void THX_kiss_fft_stride(THX_kiss_fft_cfg st,const THX_kiss_fft_cpx *fin,THX_kiss_fft_cpx *fout,int in_stride) +{ + //if (fin == fout) { + // CHECKBUF(tmpbuf,ntmpbuf,st->nfft); + // THX_kf_work(tmpbuf,fin,1,in_stride, st->factors,st); + // FMOD_memcpy(fout,tmpbuf,sizeof(THX_kiss_fft_cpx)*st->nfft); + //}else{ + THX_kf_work( fout, fin, 1,in_stride, st->factors,st ); + //} +} + +void THX_kiss_fft(THX_kiss_fft_cfg cfg,const THX_kiss_fft_cpx *fin,THX_kiss_fft_cpx *fout) +{ + THX_kiss_fft_stride(cfg,fin,fout,1); +} + + +/* not really necessary to call, but if someone is doing in-place ffts, they may want to free the + buffers from CHECKBUF + */ +void THX_kiss_fft_cleanup(void) +{ + //free(scratchbuf); + //scratchbuf = NULL; + //nscratchbuf=0; + //free(tmpbuf); + //tmpbuf=NULL; + //ntmpbuf=0; +} + +int THX_kiss_fft_next_fast_size(int n) +{ + while(1) { + int m=n; + while ( (m%2) == 0 ) m/=2; + while ( (m%3) == 0 ) m/=3; + while ( (m%5) == 0 ) m/=5; + if (m<=1) + break; /* n is completely factorable by twos, threes, and fives */ + n++; + } + return n; +} diff --git a/lib/neural_thx/THX_kiss_fft.h b/lib/neural_thx/THX_kiss_fft.h new file mode 100755 index 0000000..4479717 --- /dev/null +++ b/lib/neural_thx/THX_kiss_fft.h @@ -0,0 +1,119 @@ +#ifndef THX_KISS_FFT_H +#define THX_KISS_FFT_H + +#include +#include +#include +//#include +//#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ATTENTION! + If you would like a : + -- a utility that will handle the caching of fft objects + -- real-only (no imaginary time component ) FFT + -- a multi-dimensional FFT + -- a command-line utility to perform ffts + -- a command-line utility to perform fast-convolution filtering + + Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c + in the tools/ directory. +*/ + +//#ifdef USE_SIMD +//# include +//# define THX_kiss_fft_scalar __m128 +//#define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes) +//#else +//#define THX_KISS_FFT_MALLOC malloc +//#endif + + +#ifdef FIXED_POINT +#include +# if (FIXED_POINT == 32) +# define kiss_fft_scalar int32_t +# else +# define kiss_fft_scalar int16_t +# endif +#else +# ifndef THX_kiss_fft_scalar +/* default is float */ +# define THX_kiss_fft_scalar float +# endif +#endif + +typedef struct { + THX_kiss_fft_scalar r; + THX_kiss_fft_scalar i; +}THX_kiss_fft_cpx; + +typedef struct THX_kiss_fft_state* THX_kiss_fft_cfg; + +/* + * kiss_fft_alloc + * + * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. + * + * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); + * + * The return value from fft_alloc is a cfg buffer used internally + * by the fft routine or NULL. + * + * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. + * The returned value should be free()d when done to avoid memory leaks. + * + * The state can be placed in a user supplied buffer 'mem': + * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, + * then the function places the cfg in mem and the size used in *lenmem + * and returns mem. + * + * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), + * then the function returns NULL and places the minimum cfg + * buffer size in *lenmem. + * */ + +THX_kiss_fft_cfg THX_kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); + +/* + * kiss_fft(cfg,in_out_buf) + * + * Perform an FFT on a complex input buffer. + * for a forward FFT, + * fin should be f[0] , f[1] , ... ,f[nfft-1] + * fout will be F[0] , F[1] , ... ,F[nfft-1] + * Note that each element is complex and can be accessed like + f[k].r and f[k].i + * */ +void THX_kiss_fft(THX_kiss_fft_cfg cfg,const THX_kiss_fft_cpx *fin,THX_kiss_fft_cpx *fout); + +/* + A more generic version of the above function. It reads its input from every Nth sample. + * */ +void THX_kiss_fft_stride(THX_kiss_fft_cfg cfg,const THX_kiss_fft_cpx *fin,THX_kiss_fft_cpx *fout,int fin_stride); + +/* If kiss_fft_alloc allocated a buffer, it is one contiguous + buffer and can be simply free()d when no longer needed*/ +//#define THX_kiss_fft_free free + +/* + Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up + your compiler output to call this before you exit. +*/ +void THX_kiss_fft_cleanup(void); + + +/* + * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) + */ +int THX_kiss_fft_next_fast_size(int n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/neural_thx/VectorOperations.c b/lib/neural_thx/VectorOperations.c new file mode 100755 index 0000000..c2f37ef --- /dev/null +++ b/lib/neural_thx/VectorOperations.c @@ -0,0 +1,710 @@ +// _____________ +// _________________ +// _____/~\___________/\_ +// ______/\_/\_________/\____ +// ______/\___/\_______/\______ +// _____/\_____/\_____/\_______ +// ____/\_______/\___/\________ +// __/\_________/\_/\________ +// /\___________/~\_______ +// ___________________ +// _____________ +// +//***************************************************************************// +//* *// +//* Project : Neural Surround *// +//* File : VectorOperations.c *// +//* Description : Basic vector (array) operations *// +//* Author(s) : Jeff Thompson *// +//* *// +//* Copyright (c) Neural Audio Corp. 2006 *// +//* *// +//***************************************************************************// +#include "Neural_THX_Encoders.h" +#include +#include + +//***************************************************************************// +//* Add2(...) *// +//***************************************************************************// +void Add2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index]; + } +} + +//***************************************************************************// +//* Add2_x2(...) *// +//***************************************************************************// +void Add2_x2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] + PtrIn3[index]; + } +} + +//***************************************************************************// +//* Add2_x3(...) *// +//***************************************************************************// +void Add2_x3(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + float * PtrIn4, + float * PtrIn5, + float * PtrOut2, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] + PtrIn3[index]; + PtrOut2[index] = PtrIn4[index] + PtrIn5[index]; + } +} + +//***************************************************************************// +//* Add2_x4(...) *// +//***************************************************************************// +void Add2_x4(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + float * PtrIn4, + float * PtrIn5, + float * PtrOut2, + float * PtrIn6, + float * PtrIn7, + float * PtrOut3, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] + PtrIn3[index]; + PtrOut2[index] = PtrIn4[index] + PtrIn5[index]; + PtrOut3[index] = PtrIn6[index] + PtrIn7[index]; + } +} + +//***************************************************************************// +//* Add3(...) *// +//***************************************************************************// +void Add3(float * PtrIn0, + float * PtrIn1, + float * PtrIn2, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index] + PtrIn2[index]; + } +} + +//***************************************************************************// +//* Add4(...) *// +//***************************************************************************// +void Add4(float * PtrIn0, + float * PtrIn1, + float * PtrIn2, + float * PtrIn3, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index] + PtrIn2[index] + PtrIn3[index]; + } +} + +//***************************************************************************// +//* Add5(...) *// +//***************************************************************************// +void Add5(float * PtrIn0, + float * PtrIn1, + float * PtrIn2, + float * PtrIn3, + float * PtrIn4, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] + PtrIn1[index] + PtrIn2[index] + PtrIn3[index] + PtrIn4[index]; + } +} + +//***************************************************************************// +//* Subtract2(...) *// +//***************************************************************************// +void Subtract2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] - PtrIn1[index]; + } +} + +//***************************************************************************// +//* Subtract2_x2(...) *// +//***************************************************************************// +void Subtract2_x2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] - PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] - PtrIn3[index]; + } +} + +//***************************************************************************// +//* Subtract2_x3(...) *// +//***************************************************************************// +void Subtract2_x3(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + float * PtrIn4, + float * PtrIn5, + float * PtrOut2, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] - PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] - PtrIn3[index]; + PtrOut2[index] = PtrIn4[index] - PtrIn5[index]; + } +} + +//***************************************************************************// +//* Subtract2_x4(...) *// +//***************************************************************************// +void Subtract2_x4(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + float * PtrIn4, + float * PtrIn5, + float * PtrOut2, + float * PtrIn6, + float * PtrIn7, + float * PtrOut3, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] - PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] - PtrIn3[index]; + PtrOut2[index] = PtrIn4[index] - PtrIn5[index]; + PtrOut3[index] = PtrIn6[index] - PtrIn7[index]; + } +} + +//***************************************************************************// +//* Multiply2(...) *// +//***************************************************************************// +void Multiply2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index]; + } +} + +//***************************************************************************// +//* Multiply2_x2(...) *// +//***************************************************************************// +void Multiply2_x2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] * PtrIn3[index]; + } +} + +//***************************************************************************// +//* Multiply2_x3(...) *// +//***************************************************************************// +void Multiply2_x3(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + float * PtrIn4, + float * PtrIn5, + float * PtrOut2, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] * PtrIn3[index]; + PtrOut2[index] = PtrIn4[index] * PtrIn5[index]; + } +} + +//***************************************************************************// +//* Multiply2_x4(...) *// +//***************************************************************************// +void Multiply2_x4(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + float * PtrIn2, + float * PtrIn3, + float * PtrOut1, + float * PtrIn4, + float * PtrIn5, + float * PtrOut2, + float * PtrIn6, + float * PtrIn7, + float * PtrOut3, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index]; + PtrOut1[index] = PtrIn2[index] * PtrIn3[index]; + PtrOut2[index] = PtrIn4[index] * PtrIn5[index]; + PtrOut3[index] = PtrIn6[index] * PtrIn7[index]; + } +} + +//***************************************************************************// +//* Multiply3(...) *// +//***************************************************************************// +void Multiply3(float * PtrIn0, + float * PtrIn1, + float * PtrIn2, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index] * PtrIn2[index]; + } +} + +//***************************************************************************// +//* Multiply4(...) *// +//***************************************************************************// +void Multiply4(float * PtrIn0, + float * PtrIn1, + float * PtrIn2, + float * PtrIn3, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index] * PtrIn2[index] * PtrIn3[index]; + } +} + +//***************************************************************************// +//* Multiply5(...) *// +//***************************************************************************// +void Multiply5(float * PtrIn0, + float * PtrIn1, + float * PtrIn2, + float * PtrIn3, + float * PtrIn4, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + PtrOut0[index] = PtrIn0[index] * PtrIn1[index] * PtrIn2[index] * PtrIn3[index] * PtrIn4[index]; + } +} + +//***************************************************************************// +//* ComplexMultiply2(...) *// +//***************************************************************************// +void ComplexMultiply2(float * PtrInReal0, + float * PtrInImag0, + float * PtrInReal1, + float * PtrInImag1, + float * PtrOutReal, + float * PtrOutImag, + unsigned int Framesize) +{ + unsigned int index; + float Real0, Real1, Imag0, Imag1; + + for (index = 0; index < Framesize; index++) { + Real0 = PtrInReal0[index]; + Real1 = PtrInReal1[index]; + Imag0 = PtrInImag0[index]; + Imag1 = PtrInImag1[index]; + PtrOutReal[index] = Real0 * Real1 - Imag0 * Imag1; + PtrOutImag[index] = Real0 * Imag1 + Real1 * Imag0; + } +} + +//***************************************************************************// +//* Divide2(...) *// +//***************************************************************************// +void Divide2(float * PtrIn0, + float * PtrIn1, + float * PtrOut0, + unsigned int Framesize) +{ + unsigned int index; + + for (index = 0; index MaxValue) + PtrArray[index] = MaxValue; + } +} + +//***************************************************************************// +//* MinClip(...) *// +//***************************************************************************// +void MinClip(float * PtrArray, + float MinValue, + unsigned int Framesize) +{ + unsigned int index; + + for(index = 0; index < Framesize; index++){ + if(PtrArray[index] < MinValue) + PtrArray[index] = MinValue; + } +} + +//***************************************************************************// +//* SaturateArray(...) *// +//***************************************************************************// +void SaturateArray(float * PtrArray, + float SaturationValue, + unsigned int Framesize) +{ + unsigned int index; + float MaxValuePos, MaxValueNeg, Value; + + MaxValuePos = (float)fabs(SaturationValue); + MaxValueNeg = -MaxValuePos; + + for(index = 0; index < Framesize; index++){ + Value = PtrArray[index]; + if(Value > MaxValuePos) + Value = MaxValuePos; + if(Value < MaxValueNeg) + Value = MaxValueNeg; + PtrArray[index] = Value; + } +} + +//***************************************************************************// +//* ReverseArray(...) *// +//***************************************************************************// +void ReverseArray(float * PtrArray, + unsigned int Framesize) +{ + unsigned int index; + float temp; + + for(index = 0; index < (Framesize>>1); index++){ + temp = PtrArray[index]; + PtrArray[index] = PtrArray[Framesize-index-1]; + PtrArray[Framesize-index-1] = temp; + } +} diff --git a/lib/ogg_vorbis/ogg/include/ogg/ogg.h b/lib/ogg_vorbis/ogg/include/ogg/ogg.h new file mode 100755 index 0000000..332b514 --- /dev/null +++ b/lib/ogg_vorbis/ogg/include/ogg/ogg.h @@ -0,0 +1,208 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel libogg include + last mod: $Id: ogg.h 16051 2009-05-27 05:00:06Z xiphmont $ + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct { + void *iov_base; + size_t iov_len; +} ogg_iovec_t; + +typedef struct { + ogg_int32_t endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + ogg_int32_t storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + ogg_int32_t header_len; + unsigned char *body; + ogg_int32_t body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + ogg_int32_t body_storage; /* storage elements allocated */ + ogg_int32_t body_fill; /* elements stored; fill mark */ + ogg_int32_t body_returned; /* elements of fill returned */ + + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + ogg_int32_t lacing_storage; + ogg_int32_t lacing_fill; + ogg_int32_t lacing_packet; + ogg_int32_t lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + ogg_int32_t serialno; + ogg_int32_t pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata beogg_int32_ting + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + ogg_int32_t bytes; + ogg_int32_t b_o_s; + ogg_int32_t e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +//extern void FMOD_oggpack_writeinit(void *context, oggpack_buffer *b); +//extern int FMOD_oggpack_writecheck(oggpack_buffer *b); +//extern void FMOD_oggpack_writetrunc(oggpack_buffer *b,ogg_int32_t bits); +//extern void FMOD_oggpack_writealign(oggpack_buffer *b); +//extern void FMOD_oggpack_writecopy(oggpack_buffer *b,void *source,ogg_int32_t bits); +extern void FMOD_oggpack_reset(oggpack_buffer *b); +//extern void FMOD_oggpack_writeclear(oggpack_buffer *b); +extern void FMOD_oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +//extern void FMOD_oggpack_write(oggpack_buffer *b, ogg_uint32_t value,int bits); +extern ogg_int32_t FMOD_oggpack_look(oggpack_buffer *b,int bits); +extern ogg_int32_t FMOD_oggpack_look1(oggpack_buffer *b); +extern void FMOD_oggpack_adv(oggpack_buffer *b,int bits); +extern void FMOD_oggpack_adv1(oggpack_buffer *b); +extern ogg_int32_t FMOD_oggpack_read(oggpack_buffer *b,int bits); +extern ogg_int32_t FMOD_oggpack_read1(oggpack_buffer *b); +extern ogg_int32_t FMOD_oggpack_bytes(oggpack_buffer *b); +extern ogg_int32_t FMOD_oggpack_bits(oggpack_buffer *b); +extern unsigned char *FMOD_oggpack_get_buffer(oggpack_buffer *b); + +//extern void FMOD_oggpackB_writeinit(oggpack_buffer *b); +//extern int FMOD_oggpackB_writecheck(oggpack_buffer *b); +//extern void FMOD_oggpackB_writetrunc(oggpack_buffer *b,ogg_int32_t bits); +//extern void FMOD_oggpackB_writealign(oggpack_buffer *b); +//extern void FMOD_oggpackB_writecopy(oggpack_buffer *b,void *source,ogg_int32_t bits); +extern void FMOD_oggpackB_reset(oggpack_buffer *b); +//extern void FMOD_oggpackB_writeclear(oggpack_buffer *b); +extern void FMOD_oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +//extern void FMOD_oggpackB_write(oggpack_buffer *b, ogg_uint32_t value,int bits); +extern ogg_int32_t FMOD_oggpackB_look(oggpack_buffer *b,int bits); +extern ogg_int32_t FMOD_oggpackB_look1(oggpack_buffer *b); +extern void FMOD_oggpackB_adv(oggpack_buffer *b,int bits); +extern void FMOD_oggpackB_adv1(oggpack_buffer *b); +extern ogg_int32_t FMOD_oggpackB_read(oggpack_buffer *b,int bits); +extern ogg_int32_t FMOD_oggpackB_read1(oggpack_buffer *b); +extern ogg_int32_t FMOD_oggpackB_bytes(oggpack_buffer *b); +extern ogg_int32_t FMOD_oggpackB_bits(oggpack_buffer *b); +extern unsigned char *FMOD_oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +//extern int FMOD_ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +//extern int FMOD_ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, +// int count, ogg_int32_t e_o_s, ogg_int64_t granulepos); +//extern int FMOD_ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +//extern int FMOD_ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int FMOD_ogg_sync_init(ogg_sync_state *oy); +extern int FMOD_ogg_sync_clear(void *context, ogg_sync_state *oy); +extern int FMOD_ogg_sync_reset(ogg_sync_state *oy); +extern int FMOD_ogg_sync_destroy(void *context, ogg_sync_state *oy); +extern int FMOD_ogg_sync_check(ogg_sync_state *oy); + +extern char *FMOD_ogg_sync_buffer(void *context, ogg_sync_state *oy, ogg_int32_t size); +extern int FMOD_ogg_sync_wrote(ogg_sync_state *oy, ogg_int32_t bytes); +extern ogg_int32_t FMOD_ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int FMOD_ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int FMOD_ogg_stream_pagein(void *context, ogg_stream_state *os, ogg_page *og); +extern int FMOD_ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int FMOD_ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int FMOD_ogg_stream_init(void *context, ogg_stream_state *os,int serialno); +extern int FMOD_ogg_stream_clear(void *context, ogg_stream_state *os); +extern int FMOD_ogg_stream_reset(ogg_stream_state *os); +extern int FMOD_ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int FMOD_ogg_stream_destroy(void *context, ogg_stream_state *os); +extern int FMOD_ogg_stream_check(ogg_stream_state *os); +extern int FMOD_ogg_stream_eos(ogg_stream_state *os); + +extern void FMOD_ogg_page_checksum_set(ogg_page *og); + +extern int FMOD_ogg_page_version(const ogg_page *og); +extern int FMOD_ogg_page_continued(const ogg_page *og); +extern int FMOD_ogg_page_bos(const ogg_page *og); +extern int FMOD_ogg_page_eos(const ogg_page *og); +extern ogg_int64_t FMOD_ogg_page_granulepos(const ogg_page *og); +extern int FMOD_ogg_page_serialno(const ogg_page *og); +extern ogg_int32_t FMOD_ogg_page_pageno(const ogg_page *og); +extern int FMOD_ogg_page_packets(const ogg_page *og); + +extern void FMOD_ogg_packet_clear(void *context, ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ diff --git a/lib/ogg_vorbis/ogg/include/ogg/os_types.h b/lib/ogg_vorbis/ogg/include/ogg/os_types.h new file mode 100755 index 0000000..2089a92 --- /dev/null +++ b/lib/ogg_vorbis/ogg/include/ogg/os_types.h @@ -0,0 +1,201 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h 14997 2008-06-04 03:27:18Z ivo $ + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#if 1 + +extern void * FMOD_OggVorbis_Malloc(void *context, int size); +extern void * FMOD_OggVorbis_Calloc(void *context, int count, int size); +extern void * FMOD_OggVorbis_ReAlloc(void *context, void *ptr, int size); +extern void FMOD_OggVorbis_Free(void *context, void *ptr); + + +#define _ogg_malloc(_x) FMOD_OggVorbis_Malloc(context, _x) +#define _ogg_calloc(_x, _y) FMOD_OggVorbis_Calloc(context, _x, _y) +#define _ogg_realloc(_x, _y) FMOD_OggVorbis_ReAlloc(context, _x, _y) +#define _ogg_free(_x) FMOD_OggVorbis_Free(context, _x) + +#include "../../../../../src/fmod_types.h" /* For FMOD_memset/memcpy */ + +#else + +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#endif + +#if defined(WIN32) || defined(_XENON) + +# if defined(__CYGWIN__) +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; +# elif defined(__MINGW32__) +# include + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; +# elif defined(__MWERKS__) + typedef long long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 ogg_int16_t; + typedef UInt16 ogg_uint16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__HAIKU__) + + /* Haiku */ +# include + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (R5900) + + /* PS2 */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long ogg_int64_t; + + #if defined(__MWERKS__) + #define alloca __alloca + #endif + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#elif defined(SN_TARGET_PS3) || defined(_PS3) + + #include + + typedef signed short ogg_int16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef signed long long ogg_int64_t; + +#elif defined (GEKKO) + + #if defined(PLATFORM_WII) + #include + #else + #if defined(__MWERKS__) + #define alloca __alloca + #endif + #endif + + /* Gamecube */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (__psp__) + + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (__sun) || defined (__linux__) + + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#else + +# include +# include + +#endif + +#endif /* _OS_TYPES_H */ diff --git a/lib/ogg_vorbis/ogg/src/bitwise.c b/lib/ogg_vorbis/ogg/src/bitwise.c new file mode 100755 index 0000000..9dfa261 --- /dev/null +++ b/lib/ogg_vorbis/ogg/src/bitwise.c @@ -0,0 +1,286 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitwise.c 16051 2009-05-27 05:00:06Z xiphmont $ + + ********************************************************************/ + +/* We're 'LSb' endian; if we write a word but read individual bits, + then we'll read the lsb first */ + +#include +#include +#include + +#define BUFFER_INCREMENT 256 + +static const ogg_uint32_t mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +void FMOD_oggpack_reset(oggpack_buffer *b){ + if(!b->ptr)return; + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void FMOD_oggpackB_reset(oggpack_buffer *b){ + FMOD_oggpack_reset(b); +} + +void FMOD_oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + FMOD_memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +void FMOD_oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + FMOD_oggpack_readinit(b,buf,bytes); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +ogg_int32_t FMOD_oggpack_look(oggpack_buffer *b,int bits){ + ogg_uint32_t ret; + ogg_uint32_t m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +ogg_int32_t FMOD_oggpackB_look(oggpack_buffer *b,int bits){ + ogg_uint32_t ret; + int m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return ((ret&0xffffffff)>>(m>>1))>>((m+1)>>1); +} + +ogg_int32_t FMOD_oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +ogg_int32_t FMOD_oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} + +void FMOD_oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void FMOD_oggpackB_adv(oggpack_buffer *b,int bits){ + FMOD_oggpack_adv(b,bits); +} + +void FMOD_oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +void FMOD_oggpackB_adv1(oggpack_buffer *b){ + FMOD_oggpack_adv1(b); +} + +/* bits <= 32 */ +ogg_int32_t FMOD_oggpack_read(oggpack_buffer *b,int bits){ + ogg_int32_t ret; + ogg_uint32_t m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +/* bits <= 32 */ +ogg_int32_t FMOD_oggpackB_read(oggpack_buffer *b,int bits){ + ogg_int32_t ret; + ogg_int32_t m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + ret=((ret&0xffffffff)>>(m>>1))>>((m+1)>>1); + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +ogg_int32_t FMOD_oggpack_read1(oggpack_buffer *b){ + ogg_int32_t ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1; + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +ogg_int32_t FMOD_oggpackB_read1(oggpack_buffer *b){ + ogg_int32_t ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1; + goto overflow; + } + + ret=(b->ptr[0]>>(7-b->endbit))&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +ogg_int32_t FMOD_oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +ogg_int32_t FMOD_oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +ogg_int32_t FMOD_oggpackB_bytes(oggpack_buffer *b){ + return FMOD_oggpack_bytes(b); +} + +ogg_int32_t FMOD_oggpackB_bits(oggpack_buffer *b){ + return FMOD_oggpack_bits(b); +} + +unsigned char *FMOD_oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *FMOD_oggpackB_get_buffer(oggpack_buffer *b){ + return FMOD_oggpack_get_buffer(b); +} + +#undef BUFFER_INCREMENT diff --git a/lib/ogg_vorbis/ogg/src/framing.c b/lib/ogg_vorbis/ogg/src/framing.c new file mode 100755 index 0000000..0573d83 --- /dev/null +++ b/lib/ogg_vorbis/ogg/src/framing.c @@ -0,0 +1,964 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: code raw packets into framed OggSquish stream and + decode Ogg streams back into raw packets + last mod: $Id: framing.c 16051 2009-05-27 05:00:06Z xiphmont $ + + note: The CRC code is directly derived from public domain code by + Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html + for details. + + ********************************************************************/ + +#include +#include +#include + +/* A complete description of Ogg framing exists in docs/framing.html */ + +int FMOD_ogg_page_version(const ogg_page *og){ + return((int)(og->header[4])); +} + +int FMOD_ogg_page_continued(const ogg_page *og){ + return((int)(og->header[5]&0x01)); +} + +int FMOD_ogg_page_bos(const ogg_page *og){ + return((int)(og->header[5]&0x02)); +} + +int FMOD_ogg_page_eos(const ogg_page *og){ + return((int)(og->header[5]&0x04)); +} + +ogg_int64_t FMOD_ogg_page_granulepos(const ogg_page *og){ + unsigned char *page=og->header; + ogg_int64_t granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +int FMOD_ogg_page_serialno(const ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +ogg_int32_t FMOD_ogg_page_pageno(const ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + + + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +int FMOD_ogg_page_packets(const ogg_page *og){ + int i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + +static const ogg_uint32_t crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +int FMOD_ogg_stream_init(void *context, ogg_stream_state *os,int serialno){ + if(os){ + FMOD_memset(os,0,sizeof(*os)); + +#if 1 + os->body_storage=(4*1024) + 512; + os->lacing_storage=64; +#else + os->body_storage=16*1024; + os->lacing_storage=1024; +#endif + + os->body_data=_ogg_malloc(os->body_storage*sizeof(*os->body_data)); + os->lacing_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + if(!os->body_data || !os->lacing_vals || !os->granule_vals){ + FMOD_ogg_stream_clear(context, os); + return -1; + } + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* async/delayed error detection for the ogg_stream_state */ +int FMOD_ogg_stream_check(ogg_stream_state *os){ + if(!os || !os->body_data) return -1; + return 0; +} + +/* _clear does not free os, only the non-flat storage within */ +int FMOD_ogg_stream_clear(void *context, ogg_stream_state *os){ + if(os){ + if(os->body_data)_ogg_free(os->body_data); + if(os->lacing_vals)_ogg_free(os->lacing_vals); + if(os->granule_vals)_ogg_free(os->granule_vals); + + FMOD_memset(os,0,sizeof(*os)); + } + return(0); +} + +int FMOD_ogg_stream_destroy(void *context, ogg_stream_state *os){ + if(os){ + FMOD_ogg_stream_clear(context, os); + _ogg_free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static int _os_body_expand(void *context, ogg_stream_state *os,int needed){ + if(os->body_storage<=os->body_fill+needed){ + void *ret; + ret=_ogg_realloc(os->body_data,(os->body_storage+needed+1024)* + sizeof(*os->body_data)); + if(!ret){ + FMOD_ogg_stream_clear(context, os); + return -1; + } + os->body_storage+=(needed+1024); + os->body_data=ret; + } + return 0; +} + +static int _os_lacing_expand(void *context, ogg_stream_state *os,int needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + void *ret; + ret=_ogg_realloc(os->lacing_vals,(os->lacing_storage+needed+32)* + sizeof(*os->lacing_vals)); + if(!ret){ + FMOD_ogg_stream_clear(context, os); + return -1; + } + os->lacing_vals=ret; + ret=_ogg_realloc(os->granule_vals,(os->lacing_storage+needed+32)* + sizeof(*os->granule_vals)); + if(!ret){ + FMOD_ogg_stream_clear(context, os); + return -1; + } + os->granule_vals=ret; + os->lacing_storage+=(needed+32); + } + return 0; +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void FMOD_ogg_page_checksum_set(ogg_page *og){ + if(og){ + ogg_uint32_t crc_reg=0; + int i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=(unsigned char)(crc_reg&0xff); + og->header[23]=(unsigned char)((crc_reg>>8)&0xff); + og->header[24]=(unsigned char)((crc_reg>>16)&0xff); + og->header[25]=(unsigned char)((crc_reg>>24)&0xff); + } +} + +/* submit data to the internal buffer of the framing engine */ +//int ogg_stream_iovecin(void *context, ogg_stream_state *os, ogg_iovec_t *iov, int count, +// ogg_int32_t e_o_s, ogg_int64_t granulepos){ +// +// int bytes = 0, lacing_vals, i; +// +// if(FMOD_ogg_stream_check(os)) return -1; +// if(!iov) return 0; +// +// for (i = 0; i < count; ++i) bytes += (int)iov[i].iov_len; +// lacing_vals=bytes/255+1; +// +// if(os->body_returned){ +// /* advance packet data according to the body_returned pointer. We +// had to keep it around to return a pointer into the buffer last +// call */ +// +// os->body_fill-=os->body_returned; +// if(os->body_fill) +// memmove(os->body_data,os->body_data+os->body_returned, +// os->body_fill); +// os->body_returned=0; +// } +// +// /* make sure we have the buffer storage */ +// if(_os_body_expand(context, os,bytes) || _os_lacing_expand(context, os,lacing_vals)) +// return -1; +// +// /* Copy in the submitted packet. Yes, the copy is a waste; this is +// the liability of overly clean abstraction for the time being. It +// will actually be fairly easy to eliminate the extra copy in the +// future */ +// +// for (i = 0; i < count; ++i) { +// FMOD_memcpy(os->body_data+os->body_fill, iov[i].iov_base, iov[i].iov_len); +// os->body_fill += (int)iov[i].iov_len; +// } +// +// /* Store lacing vals for this packet */ +// for(i=0;ilacing_vals[os->lacing_fill+i]=255; +// os->granule_vals[os->lacing_fill+i]=os->granulepos; +// } +// os->lacing_vals[os->lacing_fill+i]=bytes%255; +// os->granulepos=os->granule_vals[os->lacing_fill+i]=granulepos; +// +// /* flag the first segment as the beginning of the packet */ +// os->lacing_vals[os->lacing_fill]|= 0x100; +// +// os->lacing_fill+=lacing_vals; +// +// /* for the sake of completeness */ +// os->packetno++; +// +// if(e_o_s)os->e_o_s=1; +// +// return(0); +//} +// +//int FMOD_ogg_stream_packetin(void *context, ogg_stream_state *os,ogg_packet *op){ +// ogg_iovec_t iov; +// iov.iov_base = op->packet; +// iov.iov_len = op->bytes; +// return ogg_stream_iovecin(context, os, &iov, 1, op->e_o_s, op->granulepos); +//} +// +///* This will flush remaining packets into a page (returning nonzero), +// even if there is not enough data to trigger a flush normally +// (undersized page). If there are no packets or partial packets to +// flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will +// try to flush a normal sized page like ogg_stream_pageout; a call to +// ogg_stream_flush does not guarantee that all packets have flushed. +// Only a return value of 0 from ogg_stream_flush indicates all packet +// data is flushed into pages. +// +// since ogg_stream_flush will flush the last page in a stream even if +// it's undersized, you almost certainly want to use ogg_stream_pageout +// (and *not* ogg_stream_flush) unless you specifically need to flush +// an page regardless of size in the middle of a stream. */ +// +//int FMOD_ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ +// int i; +// int vals=0; +// int maxvals=(os->lacing_fill>255?255:os->lacing_fill); +// int bytes=0; +// ogg_int32_t acc=0; +// ogg_int64_t granule_pos=-1; +// +// if(FMOD_ogg_stream_check(os)) return 0; +// if(maxvals==0)return 0; +// +// /* construct a page */ +// /* decide how many segments to include */ +// +// /* If this is the initial header case, the first page must only include +// the initial header packet */ +// if(os->b_o_s==0){ /* 'initial header page' case */ +// granule_pos=0; +// for(vals=0;valslacing_vals[vals]&0x0ff)<255){ +// vals++; +// break; +// } +// } +// }else{ +// for(vals=0;vals4096)break; +// acc+=os->lacing_vals[vals]&0x0ff; +// if((os->lacing_vals[vals]&0xff)<255) +// granule_pos=os->granule_vals[vals]; +// } +// } +// +// /* construct the header in temp storage */ +// FMOD_memcpy(os->header,"OggS",4); +// +// /* stream structure version */ +// os->header[4]=0x00; +// +// /* continued packet flag? */ +// os->header[5]=0x00; +// if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; +// /* first page flag? */ +// if(os->b_o_s==0)os->header[5]|=0x02; +// /* last page flag? */ +// if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; +// os->b_o_s=1; +// +// /* 64 bits of PCM position */ +// for(i=6;i<14;i++){ +// os->header[i]=(unsigned char)(granule_pos&0xff); +// granule_pos>>=8; +// } +// +// /* 32 bits of stream serial number */ +// { +// ogg_int32_t serialno=os->serialno; +// for(i=14;i<18;i++){ +// os->header[i]=(unsigned char)(serialno&0xff); +// serialno>>=8; +// } +// } +// +// /* 32 bits of page counter (we have both counter and page header +// because this val can roll over) */ +// if(os->pageno==-1)os->pageno=0; /* because someone called +// stream_reset; this would be a +// strange thing to do in an +// encode stream, but it has +// plausible uses */ +// { +// ogg_int32_t pageno=os->pageno++; +// for(i=18;i<22;i++){ +// os->header[i]=(unsigned char)(pageno&0xff); +// pageno>>=8; +// } +// } +// +// /* zero for computation; filled in later */ +// os->header[22]=0; +// os->header[23]=0; +// os->header[24]=0; +// os->header[25]=0; +// +// /* segment table */ +// os->header[26]=(unsigned char)(vals&0xff); +// for(i=0;iheader[i+27]=(unsigned char)(os->lacing_vals[i]&0xff); +// +// /* set pointers in the ogg_page struct */ +// og->header=os->header; +// og->header_len=os->header_fill=vals+27; +// og->body=os->body_data+os->body_returned; +// og->body_len=bytes; +// +// /* advance the lacing data and set the body_returned pointer */ +// +// os->lacing_fill-=vals; +// memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); +// memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); +// os->body_returned+=bytes; +// +// /* calculate the checksum */ +// +// ogg_page_checksum_set(og); +// +// /* done */ +// return(1); +//} +// +// +///* This constructs pages from buffered packet segments. The pointers +//returned are to static buffers; do not free. The returned buffers are +//good only until the next call (using the same ogg_stream_state) */ +// +//int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ +// if(FMOD_ogg_stream_check(os)) return 0; +// +// if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ +// os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ +// os->lacing_fill>=255 || /* 'segment table full' case */ +// (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ +// +// return(ogg_stream_flush(os,og)); +// } +// +// /* not enough data to construct a page and not end of stream */ +// return 0; +//} + +int FMOD_ogg_stream_eos(ogg_stream_state *os){ + if(FMOD_ogg_stream_check(os)) return 1; + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. */ + +/* initialize the struct to a known state */ +int FMOD_ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + oy->storage = -1; /* used as a readiness flag */ + FMOD_memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +int FMOD_ogg_sync_clear(void *context, ogg_sync_state *oy){ + if(oy){ + if(oy->data)_ogg_free(oy->data); + FMOD_memset(oy,0,sizeof(*oy)); + } + return(0); +} + +int FMOD_ogg_sync_destroy(void *context, ogg_sync_state *oy){ + if(oy){ + FMOD_ogg_sync_clear(context, oy); + _ogg_free(oy); + } + return(0); +} + +int FMOD_ogg_sync_check(ogg_sync_state *oy){ + if(oy->storage<0) return -1; + return 0; +} + +char *FMOD_ogg_sync_buffer(void *context, ogg_sync_state *oy, ogg_int32_t size){ + if(FMOD_ogg_sync_check(oy)) return NULL; + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + ogg_int32_t newsize=size+oy->fill+4096; /* an extra page to be nice */ + void *ret; + + if(oy->data) + ret=_ogg_realloc(oy->data,newsize); + else + ret=_ogg_malloc(newsize); + oy->data = 0; /* Null out old pointer so clear doesnt fall over on bad/released data? */ + if(!ret){ + FMOD_ogg_sync_clear(context, oy); + return NULL; + } + oy->data=ret; + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +int FMOD_ogg_sync_wrote(ogg_sync_state *oy, ogg_int32_t bytes){ + if(FMOD_ogg_sync_check(oy))return -1; + if(oy->fill+bytes>oy->storage)return -1; + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +ogg_int32_t FMOD_ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + ogg_int32_t bytes=oy->fill-oy->returned; + + if(FMOD_ogg_sync_check(oy))return 0; + + if(oy->headerbytes==0){ + int headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + FMOD_memcpy(chksum,page+22,4); + FMOD_memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + FMOD_ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + FMOD_memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + ogg_int32_t bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next=memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=(int)(next-oy->data); + return((ogg_int32_t)-(next-page)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int FMOD_ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + if(FMOD_ogg_sync_check(oy))return 0; + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + for(;;){ + ogg_int32_t ret=FMOD_ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +int FMOD_ogg_stream_pagein(void *context, ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + ogg_int32_t bodysize=og->body_len; + int segptr=0; + + int version=FMOD_ogg_page_version(og); + int continued=FMOD_ogg_page_continued(og); + int bos=FMOD_ogg_page_bos(og); + int eos=FMOD_ogg_page_eos(og); + ogg_int64_t granulepos=FMOD_ogg_page_granulepos(og); + int serialno=FMOD_ogg_page_serialno(og); + ogg_int32_t pageno=FMOD_ogg_page_pageno(og); + int segments=header[26]; + + if(FMOD_ogg_stream_check(os)) return -1; + + /* clean up 'returned data' */ + { + ogg_int32_t lr=os->lacing_returned; + ogg_int32_t br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + if(_os_lacing_expand(context, os,segments+1)) return -1; + + /* are we in sequence? */ + if(pageno!=os->pageno){ + int i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + } + + /* are we a 'continued packet' page? If so, we may need to skip + some segments */ + if(continued){ + if(os->lacing_fill<1 || + os->lacing_vals[os->lacing_fill-1]==0x400){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + int saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int FMOD_ogg_sync_reset(ogg_sync_state *oy){ + if(FMOD_ogg_sync_check(oy))return -1; + + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +int FMOD_ogg_stream_reset(ogg_stream_state *os){ + if(FMOD_ogg_stream_check(os)) return -1; + + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +int FMOD_ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + if(FMOD_ogg_stream_check(os)) return -1; + FMOD_ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + int ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=os->lacing_vals[ptr]&0xff; + int bytes=size; + int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + int val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +int FMOD_ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + if(FMOD_ogg_stream_check(os)) return 0; + return _packetout(os,op,1); +} + +int FMOD_ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + if(FMOD_ogg_stream_check(os)) return 0; + return _packetout(os,op,0); +} + +void FMOD_ogg_packet_clear(void *context, ogg_packet *op) { + _ogg_free(op->packet); + FMOD_memset(op, 0, sizeof(*op)); +} diff --git a/lib/ogg_vorbis/vorbis/include/vorbis/codec.h b/lib/ogg_vorbis/vorbis/include/vorbis/codec.h new file mode 100755 index 0000000..dcd714d --- /dev/null +++ b/lib/ogg_vorbis/vorbis/include/vorbis/codec.h @@ -0,0 +1,242 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + + ******************************************************************** + + function: libvorbis codec headers + last mod: $Id: codec.h 16037 2009-05-26 21:10:58Z xiphmont $ + + ********************************************************************/ + +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +typedef struct vorbis_info{ + int version; + int channels; + ogg_int32_t rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + ogg_int32_t bitrate_upper; + ogg_int32_t bitrate_nominal; + ogg_int32_t bitrate_lower; + ogg_int32_t bitrate_window; + + void *codec_setup; +} vorbis_info; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +typedef struct vorbis_dsp_state{ +// int analysisp; + vorbis_info *vi; + + float **pcm; + float **pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + int preextrapolate; + int eofflag; + + ogg_int32_t lW; + ogg_int32_t W; + ogg_int32_t nW; + ogg_int32_t centerW; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + + ogg_int64_t glue_bits; + ogg_int64_t time_bits; + ogg_int64_t floor_bits; + ogg_int64_t res_bits; + + void *backend_state; +} vorbis_dsp_state; + +typedef struct vorbis_block{ + /* necessary stream state for linking to the framing abstraction */ + float **pcm; /* this is a pointer into local storage */ + oggpack_buffer opb; + + ogg_int32_t lW; + ogg_int32_t W; + ogg_int32_t nW; + int pcmend; + int mode; + + int eofflag; + ogg_int64_t granulepos; + ogg_int64_t sequence; + vorbis_dsp_state *vd; /* For read-only access of configuration */ + + /* local storage to avoid remallocing; it's up to the mapping to + structure it */ + void *localstore; + ogg_int32_t localtop; + ogg_int32_t localalloc; + ogg_int32_t totaluse; + struct alloc_chain *reap; + + /* bitmetrics for the frame */ + ogg_int32_t glue_bits; + ogg_int32_t time_bits; + ogg_int32_t floor_bits; + ogg_int32_t res_bits; + + //void *internal; + +} vorbis_block; + +/* vorbis_block is a single block of data to be processed as part of +the analysis/synthesis stream; it belongs to a specific logical +bitstream, but is independant from other vorbis_blocks belonging to +that logical bitstream. *************************************************/ + +struct alloc_chain{ + void *ptr; + struct alloc_chain *next; +}; + +/* vorbis_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). vorbis_info and substructures are in backends.h. +*********************************************************************/ + +/* the comments are not part of vorbis_info so that vorbis_info can be + static storage */ +typedef struct vorbis_comment{ + /* unlimited user comment fields. libvorbis writes 'libvorbis' + whatever vendor is set to in encode */ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + + +/* libvorbis encodes in two abstraction layers; first we perform DSP + and produce a packet (see docs/analysis.txt). The packet is then + coded into a framed OggSquish bitstream by the second layer (see + docs/framing.txt). Decode is the reverse process; we sync/frame + the bitstream and extract individual packets, then decode the + packet back into PCM audio. + + The extra framing/packetizing is used in streaming formats, such as + files. Over the net (such as with UDP), the framing and + packetization aren't necessary as they're provided by the transport + and the streaming layer is not used */ + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern int FMOD_vorbis_info_init(void *context, vorbis_info *vi); +extern void FMOD_vorbis_info_clear(void *context, vorbis_info *vi); +extern int FMOD_vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void FMOD_vorbis_comment_init(vorbis_comment *vc); +//extern void FMOD_vorbis_comment_add(void *context, vorbis_comment *vc, const char *comment); +//extern void FMOD_vorbis_comment_add_tag(void *context, vorbis_comment *vc, +// const char *tag, const char *contents); +//extern char *FMOD_vorbis_comment_query(vorbis_comment *vc, const char *tag, int count); +//extern int FMOD_vorbis_comment_query_count(vorbis_comment *vc, const char *tag); +extern void FMOD_vorbis_comment_clear(void *context, vorbis_comment *vc); + +extern int FMOD_vorbis_block_init(void *context, vorbis_dsp_state *v, vorbis_block *vb); +extern int FMOD_vorbis_block_clear(void *context, vorbis_block *vb); +extern void FMOD_vorbis_dsp_clear(void *context, vorbis_dsp_state *v); +extern float FMOD_vorbis_granule_time(vorbis_dsp_state *v, + ogg_int64_t granulepos); + +extern const char *FMOD_vorbis_version_string(void); + +/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/ +//extern int FMOD_vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi); +//extern int FMOD_vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op); +//extern int FMOD_vorbis_analysis_headerout(vorbis_dsp_state *v, +// vorbis_comment *vc, +// ogg_packet *op, +// ogg_packet *op_comm, +// ogg_packet *op_code); +//extern float **FMOD_vorbis_analysis_buffer(void *context, vorbis_dsp_state *v,int vals); +//extern int FMOD_vorbis_analysis_wrote(void *context, vorbis_dsp_state *v,int vals); +//extern int FMOD_vorbis_analysis_blockout(void *context, vorbis_dsp_state *v,vorbis_block *vb); +//extern int FMOD_vorbis_analysis(vorbis_block *vb,ogg_packet *op); +extern int FMOD_vorbis_bitrate_addblock(vorbis_block *vb); +extern int FMOD_vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, + ogg_packet *op); + +/* Vorbis PRIMITIVES: synthesis layer *******************************/ +extern int FMOD_vorbis_synthesis_idheader(ogg_packet *op); +extern int FMOD_vorbis_synthesis_headerin(void *context, vorbis_info *vi,vorbis_comment *vc, + ogg_packet *op); + +extern int FMOD_vorbis_synthesis_init(void *context, vorbis_dsp_state *v,vorbis_info *vi); +extern int FMOD_vorbis_synthesis_restart(vorbis_dsp_state *v); +extern int FMOD_vorbis_synthesis(void *context, vorbis_block *vb,ogg_packet *op); +extern int FMOD_vorbis_synthesis_trackonly(void *context, vorbis_block *vb,ogg_packet *op); +extern int FMOD_vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); +extern int FMOD_vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm); +extern int FMOD_vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm); +extern int FMOD_vorbis_synthesis_read(vorbis_dsp_state *v,int samples); +extern ogg_int32_t FMOD_vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); + +extern int FMOD_vorbis_synthesis_halfrate(vorbis_info *v,int flag); +extern int FMOD_vorbis_synthesis_halfrate_p(vorbis_info *v); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 +#define OV_EMEMORY -139 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/lib/ogg_vorbis/vorbis/include/vorbis/vorbisfile.h b/lib/ogg_vorbis/vorbis/include/vorbis/vorbisfile.h new file mode 100755 index 0000000..fa89512 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/include/vorbis/vorbisfile.h @@ -0,0 +1,208 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.h 16243 2009-07-10 02:49:31Z xiphmont $ + + ********************************************************************/ + +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#define OV_EXCLUDE_STATIC_CALLBACKS + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include "codec.h" + +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + ogg_int32_t (*tell_func) (void *datasource); +} ov_callbacks; + +#ifndef OV_EXCLUDE_STATIC_CALLBACKS + +/* a few sets of convenient callbacks, especially for use under + * Windows where ov_open_callbacks() should always be used instead of + * ov_open() to avoid problems with incompatable crt.o version linking + * issues. */ + +static int _ov_header_fseek_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + +#ifdef __MINGW32__ + return fseeko64(f,off,whence); +#elif defined (_WIN32) + return _fseeki64(f,off,whence); +#else + return fseek(f,off,whence); +#endif +} + +/* These structs below (OV_CALLBACKS_DEFAULT etc) are defined here as + * static data. That means that every file which includes this header + * will get its own copy of these structs whether it uses them or + * not unless it #defines OV_EXCLUDE_STATIC_CALLBACKS. + * These static symbols are essential on platforms such as Windows on + * which several different versions of stdio support may be linked to + * by different DLLs, and we need to be certain we know which one + * we're using (the same one as the main application). + */ + +static ov_callbacks OV_CALLBACKS_DEFAULT = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap, + (int (*)(void *)) fclose, + (ogg_int32_t (*)(void *)) ftell +}; + +static ov_callbacks OV_CALLBACKS_NOCLOSE = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap, + (int (*)(void *)) NULL, + (ogg_int32_t (*)(void *)) ftell +}; + +static ov_callbacks OV_CALLBACKS_STREAMONLY = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) NULL, + (int (*)(void *)) fclose, + (ogg_int32_t (*)(void *)) NULL +}; + +static ov_callbacks OV_CALLBACKS_STREAMONLY_NOCLOSE = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) NULL, + (int (*)(void *)) NULL, + (ogg_int32_t (*)(void *)) NULL +}; + +#endif + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 +#define INITSET 4 + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + ogg_int32_t *serialnos; + ogg_int64_t *pcmlengths; /* overloaded to maintain binary + compatability; x2 size, stores both + beginning and end values */ + vorbis_info *vi; + vorbis_comment *vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + ogg_int32_t current_serialno; + int current_link; + + float bittrack; + float samptrack; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ov_callbacks callbacks; + +} OggVorbis_File; + + +extern int ov_clear(void *context, OggVorbis_File *vf); +extern int ov_fopen(void *context, char *path,OggVorbis_File *vf); +extern int ov_open(void *context, FILE *f,OggVorbis_File *vf,char *initial,ogg_int32_t ibytes); +extern int ov_open_callbacks(void *context, void *datasource, OggVorbis_File *vf, + char *initial, ogg_int32_t ibytes, ov_callbacks callbacks); + +extern int ov_test(void *context, FILE *f,OggVorbis_File *vf,char *initial,ogg_int32_t ibytes); +extern int ov_test_callbacks(void *context, void *datasource, OggVorbis_File *vf, + char *initial, ogg_int32_t ibytes, ov_callbacks callbacks); +extern int ov_test_open(void *context, OggVorbis_File *vf); + +extern ogg_int32_t ov_bitrate(OggVorbis_File *vf,int i); +extern ogg_int32_t ov_bitrate_instant(OggVorbis_File *vf); +extern ogg_int32_t ov_streams(OggVorbis_File *vf); +extern ogg_int32_t ov_seekable(OggVorbis_File *vf); +extern ogg_int32_t ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern float ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(void *context, OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(void *context, OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(void *context, OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(void *context, OggVorbis_File *vf,float pos); +extern int ov_time_seek_page(void *context, OggVorbis_File *vf,float pos); + +extern int ov_raw_seek_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_lap(void *context, OggVorbis_File *vf,float pos); +extern int ov_time_seek_page_lap(void *context, OggVorbis_File *vf,float pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern float ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern ogg_int32_t ov_read_float(void *context, OggVorbis_File *vf,float ***pcm_channels,int samples, + int *bitstream); +extern ogg_int32_t ov_read_filter(void *context, OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream, + void (*filter)(float **pcm,ogg_int32_t channels,ogg_int32_t samples,void *filter_param),void *filter_param); +extern ogg_int32_t ov_read(void *context, OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream); +extern int ov_crosslap(void *context, OggVorbis_File *vf1,OggVorbis_File *vf2); + +extern int ov_halfrate(void *context, OggVorbis_File *vf,int flag); +extern int ov_halfrate_p(OggVorbis_File *vf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/lib/ogg_vorbis/vorbis/lib/backends.h b/lib/ogg_vorbis/vorbis/lib/backends.h new file mode 100755 index 0000000..b0f6f04 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/backends.h @@ -0,0 +1,143 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis backend and mapping structures; needed for + static mode headers + last mod: $Id: backends.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +/* this is exposed up here because we need it for static modes. + Lookups for each backend aren't exposed because there's no reason + to do so */ + +#ifndef _vorbis_backend_h_ +#define _vorbis_backend_h_ + +#include "codec_internal.h" + +/* this would all be simpler/shorter with templates, but.... */ +/* Floor backend generic *****************************************/ +typedef struct{ + void (*pack) (vorbis_info_floor *,oggpack_buffer *); + vorbis_info_floor *(*unpack)(void *context, vorbis_info *,oggpack_buffer *); + vorbis_look_floor *(*look) (void *context, vorbis_dsp_state *,vorbis_info_floor *); + void (*free_info) (void *context, vorbis_info_floor *); + void (*free_look) (void *context, vorbis_look_floor *); + void *(*inverse1) (void *context, struct vorbis_block *,vorbis_look_floor *); + int (*inverse2) (void *context, struct vorbis_block *,vorbis_look_floor *, + void *buffer,float *); +} vorbis_func_floor; + +typedef struct{ + int order; + ogg_int32_t rate; + ogg_int32_t barkmap; + + int ampbits; + int ampdB; + + int numbooks; /* <= 16 */ + int books[16]; + +// float lessthan; /* encode-only config setting hacks for libvorbis */ +// float greaterthan; /* encode-only config setting hacks for libvorbis */ + +} vorbis_info_floor0; + + +#define VIF_POSIT 63 +#define VIF_CLASS 16 +#define VIF_PARTS 31 +typedef struct{ + int partitions; /* 0 to 31 */ + int partitionclass[VIF_PARTS]; /* 0 to 15 */ + + int class_dim[VIF_CLASS]; /* 1 to 8 */ + int class_subs[VIF_CLASS]; /* 0,1,2,3 (bits: 1< +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" + +#include "window.h" +#include "mdct.h" +#include "lpc.h" +#include "registry.h" +#include "misc.h" + +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* pcm accumulator examples (not exhaustive): + + <-------------- lW ----------------> + <--------------- W ----------------> +: .....|..... _______________ | +: .''' | '''_--- | |\ | +:.....''' |_____--- '''......| | \_______| +:.................|__________________|_______|__|______| + |<------ Sl ------>| > Sr < |endW + |beginSl |endSl | |endSr + |beginW |endlW |beginSr + + + |< lW >| + <--------------- W ----------------> + | | .. ______________ | + | | ' `/ | ---_ | + |___.'___/`. | ---_____| + |_______|__|_______|_________________| + | >|Sl|< |<------ Sr ----->|endW + | | |endSl |beginSr |endSr + |beginW | |endlW + mult[0] |beginSl mult[n] + + <-------------- lW -----------------> + |<--W-->| +: .............. ___ | | +: .''' |`/ \ | | +:.....''' |/`....\|...| +:.........................|___|___|___| + |Sl |Sr |endW + | | |endSr + | |beginSr + | |endSl + |beginSl + |beginW +*/ + +/* block abstraction setup *********************************************/ + +#ifndef WORD_ALIGN +#define WORD_ALIGN 8 +#endif + +int FMOD_vorbis_block_init(void *context, vorbis_dsp_state *v, vorbis_block *vb){ + int i = 0; + FMOD_memset(vb,0,sizeof(*vb)); + vb->vd=v; + vb->localalloc=0; + vb->localstore=NULL; + + //if(v->analysisp){ + // vorbis_block_internal *vbi= + // vb->internal=_ogg_calloc(1,sizeof(vorbis_block_internal)); + // if (!vb->internal) + // { + // return -1; + // } + // vbi->ampmax=-9999; + + // for(i=0;ipacketblob[i]=&vb->opb; + // }else + // { + // vbi->packetblob[i]= _ogg_calloc(1,sizeof(oggpack_buffer)); + // if (!vbi->packetblob[i]) + // { + // return -1; + // } + // } + // FMOD_oggpack_writeinit(context, vbi->packetblob[i]); + // } + //} + return(0); +} + +void *_FMOD_vorbis_block_alloc(void *context, vorbis_block *vb,ogg_int32_t bytes){ + bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); + if(bytes+vb->localtop>vb->localalloc){ + /* can't just _ogg_realloc... there are outstanding pointers */ + if(vb->localstore){ + struct alloc_chain *link=_ogg_malloc(sizeof(*link)); + if (!link) + { + return 0; + } + vb->totaluse+=vb->localtop; + link->next=vb->reap; + link->ptr=vb->localstore; + vb->reap=link; + } + /* highly conservative */ + vb->localalloc=bytes; + vb->localstore=_ogg_malloc(vb->localalloc); + if (!vb->localstore) + { + return 0; + } + vb->localtop=0; + } + { + void *ret=(void *)(((char *)vb->localstore)+vb->localtop); + vb->localtop+=bytes; + return ret; + } +} + +/* reap the chain, pull the ripcord */ +int _FMOD_vorbis_block_ripcord(void *context, vorbis_block *vb){ + int ret = 0; + /* reap the chain */ + struct alloc_chain *reap=vb->reap; + while(reap){ + struct alloc_chain *next=reap->next; + _ogg_free(reap->ptr); + FMOD_memset(reap,0,sizeof(*reap)); + _ogg_free(reap); + reap=next; + } + /* consolidate storage */ + if(vb->totaluse){ + vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); + if (!vb->localstore) ret = OV_EMEMORY; + vb->localalloc+=vb->totaluse; + vb->totaluse=0; + } + + /* pull the ripcord */ + vb->localtop=0; + vb->reap=NULL; + + return ret; +} + +int FMOD_vorbis_block_clear(void *context, vorbis_block *vb){ +// int i; +// vorbis_block_internal *vbi=vb->internal; + int ret; + + ret = _FMOD_vorbis_block_ripcord(context, vb); + if (ret) + { + return ret; + } + + if(vb->localstore)_ogg_free(vb->localstore); + + //if(vbi){ + // for(i=0;ipacketblob[i]); + // if(i!=PACKETBLOBS/2)_ogg_free(vbi->packetblob[i]); + // } + // _ogg_free(vbi); + //} + + FMOD_memset(vb,0,sizeof(*vb)); + return(0); +} + +/* Analysis side code, but directly related to blocking. Thus it's + here and not in analysis.c (which is for analysis transforms only). + The init is here because some of it is shared */ + +static int _vds_shared_init(void *context, vorbis_dsp_state *v,vorbis_info *vi,int encp){ + int i; + codec_setup_info *ci=vi->codec_setup; + private_state *b=NULL; + int hs; + + if(ci==NULL) return 1; + hs=ci->halfrate_flag; + + FMOD_memset(v,0,sizeof(*v)); + b=v->backend_state=_ogg_calloc(1,sizeof(*b)); + if (!b) + { + return OV_EMEMORY; + } + + v->vi=vi; + b->modebits=ilog2(ci->modes); + + if (!(b->transform[0]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[0])))) + { + return(OV_EMEMORY); + } + if (!(b->transform[1]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[1])))) + { + return(OV_EMEMORY); + } + + /* MDCT is tranform 0 */ + + if (!(b->transform[0][0]=_ogg_calloc(1,sizeof(mdct_lookup)))) + { + return(OV_EMEMORY); + } + if (!(b->transform[1][0]=_ogg_calloc(1,sizeof(mdct_lookup)))) + { + return(OV_EMEMORY); + } + if(FMOD_mdct_init(context, b->transform[0][0],ci->blocksizes[0]>>hs)) + { + return(OV_EMEMORY); + } + if(FMOD_mdct_init(context, b->transform[1][0],ci->blocksizes[1]>>hs)) + { + return(OV_EMEMORY); + } + + /* Vorbis I uses only window type 0 */ + b->window[0]=ilog2(ci->blocksizes[0])-6; + b->window[1]=ilog2(ci->blocksizes[1])-6; + + //if(encp){ /* encode/decode differ here */ + + // /* analysis always needs an fft */ + // drft_init(&b->fft_look[0],ci->blocksizes[0]); + // drft_init(&b->fft_look[1],ci->blocksizes[1]); + + // /* finish the codebooks */ + // if(!ci->fullbooks){ + // ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + // for(i=0;ibooks;i++) + // vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); + // } + + // b->psy=_ogg_calloc(ci->psys,sizeof(*b->psy)); + // for(i=0;ipsys;i++){ + // _vp_psy_init(b->psy+i, + // ci->psy_param[i], + // &ci->psy_g_param, + // ci->blocksizes[ci->psy_param[i]->blockflag]/2, + // vi->rate); + // } + + // v->analysisp=1; + //}else{ + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + if (!ci->fullbooks) + { + return(OV_EMEMORY); + } + for(i=0;ibooks;i++){ + if (FMOD_vorbis_book_init_decode(context, ci->fullbooks+i,ci->book_param[i])) + { + return(OV_EMEMORY); + } + /* decode codebooks are now standalone after init */ + FMOD_vorbis_staticbook_destroy(context, ci->book_param[i]); + ci->book_param[i]=NULL; + } + } +// } + + /* initialize the storage vectors. blocksize[1] is small for encode, + but the correct size for decode */ + v->pcm_storage=ci->blocksizes[1]; + v->pcm=_ogg_malloc(vi->channels*sizeof(*v->pcm)); + if (!v->pcm) + { + return OV_EMEMORY; + } + v->pcmret=_ogg_malloc(vi->channels*sizeof(*v->pcmret)); + if (!v->pcmret) + { + return OV_EMEMORY; + } + { + int i; + for(i=0;ichannels;i++) + { + v->pcm[i]=_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); + if (!v->pcm[i]) + { + return OV_EMEMORY; + } + } + } + + /* all 1 (large block) or 0 (small block) */ + /* explicitly set for the sake of clarity */ + v->lW=0; /* previous window size */ + v->W=0; /* current window size */ + + /* all vector indexes */ + v->centerW=ci->blocksizes[1]/2; + + v->pcm_current=v->centerW; + + /* initialize all the backend lookups */ + b->flr=_ogg_calloc(ci->floors,sizeof(*b->flr)); + if (!b->flr) + { + return OV_EMEMORY; + } + + b->residue=_ogg_calloc(ci->residues,sizeof(*b->residue)); + if (!b->residue) + { + return OV_EMEMORY; + } + + for(i=0;ifloors;i++) + b->flr[i]=_FMOD_floor_P[ci->floor_type[i]]-> + look(context, v,ci->floor_param[i]); + + for(i=0;iresidues;i++) + b->residue[i]=_FMOD_residue_P[ci->residue_type[i]]-> + look(context, v,ci->residue_param[i]); + + return 0; +} + + +/* arbitrary settings and spec-mandated numbers get filled in here */ +//int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ +// private_state *b=NULL; +// +// if(_vds_shared_init(v,vi,1))return 1; +// b=v->backend_state; +// b->psy_g_look=_vp_global_look(vi); +// +// /* Initialize the envelope state storage */ +// b->ve=_ogg_calloc(1,sizeof(*b->ve)); +// _ve_envelope_init(b->ve,vi); +// +// FMOD_vorbis_bitrate_init(vi,&b->bms); +// +// /* compressed audio packets start after the headers +// with sequence number 3 */ +// v->sequence=3; +// +// return(0); +//} + +void FMOD_vorbis_dsp_clear(void *context, vorbis_dsp_state *v){ + int i; + if(v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(vi?vi->codec_setup:NULL); + private_state *b=v->backend_state; + + if(b){ + + //if(b->ve){ + // _ve_envelope_clear(context, b->ve); + // _ogg_free(b->ve); + //} + + if(b->transform[0]){ + FMOD_mdct_clear(context, b->transform[0][0]); + _ogg_free(b->transform[0][0]); + _ogg_free(b->transform[0]); + } + if(b->transform[1]){ + FMOD_mdct_clear(context, b->transform[1][0]); + _ogg_free(b->transform[1][0]); + _ogg_free(b->transform[1]); + } + + if(b->flr){ + if(ci) + for(i=0;ifloors;i++) + _FMOD_floor_P[ci->floor_type[i]]-> + free_look(context, b->flr[i]); + _ogg_free(b->flr); + } + if(b->residue){ + if(ci) + for(i=0;iresidues;i++) + _FMOD_residue_P[ci->residue_type[i]]-> + free_look(context, b->residue[i]); + _ogg_free(b->residue); + } +// if(b->psy){ +// if(ci) +// for(i=0;ipsys;i++) +// _vp_psy_clear(context, b->psy+i); +// _ogg_free(b->psy); +// } + +// if(b->psy_g_look)_vp_global_free(context, b->psy_g_look); +// FMOD_vorbis_bitrate_clear(&b->bms); + +// drft_clear(context, &b->fft_look[0]); +// drft_clear(context, &b->fft_look[1]); + + } + + if(v->pcm){ + if(vi) + for(i=0;ichannels;i++) + if(v->pcm[i])_ogg_free(v->pcm[i]); + _ogg_free(v->pcm); + if(v->pcmret)_ogg_free(v->pcmret); + } + + if(b){ + /* free header, header1, header2 */ + //if(b->header)_ogg_free(b->header); + //if(b->header1)_ogg_free(b->header1); + //if(b->header2)_ogg_free(b->header2); + _ogg_free(b); + } + + FMOD_memset(v,0,sizeof(*v)); + } +} + +//float **FMOD_vorbis_analysis_buffer(void *context, vorbis_dsp_state *v, int vals){ +// int i; +// vorbis_info *vi=v->vi; +// private_state *b=v->backend_state; +// +// /* free header, header1, header2 */ +// if(b->header)_ogg_free(b->header);b->header=NULL; +// if(b->header1)_ogg_free(b->header1);b->header1=NULL; +// if(b->header2)_ogg_free(b->header2);b->header2=NULL; +// +// /* Do we have enough storage space for the requested buffer? If not, +// expand the PCM (and envelope) storage */ +// +// if(v->pcm_current+vals>=v->pcm_storage){ +// v->pcm_storage=v->pcm_current+vals*2; +// +// for(i=0;ichannels;i++){ +// v->pcm[i]=_ogg_realloc(v->pcm[i],v->pcm_storage*sizeof(*v->pcm[i])); +// } +// } +// +// for(i=0;ichannels;i++) +// v->pcmret[i]=v->pcm[i]+v->pcm_current; +// +// return(v->pcmret); +//} +// +//static void _preextrapolate_helper(vorbis_dsp_state *v){ +// int i; +// int order=16; +// float *lpc=alloca(order*sizeof(*lpc)); +// float *work=alloca(v->pcm_current*sizeof(*work)); +// ogg_int32_t j; +// v->preextrapolate=1; +// +// if(v->pcm_current-v->centerW>order*2){ /* safety */ +// for(i=0;ivi->channels;i++){ +// /* need to run the extrapolation in reverse! */ +// for(j=0;jpcm_current;j++) +// work[j]=v->pcm[i][v->pcm_current-j-1]; +// +// /* prime as above */ +// vorbis_lpc_from_data(work,lpc,v->pcm_current-v->centerW,order); +// +//#if 0 +// if(v->vi->channels==2){ +// if(i==0) +// _analysis_output("predataL",0,work,v->pcm_current-v->centerW,0,0,0); +// else +// _analysis_output("predataR",0,work,v->pcm_current-v->centerW,0,0,0); +// }else{ +// _analysis_output("predata",0,work,v->pcm_current-v->centerW,0,0,0); +// } +//#endif +// +// /* run the predictor filter */ +// vorbis_lpc_predict(lpc,work+v->pcm_current-v->centerW-order, +// order, +// work+v->pcm_current-v->centerW, +// v->centerW); +// +// for(j=0;jpcm_current;j++) +// v->pcm[i][v->pcm_current-j-1]=work[j]; +// +// } +// } +//} +// +// +///* call with val<=0 to set eof */ +// +//int FMOD_vorbis_analysis_wrote(void *context, vorbis_dsp_state *v, int vals){ +// vorbis_info *vi=v->vi; +// codec_setup_info *ci=vi->codec_setup; +// +// if(vals<=0){ +// int order=32; +// int i; +// float *lpc=alloca(order*sizeof(*lpc)); +// +// /* if it wasn't done earlier (very short sample) */ +// if(!v->preextrapolate) +// _preextrapolate_helper(v); +// +// /* We're encoding the end of the stream. Just make sure we have +// [at least] a few full blocks of zeroes at the end. */ +// /* actually, we don't want zeroes; that could drop a large +// amplitude off a cliff, creating spread spectrum noise that will +// suck to encode. Extrapolate for the sake of cleanliness. */ +// +// FMOD_vorbis_analysis_buffer(context, v,ci->blocksizes[1]*3); +// v->eofflag=v->pcm_current; +// v->pcm_current+=ci->blocksizes[1]*3; +// +// for(i=0;ichannels;i++){ +// if(v->eofflag>order*2){ +// /* extrapolate with LPC to fill in */ +// ogg_int32_t n; +// +// /* make a predictor filter */ +// n=v->eofflag; +// if(n>ci->blocksizes[1])n=ci->blocksizes[1]; +// vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order); +// +// /* run the predictor filter */ +// vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order, +// v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag); +// }else{ +// /* not enough data to extrapolate (unlikely to happen due to +// guarding the overlap, but bulletproof in case that +// assumtion goes away). zeroes will do. */ +// FMOD_memset(v->pcm[i]+v->eofflag,0, +// (v->pcm_current-v->eofflag)*sizeof(*v->pcm[i])); +// +// } +// } +// }else{ +// +// if(v->pcm_current+vals>v->pcm_storage) +// return(OV_EINVAL); +// +// v->pcm_current+=vals; +// +// /* we may want to reverse extrapolate the beginning of a stream +// too... in case we're beginning on a cliff! */ +// /* clumsy, but simple. It only runs once, so simple is good. */ +// if(!v->preextrapolate && v->pcm_current-v->centerW>ci->blocksizes[1]) +// _preextrapolate_helper(v); +// +// } +// return(0); +//} +// +///* do the deltas, envelope shaping, pre-echo and determine the size of +// the next block on which to continue analysis */ +//int FMOD_vorbis_analysis_blockout(void *context, vorbis_dsp_state *v,vorbis_block *vb){ +// int i; +// vorbis_info *vi=v->vi; +// codec_setup_info *ci=vi->codec_setup; +// private_state *b=v->backend_state; +// vorbis_look_psy_global *g=b->psy_g_look; +// ogg_int32_t beginW=v->centerW-ci->blocksizes[v->W]/2,centerNext; +// vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; +// +// /* check to see if we're started... */ +// if(!v->preextrapolate)return(0); +// +// /* check to see if we're done... */ +// if(v->eofflag==-1)return(0); +// +// /* By our invariant, we have lW, W and centerW set. Search for +// the next boundary so we can determine nW (the next window size) +// which lets us compute the shape of the current block's window */ +// +// /* we do an envelope search even on a single blocksize; we may still +// be throwing more bits at impulses, and envelope search handles +// marking impulses too. */ +// { +// ogg_int32_t bp=_ve_envelope_search(context, v); +// if(bp==-1){ +// +// if(v->eofflag==0)return(0); /* not enough data currently to search for a +// full long block */ +// v->nW=0; +// }else{ +// +// if(ci->blocksizes[0]==ci->blocksizes[1]) +// v->nW=0; +// else +// v->nW=bp; +// } +// } +// +// centerNext=v->centerW+ci->blocksizes[v->W]/4+ci->blocksizes[v->nW]/4; +// +// { +// /* center of next block + next block maximum right side. */ +// +// ogg_int32_t blockbound=centerNext+ci->blocksizes[v->nW]/2; +// if(v->pcm_currentlW=v->lW; +// vb->W=v->W; +// vb->nW=v->nW; +// +// if(v->W){ +// if(!v->lW || !v->nW){ +// vbi->blocktype=BLOCKTYPE_TRANSITION; +// /*fprintf(stderr,"-");*/ +// }else{ +// vbi->blocktype=BLOCKTYPE_LONG; +// /*fprintf(stderr,"_");*/ +// } +// }else{ +// if(_ve_envelope_mark(v)){ +// vbi->blocktype=BLOCKTYPE_IMPULSE; +// /*fprintf(stderr,"|");*/ +// +// }else{ +// vbi->blocktype=BLOCKTYPE_PADDING; +// /*fprintf(stderr,".");*/ +// +// } +// } +// +// vb->vd=v; +// vb->sequence=v->sequence++; +// vb->granulepos=v->granulepos; +// vb->pcmend=ci->blocksizes[v->W]; +// +// /* copy the vectors; this uses the local storage in vb */ +// +// /* this tracks 'strongest peak' for later psychoacoustics */ +// /* moved to the global psy state; clean this mess up */ +// if(vbi->ampmax>g->ampmax)g->ampmax=vbi->ampmax; +// g->ampmax=_vp_ampmax_decay(g->ampmax,v); +// vbi->ampmax=g->ampmax; +// +// vb->pcm=_FMOD_vorbis_block_alloc(context, vb,sizeof(*vb->pcm)*vi->channels); +// if (!vb->pcm) +// { +// return OV_EMEMORY; +// } +// vbi->pcmdelay=_FMOD_vorbis_block_alloc(context, vb,sizeof(*vbi->pcmdelay)*vi->channels); +// for(i=0;ichannels;i++){ +// vbi->pcmdelay[i]= _FMOD_vorbis_block_alloc(context, vb,(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); +// if (vbi->pcmdelay[i]) +// { +// return OV_EMEMORY; +// } +// FMOD_memcpy(vbi->pcmdelay[i],v->pcm[i],(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); +// vb->pcm[i]=vbi->pcmdelay[i]+beginW; +// +// /* before we added the delay +// vb->pcm[i]=__FMOD_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); +// FMOD_memcpy(vb->pcm[i],v->pcm[i]+beginW,ci->blocksizes[v->W]*sizeof(*vb->pcm[i])); +// */ +// +// } +// +// /* handle eof detection: eof==0 means that we've not yet received EOF +// eof>0 marks the last 'real' sample in pcm[] +// eof<0 'no more to do'; doesn't get here */ +// +// if(v->eofflag){ +// if(v->centerW>=v->eofflag){ +// v->eofflag=-1; +// vb->eofflag=1; +// return(1); +// } +// } +// +// /* advance storage vectors and clean up */ +// { +// int new_centerNext=ci->blocksizes[1]/2; +// int movementW=centerNext-new_centerNext; +// +// if(movementW>0){ +// +// _ve_envelope_shift(b->ve,movementW); +// v->pcm_current-=movementW; +// +// for(i=0;ichannels;i++) +// memmove(v->pcm[i],v->pcm[i]+movementW, +// v->pcm_current*sizeof(*v->pcm[i])); +// +// +// v->lW=v->W; +// v->W=v->nW; +// v->centerW=new_centerNext; +// +// if(v->eofflag){ +// v->eofflag-=movementW; +// if(v->eofflag<=0)v->eofflag=-1; +// /* do not add padding to end of stream! */ +// if(v->centerW>=v->eofflag){ +// v->granulepos+=movementW-(v->centerW-v->eofflag); +// }else{ +// v->granulepos+=movementW; +// } +// }else{ +// v->granulepos+=movementW; +// } +// } +// } +// +// /* done */ +// return(1); +//} + +int FMOD_vorbis_synthesis_restart(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci; + int hs; + + if(!v->backend_state)return -1; + if(!vi)return -1; + ci=vi->codec_setup; + if(!ci)return -1; + hs=ci->halfrate_flag; + + v->centerW=ci->blocksizes[1]>>(hs+1); + v->pcm_current=v->centerW>>hs; + + v->pcm_returned=-1; + v->granulepos=-1; + v->sequence=-1; + v->eofflag=0; + ((private_state *)(v->backend_state))->sample_count=-1; + + return(0); +} + +int FMOD_vorbis_synthesis_init(void *context, vorbis_dsp_state *v,vorbis_info *vi){ + int ret = _vds_shared_init(context, v,vi,0); + + if (ret){ + FMOD_vorbis_dsp_clear(context, v); + return ret; + } + FMOD_vorbis_synthesis_restart(v); + return 0; +} + +/* Unlike in analysis, the window is only partially applied for each + block. The time domain envelope is not yet handled at the point of + calling (as it relies on the previous block). */ + +int FMOD_vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=v->backend_state; + int hs=ci->halfrate_flag; + int i,j; + + if(!vb)return(OV_EINVAL); + if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); + + v->lW=v->W; + v->W=vb->W; + v->nW=-1; + + if((v->sequence==-1)|| + (v->sequence+1 != vb->sequence)){ + v->granulepos=-1; /* out of sequence; lose count */ + b->sample_count=-1; + } + + v->sequence=vb->sequence; + + if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly + was called on block */ + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + + int thisCenter; + int prevCenter; + + v->glue_bits+=vb->glue_bits; + v->time_bits+=vb->time_bits; + v->floor_bits+=vb->floor_bits; + v->res_bits+=vb->res_bits; + + if(v->centerW){ + thisCenter=n1; + prevCenter=0; + }else{ + thisCenter=0; + prevCenter=n1; + } + + /* v->pcm is now used like a two-stage double buffer. We don't want + to have to constantly shift *or* adjust memory usage. Don't + accept a new block until the old is shifted out */ + + for(j=0;jchannels;j++){ + /* the overlap/add section */ + if(v->lW){ + if(v->W){ + /* large/large */ + float *w=_FMOD_vorbis_window_get(b->window[1]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; + float *p=vb->pcm[j]; + for(i=0;iW){ + /* small/large */ + float *w=_FMOD_vorbis_window_get(b->window[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]+n1/2-n0/2; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;ipcm[j]+thisCenter; + float *p=vb->pcm[j]+n; + for(i=0;icenterW) + v->centerW=0; + else + v->centerW=n1; + + /* deal with initial packet state; we do this using the explicit + pcm_returned==-1 flag otherwise we're sensitive to first block + being short or long */ + + if(v->pcm_returned==-1){ + v->pcm_returned=thisCenter; + v->pcm_current=thisCenter; + }else{ + v->pcm_returned=prevCenter; + v->pcm_current=prevCenter+ + ((ci->blocksizes[v->lW]/4+ + ci->blocksizes[v->W]/4)>>hs); + } + + } + + /* track the frame number... This is for convenience, but also + making sure our last packet doesn't end with added padding. If + the last packet is partial, the number of samples we'll have to + return will be past the vb->granulepos. + + This is not foolproof! It will be confused if we begin + decoding at the last page after a seek or hole. In that case, + we don't have a starting point to judge where the last frame + is. For this reason, vorbisfile will always try to make sure + it reads the last two marked pages in proper sequence */ + + if(b->sample_count==-1){ + b->sample_count=0; + }else{ + b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + } + + if(v->granulepos==-1){ + if(vb->granulepos!=-1){ /* only set if we have a position to set to */ + + v->granulepos=vb->granulepos; + + /* is this a short page? */ + if(b->sample_count>v->granulepos){ + /* corner case; if this is both the first and last audio page, + then spec says the end is cut, not beginning */ + if(vb->eofflag){ + /* trim the end */ + /* no preceeding granulepos; assume we started at zero (we'd + have to in a short single-page stream) */ + /* granulepos could be -1 due to a seek, but that would result + in a long count, not short count */ + + v->pcm_current-=(int)((b->sample_count-v->granulepos)>>hs); + }else{ + /* trim the beginning */ + v->pcm_returned+=(int)((b->sample_count-v->granulepos)>>hs); + if(v->pcm_returned>v->pcm_current) + v->pcm_returned=v->pcm_current; + } + + } + + } + }else{ + v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ + + if(v->granulepos>vb->granulepos){ + ogg_int32_t extra=(int)(v->granulepos-vb->granulepos); + + if(extra) + if(vb->eofflag){ + /* partial last frame. Strip the extra samples off */ + v->pcm_current-=extra>>hs; + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + v->granulepos=vb->granulepos; + } + } + + /* Update, cleanup */ + + if(vb->eofflag)v->eofflag=1; + return(0); + +} + +/* pcm==NULL indicates we just want the pending samples, no more */ +int FMOD_vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + + if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + return(v->pcm_current-v->pcm_returned); + } + return(0); +} + +int FMOD_vorbis_synthesis_read(vorbis_dsp_state *v,int n){ + if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL); + v->pcm_returned+=n; + return(0); +} + +/* intended for use with a specific vorbisfile feature; we want access + to the [usually synthetic/postextrapolated] buffer and lapping at + the end of a decode cycle, specifically, a half-short-block worth. + This funtion works like pcmout above, except it will also expose + this implicit buffer data not normally decoded. */ +int FMOD_vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + int hs=ci->halfrate_flag; + + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + int i,j; + + if(v->pcm_returned<0)return 0; + + /* our returned data ends at pcm_returned; because the synthesis pcm + buffer is a two-fragment ring, that means our data block may be + fragmented by buffering, wrapping or a short block not filling + out a buffer. To simplify things, we unfragment if it's at all + possibly needed. Otherwise, we'd need to call lapout more than + once as well as hold additional dsp state. Opt for + simplicity. */ + + /* centerW was advanced by blockin; it would be the center of the + *next* block */ + if(v->centerW==n1){ + /* the data buffer wraps; swap the halves */ + /* slow, sure, small */ + for(j=0;jchannels;j++){ + float *p=v->pcm[j]; + for(i=0;ipcm_current-=n1; + v->pcm_returned-=n1; + v->centerW=0; + } + + /* solidify buffer into contiguous space */ + if((v->lW^v->W)==1){ + /* long/short or short/long */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+(n1-n0)/2; + for(i=(n1+n0)/2-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=(n1-n0)/2; + v->pcm_current+=(n1-n0)/2; + }else{ + if(v->lW==0){ + /* short/short */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+n1-n0; + for(i=n0-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=n1-n0; + v->pcm_current+=n1-n0; + } + } + + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + + return(n1+n-v->pcm_returned); + +} + +float *FMOD_vorbis_window(vorbis_dsp_state *v,int W){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + int hs=ci->halfrate_flag; + private_state *b=v->backend_state; + + if(b->window[W]-1<0)return NULL; + return _FMOD_vorbis_window_get(b->window[W]-hs); +} diff --git a/lib/ogg_vorbis/vorbis/lib/codebook.c b/lib/ogg_vorbis/vorbis/lib/codebook.c new file mode 100755 index 0000000..4a892b7 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/codebook.c @@ -0,0 +1,656 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic codebook pack/unpack/code/decode operations + last mod: $Id: codebook.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codebook.h" +#include "scales.h" +#include "misc.h" +#include "os.h" + +/* packs the given codebook into the bitstream **************************/ +#if 0 +int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ + ogg_int32_t i,j; + int ordered=0; + + /* first the basic parameters */ + FMOD_oggpack_write(opb,0x564342,24); + FMOD_oggpack_write(opb,c->dim,16); + FMOD_oggpack_write(opb,c->entries,24); + + /* pack the codewords. There are two packings; length ordered and + length random. Decide between the two now. */ + + for(i=1;ientries;i++) + if(c->lengthlist[i-1]==0 || c->lengthlist[i]lengthlist[i-1])break; + if(i==c->entries)ordered=1; + + if(ordered){ + /* length ordered. We only need to say how many codewords of + each length. The actual codewords are generated + deterministically */ + + ogg_int32_t count=0; + FMOD_oggpack_write(opb,1,1); /* ordered */ + FMOD_oggpack_write(opb,c->lengthlist[0]-1,5); /* 1 to 32 */ + + for(i=1;ientries;i++){ + ogg_int32_t this=c->lengthlist[i]; + ogg_int32_t last=c->lengthlist[i-1]; + if(this>last){ + for(j=last;jentries-count)); + count=i; + } + } + } + FMOD_oggpack_write(opb,i-count,_FMOD_ilog(c->entries-count)); + + }else{ + /* length random. Again, we don't code the codeword itself, just + the length. This time, though, we have to encode each length */ + FMOD_oggpack_write(opb,0,1); /* unordered */ + + /* algortihmic mapping has use for 'unused entries', which we tag + here. The algorithmic mapping happens as usual, but the unused + entry has no codeword. */ + for(i=0;ientries;i++) + if(c->lengthlist[i]==0)break; + + if(i==c->entries){ + FMOD_oggpack_write(opb,0,1); /* no unused entries */ + for(i=0;ientries;i++) + FMOD_oggpack_write(opb,c->lengthlist[i]-1,5); + }else{ + FMOD_oggpack_write(opb,1,1); /* we have unused entries; thus we tag */ + for(i=0;ientries;i++){ + if(c->lengthlist[i]==0){ + FMOD_oggpack_write(opb,0,1); + }else{ + FMOD_oggpack_write(opb,1,1); + FMOD_oggpack_write(opb,c->lengthlist[i]-1,5); + } + } + } + } + + /* is the entry number the desired return value, or do we have a + mapping? If we have a mapping, what type? */ + FMOD_oggpack_write(opb,c->maptype,4); + switch(c->maptype){ + case 0: + /* no mapping */ + break; + case 1:case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + if(!c->quantlist){ + /* no quantlist? error */ + return(-1); + } + + /* values that define the dequantization */ + FMOD_oggpack_write(opb,c->q_min,32); + FMOD_oggpack_write(opb,c->q_delta,32); + FMOD_oggpack_write(opb,c->q_quant-1,4); + FMOD_oggpack_write(opb,c->q_sequencep,1); + + { + int quantvals; + switch(c->maptype){ + case 1: + /* a single column of (c->entries/c->dim) quantized values for + building a full value list algorithmically (square lattice) */ + quantvals=_FMOD_book_maptype1_quantvals(c); + break; + case 2: + /* every value (c->entries*c->dim total) specified explicitly */ + quantvals=c->entries*c->dim; + break; + default: /* NOT_REACHABLE */ + quantvals=-1; + } + + /* quantized values */ + for(i=0;iquantlist[i]),c->q_quant); + + } + break; + default: + /* error case; we don't have any other map types now */ + return(-1); + } + + return(0); +} +#endif + +/* unpacks a codebook from the packet buffer into the codebook struct, + readies the codebook auxiliary structures for decode *************/ +int FMOD_vorbis_staticbook_unpack(void *context, oggpack_buffer *opb,static_codebook *s){ + ogg_int32_t i,j; + int err = -1; + + FMOD_memset(s,0,sizeof(*s)); + s->allocedp=1; + + /* make sure alignment is correct */ + if(FMOD_oggpack_read(opb,24)!=0x564342)goto _eofout; + + /* first the basic parameters */ + s->dim=FMOD_oggpack_read(opb,16); + s->entries=FMOD_oggpack_read(opb,24); + if(s->entries==-1)goto _eofout; + + if(_FMOD_ilog(s->dim)+_FMOD_ilog(s->entries)>24)goto _eofout; + + /* codeword ordering.... length ordered or unordered? */ + switch((int)FMOD_oggpack_read(opb,1)){ + case 0: + /* unordered */ + s->lengthlist=_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + if (!s->lengthlist) + { + err = OV_EMEMORY; + goto _errout; + } + + /* allocated but unused entries? */ + if(FMOD_oggpack_read(opb,1)){ + /* yes, unused entries */ + + for(i=0;ientries;i++){ + if(FMOD_oggpack_read(opb,1)){ + ogg_int32_t num=FMOD_oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + }else + s->lengthlist[i]=0; + } + }else{ + /* all entries used; no tagging */ + for(i=0;ientries;i++){ + ogg_int32_t num=FMOD_oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + } + } + + break; + case 1: + /* ordered */ + { + ogg_int32_t length=FMOD_oggpack_read(opb,5)+1; + s->lengthlist=_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + if (!s->lengthlist) + { + err = OV_EMEMORY; + goto _errout; + } + for(i=0;ientries;){ + ogg_int32_t num=FMOD_oggpack_read(opb,_FMOD_ilog(s->entries-i)); + if(num==-1)goto _eofout; + for(j=0;jentries;j++,i++) + s->lengthlist[i]=length; + length++; + } + } + break; + default: + /* EOF */ + return(-1); + } + + /* Do we have a mapping to unpack? */ + switch((s->maptype=FMOD_oggpack_read(opb,4))){ + case 0: + /* no mapping */ + break; + case 1: case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + s->q_min=FMOD_oggpack_read(opb,32); + s->q_delta=FMOD_oggpack_read(opb,32); + s->q_quant=FMOD_oggpack_read(opb,4)+1; + s->q_sequencep=FMOD_oggpack_read(opb,1); + if(s->q_sequencep==-1)goto _eofout; + + { + int quantvals=0; + switch(s->maptype){ + case 1: + quantvals=(s->dim==0?0:_FMOD_book_maptype1_quantvals(s)); + break; + case 2: + quantvals=s->entries*s->dim; + break; + } + + /* quantized values */ + s->quantlist=_ogg_malloc(sizeof(*s->quantlist)*quantvals); + if (!s->quantlist) + { + err = OV_EMEMORY; + goto _errout; + } + for(i=0;iquantlist[i]=FMOD_oggpack_read(opb,s->q_quant); + + if(quantvals&&s->quantlist[quantvals-1]==-1)goto _eofout; + } + break; + default: + goto _errout; + } + + /* all set */ + return(0); + + _errout: + _eofout: + FMOD_vorbis_staticbook_clear(context, s); + return err; +} + +/* returns the number of bits ************************************************/ +#if 0 +int FMOD_vorbis_book_encode(codebook *book, int a, oggpack_buffer *b){ + if(a<0 || a>=book->c->entries)return(0); + FMOD_oggpack_write(b,book->codelist[a],book->c->lengthlist[a]); + return(book->c->lengthlist[a]); +} + +/* One the encode side, our vector writers are each designed for a +specific purpose, and the encoder is not flexible without modification: + +The LSP vector coder uses a single stage nearest-match with no +interleave, so no step and no error return. This is specced by floor0 +and doesn't change. + +Residue0 encoding interleaves, uses multiple stages, and each stage +peels of a specific amount of resolution from a lattice (thus we want +to match by threshold, not nearest match). Residue doesn't *have* to +be encoded that way, but to change it, one will need to add more +infrastructure on the encode side (decode side is specced and simpler) */ + +/* floor0 LSP (single stage, non interleaved, nearest match) */ +/* returns entry number and *modifies a* to the quantization value *****/ +int vorbis_book_errorv(codebook *book,float *a){ + int dim=book->dim,k; + int best=_FMOD_best(book,a,1); + for(k=0;kvaluelist+best*dim)[k]; + return(best); +} + +/* returns the number of bits and *modifies a* to the quantization value *****/ +int FMOD_vorbis_book_encodev(codebook *book,int best,float *a,oggpack_buffer *b){ + int k,dim=book->dim; + for(k=0;kvaluelist+best*dim)[k]; + return(FMOD_vorbis_book_encode(book,best,b)); +} +#endif + +/* the 'eliminate the decode tree' optimization actually requires the + codewords to be MSb first, not LSb. This is an annoying inelegancy + (and one of the first places where carefully thought out design + turned out to be wrong; Vorbis II and future Ogg codecs should go + to an MSb bitpacker), but not actually the huge hit it appears to + be. The first-stage decode table catches most words so that + bitreverse is not in the main execution path. */ + +static ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffff) | ((x<<16)&0xffff0000); + x= ((x>> 8)&0x00ff00ff) | ((x<< 8)&0xff00ff00); + x= ((x>> 4)&0x0f0f0f0f) | ((x<< 4)&0xf0f0f0f0); + x= ((x>> 2)&0x33333333) | ((x<< 2)&0xcccccccc); + return((x>> 1)&0x55555555) | ((x<< 1)&0xaaaaaaaa); +} + +STIN ogg_int32_t decode_packed_entry_number(codebook *book, oggpack_buffer *b){ + int read=book->dec_maxlength; + ogg_int32_t lo,hi; + ogg_int32_t lok = FMOD_oggpack_look(b,book->dec_firsttablen); + + if (lok >= 0) { + ogg_int32_t entry = book->dec_firsttable[lok]; + if(entry&0x80000000){ + lo=(entry>>15)&0x7fff; + hi=book->used_entries-(entry&0x7fff); + }else{ + FMOD_oggpack_adv(b, book->dec_codelengths[entry-1]); + return(entry-1); + } + }else{ + lo=0; + hi=book->used_entries; + } + + lok = FMOD_oggpack_look(b, read); + + while(lok<0 && read>1) + lok = FMOD_oggpack_look(b, --read); + if(lok<0)return -1; + + /* bisect search for the codeword in the ordered list */ + { + ogg_uint32_t testword=bitreverse((ogg_uint32_t)lok); + + while(hi-lo>1){ + ogg_int32_t p=(hi-lo)>>1; + ogg_int32_t test=book->codelist[lo+p]>testword; + lo+=p&(test-1); + hi-=p&(-test); + } + + if(book->dec_codelengths[lo]<=read){ + FMOD_oggpack_adv(b, book->dec_codelengths[lo]); + return(lo); + } + } + + FMOD_oggpack_adv(b, read); + + return(-1); +} + +/* Decode side is specced and easier, because we don't need to find + matches using different criteria; we simply read and map. There are + two things we need to do 'depending': + + We may need to support interleave. We don't really, but it's + convenient to do it here rather than rebuild the vector later. + + Cascades may be additive or multiplicitive; this is not inherent in + the codebook, but set in the code using the codebook. Like + interleaving, it's easiest to do it here. + addmul==0 -> declarative (set the value) + addmul==1 -> additive + addmul==2 -> multiplicitive */ + +/* returns the [original, not compacted] entry number or -1 on eof *********/ +ogg_int32_t FMOD_vorbis_book_decode(codebook *book, oggpack_buffer *b){ + if(book->used_entries>0){ + ogg_int32_t packed_entry=decode_packed_entry_number(book,b); + if(packed_entry>=0) + return(book->dec_index[packed_entry]); + } + + /* if there's no dec_index, the codebook unpacking isn't collapsed */ + return(-1); +} + +/* returns 0 on OK or -1 on eof *************************************/ +ogg_int32_t FMOD_vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){ + if(book->used_entries>0){ + int step=n/book->dim; + ogg_int32_t *entry = alloca(sizeof(*entry)*step); + float **t = alloca(sizeof(*t)*step); + int i,j,o; + + for (i = 0; i < step; i++) { + entry[i]=decode_packed_entry_number(book,b); + if(entry[i]==-1)return(-1); + t[i] = book->valuelist+entry[i]*book->dim; + } + for(i=0,o=0;idim;i++,o+=step) + for (j=0;jused_entries>0){ + int i,j,entry; + float *t; + + if(book->dim>8){ + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]+=t[j++]; + } + }else{ + for(i=0;ivaluelist+entry*book->dim; + j=0; + switch((int)book->dim){ + case 8: + a[i++]+=t[j++]; + case 7: + a[i++]+=t[j++]; + case 6: + a[i++]+=t[j++]; + case 5: + a[i++]+=t[j++]; + case 4: + a[i++]+=t[j++]; + case 3: + a[i++]+=t[j++]; + case 2: + a[i++]+=t[j++]; + case 1: + a[i++]+=t[j++]; + case 0: + break; + } + } + } + } + return(0); +} + +ogg_int32_t FMOD_vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){ + if(book->used_entries>0){ + int i,j,entry; + float *t; + + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]=t[j++]; + } + }else{ + int i,j; + + for(i=0;idim;) + a[i++]=0.f; + } + } + return(0); +} + +ogg_int32_t FMOD_vorbis_book_decodevv_add(codebook *book,float **a,ogg_int32_t offset,int ch, + oggpack_buffer *b,int n){ + + ogg_int32_t i,j,entry; + int chptr=0; + if(book->used_entries>0){ + for(i=offset/ch;i<(offset+n)/ch;){ + entry = decode_packed_entry_number(book,b); + if(entry==-1)return(-1); + { + const float *t = book->valuelist+entry*book->dim; + for (j=0;jdim;j++){ + a[chptr++][i]+=t[j]; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + } + } + return(0); +} + +#ifdef _V_SELFTEST +/* Simple enough; pack a few candidate codebooks, unpack them. Code a + number of vectors through (keeping track of the quantized values), + and decode using the unpacked book. quantized version of in should + exactly equal out */ + +#include + +#include "vorbis/book/lsp20_0.vqh" +#include "vorbis/book/res0a_13.vqh" +#define TESTSIZE 40 + +float test1[TESTSIZE]={ + 0.105939f, + 0.215373f, + 0.429117f, + 0.587974f, + + 0.181173f, + 0.296583f, + 0.515707f, + 0.715261f, + + 0.162327f, + 0.263834f, + 0.342876f, + 0.406025f, + + 0.103571f, + 0.223561f, + 0.368513f, + 0.540313f, + + 0.136672f, + 0.395882f, + 0.587183f, + 0.652476f, + + 0.114338f, + 0.417300f, + 0.525486f, + 0.698679f, + + 0.147492f, + 0.324481f, + 0.643089f, + 0.757582f, + + 0.139556f, + 0.215795f, + 0.324559f, + 0.399387f, + + 0.120236f, + 0.267420f, + 0.446940f, + 0.608760f, + + 0.115587f, + 0.287234f, + 0.571081f, + 0.708603f, +}; + +float test3[TESTSIZE]={ + 0,1,-2,3,4,-5,6,7,8,9, + 8,-2,7,-1,4,6,8,3,1,-9, + 10,11,12,13,14,15,26,17,18,19, + 30,-25,-30,-1,-5,-32,4,3,-2,0}; + +static_codebook *testlist[]={&_vq_book_lsp20_0, + &_vq_book_res0a_13,NULL}; +float *testvec[]={test1,test3}; + +int main(){ + oggpack_buffer write; + oggpack_buffer read; + ogg_int32_t ptr=0,i; + FMOD_oggpack_writeinit(context, &write); + + fprintf(stderr,"Testing codebook abstraction...:\n"); + + while(testlist[ptr]){ + codebook c; + static_codebook s; + float *qv=alloca(sizeof(*qv)*TESTSIZE); + float *iv=alloca(sizeof(*iv)*TESTSIZE); + FMOD_memcpy(qv,testvec[ptr],sizeof(*qv)*TESTSIZE); + FMOD_memset(iv,0,sizeof(*iv)*TESTSIZE); + + fprintf(stderr,"\tpacking/coding %ld... ",ptr); + + /* pack the codebook, write the testvector */ + oggpack_reset(&write); + vorbis_book_init_encode(&c,testlist[ptr]); /* get it into memory + we can write */ + vorbis_staticbook_pack(testlist[ptr],&write); + fprintf(stderr,"Codebook size %ld bytes... ",oggpack_bytes(&write)); + for(i=0;i.000001){ + fprintf(stderr,"read (%g) != written (%g) at position (%ld)\n", + iv[i],qv[i],i); + exit(1); + } + + fprintf(stderr,"OK\n"); + ptr++; + } + + /* The above is the trivial stuff; now try unquantizing a log scale codebook */ + + exit(0); +} + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/codebook.h b/lib/ogg_vorbis/vorbis/lib/codebook.h new file mode 100755 index 0000000..1b81daa --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/codebook.h @@ -0,0 +1,160 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + last mod: $Id: codebook.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_CODEBOOK_H_ +#define _V_CODEBOOK_H_ + +#include + +/* This structure encapsulates huffman and VQ style encoding books; it + doesn't do anything specific to either. + + valuelist/quantlist are nonNULL (and q_* significant) only if + there's entry->value mapping to be done. + + If encode-side mapping must be done (and thus the entry needs to be + hunted), the auxiliary encode pointer will point to a decision + tree. This is true of both VQ and huffman, but is mostly useful + with VQ. + +*/ + +typedef struct static_codebook{ + ogg_int32_t dim; /* codebook dimensions (elements per vector) */ + ogg_int32_t entries; /* codebook entries */ + ogg_int32_t *lengthlist; /* codeword lengths in bits */ + + /* mapping ***************************************************************/ + int maptype; /* 0=none + 1=implicitly populated values from map column + 2=listed arbitrary values */ + + /* The below does a linear, single monotonic sequence mapping. */ + ogg_int32_t q_min; /* packed 32 bit float; quant value 0 maps to minval */ + ogg_int32_t q_delta; /* packed 32 bit float; val 1 - val 0 == delta */ + int q_quant; /* bits: 0 < quant <= 16 */ + int q_sequencep; /* bitflag */ + + ogg_int32_t *quantlist; /* map == 1: (int)(entries^(1/dim)) element column map + map == 2: list of dim*entries quantized entry vals + */ + + /* encode helpers ********************************************************/ +// struct encode_aux_nearestmatch *nearest_tree; +// struct encode_aux_threshmatch *thresh_tree; +// struct encode_aux_pigeonhole *pigeon_tree; + + int allocedp; +} static_codebook; + +/* this structures an arbitrary trained book to quickly find the + nearest cell match */ +//typedef struct encode_aux_nearestmatch{ +// /* pre-calculated partitioning tree */ +// ogg_int32_t *ptr0; +// ogg_int32_t *ptr1; +// +// ogg_int32_t *p; /* decision points (each is an entry) */ +// ogg_int32_t *q; /* decision points (each is an entry) */ +// ogg_int32_t aux; /* number of tree entries */ +// ogg_int32_t alloc; +//} encode_aux_nearestmatch; + +/* assumes a maptype of 1; encode side only, so that's OK */ +//typedef struct encode_aux_threshmatch{ +// float *quantthresh; +// ogg_int32_t *quantmap; +// int quantvals; +// int threshvals; +//} encode_aux_threshmatch; + +//typedef struct encode_aux_pigeonhole{ +// float min; +// float del; +// +// int mapentries; +// int quantvals; +// ogg_int32_t *pigeonmap; +// +// ogg_int32_t fittotal; +// ogg_int32_t *fitlist; +// ogg_int32_t *fitmap; +// ogg_int32_t *fitlength; +//} encode_aux_pigeonhole; + +typedef struct codebook{ + ogg_int32_t dim; /* codebook dimensions (elements per vector) */ + ogg_int32_t entries; /* codebook entries */ + ogg_int32_t used_entries; /* populated codebook entries */ + const static_codebook *c; + + /* for encode, the below are entry-ordered, fully populated */ + /* for decode, the below are ordered by bitreversed codeword and only + used entries are populated */ + float *valuelist; /* list of dim*entries actual entry values */ + ogg_uint32_t *codelist; /* list of bitstream codewords for each entry */ + + int *dec_index; /* only used if sparseness collapsed */ + char *dec_codelengths; + ogg_uint32_t *dec_firsttable; + int dec_firsttablen; + int dec_maxlength; + +} codebook; + +extern void FMOD_vorbis_staticbook_clear(void *context, static_codebook *b); +extern void FMOD_vorbis_staticbook_destroy(void *context, static_codebook *b); +//extern int FMOD_vorbis_book_init_encode(void *context, codebook *dest,const static_codebook *source); +extern int FMOD_vorbis_book_init_decode(void *context, codebook *dest,const static_codebook *source); +extern void FMOD_vorbis_book_clear(void *context, codebook *b); + +extern float *_FMOD_book_unquantize(void *context, const static_codebook *b,int n,int *map); +extern float *_FMOD_book_logdist(const static_codebook *b,float *vals); +extern float _FMOD_float32_unpack(ogg_int32_t val); +extern ogg_int32_t _FMOD_float32_pack(float val); +extern int _FMOD_best(codebook *book, float *a, int step); +extern int _FMOD_ilog(unsigned int v); +extern ogg_int32_t _FMOD_book_maptype1_quantvals(const static_codebook *b); + +extern int FMOD_vorbis_book_besterror(codebook *book,float *a,int step,int addmul); +extern ogg_int32_t FMOD_vorbis_book_codeword(codebook *book,int entry); +extern ogg_int32_t FMOD_vorbis_book_codelen(codebook *book,int entry); + + + +extern int FMOD_vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *b); +extern int FMOD_vorbis_staticbook_unpack(void *context, oggpack_buffer *b,static_codebook *c); + +//extern int FMOD_vorbis_book_encode(codebook *book, int a, oggpack_buffer *b); +extern int FMOD_vorbis_book_errorv(codebook *book, float *a); +//extern int FMOD_vorbis_book_encodev(codebook *book, int best,float *a, +// oggpack_buffer *b); + +extern ogg_int32_t FMOD_vorbis_book_decode(codebook *book, oggpack_buffer *b); +extern ogg_int32_t FMOD_vorbis_book_decodevs_add(codebook *book, float *a, + oggpack_buffer *b,int n); +extern ogg_int32_t FMOD_vorbis_book_decodev_set(codebook *book, float *a, + oggpack_buffer *b,int n); +extern ogg_int32_t FMOD_vorbis_book_decodev_add(codebook *book, float *a, + oggpack_buffer *b,int n); +extern ogg_int32_t FMOD_vorbis_book_decodevv_add(codebook *book, float **a, + ogg_int32_t off,int ch, + oggpack_buffer *b,int n); + + + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/codec_internal.h b/lib/ogg_vorbis/vorbis/lib/codec_internal.h new file mode 100755 index 0000000..8d3a431 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/codec_internal.h @@ -0,0 +1,166 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis codec headers + last mod: $Id: codec_internal.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_CODECI_H_ +#define _V_CODECI_H_ + +//#include "envelope.h" +#include "codebook.h" + +#define BLOCKTYPE_IMPULSE 0 +#define BLOCKTYPE_PADDING 1 +#define BLOCKTYPE_TRANSITION 0 +#define BLOCKTYPE_LONG 1 + +#define PACKETBLOBS 15 + +typedef struct vorbis_block_internal{ + float **pcmdelay; /* this is a pointer into local storage */ + float ampmax; + int blocktype; + + oggpack_buffer *packetblob[PACKETBLOBS]; /* initialized, must be freed; + blob [PACKETBLOBS/2] points to + the oggpack_buffer in the + main vorbis_block */ +} vorbis_block_internal; + +typedef void vorbis_look_floor; +typedef void vorbis_look_residue; +typedef void vorbis_look_transform; + +/* mode ************************************************************/ +typedef struct { + int blockflag; + int windowtype; + int transformtype; + int mapping; +} vorbis_info_mode; + +typedef void vorbis_info_floor; +typedef void vorbis_info_residue; +typedef void vorbis_info_mapping; + +#include "psy.h" +//#include "bitrate.h" + +typedef struct private_state { + /* local lookup storage */ + //envelope_lookup *ve; /* envelope lookup */ + int window[2]; + vorbis_look_transform **transform[2]; /* block, type */ +// drft_lookup fft_look[2]; + + int modebits; + vorbis_look_floor **flr; + vorbis_look_residue **residue; +// vorbis_look_psy *psy; +// vorbis_look_psy_global *psy_g_look; + + /* local storage, only used on the encoding side. This way the + application does not need to worry about freeing some packets' + memory and not others'; packet storage is always tracked. + Cleared next call to a _dsp_ function */ +// unsigned char *header; +// unsigned char *header1; +// unsigned char *header2; + +// bitrate_manager_state bms; + + ogg_int64_t sample_count; +} private_state; + +/* codec_setup_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). +*********************************************************************/ +#include "highlevel.h" +typedef struct codec_setup_info { + + /* Vorbis supports only short and long blocks, but allows the + encoder to choose the sizes */ + + ogg_int32_t blocksizes[2]; + + /* modes are the primary means of supporting on-the-fly different + blocksizes, different channel mappings (LR or M/A), + different residue backends, etc. Each mode consists of a + blocksize flag and a mapping (along with the mapping setup */ + + int modes; + int maps; + int floors; + int residues; + int books; +// int psys; /* encode only */ + + vorbis_info_mode *mode_param[64]; + int map_type[64]; + vorbis_info_mapping *map_param[64]; + int floor_type[64]; + vorbis_info_floor *floor_param[64]; + int residue_type[64]; + vorbis_info_residue *residue_param[64]; + static_codebook *book_param[256]; + codebook *fullbooks; + +// vorbis_info_psy *psy_param[4]; /* encode only */ +// vorbis_info_psy_global psy_g_param; + +// bitrate_manager_info bi; +// highlevel_encode_setup hi; /* used only by vorbisenc.c. It's a +// highly redundant structure, but +// improves clarity of program flow. */ + int halfrate_flag; /* painless downsample for decode */ +} codec_setup_info; + +//extern vorbis_look_psy_global *_vp_global_look(void *context, vorbis_info *vi); +//extern void _vp_global_free(void *context, vorbis_look_psy_global *look); + + + +typedef struct { +// int sorted_index[VIF_POSIT+2]; + int forward_index[VIF_POSIT+2]; + int reverse_index[VIF_POSIT+2]; + + int hineighbor[VIF_POSIT]; + int loneighbor[VIF_POSIT]; + int posts; + + int n; + int quant_q; + vorbis_info_floor1 *vi; + + ogg_int32_t phrasebits; + ogg_int32_t postbits; + ogg_int32_t frames; +} vorbis_look_floor1; + + + +//extern int *floor1_fit(void *context, vorbis_block *vb,vorbis_look_floor1 *look, +// const float *logmdct, /* in */ +// const float *logmask); +//extern int *floor1_interpolate_fit(void *context, vorbis_block *vb,vorbis_look_floor1 *look, +// int *A,int *B, +// int del); +//extern int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, +// vorbis_look_floor1 *look, +// int *post,int *ilogmask); +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/floor1.c b/lib/ogg_vorbis/vorbis/lib/floor1.c new file mode 100755 index 0000000..c62f992 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/floor1.c @@ -0,0 +1,1106 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 1 implementation + last mod: $Id: floor1.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "scales.h" + +#include + +#define floor1_rangedB 140 /* floor 1 fixed at -140dB to 0dB range */ + +typedef struct lsfit_acc{ + ogg_int32_t x0; + ogg_int32_t x1; + + ogg_int32_t xa; + ogg_int32_t ya; + ogg_int32_t x2a; + ogg_int32_t y2a; + ogg_int32_t xya; + ogg_int32_t an; +} lsfit_acc; + +/***********************************************/ + +static void FMOD_floor1_free_info(void *context, vorbis_info_floor *i){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + if(info){ + FMOD_memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void FMOD_floor1_free_look(void *context, vorbis_look_floor *i){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)i; + if(look){ + /*fprintf(stderr,"floor 1 bit usage %f:%f (%f total)\n", + (float)look->phrasebits/look->frames, + (float)look->postbits/look->frames, + (float)(look->postbits+look->phrasebits)/look->frames);*/ + + FMOD_memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +#if 0 +static void FMOD_floor1_pack (vorbis_info_floor *i,oggpack_buffer *opb){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + int j,k; + int count=0; + int rangebits; + int maxposit=info->postlist[1]; + int maxclass=-1; + + /* save out partitions */ + FMOD_oggpack_write(opb,info->partitions,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + FMOD_oggpack_write(opb,info->partitionclass[j],4); /* only 0 to 15 legal */ + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* save out partition classes */ + for(j=0;jclass_dim[j]-1,3); /* 1 to 8 */ + FMOD_oggpack_write(opb,info->class_subs[j],2); /* 0 to 3 */ + if(info->class_subs[j])FMOD_oggpack_write(opb,info->class_book[j],8); + for(k=0;k<(1<class_subs[j]);k++) + FMOD_oggpack_write(opb,info->class_subbook[j][k]+1,8); + } + + /* save out the post list */ + FMOD_oggpack_write(opb,info->mult-1,2); /* only 1,2,3,4 legal now */ + FMOD_oggpack_write(opb,ilog2(maxposit),4); + rangebits=ilog2(maxposit); + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + for(;kpostlist[k+2],rangebits); + } +} +#endif +static int icomp(const void *a,const void *b){ + return(**(int **)a-**(int **)b); +} + +static vorbis_info_floor *FMOD_floor1_unpack (void *context, vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + int j,k,count=0,maxclass=-1,rangebits; + + vorbis_info_floor1 *info=_ogg_calloc(1,sizeof(*info)); + if (!info) + goto err_out; + /* read partitions */ + info->partitions=FMOD_oggpack_read(opb,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + info->partitionclass[j]=FMOD_oggpack_read(opb,4); /* only 0 to 15 legal */ + if(info->partitionclass[j]<0)goto err_out; + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* read partition classes */ + for(j=0;jclass_dim[j]=FMOD_oggpack_read(opb,3)+1; /* 1 to 8 */ + info->class_subs[j]=FMOD_oggpack_read(opb,2); /* 0,1,2,3 bits */ + if(info->class_subs[j]<0) + goto err_out; + if(info->class_subs[j])info->class_book[j]=FMOD_oggpack_read(opb,8); + if(info->class_book[j]<0 || info->class_book[j]>=ci->books) + goto err_out; + for(k=0;k<(1<class_subs[j]);k++){ + info->class_subbook[j][k]=FMOD_oggpack_read(opb,8)-1; + if(info->class_subbook[j][k]<-1 || info->class_subbook[j][k]>=ci->books) + goto err_out; + } + } + + /* read the post list */ + info->mult=FMOD_oggpack_read(opb,2)+1; /* only 1,2,3,4 legal now */ + rangebits=FMOD_oggpack_read(opb,4); + if(rangebits<0)goto err_out; + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + for(;kpostlist[k+2]=FMOD_oggpack_read(opb,rangebits); + if(t<0 || t>=(1<postlist[0]=0; + info->postlist[1]=1<postlist+j; + qsort(sortpointer,count+2,sizeof(*sortpointer),icomp); + + for(j=1;jvi=info; + look->n=info->postlist[1]; + + /* we drop each position value in-between already decoded values, + and use linear interpolation to predict each new value past the + edges. The positions are read in the order of the position + list... we precompute the bounding positions in the lookup. Of + course, the neighbors can change (if a position is declined), but + this is an initial mapping */ + + for(i=0;ipartitions;i++)n+=info->class_dim[info->partitionclass[i]]; + n+=2; + look->posts=n; + + /* also store a sorted position index */ + for(i=0;ipostlist+i; + qsort(sortpointer,n,sizeof(*sortpointer),icomp); + + /* points from sort order back to range number */ + for(i=0;iforward_index[i]=sortpointer[i]-info->postlist; + /* points from range order to sorted position */ + for(i=0;ireverse_index[look->forward_index[i]]=i; + /* we actually need the post values too */ +// for(i=0;isorted_index[i]=info->postlist[look->forward_index[i]]; + + /* quantize values to multiplier spec */ + switch(info->mult){ + case 1: /* 1024 -> 256 */ + look->quant_q=256; + break; + case 2: /* 1024 -> 128 */ + look->quant_q=128; + break; + case 3: /* 1024 -> 86 */ + look->quant_q=86; + break; + case 4: /* 1024 -> 64 */ + look->quant_q=64; + break; + } + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(i=0;in; + int currentx=info->postlist[i+2]; + for(j=0;jpostlist[j]; + if(x>lx && xcurrentx){ + hi=j; + hx=x; + } + } + look->loneighbor[i]=lo; + look->hineighbor[i]=hi; + } + + return(look); +} + +static int render_point(int x0,int x1,int y0,int y1,int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int err=ady*(x-x0); + + int off=err/adx; + if(dy<0)return(y0-off); + return(y0+off); + } +} + +static int FMOD_vorbis_dBquant(const float *x){ + int i= (int)(*x*7.3142857f+1023.5f); + if(i>1023)return(1023); + if(i<0)return(0); + return i; +} + +static const float FLOOR1_fromdB_LOOKUP[256]={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F, +}; + +static void render_line(int n, int x0,int x1,int y0,int y1,float *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=abs(base*adx); + + if(n>x1)n=x1; + + if(x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]*=FLOOR1_fromdB_LOOKUP[y]; + } +} + +static void render_line0(int x0,int x1,int y0,int y1,int *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=abs(base*adx); + + d[x]=y; + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]=y; + } +} + +/* the floor has already been filtered to only include relevant sections */ +static int accumulate_fit(const float *flr,const float *mdct, + int x0, int x1,lsfit_acc *a, + int n,vorbis_info_floor1 *info){ + ogg_int32_t i; + + ogg_int32_t xa=0,ya=0,x2a=0,y2a=0,xya=0,na=0, xb=0,yb=0,x2b=0,y2b=0,xyb=0,nb=0; + + FMOD_memset(a,0,sizeof(*a)); + a->x0=x0; + a->x1=x1; + if(x1>=n)x1=n-1; + + for(i=x0;i<=x1;i++){ + int quantized=FMOD_vorbis_dBquant(flr+i); + if(quantized){ + if(mdct[i]+info->twofitatten>=flr[i]){ + xa += i; + ya += quantized; + x2a += i*i; + y2a += quantized*quantized; + xya += i*quantized; + na++; + }else{ + xb += i; + yb += quantized; + x2b += i*i; + y2b += quantized*quantized; + xyb += i*quantized; + nb++; + } + } + } + + xb+=xa; + yb+=ya; + x2b+=x2a; + y2b+=y2a; + xyb+=xya; + nb+=na; + + /* weight toward the actually used frequencies if we meet the threshhold */ + { + int weight=(int)(nb*info->twofitweight/(na+1)); + + a->xa=xa*weight+xb; + a->ya=ya*weight+yb; + a->x2a=x2a*weight+x2b; + a->y2a=y2a*weight+y2b; + a->xya=xya*weight+xyb; + a->an=na*weight+nb; + } + + return(na); +} + +#if 0 +static int fit_line(lsfit_acc *a,int fits,int *y0,int *y1){ + ogg_int32_t x=0,y=0,x2=0,y2=0,xy=0,an=0,i; + ogg_int32_t x0=a[0].x0; + ogg_int32_t x1=a[fits-1].x1; + + for(i=0;i=0){ + x+= x0; + y+= *y0; + x2+= x0 * x0; + y2+= *y0 * *y0; + xy+= *y0 * x0; + an++; + } + + if(*y1>=0){ + x+= x1; + y+= *y1; + x2+= x1 * x1; + y2+= *y1 * *y1; + xy+= *y1 * x1; + an++; + } + + { + /* need 64 bit multiplies, which C doesn't give portably as int */ + float fx=x; + float fx2=x2; + float denom=(an*fx2-fx*fx); + + if(denom>0.){ + float fy=y; + float fxy=xy; + + float a=(fy*fx2-fxy*fx)/denom; + float b=(an*fxy-fx*fy)/denom; + *y0=(int)FMOD_ogg_rint(a+b*x0); + *y1=(int)FMOD_ogg_rint(a+b*x1); + + /* limit to our range! */ + if(*y0>1023)*y0=1023; + if(*y1>1023)*y1=1023; + if(*y0<0)*y0=0; + if(*y1<0)*y1=0; + + return 0; + }else{ + *y0=0; + *y1=0; + return 1; + } + } +} + +/*static void fit_line_point(lsfit_acc *a,int fits,int *y0,int *y1){ + ogg_int32_t y=0; + int i; + + for(i=0;itwofitatten>=mask[x]){ + if(y+info->maxovermaxunder>val)return(1); + } + + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + + val=FMOD_vorbis_dBquant(mask+x); + mse+=((y-val)*(y-val)); + n++; + if(mdct[x]+info->twofitatten>=mask[x]){ + if(val){ + if(y+info->maxovermaxunder>val)return(1); + } + } + } + + if(info->maxover*info->maxover/n>info->maxerr)return(0); + if(info->maxunder*info->maxunder/n>info->maxerr)return(0); + if(mse/n>info->maxerr)return(1); + return(0); +} +#endif + +static int post_Y(int *A,int *B,int pos){ + if(A[pos]<0) + return B[pos]; + if(B[pos]<0) + return A[pos]; + + return (A[pos]+B[pos])>>1; +} + +//int *floor1_fit(void *context, vorbis_block *vb,vorbis_look_floor1 *look, +// const float *logmdct, /* in */ +// const float *logmask){ +// ogg_int32_t i,j; +// vorbis_info_floor1 *info=look->vi; +// ogg_int32_t n=look->n; +// ogg_int32_t posts=look->posts; +// ogg_int32_t nonzero=0; +// lsfit_acc fits[VIF_POSIT+1]; +// int fit_valueA[VIF_POSIT+2]; /* index by range list position */ +// int fit_valueB[VIF_POSIT+2]; /* index by range list position */ +// +// int loneighbor[VIF_POSIT+2]; /* sorted index of range list position (+2) */ +// int hineighbor[VIF_POSIT+2]; +// int *output=NULL; +// int memo[VIF_POSIT+2]; +// +// for(i=0;isorted_index[i], +// look->sorted_index[i+1],fits+i, +// n,info); +// } +// +// if(nonzero){ +// /* start by fitting the implicit base case.... */ +// int y0=-200; +// int y1=-200; +// fit_line(fits,posts-1,&y0,&y1); +// +// fit_valueA[0]=y0; +// fit_valueB[0]=y0; +// fit_valueB[1]=y1; +// fit_valueA[1]=y1; +// +// /* Non degenerate case */ +// /* start progressive splitting. This is a greedy, non-optimal +// algorithm, but simple and close enough to the best +// answer. */ +// for(i=2;ireverse_index[i]; +// int ln=loneighbor[sortpos]; +// int hn=hineighbor[sortpos]; +// +// /* eliminate repeat searches of a particular range with a memo */ +// if(memo[ln]!=hn){ +// /* haven't performed this error search yet */ +// int lsortpos=look->reverse_index[ln]; +// int hsortpos=look->reverse_index[hn]; +// memo[ln]=hn; +// +// { +// /* A note: we want to bound/minimize *local*, not global, error */ +// int lx=info->postlist[ln]; +// int hx=info->postlist[hn]; +// int ly=post_Y(fit_valueA,fit_valueB,ln); +// int hy=post_Y(fit_valueA,fit_valueB,hn); +// +// if(ly==-1 || hy==-1){ +// exit(1); +// } +// +// if(inspect_error(lx,hx,ly,hy,logmask,logmdct,info)){ +// /* outside error bounds/begin search area. Split it. */ +// int ly0=-200; +// int ly1=-200; +// int hy0=-200; +// int hy1=-200; +// int ret0=fit_line(fits+lsortpos,sortpos-lsortpos,&ly0,&ly1); +// int ret1=fit_line(fits+sortpos,hsortpos-sortpos,&hy0,&hy1); +// +// if(ret0){ +// ly0=ly; +// ly1=hy0; +// } +// if(ret1){ +// hy0=ly1; +// hy1=hy; +// } +// +// if(ret0 && ret1){ +// fit_valueA[i]=-200; +// fit_valueB[i]=-200; +// }else{ +// /* store new edge values */ +// fit_valueB[ln]=ly0; +// if(ln==0)fit_valueA[ln]=ly0; +// fit_valueA[i]=ly1; +// fit_valueB[i]=hy0; +// fit_valueA[hn]=hy1; +// if(hn==1)fit_valueB[hn]=hy1; +// +// if(ly1>=0 || hy0>=0){ +// /* store new neighbor values */ +// for(j=sortpos-1;j>=0;j--) +// if(hineighbor[j]==hn) +// hineighbor[j]=i; +// else +// break; +// for(j=sortpos+1;jloneighbor[i-2]; +// int hn=look->hineighbor[i-2]; +// int x0=info->postlist[ln]; +// int x1=info->postlist[hn]; +// int y0=output[ln]; +// int y1=output[hn]; +// +// int predicted=render_point(x0,x1,y0,y1,info->postlist[i]); +// int vx=post_Y(fit_valueA,fit_valueB,i); +// +// if(vx>=0 && predicted!=vx){ +// output[i]=vx; +// }else{ +// output[i]= predicted|0x8000; +// } +// } +// } +// +// return(output); +// +//} +// +//int *floor1_interpolate_fit(void *context, vorbis_block *vb,vorbis_look_floor1 *look, +// int *A,int *B, +// int del){ +// +// ogg_int32_t i; +// ogg_int32_t posts=look->posts; +// int *output=NULL; +// +// if(A && B){ +// output=_FMOD_vorbis_block_alloc(context, vb,sizeof(*output)*posts); +// +// /* overly simpleminded--- look again post 1.2 */ +// for(i=0;i>16; +// if(A[i]&0x8000 && B[i]&0x8000)output[i]|=0x8000; +// } +// } +// +// return(output); +//} +// +// +//int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, +// vorbis_look_floor1 *look, +// int *post,int *ilogmask){ +// +// ogg_int32_t i,j; +// vorbis_info_floor1 *info=look->vi; +// ogg_int32_t posts=look->posts; +// codec_setup_info *ci=vb->vd->vi->codec_setup; +// int out[VIF_POSIT+2]; +// static_codebook **sbooks=ci->book_param; +// codebook *books=ci->fullbooks; +// +// /* quantize values to multiplier spec */ +// if(post){ +// for(i=0;imult){ +// case 1: /* 1024 -> 256 */ +// val>>=2; +// break; +// case 2: /* 1024 -> 128 */ +// val>>=3; +// break; +// case 3: /* 1024 -> 86 */ +// val/=12; +// break; +// case 4: /* 1024 -> 64 */ +// val>>=4; +// break; +// } +// post[i]=val | (post[i]&0x8000); +// } +// +// out[0]=post[0]; +// out[1]=post[1]; +// +// /* find prediction values for each post and subtract them */ +// for(i=2;iloneighbor[i-2]; +// int hn=look->hineighbor[i-2]; +// int x0=info->postlist[ln]; +// int x1=info->postlist[hn]; +// int y0=post[ln]; +// int y1=post[hn]; +// +// int predicted=render_point(x0,x1,y0,y1,info->postlist[i]); +// +// if((post[i]&0x8000) || (predicted==post[i])){ +// post[i]=predicted|0x8000; /* in case there was roundoff jitter +// in interpolation */ +// out[i]=0; +// }else{ +// int headroom=(look->quant_q-predictedquant_q-predicted:predicted); +// +// int val=post[i]-predicted; +// +// /* at this point the 'deviation' value is in the range +/- max +// range, but the real, unique range can always be mapped to +// only [0-maxrange). So we want to wrap the deviation into +// this limited range, but do it in the way that least screws +// an essentially gaussian probability distribution. */ +// +// if(val<0) +// if(val<-headroom) +// val=headroom-val-1; +// else +// val=-1-(val<<1); +// else +// if(val>=headroom) +// val= val+headroom; +// else +// val<<=1; +// +// out[i]=val; +// post[ln]&=0x7fff; +// post[hn]&=0x7fff; +// } +// } +// +// /* we have everything we need. pack it out */ +// /* mark nontrivial floor */ +// FMOD_oggpack_write(opb,1,1); +// +// /* beginning/end post */ +// look->frames++; +// look->postbits+=ilog(look->quant_q-1)*2; +// FMOD_oggpack_write(opb,out[0],ilog(look->quant_q-1)); +// FMOD_oggpack_write(opb,out[1],ilog(look->quant_q-1)); +// +// +// /* partition by partition */ +// for(i=0,j=2;ipartitions;i++){ +// int class=info->partitionclass[i]; +// int cdim=info->class_dim[class]; +// int csubbits=info->class_subs[class]; +// int csub=1<class_subbook[class][k]; +// if(booknum<0){ +// maxval[k]=1; +// }else{ +// maxval[k]=sbooks[info->class_subbook[class][k]]->entries; +// } +// } +// for(k=0;kphrasebits+= +// FMOD_vorbis_book_encode(books+info->class_book[class],cval,opb); +// +//#ifdef TRAIN_FLOOR1 +// { +// FILE *of; +// char buffer[80]; +// sprintf(buffer,"line_%dx%ld_class%d.vqd", +// vb->pcmend/2,posts-2,class); +// of=fopen(buffer,"a"); +// fprintf(of,"%d\n",cval); +// fclose(of); +// } +//#endif +// } +// +// /* write post values */ +// for(k=0;kclass_subbook[class][bookas[k]]; +// if(book>=0){ +// /* hack to allow training with 'bad' books */ +// if(out[j+k]<(books+book)->entries) +// look->postbits+=FMOD_vorbis_book_encode(books+book, +// out[j+k],opb); +// /*else +// fprintf(stderr,"+!");*/ +// +//#ifdef TRAIN_FLOOR1 +// { +// FILE *of; +// char buffer[80]; +// sprintf(buffer,"line_%dx%ld_%dsub%d.vqd", +// vb->pcmend/2,posts-2,class,bookas[k]); +// of=fopen(buffer,"a"); +// fprintf(of,"%d\n",out[j+k]); +// fclose(of); +// } +//#endif +// } +// } +// j+=cdim; +// } +// +// { +// /* generate quantized floor equivalent to what we'd unpack in decode */ +// /* render the lines */ +// int hx=0; +// int lx=0; +// int ly=post[0]*info->mult; +// for(j=1;jposts;j++){ +// int current=look->forward_index[j]; +// int hy=post[current]&0x7fff; +// if(hy==post[current]){ +// +// hy*=info->mult; +// hx=info->postlist[current]; +// +// render_line0(lx,hx,ly,hy,ilogmask); +// +// lx=hx; +// ly=hy; +// } +// } +// for(j=hx;jpcmend/2;j++)ilogmask[j]=ly; /* be certain */ +// return(1); +// } +// }else{ +// FMOD_oggpack_write(opb,0,1); +// FMOD_memset(ilogmask,0,vb->pcmend/2*sizeof(*ilogmask)); +// return(0); +// } +//} + +static void *FMOD_floor1_inverse1(void *context, vorbis_block *vb,vorbis_look_floor *in){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + codec_setup_info *ci=vb->vd->vi->codec_setup; + + int i,j,k; + codebook *books=ci->fullbooks; + + /* unpack wrapped/predicted values from stream */ + if(FMOD_oggpack_read(&vb->opb,1)==1){ + int *fit_value=_FMOD_vorbis_block_alloc(context, vb,(look->posts)*sizeof(*fit_value)); + + fit_value[0]=FMOD_oggpack_read(&vb->opb,ilog(look->quant_q-1)); + fit_value[1]=FMOD_oggpack_read(&vb->opb,ilog(look->quant_q-1)); + + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int class=info->partitionclass[i]; + int cdim=info->class_dim[class]; + int csubbits=info->class_subs[class]; + int csub=1<class_book[class],&vb->opb); + + if(cval==-1)goto eop; + } + + for(k=0;kclass_subbook[class][cval&(csub-1)]; + cval>>=csubbits; + if(book>=0){ + if((fit_value[j+k]=FMOD_vorbis_book_decode(books+book,&vb->opb))==-1) + goto eop; + }else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(i=2;iposts;i++){ + int predicted=render_point(info->postlist[look->loneighbor[i-2]], + info->postlist[look->hineighbor[i-2]], + fit_value[look->loneighbor[i-2]], + fit_value[look->hineighbor[i-2]], + info->postlist[i]); + int hiroom=look->quant_q-predicted; + int loroom=predicted; + int room=(hiroom=room){ + if(hiroom>loroom){ + val = val-loroom; + }else{ + val = -1-(val-hiroom); + } + }else{ + if(val&1){ + val= -((val+1)>>1); + }else{ + val>>=1; + } + } + + fit_value[i]=val+predicted; + fit_value[look->loneighbor[i-2]]&=0x7fff; + fit_value[look->hineighbor[i-2]]&=0x7fff; + + }else{ + fit_value[i]=predicted|0x8000; + } + + } + + return(fit_value); + } + eop: + return(NULL); +} + +static int FMOD_floor1_inverse2(void *context, vorbis_block *vb,vorbis_look_floor *in,void *memo, + float *out){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + + codec_setup_info *ci=vb->vd->vi->codec_setup; + int n=ci->blocksizes[vb->W]/2; + int j; + + if(memo){ + /* render the lines */ + int *fit_value=(int *)memo; + int hx=0; + int lx=0; + int ly=fit_value[0]*info->mult; + for(j=1;jposts;j++){ + int current=look->forward_index[j]; + int hy=fit_value[current]&0x7fff; + if(hy==fit_value[current]){ + + hy*=info->mult; + hx=info->postlist[current]; + + render_line(n,lx,hx,ly,hy,out); + + lx=hx; + ly=hy; + } + } + for(j=hx;j header packets + last mod: $Id: info.c 16243 2009-07-10 02:49:31Z xiphmont $ + + ********************************************************************/ + +/* general handling of the header and the vorbis_info structure (and + substructures) */ + +#include +#include +//#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "registry.h" +#include "window.h" +#include "psy.h" +#include "misc.h" +#include "os.h" + +#define GENERAL_VENDOR_STRING "Xiph.Org libVorbis 1.2.3" +#define ENCODE_VENDOR_STRING "Xiph.Org libVorbis I 20090709" + +/* helpers */ +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +char FMOD_ogg_toupper(char in) +{ + if (in >= 'a' && in <= 'z') + { + in += 'A' - 'a'; + } + + return in; +} + +#if 0 +static void _v_writestring(oggpack_buffer *o,const char *s, int bytes){ + + while(bytes--){ + FMOD_oggpack_write(o,*s++,8); + } +} +#endif + +static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ + while(bytes--){ + *buf++=(char)FMOD_oggpack_read(o,8); + } +} + +void FMOD_vorbis_comment_init(vorbis_comment *vc){ + FMOD_memset(vc,0,sizeof(*vc)); +} + +//void FMOD_vorbis_comment_add(void *context, vorbis_comment *vc,const char *comment){ +// vc->user_comments=_ogg_realloc(vc->user_comments, +// (vc->comments+2)*sizeof(*vc->user_comments)); +// vc->comment_lengths=_ogg_realloc(vc->comment_lengths, +// (vc->comments+2)*sizeof(*vc->comment_lengths)); +// vc->comment_lengths[vc->comments]=ogg_strlen(comment); +// vc->user_comments[vc->comments]=_ogg_malloc(vc->comment_lengths[vc->comments]+1); +// ogg_strcpy(vc->user_comments[vc->comments], comment); +// vc->comments++; +// vc->user_comments[vc->comments]=NULL; +//} +// +//void vorbis_comment_add_tag(void *context, vorbis_comment *vc, const char *tag, const char *contents){ +// char *comment=alloca(ogg_strlen(tag)+ogg_strlen(contents)+2); /* +2 for = and \0 */ +// ogg_strcpy(comment, tag); +// ogg_strcat(comment, "="); +// ogg_strcat(comment, contents); +// FMOD_vorbis_comment_add(context, vc, comment); +//} + +/* This is more or less the same as strncasecmp - but that doesn't exist + * everywhere, and this is a fairly trivial function, so we include it */ +static int tagcompare(const char *s1, const char *s2, int n){ + int c=0; + while(c < n){ + if(FMOD_ogg_toupper(s1[c]) != FMOD_ogg_toupper(s2[c])) + return !0; + c++; + } + return 0; +} + +//char *FMOD_vorbis_comment_query(vorbis_comment *vc, const char *tag, int count){ +// ogg_int32_t i; +// int found = 0; +// int taglen = ogg_strlen(tag)+1; /* +1 for the = we append */ +// char *fulltag = alloca(taglen+ 1); +// +// ogg_strcpy(fulltag, tag); +// ogg_strcat(fulltag, "="); +// +// for(i=0;icomments;i++){ +// if(!tagcompare(vc->user_comments[i], fulltag, taglen)){ +// if(count == found) +// /* We return a pointer to the data, not a copy */ +// return vc->user_comments[i] + taglen; +// else +// found++; +// } +// } +// return NULL; /* didn't find anything */ +//} +// +//int FMOD_vorbis_comment_query_count(vorbis_comment *vc, const char *tag){ +// int i,count=0; +// int taglen = ogg_strlen(tag)+1; /* +1 for the = we append */ +// char *fulltag = alloca(taglen+1); +// ogg_strcpy(fulltag,tag); +// ogg_strcat(fulltag, "="); +// +// for(i=0;icomments;i++){ +// if(!tagcompare(vc->user_comments[i], fulltag, taglen)) +// count++; +// } +// +// return count; +//} + +void FMOD_vorbis_comment_clear(void *context, vorbis_comment *vc){ + if(vc){ + ogg_int32_t i; + if(vc->user_comments){ + for(i=0;icomments;i++) + if(vc->user_comments[i])_ogg_free(vc->user_comments[i]); + _ogg_free(vc->user_comments); + } + if(vc->comment_lengths)_ogg_free(vc->comment_lengths); + if(vc->vendor)_ogg_free(vc->vendor); + FMOD_memset(vc,0,sizeof(*vc)); + } +} + +/* blocksize 0 is guaranteed to be short, 1 is guaranteed to be long. + They may be equal, but short will never ge greater than long */ +int FMOD_vorbis_info_blocksize(vorbis_info *vi,int zo){ + codec_setup_info *ci = vi->codec_setup; + return ci ? ci->blocksizes[zo] : -1; +} + +/* used by synthesis, which has a full, alloced vi */ +int FMOD_vorbis_info_init(void *context, vorbis_info *vi){ + FMOD_memset(vi,0,sizeof(*vi)); + vi->codec_setup=_ogg_calloc(1,sizeof(codec_setup_info)); + if(!vi->codec_setup) + { + return(OV_EMEMORY); + } + + return 0; +} + +void FMOD_vorbis_info_clear(void *context, vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + int i; + + if(ci){ + + for(i=0;imodes;i++) + if(ci->mode_param[i])_ogg_free(ci->mode_param[i]); + + for(i=0;imaps;i++) /* unpack does the range checking */ + if(ci->map_param[i]) /* this may be cleaning up an aborted + unpack, in which case the below type + cannot be trusted */ + _FMOD_mapping_P[ci->map_type[i]]->free_info(context, ci->map_param[i]); + + for(i=0;ifloors;i++) /* unpack does the range checking */ + if(ci->floor_param[i]) /* this may be cleaning up an aborted + unpack, in which case the below type + cannot be trusted */ + _FMOD_floor_P[ci->floor_type[i]]->free_info(context, ci->floor_param[i]); + + for(i=0;iresidues;i++) /* unpack does the range checking */ + if(ci->residue_param[i]) /* this may be cleaning up an aborted + unpack, in which case the below type + cannot be trusted */ + _FMOD_residue_P[ci->residue_type[i]]->free_info(context, ci->residue_param[i]); + + for(i=0;ibooks;i++){ + if(ci->book_param[i]){ + /* knows if the book was not alloced */ + FMOD_vorbis_staticbook_destroy(context, ci->book_param[i]); + } + if(ci->fullbooks) + FMOD_vorbis_book_clear(context, ci->fullbooks+i); + } + if(ci->fullbooks) + _ogg_free(ci->fullbooks); + +// for(i=0;ipsys;i++) +// _vi_psy_free(context, ci->psy_param[i]); + + _ogg_free(ci); + } + + FMOD_memset(vi,0,sizeof(*vi)); +} + +/* Header packing/unpacking ********************************************/ + +static int _FMOD_vorbis_unpack_info(void *context, vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + if(!ci)return(OV_EFAULT); + + vi->version=FMOD_oggpack_read(opb,32); + if(vi->version!=0)return(OV_EVERSION); + + vi->channels=FMOD_oggpack_read(opb,8); + vi->rate=FMOD_oggpack_read(opb,32); + + vi->bitrate_upper=FMOD_oggpack_read(opb,32); + vi->bitrate_nominal=FMOD_oggpack_read(opb,32); + vi->bitrate_lower=FMOD_oggpack_read(opb,32); + + ci->blocksizes[0]=1<blocksizes[1]=1<rate<1)goto err_out; + if(vi->channels<1)goto err_out; + if(ci->blocksizes[0]<64)goto err_out; + if(ci->blocksizes[1]blocksizes[0])goto err_out; + if(ci->blocksizes[1]>8192)goto err_out; + + if(FMOD_oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + FMOD_vorbis_info_clear(context, vi); + return(OV_EBADHEADER); +} + +static int _vorbis_unpack_comment(void *context, vorbis_comment *vc,oggpack_buffer *opb){ + int i,err = OV_EBADHEADER; + int vendorlen=FMOD_oggpack_read(opb,32); + if(vendorlen<0)goto err_out; + if(vendorlen>opb->storage-8)goto err_out; + vc->vendor=_ogg_calloc(vendorlen+1,1); + if (!vc->vendor) + { + err = OV_EMEMORY; + goto err_out; + } + _v_readstring(opb,vc->vendor,vendorlen); + i=FMOD_oggpack_read(opb,32); + if(i<0)goto err_out; + if(i>((opb->storage-FMOD_oggpack_bytes(opb))>>2))goto err_out; + vc->comments=i; + vc->user_comments=_ogg_calloc(vc->comments+1,sizeof(*vc->user_comments)); + if (!vc->user_comments) + { + err = OV_EMEMORY; + goto err_out; + } + vc->comment_lengths=_ogg_calloc(vc->comments+1, sizeof(*vc->comment_lengths)); + if (!vc->comment_lengths) + { + err = OV_EMEMORY; + goto err_out; + } + + + for(i=0;icomments;i++){ + int len=FMOD_oggpack_read(opb,32); + if(len<0)goto err_out; + if(len>opb->storage-FMOD_oggpack_bytes(opb))goto err_out; + vc->comment_lengths[i]=len; + vc->user_comments[i]=_ogg_calloc(len+1,1); + if (!vc->user_comments[i]) + { + err = OV_EMEMORY; + goto err_out; + } + + _v_readstring(opb,vc->user_comments[i],len); + } + if(FMOD_oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + FMOD_vorbis_comment_clear(context, vc); + return err; +} + +/* all of the real encoding details are here. The modes, books, + everything */ +static int _vorbis_unpack_books(void *context, vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + int i,err = OV_EBADHEADER; + if(!ci)return(OV_EFAULT); + + /* codebooks */ + ci->books=FMOD_oggpack_read(opb,8)+1; + if(ci->books<=0)goto err_out; + for(i=0;ibooks;i++){ + int ret; + ci->book_param[i]=_ogg_calloc(1,sizeof(*ci->book_param[i])); + if (!ci->book_param[i]) + { + err = OV_EMEMORY; + goto err_out; + } + ret = FMOD_vorbis_staticbook_unpack(context, opb,ci->book_param[i]); + if(ret) + { + err = ret; + goto err_out; + } + } + + /* time backend settings; hooks are unused */ + { + int times=FMOD_oggpack_read(opb,6)+1; + if(times<=0)goto err_out; + for(i=0;i=VI_TIMEB)goto err_out; + } + } + + /* floor backend settings */ + ci->floors=FMOD_oggpack_read(opb,6)+1; + if(ci->floors<=0)goto err_out; + for(i=0;ifloors;i++){ + ci->floor_type[i]=FMOD_oggpack_read(opb,16); + if (ci->floor_type[i] == 0) + { + err = OV_EVERSION; + goto err_out; /* FMOD ADDED - NOT SUPPORTING FLOOR 0! */ + } + if(ci->floor_type[i]<0 || ci->floor_type[i]>=VI_FLOORB)goto err_out; + ci->floor_param[i]=_FMOD_floor_P[ci->floor_type[i]]->unpack(context, vi,opb); + if(!ci->floor_param[i])goto err_out; + } + + /* residue backend settings */ + ci->residues=FMOD_oggpack_read(opb,6)+1; + if(ci->residues<=0)goto err_out; + for(i=0;iresidues;i++){ + ci->residue_type[i]=FMOD_oggpack_read(opb,16); + if(ci->residue_type[i]<0 || ci->residue_type[i]>=VI_RESB)goto err_out; + ci->residue_param[i]=_FMOD_residue_P[ci->residue_type[i]]->unpack(context, vi,opb); + if(!ci->residue_param[i])goto err_out; + } + + /* map backend settings */ + ci->maps=FMOD_oggpack_read(opb,6)+1; + if(ci->maps<=0)goto err_out; + for(i=0;imaps;i++){ + ci->map_type[i]=FMOD_oggpack_read(opb,16); + if(ci->map_type[i]<0 || ci->map_type[i]>=VI_MAPB)goto err_out; + ci->map_param[i]=_FMOD_mapping_P[ci->map_type[i]]->unpack(context, vi,opb); + if(!ci->map_param[i])goto err_out; + } + + /* mode settings */ + ci->modes=FMOD_oggpack_read(opb,6)+1; + if(ci->modes<=0)goto err_out; + for(i=0;imodes;i++){ + ci->mode_param[i]=_ogg_calloc(1,sizeof(*ci->mode_param[i])); + if (!ci->mode_param[i]) + { + err = OV_EMEMORY; + goto err_out; + } + ci->mode_param[i]->blockflag=FMOD_oggpack_read(opb,1); + ci->mode_param[i]->windowtype=FMOD_oggpack_read(opb,16); + ci->mode_param[i]->transformtype=FMOD_oggpack_read(opb,16); + ci->mode_param[i]->mapping=FMOD_oggpack_read(opb,8); + + if(ci->mode_param[i]->windowtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->transformtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->mapping>=ci->maps)goto err_out; + if(ci->mode_param[i]->mapping<0)goto err_out; + } + + if(FMOD_oggpack_read(opb,1)!=1)goto err_out; /* top level EOP check */ + + return(0); + err_out: + FMOD_vorbis_info_clear(context, vi); + return err; +} + +/* Is this packet a vorbis ID header? */ +int FMOD_vorbis_synthesis_idheader(ogg_packet *op){ + oggpack_buffer opb; + char buffer[6]; + + if(op){ + FMOD_oggpack_readinit(&opb,op->packet,op->bytes); + + if(!op->b_o_s) + return(0); /* Not the initial packet */ + + if(FMOD_oggpack_read(&opb,8) != 1) + return 0; /* not an ID header */ + + FMOD_memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)) + return 0; /* not vorbis */ + + return 1; + } + + return 0; +} + +/* The Vorbis header is in three packets; the initial small packet in + the first page that identifies basic parameters, a second packet + with bitstream comments and a third packet that holds the + codebook. */ + +int FMOD_vorbis_synthesis_headerin(void *context, vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){ + oggpack_buffer opb; + + if(op){ + FMOD_oggpack_readinit(&opb,op->packet,op->bytes); + + /* Which of the three types of header is this? */ + /* Also verify header-ness, vorbis */ + { + char buffer[6]; + int packtype=FMOD_oggpack_read(&opb,8); + FMOD_memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)){ + /* not a vorbis header */ + return(OV_ENOTVORBIS); + } + switch(packtype){ + case 0x01: /* least significant *bit* is read first */ + if(!op->b_o_s){ + /* Not the initial packet */ + return(OV_EBADHEADER); + } + if(vi->rate!=0){ + /* previously initialized info header */ + return(OV_EBADHEADER); + } + + return(_FMOD_vorbis_unpack_info(context, vi,&opb)); + + case 0x03: /* least significant *bit* is read first */ + if(vi->rate==0){ + /* um... we didn't get the initial header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_comment(context, vc,&opb)); + + case 0x05: /* least significant *bit* is read first */ + if(vi->rate==0 || vc->vendor==NULL){ + /* um... we didn;t get the initial header or comments yet */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_books(context, vi,&opb)); + + default: + /* Not a valid vorbis header type */ + return(OV_EBADHEADER); + break; + } + } + } + return(OV_EBADHEADER); +} + +///* pack side **********************************************************/ +// +//static int _vorbis_pack_info(oggpack_buffer *opb,vorbis_info *vi){ +// codec_setup_info *ci=vi->codec_setup; +// if(!ci)return(OV_EFAULT); +// +// /* preamble */ +// FMOD_oggpack_write(opb,0x01,8); +// _v_writestring(opb,"vorbis", 6); +// +// /* basic information about the stream */ +// FMOD_oggpack_write(opb,0x00,32); +// FMOD_oggpack_write(opb,vi->channels,8); +// FMOD_oggpack_write(opb,vi->rate,32); +// +// FMOD_oggpack_write(opb,vi->bitrate_upper,32); +// FMOD_oggpack_write(opb,vi->bitrate_nominal,32); +// FMOD_oggpack_write(opb,vi->bitrate_lower,32); +// +// FMOD_oggpack_write(opb,ilog2(ci->blocksizes[0]),4); +// FMOD_oggpack_write(opb,ilog2(ci->blocksizes[1]),4); +// FMOD_oggpack_write(opb,1,1); +// +// return(0); +//} +// +//static int _vorbis_pack_comment(oggpack_buffer *opb,vorbis_comment *vc){ +// int bytes = ogg_strlen(ENCODE_VENDOR_STRING); +// +// /* preamble */ +// FMOD_oggpack_write(opb,0x03,8); +// _v_writestring(opb,"vorbis", 6); +// +// /* vendor */ +// FMOD_oggpack_write(opb,bytes,32); +// _v_writestring(opb,ENCODE_VENDOR_STRING, bytes); +// +// /* comments */ +// +// FMOD_oggpack_write(opb,vc->comments,32); +// if(vc->comments){ +// int i; +// for(i=0;icomments;i++){ +// if(vc->user_comments[i]){ +// FMOD_oggpack_write(opb,vc->comment_lengths[i],32); +// _v_writestring(opb,vc->user_comments[i], vc->comment_lengths[i]); +// }else{ +// FMOD_oggpack_write(opb,0,32); +// } +// } +// } +// FMOD_oggpack_write(opb,1,1); +// +// return(0); +//} +// +//static int _vorbis_pack_books(void *context, oggpack_buffer *opb,vorbis_info *vi){ +// codec_setup_info *ci=vi->codec_setup; +// int i; +// if(!ci)return(OV_EFAULT); +// +// FMOD_oggpack_write(opb,0x05,8); +// _v_writestring(opb,"vorbis", 6); +// +// /* books */ +// FMOD_oggpack_write(opb,ci->books-1,8); +// for(i=0;ibooks;i++) +// if(FMOD_vorbis_staticbook_pack(ci->book_param[i],opb))goto err_out; +// +// /* times; hook placeholders */ +// FMOD_oggpack_write(opb,0,6); +// FMOD_oggpack_write(opb,0,16); +// +// /* floors */ +// FMOD_oggpack_write(opb,ci->floors-1,6); +// for(i=0;ifloors;i++){ +// FMOD_oggpack_write(opb,ci->floor_type[i],16); +// if(_floor_P[ci->floor_type[i]]->pack) +// _floor_P[ci->floor_type[i]]->pack(ci->floor_param[i],opb); +// else +// goto err_out; +// } +// +// /* residues */ +// FMOD_oggpack_write(opb,ci->residues-1,6); +// for(i=0;iresidues;i++){ +// FMOD_oggpack_write(opb,ci->residue_type[i],16); +// _FMOD_residue_P[ci->residue_type[i]]->pack(ci->residue_param[i],opb); +// } +// +// /* maps */ +// FMOD_oggpack_write(opb,ci->maps-1,6); +// for(i=0;imaps;i++){ +// FMOD_oggpack_write(opb,ci->map_type[i],16); +// _FMOD_mapping_P[ci->map_type[i]]->pack(vi,ci->map_param[i],opb); +// } +// +// /* modes */ +// FMOD_oggpack_write(opb,ci->modes-1,6); +// for(i=0;imodes;i++){ +// FMOD_oggpack_write(opb,ci->mode_param[i]->blockflag,1); +// FMOD_oggpack_write(opb,ci->mode_param[i]->windowtype,16); +// FMOD_oggpack_write(opb,ci->mode_param[i]->transformtype,16); +// FMOD_oggpack_write(opb,ci->mode_param[i]->mapping,8); +// } +// FMOD_oggpack_write(opb,1,1); +// +// return(0); +//err_out: +// return(-1); +//} +// +//int vorbis_commentheader_out(void *context, vorbis_comment *vc, +// ogg_packet *op){ +// +// oggpack_buffer opb; +// +// FMOD_oggpack_writeinit(context, &opb); +// if(_vorbis_pack_comment(&opb,vc)) return OV_EIMPL; +// +// op->packet = _ogg_malloc(FMOD_oggpack_bytes(&opb)); +// FMOD_memcpy(op->packet, opb.buffer, FMOD_oggpack_bytes(&opb)); +// +// op->bytes=FMOD_oggpack_bytes(&opb); +// op->b_o_s=0; +// op->e_o_s=0; +// op->granulepos=0; +// op->packetno=1; +// +// return 0; +//} +// +//int vorbis_analysis_headerout(vorbis_dsp_state *v, +// vorbis_comment *vc, +// ogg_packet *op, +// ogg_packet *op_comm, +// ogg_packet *op_code){ +// int ret=OV_EIMPL; +// vorbis_info *vi=v->vi; +// oggpack_buffer opb; +// private_state *b=v->backend_state; +// +// if(!b){ +// ret=OV_EFAULT; +// goto err_out; +// } +// +// /* first header packet **********************************************/ +// +// FMOD_oggpack_writeinit(context, &opb); +// if(_vorbis_pack_info(&opb,vi))goto err_out; +// +// /* build the packet */ +// if(b->header)_ogg_free(b->header); +// b->header=_ogg_malloc(FMOD_oggpack_bytes(&opb)); +// FMOD_memcpy(b->header,opb.buffer,FMOD_oggpack_bytes(&opb)); +// op->packet=b->header; +// op->bytes=FMOD_oggpack_bytes(&opb); +// op->b_o_s=1; +// op->e_o_s=0; +// op->granulepos=0; +// op->packetno=0; +// +// /* second header packet (comments) **********************************/ +// +// oggpack_reset(&opb); +// if(_vorbis_pack_comment(&opb,vc))goto err_out; +// +// if(b->header1)_ogg_free(b->header1); +// b->header1=_ogg_malloc(FMOD_oggpack_bytes(&opb)); +// FMOD_memcpy(b->header1,opb.buffer,FMOD_oggpack_bytes(&opb)); +// op_comm->packet=b->header1; +// op_comm->bytes=FMOD_oggpack_bytes(&opb); +// op_comm->b_o_s=0; +// op_comm->e_o_s=0; +// op_comm->granulepos=0; +// op_comm->packetno=1; +// +// /* third header packet (modes/codebooks) ****************************/ +// +// oggpack_reset(&opb); +// if(_vorbis_pack_books(&opb,vi))goto err_out; +// +// if(b->header2)_ogg_free(b->header2); +// b->header2=_ogg_malloc(FMOD_oggpack_bytes(&opb)); +// FMOD_memcpy(b->header2,opb.buffer,FMOD_oggpack_bytes(&opb)); +// op_code->packet=b->header2; +// op_code->bytes=FMOD_oggpack_bytes(&opb); +// op_code->b_o_s=0; +// op_code->e_o_s=0; +// op_code->granulepos=0; +// op_code->packetno=2; +// +// FMOD_oggpack_writeclear(&opb); +// return(0); +// err_out: +// FMOD_oggpack_writeclear(&opb); +// FMOD_memset(op,0,sizeof(*op)); +// FMOD_memset(op_comm,0,sizeof(*op_comm)); +// FMOD_memset(op_code,0,sizeof(*op_code)); +// +// if(b){ +// if(b->header)_ogg_free(b->header); +// if(b->header1)_ogg_free(b->header1); +// if(b->header2)_ogg_free(b->header2); +// b->header=NULL; +// b->header1=NULL; +// b->header2=NULL; +// } +// return(ret); +//} +// +//double vorbis_granule_time(vorbis_dsp_state *v,ogg_int64_t granulepos){ +// if(granulepos>=0) +// return((double)granulepos/v->vi->rate); +// return(-1); +//} +// +//const char *vorbis_version_string(void){ +// return GENERAL_VENDOR_STRING; +//} diff --git a/lib/ogg_vorbis/vorbis/lib/lookup.h b/lib/ogg_vorbis/vorbis/lib/lookup.h new file mode 100755 index 0000000..93788a0 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/lookup.h @@ -0,0 +1,32 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup based functions + last mod: $Id: lookup.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_LOOKUP_H_ + +#ifdef FLOAT_LOOKUP +extern float FMOD_vorbis_coslook(float a); +extern float FMOD_vorbis_invsqlook(float a); +extern float FMOD_vorbis_invsq2explook(int a); +extern float FMOD_vorbis_fromdBlook(float a); +#endif +#ifdef INT_LOOKUP +extern ogg_int32_t FMOD_vorbis_invsqlook_i(ogg_int32_t a,ogg_int32_t e); +extern ogg_int32_t FMOD_vorbis_coslook_i(ogg_int32_t a); +extern float FMOD_vorbis_fromdBlook_i(ogg_int32_t a); +#endif + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/lookup_data.h b/lib/ogg_vorbis/vorbis/lib/lookup_data.h new file mode 100755 index 0000000..6388a0b --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/lookup_data.h @@ -0,0 +1,192 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup data; generated by lookups.pl; edit there + last mod: $Id: lookup_data.h 16037 2009-05-26 21:10:58Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_LOOKUP_DATA_H_ + +#ifdef FLOAT_LOOKUP +#define COS_LOOKUP_SZ 128 +static const float COS_LOOKUP[COS_LOOKUP_SZ+1]={ + +1.0000000000000f,+0.9996988186962f,+0.9987954562052f,+0.9972904566787f, + +0.9951847266722f,+0.9924795345987f,+0.9891765099648f,+0.9852776423889f, + +0.9807852804032f,+0.9757021300385f,+0.9700312531945f,+0.9637760657954f, + +0.9569403357322f,+0.9495281805930f,+0.9415440651830f,+0.9329927988347f, + +0.9238795325113f,+0.9142097557035f,+0.9039892931234f,+0.8932243011955f, + +0.8819212643484f,+0.8700869911087f,+0.8577286100003f,+0.8448535652497f, + +0.8314696123025f,+0.8175848131516f,+0.8032075314806f,+0.7883464276266f, + +0.7730104533627f,+0.7572088465065f,+0.7409511253550f,+0.7242470829515f, + +0.7071067811865f,+0.6895405447371f,+0.6715589548470f,+0.6531728429538f, + +0.6343932841636f,+0.6152315905806f,+0.5956993044924f,+0.5758081914178f, + +0.5555702330196f,+0.5349976198871f,+0.5141027441932f,+0.4928981922298f, + +0.4713967368260f,+0.4496113296546f,+0.4275550934303f,+0.4052413140050f, + +0.3826834323651f,+0.3598950365350f,+0.3368898533922f,+0.3136817403989f, + +0.2902846772545f,+0.2667127574749f,+0.2429801799033f,+0.2191012401569f, + +0.1950903220161f,+0.1709618887603f,+0.1467304744554f,+0.1224106751992f, + +0.0980171403296f,+0.0735645635997f,+0.0490676743274f,+0.0245412285229f, + +0.0000000000000f,-0.0245412285229f,-0.0490676743274f,-0.0735645635997f, + -0.0980171403296f,-0.1224106751992f,-0.1467304744554f,-0.1709618887603f, + -0.1950903220161f,-0.2191012401569f,-0.2429801799033f,-0.2667127574749f, + -0.2902846772545f,-0.3136817403989f,-0.3368898533922f,-0.3598950365350f, + -0.3826834323651f,-0.4052413140050f,-0.4275550934303f,-0.4496113296546f, + -0.4713967368260f,-0.4928981922298f,-0.5141027441932f,-0.5349976198871f, + -0.5555702330196f,-0.5758081914178f,-0.5956993044924f,-0.6152315905806f, + -0.6343932841636f,-0.6531728429538f,-0.6715589548470f,-0.6895405447371f, + -0.7071067811865f,-0.7242470829515f,-0.7409511253550f,-0.7572088465065f, + -0.7730104533627f,-0.7883464276266f,-0.8032075314806f,-0.8175848131516f, + -0.8314696123025f,-0.8448535652497f,-0.8577286100003f,-0.8700869911087f, + -0.8819212643484f,-0.8932243011955f,-0.9039892931234f,-0.9142097557035f, + -0.9238795325113f,-0.9329927988347f,-0.9415440651830f,-0.9495281805930f, + -0.9569403357322f,-0.9637760657954f,-0.9700312531945f,-0.9757021300385f, + -0.9807852804032f,-0.9852776423889f,-0.9891765099648f,-0.9924795345987f, + -0.9951847266722f,-0.9972904566787f,-0.9987954562052f,-0.9996988186962f, + -1.0000000000000f, +}; + +#define INVSQ_LOOKUP_SZ 32 +static const float INVSQ_LOOKUP[INVSQ_LOOKUP_SZ+1]={ + 1.414213562373f,1.392621247646f,1.371988681140f,1.352246807566f, + 1.333333333333f,1.315191898443f,1.297771369046f,1.281025230441f, + 1.264911064067f,1.249390095109f,1.234426799697f,1.219988562661f, + 1.206045378311f,1.192569588000f,1.179535649239f,1.166919931983f, + 1.154700538379f,1.142857142857f,1.131370849898f,1.120224067222f, + 1.109400392450f,1.098884511590f,1.088662107904f,1.078719779941f, + 1.069044967650f,1.059625885652f,1.050451462878f,1.041511287847f, + 1.032795558989f,1.024295039463f,1.016001016002f,1.007905261358f, + 1.000000000000f, +}; + +#define INVSQ2EXP_LOOKUP_MIN (-32) +#define INVSQ2EXP_LOOKUP_MAX 32 +static const float INVSQ2EXP_LOOKUP[INVSQ2EXP_LOOKUP_MAX-\ + INVSQ2EXP_LOOKUP_MIN+1]={ + 65536.f, 46340.95001f, 32768.f, 23170.47501f, + 16384.f, 11585.2375f, 8192.f, 5792.618751f, + 4096.f, 2896.309376f, 2048.f, 1448.154688f, + 1024.f, 724.0773439f, 512.f, 362.038672f, + 256.f, 181.019336f, 128.f, 90.50966799f, + 64.f, 45.254834f, 32.f, 22.627417f, + 16.f, 11.3137085f, 8.f, 5.656854249f, + 4.f, 2.828427125f, 2.f, 1.414213562f, + 1.f, 0.7071067812f, 0.5f, 0.3535533906f, + 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, + 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, + 0.015625f, 0.01104854346f, 0.0078125f, 0.005524271728f, + 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f, + 0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, + 0.000244140625f,0.0001726334915f,0.0001220703125f,8.631674575e-05f, + 6.103515625e-05f,4.315837288e-05f,3.051757812e-05f,2.157918644e-05f, + 1.525878906e-05f, +}; + +#endif + +#define FROMdB_LOOKUP_SZ 35 +#define FROMdB2_LOOKUP_SZ 32 +#define FROMdB_SHIFT 5 +#define FROMdB2_SHIFT 3 +#define FROMdB2_MASK 31 + +#ifdef FLOAT_LOOKUP +static const float FROMdB_LOOKUP[FROMdB_LOOKUP_SZ]={ + 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, + 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f, + 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, + 0.003981071706f, 0.002511886432f, 0.001584893192f, 0.001f, + 0.0006309573445f,0.0003981071706f,0.0002511886432f,0.0001584893192f, + 0.0001f,6.309573445e-05f,3.981071706e-05f,2.511886432e-05f, + 1.584893192e-05f, 1e-05f,6.309573445e-06f,3.981071706e-06f, + 2.511886432e-06f,1.584893192e-06f, 1e-06f,6.309573445e-07f, + 3.981071706e-07f,2.511886432e-07f,1.584893192e-07f, +}; + +static const float FROMdB2_LOOKUP[FROMdB2_LOOKUP_SZ]={ + 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, + 0.9372921937f, 0.92390007f, 0.9106992942f, 0.8976871324f, + 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f, + 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, + 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, + 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, + 0.7028699885f, 0.6928273125f, 0.6829281272f, 0.6731703824f, + 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, +}; +#endif + +#ifdef INT_LOOKUP + +#define INVSQ_LOOKUP_I_SHIFT 10 +#define INVSQ_LOOKUP_I_MASK 1023 +static const ogg_int32_t INVSQ_LOOKUP_I[64+1]={ + 92682l, 91966l, 91267l, 90583l, + 89915l, 89261l, 88621l, 87995l, + 87381l, 86781l, 86192l, 85616l, + 85051l, 84497l, 83953l, 83420l, + 82897l, 82384l, 81880l, 81385l, + 80899l, 80422l, 79953l, 79492l, + 79039l, 78594l, 78156l, 77726l, + 77302l, 76885l, 76475l, 76072l, + 75674l, 75283l, 74898l, 74519l, + 74146l, 73778l, 73415l, 73058l, + 72706l, 72359l, 72016l, 71679l, + 71347l, 71019l, 70695l, 70376l, + 70061l, 69750l, 69444l, 69141l, + 68842l, 68548l, 68256l, 67969l, + 67685l, 67405l, 67128l, 66855l, + 66585l, 66318l, 66054l, 65794l, + 65536l, +}; + +#define COS_LOOKUP_I_SHIFT 9 +#define COS_LOOKUP_I_MASK 511 +#define COS_LOOKUP_I_SZ 128 +static const ogg_int32_t COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ + 16384l, 16379l, 16364l, 16340l, + 16305l, 16261l, 16207l, 16143l, + 16069l, 15986l, 15893l, 15791l, + 15679l, 15557l, 15426l, 15286l, + 15137l, 14978l, 14811l, 14635l, + 14449l, 14256l, 14053l, 13842l, + 13623l, 13395l, 13160l, 12916l, + 12665l, 12406l, 12140l, 11866l, + 11585l, 11297l, 11003l, 10702l, + 10394l, 10080l, 9760l, 9434l, + 9102l, 8765l, 8423l, 8076l, + 7723l, 7366l, 7005l, 6639l, + 6270l, 5897l, 5520l, 5139l, + 4756l, 4370l, 3981l, 3590l, + 3196l, 2801l, 2404l, 2006l, + 1606l, 1205l, 804l, 402l, + 0l, -401l, -803l, -1204l, + -1605l, -2005l, -2403l, -2800l, + -3195l, -3589l, -3980l, -4369l, + -4755l, -5138l, -5519l, -5896l, + -6269l, -6638l, -7004l, -7365l, + -7722l, -8075l, -8422l, -8764l, + -9101l, -9433l, -9759l, -10079l, + -10393l, -10701l, -11002l, -11296l, + -11584l, -11865l, -12139l, -12405l, + -12664l, -12915l, -13159l, -13394l, + -13622l, -13841l, -14052l, -14255l, + -14448l, -14634l, -14810l, -14977l, + -15136l, -15285l, -15425l, -15556l, + -15678l, -15790l, -15892l, -15985l, + -16068l, -16142l, -16206l, -16260l, + -16304l, -16339l, -16363l, -16378l, + -16383l, +}; + +#endif + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/lpc.c b/lib/ogg_vorbis/vorbis/lib/lpc.c new file mode 100755 index 0000000..7cf5ddb --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/lpc.c @@ -0,0 +1,160 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: LPC low level routines + last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +/* Some of these routines (autocorrelator, LPC coefficient estimator) + are derived from code written by Jutta Degener and Carsten Bormann; + thus we include their copyright below. The entirety of this file + is freely redistributable on the condition that both of these + copyright notices are preserved without modification. */ + +/* Preserved Copyright: *********************************************/ + +/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universita"t Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universita"t +Berlin are deemed to have made any representations as to the +suitability of this software for any purpose nor are held responsible +for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR +THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener +Carsten Bormann + +*********************************************************************/ + +#include +#include +//#include +#include "os.h" +#include "smallft.h" +#include "lpc.h" +#include "scales.h" +#include "misc.h" + +/* Autocorrelation LPC coeff generation algorithm invented by + N. Levinson in 1947, modified by J. Durbin in 1959. */ + +/* Input : n elements of time doamin data + Output: m lpc coefficients, excitation energy */ + +float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){ + float *aut=alloca(sizeof(*aut)*(m+1)); + float *lpc=alloca(sizeof(*lpc)*(m)); + float error; + float epsilon; + int i,j; + + /* autocorrelation, p+1 lag coefficients */ + j=m+1; + while(j--){ + float d=0; /* float needed for accumulator depth */ + for(i=j;i +#include +#include +#include "lsp.h" +#include "os.h" +#include "misc.h" +#include "lookup.h" +#include "scales.h" + +/* three possible LSP to f curve functions; the exact computation + (float), a lookup based float implementation, and an integer + implementation. The float lookup is likely the optimal choice on + any machine with an FPU. The integer implementation is *not* fixed + point (due to the need for a large dynamic range and thus a + seperately tracked exponent) and thus much more complex than the + relatively simple float implementations. It's mostly for future + work on a fully fixed point implementation for processors like the + ARM family. */ + +/* define either of these (preferably FLOAT_LOOKUP) to have faster + but less precise implementation. */ +#undef FLOAT_LOOKUP +#undef INT_LOOKUP + +#ifdef FLOAT_LOOKUP +#include "lookup.c" /* catch this in the build system; we #include for + compilers (like gcc) that can't inline across + modules */ + +/* side effect: changes *lsp to cosines of lsp */ +void FMOD_vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + int i; + float wdel=M_PI/ln; + vorbis_fpu_control fpu; + + FMOD_vorbis_fpu_setround(&fpu); + for(i=0;i>1; + + do{ + q*=ftmp[0]-w; + p*=ftmp[1]-w; + ftmp+=2; + }while(--c); + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + q*=ftmp[0]-w; + q*=q; + p*=p*(1.f-w*w); + }else{ + /* even order filter; still symmetric */ + q*=q*(1.f+w); + p*=p*(1.f-w); + } + + q=FMOD_ogg_frexp(p+q,&qexp); + q=FMOD_vorbis_fromdBlook(amp* + vorbis_invsqlook(q)* + vorbis_invsq2explook(qexp+m)- + ampoffset); + + do{ + curve[i++]*=q; + }while(map[i]==k); + } + FMOD_vorbis_fpu_restore(fpu); +} + +#else + +#ifdef INT_LOOKUP +#include "lookup.c" /* catch this in the build system; we #include for + compilers (like gcc) that can't inline across + modules */ + +static const int MLOOP_1[64]={ + 0,10,11,11, 12,12,12,12, 13,13,13,13, 13,13,13,13, + 14,14,14,14, 14,14,14,14, 14,14,14,14, 14,14,14,14, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, +}; + +static const int MLOOP_2[64]={ + 0,4,5,5, 6,6,6,6, 7,7,7,7, 7,7,7,7, + 8,8,8,8, 8,8,8,8, 8,8,8,8, 8,8,8,8, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, +}; + +static const int MLOOP_3[8]={0,1,2,2,3,3,3,3}; + + +/* side effect: changes *lsp to cosines of lsp */ +void FMOD_vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + + /* 0 <= m < 256 */ + + /* set up for using all int later */ + int i; + int ampoffseti=FMOD_ogg_rint(ampoffset*4096.f); + int ampi=FMOD_ogg_rint(amp*16.f); + ogg_int32_t *ilsp=alloca(m*sizeof(*ilsp)); + for(i=0;i>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)*labs(ilsp[j]-wi); + qexp+=shift; + } + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + /* pi,qi normalized collectively, both tracked using qexp */ + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)<<14; + qexp+=shift; + + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + pi>>=shift; + qi>>=shift; + qexp+=shift-14*((m+1)>>1); + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + + }else{ + /* even order filter; still symmetric */ + + /* p*=p(1-w), q*=q(1+w), let normalization drift because it isn't + worth tracking step by step */ + + pi>>=shift; + qi>>=shift; + qexp+=shift-7*m; + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + qi=(qi+pi)>>14; + + } + + + /* we've let the normalization drift because it wasn't important; + however, for the lookup, things must be normalized again. We + need at most one right shift or a number of left shifts */ + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + while(qi && !(qi&0x8000)){ /* checks for 0.0xxxxxxxxxxxxxxx or less*/ + qi<<=1; qexp--; + } + + amp=FMOD_vorbis_fromdBlook_i(ampi* /* n.4 */ + vorbis_invsqlook_i(qi,qexp)- + /* m.8, m+n<=8 */ + ampoffseti); /* 8.12[0] */ + + curve[i]*=amp; + while(map[++i]==k)curve[i]*=amp; + } +} + +#else + +/* old, nonoptimized but simple version for any poor sap who needs to + figure out what the hell this code does, or wants the other + fraction of a dB precision */ + +/* side effect: changes *lsp to cosines of lsp */ +void FMOD_vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + int i; + float wdel=M_PI/ln; + for(i=0;i= i; j--) { + g[j-2] -= g[j]; + g[j] += g[j]; + } + } +} + +static int comp(const void *a,const void *b){ + return (*(float *)a<*(float *)b)-(*(float *)a>*(float *)b); +} + +/* Newton-Raphson-Maehly actually functioned as a decent root finder, + but there are root sets for which it gets into limit cycles + (exacerbated by zero suppression) and fails. We can't afford to + fail, even if the failure is 1 in 100,000,000, so we now use + Laguerre and later polish with Newton-Raphson (which can then + afford to fail) */ + +#define EPSILON 10e-7 +static int Laguerre_With_Deflation(float *a,int ord,float *r){ + int i,m; + double lastdelta=0.f; + double *defl=alloca(sizeof(*defl)*(ord+1)); + for(i=0;i<=ord;i++)defl[i]=a[i]; + + for(m=ord;m>0;m--){ + double new=0.f,delta; + + /* iterate a root */ + while(1){ + double p=defl[m],pp=0.f,ppp=0.f,denom; + + /* eval the polynomial and its first two derivatives */ + for(i=m;i>0;i--){ + ppp = new*ppp + pp; + pp = new*pp + p; + p = new*p + defl[i-1]; + } + + /* Laguerre's method */ + denom=(m-1) * ((m-1)*pp*pp - m*p*ppp); + if(denom<0) + return(-1); /* complex root! The LPC generator handed us a bad filter */ + + if(pp>0){ + denom = pp + FMOD_ogg_sqrt(denom); + if(denom-(EPSILON))denom=-(EPSILON); + } + + delta = m*p/denom; + new -= delta; + + if(delta<0.f)delta*=-1; + + if(fabs(delta/new)<10e-12)break; + lastdelta=delta; + } + + r[m-1]=(float)new; + + /* forward deflation */ + + for(i=m;i>0;i--) + defl[i-1]+=new*defl[i]; + defl++; + + } + return(0); +} + + +/* for spit-and-polish only */ +static int Newton_Raphson(float *a,int ord,float *r){ + int i, k, count=0; + double error=1.f; + double *root=alloca(ord*sizeof(*root)); + + for(i=0; i1e-20){ + error=0; + + for(i=0; i= 0; k--) { + + pp= pp* rooti + p; + p = p * rooti + a[k]; + } + + delta = p/pp; + root[i] -= delta; + error+= delta*delta; + } + + if(count>40)return(-1); + + count++; + } + + /* Replaced the original bubble sort with a real sort. With your + help, we can eliminate the bubble sort in our lifetime. --Monty */ + + for(i=0; i>1; + int g1_order,g2_order; + float *g1=alloca(sizeof(*g1)*(order2+1)); + float *g2=alloca(sizeof(*g2)*(order2+1)); + float *g1r=alloca(sizeof(*g1r)*(order2+1)); + float *g2r=alloca(sizeof(*g2r)*(order2+1)); + int i; + + /* even and odd are slightly different base cases */ + g1_order=(m+1)>>1; + g2_order=(m) >>1; + + /* Compute the lengths of the x polynomials. */ + /* Compute the first half of K & R F1 & F2 polynomials. */ + /* Compute half of the symmetric and antisymmetric polynomials. */ + /* Remove the roots at +1 and -1. */ + + g1[g1_order] = 1.f; + for(i=1;i<=g1_order;i++) g1[g1_order-i] = lpc[i-1]+lpc[m-i]; + g2[g2_order] = 1.f; + for(i=1;i<=g2_order;i++) g2[g2_order-i] = lpc[i-1]-lpc[m-i]; + + if(g1_order>g2_order){ + for(i=2; i<=g2_order;i++) g2[g2_order-i] += g2[g2_order-i+2]; + }else{ + for(i=1; i<=g1_order;i++) g1[g1_order-i] -= g1[g1_order-i+1]; + for(i=1; i<=g2_order;i++) g2[g2_order-i] += g2[g2_order-i+1]; + } + + /* Convert into polynomials in cos(alpha) */ + cheby(g1,g1_order); + cheby(g2,g2_order); + + /* Find the roots of the 2 even polynomials.*/ + if(Laguerre_With_Deflation(g1,g1_order,g1r) || + Laguerre_With_Deflation(g2,g2_order,g2r)) + return(-1); + + Newton_Raphson(g1,g1_order,g1r); /* if it fails, it leaves g1r alone */ + Newton_Raphson(g2,g2_order,g2r); /* if it fails, it leaves g2r alone */ + + qsort(g1r,g1_order,sizeof(*g1r),comp); + qsort(g2r,g2_order,sizeof(*g2r),comp); + + for(i=0;i +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "window.h" +#include "registry.h" +#include "psy.h" +#include "misc.h" + +/* simplistic, wasteful way of doing this (unique lookup for each + mode/submapping); there should be a central repository for + identical lookups. That will require minor work, so I'm putting it + off as low priority. + + Why a lookup for each backend in a given mode? Because the + blocksize is set by the mode, and low backend lookups may require + parameters from other areas of the mode/mapping */ + +static void mapping0_free_info(void *context, vorbis_info_mapping *i){ + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)i; + if(info){ + FMOD_memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static int ilog(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +#if 0 +static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, + oggpack_buffer *opb){ + int i; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)vm; + + /* another 'we meant to do it this way' hack... up to beta 4, we + packed 4 binary zeros here to signify one submapping in use. We + now redefine that to mean four bitflags that indicate use of + deeper features; bit0:submappings, bit1:coupling, + bit2,3:reserved. This is backward compatable with all actual uses + of the beta code. */ + + if(info->submaps>1){ + FMOD_oggpack_write(opb,1,1); + FMOD_oggpack_write(opb,info->submaps-1,4); + }else + FMOD_oggpack_write(opb,0,1); + + if(info->coupling_steps>0){ + FMOD_oggpack_write(opb,1,1); + FMOD_oggpack_write(opb,info->coupling_steps-1,8); + + for(i=0;icoupling_steps;i++){ + FMOD_oggpack_write(opb,info->coupling_mag[i],ilog(vi->channels)); + FMOD_oggpack_write(opb,info->coupling_ang[i],ilog(vi->channels)); + } + }else + FMOD_oggpack_write(opb,0,1); + + FMOD_oggpack_write(opb,0,2); /* 2,3:reserved */ + + /* we don't write the channel submappings if we only have one... */ + if(info->submaps>1){ + for(i=0;ichannels;i++) + FMOD_oggpack_write(opb,info->chmuxlist[i],4); + } + for(i=0;isubmaps;i++){ + FMOD_oggpack_write(opb,0,8); /* time submap unused */ + FMOD_oggpack_write(opb,info->floorsubmap[i],8); + FMOD_oggpack_write(opb,info->residuesubmap[i],8); + } +} +#endif + +/* also responsible for range checking */ +static vorbis_info_mapping *mapping0_unpack(void *context, vorbis_info *vi,oggpack_buffer *opb){ + int i,b; + vorbis_info_mapping0 *info=_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=vi->codec_setup; + FMOD_memset(info,0,sizeof(*info)); + + b=FMOD_oggpack_read(opb,1); + if(b<0)goto err_out; + if(b){ + info->submaps=FMOD_oggpack_read(opb,4)+1; + if(info->submaps<=0)goto err_out; + }else + info->submaps=1; + + b=FMOD_oggpack_read(opb,1); + if(b<0)goto err_out; + if(b){ + info->coupling_steps=FMOD_oggpack_read(opb,8)+1; + if(info->coupling_steps<=0)goto err_out; + for(i=0;icoupling_steps;i++){ + int testM=info->coupling_mag[i]=FMOD_oggpack_read(opb,ilog(vi->channels)); + int testA=info->coupling_ang[i]=FMOD_oggpack_read(opb,ilog(vi->channels)); + + if(testM<0 || + testA<0 || + testM==testA || + testM>=vi->channels || + testA>=vi->channels) goto err_out; + } + + } + + if(FMOD_oggpack_read(opb,2)!=0)goto err_out; /* 2,3:reserved */ + + if(info->submaps>1){ + for(i=0;ichannels;i++){ + info->chmuxlist[i]=FMOD_oggpack_read(opb,4); + if(info->chmuxlist[i]>=info->submaps || info->chmuxlist[i]<0)goto err_out; + } + } + for(i=0;isubmaps;i++){ + FMOD_oggpack_read(opb,8); /* time submap unused */ + info->floorsubmap[i]=FMOD_oggpack_read(opb,8); + if(info->floorsubmap[i]>=ci->floors || info->floorsubmap[i]<0)goto err_out; + info->residuesubmap[i]=FMOD_oggpack_read(opb,8); + if(info->residuesubmap[i]>=ci->residues || info->residuesubmap[i]<0)goto err_out; + } + + return info; + + err_out: + mapping0_free_info(context, info); + return(NULL); +} + +#include "os.h" +#include "lpc.h" +#include "lsp.h" +//#include "envelope.h" +#include "mdct.h" +#include "psy.h" +#include "scales.h" + +#if 0 +static ogg_int32_t seq=0; +static ogg_int64_t total=0; +static float FLOOR1_fromdB_LOOKUP[256]={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F, +}; + +#endif + +//static int mapping0_forward(void *context, vorbis_block *vb){ +// vorbis_dsp_state *vd=vb->vd; +// vorbis_info *vi=vd->vi; +// codec_setup_info *ci=vi->codec_setup; +// private_state *b=vb->vd->backend_state; +// vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; +// int n=vb->pcmend; +// int i,j,k; +// +// int *nonzero = alloca(sizeof(*nonzero)*vi->channels); +// float **gmdct = _FMOD_vorbis_block_alloc(context, vb,vi->channels*sizeof(*gmdct)); +// int **ilogmaskch= _FMOD_vorbis_block_alloc(context, vb,vi->channels*sizeof(*ilogmaskch)); +// int ***floor_posts = _FMOD_vorbis_block_alloc(context, vb,vi->channels*sizeof(*floor_posts)); +// +// float global_ampmax=vbi->ampmax; +// float *local_ampmax=alloca(sizeof(*local_ampmax)*vi->channels); +// int blocktype=vbi->blocktype; +// +// int modenumber=vb->W; +// vorbis_info_mapping0 *info=ci->map_param[modenumber]; +// vorbis_look_psy *psy_look= +// b->psy+blocktype+(vb->W?2:0); +// +// vb->mode=modenumber; +// +// for(i=0;ichannels;i++){ +// float scale=4.f/n; +// float scale_dB; +// +// float *pcm =vb->pcm[i]; +// float *logfft =pcm; +// +// gmdct[i]=_FMOD_vorbis_block_alloc(context, vb,n/2*sizeof(**gmdct)); +// +// scale_dB=todB(&scale) + .345f; /* + .345 is a hack; the original +// todB estimation used on IEEE 754 +// compliant machines had a bug that +// returned dB values about a third +// of a decibel too high. The bug +// was harmless because tunings +// implicitly took that into +// account. However, fixing the bug +// in the estimator requires +// changing all the tunings as well. +// For now, it's easier to sync +// things back up here, and +// recalibrate the tunings in the +// next major model upgrade. */ +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("pcmL",seq,pcm,n,0,0,total-n/2); +// else +// _analysis_output("pcmR",seq,pcm,n,0,0,total-n/2); +// }else{ +// _analysis_output("pcm",seq,pcm,n,0,0,total-n/2); +// } +//#endif +// +// /* window the PCM data */ +// _FMOD_vorbis_apply_window(pcm,b->window,ci->blocksizes,vb->lW,vb->W,vb->nW); +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("windowedL",seq,pcm,n,0,0,total-n/2); +// else +// _analysis_output("windowedR",seq,pcm,n,0,0,total-n/2); +// }else{ +// _analysis_output("windowed",seq,pcm,n,0,0,total-n/2); +// } +//#endif +// +// /* transform the PCM data */ +// /* only MDCT right now.... */ +// FMOD_mdct_forward(b->transform[vb->W][0],pcm,gmdct[i]); +// +// /* FFT yields more accurate tonal estimation (not phase sensitive) */ +// drft_forward(&b->fft_look[vb->W],pcm); +// logfft[0]=scale_dB+todB(pcm) + .345f; /* + .345 is a hack; the +// original todB estimation used on +// IEEE 754 compliant machines had a +// bug that returned dB values about +// a third of a decibel too high. +// The bug was harmless because +// tunings implicitly took that into +// account. However, fixing the bug +// in the estimator requires +// changing all the tunings as well. +// For now, it's easier to sync +// things back up here, and +// recalibrate the tunings in the +// next major model upgrade. */ +// local_ampmax[i]=logfft[0]; +// for(j=1;j>1]=scale_dB+.5f*todB(&temp) + .345f; /* + +// .345 is a hack; the original todB +// estimation used on IEEE 754 +// compliant machines had a bug that +// returned dB values about a third +// of a decibel too high. The bug +// was harmless because tunings +// implicitly took that into +// account. However, fixing the bug +// in the estimator requires +// changing all the tunings as well. +// For now, it's easier to sync +// things back up here, and +// recalibrate the tunings in the +// next major model upgrade. */ +// if(temp>local_ampmax[i])local_ampmax[i]=temp; +// } +// +// if(local_ampmax[i]>0.f)local_ampmax[i]=0.f; +// if(local_ampmax[i]>global_ampmax)global_ampmax=local_ampmax[i]; +// +//#if 0 +// if(vi->channels==2){ +// if(i==0){ +// _analysis_output("fftL",seq,logfft,n/2,1,0,0); +// }else{ +// _analysis_output("fftR",seq,logfft,n/2,1,0,0); +// } +// }else{ +// _analysis_output("fft",seq,logfft,n/2,1,0,0); +// } +//#endif +// +// } +// +// { +// float *noise = _FMOD_vorbis_block_alloc(context, vb,n/2*sizeof(*noise)); +// float *tone = _FMOD_vorbis_block_alloc(context, vb,n/2*sizeof(*tone)); +// +// for(i=0;ichannels;i++){ +// /* the encoder setup assumes that all the modes used by any +// specific bitrate tweaking use the same floor */ +// +// int submap=info->chmuxlist[i]; +// +// /* the following makes things clearer to *me* anyway */ +// float *mdct =gmdct[i]; +// float *logfft =vb->pcm[i]; +// +// float *logmdct =logfft+n/2; +// float *logmask =logfft; +// +// vb->mode=modenumber; +// +// floor_posts[i]=_FMOD_vorbis_block_alloc(context, vb,PACKETBLOBS*sizeof(**floor_posts)); +// FMOD_memset(floor_posts[i],0,sizeof(**floor_posts)*PACKETBLOBS); +// +// for(j=0;jchannels==2){ +// if(i==0) +// _analysis_output("mdctL",seq,logmdct,n/2,1,0,0); +// else +// _analysis_output("mdctR",seq,logmdct,n/2,1,0,0); +// }else{ +// _analysis_output("mdct",seq,logmdct,n/2,1,0,0); +// } +//#endif +// +// /* first step; noise masking. Not only does 'noise masking' +// give us curves from which we can decide how much resolution +// to give noise parts of the spectrum, it also implicitly hands +// us a tonality estimate (the larger the value in the +// 'noise_depth' vector, the more tonal that area is) */ +// +// _vp_noisemask(psy_look, +// logmdct, +// noise); /* noise does not have by-frequency offset +// bias applied yet */ +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("noiseL",seq,noise,n/2,1,0,0); +// else +// _analysis_output("noiseR",seq,noise,n/2,1,0,0); +// }else{ +// _analysis_output("noise",seq,noise,n/2,1,0,0); +// } +//#endif +// +// /* second step: 'all the other crap'; all the stuff that isn't +// computed/fit for bitrate management goes in the second psy +// vector. This includes tone masking, peak limiting and ATH */ +// +// _vp_tonemask(psy_look, +// logfft, +// tone, +// global_ampmax, +// local_ampmax[i]); +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("toneL",seq,tone,n/2,1,0,0); +// else +// _analysis_output("toneR",seq,tone,n/2,1,0,0); +// }else{ +// _analysis_output("tone",seq,tone,n/2,1,0,0); +// } +//#endif +// +// /* third step; we offset the noise vectors, overlay tone +// masking. We then do a floor1-specific line fit. If we're +// performing bitrate management, the line fit is performed +// multiple times for up/down tweakage on demand. */ +// +//#if 0 +// { +// float aotuv[psy_look->n]; +//#endif +// +// _vp_offset_and_mix(psy_look, +// noise, +// tone, +// 1, +// logmask, +// mdct, +// logmdct); +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("aotuvM1_L",seq,aotuv,psy_look->n,1,1,0); +// else +// _analysis_output("aotuvM1_R",seq,aotuv,psy_look->n,1,1,0); +// }else{ +// _analysis_output("aotuvM1",seq,aotuv,psy_look->n,1,1,0); +// } +// } +//#endif +// +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("mask1L",seq,logmask,n/2,1,0,0); +// else +// _analysis_output("mask1R",seq,logmask,n/2,1,0,0); +// }else{ +// _analysis_output("mask1",seq,logmask,n/2,1,0,0); +// } +//#endif +// +// /* this algorithm is hardwired to floor 1 for now; abort out if +// we're *not* floor1. This won't happen unless someone has +// broken the encode setup lib. Guard it anyway. */ +// if(ci->floor_type[info->floorsubmap[submap]]!=1)return(-1); +// +// floor_posts[i][PACKETBLOBS/2]= +// floor1_fit(context, vb,b->flr[info->floorsubmap[submap]], +// logmdct, +// logmask); +// +// /* are we managing bitrate? If so, perform two more fits for +// later rate tweaking (fits represent hi/lo) */ +// if(FMOD_vorbis_bitrate_managed(vb) && floor_posts[i][PACKETBLOBS/2]){ +// /* higher rate by way of lower noise curve */ +// +// _vp_offset_and_mix(psy_look, +// noise, +// tone, +// 2, +// logmask, +// mdct, +// logmdct); +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("mask2L",seq,logmask,n/2,1,0,0); +// else +// _analysis_output("mask2R",seq,logmask,n/2,1,0,0); +// }else{ +// _analysis_output("mask2",seq,logmask,n/2,1,0,0); +// } +//#endif +// +// floor_posts[i][PACKETBLOBS-1]= +// floor1_fit(context, vb,b->flr[info->floorsubmap[submap]], +// logmdct, +// logmask); +// +// /* lower rate by way of higher noise curve */ +// _vp_offset_and_mix(psy_look, +// noise, +// tone, +// 0, +// logmask, +// mdct, +// logmdct); +// +//#if 0 +// if(vi->channels==2){ +// if(i==0) +// _analysis_output("mask0L",seq,logmask,n/2,1,0,0); +// else +// _analysis_output("mask0R",seq,logmask,n/2,1,0,0); +// }else{ +// _analysis_output("mask0",seq,logmask,n/2,1,0,0); +// } +//#endif +// +// floor_posts[i][0]= +// floor1_fit(context, vb,b->flr[info->floorsubmap[submap]], +// logmdct, +// logmask); +// +// /* we also interpolate a range of intermediate curves for +// intermediate rates */ +// for(k=1;kflr[info->floorsubmap[submap]], +// floor_posts[i][0], +// floor_posts[i][PACKETBLOBS/2], +// k*65536/(PACKETBLOBS/2)); +// for(k=PACKETBLOBS/2+1;kflr[info->floorsubmap[submap]], +// floor_posts[i][PACKETBLOBS/2], +// floor_posts[i][PACKETBLOBS-1], +// (k-PACKETBLOBS/2)*65536/(PACKETBLOBS/2)); +// } +// } +// } +// vbi->ampmax=global_ampmax; +// +// /* +// the next phases are performed once for vbr-only and PACKETBLOB +// times for bitrate managed modes. +// +// 1) encode actual mode being used +// 2) encode the floor for each channel, compute coded mask curve/res +// 3) normalize and couple. +// 4) encode residue +// 5) save packet bytes to the packetblob vector +// +// */ +// +// /* iterate over the many masking curve fits we've created */ +// +// { +// float **res_bundle=alloca(sizeof(*res_bundle)*vi->channels); +// float **couple_bundle=alloca(sizeof(*couple_bundle)*vi->channels); +// int *zerobundle=alloca(sizeof(*zerobundle)*vi->channels); +// int **sortindex=alloca(sizeof(*sortindex)*vi->channels); +// float **mag_memo=NULL; +// int **mag_sort=NULL; +// +// if(info->coupling_steps){ +// mag_memo=_vp_quantize_couple_memo(context, vb, +// &ci->psy_g_param, +// psy_look, +// info, +// gmdct); +// +// mag_sort=_vp_quantize_couple_sort(context, vb, +// psy_look, +// info, +// mag_memo); +// +// hf_reduction(&ci->psy_g_param, +// psy_look, +// info, +// mag_memo); +// } +// +// FMOD_memset(sortindex,0,sizeof(*sortindex)*vi->channels); +// if(psy_look->vi->normal_channel_p){ +// for(i=0;ichannels;i++){ +// float *mdct =gmdct[i]; +// sortindex[i]=alloca(sizeof(**sortindex)*n/2); +// _vp_noise_normalize_sort(psy_look,mdct,sortindex[i]); +// } +// } +// +// for(k=(FMOD_vorbis_bitrate_managed(vb)?0:PACKETBLOBS/2); +// k<=(FMOD_vorbis_bitrate_managed(vb)?PACKETBLOBS-1:PACKETBLOBS/2); +// k++){ +// oggpack_buffer *opb=vbi->packetblob[k]; +// +// /* start out our new packet blob with packet type and mode */ +// /* Encode the packet type */ +// FMOD_oggpack_write(opb,0,1); +// /* Encode the modenumber */ +// /* Encode frame mode, pre,post windowsize, then dispatch */ +// FMOD_oggpack_write(opb,modenumber,b->modebits); +// if(vb->W){ +// FMOD_oggpack_write(opb,vb->lW,1); +// FMOD_oggpack_write(opb,vb->nW,1); +// } +// +// /* encode floor, compute masking curve, sep out residue */ +// for(i=0;ichannels;i++){ +// int submap=info->chmuxlist[i]; +// float *mdct =gmdct[i]; +// float *res =vb->pcm[i]; +// int *ilogmask=ilogmaskch[i]= +// _FMOD_vorbis_block_alloc(context, vb,n/2*sizeof(**gmdct)); +// +// nonzero[i]=floor1_encode(opb,vb,b->flr[info->floorsubmap[submap]], +// floor_posts[i][k], +// ilogmask); +//#if 0 +// { +// char buf[80]; +// sprintf(buf,"maskI%c%d",i?'R':'L',k); +// float work[n/2]; +// for(j=0;jpsy_g_param.sliding_lowpass[vb->W][k]); +// +// _vp_noise_normalize(psy_look,res,res+n/2,sortindex[i]); +// +// +//#if 0 +// { +// char buf[80]; +// float work[n/2]; +// for(j=0;jcoupling_steps){ +// _vp_couple(k, +// &ci->psy_g_param, +// psy_look, +// info, +// vb->pcm, +// mag_memo, +// mag_sort, +// ilogmaskch, +// nonzero, +// ci->psy_g_param.sliding_lowpass[vb->W][k]); +// } +// +// /* classify and encode by submap */ +// for(i=0;isubmaps;i++){ +// int ch_in_bundle=0; +// ogg_int32_t **classifications; +// int resnum=info->residuesubmap[i]; +// +// for(j=0;jchannels;j++){ +// if(info->chmuxlist[j]==i){ +// zerobundle[ch_in_bundle]=0; +// if(nonzero[j])zerobundle[ch_in_bundle]=1; +// res_bundle[ch_in_bundle]=vb->pcm[j]; +// couple_bundle[ch_in_bundle++]=vb->pcm[j]+n/2; +// } +// } +// +// classifications=_FMOD_residue_P[ci->residue_type[resnum]]-> +// class(context, vb,b->residue[resnum],couple_bundle,zerobundle,ch_in_bundle); +// +// /* couple_bundle is destructively overwritten by +// the class function if some but not all of the channels are +// marked as silence; build a fresh copy */ +// ch_in_bundle=0; +// for(j=0;jchannels;j++) +// if(info->chmuxlist[j]==i) +// couple_bundle[ch_in_bundle++]=vb->pcm[j]+n/2; +// +// _FMOD_residue_P[ci->residue_type[resnum]]-> +// forward(context, opb,vb,b->residue[resnum], +// couple_bundle,NULL,zerobundle,ch_in_bundle,classifications); +// } +// +// /* ok, done encoding. Next protopacket. */ +// } +// +// } +// +//#if 0 +// seq++; +// total+=ci->blocksizes[vb->W]/4+ci->blocksizes[vb->nW]/4; +//#endif +// return(0); +//} + +static int mapping0_inverse(void *context, vorbis_block *vb,vorbis_info_mapping *l){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=vd->backend_state; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)l; + + int i,j; + ogg_int32_t n=vb->pcmend=ci->blocksizes[vb->W]; + + float **pcmbundle=alloca(sizeof(*pcmbundle)*vi->channels); + int *zerobundle=alloca(sizeof(*zerobundle)*vi->channels); + + int *nonzero =alloca(sizeof(*nonzero)*vi->channels); + void **floormemo=alloca(sizeof(*floormemo)*vi->channels); + + /* recover the spectral envelope; store it in the PCM vector for now */ + for(i=0;ichannels;i++){ + int submap=info->chmuxlist[i]; + floormemo[i]=_FMOD_floor_P[ci->floor_type[info->floorsubmap[submap]]]-> + inverse1(context, vb,b->flr[info->floorsubmap[submap]]); + if(floormemo[i]) + nonzero[i]=1; + else + nonzero[i]=0; + FMOD_memset(vb->pcm[i],0,sizeof(*vb->pcm[i])*n/2); + } + + /* channel coupling can 'dirty' the nonzero listing */ + for(i=0;icoupling_steps;i++){ + if(nonzero[info->coupling_mag[i]] || + nonzero[info->coupling_ang[i]]){ + nonzero[info->coupling_mag[i]]=1; + nonzero[info->coupling_ang[i]]=1; + } + } + + /* recover the residue into our working vectors */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + for(j=0;jchannels;j++){ + if(info->chmuxlist[j]==i){ + if(nonzero[j]) + zerobundle[ch_in_bundle]=1; + else + zerobundle[ch_in_bundle]=0; + pcmbundle[ch_in_bundle++]=vb->pcm[j]; + } + } + + _FMOD_residue_P[ci->residue_type[info->residuesubmap[i]]]-> + inverse(context, vb,b->residue[info->residuesubmap[i]], + pcmbundle,zerobundle,ch_in_bundle); + } + + /* channel coupling */ + for(i=info->coupling_steps-1;i>=0;i--){ + float *pcmM=vb->pcm[info->coupling_mag[i]]; + float *pcmA=vb->pcm[info->coupling_ang[i]]; + + for(j=0;j0) + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + else + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + + /* compute and apply spectral envelope */ + for(i=0;ichannels;i++){ + float *pcm=vb->pcm[i]; + int submap=info->chmuxlist[i]; + _FMOD_floor_P[ci->floor_type[info->floorsubmap[submap]]]-> + inverse2(context, vb,b->flr[info->floorsubmap[submap]], + floormemo[i],pcm); + } + + /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */ + /* only MDCT right now.... */ + for(i=0;ichannels;i++){ + float *pcm=vb->pcm[i]; + FMOD_mdct_backward(b->transform[vb->W][0],pcm,pcm); + } + + /* all done! */ + return(0); +} + +/* export hooks */ +const vorbis_func_mapping FMOD_mapping0_exportbundle={ + NULL, /* &mapping0_pack, */ + &mapping0_unpack, + &mapping0_free_info, + NULL, /* &mapping0_forward, */ + &mapping0_inverse +}; diff --git a/lib/ogg_vorbis/vorbis/lib/masking.h b/lib/ogg_vorbis/vorbis/lib/masking.h new file mode 100755 index 0000000..3576ab7 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/masking.h @@ -0,0 +1,785 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: masking curve data for psychoacoustics + last mod: $Id: masking.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_MASKING_H_ +#define _V_MASKING_H_ + +/* more detailed ATH; the bass if flat to save stressing the floor + overly for only a bin or two of savings. */ + +#define MAX_ATH 88 +static const float ATH[]={ + /*15*/ -51, -52, -53, -54, -55, -56, -57, -58, + /*31*/ -59, -60, -61, -62, -63, -64, -65, -66, + /*63*/ -67, -68, -69, -70, -71, -72, -73, -74, + /*125*/ -75, -76, -77, -78, -80, -81, -82, -83, + /*250*/ -84, -85, -86, -87, -88, -88, -89, -89, + /*500*/ -90, -91, -91, -92, -93, -94, -95, -96, + /*1k*/ -96, -97, -98, -98, -99, -99,-100,-100, + /*2k*/ -101,-102,-103,-104,-106,-107,-107,-107, + /*4k*/ -107,-105,-103,-102,-101, -99, -98, -96, + /*8k*/ -95, -95, -96, -97, -96, -95, -93, -90, + /*16k*/ -80, -70, -50, -40, -30, -30, -30, -30 +}; + +/* The tone masking curves from Ehmer's and Fielder's papers have been + replaced by an empirically collected data set. The previously + published values were, far too often, simply on crack. */ + +#define EHMER_OFFSET 16 +#define EHMER_MAX 56 + +/* masking tones from -50 to 0dB, 62.5 through 16kHz at half octaves + test tones from -2 octaves to +5 octaves sampled at eighth octaves */ +/* (Vorbis 0dB, the loudest possible tone, is assumed to be ~100dB SPL + for collection of these curves) */ + +static const float tonemasks[P_BANDS][6][EHMER_MAX]={ + /* 62.5 Hz */ + {{ -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -62, -62, -65, -73, + -69, -68, -68, -67, -70, -70, -72, -74, + -75, -79, -79, -80, -83, -88, -93, -100, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -53, -61, -66, + -66, -68, -67, -70, -76, -76, -72, -73, + -75, -76, -78, -79, -83, -88, -93, -100, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -37, -37, -37, -37, -37, -37, -37, -37, + -38, -40, -42, -46, -48, -53, -55, -62, + -65, -58, -56, -56, -61, -60, -65, -67, + -69, -71, -77, -77, -78, -80, -82, -84, + -88, -93, -98, -106, -112, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -25, -25, -25, -25, -25, -25, -25, -25, + -25, -26, -27, -29, -32, -38, -48, -52, + -52, -50, -48, -48, -51, -52, -54, -60, + -67, -67, -66, -68, -69, -73, -73, -76, + -80, -81, -81, -85, -85, -86, -88, -93, + -100, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -16, -16, -16, -16, -16, -16, -16, -16, + -17, -19, -20, -22, -26, -28, -31, -40, + -47, -39, -39, -40, -42, -43, -47, -51, + -57, -52, -55, -55, -60, -58, -62, -63, + -70, -67, -69, -72, -73, -77, -80, -82, + -83, -87, -90, -94, -98, -104, -115, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -8, -8, -8, -8, -8, -8, -8, -8, + -8, -8, -10, -11, -15, -19, -25, -30, + -34, -31, -30, -31, -29, -32, -35, -42, + -48, -42, -44, -46, -50, -50, -51, -52, + -59, -54, -55, -55, -58, -62, -63, -66, + -72, -73, -76, -75, -78, -80, -80, -81, + -84, -88, -90, -94, -98, -101, -106, -110}}, + /* 88Hz */ + {{ -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -67, -67, -67, + -76, -72, -71, -74, -76, -76, -75, -78, + -79, -79, -81, -83, -86, -89, -93, -97, + -100, -105, -110, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -47, -47, -47, -47, -47, -47, -47, -47, + -47, -47, -47, -48, -51, -55, -59, -66, + -66, -66, -67, -66, -68, -69, -70, -74, + -79, -77, -77, -78, -80, -81, -82, -84, + -86, -88, -91, -95, -100, -108, -116, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -36, -36, -36, -36, -36, -36, -36, -36, + -36, -37, -37, -41, -44, -48, -51, -58, + -62, -60, -57, -59, -59, -60, -63, -65, + -72, -71, -70, -72, -74, -77, -76, -78, + -81, -81, -80, -83, -86, -91, -96, -100, + -105, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -28, -28, -28, -28, -28, -28, -28, -28, + -28, -30, -32, -32, -33, -35, -41, -49, + -50, -49, -47, -48, -48, -52, -51, -57, + -65, -61, -59, -61, -64, -69, -70, -74, + -77, -77, -78, -81, -84, -85, -87, -90, + -92, -96, -100, -107, -112, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -19, -19, -19, -19, -19, -19, -19, -19, + -20, -21, -23, -27, -30, -35, -36, -41, + -46, -44, -42, -40, -41, -41, -43, -48, + -55, -53, -52, -53, -56, -59, -58, -60, + -67, -66, -69, -71, -72, -75, -79, -81, + -84, -87, -90, -93, -97, -101, -107, -114, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -9, -9, -9, -9, -9, -9, -9, -9, + -11, -12, -12, -15, -16, -20, -23, -30, + -37, -34, -33, -34, -31, -32, -32, -38, + -47, -44, -41, -40, -47, -49, -46, -46, + -58, -50, -50, -54, -58, -62, -64, -67, + -67, -70, -72, -76, -79, -83, -87, -91, + -96, -100, -104, -110, -999, -999, -999, -999}}, + /* 125 Hz */ + {{ -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -63, -64, -66, -67, -66, -68, + -75, -72, -76, -75, -76, -78, -79, -82, + -84, -85, -90, -94, -101, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -60, -60, -61, -63, -66, + -71, -68, -70, -70, -71, -72, -72, -75, + -81, -78, -79, -82, -83, -86, -90, -97, + -103, -113, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -53, -53, -53, -53, -53, -53, -53, -53, + -53, -54, -55, -57, -56, -57, -55, -61, + -65, -60, -60, -62, -63, -63, -66, -68, + -74, -73, -75, -75, -78, -80, -80, -82, + -85, -90, -96, -101, -108, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -47, -47, -47, -47, -48, -51, + -57, -51, -49, -50, -51, -53, -54, -59, + -66, -60, -62, -67, -67, -70, -72, -75, + -76, -78, -81, -85, -88, -94, -97, -104, + -112, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -36, -36, -36, -36, -36, -36, -36, -36, + -39, -41, -42, -42, -39, -38, -41, -43, + -52, -44, -40, -39, -37, -37, -40, -47, + -54, -50, -48, -50, -55, -61, -59, -62, + -66, -66, -66, -69, -69, -73, -74, -74, + -75, -77, -79, -82, -87, -91, -95, -100, + -108, -115, -999, -999, -999, -999, -999, -999}, + { -28, -26, -24, -22, -20, -20, -23, -29, + -30, -31, -28, -27, -28, -28, -28, -35, + -40, -33, -32, -29, -30, -30, -30, -37, + -45, -41, -37, -38, -45, -47, -47, -48, + -53, -49, -48, -50, -49, -49, -51, -52, + -58, -56, -57, -56, -60, -61, -62, -70, + -72, -74, -78, -83, -88, -93, -100, -106}}, + /* 177 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -110, -105, -100, -95, -91, -87, -83, + -80, -78, -76, -78, -78, -81, -83, -85, + -86, -85, -86, -87, -90, -97, -107, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -110, -105, -100, -95, -90, + -85, -81, -77, -73, -70, -67, -67, -68, + -75, -73, -70, -69, -70, -72, -75, -79, + -84, -83, -84, -86, -88, -89, -89, -93, + -98, -105, -112, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-105, -100, -95, -90, -85, -80, -76, -71, + -68, -68, -65, -63, -63, -62, -62, -64, + -65, -64, -61, -62, -63, -64, -66, -68, + -73, -73, -74, -75, -76, -81, -83, -85, + -88, -89, -92, -95, -100, -108, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -80, -75, -71, -68, -65, -63, -62, -61, + -61, -61, -61, -59, -56, -57, -53, -50, + -58, -52, -50, -50, -52, -53, -54, -58, + -67, -63, -67, -68, -72, -75, -78, -80, + -81, -81, -82, -85, -89, -90, -93, -97, + -101, -107, -114, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -65, -61, -59, -57, -56, -55, -55, -56, + -56, -57, -55, -53, -52, -47, -44, -44, + -50, -44, -41, -39, -39, -42, -40, -46, + -51, -49, -50, -53, -54, -63, -60, -61, + -62, -66, -66, -66, -70, -73, -74, -75, + -76, -75, -79, -85, -89, -91, -96, -102, + -110, -999, -999, -999, -999, -999, -999, -999}, + { -52, -50, -49, -49, -48, -48, -48, -49, + -50, -50, -49, -46, -43, -39, -35, -33, + -38, -36, -32, -29, -32, -32, -32, -35, + -44, -39, -38, -38, -46, -50, -45, -46, + -53, -50, -50, -50, -54, -54, -53, -53, + -56, -57, -59, -66, -70, -72, -74, -79, + -83, -85, -90, -97, -114, -999, -999, -999}}, + /* 250 Hz */ + {{-999, -999, -999, -999, -999, -999, -110, -105, + -100, -95, -90, -86, -80, -75, -75, -79, + -80, -79, -80, -81, -82, -88, -95, -103, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -108, -103, -98, -93, + -88, -83, -79, -78, -75, -71, -67, -68, + -73, -73, -72, -73, -75, -77, -80, -82, + -88, -93, -100, -107, -114, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -110, -105, -101, -96, -90, + -86, -81, -77, -73, -69, -66, -61, -62, + -66, -64, -62, -65, -66, -70, -72, -76, + -81, -80, -84, -90, -95, -102, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -107, -103, -97, -92, -88, + -83, -79, -74, -70, -66, -59, -53, -58, + -62, -55, -54, -54, -54, -58, -61, -62, + -72, -70, -72, -75, -78, -80, -81, -80, + -83, -83, -88, -93, -100, -107, -115, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -105, -100, -95, -90, -85, + -80, -75, -70, -66, -62, -56, -48, -44, + -48, -46, -46, -43, -46, -48, -48, -51, + -58, -58, -59, -60, -62, -62, -61, -61, + -65, -64, -65, -68, -70, -74, -75, -78, + -81, -86, -95, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -105, -100, -95, -90, -85, -80, + -75, -70, -65, -61, -55, -49, -39, -33, + -40, -35, -32, -38, -40, -33, -35, -37, + -46, -41, -45, -44, -46, -42, -45, -46, + -52, -50, -50, -50, -54, -54, -55, -57, + -62, -64, -66, -68, -70, -76, -81, -90, + -100, -110, -999, -999, -999, -999, -999, -999}}, + /* 354 hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -85, -82, -83, -80, -78, + -84, -79, -80, -83, -87, -89, -91, -93, + -99, -106, -117, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -85, -80, -75, -70, -68, + -74, -72, -74, -77, -80, -82, -85, -87, + -92, -89, -91, -95, -100, -106, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -83, -75, -71, -63, -64, + -67, -62, -64, -67, -70, -73, -77, -81, + -84, -83, -85, -89, -90, -93, -98, -104, + -109, -114, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -103, -96, -88, -81, -75, -68, -58, -54, + -56, -54, -56, -56, -58, -60, -63, -66, + -74, -69, -72, -72, -75, -74, -77, -81, + -81, -82, -84, -87, -93, -96, -99, -104, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -108, -102, -96, + -91, -85, -80, -74, -68, -60, -51, -46, + -48, -46, -43, -45, -47, -47, -49, -48, + -56, -53, -55, -58, -57, -63, -58, -60, + -66, -64, -67, -70, -70, -74, -77, -84, + -86, -89, -91, -93, -94, -101, -109, -118, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -108, -103, -98, -93, -88, + -83, -78, -73, -68, -60, -53, -44, -35, + -38, -38, -34, -34, -36, -40, -41, -44, + -51, -45, -46, -47, -46, -54, -50, -49, + -50, -50, -50, -51, -54, -57, -58, -60, + -66, -66, -66, -64, -65, -68, -77, -82, + -87, -95, -110, -999, -999, -999, -999, -999}}, + /* 500 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -107, -102, -97, -92, -87, -83, -78, -75, + -82, -79, -83, -85, -89, -92, -95, -98, + -101, -105, -109, -113, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -106, + -100, -95, -90, -86, -81, -78, -74, -69, + -74, -74, -76, -79, -83, -84, -86, -89, + -92, -97, -93, -100, -103, -107, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -100, + -95, -90, -87, -83, -80, -75, -69, -60, + -66, -66, -68, -70, -74, -78, -79, -81, + -81, -83, -84, -87, -93, -96, -99, -103, + -107, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -108, -103, -98, + -93, -89, -85, -82, -78, -71, -62, -55, + -58, -58, -54, -54, -55, -59, -61, -62, + -70, -66, -66, -67, -70, -72, -75, -78, + -84, -84, -84, -88, -91, -90, -95, -98, + -102, -103, -106, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -108, -103, -98, -94, + -90, -87, -82, -79, -73, -67, -58, -47, + -50, -45, -41, -45, -48, -44, -44, -49, + -54, -51, -48, -47, -49, -50, -51, -57, + -58, -60, -63, -69, -70, -69, -71, -74, + -78, -82, -90, -95, -101, -105, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -105, -101, -97, -93, -90, + -85, -80, -77, -72, -65, -56, -48, -37, + -40, -36, -34, -40, -50, -47, -38, -41, + -47, -38, -35, -39, -38, -43, -40, -45, + -50, -45, -44, -47, -50, -55, -48, -48, + -52, -66, -70, -76, -82, -90, -97, -105, + -110, -999, -999, -999, -999, -999, -999, -999}}, + /* 707 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -108, -103, -98, -93, -86, -79, -76, + -83, -81, -85, -87, -89, -93, -98, -102, + -107, -112, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -108, -103, -98, -93, -86, -79, -71, + -77, -74, -77, -79, -81, -84, -85, -90, + -92, -93, -92, -98, -101, -108, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -108, -103, -98, -93, -87, -78, -68, -65, + -66, -62, -65, -67, -70, -73, -75, -78, + -82, -82, -83, -84, -91, -93, -98, -102, + -106, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -100, -95, -90, -82, -74, -62, -57, + -58, -56, -51, -52, -52, -54, -54, -58, + -66, -59, -60, -63, -66, -69, -73, -79, + -83, -84, -80, -81, -81, -82, -88, -92, + -98, -105, -113, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -102, -97, -92, -84, -79, -69, -57, -47, + -52, -47, -44, -45, -50, -52, -42, -42, + -53, -43, -43, -48, -51, -56, -55, -52, + -57, -59, -61, -62, -67, -71, -78, -83, + -86, -94, -98, -103, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -105, -100, + -95, -90, -84, -78, -70, -61, -51, -41, + -40, -38, -40, -46, -52, -51, -41, -40, + -46, -40, -38, -38, -41, -46, -41, -46, + -47, -43, -43, -45, -41, -45, -56, -67, + -68, -83, -87, -90, -95, -102, -107, -113, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 1000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -109, -105, -101, -96, -91, -84, -77, + -82, -82, -85, -89, -94, -100, -106, -110, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -106, -103, -98, -92, -85, -80, -71, + -75, -72, -76, -80, -84, -86, -89, -93, + -100, -107, -113, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -104, -101, -97, -92, -88, -84, -80, -64, + -66, -63, -64, -66, -69, -73, -77, -83, + -83, -86, -91, -98, -104, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -104, -101, -97, -92, -90, -84, -74, -57, + -58, -52, -55, -54, -50, -52, -50, -52, + -63, -62, -69, -76, -77, -78, -78, -79, + -82, -88, -94, -100, -106, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -102, + -98, -95, -90, -85, -83, -78, -70, -50, + -50, -41, -44, -49, -47, -50, -50, -44, + -55, -46, -47, -48, -48, -54, -49, -49, + -58, -62, -71, -81, -87, -92, -97, -102, + -108, -114, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -102, + -98, -95, -90, -85, -83, -78, -70, -45, + -43, -41, -47, -50, -51, -50, -49, -45, + -47, -41, -44, -41, -39, -43, -38, -37, + -40, -41, -44, -50, -58, -65, -73, -79, + -85, -92, -97, -101, -105, -109, -113, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 1414 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -107, -100, -95, -87, -81, + -85, -83, -88, -93, -100, -107, -114, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -107, -101, -95, -88, -83, -76, + -73, -72, -79, -84, -90, -95, -100, -105, + -110, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -104, -98, -92, -87, -81, -70, + -65, -62, -67, -71, -74, -80, -85, -91, + -95, -99, -103, -108, -111, -114, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -103, -97, -90, -85, -76, -60, + -56, -54, -60, -62, -61, -56, -63, -65, + -73, -74, -77, -75, -78, -81, -86, -87, + -88, -91, -94, -98, -103, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -105, + -100, -97, -92, -86, -81, -79, -70, -57, + -51, -47, -51, -58, -60, -56, -53, -50, + -58, -52, -50, -50, -53, -55, -64, -69, + -71, -85, -82, -78, -81, -85, -95, -102, + -112, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -105, + -100, -97, -92, -85, -83, -79, -72, -49, + -40, -43, -43, -54, -56, -51, -50, -40, + -43, -38, -36, -35, -37, -38, -37, -44, + -54, -60, -57, -60, -70, -75, -84, -92, + -103, -112, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 2000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -102, -95, -89, -82, + -83, -84, -90, -92, -99, -107, -113, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -107, -101, -95, -89, -83, -72, + -74, -78, -85, -88, -88, -90, -92, -98, + -105, -111, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -109, -103, -97, -93, -87, -81, -70, + -70, -67, -75, -73, -76, -79, -81, -83, + -88, -89, -97, -103, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -107, -100, -94, -88, -83, -75, -63, + -59, -59, -63, -66, -60, -62, -67, -67, + -77, -76, -81, -88, -86, -92, -96, -102, + -109, -116, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -105, -98, -92, -86, -81, -73, -56, + -52, -47, -55, -60, -58, -52, -51, -45, + -49, -50, -53, -54, -61, -71, -70, -69, + -78, -79, -87, -90, -96, -104, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -103, -96, -90, -86, -78, -70, -51, + -42, -47, -48, -55, -54, -54, -53, -42, + -35, -28, -33, -38, -37, -44, -47, -49, + -54, -63, -68, -78, -82, -89, -94, -99, + -104, -109, -114, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 2828 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -100, -90, -79, + -85, -81, -82, -82, -89, -94, -99, -103, + -109, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -105, -97, -85, -72, + -74, -70, -70, -70, -76, -85, -91, -93, + -97, -103, -109, -115, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -112, -93, -81, -68, + -62, -60, -60, -57, -63, -70, -77, -82, + -90, -93, -98, -104, -109, -113, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -113, -100, -93, -84, -63, + -58, -48, -53, -54, -52, -52, -57, -64, + -66, -76, -83, -81, -85, -85, -90, -95, + -98, -101, -103, -106, -108, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -105, -95, -86, -74, -53, + -50, -38, -43, -49, -43, -42, -39, -39, + -46, -52, -57, -56, -72, -69, -74, -81, + -87, -92, -94, -97, -99, -102, -105, -108, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -108, -99, -90, -76, -66, -45, + -43, -41, -44, -47, -43, -47, -40, -30, + -31, -31, -39, -33, -40, -41, -43, -53, + -59, -70, -73, -77, -79, -82, -84, -87, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 4000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -91, -76, + -75, -85, -93, -98, -104, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -91, -70, + -70, -75, -86, -89, -94, -98, -101, -106, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -60, + -65, -64, -74, -83, -88, -91, -95, -99, + -103, -107, -110, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -58, + -55, -49, -66, -68, -71, -78, -78, -80, + -88, -85, -89, -97, -100, -105, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -53, + -52, -41, -59, -59, -49, -58, -56, -63, + -86, -79, -90, -93, -98, -103, -107, -112, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -97, -91, -73, -45, + -40, -33, -53, -61, -49, -54, -50, -50, + -60, -52, -67, -74, -81, -92, -96, -100, + -105, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 5657 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -113, -106, -99, -92, -77, + -80, -88, -97, -106, -115, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -116, -109, -102, -95, -89, -74, + -72, -88, -87, -95, -102, -109, -116, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -116, -109, -102, -95, -89, -75, + -66, -74, -77, -78, -86, -87, -90, -96, + -105, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -115, -108, -101, -94, -88, -66, + -56, -61, -70, -65, -78, -72, -83, -84, + -93, -98, -105, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -110, -105, -95, -89, -82, -57, + -52, -52, -59, -56, -59, -58, -69, -67, + -88, -82, -82, -89, -94, -100, -108, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -110, -101, -96, -90, -83, -77, -54, + -43, -38, -50, -48, -52, -48, -42, -42, + -51, -52, -53, -59, -65, -71, -78, -85, + -95, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 8000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -120, -105, -86, -68, + -78, -79, -90, -100, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -120, -105, -86, -66, + -73, -77, -88, -96, -105, -115, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -120, -105, -92, -80, -61, + -64, -68, -80, -87, -92, -100, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -120, -104, -91, -79, -52, + -60, -54, -64, -69, -77, -80, -82, -84, + -85, -87, -88, -90, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -118, -100, -87, -77, -49, + -50, -44, -58, -61, -61, -67, -65, -62, + -62, -62, -65, -68, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -115, -98, -84, -62, -49, + -44, -38, -46, -49, -49, -46, -39, -37, + -39, -40, -42, -43, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 11314 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -74, + -77, -82, -82, -85, -90, -94, -99, -104, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -66, + -70, -81, -80, -81, -84, -88, -91, -93, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -61, + -63, -70, -71, -74, -77, -80, -83, -85, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -86, -62, + -63, -62, -62, -58, -52, -50, -50, -52, + -54, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -118, -108, -84, -53, + -50, -50, -50, -55, -47, -45, -40, -40, + -40, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -118, -100, -73, -43, + -37, -42, -43, -53, -38, -37, -35, -35, + -38, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 16000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -91, -84, -74, + -80, -80, -80, -80, -80, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -91, -84, -74, + -68, -68, -68, -68, -68, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -86, -78, -70, + -60, -45, -30, -21, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -87, -78, -67, + -48, -38, -29, -21, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -86, -69, -56, + -45, -35, -33, -29, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -83, -71, -48, + -27, -38, -37, -34, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}} +}; + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/mdct.c b/lib/ogg_vorbis/vorbis/lib/mdct.c new file mode 100755 index 0000000..21e7beb --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/mdct.c @@ -0,0 +1,570 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: normalized modified discrete cosine transform + power of two length transform only [64 <= n ] + last mod: $Id: mdct.c 16227 2009-07-08 06:58:46Z xiphmont $ + + Original algorithm adapted long ago from _The use of multirate filter + banks for coding of high quality digital audio_, by T. Sporer, + K. Brandenburg and B. Edler, collection of the European Signal + Processing Conference (EUSIPCO), Amsterdam, June 1992, Vol.1, pp + 211-214 + + The below code implements an algorithm that no longer looks much like + that presented in the paper, but the basic structure remains if you + dig deep enough to see it. + + This module DOES NOT INCLUDE code to generate/apply the window + function. Everybody has their own weird favorite including me... I + happen to like the properties of y=sin(.5PI*sin^2(x)), but others may + vehemently disagree. + + ********************************************************************/ + +/* this can also be run as an integer transform by uncommenting a + define in mdct.h; the integerization is a first pass and although + it's likely stable for Vorbis, the dynamic range is constrained and + roundoff isn't done (so it's noisy). Consider it functional, but + only a starting point. There's no point on a machine with an FPU */ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "mdct.h" +#include "os.h" +#include "misc.h" + +/* build lookups for trig functions; also pre-figure scaling and + some window function algebra. */ + +int FMOD_mdct_init(void *context, mdct_lookup *lookup,int n){ + int *bitrev=_ogg_malloc(sizeof(*bitrev)*(n/4)); + DATA_TYPE *T=_ogg_malloc(sizeof(*T)*(n+n/4)); + int i; + int n2=n>>1; + int log2n=lookup->log2n=(int)(FMOD_ogg_rint(FMOD_ogg_log((float)n)/FMOD_ogg_log(2.f))); + + if (!bitrev || !T) + { + return(OV_EMEMORY); + } + + lookup->n=n; + lookup->trig=T; + lookup->bitrev=bitrev; + +/* trig lookups... */ + + for(i=0;i>j;j++) + if((msb>>j)&i)acc|=1<scale=FLOAT_CONV(4.f/n); + + return 0; +} + +/* 8 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_8(DATA_TYPE *x){ + REG_TYPE r0 = x[6] + x[2]; + REG_TYPE r1 = x[6] - x[2]; + REG_TYPE r2 = x[4] + x[0]; + REG_TYPE r3 = x[4] - x[0]; + + x[6] = r0 + r2; + x[4] = r0 - r2; + + r0 = x[5] - x[1]; + r2 = x[7] - x[3]; + x[0] = r1 + r0; + x[2] = r1 - r0; + + r0 = x[5] + x[1]; + r1 = x[7] + x[3]; + x[3] = r2 + r3; + x[1] = r2 - r3; + x[7] = r1 + r0; + x[5] = r1 - r0; + +} + +/* 16 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_16(DATA_TYPE *x){ + REG_TYPE r0 = x[1] - x[9]; + REG_TYPE r1 = x[0] - x[8]; + + x[8] += x[0]; + x[9] += x[1]; + x[0] = MULT_NORM((r0 + r1) * cPI2_8); + x[1] = MULT_NORM((r0 - r1) * cPI2_8); + + r0 = x[3] - x[11]; + r1 = x[10] - x[2]; + x[10] += x[2]; + x[11] += x[3]; + x[2] = r0; + x[3] = r1; + + r0 = x[12] - x[4]; + r1 = x[13] - x[5]; + x[12] += x[4]; + x[13] += x[5]; + x[4] = MULT_NORM((r0 - r1) * cPI2_8); + x[5] = MULT_NORM((r0 + r1) * cPI2_8); + + r0 = x[14] - x[6]; + r1 = x[15] - x[7]; + x[14] += x[6]; + x[15] += x[7]; + x[6] = r0; + x[7] = r1; + + mdct_butterfly_8(x); + mdct_butterfly_8(x+8); +} + +/* 32 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_32(DATA_TYPE *x){ + REG_TYPE r0 = x[30] - x[14]; + REG_TYPE r1 = x[31] - x[15]; + + x[30] += x[14]; + x[31] += x[15]; + x[14] = r0; + x[15] = r1; + + r0 = x[28] - x[12]; + r1 = x[29] - x[13]; + x[28] += x[12]; + x[29] += x[13]; + x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 ); + x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 ); + + r0 = x[26] - x[10]; + r1 = x[27] - x[11]; + x[26] += x[10]; + x[27] += x[11]; + x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8); + x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8); + + r0 = x[24] - x[8]; + r1 = x[25] - x[9]; + x[24] += x[8]; + x[25] += x[9]; + x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 ); + x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + + r0 = x[22] - x[6]; + r1 = x[7] - x[23]; + x[22] += x[6]; + x[23] += x[7]; + x[6] = r1; + x[7] = r0; + + r0 = x[4] - x[20]; + r1 = x[5] - x[21]; + x[20] += x[4]; + x[21] += x[5]; + x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 ); + x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 ); + + r0 = x[2] - x[18]; + r1 = x[3] - x[19]; + x[18] += x[2]; + x[19] += x[3]; + x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8); + x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8); + + r0 = x[0] - x[16]; + r1 = x[1] - x[17]; + x[16] += x[0]; + x[17] += x[1]; + x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 ); + + mdct_butterfly_16(x); + mdct_butterfly_16(x+16); + +} + +/* N point first stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_first(DATA_TYPE *T, + DATA_TYPE *x, + int points){ + + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]); + x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]); + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]); + x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]); + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]); + x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]); + + x1-=8; + x2-=8; + T+=16; + + }while(x2>=x); +} + +/* N/stage point generic N stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_generic(DATA_TYPE *T, + DATA_TYPE *x, + int points, + int trigint){ + + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + x1-=8; + x2-=8; + + }while(x2>=x); +} + +STIN void mdct_butterflies(mdct_lookup *init, + DATA_TYPE *x, + int points){ + + DATA_TYPE *T=init->trig; + int stages=init->log2n-5; + int i,j; + + if(--stages>0){ + mdct_butterfly_first(T,x,points); + } + + for(i=1;--stages>0;i++){ + for(j=0;j<(1<>i)*j,points>>i,4<trig)_ogg_free(l->trig); + if(l->bitrev)_ogg_free(l->bitrev); + FMOD_memset(l,0,sizeof(*l)); + } +} + +STIN void mdct_bitreverse(mdct_lookup *init, + DATA_TYPE *x){ + int n = init->n; + int *bit = init->bitrev; + DATA_TYPE *w0 = x; + DATA_TYPE *w1 = x = w0+(n>>1); + DATA_TYPE *T = init->trig+n; + + do{ + DATA_TYPE *x0 = x+bit[0]; + DATA_TYPE *x1 = x+bit[1]; + + REG_TYPE r0 = x0[1] - x1[1]; + REG_TYPE r1 = x0[0] + x1[0]; + REG_TYPE r2 = MULT_NORM(r1 * T[0] + r0 * T[1]); + REG_TYPE r3 = MULT_NORM(r1 * T[1] - r0 * T[0]); + + w1 -= 4; + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[0] = r0 + r2; + w1[2] = r0 - r2; + w0[1] = r1 + r3; + w1[3] = r3 - r1; + + x0 = x+bit[2]; + x1 = x+bit[3]; + + r0 = x0[1] - x1[1]; + r1 = x0[0] + x1[0]; + r2 = MULT_NORM(r1 * T[2] + r0 * T[3]); + r3 = MULT_NORM(r1 * T[3] - r0 * T[2]); + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[2] = r0 + r2; + w1[0] = r0 - r2; + w0[3] = r1 + r3; + w1[1] = r3 - r1; + + T += 4; + bit += 4; + w0 += 4; + + }while(w0n; + int n2=n>>1; + int n4=n>>2; + + /* rotate */ + + DATA_TYPE *iX = in+n2-7; + DATA_TYPE *oX = out+n2+n4; + DATA_TYPE *T = init->trig+n4; + + do{ + oX -= 4; + oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]); + oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]); + oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]); + oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]); + iX -= 8; + T += 4; + }while(iX>=in); + + iX = in+n2-8; + oX = out+n2+n4; + T = init->trig+n4; + + do{ + T -= 4; + oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]); + oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]); + oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]); + oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]); + iX -= 8; + oX += 4; + }while(iX>=in); + + mdct_butterflies(init,out+n2,n2); + mdct_bitreverse(init,out); + + /* roatate + window */ + + { + DATA_TYPE *oX1=out+n2+n4; + DATA_TYPE *oX2=out+n2+n4; + DATA_TYPE *iX =out; + T =init->trig+n2; + + do{ + oX1-=4; + + oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]); + oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]); + + oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]); + oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]); + + oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]); + oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]); + + oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]); + oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]); + + oX2+=4; + iX += 8; + T += 8; + }while(iXoX2); + } +} + +void FMOD_mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ + int n=init->n; + int n2=n>>1; + int n4=n>>2; + int n8=n>>3; + DATA_TYPE *w=alloca(n*sizeof(*w)); /* forward needs working space */ + DATA_TYPE *w2=w+n2; + + /* rotate */ + + /* window + rotate + step 1 */ + + REG_TYPE r0; + REG_TYPE r1; + DATA_TYPE *x0=in+n2+n4; + DATA_TYPE *x1=x0+1; + DATA_TYPE *T=init->trig+n2; + + int i=0; + + for(i=0;itrig+n2; + x0=out+n2; + + for(i=0;iscale); + x0[0] =MULT_NORM((w[0]*T[1]-w[1]*T[0])*init->scale); + w+=2; + T+=2; + } +} diff --git a/lib/ogg_vorbis/vorbis/lib/mdct.h b/lib/ogg_vorbis/vorbis/lib/mdct.h new file mode 100755 index 0000000..8198458 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/mdct.h @@ -0,0 +1,71 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: modified discrete cosine transform prototypes + last mod: $Id: mdct.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _OGG_mdct_H_ +#define _OGG_mdct_H_ + +#include "vorbis/codec.h" + + + + + +/*#define MDCT_INTEGERIZED <- be warned there could be some hurt left here*/ +#ifdef MDCT_INTEGERIZED + +#define DATA_TYPE int +#define REG_TYPE register int +#define TRIGBITS 14 +#define cPI3_8 6270 +#define cPI2_8 11585 +#define cPI1_8 15137 + +#define FLOAT_CONV(x) ((int)((x)*(1<>TRIGBITS) +#define HALVE(x) ((x)>>1) + +#else + +#define DATA_TYPE float +#define REG_TYPE float +#define cPI3_8 .38268343236508977175F +#define cPI2_8 .70710678118654752441F +#define cPI1_8 .92387953251128675613F + +#define FLOAT_CONV(x) (x) +#define MULT_NORM(x) (x) +#define HALVE(x) ((x)*.5f) + +#endif + + +typedef struct { + int n; + int log2n; + + DATA_TYPE *trig; + int *bitrev; + + DATA_TYPE scale; +} mdct_lookup; + +extern int FMOD_mdct_init(void *context, mdct_lookup *lookup,int n); +extern void FMOD_mdct_clear(void *context, mdct_lookup *l); +extern void FMOD_mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out); +extern void FMOD_mdct_backward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out); + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/misc.h b/lib/ogg_vorbis/vorbis/lib/misc.h new file mode 100755 index 0000000..d257f80 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/misc.h @@ -0,0 +1,57 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: miscellaneous prototypes + last mod: $Id: misc.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_RANDOM_H_ +#define _V_RANDOM_H_ +#include "vorbis/codec.h" + +extern void *_FMOD_vorbis_block_alloc(void *context, vorbis_block *vb,ogg_int32_t bytes); +extern int _FMOD_vorbis_block_ripcord(void *context, vorbis_block *vb); + +#ifdef ANALYSIS +extern int analysis_noisy; +extern void _analysis_output(char *base,int i,float *v,int n,int bark,int dB, + ogg_int64_t off); +extern void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB, + ogg_int64_t off); +#endif + +#ifdef DEBUG_MALLOC + +#define _VDBG_GRAPHFILE "malloc.m" +#undef _VDBG_GRAPHFILE +extern void *_VDBG_malloc(void *ptr,ogg_int32_t bytes,char *file,ogg_int32_t line); +extern void _VDBG_free(void *ptr,char *file,ogg_int32_t line); + +#ifndef MISC_C +#undef _ogg_malloc +#undef _ogg_calloc +#undef _ogg_realloc +#undef _ogg_free + +#define _ogg_malloc(x) _VDBG_malloc(NULL,(x),__FILE__,__LINE__) +#define _ogg_calloc(x,y) _VDBG_malloc(NULL,(x)*(y),__FILE__,__LINE__) +#define _ogg_realloc(x,y) _VDBG_malloc((x),(y),__FILE__,__LINE__) +#define _ogg_free(x) _VDBG_free((x),__FILE__,__LINE__) +#endif +#endif + +#endif + + + + diff --git a/lib/ogg_vorbis/vorbis/lib/os.h b/lib/ogg_vorbis/vorbis/lib/os.h new file mode 100755 index 0000000..5256d6c --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/os.h @@ -0,0 +1,306 @@ +#ifndef _OS_H +#define _OS_H +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "misc.h" + +#ifndef _V_IFDEFJAIL_H_ +# define _V_IFDEFJAIL_H_ + +# ifdef __GNUC__ +# define STIN static __inline__ +# elif _WIN32 +# define STIN static __inline +# else +# define STIN static +# endif + +#ifdef DJGPP +# define rint(x) (FMOD_ogg_floor((x)+0.5f)) +#endif + +#ifndef M_PI +# define M_PI (3.1415926536f) +#endif + +#if (defined(_WIN32) && !defined(__SYMBIAN32__)) || defined(_XENON) +# include +# define rint(x) (FMOD_ogg_floor((x)+0.5f)) +# define NO_FLOAT_MATH_LIB +# define FAST_HYPOT(a, b) FMOD_ogg_sqrt((a)*(a) + (b)*(b)) +#endif + +#if defined(__SYMBIAN32__) && defined(__WINS__) +void *_alloca(size_t size); +# define alloca _alloca +#endif + +#ifndef FAST_HYPOT +# define FAST_HYPOT hypot +#endif + +#endif + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#ifdef USE_MEMORY_H +# include +#endif + +#ifndef min +# define min(x,y) ((x)>(y)?(y):(x)) +#endif + +#ifndef max +# define max(x,y) ((x)<(y)?(y):(x)) +#endif + + +/* Special i386 GCC implementation */ +#if defined(__i386__) && defined(__GNUC__) && !defined(__BEOS__) +# define VORBIS_FPU_CONTROL +/* both GCC and MSVC are kinda stupid about rounding/casting to int. + Because of encapsulation constraints (GCC can't see inside the asm + block and so we end up doing stupid things like a store/load that + is collectively a noop), we do it this way */ + +/* we must set up the fpu before this works!! */ + +typedef ogg_int16_t vorbis_fpu_control; + +static inline void FMOD_vorbis_fpu_setround(vorbis_fpu_control *fpu){ + ogg_int16_t ret; + ogg_int16_t temp; + __asm__ __volatile__("fnstcw %0\n\t" + "movw %0,%%dx\n\t" + "andw $62463,%%dx\n\t" + "movw %%dx,%1\n\t" + "fldcw %1\n\t":"=m"(ret):"m"(temp): "dx"); + *fpu=ret; +} + +static inline void FMOD_vorbis_fpu_restore(vorbis_fpu_control fpu){ + __asm__ __volatile__("fldcw %0":: "m"(fpu)); +} + +/* assumes the FPU is in round mode! */ +static inline int FMOD_vorbis_ftoi(double f){ /* yes, double! Otherwise, + we get extra fst/fld to + truncate precision */ + int i; + __asm__("fistl %0": "=m"(i) : "t"(f)); + return(i); +} + + #ifdef __MACH__ + #define FMOD_ogg_ldexp (float)ldexp + #else + #define FMOD_ogg_ldexp ldexpf + #endif + + #define FMOD_ogg_cos cosf + #define FMOD_ogg_acos acosf + #define FMOD_ogg_sin sinf + #define FMOD_ogg_sqrt sqrtf + #define FMOD_ogg_fabs fabsf + #define FMOD_ogg_ceil ceilf + #define FMOD_ogg_floor floorf + #define FMOD_ogg_exp expf + #define FMOD_ogg_log logf + #define FMOD_ogg_frexp frexpf + #define FMOD_ogg_atan atanf + #define FMOD_ogg_pow powf + #define FMOD_ogg_rint(_x) (FMOD_ogg_floor((_x)+0.5f)) /* rintf doesnt work. causes horrible corruption. bug in compiler i think */ +#endif /* Special i386 GCC implementation */ + + +#if defined(R5900) + # define VORBIS_FPU_CONTROL + + typedef int vorbis_fpu_control; + + static __inline int FMOD_vorbis_ftoi(float val) + { + register int result; + register float tmp = 0; + __asm__ volatile ( + "cvt.w.s %2,%1\n" + "mfc1 %0,%2\n" + : "=r" (result) + : "f" (val),"f" (tmp) + ); + return result; + } + static __inline int FMOD_vorbis_ftoipcm(float val) + { + register int result; + register float tmp = 0; + __asm__ volatile ( + "addi $4, $0, -32768\n" + "addi $5, $0, 32767\n" + "cvt.w.s %2,%1\n" + "mfc1 %0,%2\n" + "pmaxw %0, %0, $4\n" + "pminw %0, %0, $5\n" + : "=r" (result) + : "f" (val),"f" (tmp) + : "$4", "$5" + ); + return result; + } + # define FMOD_vorbis_fpu_setround(vorbis_fpu_control) {} + # define FMOD_vorbis_fpu_restore(vorbis_fpu_control) {} + + #define FMOD_ogg_cos cosf + #define FMOD_ogg_acos acosf + #define FMOD_ogg_sin sinf + #define FMOD_ogg_sqrt sqrtf + #define FMOD_ogg_fabs fabsf + #define FMOD_ogg_ceil ceilf + #define FMOD_ogg_floor floorf + #define FMOD_ogg_exp expf + #define FMOD_ogg_log logf + #define FMOD_ogg_ldexp ldexpf + #define FMOD_ogg_frexp frexpf + #define FMOD_ogg_atan atanf + #define FMOD_ogg_pow powf + #define FMOD_ogg_rint(_x) (FMOD_ogg_floor((_x)+0.5f)) /* rintf doesnt work. causes horrible corruption. bug in compiler i think */ +#endif + +/* MSVC inline assembly. 32 bit only; inline ASM isn't implemented in the + * 64 bit compiler */ +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_WIN32_WCE) && !defined(_XENON) +# define VORBIS_FPU_CONTROL + +typedef ogg_int16_t vorbis_fpu_control; + +static __inline int FMOD_vorbis_ftoi(double f){ + int i; + __asm{ + fld f + fistp i + } + return i; +} + +static __inline void FMOD_vorbis_fpu_setround(vorbis_fpu_control *fpu){ +} + +static __inline void FMOD_vorbis_fpu_restore(vorbis_fpu_control fpu){ +} + #define FMOD_ogg_cos (float)cos + #define FMOD_ogg_acos (float)acos + #define FMOD_ogg_sin (float)sin + #define FMOD_ogg_sqrt (float)sqrt + #define FMOD_ogg_fabs (float)fabs + #define FMOD_ogg_ceil (float)ceil + #define FMOD_ogg_floor (float)floor + #define FMOD_ogg_exp (float)exp + #define FMOD_ogg_log (float)log + #define FMOD_ogg_ldexp (float)ldexp + #define FMOD_ogg_frexp (float)frexp + #define FMOD_ogg_atan (float)atan + #define FMOD_ogg_pow (float)pow + #define FMOD_ogg_rint (float)rint +#endif /* Special MSVC 32 bit implementation */ + + +/* Optimized code path for x86_64 builds. Uses SSE2 intrinsics. This can be + done safely because all x86_64 CPUs supports SSE2. */ +#if (defined(_MSC_VER) && defined(_WIN64)) || (defined(__GNUC__) && defined (__x86_64__)) +# define VORBIS_FPU_CONTROL + +typedef ogg_int16_t vorbis_fpu_control; + +#include +static __inline int FMOD_vorbis_ftoi(double f){ + return _mm_cvtsd_si32(_mm_load_sd(&f)); +} + +static __inline void FMOD_vorbis_fpu_setround(vorbis_fpu_control *fpu){ +} + +static __inline void FMOD_vorbis_fpu_restore(vorbis_fpu_control fpu){ +} + #define FMOD_ogg_cos (float)cos + #define FMOD_ogg_acos (float)acos + #define FMOD_ogg_sin (float)sin + #define FMOD_ogg_sqrt (float)sqrt + #define FMOD_ogg_fabs (float)fabs + #define FMOD_ogg_ceil (float)ceil + #define FMOD_ogg_floor (float)floor + #define FMOD_ogg_exp (float)exp + #define FMOD_ogg_log (float)log + #define FMOD_ogg_ldexp (float)ldexp + #define FMOD_ogg_frexp (float)frexp + #define FMOD_ogg_atan (float)atan + #define FMOD_ogg_pow (float)pow + #define FMOD_ogg_rint (float)rint + +#endif /* Special MSVC x64 implementation */ + + +/* If no special implementation was found for the current compiler / platform, + use the default implementation here: */ +#ifndef VORBIS_FPU_CONTROL + +typedef int vorbis_fpu_control; + + #define FMOD_ogg_cos (float)cos + #define FMOD_ogg_acos (float)acos + #define FMOD_ogg_sin (float)sin + #define FMOD_ogg_sqrt (float)sqrt + #define FMOD_ogg_fabs (float)fabs + #define FMOD_ogg_ceil (float)ceil + #define FMOD_ogg_floor (float)floor + #define FMOD_ogg_exp (float)exp + #define FMOD_ogg_log (float)log + #define FMOD_ogg_ldexp (float)ldexp + #define FMOD_ogg_frexp (float)frexp + #define FMOD_ogg_atan (float)atan + #define FMOD_ogg_pow (float)pow + #define FMOD_ogg_rint (float)rint + +static int FMOD_vorbis_ftoi(double f){ + /* Note: MSVC and GCC (at least on some systems) round towards zero, thus, + the FMOD_ogg_floor() call is required to ensure correct roudning of + negative numbers */ + return (int)FMOD_ogg_floor(f+.5f); +} + +/* We don't have special code for this compiler/arch, so do it the slow way */ +# define FMOD_vorbis_fpu_setround(vorbis_fpu_control) {} +# define FMOD_vorbis_fpu_restore(vorbis_fpu_control) {} + +#endif /* default implementation */ + +#include +#define ogg_strlen strlen +#define ogg_strcpy strcpy +#define ogg_strcat strcat + +#endif /* _OS_H */ diff --git a/lib/ogg_vorbis/vorbis/lib/psy.h b/lib/ogg_vorbis/vorbis/lib/psy.h new file mode 100755 index 0000000..2a82b52 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/psy.h @@ -0,0 +1,185 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: random psychoacoustics (not including preecho) + last mod: $Id: psy.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_PSY_H_ +#define _V_PSY_H_ +#include "smallft.h" + +#include "backends.h" +//#include "envelope.h" + +#ifndef EHMER_MAX +#define EHMER_MAX 56 +#endif + +/* psychoacoustic setup ********************************************/ +#define P_BANDS 17 /* 62Hz to 16kHz */ +#define P_LEVELS 8 /* 30dB to 100dB */ +#define P_LEVEL_0 30. /* 30 dB */ +#define P_NOISECURVES 3 + +//#define NOISE_COMPAND_LEVELS 40 +//typedef struct vorbis_info_psy{ +// int blockflag; +// +// float ath_adjatt; +// float ath_maxatt; +// +// float tone_masteratt[P_NOISECURVES]; +// float tone_centerboost; +// float tone_decay; +// float tone_abs_limit; +// float toneatt[P_BANDS]; +// +// int noisemaskp; +// float noisemaxsupp; +// float noisewindowlo; +// float noisewindowhi; +// int noisewindowlomin; +// int noisewindowhimin; +// int noisewindowfixed; +// float noiseoff[P_NOISECURVES][P_BANDS]; +// float noisecompand[NOISE_COMPAND_LEVELS]; +// +// float max_curve_dB; +// +// int normal_channel_p; +// int normal_point_p; +// int normal_start; +// int normal_partition; +// double normal_thresh; +//} vorbis_info_psy; +// +//typedef struct{ +// int eighth_octave_lines; +// +// /* for block long/short tuning; encode only */ +// float preecho_thresh[VE_BANDS]; +// float postecho_thresh[VE_BANDS]; +// float stretch_penalty; +// float preecho_minenergy; +// +// float ampmax_att_per_sec; +// +// /* channel coupling config */ +// int coupling_pkHz[PACKETBLOBS]; +// int coupling_pointlimit[2][PACKETBLOBS]; +// int coupling_prepointamp[PACKETBLOBS]; +// int coupling_postpointamp[PACKETBLOBS]; +// int sliding_lowpass[2][PACKETBLOBS]; +// +//} vorbis_info_psy_global; +// +//typedef struct { +// float ampmax; +// int channels; +// +// vorbis_info_psy_global *gi; +// int coupling_pointlimit[2][P_NOISECURVES]; +//} vorbis_look_psy_global; +// +// +//typedef struct { +// int n; +// struct vorbis_info_psy *vi; +// +// float ***tonecurves; +// float **noiseoffset; +// +// float *ath; +// long *octave; /* in n.ocshift format */ +// long *bark; +// +// long firstoc; +// long shiftoc; +// int eighth_octave_lines; /* power of two, please */ +// int total_octave_lines; +// long rate; /* cache it */ +// +// float m_val; /* Masking compensation value */ +// +//} vorbis_look_psy; +// +//extern void _vp_psy_init(vorbis_look_psy *p,vorbis_info_psy *vi, +// vorbis_info_psy_global *gi,int n,long rate); +//extern void _vp_psy_clear(vorbis_look_psy *p); +//extern void *_vi_psy_dup(void *source); +// +//extern void _vi_psy_free(vorbis_info_psy *i); +//extern vorbis_info_psy *_vi_psy_copy(vorbis_info_psy *i); +// +//extern void _vp_remove_floor(vorbis_look_psy *p, +// float *mdct, +// int *icodedflr, +// float *residue, +// int sliding_lowpass); +// +//extern void _vp_noisemask(vorbis_look_psy *p, +// float *logmdct, +// float *logmask); +// +//extern void _vp_tonemask(vorbis_look_psy *p, +// float *logfft, +// float *logmask, +// float global_specmax, +// float local_specmax); +// +//extern void _vp_offset_and_mix(vorbis_look_psy *p, +// float *noise, +// float *tone, +// int offset_select, +// float *logmask, +// float *mdct, +// float *logmdct); +// +//extern float _vp_ampmax_decay(float amp,vorbis_dsp_state *vd); +// +//extern float **_vp_quantize_couple_memo(vorbis_block *vb, +// vorbis_info_psy_global *g, +// vorbis_look_psy *p, +// vorbis_info_mapping0 *vi, +// float **mdct); +// +//extern void _vp_couple(int blobno, +// vorbis_info_psy_global *g, +// vorbis_look_psy *p, +// vorbis_info_mapping0 *vi, +// float **res, +// float **mag_memo, +// int **mag_sort, +// int **ifloor, +// int *nonzero, +// int sliding_lowpass); +// +//extern void _vp_noise_normalize(vorbis_look_psy *p, +// float *in,float *out,int *sortedindex); +// +//extern void _vp_noise_normalize_sort(vorbis_look_psy *p, +// float *magnitudes,int *sortedindex); +// +//extern int **_vp_quantize_couple_sort(vorbis_block *vb, +// vorbis_look_psy *p, +// vorbis_info_mapping0 *vi, +// float **mags); +// +//extern void hf_reduction(vorbis_info_psy_global *g, +// vorbis_look_psy *p, +// vorbis_info_mapping0 *vi, +// float **mdct); + + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/registry.c b/lib/ogg_vorbis/vorbis/lib/registry.c new file mode 100755 index 0000000..545229a --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/registry.c @@ -0,0 +1,45 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: registry for time, floor, res backends and channel mappings + last mod: $Id: registry.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "misc.h" +/* seems like major overkill now; the backend numbers will grow into + the infrastructure soon enough */ + +extern const vorbis_func_floor FMOD_floor0_exportbundle; +extern const vorbis_func_floor FMOD_floor1_exportbundle; +extern const vorbis_func_residue FMOD_residue0_exportbundle; +extern const vorbis_func_residue FMOD_residue1_exportbundle; +extern const vorbis_func_residue FMOD_residue2_exportbundle; +extern const vorbis_func_mapping FMOD_mapping0_exportbundle; + +const vorbis_func_floor *const _FMOD_floor_P[]={ + 0, //&FMOD_floor0_exportbundle, // REMOVED BY FMOD - OBSOLETE FORMAT - error check is in info.c + &FMOD_floor1_exportbundle, +}; + +const vorbis_func_residue *const _FMOD_residue_P[]={ + &FMOD_residue0_exportbundle, + &FMOD_residue1_exportbundle, + &FMOD_residue2_exportbundle, +}; + +const vorbis_func_mapping *const _FMOD_mapping_P[]={ + &FMOD_mapping0_exportbundle, +}; diff --git a/lib/ogg_vorbis/vorbis/lib/registry.h b/lib/ogg_vorbis/vorbis/lib/registry.h new file mode 100755 index 0000000..76a158a --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/registry.h @@ -0,0 +1,32 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: registry for time, floor, res backends and channel mappings + last mod: $Id: registry.h 15531 2008-11-24 23:50:06Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_REG_H_ +#define _V_REG_H_ + +#define VI_TRANSFORMB 1 +#define VI_WINDOWB 1 +#define VI_TIMEB 1 +#define VI_FLOORB 2 +#define VI_RESB 3 +#define VI_MAPB 1 + +extern const vorbis_func_floor *const _FMOD_floor_P[]; +extern const vorbis_func_residue *const _FMOD_residue_P[]; +extern const vorbis_func_mapping *const _FMOD_mapping_P[]; + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/res0.c b/lib/ogg_vorbis/vorbis/lib/res0.c new file mode 100755 index 0000000..71897b7 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/res0.c @@ -0,0 +1,998 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: residue backend 0, 1 and 2 implementation + last mod: $Id: res0.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +/* Slow, slow, slow, simpleminded and did I mention it was slow? The + encode/decode loops are coded for clarity and performance is not + yet even a nagging little idea lurking in the shadows. Oh and BTW, + it's slow. */ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" + +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) +#include +#endif + +typedef struct { + vorbis_info_residue0 *info; + + int parts; + int stages; + codebook *fullbooks; + codebook *phrasebook; + codebook ***partbooks; + + int partvals; + int **decodemap; + + ogg_int32_t postbits; + ogg_int32_t phrasebits; + ogg_int32_t frames; + +#if defined(TRAIN_RES) || defined(TRAIN_RESAUX) + int train_seq; + ogg_int32_t *training_data[8][64]; + float training_max[8][64]; + float training_min[8][64]; + float tmin; + float tmax; +#endif + +} vorbis_look_residue0; + +void FMOD_res0_free_info(void *context, vorbis_info_residue *i){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)i; + if(info){ + FMOD_memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +void FMOD_res0_free_look(void *context, vorbis_look_residue *i){ + int j; + if(i){ + + vorbis_look_residue0 *look=(vorbis_look_residue0 *)i; + +#ifdef TRAIN_RES + { + int j,k,l; + for(j=0;jparts;j++){ + /*fprintf(stderr,"partition %d: ",j);*/ + for(k=0;k<8;k++) + if(look->training_data[k][j]){ + char buffer[80]; + FILE *of; + codebook *statebook=look->partbooks[j][k]; + + /* long and short into the same bucket by current convention */ + sprintf(buffer,"res_part%d_pass%d.vqd",j,k); + of=fopen(buffer,"a"); + + for(l=0;lentries;l++) + fprintf(of,"%d:%ld\n",l,look->training_data[k][j][l]); + + fclose(of); + + /*fprintf(stderr,"%d(%.2f|%.2f) ",k, + look->training_min[k][j],look->training_max[k][j]);*/ + + _ogg_free(look->training_data[k][j]); + look->training_data[k][j]=NULL; + } + /*fprintf(stderr,"\n");*/ + } + } + fprintf(stderr,"min/max residue: %g::%g\n",look->tmin,look->tmax); + + /*fprintf(stderr,"residue bit usage %f:%f (%f total)\n", + (float)look->phrasebits/look->frames, + (float)look->postbits/look->frames, + (float)(look->postbits+look->phrasebits)/look->frames);*/ +#endif + + + /*vorbis_info_residue0 *info=look->info; + + fprintf(stderr, + "%ld frames encoded in %ld phrasebits and %ld residue bits " + "(%g/frame) \n",look->frames,look->phrasebits, + look->resbitsflat, + (look->phrasebits+look->resbitsflat)/(float)look->frames); + + for(j=0;jparts;j++){ + ogg_int32_t acc=0; + fprintf(stderr,"\t[%d] == ",j); + for(k=0;kstages;k++) + if((info->secondstages[j]>>k)&1){ + fprintf(stderr,"%ld,",look->resbits[j][k]); + acc+=look->resbits[j][k]; + } + + fprintf(stderr,":: (%ld vals) %1.2fbits/sample\n",look->resvals[j], + acc?(float)acc/(look->resvals[j]*info->grouping):0); + } + fprintf(stderr,"\n");*/ + + for(j=0;jparts;j++) + if(look->partbooks[j])_ogg_free(look->partbooks[j]); + _ogg_free(look->partbooks); + for(j=0;jpartvals;j++) + _ogg_free(look->decodemap[j]); + _ogg_free(look->decodemap); + + FMOD_memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int icount(unsigned int v){ + int ret=0; + while(v){ + ret+=v&1; + v>>=1; + } + return(ret); +} + +#if 0 +void FMOD_res0_pack(void *context, vorbis_info_residue *vr,oggpack_buffer *opb){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + int j,acc=0; + FMOD_oggpack_write(opb,info->begin,24); + FMOD_oggpack_write(opb,info->end,24); + + FMOD_oggpack_write(opb,info->grouping-1,24); /* residue vectors to group and + code with a partitioned book */ + FMOD_oggpack_write(opb,info->partitions-1,6); /* possible partition choices */ + FMOD_oggpack_write(opb,info->groupbook,8); /* group huffman book */ + + /* secondstages is a bitmask; as encoding progresses pass by pass, a + bitmask of one indicates this partition class has bits to write + this pass */ + for(j=0;jpartitions;j++){ + if(ilog(info->secondstages[j])>3){ + /* yes, this is a minor hack due to not thinking ahead */ + FMOD_oggpack_write(opb,info->secondstages[j],3); + FMOD_oggpack_write(opb,1,1); + FMOD_oggpack_write(opb,info->secondstages[j]>>3,5); + }else + FMOD_oggpack_write(opb,info->secondstages[j],4); /* trailing zero */ + acc+=icount(info->secondstages[j]); + } + for(j=0;jbooklist[j],8); + +} +#endif + +/* vorbis_info is for range checking */ +vorbis_info_residue *FMOD_res0_unpack(void *context, vorbis_info *vi,oggpack_buffer *opb){ + int j,acc=0; + vorbis_info_residue0 *info=_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=vi->codec_setup; + + if (!info) + { + goto errout; + } + + info->begin=FMOD_oggpack_read(opb,24); + info->end=FMOD_oggpack_read(opb,24); + info->grouping=FMOD_oggpack_read(opb,24)+1; + info->partitions=FMOD_oggpack_read(opb,6)+1; + info->groupbook=FMOD_oggpack_read(opb,8); + + /* check for premature EOP */ + if(info->groupbook<0)goto errout; + + for(j=0;jpartitions;j++){ + int cascade=FMOD_oggpack_read(opb,3); + int cflag=FMOD_oggpack_read(opb,1); + if(cflag<0) goto errout; + if(cflag){ + int c=FMOD_oggpack_read(opb,5); + if(c<0) goto errout; + cascade|=(c<<3); + } + info->secondstages[j]=cascade; + + acc+=icount(cascade); + } + for(j=0;jbooklist[j]=book; + } + + if(info->groupbook>=ci->books)goto errout; + for(j=0;jbooklist[j]>=ci->books)goto errout; + if(ci->book_param[info->booklist[j]]->maptype==0)goto errout; + } + + /* verify the phrasebook is not specifying an impossible or + inconsistent partitioning scheme. */ + { + int entries = ci->book_param[info->groupbook]->entries; + int dim = ci->book_param[info->groupbook]->dim; + int partvals = 1; + while(dim>0){ + partvals *= info->partitions; + if(partvals > entries) goto errout; + dim--; + } + } + + return(info); + errout: + FMOD_res0_free_info(context, info); + return(NULL); +} + +vorbis_look_residue *FMOD_res0_look(void *context, vorbis_dsp_state *vd, + vorbis_info_residue *vr){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + vorbis_look_residue0 *look=_ogg_calloc(1,sizeof(*look)); + codec_setup_info *ci=vd->vi->codec_setup; + int j,k,acc=0; + int dim; + int maxstage=0; + + if (!look) + { + return(NULL); + } + + look->info=info; + + look->parts=info->partitions; + look->fullbooks=ci->fullbooks; + look->phrasebook=ci->fullbooks+info->groupbook; + dim=look->phrasebook->dim; + + look->partbooks=_ogg_calloc(look->parts,sizeof(*look->partbooks)); + if (!look->partbooks) + { + return(NULL); + } + + for(j=0;jparts;j++){ + int stages=ilog(info->secondstages[j]); + if(stages){ + if(stages>maxstage)maxstage=stages; + look->partbooks[j]=_ogg_calloc(stages,sizeof(*look->partbooks[j])); + if (!look->partbooks[j]) + { + return 0; + } + for(k=0;ksecondstages[j]&(1<partbooks[j][k]=ci->fullbooks+info->booklist[acc++]; +#ifdef TRAIN_RES + look->training_data[k][j]=_ogg_calloc(look->partbooks[j][k]->entries, + sizeof(***look->training_data)); +#endif + } + } + } + + look->partvals=1; + for(j=0;jpartvals*=look->parts; + + look->stages=maxstage; + look->decodemap=_ogg_malloc(look->partvals*sizeof(*look->decodemap)); + if (!look->decodemap) + { + return 0; + } + for(j=0;jpartvals;j++){ + ogg_int32_t val=j; + ogg_int32_t mult=look->partvals/look->parts; + look->decodemap[j]=_ogg_malloc(dim*sizeof(*look->decodemap[j])); + if (!look->decodemap[j]) + { + return 0; + } + for(k=0;kparts; + look->decodemap[j][k]=deco; + } + } +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) + { + static int train_seq=0; + look->train_seq=train_seq++; + } +#endif + return(look); +} + +#if 0 +/* break an abstraction and copy some code for performance purposes */ +static int local_book_besterror(codebook *book,float *a){ + int dim=book->dim,i,k,o; + int best=0; + encode_aux_threshmatch *tt=book->c->thresh_tree; + + /* find the quant val of each scalar */ + for(k=0,o=dim;kthreshvals>>1; + + if(valquantthresh[i]){ + if(valquantthresh[i-1]){ + for(--i;i>0;--i) + if(val>=tt->quantthresh[i-1]) + break; + } + }else{ + + for(++i;ithreshvals-1;++i) + if(valquantthresh[i])break; + + } + + best=(best*tt->quantvals)+tt->quantmap[i]; + } + /* regular lattices are easy :-) */ + + if(book->c->lengthlist[best]<=0){ + const static_codebook *c=book->c; + int i,j; + float bestf=0.f; + float *e=book->valuelist; + best=-1; + for(i=0;ientries;i++){ + if(c->lengthlist[i]>0){ + float this=0.f; + for(j=0;j-1){ + float *ptr=book->valuelist+best*dim; + for(i=0;idim; + int step=n/dim; + + for(i=0;i0) + acc[entry]++; +#endif + + bits+=FMOD_vorbis_book_encode(book,entry,opb); + + } + + return(bits); +} +#endif + +static ogg_int32_t **_FMOD_01class(void *context, vorbis_block *vb,vorbis_look_residue *vl, + float **in,int ch){ + ogg_int32_t i,j,k; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + ogg_int32_t **partword=_FMOD_vorbis_block_alloc(context, vb,ch*sizeof(*partword)); + float scale=100.0f/samples_per_partition; + + if (!partword) + { + return(NULL); + } + + /* we find the partition type for each partition of each + channel. We'll go back and do the interleaved encoding in a + bit. For now, clarity */ + + for(i=0;ibegin; + for(j=0;jmax)max=FMOD_ogg_fabs(in[j][offset+k]); + ent+=FMOD_ogg_fabs(FMOD_ogg_rint(in[j][offset+k])); + } + ent*=scale; + + for(k=0;kclassmetric1[k] && + (info->classmetric2[k]<0 || (int)entclassmetric2[k])) + break; + + partword[j][i]=k; + } + } + +#ifdef TRAIN_RESAUX + { + FILE *of; + char buffer[80]; + + for(i=0;itrain_seq); + of=fopen(buffer,"a"); + for(j=0;jframes++; + + return(partword); +} + +#if 0 +/* designed for stereo or other modes where the partition size is an + integer multiple of the number of channels encoded in the current + submap */ +static ogg_int32_t **_2class(void *context, vorbis_block *vb,vorbis_look_residue *vl,float **in, + int ch){ + ogg_int32_t i,j,k,l; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + ogg_int32_t **partword=_FMOD_vorbis_block_alloc(context, vb,sizeof(*partword)); + +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) + FILE *of; + char buffer[80]; +#endif + + partword[0]=_FMOD_vorbis_block_alloc(context, vb,n*ch/samples_per_partition*sizeof(*partword[0])); + FMOD_memset(partword[0],0,n*ch/samples_per_partition*sizeof(*partword[0])); + + for(i=0,l=info->begin/ch;imagmax)magmax=FMOD_ogg_fabs(in[0][l]); + for(k=1;kangmax)angmax=FMOD_ogg_fabs(in[k][l]); + l++; + } + + for(j=0;jclassmetric1[j] && + angmax<=info->classmetric2[j]) + break; + + partword[0][i]=j; + + } + +#ifdef TRAIN_RESAUX + sprintf(buffer,"resaux_%d.vqd",look->train_seq); + of=fopen(buffer,"a"); + for(i=0;iframes++; + + return(partword); +} + +static int _01forward(oggpack_buffer *opb, + vorbis_block *vb,vorbis_look_residue *vl, + float **in,int ch, + ogg_int32_t **partword, + int (*encode)(oggpack_buffer *,float *,int, + codebook *,ogg_int32_t *)){ + ogg_int32_t i,j,k,s; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int partitions_per_word=look->phrasebook->dim; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + ogg_int32_t resbits[128]; + ogg_int32_t resvals[128]; + +#ifdef TRAIN_RES + for(i=0;ibegin;jlook->tmax)look->tmax=in[i][j]; + if(in[i][j]tmin)look->tmin=in[i][j]; + } +#endif + + FMOD_memset(resbits,0,sizeof(resbits)); + FMOD_memset(resvals,0,sizeof(resvals)); + + /* we code the partition words for each channel, then the residual + words for a partition per channel until we've written all the + residual words for that partition word. Then write the next + partition channel words... */ + + for(s=0;sstages;s++){ + + for(i=0;iphrasebook->entries) + look->phrasebits+=FMOD_vorbis_book_encode(look->phrasebook,val,opb); +#if 0 /*def TRAIN_RES*/ + else + fprintf(stderr,"!"); +#endif + + } + } + + /* now we encode interleaved residual values for the partitions */ + for(k=0;kbegin; + + for(j=0;jsecondstages[partword[j][i]]&(1<partbooks[partword[j][i]][s]; + if(statebook){ + int ret; + ogg_int32_t *accumulator=NULL; + +#ifdef TRAIN_RES + accumulator=look->training_data[s][partword[j][i]]; + { + int l; + float *samples=in[j]+offset; + for(l=0;ltraining_min[s][partword[j][i]]) + look->training_min[s][partword[j][i]]=samples[l]; + if(samples[l]>look->training_max[s][partword[j][i]]) + look->training_max[s][partword[j][i]]=samples[l]; + } + } +#endif + + ret=encode(opb,in[j]+offset,samples_per_partition, + statebook,accumulator); + + look->postbits+=ret; + resbits[partword[j][i]]+=ret; + } + } + } + } + } + } + + /*{ + ogg_int32_t total=0; + ogg_int32_t totalbits=0; + fprintf(stderr,"%d :: ",vb->mode); + for(k=0;kinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int max=vb->pcmend>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int ***partword=alloca(ch*sizeof(*partword)); + + for(j=0;jstages;s++){ + + /* each loop decodes on partition codeword containing + partitions_per_word partitions */ + for(i=0,l=0;iphrasebook,&vb->opb); + + if(temp==-1)goto eopbreak; + partword[j][l]=look->decodemap[temp]; + if(partword[j][l]==NULL)goto errout; + } + } + + /* now we decode residual values for the partitions */ + for(k=0;kbegin+i*samples_per_partition; + if(info->secondstages[partword[j][l][k]]&(1<partbooks[partword[j][l][k]][s]; + if(stagebook){ + if(decodepart(stagebook,in[j]+offset,&vb->opb, + samples_per_partition)==-1)goto eopbreak; + } + } + } + } + } + } + errout: + eopbreak: + return(0); +} + +#if 0 +/* residue 0 and 1 are just slight variants of one another. 0 is + interleaved, 1 is not */ +ogg_int32_t **FMOD_res0_class(vorbis_block *vb,vorbis_look_residue *vl, + float **in,int *nonzero,int ch){ + /* we encode only the nonzero parts of a bundle */ + int i,used=0; + for(i=0;ipcmend/2; + for(i=0;ipcmend/2; + for(i=0;ipcmend/2,used=0; + + /* don't duplicate the code; use a working vector hack for now and + reshape ourselves into a single channel res1 */ + /* ugly; reallocs for each coupling pass :-( */ + float *work=_FMOD_vorbis_block_alloc(context, vb,ch*n*sizeof(*work)); + + for(i=0;iinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int max=(vb->pcmend*ch)>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int **partword=_FMOD_vorbis_block_alloc(context, vb,partwords*sizeof(*partword)); + if (!partword) + { + return -1; + } + + for(i=0;istages;s++){ + for(i=0,l=0;iphrasebook,&vb->opb); + if(temp==-1)goto eopbreak; + partword[l]=look->decodemap[temp]; + if(partword[l]==NULL)goto errout; + } + + /* now we decode residual values for the partitions */ + for(k=0;ksecondstages[partword[l][k]]&(1<partbooks[partword[l][k]][s]; + + if(stagebook){ + if(FMOD_vorbis_book_decodevv_add(stagebook,in, + i*samples_per_partition+info->begin,ch, + &vb->opb,samples_per_partition)==-1) + goto eopbreak; + } + } + } + } + } + errout: + eopbreak: + return(0); +} + + +const vorbis_func_residue FMOD_residue0_exportbundle={ + NULL, + &FMOD_res0_unpack, + &FMOD_res0_look, + &FMOD_res0_free_info, + &FMOD_res0_free_look, + NULL, + NULL, + &FMOD_res0_inverse +}; + +const vorbis_func_residue FMOD_residue1_exportbundle={ + NULL, //&res0_pack, + &FMOD_res0_unpack, + &FMOD_res0_look, + &FMOD_res0_free_info, + &FMOD_res0_free_look, + NULL, //&res1_class, + NULL, //&res1_forward, + &FMOD_res1_inverse +}; + +const vorbis_func_residue FMOD_residue2_exportbundle={ + NULL, //&res0_pack, + &FMOD_res0_unpack, + &FMOD_res0_look, + &FMOD_res0_free_info, + &FMOD_res0_free_look, + NULL, //&res2_class, + NULL, //&res2_forward, + &FMOD_res2_inverse +}; diff --git a/lib/ogg_vorbis/vorbis/lib/scales.h b/lib/ogg_vorbis/vorbis/lib/scales.h new file mode 100755 index 0000000..4693d37 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/scales.h @@ -0,0 +1,90 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: linear scale -> dB, Bark and Mel scales + last mod: $Id: scales.h 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_SCALES_H_ +#define _V_SCALES_H_ + +#include +#include "os.h" + +#ifdef _MSC_VER +/* MS Visual Studio doesn't have C99 inline keyword. */ +#define inline __inline +#endif + +/* 20log10(x) */ +#define VORBIS_IEEE_FLOAT32 1 +#ifdef VORBIS_IEEE_FLOAT32 + +static inline float unitnorm(float x){ + union { + ogg_uint32_t i; + float f; + } ix; + ix.f = x; + ix.i = (ix.i & 0x80000000U) | (0x3f800000U); + return ix.f; +} + +/* Segher was off (too high) by ~ .3 decibel. Center the conversion correctly. */ +static inline float todB(const float *x){ + union { + ogg_uint32_t i; + float f; + } ix; + ix.f = *x; + ix.i = ix.i&0x7fffffff; + return (float)(ix.i * 7.17711438e-7f -764.6161886f); +} + +#define todB_nn(x) todB(x) + +#else + +static float unitnorm(float x){ + if(x<0)return(-1.f); + return(1.f); +} + +#define todB(x) (*(x)==0?-400.f:FMOD_ogg_log(*(x)**(x))*4.34294480f) +#define todB_nn(x) (*(x)==0.f?-400.f:FMOD_ogg_log(*(x))*8.6858896f) + +#endif + +#define fromdB(x) (FMOD_ogg_exp((x)*.11512925f)) + +/* The bark scale equations are approximations, since the original + table was somewhat hand rolled. The below are chosen to have the + best possible fit to the rolled tables, thus their somewhat odd + appearance (these are more accurate and over a longer range than + the oft-quoted bark equations found in the texts I have). The + approximations are valid from 0 - 30kHz (nyquist) or so. + + all f in Hz, z in Bark */ + +#define toBARK(n) (13.1f*FMOD_ogg_atan(.00074f*(n))+2.24f*FMOD_ogg_atan((n)*(n)*1.85e-8f)+1e-4f*(n)) +#define fromBARK(z) (102.f*(z)-2.f*FMOD_ogg_pow(z,2.f)+.4f*FMOD_ogg_pow(z,3.f)+FMOD_ogg_pow(1.46f,z)-1.f) +#define toMEL(n) (FMOD_ogg_log(1.f+(n)*.001f)*1442.695f) +#define fromMEL(m) (1000.f*FMOD_ogg_exp((m)/1442.695f)-1000.f) + +/* Frequency to octave. We arbitrarily declare 63.5 Hz to be octave + 0.0 */ + +#define toOC(n) (FMOD_ogg_log(n)*1.442695f-5.965784f) +#define fromOC(o) (FMOD_ogg_exp(((o)+5.965784f)*.693147f)) + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/sharedbook.c b/lib/ogg_vorbis/vorbis/lib/sharedbook.c new file mode 100755 index 0000000..03dd3a9 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/sharedbook.c @@ -0,0 +1,782 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + last mod: $Id: sharedbook.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "os.h" +#include "misc.h" +#include "vorbis/codec.h" +#include "codebook.h" +#include "scales.h" + +/**** pack/unpack helpers ******************************************/ +int _FMOD_ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* 32 bit float (not IEEE; nonnormalized mantissa + + biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + Why not IEEE? It's just not that important here. */ + +#define VQ_FEXP 10 +#define VQ_FMAN 21 +#define VQ_FEXP_BIAS 768 /* bias toward values smaller than 1. */ + +/* doesn't currently guard under/overflow */ +ogg_int32_t _FMOD_float32_pack(float val){ + int sign=0; + ogg_int32_t exp; + ogg_int32_t mant; + if(val<0){ + sign=0x80000000; + val= -val; + } + exp= (ogg_int32_t)FMOD_ogg_floor(FMOD_ogg_log(val)/log(2.f)); + mant=(ogg_int32_t)FMOD_ogg_rint(FMOD_ogg_ldexp(val,(VQ_FMAN-1)-exp)); + exp=(exp+VQ_FEXP_BIAS)<>VQ_FMAN; + if(sign)mant= -mant; + return(FMOD_ogg_ldexp(mant,exp-(VQ_FMAN-1)-VQ_FEXP_BIAS)); +} + +/* given a list of word lengths, generate a list of codewords. Works + for length ordered or unordered, always assigns the lowest valued + codewords first. Extended to handle unused entries (length 0) */ +ogg_uint32_t *_FMOD_make_words(void *context, ogg_int32_t *l,ogg_int32_t n,ogg_int32_t sparsecount){ + ogg_int32_t i,j,count=0; + ogg_uint32_t marker[33]; + ogg_uint32_t *r=_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r)); + if (!r) + { + return(NULL); + } + FMOD_memset(marker,0,sizeof(marker)); + + for(i=0;i0){ + ogg_uint32_t entry=marker[length]; + + /* when we claim a node for an entry, we also claim the nodes + below it (pruning off the imagined tree that may have dangled + from it) as well as blocking the use of any nodes directly + above for leaves */ + + /* update ourself */ + if(length<32 && (entry>>length)){ + /* error condition; the lengths must specify an overpopulated tree */ + _ogg_free(r); + return(NULL); + } + r[count++]=entry; + + /* Look to see if the next shorter marker points to the node + above. if so, update it and repeat. */ + { + for(j=length;j>0;j--){ + + if(marker[j]&1){ + /* have to jump branches */ + if(j==1) + marker[1]++; + else + marker[j]=marker[j-1]<<1; + break; /* invariant says next upper marker would already + have been moved if it was on the same path */ + } + marker[j]++; + } + } + + /* prune the tree; the implicit invariant says all the longer + markers were dangling from our just-taken node. Dangle them + from our *new* node. */ + for(j=length+1;j<33;j++) + if((marker[j]>>1) == entry){ + entry=marker[j]; + marker[j]=marker[j-1]<<1; + }else + break; + }else + if(sparsecount==0)count++; + } + + /* sanity check the huffman tree; an underpopulated tree must be + rejected. The only exception is the one-node pseudo-nil tree, + which appears to be underpopulated because the tree doesn't + really exist; there's only one possible 'codeword' or zero bits, + but the above tree-gen code doesn't mark that. */ + if(sparsecount != 1){ + for(i=1;i<33;i++) + if(marker[i] & (0xffffffffUL>>(32-i))){ + _ogg_free(r); + return(NULL); + } + } + + /* bitreverse the words because our bitwise packer/unpacker is LSb + endian */ + for(i=0,count=0;i>j)&1; + } + + if(sparsecount){ + if(l[i]) + r[count++]=temp; + }else + r[count++]=temp; + } + + return(r); +} + +/* there might be a straightforward one-line way to do the below + that's portable and totally safe against roundoff, but I haven't + thought of it. Therefore, we opt on the side of caution */ +ogg_int32_t _FMOD_book_maptype1_quantvals(const static_codebook *b){ + ogg_int32_t vals=(ogg_int32_t)FMOD_ogg_floor(FMOD_ogg_pow((float)b->entries,1.f/b->dim)); + + /* the above *should* be reliable, but we'll not assume that FP is + ever reliable when bitstream sync is at stake; verify via integer + means that vals really is the greatest value of dim for which + vals^b->bim <= b->entries */ + /* treat the above as an initial guess */ + while(1){ + ogg_int32_t acc=1; + ogg_int32_t acc1=1; + int i; + for(i=0;idim;i++){ + acc*=vals; + acc1*=vals+1; + } + if(acc<=b->entries && acc1>b->entries){ + return(vals); + }else{ + if(acc>b->entries){ + vals--; + }else{ + vals++; + } + } + } +} + +/* unpack the quantized list of values for encode/decode ***********/ +/* we need to deal with two map types: in map type 1, the values are + generated algorithmically (each column of the vector counts through + the values in the quant vector). in map type 2, all the values came + in in an explicit list. Both value lists must be unpacked */ +float *_FMOD_book_unquantize(void *context, const static_codebook *b,int n,int *sparsemap){ + ogg_int32_t j,k,count=0; + if(b->maptype==1 || b->maptype==2){ + int quantvals; + float mindel=_FMOD_float32_unpack(b->q_min); + float delta=_FMOD_float32_unpack(b->q_delta); + float *r=_ogg_calloc(n*b->dim,sizeof(*r)); + if (!r) + { + return(NULL); + } + + /* maptype 1 and 2 both use a quantized value vector, but + different sizes */ + switch(b->maptype){ + case 1: + /* most of the time, entries%dimensions == 0, but we need to be + well defined. We define that the possible vales at each + scalar is values == entries/dim. If entries%dim != 0, we'll + have 'too few' values (values*dimentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + float last=0.f; + int indexdiv=1; + for(k=0;kdim;k++){ + int index= (j/indexdiv)%quantvals; + float val=(float)b->quantlist[index]; + val=FMOD_ogg_fabs(val)*delta+mindel+last; + if(b->q_sequencep)last=val; + if(sparsemap) + r[sparsemap[count]*b->dim+k]=val; + else + r[count*b->dim+k]=val; + indexdiv*=quantvals; + } + count++; + } + + } + break; + case 2: + for(j=0;jentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + float last=0.f; + + for(k=0;kdim;k++){ + float val=(float)b->quantlist[j*b->dim+k]; + val=FMOD_ogg_fabs(val)*delta+mindel+last; + if(b->q_sequencep)last=val; + if(sparsemap) + r[sparsemap[count]*b->dim+k]=val; + else + r[count*b->dim+k]=val; + } + count++; + } + } + break; + } + + return(r); + } + return(NULL); +} + +void FMOD_vorbis_staticbook_clear(void *context, static_codebook *b){ + if(b->allocedp){ + if(b->quantlist)_ogg_free(b->quantlist); + if(b->lengthlist)_ogg_free(b->lengthlist); + //if(b->nearest_tree){ + // _ogg_free(b->nearest_tree->ptr0); + // _ogg_free(b->nearest_tree->ptr1); + // _ogg_free(b->nearest_tree->p); + // _ogg_free(b->nearest_tree->q); + // FMOD_memset(b->nearest_tree,0,sizeof(*b->nearest_tree)); + // _ogg_free(b->nearest_tree); + //} + //if(b->thresh_tree){ + // _ogg_free(b->thresh_tree->quantthresh); + // _ogg_free(b->thresh_tree->quantmap); + // FMOD_memset(b->thresh_tree,0,sizeof(*b->thresh_tree)); + // _ogg_free(b->thresh_tree); + //} + + FMOD_memset(b,0,sizeof(*b)); + } +} + +void FMOD_vorbis_staticbook_destroy(void *context, static_codebook *b){ + if(b->allocedp){ + FMOD_vorbis_staticbook_clear(context, b); + _ogg_free(b); + } +} + +void FMOD_vorbis_book_clear(void *context, codebook *b){ + /* static book is not cleared; we're likely called on the lookup and + the static codebook belongs to the info struct */ + if(b->valuelist)_ogg_free(b->valuelist); + if(b->codelist)_ogg_free(b->codelist); + + if(b->dec_index)_ogg_free(b->dec_index); + if(b->dec_codelengths)_ogg_free(b->dec_codelengths); + if(b->dec_firsttable)_ogg_free(b->dec_firsttable); + + FMOD_memset(b,0,sizeof(*b)); +} + +#if 0 +int FMOD_vorbis_book_init_encode(void *context, codebook *c,const static_codebook *s){ + + FMOD_memset(c,0,sizeof(*c)); + c->c=s; + c->entries=s->entries; + c->used_entries=s->entries; + c->dim=s->dim; + c->codelist=_FMOD_make_words(context, s->lengthlist,s->entries,0); + c->valuelist=_FMOD_book_unquantize(context, s,s->entries,NULL); + + return(0); +} +#endif + +static ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffff) | ((x<<16)&0xffff0000); + x= ((x>> 8)&0x00ff00ff) | ((x<< 8)&0xff00ff00); + x= ((x>> 4)&0x0f0f0f0f) | ((x<< 4)&0xf0f0f0f0); + x= ((x>> 2)&0x33333333) | ((x<< 2)&0xcccccccc); + return((x>> 1)&0x55555555) | ((x<< 1)&0xaaaaaaaa); +} + +static int sort32a(const void *a,const void *b){ + return ( **(ogg_uint32_t **)a>**(ogg_uint32_t **)b)- + ( **(ogg_uint32_t **)a<**(ogg_uint32_t **)b); +} + +/* decode codebook arrangement is more heavily optimized than encode */ +int FMOD_vorbis_book_init_decode(void *context, codebook *c,const static_codebook *s){ + int i,j,n=0,tabn; + int *sortindex; + FMOD_memset(c,0,sizeof(*c)); + + /* count actually used entries */ + for(i=0;ientries;i++) + if(s->lengthlist[i]>0) + n++; + + c->entries=s->entries; + c->used_entries=n; + c->dim=s->dim; + + if(n>0){ + + /* two different remappings go on here. + + First, we collapse the likely sparse codebook down only to + actually represented values/words. This collapsing needs to be + indexed as map-valueless books are used to encode original entry + positions as integers. + + Second, we reorder all vectors, including the entry index above, + by sorted bitreversed codeword to allow treeless decode. */ + + /* perform sort */ + ogg_uint32_t *codes=_FMOD_make_words(context, s->lengthlist,s->entries,c->used_entries); + ogg_uint32_t **codep=alloca(sizeof(*codep)*n); + + if(codes==NULL)goto err_out; + + for(i=0;icodelist=_ogg_malloc(n*sizeof(*c->codelist)); + if (!c->codelist) + { + goto err_out; + } + /* the index is a reverse index */ + for(i=0;icodelist[sortindex[i]]=codes[i]; + _ogg_free(codes); + + + c->valuelist=_FMOD_book_unquantize(context, s,n,sortindex); + c->dec_index=_ogg_malloc(n*sizeof(*c->dec_index)); + if (!c->dec_index) + { + goto err_out; + } + + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_index[sortindex[n++]]=i; + + c->dec_codelengths=_ogg_malloc(n*sizeof(*c->dec_codelengths)); + if (!c->dec_codelengths) + { + goto err_out; + } + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_codelengths[sortindex[n++]]=s->lengthlist[i]; + + c->dec_firsttablen=_FMOD_ilog(c->used_entries)-4; /* this is magic */ + if(c->dec_firsttablen<5)c->dec_firsttablen=5; + if(c->dec_firsttablen>8)c->dec_firsttablen=8; + + tabn=1<dec_firsttablen; + c->dec_firsttable=_ogg_calloc(tabn,sizeof(*c->dec_firsttable)); + if (!c->dec_firsttable) + { + goto err_out; + } + c->dec_maxlength=0; + + for(i=0;idec_maxlengthdec_codelengths[i]) + c->dec_maxlength=c->dec_codelengths[i]; + if(c->dec_codelengths[i]<=c->dec_firsttablen){ + ogg_uint32_t orig=bitreverse(c->codelist[i]); + for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++) + c->dec_firsttable[orig|(j<dec_codelengths[i])]=i+1; + } + } + + /* now fill in 'unused' entries in the firsttable with hi/lo search + hints for the non-direct-hits */ + { + ogg_uint32_t mask=0xfffffffeUL<<(31-c->dec_firsttablen); + ogg_int32_t lo=0,hi=0; + + for(i=0;idec_firsttablen); + if(c->dec_firsttable[bitreverse(word)]==0){ + while((lo+1)codelist[lo+1]<=word)lo++; + while( hi=(c->codelist[hi]&mask))hi++; + + /* we only actually have 15 bits per hint to play with here. + In order to overflow gracefully (nothing breaks, efficiency + just drops), encode as the difference from the extremes. */ + { + ogg_uint32_t loval=lo; + ogg_uint32_t hival=n-hi; + + if(loval>0x7fff)loval=0x7fff; + if(hival>0x7fff)hival=0x7fff; + c->dec_firsttable[bitreverse(word)]= + 0x80000000UL | (loval<<15) | hival; + } + } + } + } + } + + return(0); + err_out: + FMOD_vorbis_book_clear(context, c); + return(-1); +} + +static float _dist(int el,float *ref, float *b,int step){ + int i; + float acc=0.f; + for(i=0;ic->thresh_tree; + +#if 0 + encode_aux_nearestmatch *nt=book->c->nearest_tree; + encode_aux_pigeonhole *pt=book->c->pigeon_tree; +#endif + int dim=book->dim; + int k,o; + /*int savebest=-1; + float saverr;*/ + + /* do we have a threshhold encode hint? */ + if(tt){ + int index=0,i; + /* find the quant val of each scalar */ + for(k=0,o=step*(dim-1);kthreshvals>>1; + if(a[o]quantthresh[i]){ + + for(;i>0;i--) + if(a[o]>=tt->quantthresh[i-1]) + break; + + }else{ + + for(i++;ithreshvals-1;i++) + if(a[o]quantthresh[i])break; + + } + + index=(index*tt->quantvals)+tt->quantmap[i]; + } + /* regular lattices are easy :-) */ + if(book->c->lengthlist[index]>0) /* is this unused? If so, we'll + use a decision tree after all + and fall through*/ + return(index); + } + +#if 0 + /* do we have a pigeonhole encode hint? */ + if(pt){ + const static_codebook *c=book->c; + int i,besti=-1; + float best=0.f; + int entry=0; + + /* dealing with sequentialness is a pain in the ass */ + if(c->q_sequencep){ + int pv; + ogg_int32_t mul=1; + float qlast=0; + for(k=0,o=0;kmin)/pt->del); + if(pv<0 || pv>=pt->mapentries)break; + entry+=pt->pigeonmap[pv]*mul; + mul*=pt->quantvals; + qlast+=pv*pt->del+pt->min; + } + }else{ + for(k=0,o=step*(dim-1);kmin)/pt->del); + if(pv<0 || pv>=pt->mapentries)break; + entry=entry*pt->quantvals+pt->pigeonmap[pv]; + } + } + + /* must be within the pigeonholable range; if we quant outside (or + in an entry that we define no list for), brute force it */ + if(k==dim && pt->fitlength[entry]){ + /* search the abbreviated list */ + ogg_int32_t *list=pt->fitlist+pt->fitmap[entry]; + for(i=0;ifitlength[entry];i++){ + float this=_dist(dim,book->valuelist+list[i]*dim,a,step); + if(besti==-1 || thisvaluelist+nt->p[ptr]; + float *q=book->valuelist+nt->q[ptr]; + + for(k=0,o=0;k0.f) /* in A */ + ptr= -nt->ptr0[ptr]; + else /* in B */ + ptr= -nt->ptr1[ptr]; + if(ptr<=0)break; + } + return(-ptr); + } +#endif + + /* brute force it! */ + { + const static_codebook *c=book->c; + int i,besti=-1; + float best=0.f; + float *e=book->valuelist; + for(i=0;ientries;i++){ + if(c->lengthlist[i]>0){ + float this=_dist(dim,e,a,step); + if(besti==-1 || thisvaluelist+savebest*dim)[i]); + fprintf(stderr,"\n" + "bruteforce (entry %d, err %g):",besti,best); + for(i=0;ivaluelist+besti*dim)[i]); + fprintf(stderr,"\n"); + }*/ + return(besti); + } +} + +ogg_int32_t vorbis_book_codeword(codebook *book,int entry){ + if(book->c) /* only use with encode; decode optimizations are + allowed to break this */ + return book->codelist[entry]; + return -1; +} + +ogg_int32_t vorbis_book_codelen(codebook *book,int entry){ + if(book->c) /* only use with encode; decode optimizations are + allowed to break this */ + return book->c->lengthlist[entry]; + return -1; +} + +#endif + +#ifdef _V_SELFTEST + +/* Unit tests of the dequantizer; this stuff will be OK + cross-platform, I simply want to be sure that special mapping cases + actually work properly; a bug could go unnoticed for a while */ + +#include + +/* cases: + + no mapping + full, explicit mapping + algorithmic mapping + + nonsequential + sequential +*/ + +static ogg_int32_t full_quantlist1[]={0,1,2,3, 4,5,6,7, 8,3,6,1}; +static ogg_int32_t partial_quantlist1[]={0,7,2}; + +/* no mapping */ +static_codebook test1={ + 4,16, + NULL, + 0, + 0,0,0,0, + NULL, + NULL,NULL,NULL, + 0 +}; +static float *test1_result=NULL; + +/* linear, full mapping, nonsequential */ +static_codebook test2={ + 4,3, + NULL, + 2, + -533200896,1611661312,4,0, + full_quantlist1, + NULL,NULL,NULL, + 0 +}; +static float test2_result[]={-3,-2,-1,0, 1,2,3,4, 5,0,3,-2}; + +/* linear, full mapping, sequential */ +static_codebook test3={ + 4,3, + NULL, + 2, + -533200896,1611661312,4,1, + full_quantlist1, + NULL,NULL,NULL, + 0 +}; +static float test3_result[]={-3,-5,-6,-6, 1,3,6,10, 5,5,8,6}; + +/* linear, algorithmic mapping, nonsequential */ +static_codebook test4={ + 3,27, + NULL, + 1, + -533200896,1611661312,4,0, + partial_quantlist1, + NULL,NULL,NULL, + 0 +}; +static float test4_result[]={-3,-3,-3, 4,-3,-3, -1,-3,-3, + -3, 4,-3, 4, 4,-3, -1, 4,-3, + -3,-1,-3, 4,-1,-3, -1,-1,-3, + -3,-3, 4, 4,-3, 4, -1,-3, 4, + -3, 4, 4, 4, 4, 4, -1, 4, 4, + -3,-1, 4, 4,-1, 4, -1,-1, 4, + -3,-3,-1, 4,-3,-1, -1,-3,-1, + -3, 4,-1, 4, 4,-1, -1, 4,-1, + -3,-1,-1, 4,-1,-1, -1,-1,-1}; + +/* linear, algorithmic mapping, sequential */ +static_codebook test5={ + 3,27, + NULL, + 1, + -533200896,1611661312,4,1, + partial_quantlist1, + NULL,NULL,NULL, + 0 +}; +static float test5_result[]={-3,-6,-9, 4, 1,-2, -1,-4,-7, + -3, 1,-2, 4, 8, 5, -1, 3, 0, + -3,-4,-7, 4, 3, 0, -1,-2,-5, + -3,-6,-2, 4, 1, 5, -1,-4, 0, + -3, 1, 5, 4, 8,12, -1, 3, 7, + -3,-4, 0, 4, 3, 7, -1,-2, 2, + -3,-6,-7, 4, 1, 0, -1,-4,-5, + -3, 1, 0, 4, 8, 7, -1, 3, 2, + -3,-4,-5, 4, 3, 2, -1,-2,-3}; + +void run_test(static_codebook *b,float *comp){ + float *out=_FMOD_book_unquantize(context, b,b->entries,NULL); + int i; + + if(comp){ + if(!out){ + fprintf(stderr,"_book_unquantize incorrectly returned NULL\n"); + exit(1); + } + + for(i=0;ientries*b->dim;i++) + if(FMOD_ogg_fabs(out[i]-comp[i])>.0001f){ + fprintf(stderr,"disagreement in unquantized and reference data:\n" + "position %d, %g != %g\n",i,out[i],comp[i]); + exit(1); + } + + }else{ + if(out){ + fprintf(stderr,"_book_unquantize returned a value array: \n" + " correct result should have been NULL\n"); + exit(1); + } + } +} + +int main(){ + /* run the nine dequant tests, and compare to the hand-rolled results */ + fprintf(stderr,"Dequant test 1... "); + run_test(&test1,test1_result); + fprintf(stderr,"OK\nDequant test 2... "); + run_test(&test2,test2_result); + fprintf(stderr,"OK\nDequant test 3... "); + run_test(&test3,test3_result); + fprintf(stderr,"OK\nDequant test 4... "); + run_test(&test4,test4_result); + fprintf(stderr,"OK\nDequant test 5... "); + run_test(&test5,test5_result); + fprintf(stderr,"OK\n\n"); + + return(0); +} + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/smallft.c b/lib/ogg_vorbis/vorbis/lib/smallft.c new file mode 100755 index 0000000..a6a96f2 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/smallft.c @@ -0,0 +1,1255 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: *unnormalized* fft transform + last mod: $Id: smallft.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +/* FFT implementation from OggSquish, minus cosine transforms, + * minus all but radix 2/4 case. In Vorbis we only need this + * cut-down version. + * + * To do more than just power-of-two sized vectors, see the full + * version I wrote for NetLib. + * + * Note that the packing is a little strange; rather than the FFT r/i + * packing following R_0, I_n, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, + * it follows R_0, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, I_n like the + * FORTRAN version + */ + +#include +#include +#include +#include "smallft.h" +#include "os.h" +#include "misc.h" + +static void drfti1(int n, float *wa, int *ifac){ + static int ntryh[4] = { 4,2,3,5 }; + static float tpi = 6.28318530717958648f; + float arg,argh,argld,fi; + int ntry=0,i,j=-1; + int k1, l1, l2, ib; + int ld, ii, ip, is, nq, nr; + int ido, ipm, nfm1; + int nl=n; + int nf=0; + + L101: + j++; + if (j < 4) + ntry=ntryh[j]; + else + ntry+=2; + + L104: + nq=nl/ntry; + nr=nl-ntry*nq; + if (nr!=0) goto L101; + + nf++; + ifac[nf+1]=ntry; + nl=nq; + if(ntry!=2)goto L107; + if(nf==1)goto L107; + + for (i=1;i>1; + ipp2=ip; + idp2=ido; + nbd=(ido-1)>>1; + t0=l1*ido; + t10=ip*ido; + + if(ido==1)goto L119; + for(ik=0;ikl1){ + for(j=1;j>1; + ipp2=ip; + ipph=(ip+1)>>1; + if(idol1)goto L139; + + is= -ido-1; + t1=0; + for(j=1;jn==1)return; + drftf1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); +} + +void drft_backward(drft_lookup *l,float *data){ + if (l->n==1)return; + drftb1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); +} + +void drft_init(void *context, drft_lookup *l,int n){ + l->n=n; + l->trigcache=_ogg_calloc(3*n,sizeof(*l->trigcache)); + l->splitcache=_ogg_calloc(32,sizeof(*l->splitcache)); + fdrffti(n, l->trigcache, l->splitcache); +} + +void drft_clear(void *context, drft_lookup *l){ + if(l){ + if(l->trigcache)_ogg_free(l->trigcache); + if(l->splitcache)_ogg_free(l->splitcache); + FMOD_memset(l,0,sizeof(*l)); + } +} diff --git a/lib/ogg_vorbis/vorbis/lib/smallft.h b/lib/ogg_vorbis/vorbis/lib/smallft.h new file mode 100755 index 0000000..65e1545 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/smallft.h @@ -0,0 +1,34 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: fft transform + last mod: $Id: smallft.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_SMFT_H_ +#define _V_SMFT_H_ + +#include "vorbis/codec.h" + +typedef struct { + int n; + float *trigcache; + int *splitcache; +} drft_lookup; + +extern void drft_forward(drft_lookup *l,float *data); +extern void drft_backward(drft_lookup *l,float *data); +extern void drft_init(void *context, drft_lookup *l,int n); +extern void drft_clear(void *context, drft_lookup *l); + +#endif diff --git a/lib/ogg_vorbis/vorbis/lib/synthesis.c b/lib/ogg_vorbis/vorbis/lib/synthesis.c new file mode 100755 index 0000000..d637f07 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/synthesis.c @@ -0,0 +1,189 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: single-block PCM synthesis + last mod: $Id: synthesis.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "misc.h" +#include "os.h" + +int FMOD_vorbis_synthesis(void *context, vorbis_block *vb,ogg_packet *op){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=vd->backend_state; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + oggpack_buffer *opb=&vb->opb; + int type,mode,i,ret; + + /* first things first. Make sure decode is ready */ + ret = _FMOD_vorbis_block_ripcord(context, vb); + if (ret) + { + return ret; + } + + FMOD_oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(FMOD_oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=FMOD_oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + + /* this doesn;t get mapped through mode selection as it's used + only for window selection */ + vb->lW=FMOD_oggpack_read(opb,1); + vb->nW=FMOD_oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; + vb->eofflag=op->e_o_s; + + /* alloc pcm passback storage */ + vb->pcmend=ci->blocksizes[vb->W]; + vb->pcm=_FMOD_vorbis_block_alloc(context, vb,sizeof(*vb->pcm)*vi->channels); + if (!vb->pcm) + { + return(OV_EMEMORY); + } + + for(i=0;ichannels;i++) + { + vb->pcm[i]=_FMOD_vorbis_block_alloc(context, vb,vb->pcmend*sizeof(*vb->pcm[i])); + if (!vb->pcm[i]) + { + return(OV_EMEMORY); + } + } + + /* unpack_header enforces range checking */ + type=ci->map_type[ci->mode_param[mode]->mapping]; + + return(_FMOD_mapping_P[type]->inverse(context, vb,ci->map_param[ci->mode_param[mode]-> + mapping])); +} + +/* used to track pcm position without actually performing decode. + Useful for sequential 'fast forward' */ +int FMOD_vorbis_synthesis_trackonly(void *context, vorbis_block *vb,ogg_packet *op){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=vd->backend_state; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + oggpack_buffer *opb=&vb->opb; + int mode,ret; + + /* first things first. Make sure decode is ready */ + ret = _FMOD_vorbis_block_ripcord(context, vb); + if (ret) + { + return ret; + } + + FMOD_oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(FMOD_oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=FMOD_oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + vb->lW=FMOD_oggpack_read(opb,1); + vb->nW=FMOD_oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; + vb->eofflag=op->e_o_s; + + /* no pcm */ + vb->pcmend=0; + vb->pcm=NULL; + + return(0); +} + +ogg_int32_t FMOD_vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ + codec_setup_info *ci=vi->codec_setup; + oggpack_buffer opb; + int mode; + + FMOD_oggpack_readinit(&opb,op->packet,op->bytes); + + /* Check the packet type */ + if(FMOD_oggpack_read(&opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + { + int modebits=0; + int v=ci->modes; + while(v>1){ + modebits++; + v>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=FMOD_oggpack_read(&opb,modebits); + } + if(mode==-1)return(OV_EBADPACKET); + return(ci->blocksizes[ci->mode_param[mode]->blockflag]); +} + +int FMOD_vorbis_synthesis_halfrate(vorbis_info *vi,int flag){ + /* set / clear half-sample-rate mode */ + codec_setup_info *ci=vi->codec_setup; + + /* right now, our MDCT can't handle < 64 sample windows. */ + if(ci->blocksizes[0]<=64 && flag)return -1; + ci->halfrate_flag=(flag?1:0); + return 0; +} + +int FMOD_vorbis_synthesis_halfrate_p(vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + return ci->halfrate_flag; +} diff --git a/lib/ogg_vorbis/vorbis/lib/vorbisfile.c b/lib/ogg_vorbis/vorbis/lib/vorbisfile.c new file mode 100755 index 0000000..90116b8 --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/vorbisfile.c @@ -0,0 +1,2409 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.c 16246 2009-07-10 03:19:29Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include + +#ifndef SN_TARGET_PS3 + #include +#endif + +#include "vorbis/codec.h" + +/* we don't need or want the static callback symbols here */ +#define OV_EXCLUDE_STATIC_CALLBACKS +#include "vorbis/vorbisfile.h" + +#include "os.h" +#include "misc.h" + +/* A 'chained bitstream' is a Vorbis bitstream that contains more than + one logical bitstream arranged end to end (the only form of Ogg + multiplexing allowed in a Vorbis bitstream; grouping [parallel + multiplexing] is not allowed in Vorbis) */ + +/* A Vorbis file can be played beginning to end (streamed) without + worrying ahead of time about chaining (see decoder_example.c). If + we have the whole file, however, and want random access + (seeking/scrubbing) or desire to know the total length/time of a + file, we need to account for the possibility of chaining. */ + +/* We can handle things a number of ways; we can determine the entire + bitstream structure right off the bat, or find pieces on demand. + This example determines and caches structure for the entire + bitstream, but builds a virtual decoder on the fly when moving + between links in the chain. */ + +/* There are also different ways to implement seeking. Enough + information exists in an Ogg bitstream to seek to + sample-granularity positions in the output. Or, one can seek by + picking some portion of the stream roughly in the desired area if + we only want coarse navigation through the stream. */ + +/************************************************************************* + * Many, many internal helpers. The intention is not to be confusing; + * rampant duplication and monolithic function implementation would be + * harder to understand anyway. The high level functions are last. Begin + * grokking near the end of the file */ + +/* read a little more data from the file/pipe into the ogg_sync framer +*/ +//#define CHUNKSIZE 65536 +#define CHUNKSIZE 8500 + + +static ogg_int32_t _get_data(void *context, OggVorbis_File *vf){ +#ifndef SN_TARGET_PS3 + errno=0; +#endif + if(!(vf->callbacks.read_func))return(-1); + if(vf->datasource) + { + ogg_int32_t bytes; + char *buffer=FMOD_ogg_sync_buffer(context, &vf->oy,CHUNKSIZE); + if (!buffer) + { + return OV_EMEMORY; + } + bytes=(ogg_int32_t)(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); + if(bytes>0)FMOD_ogg_sync_wrote(&vf->oy,bytes); +#ifndef SN_TARGET_PS3 + if(bytes==0 && errno)return(-1); +#endif + return(bytes); + }else + return(0); +} + +/* save a tiny smidge of verbosity to make the code more readable */ +static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ + if(vf->datasource){ + if(!(vf->callbacks.seek_func)|| + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) + return OV_EREAD; + vf->offset=offset; + FMOD_ogg_sync_reset(&vf->oy); + }else{ + /* shouldn't happen unless someone writes a broken callback */ + return OV_EFAULT; + } + return 0; +} + +/* The read/seek functions track absolute position within the stream */ + +/* from the head of the stream, get the next page. boundary specifies + if the function is allowed to fetch more data from the stream (and + how much) or only use internally buffered data. + + boundary: -1) unbounded search + 0) read no additional data; use cached only + n) search for a new page beginning for n bytes + + return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) + n) found a page at absolute offset n */ + +static ogg_int64_t _get_next_page(void *context, OggVorbis_File *vf,ogg_page *og, + ogg_int64_t boundary){ + if(boundary>0)boundary+=vf->offset; + while(1){ + ogg_int32_t more; + + if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); + more=FMOD_ogg_sync_pageseek(&vf->oy,og); + + if(more<0){ + /* skipped n bytes */ + vf->offset-=more; + }else{ + if(more==0){ + /* send more paramedics */ + if(!boundary)return(OV_FALSE); + { + ogg_int32_t ret=_get_data(context, vf); + if(ret==0)return(OV_EOF); + if(ret==OV_EMEMORY)return(OV_EMEMORY); + if(ret<0)return(OV_EREAD); + } + }else{ + /* got a page. Return the offset at the page beginning, + advance the internal offset past the page end */ + ogg_int64_t ret=vf->offset; + vf->offset+=more; + return(ret); + + } + } + } +} + +/* find the latest page beginning before the current stream cursor + position. Much dirtier than the above as Ogg doesn't have any + backward search linkage. no 'readp' as it will certainly have to + read. */ +/* returns offset or OV_EREAD, OV_FAULT */ +static ogg_int64_t _get_prev_page(void *context, OggVorbis_File *vf,ogg_page *og){ + ogg_int64_t begin=vf->offset; + ogg_int64_t end=begin; + ogg_int64_t ret; + ogg_int64_t offset=-1; + + if (end == 0) + { + return OV_EREAD; + } + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + + ret=_seek_helper(vf,begin); + if(ret)return(ret); + + while(vf->offsetoffset); + if(ret==OV_EREAD)return(OV_EREAD); + if(ret==OV_EMEMORY)return(OV_EMEMORY); + if(ret<0){ + break; + }else{ + offset=ret; + } + } + } + + /* In a fully compliant, non-multiplexed stream, we'll still be + holding the last page. In multiplexed (or noncompliant streams), + we will probably have to re-read the last page we saw */ + if(og->header_len==0){ + ret=_seek_helper(vf,offset); + if(ret)return(ret); + + ret=_get_next_page(context, vf,og,CHUNKSIZE); + if(ret<0) + /* this shouldn't be possible */ + return(OV_EFAULT); + } + + return(offset); +} + +static int _add_serialno(void *context, ogg_page *og,ogg_int32_t **serialno_list, int *n){ + ogg_int32_t s = FMOD_ogg_page_serialno(og); + (*n)++; + + if(*serialno_list){ + *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n)); + }else{ + *serialno_list = _ogg_malloc(sizeof(**serialno_list)); + } + if (!*serialno_list) + { + return OV_EMEMORY; + } + + (*serialno_list)[(*n)-1] = s; + + return 0; +} + +/* returns nonzero if found */ +static int _lookup_serialno(ogg_int32_t s, ogg_int32_t *serialno_list, int n){ + if(serialno_list){ + while(n--){ + if(*serialno_list == s) return 1; + serialno_list++; + } + } + return 0; +} + +static int _lookup_page_serialno(ogg_page *og, ogg_int32_t *serialno_list, int n){ + ogg_int32_t s = FMOD_ogg_page_serialno(og); + return _lookup_serialno(s,serialno_list,n); +} + +/* performs the same search as _get_prev_page, but prefers pages of + the specified serial number. If a page of the specified serialno is + spotted during the seek-back-and-read-forward, it will return the + info of last page of the matching serial number instead of the very + last page. If no page of the specified serialno is seen, it will + return the info of last page and alter *serialno. */ +static ogg_int64_t _get_prev_page_serial(void *context, OggVorbis_File *vf, + ogg_int32_t *serial_list, int serial_n, + int *serialno, ogg_int64_t *granpos){ + ogg_page og; + ogg_int64_t begin=vf->offset; + ogg_int64_t end=begin; + ogg_int64_t ret; + + ogg_int64_t prefoffset=-1; + ogg_int64_t offset=-1; + ogg_int64_t ret_serialno=-1; + ogg_int64_t ret_gran=-1; + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + + ret=_seek_helper(vf,begin); + if(ret)return(ret); + + while(vf->offsetoffset); + if(ret==OV_EREAD)return(OV_EREAD); + if(ret==OV_EMEMORY)return(OV_EMEMORY); + if(ret<0){ + break; + }else{ + ret_serialno=FMOD_ogg_page_serialno(&og); + ret_gran=FMOD_ogg_page_granulepos(&og); + offset=ret; + + if(ret_serialno == *serialno){ + prefoffset=ret; + *granpos=ret_gran; + } + + if(!_lookup_serialno((ogg_int32_t)ret_serialno,serial_list,serial_n)){ + /* we fell off the end of the link, which means we seeked + back too far and shouldn't have been looking in that link + to begin with. If we found the preferred serial number, + forget that we saw it. */ + prefoffset=-1; + } + } + } + } + + /* we're not interested in the page... just the serialno and granpos. */ + if(prefoffset>=0)return(prefoffset); + + *serialno = (int)ret_serialno; + *granpos = ret_gran; + return(offset); + +} + +/* uses the local ogg_stream storage in vf; this is important for + non-streaming input sources */ +static int _fetch_headers(void *context, OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, + ogg_int32_t **serialno_list, int *serialno_n, + ogg_page *og_ptr){ + ogg_page og; + ogg_packet op; + int i,ret; + int allbos=0; + + if(!og_ptr){ + ogg_int64_t llret=_get_next_page(context, vf,&og,CHUNKSIZE); + if(llret==OV_EREAD)return(OV_EREAD); + if(llret==OV_EMEMORY)return(OV_EMEMORY); + if(llret<0)return(OV_ENOTVORBIS); + og_ptr=&og; + } + + if (FMOD_vorbis_info_init(context, vi)) + { + return(OV_EMEMORY); + } + FMOD_vorbis_comment_init(vc); + vf->ready_state=OPENED; + + /* extract the serialnos of all BOS pages + the first set of vorbis + headers we see in the link */ + + while(FMOD_ogg_page_bos(og_ptr)){ + if(serialno_list){ + if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){ + /* a dupe serialnumber in an initial header packet set == invalid stream */ + if(*serialno_list)_ogg_free(*serialno_list); + *serialno_list=0; + *serialno_n=0; + ret=OV_EBADHEADER; + goto bail_header; + } + + if (_add_serialno(context, og_ptr,serialno_list,serialno_n) < 0) + { + return OV_EMEMORY; + } + } + + if(vf->ready_stateos,FMOD_ogg_page_serialno(og_ptr)); + FMOD_ogg_stream_pagein(context, &vf->os,og_ptr); + + if(FMOD_ogg_stream_packetout(&vf->os,&op) > 0 && + FMOD_vorbis_synthesis_idheader(&op)){ + /* vorbis header; continue setup */ + vf->ready_state=STREAMSET; + if((ret=FMOD_vorbis_synthesis_headerin(context, vi,vc,&op))){ + ret=OV_EBADHEADER; + goto bail_header; + } + } + } + + /* get next page */ + { + ogg_int64_t llret=_get_next_page(context, vf,og_ptr,CHUNKSIZE); + if(llret==OV_EREAD){ + ret=OV_EREAD; + goto bail_header; + } + if(llret==OV_EMEMORY) + { + ret = OV_EMEMORY; + goto bail_header; + } + if(llret<0){ + ret=OV_ENOTVORBIS; + goto bail_header; + } + + /* if this page also belongs to our vorbis stream, submit it and break */ + if(vf->ready_state==STREAMSET && + vf->os.serialno == FMOD_ogg_page_serialno(og_ptr)){ + FMOD_ogg_stream_pagein(context, &vf->os,og_ptr); + break; + } + } + } + + if(vf->ready_state!=STREAMSET){ + ret = OV_ENOTVORBIS; + goto bail_header; + } + + while(1){ + + i=0; + while(i<2){ /* get a page loop */ + + while(i<2){ /* get a packet loop */ + + int result=FMOD_ogg_stream_packetout(&vf->os,&op); + if(result==0)break; + if(result==-1){ + ret=OV_EBADHEADER; + goto bail_header; + } + + if((ret=FMOD_vorbis_synthesis_headerin(context, vi,vc,&op))) + goto bail_header; + + i++; + } + + while(i<2){ + if(_get_next_page(context, vf,og_ptr,CHUNKSIZE)<0){ + ret=OV_EBADHEADER; + goto bail_header; + } + + /* if this page belongs to the correct stream, go parse it */ + if(vf->os.serialno == FMOD_ogg_page_serialno(og_ptr)){ + FMOD_ogg_stream_pagein(context, &vf->os,og_ptr); + break; + } + + /* if we never see the final vorbis headers before the link + ends, abort */ + if(FMOD_ogg_page_bos(og_ptr)){ + if(allbos){ + ret = OV_EBADHEADER; + goto bail_header; + }else + allbos=1; + } + + /* otherwise, keep looking */ + } + } + + return 0; + } + + bail_header: + FMOD_vorbis_info_clear(context, vi); + FMOD_vorbis_comment_clear(context, vc); + vf->ready_state=OPENED; + + return ret; +} + +/* Starting from current cursor position, get initial PCM offset of + next page. Consumes the page in the process without decoding + audio, however this is only called during stream parsing upon + seekable open. */ +static ogg_int64_t _initial_pcmoffset(void *context, OggVorbis_File *vf, vorbis_info *vi){ + ogg_page og; + ogg_int64_t accumulated=0; + ogg_int32_t lastblock=-1; + int result; + int serialno = vf->os.serialno; + + while(1){ + ogg_packet op; + if(_get_next_page(context, vf,&og,-1)<0) + break; /* should not be possible unless the file is truncated/mangled */ + + if(FMOD_ogg_page_bos(&og)) break; + if(FMOD_ogg_page_serialno(&og)!=serialno) continue; + + /* count blocksizes of all frames in the page */ + FMOD_ogg_stream_pagein(context, &vf->os,&og); + while((result=FMOD_ogg_stream_packetout(&vf->os,&op))){ + if(result>0){ /* ignore holes */ + ogg_int32_t thisblock=FMOD_vorbis_packet_blocksize(vi,&op); + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } + } + + if(FMOD_ogg_page_granulepos(&og)!=-1){ + /* pcm offset of last packet on the first audio page */ + accumulated= FMOD_ogg_page_granulepos(&og)-accumulated; + break; + } + } + + /* less than zero? This is a stream with samples trimmed off + the beginning, a normal occurrence; set the offset to zero */ + if(accumulated<0)accumulated=0; + + return accumulated; +} + +/* finds each bitstream link one at a time using a bisection search + (has to begin by knowing the offset of the lb's initial page). + Recurses for each link so it can alloc the link storage after + finding them all, then unroll and fill the cache at the same time */ +static int _bisect_forward_serialno(void *context, + OggVorbis_File *vf, + ogg_int64_t begin, + ogg_int64_t searched, + ogg_int64_t end, + ogg_int64_t endgran, + int endserial, + ogg_int32_t *currentno_list, + int currentnos, + ogg_int32_t m){ + ogg_int64_t pcmoffset; + ogg_int64_t dataoffset=searched; + ogg_int64_t endsearched=end; + ogg_int64_t next=end; + ogg_int64_t searchgran=-1; + ogg_page og; + ogg_int64_t ret,last; + int serialno = vf->os.serialno; + + /* invariants: + we have the headers and serialnos for the link beginning at 'begin' + we have the offset and granpos of the last page in the file (potentially + not a page we care about) + */ + + /* Is the last page in our list of current serialnumbers? */ + if(_lookup_serialno(endserial,currentno_list,currentnos)){ + + /* last page is in the starting serialno list, so we've bisected + down to (or just started with) a single link. Now we need to + find the last vorbis page belonging to the first vorbis stream + for this link. */ + + while(endserial != serialno){ + endserial = serialno; + vf->offset=_get_prev_page_serial(context, vf,currentno_list,currentnos,&endserial,&endgran); + } + + vf->links=m+1; + if(vf->offsets){ _ogg_free(vf->offsets); vf->offsets = 0; }; + if(vf->serialnos){ _ogg_free(vf->serialnos); vf->serialnos = 0; } + if(vf->dataoffsets) { _ogg_free(vf->dataoffsets); vf->dataoffsets = 0; } + + vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); + if (!vf->offsets) return (OV_EMEMORY); + vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); + if (!vf->vi) return (OV_EMEMORY); + vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); + if (!vf->vc) return (OV_EMEMORY); + vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); + if (!vf->serialnos) return (OV_EMEMORY); + vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); + if (!vf->dataoffsets) return (OV_EMEMORY); + vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); + if (!vf->pcmlengths) return (OV_EMEMORY); + + vf->offsets[m+1]=end; + vf->offsets[m]=begin; + vf->pcmlengths[m*2+1]=endgran; + + }else{ + + ogg_int32_t *next_serialno_list=NULL; + int next_serialnos=0; + vorbis_info vi; + vorbis_comment vc; + + /* the below guards against garbage seperating the last and + first pages of two links. */ + while(searched=0)next=last; + }else{ + searched=last+og.header_len+og.body_len; + } + } + + /* Bisection point found */ + + /* for the time being, fetch end PCM offset the simple way */ + { + int testserial = serialno+1; + vf->offset = next; + while(testserial != serialno){ + testserial = serialno; + vf->offset=_get_prev_page_serial(context, vf,currentno_list,currentnos,&testserial,&searchgran); + } + } + + if(vf->offset!=next){ + ret=_seek_helper(vf,next); + if(ret) return (int)(ret); + } + + ret=_fetch_headers(context, vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL); + if(ret) return (int)(ret); + serialno = vf->os.serialno; + dataoffset = vf->offset; + + /* this will consume a page, however the next bistection always + starts with a raw seek */ + pcmoffset = _initial_pcmoffset(context, vf,&vi); + + ret=_bisect_forward_serialno(context, vf,next,vf->offset,end,endgran,endserial, + next_serialno_list,next_serialnos,m+1); + if(ret) return (int)(ret); + + if(next_serialno_list)_ogg_free(next_serialno_list); + + vf->offsets[m+1]=next; + vf->serialnos[m+1]=serialno; + vf->dataoffsets[m+1]=dataoffset; + + vf->vi[m+1]=vi; + vf->vc[m+1]=vc; + + vf->pcmlengths[m*2+1]=searchgran; + vf->pcmlengths[m*2+2]=pcmoffset; + vf->pcmlengths[m*2+3]-=pcmoffset; + + } + return(0); +} + +static int _make_decode_ready(void *context, OggVorbis_File *vf){ + if(vf->ready_state>STREAMSET)return 0; + if(vf->ready_stateseekable){ + int ret = FMOD_vorbis_synthesis_init(context, &vf->vd,vf->vi+vf->current_link); + if(ret == OV_EMEMORY) + { + return OV_EMEMORY; + } + if(ret) + return OV_EBADLINK; + }else{ + int ret = FMOD_vorbis_synthesis_init(context, &vf->vd,vf->vi); + if(ret == OV_EMEMORY) + { + return OV_EMEMORY; + } + if (ret) + return OV_EBADLINK; + } + if (FMOD_vorbis_block_init(context, &vf->vd,&vf->vb)) + { + return(OV_EMEMORY); + } + vf->ready_state=INITSET; + vf->bittrack=0.f; + vf->samptrack=0.f; + return 0; +} + +static int _open_seekable2(void *context, OggVorbis_File *vf){ + ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1; + int endserial=vf->os.serialno; + int serialno=vf->os.serialno; + + /* we're partially open and have a first link header state in + storage in vf */ + + /* fetch initial PCM offset */ + ogg_int64_t pcmoffset = _initial_pcmoffset(context, vf,vf->vi); + + /* we can seek, so set out learning all about this file */ + if(vf->callbacks.seek_func && vf->callbacks.tell_func){ + (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); + vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); + }else{ + vf->offset=vf->end=-1; + } + + /* If seek_func is implemented, tell_func must also be implemented */ + if(vf->end==-1) return(OV_EINVAL); + + /* Get the offset of the last page of the physical bitstream, or, if + we're lucky the last vorbis page of this link as most OggVorbis + files will contain a single logical bitstream */ + end=_get_prev_page_serial(context, vf,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); + if(end<0)return (int)(end); + + /* now determine bitstream structure recursively */ + if(_bisect_forward_serialno(context, vf,0,dataoffset,vf->offset,endgran,endserial, + vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD); + + vf->offsets[0]=0; + vf->serialnos[0]=serialno; + vf->dataoffsets[0]=dataoffset; + vf->pcmlengths[0]=pcmoffset; + vf->pcmlengths[1]-=pcmoffset; + + return(ov_raw_seek(context, vf,dataoffset)); +} + +/* clear out the current logical bitstream decoder */ +static void _decode_clear(void *context, OggVorbis_File *vf){ + FMOD_vorbis_dsp_clear(context, &vf->vd); + FMOD_vorbis_block_clear(context, &vf->vb); + vf->ready_state=OPENED; +} + +/* fetch and process a packet. Handles the case where we're at a + bitstream boundary and dumps the decoding machine. If the decoding + machine is unloaded, it loads it. It also keeps pcm_offset up to + date (seek and read both use this. seek uses a special hack with + readp). + + return: <0) error, OV_HOLE (lost packet) or OV_EOF + 0) need more data (only if readp==0) + 1) got a packet +*/ + +static int _fetch_and_process_packet(void *context, OggVorbis_File *vf, + ogg_packet *op_in, + int readp, + int spanp){ + ogg_page og; + + /* handle one packet. Try to fetch it from current stream state */ + /* extract packets from page */ + while(1){ + + if(vf->ready_state==STREAMSET){ + int ret=_make_decode_ready(context, vf); + if(ret<0)return ret; + } + + /* process a packet if we can. */ + + if(vf->ready_state==INITSET){ + while(1) { + ogg_packet op; + ogg_packet *op_ptr=(op_in?op_in:&op); + int result=FMOD_ogg_stream_packetout(&vf->os,op_ptr); + ogg_int64_t granulepos; + + op_in=NULL; + if(result==-1)return(OV_HOLE); /* hole in the data. */ + if(result==OV_EMEMORY) return(OV_EMEMORY); + if(result>0){ + /* got a packet. process it */ + granulepos=op_ptr->granulepos; + result = FMOD_vorbis_synthesis(context, &vf->vb,op_ptr); + if (result == OV_EMEMORY) + { + return OV_EMEMORY; + } + if(!result){ /* lazy check for lazy + header handling. The + header packets aren't + audio, so if/when we + submit them, + vorbis_synthesis will + reject them */ + + /* suck in the synthesis data and track bitrate */ + { + int oldsamples=FMOD_vorbis_synthesis_pcmout(&vf->vd,NULL); + /* for proper use of libvorbis within libvorbisfile, + oldsamples will always be zero. */ + if(oldsamples)return(OV_EFAULT); + + FMOD_vorbis_synthesis_blockin(&vf->vd,&vf->vb); + vf->samptrack+=FMOD_vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; + vf->bittrack+=op_ptr->bytes*8; + } + + /* update the pcm offset. */ + if(granulepos!=-1 && !op_ptr->e_o_s){ + int link=(vf->seekable?vf->current_link:0); + int i,samples; + + /* this packet has a pcm_offset on it (the last packet + completed on a page carries the offset) After processing + (above), we know the pcm position of the *last* sample + ready to be returned. Find the offset of the *first* + + As an aside, this trick is inaccurate if we begin + reading anew right at the last page; the end-of-stream + granulepos declares the last frame in the stream, and the + last packet of the last page may be a partial frame. + So, we need a previous granulepos from an in-sequence page + to have a reference point. Thus the !op_ptr->e_o_s clause + above */ + + if(vf->seekable && link>0) + granulepos-=vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; /* actually, this + shouldn't be possible + here unless the stream + is very broken */ + + samples=FMOD_vorbis_synthesis_pcmout(&vf->vd,NULL); + + granulepos-=samples; + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos; + } + return(1); + } + } + else + break; + } + } + + if(vf->ready_state>=OPENED){ + ogg_int64_t ret; + + while(1){ + /* the loop is not strictly necessary, but there's no sense in + doing the extra checks of the larger loop for the common + case in a multiplexed bistream where the page is simply + part of a different logical bitstream; keep reading until + we get one with the correct serialno */ + + if(!readp)return(0); + if((ret=_get_next_page(context, vf,&og,-1))<0){ + return(OV_EOF); /* eof. leave unitialized */ + } + + /* bitrate tracking; add the header's bytes here, the body bytes + are done by packet above */ + vf->bittrack+=og.header_len*8; + + if(vf->ready_state==INITSET){ + if(vf->current_serialno!=FMOD_ogg_page_serialno(&og)){ + + /* two possibilities: + 1) our decoding just traversed a bitstream boundary + 2) another stream is multiplexed into this logical section */ + + if(FMOD_ogg_page_bos(&og)){ + /* boundary case */ + if(!spanp) + return(OV_EOF); + + _decode_clear(context, vf); + + if(!vf->seekable){ + FMOD_vorbis_info_clear(context, vf->vi); + FMOD_vorbis_comment_clear(context, vf->vc); + } + break; + + }else + continue; /* possibility #2 */ + } + } + + break; + } + } + + /* Do we need to load a new machine before submitting the page? */ + /* This is different in the seekable and non-seekable cases. + + In the seekable case, we already have all the header + information loaded and cached; we just initialize the machine + with it and continue on our merry way. + + In the non-seekable (streaming) case, we'll only be at a + boundary if we just left the previous logical bitstream and + we're now nominally at the header of the next bitstream + */ + + if(vf->ready_state!=INITSET){ + int link; + + if(vf->ready_stateseekable){ + ogg_int32_t serialno = FMOD_ogg_page_serialno(&og); + + /* match the serialno to bitstream section. We use this rather than + offset positions to avoid problems near logical bitstream + boundaries */ + + for(link=0;linklinks;link++) + if(vf->serialnos[link]==serialno)break; + + if(link==vf->links) continue; /* not the desired Vorbis + bitstream section; keep + trying */ + + vf->current_serialno=serialno; + vf->current_link=link; + + FMOD_ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + vf->ready_state=STREAMSET; + + }else{ + /* we're streaming */ + /* fetch the three header packets, build the info struct */ + + int ret=_fetch_headers(context, vf,vf->vi,vf->vc,NULL,NULL,&og); + if(ret)return(ret); + vf->current_serialno=vf->os.serialno; + vf->current_link++; + link=0; + } + } + } + + /* the buffered page is the data we want, and we're ready for it; + add it to the stream state */ + FMOD_ogg_stream_pagein(context, &vf->os,&og); + + } +} + +/* if, eg, 64 bit stdio is configured by default, this will build with + fseek64 */ +static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + return fseek(f,(int)off,whence); +} + +static int _ov_open1(void *context, void *f,OggVorbis_File *vf,char *initial, + ogg_int32_t ibytes, ov_callbacks callbacks){ + int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1); + ogg_int32_t *serialno_list=NULL; + int serialno_list_size=0; + int ret; + + FMOD_memset(vf,0,sizeof(*vf)); + vf->datasource=f; + vf->callbacks = callbacks; + + /* init the framing state */ + FMOD_ogg_sync_init(&vf->oy); + + /* perhaps some data was previously read into a buffer for testing + against other stream types. Allow initialization from this + previously read data (especially as we may be reading from a + non-seekable stream) */ + if(initial){ + char *buffer=(char *)FMOD_ogg_sync_buffer(context, &vf->oy,ibytes); + if (!buffer) + { + return OV_EMEMORY; + } + FMOD_memcpy(buffer,initial,ibytes); + FMOD_ogg_sync_wrote(&vf->oy,ibytes); + } + + /* can we seek? Stevens suggests the seek test was portable */ + if(offsettest!=-1)vf->seekable=1; + + /* No seeking yet; Set up a 'single' (current) logical bitstream + entry for partial open */ + vf->links=1; + vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); + if (!vf->vi) + { + return OV_EMEMORY; + } + vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); + if (!vf->vc) + { + return OV_EMEMORY; + } + FMOD_ogg_stream_init(context, &vf->os,-1); /* fill in the serialno later */ + + /* Fetch all BOS pages, store the vorbis header and all seen serial + numbers, load subsequent vorbis setup headers */ + if((ret=_fetch_headers(context, vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){ + vf->datasource=NULL; + ov_clear(context, vf); + }else{ + /* serial number list for first link needs to be held somewhere + for second stage of seekable stream open; this saves having to + seek/reread first link's serialnumber data then. */ + vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos)); + if (!vf->serialnos) + { + return OV_EMEMORY; + } + vf->serialnos[0]=vf->current_serialno; + vf->serialnos[1]=serialno_list_size; + FMOD_memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos)); + + vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets)); + if (!vf->offsets) + { + return OV_EMEMORY; + } + vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets)); + if (!vf->dataoffsets) + { + return OV_EMEMORY; + } + vf->offsets[0]=0; + vf->dataoffsets[0]=vf->offset; + vf->current_serialno=vf->os.serialno; + + vf->ready_state=PARTOPEN; + } + if(serialno_list)_ogg_free(serialno_list); + return(ret); +} + +static int _ov_open2(void *context, OggVorbis_File *vf){ + if(vf->ready_state != PARTOPEN) return OV_EINVAL; + vf->ready_state=OPENED; + if(vf->seekable){ + int ret=_open_seekable2(context, vf); + if(ret){ + vf->datasource=NULL; + ov_clear(context, vf); + } + return(ret); + }else + vf->ready_state=STREAMSET; + + return 0; +} + + +/* clear out the OggVorbis_File struct */ +int ov_clear(void *context, OggVorbis_File *vf){ + if(vf){ + FMOD_vorbis_block_clear(context, &vf->vb); + FMOD_vorbis_dsp_clear(context, &vf->vd); + FMOD_ogg_stream_clear(context, &vf->os); + + if(vf->vi && vf->links){ + int i; + for(i=0;ilinks;i++){ + FMOD_vorbis_info_clear(context, vf->vi+i); + FMOD_vorbis_comment_clear(context, vf->vc+i); + } + _ogg_free(vf->vi); + _ogg_free(vf->vc); + } + if(vf->dataoffsets)_ogg_free(vf->dataoffsets); + if(vf->pcmlengths)_ogg_free(vf->pcmlengths); + if(vf->serialnos)_ogg_free(vf->serialnos); + if(vf->offsets)_ogg_free(vf->offsets); + FMOD_ogg_sync_clear(context, &vf->oy); + if(vf->datasource && vf->callbacks.close_func) + (vf->callbacks.close_func)(vf->datasource); + FMOD_memset(vf,0,sizeof(*vf)); + } +#ifdef DEBUG_LEAKS + _VDBG_dump(); +#endif + return(0); +} + +/* inspects the OggVorbis file and finds/documents all the logical + bitstreams contained in it. Tries to be tolerant of logical + bitstream sections that are truncated/woogie. + + return: -1) error + 0) OK +*/ + +int ov_open_callbacks(void *context, void *f,OggVorbis_File *vf,char *initial,ogg_int32_t ibytes, + ov_callbacks callbacks){ + int ret=_ov_open1(context, f,vf,initial,ibytes,callbacks); + if(ret)return ret; + return _ov_open2(context, vf); +} + +int ov_open(void *context, FILE *f,OggVorbis_File *vf,char *initial,ogg_int32_t ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (ogg_int32_t (*)(void *)) ftell + }; + + return ov_open_callbacks(context, (void *)f, vf, initial, ibytes, callbacks); +} + +int ov_fopen(void *context, char *path,OggVorbis_File *vf){ + int ret; + FILE *f = fopen(path,"rb"); + if(!f) return -1; + + ret = ov_open(context, f,vf,NULL,0); + if(ret) fclose(f); + return ret; +} + + +/* cheap hack for game usage where downsampling is desirable; there's + no need for SRC as we can just do it cheaply in libvorbis. */ + +int ov_halfrate(void *context, OggVorbis_File *vf,int flag){ + int i; + if(vf->vi==NULL)return OV_EINVAL; + if(!vf->seekable)return OV_EINVAL; + if(vf->ready_state>=STREAMSET) + _decode_clear(context, vf); /* clear out stream state; later on libvorbis + will be able to swap this on the fly, but + for now dumping the decode machine is needed + to reinit the MDCT lookups. 1.1 libvorbis + is planned to be able to switch on the fly */ + + for(i=0;ilinks;i++){ + if(FMOD_vorbis_synthesis_halfrate(vf->vi+i,flag)){ + ov_halfrate(context, vf,0); + return OV_EINVAL; + } + } + return 0; +} + +int ov_halfrate_p(OggVorbis_File *vf){ + if(vf->vi==NULL)return OV_EINVAL; + return FMOD_vorbis_synthesis_halfrate_p(vf->vi); +} + +/* Only partially open the vorbis file; test for Vorbisness, and load + the headers for the first chain. Do not seek (although test for + seekability). Use ov_test_open to finish opening the file, else + ov_clear to close/free it. Same return codes as open. */ + +int ov_test_callbacks(void *context, void *f,OggVorbis_File *vf,char *initial,ogg_int32_t ibytes, + ov_callbacks callbacks) +{ + return _ov_open1(context, f,vf,initial,ibytes,callbacks); +} + +int ov_test(void *context, FILE *f,OggVorbis_File *vf,char *initial,ogg_int32_t ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (ogg_int32_t (*)(void *)) ftell + }; + + return ov_test_callbacks(context, (void *)f, vf, initial, ibytes, callbacks); +} + +int ov_test_open(void *context, OggVorbis_File *vf){ + if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); + return _ov_open2(context, vf); +} + +/* How many logical bitstreams in this physical bitstream? */ +ogg_int32_t ov_streams(OggVorbis_File *vf){ + return vf->links; +} + +/* Is the FILE * associated with vf seekable? */ +ogg_int32_t ov_seekable(OggVorbis_File *vf){ + return vf->seekable; +} + +/* returns the bitrate for a given logical bitstream or the entire + physical bitstream. If the file is open for random access, it will + find the *actual* average bitrate. If the file is streaming, it + returns the nominal bitrate (if set) else the average of the + upper/lower bounds (if set) else -1 (unset). + + If you want the actual bitrate field settings, get them from the + vorbis_info structs */ + +ogg_int32_t ov_bitrate(OggVorbis_File *vf,int i){ + if(vf->ready_state=vf->links)return(OV_EINVAL); + if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); + if(i<0){ + ogg_int64_t bits=0; + int i; + float br; + for(i=0;ilinks;i++) + bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; + /* This once read: return(FMOD_ogg_rint(bits/ov_time_total(vf,-1))); + * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, + * so this is slightly transformed to make it work. + */ + br = bits/ov_time_total(vf,-1); + return((int)FMOD_ogg_rint(br)); + }else{ + if(vf->seekable){ + /* return the actual bitrate */ + return((int)FMOD_ogg_rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i))); + }else{ + /* return nominal if set */ + if(vf->vi[i].bitrate_nominal>0){ + return vf->vi[i].bitrate_nominal; + }else{ + if(vf->vi[i].bitrate_upper>0){ + if(vf->vi[i].bitrate_lower>0){ + return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; + }else{ + return vf->vi[i].bitrate_upper; + } + } + return(OV_FALSE); + } + } + } +} + +/* returns the actual bitrate since last call. returns -1 if no + additional data to offer since last call (or at beginning of stream), + EINVAL if stream is only partially open +*/ +ogg_int32_t ov_bitrate_instant(OggVorbis_File *vf){ + int link=(vf->seekable?vf->current_link:0); + ogg_int32_t ret; + if(vf->ready_statesamptrack==0)return(OV_FALSE); + ret=(ogg_int32_t)(vf->bittrack/vf->samptrack*vf->vi[link].rate+.5f); + vf->bittrack=0.f; + vf->samptrack=0.f; + return(ret); +} + +/* Guess */ +ogg_int32_t ov_serialnumber(OggVorbis_File *vf,int i){ + if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); + if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); + if(i<0){ + return(vf->current_serialno); + }else{ + return(vf->serialnos[i]); + } +} + +/* returns: total raw (compressed) length of content if i==-1 + raw (compressed) length of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the length) + or if stream is only partially open +*/ +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_raw_total(vf,i); + return(acc); + }else{ + return(vf->offsets[i+1]-vf->offsets[i]); + } +} + +/* returns: total PCM length (samples) of content if i==-1 PCM length + (samples) of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_pcm_total(vf,i); + return(acc); + }else{ + return(vf->pcmlengths[i*2+1]); + } +} + +/* returns: total seconds of content if i==-1 + seconds in that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +float ov_time_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + float acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_time_total(vf,i); + return(acc); + }else{ + return((float)(vf->pcmlengths[i*2+1])/vf->vi[i].rate); + } +} + +/* seek to an offset relative to the *compressed* data. This also + scans packets to update the PCM cursor. It will cross a logical + bitstream boundary, but only if it can't get any packets out of the + tail of the bitstream we seek to (so no surprises). + + returns zero on success, nonzero on failure */ + +int ov_raw_seek(void *context, OggVorbis_File *vf,ogg_int64_t pos){ + ogg_stream_state work_os; + int ret; + + if(vf->ready_stateseekable) + return(OV_ENOSEEK); /* don't dump machine if we can't seek */ + + if(pos<0 || pos>vf->end)return(OV_EINVAL); + + /* don't yet clear out decoding machine (if it's initialized), in + the case we're in the same link. Restart the decode lapping, and + let _fetch_and_process_packet deal with a potential bitstream + boundary */ + vf->pcm_offset=-1; + FMOD_ogg_stream_reset_serialno(&vf->os, + vf->current_serialno); /* must set serialno */ + FMOD_vorbis_synthesis_restart(&vf->vd); + + ret=_seek_helper(vf,pos); + if(ret)goto seek_error; + + /* we need to make sure the pcm_offset is set, but we don't want to + advance the raw cursor past good packets just to get to the first + with a granulepos. That's not equivalent behavior to beginning + decoding as immediately after the seek position as possible. + + So, a hack. We use two stream states; a local scratch state and + the shared vf->os stream state. We use the local state to + scan, and the shared state as a buffer for later decode. + + Unfortuantely, on the last page we still advance to last packet + because the granulepos on the last page is not necessarily on a + packet boundary, and we need to make sure the granpos is + correct. + */ + + { + ogg_page og; + ogg_packet op; + int lastblock=0; + int accblock=0; + int thisblock=0; + int lastflag=0; + int firstflag=0; + ogg_int64_t pagepos=-1; + + if (FMOD_ogg_stream_init(context, &work_os,vf->current_serialno) < 0) /* get the memory ready */ + { + return OV_EMEMORY; + } + + FMOD_ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE + return from not necessarily + starting from the beginning */ + + while(1){ + if(vf->ready_state>=STREAMSET){ + /* snarf/scan a packet if we can */ + int result=FMOD_ogg_stream_packetout(&work_os,&op); + + if(result>0){ + + if(vf->vi[vf->current_link].codec_setup){ + thisblock=FMOD_vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + FMOD_ogg_stream_packetout(&vf->os,NULL); + thisblock=0; + }else{ + + /* We can't get a guaranteed correct pcm position out of the + last page in a stream because it might have a 'short' + granpos, which can only be detected in the presence of a + preceeding page. However, if the last page is also the first + page, the granpos rules of a first page take precedence. Not + only that, but for first==last, the EOS page must be treated + as if its a normal first page for the stream to open/play. */ + if(lastflag && !firstflag) + FMOD_ogg_stream_packetout(&vf->os,NULL); + else + if(lastblock)accblock+=(lastblock+thisblock)>>2; + } + + if(op.granulepos!=-1){ + int i,link=vf->current_link; + ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; + + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos-accblock; + if(vf->pcm_offset<0)vf->pcm_offset=0; + break; + } + lastblock=thisblock; + continue; + }else + FMOD_ogg_stream_packetout(&vf->os,NULL); + } + } + + if(!lastblock){ + pagepos=_get_next_page(context, vf,&og,-1); + if(pagepos<0){ + vf->pcm_offset=ov_pcm_total(vf,-1); + break; + } + }else{ + /* huh? Bogus stream with packets but no granulepos */ + vf->pcm_offset=-1; + break; + } + + /* has our decoding just traversed a bitstream boundary? */ + if(vf->ready_state>=STREAMSET){ + if(vf->current_serialno!=FMOD_ogg_page_serialno(&og)){ + + /* two possibilities: + 1) our decoding just traversed a bitstream boundary + 2) another stream is multiplexed into this logical section? */ + + if(FMOD_ogg_page_bos(&og)){ + /* we traversed */ + _decode_clear(context, vf); /* clear out stream state */ + FMOD_ogg_stream_clear(context, &work_os); + } /* else, do nothing; next loop will scoop another page */ + } + } + + if(vf->ready_statelinks;link++) + if(vf->serialnos[link]==serialno)break; + + if(link==vf->links) continue; /* not the desired Vorbis + bitstream section; keep + trying */ + vf->current_link=link; + vf->current_serialno=serialno; + FMOD_ogg_stream_reset_serialno(&vf->os,serialno); + FMOD_ogg_stream_reset_serialno(&work_os,serialno); + vf->ready_state=STREAMSET; + firstflag=(pagepos<=vf->dataoffsets[link]); + } + + FMOD_ogg_stream_pagein(context, &vf->os,&og); + FMOD_ogg_stream_pagein(context, &work_os,&og); + lastflag=FMOD_ogg_page_eos(&og); + + } + } + + FMOD_ogg_stream_clear(context, &work_os); + vf->bittrack=0.f; + vf->samptrack=0.f; + return(0); + + seek_error: + /* dump the machine so we're in a known state */ + vf->pcm_offset=-1; + FMOD_ogg_stream_clear(context, &work_os); + _decode_clear(context, vf); + return OV_EBADLINK; +} + +/* Page granularity seek (faster than sample granularity because we + don't do the last bit of decode to find a specific sample). + + Seek to the last [granule marked] page preceeding the specified pos + location, such that decoding past the returned point will quickly + arrive at the requested position. */ +int ov_pcm_seek_page(void *context, OggVorbis_File *vf,ogg_int64_t pos){ + int link=-1; + ogg_int64_t result=0; + ogg_int64_t total=ov_pcm_total(vf,-1); + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + + if(pos<0 || pos>total)return(OV_EINVAL); + + /* which bitstream section does this pcm offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + total-=vf->pcmlengths[link*2+1]; + if(pos>=total)break; + } + + /* search within the logical bitstream for the page with the highest + pcm_pos preceeding (or equal to) pos. There is a danger here; + missing pages or incorrect frame number information in the + bitstream could make our task impossible. Account for that (it + would be an error condition) */ + + /* new search algorithm by HB (Nicholas Vinen) */ + { + ogg_int64_t end=vf->offsets[link+1]; + ogg_int64_t begin=vf->offsets[link]; + ogg_int64_t begintime = vf->pcmlengths[link*2]; + ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; + ogg_int64_t target=pos-total+begintime; + ogg_int64_t best=begin; + + ogg_page og; + while(beginoffset); + if(result==OV_EREAD) goto seek_error; + if(result<0){ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(bisect==0) goto seek_error; + bisect-=CHUNKSIZE; + if(bisect<=begin)bisect=begin+1; + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + } + }else{ + ogg_int64_t granulepos; + + if(FMOD_ogg_page_serialno(&og)!=vf->serialnos[link]) + continue; + + granulepos=FMOD_ogg_page_granulepos(&og); + if(granulepos==-1)continue; + + if(granuleposoffset; /* raw offset of next page */ + begintime=granulepos; + + if(target-begintime>44100)break; + bisect=begin; /* *not* begin + 1 */ + }else{ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ + end=result; + bisect-=CHUNKSIZE; /* an endless loop otherwise. */ + if(bisect<=begin)bisect=begin+1; + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + }else{ + end=bisect; + endtime=granulepos; + break; + } + } + } + } + } + } + + /* found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceeding granulepos. */ + { + ogg_page og; + ogg_packet op; + + /* seek */ + result=_seek_helper(vf,best); + vf->pcm_offset=-1; + if(result) goto seek_error; + result=_get_next_page(context, vf,&og,-1); + if(result<0) goto seek_error; + + if(link!=vf->current_link){ + /* Different link; dump entire decode machine */ + _decode_clear(context, vf); + + vf->current_link=link; + vf->current_serialno=vf->serialnos[link]; + vf->ready_state=STREAMSET; + + }else{ + FMOD_vorbis_synthesis_restart(&vf->vd); + } + + FMOD_ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + FMOD_ogg_stream_pagein(context, &vf->os,&og); + + /* pull out all but last packet; the one with granulepos */ + while(1){ + result=FMOD_ogg_stream_packetpeek(&vf->os,&op); + if(result==0){ + /* !!! the packet finishing this page originated on a + preceeding page. Keep fetching previous pages until we + get one with a granulepos or without the 'continued' flag + set. Then just use raw_seek for simplicity. */ + + result=_seek_helper(vf,best); + if(result<0) goto seek_error; + + while(1){ + result=_get_prev_page(context, vf,&og); + if(result<0) goto seek_error; + if(FMOD_ogg_page_serialno(&og)==vf->current_serialno && + (FMOD_ogg_page_granulepos(&og)>-1 || + !FMOD_ogg_page_continued(&og))){ + return ov_raw_seek(context, vf,result); + } + vf->offset=result; + } + } + if(result<0){ + result = OV_EBADPACKET; + goto seek_error; + } + if(op.granulepos!=-1){ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + vf->pcm_offset+=total; + break; + }else + result=FMOD_ogg_stream_packetout(&vf->os,NULL); + } + } + } + + /* verify result */ + if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ + result=OV_EFAULT; + goto seek_error; + } + vf->bittrack=0.f; + vf->samptrack=0.f; + return(0); + + seek_error: + /* dump machine so we're in a known state */ + vf->pcm_offset=-1; + _decode_clear(context, vf); + return (int)result; +} + +/* seek to a sample offset relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ + +int ov_pcm_seek(void *context, OggVorbis_File *vf,ogg_int64_t pos){ + int thisblock,lastblock=0; + int ret=ov_pcm_seek_page(context, vf,pos); + if(ret<0)return(ret); + if((ret=_make_decode_ready(context, vf)))return ret; + + /* discard leading packets we don't need for the lapping of the + position we want; don't decode them */ + + while(1){ + ogg_packet op; + ogg_page og; + + int ret=FMOD_ogg_stream_packetpeek(&vf->os,&op); + if(ret>0){ + thisblock=FMOD_vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + FMOD_ogg_stream_packetout(&vf->os,NULL); + continue; /* non audio packet */ + } + if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; + + if(vf->pcm_offset+((thisblock+ + FMOD_vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; + + /* remove the packet from packet queue and track its granulepos */ + FMOD_ogg_stream_packetout(&vf->os,NULL); + ret = FMOD_vorbis_synthesis_trackonly(context, &vf->vb,&op); /* set up a vb with + only tracking, no + pcm_decode */ + if (ret < 0) + { + return ret; + } + FMOD_vorbis_synthesis_blockin(&vf->vd,&vf->vb); + + /* end of logical stream case is hard, especially with exact + length positioning. */ + + if(op.granulepos>-1){ + int i; + /* always believe the stream markers */ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + for(i=0;icurrent_link;i++) + vf->pcm_offset+=vf->pcmlengths[i*2+1]; + } + + lastblock=thisblock; + + }else{ + if(ret<0 && ret!=OV_HOLE)break; + + /* suck in a new page */ + if(_get_next_page(context, vf,&og,-1)<0)break; + if(FMOD_ogg_page_bos(&og))_decode_clear(context, vf); + + if(vf->ready_statelinks;link++) + if(vf->serialnos[link]==serialno)break; + if(link==vf->links) continue; + vf->current_link=link; + + vf->ready_state=STREAMSET; + vf->current_serialno=FMOD_ogg_page_serialno(&og); + FMOD_ogg_stream_reset_serialno(&vf->os,serialno); + ret=_make_decode_ready(context, vf); + if(ret)return ret; + lastblock=0; + } + + FMOD_ogg_stream_pagein(context, &vf->os,&og); + } + } + + vf->bittrack=0.f; + vf->samptrack=0.f; + /* discard samples until we reach the desired position. Crossing a + logical bitstream boundary with abandon is OK. */ + while(vf->pcm_offsetpcm_offset; + ogg_int32_t samples=FMOD_vorbis_synthesis_pcmout(&vf->vd,NULL); + + if(samples>target)samples=(ogg_int32_t)target; + FMOD_vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + + if(samplespcm_offset=ov_pcm_total(vf,-1); /* eof */ + } + return 0; +} + +/* seek to a playback time relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ +int ov_time_seek(void *context, OggVorbis_File *vf,float seconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=0; + float time_total=0; + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(seconds<0)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=0;linklinks;link++){ + float addsec = ov_time_total(vf,link); + if(secondspcmlengths[link*2+1]; + } + + if(link==vf->links)return(OV_EINVAL); + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=(ogg_int64_t)(pcm_total+(seconds-time_total)*vf->vi[link].rate); + return(ov_pcm_seek(context, vf,target)); + } +} + +/* page-granularity version of ov_time_seek + returns zero on success, nonzero on failure */ +int ov_time_seek_page(void *context, OggVorbis_File *vf,float seconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=0; + float time_total=0; + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(seconds<0)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=0;linklinks;link++){ + float addsec = ov_time_total(vf,link); + if(secondspcmlengths[link*2+1]; + } + + if(link==vf->links)return(OV_EINVAL); + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target= (ogg_int64_t)(pcm_total+(seconds-time_total)*vf->vi[link].rate); + return(ov_pcm_seek_page(context, vf,target)); + } +} + +/* tell the current stream offset cursor. Note that seek followed by + tell will likely not give the set offset due to caching */ +ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ + if(vf->ready_stateoffset); +} + +/* return PCM offset (sample) of next PCM sample to be read */ +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ + if(vf->ready_statepcm_offset); +} + +/* return time offset (seconds) of next PCM sample to be read */ +float ov_time_tell(OggVorbis_File *vf){ + int link=0; + ogg_int64_t pcm_total=0; + float time_total=0.f; + + if(vf->ready_stateseekable){ + pcm_total=ov_pcm_total(vf,-1); + time_total=ov_time_total(vf,-1); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(vf->pcm_offset>=pcm_total)break; + } + } + + return((float)time_total+(float)(vf->pcm_offset-pcm_total)/vf->vi[link].rate); +} + +/* link: -1) return the vorbis_info struct for the bitstream section + currently being decoded + 0-n) to request information for a specific bitstream section + + In the case of a non-seekable bitstream, any call returns the + current bitstream. NULL in the case that the machine is not + initialized */ + +vorbis_info *ov_info(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vi+vf->current_link; + else + return vf->vi; + else + if(link>=vf->links) + return NULL; + else + return vf->vi+link; + }else{ + return vf->vi; + } +} + +/* grr, strong typing, grr, no templates/inheritence, grr */ +vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vc+vf->current_link; + else + return vf->vc; + else + if(link>=vf->links) + return NULL; + else + return vf->vc+link; + }else{ + return vf->vc; + } +} + +static int host_is_big_endian() { + ogg_int32_t pattern = 0xfeedface; /* deadbeef */ + unsigned char *bytewise = (unsigned char *)&pattern; + if (bytewise[0] == 0xfe) return 1; + return 0; +} + +/* up to this point, everything could more or less hide the multiple + logical bitstream nature of chaining from the toplevel application + if the toplevel application didn't particularly care. However, at + the point that we actually read audio back, the multiple-section + nature must surface: Multiple bitstream sections do not necessarily + have to have the same number of channels or sampling rate. + + ov_read returns the sequential logical bitstream number currently + being decoded along with the PCM data in order that the toplevel + application can take action on channel/sample rate changes. This + number will be incremented even for streamed (non-seekable) streams + (for seekable streams, it represents the actual logical bitstream + index within the physical bitstream. Note that the accessor + functions above are aware of this dichotomy). + + ov_read_filter is exactly the same as ov_read except that it processes + the decoded audio data through a filter before packing it into the + requested format. This gives greater accuracy than applying a filter + after the audio has been converted into integral PCM. + + input values: buffer) a buffer to hold packed PCM data for return + length) the byte length requested to be placed into buffer + bigendianp) should the data be packed LSB first (0) or + MSB first (1) + word) word size for output. currently 1 (byte) or + 2 (16 bit short) + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of bytes of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + +ogg_int32_t ov_read_filter(void *context, OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream, + void (*filter)(float **pcm,ogg_int32_t channels,ogg_int32_t samples,void *filter_param),void *filter_param){ + int i,j; + int host_endian = host_is_big_endian(); + + float **pcm; + ogg_int32_t samples; + + if(vf->ready_stateready_state==INITSET){ + samples=FMOD_vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples)break; + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(context, vf,NULL,1,1); + if(ret==OV_EOF) + return(0); + if(ret<=0) + return(ret); + } + + } + + if(samples>0){ + + /* yay! proceed to pack data into the byte buffer */ + + ogg_int32_t channels=ov_info(vf,-1)->channels; + ogg_int32_t bytespersample=word * channels; + vorbis_fpu_control fpu; + if(samples>length/bytespersample)samples=length/bytespersample; + + if(samples <= 0) + return OV_EINVAL; + + /* Here. */ + if(filter) + filter(pcm,channels,samples,filter_param); + + /* a tight loop to pack each size */ + { + int val; + if(word==1){ + int off=(sgned?0:128); + FMOD_vorbis_fpu_setround(&fpu); + for(j=0;j127)val=127; + else if(val<-128)val=-128; + *buffer++=val+off; + } + FMOD_vorbis_fpu_restore(fpu); + }else{ + int off=(sgned?0:32768); + + if(host_endian==bigendianp){ + if(sgned){ + + FMOD_vorbis_fpu_setround(&fpu); + for(i=0;i32767)val=32767; + else if(val<-32768)val=-32768; + *dest=val; + dest+=channels; + } + } + FMOD_vorbis_fpu_restore(fpu); + + }else{ + + FMOD_vorbis_fpu_setround(&fpu); + for(i=0;i32767)val=32767; + else if(val<-32768)val=-32768; + *dest=val+off; + dest+=channels; + } + } + FMOD_vorbis_fpu_restore(fpu); + + } + }else if(bigendianp){ + + FMOD_vorbis_fpu_setround(&fpu); + for(j=0;j32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + *buffer++=(val>>8); + *buffer++=(val&0xff); + } + FMOD_vorbis_fpu_restore(fpu); + + }else{ + int val; + FMOD_vorbis_fpu_setround(&fpu); + for(j=0;j32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + *buffer++=(val&0xff); + *buffer++=(val>>8); + } + FMOD_vorbis_fpu_restore(fpu); + + } + } + } + + FMOD_vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return(samples*bytespersample); + }else{ + return(samples); + } +} + +ogg_int32_t ov_read(void *context, OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream){ + return ov_read_filter(context, vf, buffer, length, bigendianp, word, sgned, bitstream, NULL, NULL); +} + +/* input values: pcm_channels) a float vector per channel of output + length) the sample length being read by the app + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of samples of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + + + +ogg_int32_t ov_read_float(void *context, OggVorbis_File *vf,float ***pcm_channels,int length, + int *bitstream){ + + if(vf->ready_stateready_state==INITSET){ + float **pcm; + ogg_int32_t samples=FMOD_vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples){ + if(pcm_channels)*pcm_channels=pcm; + if(samples>length)samples=length; + FMOD_vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return samples; + + } + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(context, vf,NULL,1,1); + if(ret==OV_EOF)return(0); + if(ret<=0)return(ret); + } + + } +} + +extern float *FMOD_vorbis_window(vorbis_dsp_state *v,int W); + +static void _ov_splice(float **pcm,float **lappcm, + int n1, int n2, + int ch1, int ch2, + float *w1, float *w2){ + int i,j; + float *w=w1; + int n=n1; + + if(n1>n2){ + n=n2; + w=w2; + } + + /* splice */ + for(j=0;jready_state==INITSET)break; + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(context, vf,NULL,1,0); + if(ret<0 && ret!=OV_HOLE)return(ret); + } + } + return 0; +} + +/* make sure vf is INITSET and that we have a primed buffer; if + we're crosslapping at a stream section boundary, this also makes + sure we're sanity checking against the right stream information */ +static int _ov_initprime(void *context, OggVorbis_File *vf){ + vorbis_dsp_state *vd=&vf->vd; + while(1){ + if(vf->ready_state==INITSET) + if(FMOD_vorbis_synthesis_pcmout(vd,NULL))break; + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(context, vf,NULL,1,0); + if(ret<0 && ret!=OV_HOLE)return(ret); + } + } + return 0; +} + +/* grab enough data for lapping from vf; this may be in the form of + unreturned, already-decoded pcm, remaining PCM we will need to + decode, or synthetic postextrapolation from last packets. */ +static void _ov_getlap(void *context, OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, + float **lappcm,int lapsize){ + int lapcount=0,i; + float **pcm; + + /* try first to decode the lapping data */ + while(lapcountlapsize-lapcount)samples=lapsize-lapcount; + for(i=0;ichannels;i++) + FMOD_memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); + lapcount+=samples; + FMOD_vorbis_synthesis_read(vd,samples); + }else{ + /* suck in another packet */ + int ret=_fetch_and_process_packet(context, vf,NULL,1,0); /* do *not* span */ + if(ret==OV_EOF)break; + } + } + if(lapcountvd,&pcm); + if(samples==0){ + for(i=0;ichannels;i++) + FMOD_memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount); + lapcount=lapsize; + }else{ + if(samples>lapsize-lapcount)samples=lapsize-lapcount; + for(i=0;ichannels;i++) + FMOD_memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); + lapcount+=samples; + } + } +} + +/* this sets up crosslapping of a sample by using trailing data from + sample 1 and lapping it into the windowing buffer of sample 2 */ +int ov_crosslap(void *context, OggVorbis_File *vf1, OggVorbis_File *vf2){ + vorbis_info *vi1,*vi2; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,i,ret,hs1,hs2; + + if(vf1==vf2)return(0); /* degenerate case */ + if(vf1->ready_stateready_statechannels); + n1=FMOD_vorbis_info_blocksize(vi1,0)>>(1+hs1); + n2=FMOD_vorbis_info_blocksize(vi2,0)>>(1+hs2); + w1=FMOD_vorbis_window(&vf1->vd,0); + w2=FMOD_vorbis_window(&vf2->vd,0); + + for(i=0;ichannels;i++) + lappcm[i]=alloca(sizeof(**lappcm)*n1); + + _ov_getlap(context, vf1,vi1,&vf1->vd,lappcm,n1); + + /* have a lapping buffer from vf1; now to splice it into the lapping + buffer of vf2 */ + /* consolidate and expose the buffer. */ + FMOD_vorbis_synthesis_lapout(&vf2->vd,&pcm); + +#if 0 + _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0); + _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0); +#endif + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2); + + /* done */ + return(0); +} + +static int _ov_64_seek_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos, + int (*localseek)(void *context, OggVorbis_File *,ogg_int64_t)){ + vorbis_info *vi; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,ch1,ch2,hs; + int i,ret; + + if(vf->ready_statechannels; + n1=FMOD_vorbis_info_blocksize(vi,0)>>(1+hs); + w1=FMOD_vorbis_window(&vf->vd,0); /* window arrays from libvorbis are + persistent; even if the decode state + from this link gets dumped, this + window array continues to exist */ + + lappcm=alloca(sizeof(*lappcm)*ch1); + for(i=0;ivd,lappcm,n1); + + /* have lapping data; seek and prime the buffer */ + ret=localseek(context, vf,pos); + if(ret)return ret; + ret=_ov_initprime(context, vf); + if(ret)return(ret); + + /* Guard against cross-link changes; they're perfectly legal */ + vi=ov_info(vf,-1); + ch2=vi->channels; + n2=FMOD_vorbis_info_blocksize(vi,0)>>(1+hs); + w2=FMOD_vorbis_window(&vf->vd,0); + + /* consolidate and expose the buffer. */ + FMOD_vorbis_synthesis_lapout(&vf->vd,&pcm); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); + + /* done */ + return(0); +} + +int ov_raw_seek_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(context, vf,pos,ov_raw_seek); +} + +int ov_pcm_seek_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(context, vf,pos,ov_pcm_seek); +} + +int ov_pcm_seek_page_lap(void *context, OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(context, vf,pos,ov_pcm_seek_page); +} + +static int _ov_d_seek_lap(void *context, OggVorbis_File *vf,float pos, + int (*localseek)(void *context, OggVorbis_File *,float)){ + vorbis_info *vi; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,ch1,ch2,hs; + int i,ret; + + if(vf->ready_statechannels; + n1=FMOD_vorbis_info_blocksize(vi,0)>>(1+hs); + w1=FMOD_vorbis_window(&vf->vd,0); /* window arrays from libvorbis are + persistent; even if the decode state + from this link gets dumped, this + window array continues to exist */ + + lappcm=alloca(sizeof(*lappcm)*ch1); + for(i=0;ivd,lappcm,n1); + + /* have lapping data; seek and prime the buffer */ + ret=localseek(context, vf,pos); + if(ret)return ret; + ret=_ov_initprime(context, vf); + if(ret)return(ret); + + /* Guard against cross-link changes; they're perfectly legal */ + vi=ov_info(vf,-1); + ch2=vi->channels; + n2=FMOD_vorbis_info_blocksize(vi,0)>>(1+hs); + w2=FMOD_vorbis_window(&vf->vd,0); + + /* consolidate and expose the buffer. */ + FMOD_vorbis_synthesis_lapout(&vf->vd,&pcm); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); + + /* done */ + return(0); +} + +int ov_time_seek_lap(void *context, OggVorbis_File *vf,float pos){ + return _ov_d_seek_lap(context, vf,pos,ov_time_seek); +} + +int ov_time_seek_page_lap(void *context, OggVorbis_File *vf,float pos){ + return _ov_d_seek_lap(context, vf,pos,ov_time_seek_page); +} diff --git a/lib/ogg_vorbis/vorbis/lib/window.c b/lib/ogg_vorbis/vorbis/lib/window.c new file mode 100755 index 0000000..50e778f --- /dev/null +++ b/lib/ogg_vorbis/vorbis/lib/window.c @@ -0,0 +1,2193 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: window functions + last mod: $Id: window.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include "os.h" +#include "misc.h" + +static float vwin64[32]; /* = { + 0.0009460463F, 0.0085006468F, 0.0235352254F, 0.0458950567F, + 0.0753351908F, 0.1115073077F, 0.1539457973F, 0.2020557475F, + 0.2551056759F, 0.3122276645F, 0.3724270287F, 0.4346027792F, + 0.4975789974F, 0.5601459521F, 0.6211085051F, 0.6793382689F, + 0.7338252629F, 0.7837245849F, 0.8283939355F, 0.8674186656F, + 0.9006222429F, 0.9280614787F, 0.9500073081F, 0.9669131782F, + 0.9793740220F, 0.9880792941F, 0.9937636139F, 0.9971582668F, + 0.9989462667F, 0.9997230082F, 0.9999638688F, 0.9999995525F, +};*/ + +static float vwin128[64]; /* = { + 0.0002365472F, 0.0021280687F, 0.0059065254F, 0.0115626550F, + 0.0190823442F, 0.0284463735F, 0.0396300935F, 0.0526030430F, + 0.0673285281F, 0.0837631763F, 0.1018564887F, 0.1215504095F, + 0.1427789367F, 0.1654677960F, 0.1895342001F, 0.2148867160F, + 0.2414252576F, 0.2690412240F, 0.2976177952F, 0.3270303960F, + 0.3571473350F, 0.3878306189F, 0.4189369387F, 0.4503188188F, + 0.4818259135F, 0.5133064334F, 0.5446086751F, 0.5755826278F, + 0.6060816248F, 0.6359640047F, 0.6650947483F, 0.6933470543F, + 0.7206038179F, 0.7467589810F, 0.7717187213F, 0.7954024542F, + 0.8177436264F, 0.8386902831F, 0.8582053981F, 0.8762669622F, + 0.8928678298F, 0.9080153310F, 0.9217306608F, 0.9340480615F, + 0.9450138200F, 0.9546851041F, 0.9631286621F, 0.9704194171F, + 0.9766389810F, 0.9818741197F, 0.9862151938F, 0.9897546035F, + 0.9925852598F, 0.9947991032F, 0.9964856900F, 0.9977308602F, + 0.9986155015F, 0.9992144193F, 0.9995953200F, 0.9998179155F, + 0.9999331503F, 0.9999825563F, 0.9999977357F, 0.9999999720F, +};*/ + +static float vwin256[128]; /* = { + 0.0000591390F, 0.0005321979F, 0.0014780301F, 0.0028960636F, + 0.0047854363F, 0.0071449926F, 0.0099732775F, 0.0132685298F, + 0.0170286741F, 0.0212513119F, 0.0259337111F, 0.0310727950F, + 0.0366651302F, 0.0427069140F, 0.0491939614F, 0.0561216907F, + 0.0634851102F, 0.0712788035F, 0.0794969160F, 0.0881331402F, + 0.0971807028F, 0.1066323515F, 0.1164803426F, 0.1267164297F, + 0.1373318534F, 0.1483173323F, 0.1596630553F, 0.1713586755F, + 0.1833933062F, 0.1957555184F, 0.2084333404F, 0.2214142599F, + 0.2346852280F, 0.2482326664F, 0.2620424757F, 0.2761000481F, + 0.2903902813F, 0.3048975959F, 0.3196059553F, 0.3344988887F, + 0.3495595160F, 0.3647705766F, 0.3801144597F, 0.3955732382F, + 0.4111287047F, 0.4267624093F, 0.4424557009F, 0.4581897696F, + 0.4739456913F, 0.4897044744F, 0.5054471075F, 0.5211546088F, + 0.5368080763F, 0.5523887395F, 0.5678780103F, 0.5832575361F, + 0.5985092508F, 0.6136154277F, 0.6285587300F, 0.6433222619F, + 0.6578896175F, 0.6722449294F, 0.6863729144F, 0.7002589187F, + 0.7138889597F, 0.7272497662F, 0.7403288154F, 0.7531143679F, + 0.7655954985F, 0.7777621249F, 0.7896050322F, 0.8011158947F, + 0.8122872932F, 0.8231127294F, 0.8335866365F, 0.8437043850F, + 0.8534622861F, 0.8628575905F, 0.8718884835F, 0.8805540765F, + 0.8888543947F, 0.8967903616F, 0.9043637797F, 0.9115773078F, + 0.9184344360F, 0.9249394562F, 0.9310974312F, 0.9369141608F, + 0.9423961446F, 0.9475505439F, 0.9523851406F, 0.9569082947F, + 0.9611289005F, 0.9650563408F, 0.9687004405F, 0.9720714191F, + 0.9751798427F, 0.9780365753F, 0.9806527301F, 0.9830396204F, + 0.9852087111F, 0.9871715701F, 0.9889398207F, 0.9905250941F, + 0.9919389832F, 0.9931929973F, 0.9942985174F, 0.9952667537F, + 0.9961087037F, 0.9968351119F, 0.9974564312F, 0.9979827858F, + 0.9984239359F, 0.9987892441F, 0.9990876435F, 0.9993276081F, + 0.9995171241F, 0.9996636648F, 0.9997741654F, 0.9998550016F, + 0.9999119692F, 0.9999502656F, 0.9999744742F, 0.9999885497F, + 0.9999958064F, 0.9999989077F, 0.9999998584F, 0.9999999983F, +};*/ + +static float vwin512[256]; /* = { + 0.0000147849F, 0.0001330607F, 0.0003695946F, 0.0007243509F, + 0.0011972759F, 0.0017882983F, 0.0024973285F, 0.0033242588F, + 0.0042689632F, 0.0053312973F, 0.0065110982F, 0.0078081841F, + 0.0092223540F, 0.0107533880F, 0.0124010466F, 0.0141650703F, + 0.0160451800F, 0.0180410758F, 0.0201524373F, 0.0223789233F, + 0.0247201710F, 0.0271757958F, 0.0297453914F, 0.0324285286F, + 0.0352247556F, 0.0381335972F, 0.0411545545F, 0.0442871045F, + 0.0475306997F, 0.0508847676F, 0.0543487103F, 0.0579219038F, + 0.0616036982F, 0.0653934164F, 0.0692903546F, 0.0732937809F, + 0.0774029356F, 0.0816170305F, 0.0859352485F, 0.0903567428F, + 0.0948806375F, 0.0995060259F, 0.1042319712F, 0.1090575056F, + 0.1139816300F, 0.1190033137F, 0.1241214941F, 0.1293350764F, + 0.1346429333F, 0.1400439046F, 0.1455367974F, 0.1511203852F, + 0.1567934083F, 0.1625545735F, 0.1684025537F, 0.1743359881F, + 0.1803534820F, 0.1864536069F, 0.1926349000F, 0.1988958650F, + 0.2052349715F, 0.2116506555F, 0.2181413191F, 0.2247053313F, + 0.2313410275F, 0.2380467105F, 0.2448206500F, 0.2516610835F, + 0.2585662164F, 0.2655342226F, 0.2725632448F, 0.2796513950F, + 0.2867967551F, 0.2939973773F, 0.3012512852F, 0.3085564739F, + 0.3159109111F, 0.3233125375F, 0.3307592680F, 0.3382489922F, + 0.3457795756F, 0.3533488602F, 0.3609546657F, 0.3685947904F, + 0.3762670121F, 0.3839690896F, 0.3916987634F, 0.3994537572F, + 0.4072317788F, 0.4150305215F, 0.4228476653F, 0.4306808783F, + 0.4385278181F, 0.4463861329F, 0.4542534630F, 0.4621274424F, + 0.4700057001F, 0.4778858615F, 0.4857655502F, 0.4936423891F, + 0.5015140023F, 0.5093780165F, 0.5172320626F, 0.5250737772F, + 0.5329008043F, 0.5407107971F, 0.5485014192F, 0.5562703465F, + 0.5640152688F, 0.5717338914F, 0.5794239366F, 0.5870831457F, + 0.5947092801F, 0.6023001235F, 0.6098534829F, 0.6173671907F, + 0.6248391059F, 0.6322671161F, 0.6396491384F, 0.6469831217F, + 0.6542670475F, 0.6614989319F, 0.6686768267F, 0.6757988210F, + 0.6828630426F, 0.6898676592F, 0.6968108799F, 0.7036909564F, + 0.7105061843F, 0.7172549043F, 0.7239355032F, 0.7305464154F, + 0.7370861235F, 0.7435531598F, 0.7499461068F, 0.7562635986F, + 0.7625043214F, 0.7686670148F, 0.7747504721F, 0.7807535410F, + 0.7866751247F, 0.7925141825F, 0.7982697296F, 0.8039408387F, + 0.8095266395F, 0.8150263196F, 0.8204391248F, 0.8257643590F, + 0.8310013848F, 0.8361496236F, 0.8412085555F, 0.8461777194F, + 0.8510567129F, 0.8558451924F, 0.8605428730F, 0.8651495278F, + 0.8696649882F, 0.8740891432F, 0.8784219392F, 0.8826633797F, + 0.8868135244F, 0.8908724888F, 0.8948404441F, 0.8987176157F, + 0.9025042831F, 0.9062007791F, 0.9098074886F, 0.9133248482F, + 0.9167533451F, 0.9200935163F, 0.9233459472F, 0.9265112712F, + 0.9295901680F, 0.9325833632F, 0.9354916263F, 0.9383157705F, + 0.9410566504F, 0.9437151618F, 0.9462922398F, 0.9487888576F, + 0.9512060252F, 0.9535447882F, 0.9558062262F, 0.9579914516F, + 0.9601016078F, 0.9621378683F, 0.9641014348F, 0.9659935361F, + 0.9678154261F, 0.9695683830F, 0.9712537071F, 0.9728727198F, + 0.9744267618F, 0.9759171916F, 0.9773453842F, 0.9787127293F, + 0.9800206298F, 0.9812705006F, 0.9824637665F, 0.9836018613F, + 0.9846862258F, 0.9857183066F, 0.9866995544F, 0.9876314227F, + 0.9885153662F, 0.9893528393F, 0.9901452948F, 0.9908941823F, + 0.9916009470F, 0.9922670279F, 0.9928938570F, 0.9934828574F, + 0.9940354423F, 0.9945530133F, 0.9950369595F, 0.9954886562F, + 0.9959094633F, 0.9963007242F, 0.9966637649F, 0.9969998925F, + 0.9973103939F, 0.9975965351F, 0.9978595598F, 0.9981006885F, + 0.9983211172F, 0.9985220166F, 0.9987045311F, 0.9988697776F, + 0.9990188449F, 0.9991527924F, 0.9992726499F, 0.9993794157F, + 0.9994740570F, 0.9995575079F, 0.9996306699F, 0.9996944099F, + 0.9997495605F, 0.9997969190F, 0.9998372465F, 0.9998712678F, + 0.9998996704F, 0.9999231041F, 0.9999421807F, 0.9999574732F, + 0.9999695157F, 0.9999788026F, 0.9999857885F, 0.9999908879F, + 0.9999944746F, 0.9999968817F, 0.9999984010F, 0.9999992833F, + 0.9999997377F, 0.9999999317F, 0.9999999911F, 0.9999999999F, +};*/ + +static float vwin1024[512]; /* = { + 0.0000036962F, 0.0000332659F, 0.0000924041F, 0.0001811086F, + 0.0002993761F, 0.0004472021F, 0.0006245811F, 0.0008315063F, + 0.0010679699F, 0.0013339631F, 0.0016294757F, 0.0019544965F, + 0.0023090133F, 0.0026930125F, 0.0031064797F, 0.0035493989F, + 0.0040217533F, 0.0045235250F, 0.0050546946F, 0.0056152418F, + 0.0062051451F, 0.0068243817F, 0.0074729278F, 0.0081507582F, + 0.0088578466F, 0.0095941655F, 0.0103596863F, 0.0111543789F, + 0.0119782122F, 0.0128311538F, 0.0137131701F, 0.0146242260F, + 0.0155642855F, 0.0165333111F, 0.0175312640F, 0.0185581042F, + 0.0196137903F, 0.0206982797F, 0.0218115284F, 0.0229534910F, + 0.0241241208F, 0.0253233698F, 0.0265511886F, 0.0278075263F, + 0.0290923308F, 0.0304055484F, 0.0317471241F, 0.0331170013F, + 0.0345151222F, 0.0359414274F, 0.0373958560F, 0.0388783456F, + 0.0403888325F, 0.0419272511F, 0.0434935347F, 0.0450876148F, + 0.0467094213F, 0.0483588828F, 0.0500359261F, 0.0517404765F, + 0.0534724575F, 0.0552317913F, 0.0570183983F, 0.0588321971F, + 0.0606731048F, 0.0625410369F, 0.0644359070F, 0.0663576272F, + 0.0683061077F, 0.0702812571F, 0.0722829821F, 0.0743111878F, + 0.0763657775F, 0.0784466526F, 0.0805537129F, 0.0826868561F, + 0.0848459782F, 0.0870309736F, 0.0892417345F, 0.0914781514F, + 0.0937401128F, 0.0960275056F, 0.0983402145F, 0.1006781223F, + 0.1030411101F, 0.1054290568F, 0.1078418397F, 0.1102793336F, + 0.1127414119F, 0.1152279457F, 0.1177388042F, 0.1202738544F, + 0.1228329618F, 0.1254159892F, 0.1280227980F, 0.1306532471F, + 0.1333071937F, 0.1359844927F, 0.1386849970F, 0.1414085575F, + 0.1441550230F, 0.1469242403F, 0.1497160539F, 0.1525303063F, + 0.1553668381F, 0.1582254875F, 0.1611060909F, 0.1640084822F, + 0.1669324936F, 0.1698779549F, 0.1728446939F, 0.1758325362F, + 0.1788413055F, 0.1818708232F, 0.1849209084F, 0.1879913785F, + 0.1910820485F, 0.1941927312F, 0.1973232376F, 0.2004733764F, + 0.2036429541F, 0.2068317752F, 0.2100396421F, 0.2132663552F, + 0.2165117125F, 0.2197755102F, 0.2230575422F, 0.2263576007F, + 0.2296754753F, 0.2330109540F, 0.2363638225F, 0.2397338646F, + 0.2431208619F, 0.2465245941F, 0.2499448389F, 0.2533813719F, + 0.2568339669F, 0.2603023956F, 0.2637864277F, 0.2672858312F, + 0.2708003718F, 0.2743298135F, 0.2778739186F, 0.2814324472F, + 0.2850051576F, 0.2885918065F, 0.2921921485F, 0.2958059366F, + 0.2994329219F, 0.3030728538F, 0.3067254799F, 0.3103905462F, + 0.3140677969F, 0.3177569747F, 0.3214578205F, 0.3251700736F, + 0.3288934718F, 0.3326277513F, 0.3363726468F, 0.3401278914F, + 0.3438932168F, 0.3476683533F, 0.3514530297F, 0.3552469734F, + 0.3590499106F, 0.3628615659F, 0.3666816630F, 0.3705099239F, + 0.3743460698F, 0.3781898204F, 0.3820408945F, 0.3858990095F, + 0.3897638820F, 0.3936352274F, 0.3975127601F, 0.4013961936F, + 0.4052852405F, 0.4091796123F, 0.4130790198F, 0.4169831732F, + 0.4208917815F, 0.4248045534F, 0.4287211965F, 0.4326414181F, + 0.4365649248F, 0.4404914225F, 0.4444206167F, 0.4483522125F, + 0.4522859146F, 0.4562214270F, 0.4601584538F, 0.4640966984F, + 0.4680358644F, 0.4719756548F, 0.4759157726F, 0.4798559209F, + 0.4837958024F, 0.4877351199F, 0.4916735765F, 0.4956108751F, + 0.4995467188F, 0.5034808109F, 0.5074128550F, 0.5113425550F, + 0.5152696149F, 0.5191937395F, 0.5231146336F, 0.5270320028F, + 0.5309455530F, 0.5348549910F, 0.5387600239F, 0.5426603597F, + 0.5465557070F, 0.5504457754F, 0.5543302752F, 0.5582089175F, + 0.5620814145F, 0.5659474793F, 0.5698068262F, 0.5736591704F, + 0.5775042283F, 0.5813417176F, 0.5851713571F, 0.5889928670F, + 0.5928059689F, 0.5966103856F, 0.6004058415F, 0.6041920626F, + 0.6079687761F, 0.6117357113F, 0.6154925986F, 0.6192391705F, + 0.6229751612F, 0.6267003064F, 0.6304143441F, 0.6341170137F, + 0.6378080569F, 0.6414872173F, 0.6451542405F, 0.6488088741F, + 0.6524508681F, 0.6560799742F, 0.6596959469F, 0.6632985424F, + 0.6668875197F, 0.6704626398F, 0.6740236662F, 0.6775703649F, + 0.6811025043F, 0.6846198554F, 0.6881221916F, 0.6916092892F, + 0.6950809269F, 0.6985368861F, 0.7019769510F, 0.7054009085F, + 0.7088085484F, 0.7121996632F, 0.7155740484F, 0.7189315023F, + 0.7222718263F, 0.7255948245F, 0.7289003043F, 0.7321880760F, + 0.7354579530F, 0.7387097518F, 0.7419432921F, 0.7451583966F, + 0.7483548915F, 0.7515326059F, 0.7546913723F, 0.7578310265F, + 0.7609514077F, 0.7640523581F, 0.7671337237F, 0.7701953535F, + 0.7732371001F, 0.7762588195F, 0.7792603711F, 0.7822416178F, + 0.7852024259F, 0.7881426654F, 0.7910622097F, 0.7939609356F, + 0.7968387237F, 0.7996954579F, 0.8025310261F, 0.8053453193F, + 0.8081382324F, 0.8109096638F, 0.8136595156F, 0.8163876936F, + 0.8190941071F, 0.8217786690F, 0.8244412960F, 0.8270819086F, + 0.8297004305F, 0.8322967896F, 0.8348709171F, 0.8374227481F, + 0.8399522213F, 0.8424592789F, 0.8449438672F, 0.8474059356F, + 0.8498454378F, 0.8522623306F, 0.8546565748F, 0.8570281348F, + 0.8593769787F, 0.8617030779F, 0.8640064080F, 0.8662869477F, + 0.8685446796F, 0.8707795899F, 0.8729916682F, 0.8751809079F, + 0.8773473059F, 0.8794908626F, 0.8816115819F, 0.8837094713F, + 0.8857845418F, 0.8878368079F, 0.8898662874F, 0.8918730019F, + 0.8938569760F, 0.8958182380F, 0.8977568194F, 0.8996727552F, + 0.9015660837F, 0.9034368465F, 0.9052850885F, 0.9071108577F, + 0.9089142057F, 0.9106951869F, 0.9124538591F, 0.9141902832F, + 0.9159045233F, 0.9175966464F, 0.9192667228F, 0.9209148257F, + 0.9225410313F, 0.9241454187F, 0.9257280701F, 0.9272890704F, + 0.9288285075F, 0.9303464720F, 0.9318430576F, 0.9333183603F, + 0.9347724792F, 0.9362055158F, 0.9376175745F, 0.9390087622F, + 0.9403791881F, 0.9417289644F, 0.9430582055F, 0.9443670283F, + 0.9456555521F, 0.9469238986F, 0.9481721917F, 0.9494005577F, + 0.9506091252F, 0.9517980248F, 0.9529673894F, 0.9541173540F, + 0.9552480557F, 0.9563596334F, 0.9574522282F, 0.9585259830F, + 0.9595810428F, 0.9606175542F, 0.9616356656F, 0.9626355274F, + 0.9636172915F, 0.9645811114F, 0.9655271425F, 0.9664555414F, + 0.9673664664F, 0.9682600774F, 0.9691365355F, 0.9699960034F, + 0.9708386448F, 0.9716646250F, 0.9724741103F, 0.9732672685F, + 0.9740442683F, 0.9748052795F, 0.9755504729F, 0.9762800205F, + 0.9769940950F, 0.9776928703F, 0.9783765210F, 0.9790452223F, + 0.9796991504F, 0.9803384823F, 0.9809633954F, 0.9815740679F, + 0.9821706784F, 0.9827534063F, 0.9833224312F, 0.9838779332F, + 0.9844200928F, 0.9849490910F, 0.9854651087F, 0.9859683274F, + 0.9864589286F, 0.9869370940F, 0.9874030054F, 0.9878568447F, + 0.9882987937F, 0.9887290343F, 0.9891477481F, 0.9895551169F, + 0.9899513220F, 0.9903365446F, 0.9907109658F, 0.9910747662F, + 0.9914281260F, 0.9917712252F, 0.9921042433F, 0.9924273593F, + 0.9927407516F, 0.9930445982F, 0.9933390763F, 0.9936243626F, + 0.9939006331F, 0.9941680631F, 0.9944268269F, 0.9946770982F, + 0.9949190498F, 0.9951528537F, 0.9953786808F, 0.9955967011F, + 0.9958070836F, 0.9960099963F, 0.9962056061F, 0.9963940787F, + 0.9965755786F, 0.9967502693F, 0.9969183129F, 0.9970798704F, + 0.9972351013F, 0.9973841640F, 0.9975272151F, 0.9976644103F, + 0.9977959036F, 0.9979218476F, 0.9980423932F, 0.9981576901F, + 0.9982678862F, 0.9983731278F, 0.9984735596F, 0.9985693247F, + 0.9986605645F, 0.9987474186F, 0.9988300248F, 0.9989085193F, + 0.9989830364F, 0.9990537085F, 0.9991206662F, 0.9991840382F, + 0.9992439513F, 0.9993005303F, 0.9993538982F, 0.9994041757F, + 0.9994514817F, 0.9994959330F, 0.9995376444F, 0.9995767286F, + 0.9996132960F, 0.9996474550F, 0.9996793121F, 0.9997089710F, + 0.9997365339F, 0.9997621003F, 0.9997857677F, 0.9998076311F, + 0.9998277836F, 0.9998463156F, 0.9998633155F, 0.9998788692F, + 0.9998930603F, 0.9999059701F, 0.9999176774F, 0.9999282586F, + 0.9999377880F, 0.9999463370F, 0.9999539749F, 0.9999607685F, + 0.9999667820F, 0.9999720773F, 0.9999767136F, 0.9999807479F, + 0.9999842344F, 0.9999872249F, 0.9999897688F, 0.9999919127F, + 0.9999937009F, 0.9999951749F, 0.9999963738F, 0.9999973342F, + 0.9999980900F, 0.9999986724F, 0.9999991103F, 0.9999994297F, + 0.9999996543F, 0.9999998049F, 0.9999999000F, 0.9999999552F, + 0.9999999836F, 0.9999999957F, 0.9999999994F, 1.0000000000F, +};*/ + +static float vwin2048[1024]; /* = { + 0.0000009241F, 0.0000083165F, 0.0000231014F, 0.0000452785F, + 0.0000748476F, 0.0001118085F, 0.0001561608F, 0.0002079041F, + 0.0002670379F, 0.0003335617F, 0.0004074748F, 0.0004887765F, + 0.0005774661F, 0.0006735427F, 0.0007770054F, 0.0008878533F, + 0.0010060853F, 0.0011317002F, 0.0012646969F, 0.0014050742F, + 0.0015528307F, 0.0017079650F, 0.0018704756F, 0.0020403610F, + 0.0022176196F, 0.0024022497F, 0.0025942495F, 0.0027936173F, + 0.0030003511F, 0.0032144490F, 0.0034359088F, 0.0036647286F, + 0.0039009061F, 0.0041444391F, 0.0043953253F, 0.0046535621F, + 0.0049191472F, 0.0051920781F, 0.0054723520F, 0.0057599664F, + 0.0060549184F, 0.0063572052F, 0.0066668239F, 0.0069837715F, + 0.0073080449F, 0.0076396410F, 0.0079785566F, 0.0083247884F, + 0.0086783330F, 0.0090391871F, 0.0094073470F, 0.0097828092F, + 0.0101655700F, 0.0105556258F, 0.0109529726F, 0.0113576065F, + 0.0117695237F, 0.0121887200F, 0.0126151913F, 0.0130489335F, + 0.0134899422F, 0.0139382130F, 0.0143937415F, 0.0148565233F, + 0.0153265536F, 0.0158038279F, 0.0162883413F, 0.0167800889F, + 0.0172790660F, 0.0177852675F, 0.0182986882F, 0.0188193231F, + 0.0193471668F, 0.0198822141F, 0.0204244594F, 0.0209738974F, + 0.0215305225F, 0.0220943289F, 0.0226653109F, 0.0232434627F, + 0.0238287784F, 0.0244212519F, 0.0250208772F, 0.0256276481F, + 0.0262415582F, 0.0268626014F, 0.0274907711F, 0.0281260608F, + 0.0287684638F, 0.0294179736F, 0.0300745833F, 0.0307382859F, + 0.0314090747F, 0.0320869424F, 0.0327718819F, 0.0334638860F, + 0.0341629474F, 0.0348690586F, 0.0355822122F, 0.0363024004F, + 0.0370296157F, 0.0377638502F, 0.0385050960F, 0.0392533451F, + 0.0400085896F, 0.0407708211F, 0.0415400315F, 0.0423162123F, + 0.0430993552F, 0.0438894515F, 0.0446864926F, 0.0454904698F, + 0.0463013742F, 0.0471191969F, 0.0479439288F, 0.0487755607F, + 0.0496140836F, 0.0504594879F, 0.0513117642F, 0.0521709031F, + 0.0530368949F, 0.0539097297F, 0.0547893979F, 0.0556758894F, + 0.0565691941F, 0.0574693019F, 0.0583762026F, 0.0592898858F, + 0.0602103410F, 0.0611375576F, 0.0620715250F, 0.0630122324F, + 0.0639596688F, 0.0649138234F, 0.0658746848F, 0.0668422421F, + 0.0678164838F, 0.0687973985F, 0.0697849746F, 0.0707792005F, + 0.0717800645F, 0.0727875547F, 0.0738016591F, 0.0748223656F, + 0.0758496620F, 0.0768835359F, 0.0779239751F, 0.0789709668F, + 0.0800244985F, 0.0810845574F, 0.0821511306F, 0.0832242052F, + 0.0843037679F, 0.0853898056F, 0.0864823050F, 0.0875812525F, + 0.0886866347F, 0.0897984378F, 0.0909166480F, 0.0920412513F, + 0.0931722338F, 0.0943095813F, 0.0954532795F, 0.0966033140F, + 0.0977596702F, 0.0989223336F, 0.1000912894F, 0.1012665227F, + 0.1024480185F, 0.1036357616F, 0.1048297369F, 0.1060299290F, + 0.1072363224F, 0.1084489014F, 0.1096676504F, 0.1108925534F, + 0.1121235946F, 0.1133607577F, 0.1146040267F, 0.1158533850F, + 0.1171088163F, 0.1183703040F, 0.1196378312F, 0.1209113812F, + 0.1221909370F, 0.1234764815F, 0.1247679974F, 0.1260654674F, + 0.1273688740F, 0.1286781995F, 0.1299934263F, 0.1313145365F, + 0.1326415121F, 0.1339743349F, 0.1353129866F, 0.1366574490F, + 0.1380077035F, 0.1393637315F, 0.1407255141F, 0.1420930325F, + 0.1434662677F, 0.1448452004F, 0.1462298115F, 0.1476200814F, + 0.1490159906F, 0.1504175195F, 0.1518246482F, 0.1532373569F, + 0.1546556253F, 0.1560794333F, 0.1575087606F, 0.1589435866F, + 0.1603838909F, 0.1618296526F, 0.1632808509F, 0.1647374648F, + 0.1661994731F, 0.1676668546F, 0.1691395880F, 0.1706176516F, + 0.1721010238F, 0.1735896829F, 0.1750836068F, 0.1765827736F, + 0.1780871610F, 0.1795967468F, 0.1811115084F, 0.1826314234F, + 0.1841564689F, 0.1856866221F, 0.1872218600F, 0.1887621595F, + 0.1903074974F, 0.1918578503F, 0.1934131947F, 0.1949735068F, + 0.1965387630F, 0.1981089393F, 0.1996840117F, 0.2012639560F, + 0.2028487479F, 0.2044383630F, 0.2060327766F, 0.2076319642F, + 0.2092359007F, 0.2108445614F, 0.2124579211F, 0.2140759545F, + 0.2156986364F, 0.2173259411F, 0.2189578432F, 0.2205943168F, + 0.2222353361F, 0.2238808751F, 0.2255309076F, 0.2271854073F, + 0.2288443480F, 0.2305077030F, 0.2321754457F, 0.2338475493F, + 0.2355239869F, 0.2372047315F, 0.2388897560F, 0.2405790329F, + 0.2422725350F, 0.2439702347F, 0.2456721043F, 0.2473781159F, + 0.2490882418F, 0.2508024539F, 0.2525207240F, 0.2542430237F, + 0.2559693248F, 0.2576995986F, 0.2594338166F, 0.2611719498F, + 0.2629139695F, 0.2646598466F, 0.2664095520F, 0.2681630564F, + 0.2699203304F, 0.2716813445F, 0.2734460691F, 0.2752144744F, + 0.2769865307F, 0.2787622079F, 0.2805414760F, 0.2823243047F, + 0.2841106637F, 0.2859005227F, 0.2876938509F, 0.2894906179F, + 0.2912907928F, 0.2930943447F, 0.2949012426F, 0.2967114554F, + 0.2985249520F, 0.3003417009F, 0.3021616708F, 0.3039848301F, + 0.3058111471F, 0.3076405901F, 0.3094731273F, 0.3113087266F, + 0.3131473560F, 0.3149889833F, 0.3168335762F, 0.3186811024F, + 0.3205315294F, 0.3223848245F, 0.3242409552F, 0.3260998886F, + 0.3279615918F, 0.3298260319F, 0.3316931758F, 0.3335629903F, + 0.3354354423F, 0.3373104982F, 0.3391881247F, 0.3410682882F, + 0.3429509551F, 0.3448360917F, 0.3467236642F, 0.3486136387F, + 0.3505059811F, 0.3524006575F, 0.3542976336F, 0.3561968753F, + 0.3580983482F, 0.3600020179F, 0.3619078499F, 0.3638158096F, + 0.3657258625F, 0.3676379737F, 0.3695521086F, 0.3714682321F, + 0.3733863094F, 0.3753063055F, 0.3772281852F, 0.3791519134F, + 0.3810774548F, 0.3830047742F, 0.3849338362F, 0.3868646053F, + 0.3887970459F, 0.3907311227F, 0.3926667998F, 0.3946040417F, + 0.3965428125F, 0.3984830765F, 0.4004247978F, 0.4023679403F, + 0.4043124683F, 0.4062583455F, 0.4082055359F, 0.4101540034F, + 0.4121037117F, 0.4140546246F, 0.4160067058F, 0.4179599190F, + 0.4199142277F, 0.4218695956F, 0.4238259861F, 0.4257833627F, + 0.4277416888F, 0.4297009279F, 0.4316610433F, 0.4336219983F, + 0.4355837562F, 0.4375462803F, 0.4395095337F, 0.4414734797F, + 0.4434380815F, 0.4454033021F, 0.4473691046F, 0.4493354521F, + 0.4513023078F, 0.4532696345F, 0.4552373954F, 0.4572055533F, + 0.4591740713F, 0.4611429123F, 0.4631120393F, 0.4650814151F, + 0.4670510028F, 0.4690207650F, 0.4709906649F, 0.4729606651F, + 0.4749307287F, 0.4769008185F, 0.4788708972F, 0.4808409279F, + 0.4828108732F, 0.4847806962F, 0.4867503597F, 0.4887198264F, + 0.4906890593F, 0.4926580213F, 0.4946266753F, 0.4965949840F, + 0.4985629105F, 0.5005304176F, 0.5024974683F, 0.5044640255F, + 0.5064300522F, 0.5083955114F, 0.5103603659F, 0.5123245790F, + 0.5142881136F, 0.5162509328F, 0.5182129997F, 0.5201742774F, + 0.5221347290F, 0.5240943178F, 0.5260530070F, 0.5280107598F, + 0.5299675395F, 0.5319233095F, 0.5338780330F, 0.5358316736F, + 0.5377841946F, 0.5397355596F, 0.5416857320F, 0.5436346755F, + 0.5455823538F, 0.5475287304F, 0.5494737691F, 0.5514174337F, + 0.5533596881F, 0.5553004962F, 0.5572398218F, 0.5591776291F, + 0.5611138821F, 0.5630485449F, 0.5649815818F, 0.5669129570F, + 0.5688426349F, 0.5707705799F, 0.5726967564F, 0.5746211290F, + 0.5765436624F, 0.5784643212F, 0.5803830702F, 0.5822998743F, + 0.5842146984F, 0.5861275076F, 0.5880382669F, 0.5899469416F, + 0.5918534968F, 0.5937578981F, 0.5956601107F, 0.5975601004F, + 0.5994578326F, 0.6013532732F, 0.6032463880F, 0.6051371429F, + 0.6070255039F, 0.6089114372F, 0.6107949090F, 0.6126758856F, + 0.6145543334F, 0.6164302191F, 0.6183035092F, 0.6201741706F, + 0.6220421700F, 0.6239074745F, 0.6257700513F, 0.6276298674F, + 0.6294868903F, 0.6313410873F, 0.6331924262F, 0.6350408745F, + 0.6368864001F, 0.6387289710F, 0.6405685552F, 0.6424051209F, + 0.6442386364F, 0.6460690702F, 0.6478963910F, 0.6497205673F, + 0.6515415682F, 0.6533593625F, 0.6551739194F, 0.6569852082F, + 0.6587931984F, 0.6605978593F, 0.6623991609F, 0.6641970728F, + 0.6659915652F, 0.6677826081F, 0.6695701718F, 0.6713542268F, + 0.6731347437F, 0.6749116932F, 0.6766850461F, 0.6784547736F, + 0.6802208469F, 0.6819832374F, 0.6837419164F, 0.6854968559F, + 0.6872480275F, 0.6889954034F, 0.6907389556F, 0.6924786566F, + 0.6942144788F, 0.6959463950F, 0.6976743780F, 0.6993984008F, + 0.7011184365F, 0.7028344587F, 0.7045464407F, 0.7062543564F, + 0.7079581796F, 0.7096578844F, 0.7113534450F, 0.7130448359F, + 0.7147320316F, 0.7164150070F, 0.7180937371F, 0.7197681970F, + 0.7214383620F, 0.7231042077F, 0.7247657098F, 0.7264228443F, + 0.7280755871F, 0.7297239147F, 0.7313678035F, 0.7330072301F, + 0.7346421715F, 0.7362726046F, 0.7378985069F, 0.7395198556F, + 0.7411366285F, 0.7427488034F, 0.7443563584F, 0.7459592717F, + 0.7475575218F, 0.7491510873F, 0.7507399471F, 0.7523240803F, + 0.7539034661F, 0.7554780839F, 0.7570479136F, 0.7586129349F, + 0.7601731279F, 0.7617284730F, 0.7632789506F, 0.7648245416F, + 0.7663652267F, 0.7679009872F, 0.7694318044F, 0.7709576599F, + 0.7724785354F, 0.7739944130F, 0.7755052749F, 0.7770111035F, + 0.7785118815F, 0.7800075916F, 0.7814982170F, 0.7829837410F, + 0.7844641472F, 0.7859394191F, 0.7874095408F, 0.7888744965F, + 0.7903342706F, 0.7917888476F, 0.7932382124F, 0.7946823501F, + 0.7961212460F, 0.7975548855F, 0.7989832544F, 0.8004063386F, + 0.8018241244F, 0.8032365981F, 0.8046437463F, 0.8060455560F, + 0.8074420141F, 0.8088331080F, 0.8102188253F, 0.8115991536F, + 0.8129740810F, 0.8143435957F, 0.8157076861F, 0.8170663409F, + 0.8184195489F, 0.8197672994F, 0.8211095817F, 0.8224463853F, + 0.8237777001F, 0.8251035161F, 0.8264238235F, 0.8277386129F, + 0.8290478750F, 0.8303516008F, 0.8316497814F, 0.8329424083F, + 0.8342294731F, 0.8355109677F, 0.8367868841F, 0.8380572148F, + 0.8393219523F, 0.8405810893F, 0.8418346190F, 0.8430825345F, + 0.8443248294F, 0.8455614974F, 0.8467925323F, 0.8480179285F, + 0.8492376802F, 0.8504517822F, 0.8516602292F, 0.8528630164F, + 0.8540601391F, 0.8552515928F, 0.8564373733F, 0.8576174766F, + 0.8587918990F, 0.8599606368F, 0.8611236868F, 0.8622810460F, + 0.8634327113F, 0.8645786802F, 0.8657189504F, 0.8668535195F, + 0.8679823857F, 0.8691055472F, 0.8702230025F, 0.8713347503F, + 0.8724407896F, 0.8735411194F, 0.8746357394F, 0.8757246489F, + 0.8768078479F, 0.8778853364F, 0.8789571146F, 0.8800231832F, + 0.8810835427F, 0.8821381942F, 0.8831871387F, 0.8842303777F, + 0.8852679127F, 0.8862997456F, 0.8873258784F, 0.8883463132F, + 0.8893610527F, 0.8903700994F, 0.8913734562F, 0.8923711263F, + 0.8933631129F, 0.8943494196F, 0.8953300500F, 0.8963050083F, + 0.8972742985F, 0.8982379249F, 0.8991958922F, 0.9001482052F, + 0.9010948688F, 0.9020358883F, 0.9029712690F, 0.9039010165F, + 0.9048251367F, 0.9057436357F, 0.9066565195F, 0.9075637946F, + 0.9084654678F, 0.9093615456F, 0.9102520353F, 0.9111369440F, + 0.9120162792F, 0.9128900484F, 0.9137582595F, 0.9146209204F, + 0.9154780394F, 0.9163296248F, 0.9171756853F, 0.9180162296F, + 0.9188512667F, 0.9196808057F, 0.9205048559F, 0.9213234270F, + 0.9221365285F, 0.9229441704F, 0.9237463629F, 0.9245431160F, + 0.9253344404F, 0.9261203465F, 0.9269008453F, 0.9276759477F, + 0.9284456648F, 0.9292100080F, 0.9299689889F, 0.9307226190F, + 0.9314709103F, 0.9322138747F, 0.9329515245F, 0.9336838721F, + 0.9344109300F, 0.9351327108F, 0.9358492275F, 0.9365604931F, + 0.9372665208F, 0.9379673239F, 0.9386629160F, 0.9393533107F, + 0.9400385220F, 0.9407185637F, 0.9413934501F, 0.9420631954F, + 0.9427278141F, 0.9433873208F, 0.9440417304F, 0.9446910576F, + 0.9453353176F, 0.9459745255F, 0.9466086968F, 0.9472378469F, + 0.9478619915F, 0.9484811463F, 0.9490953274F, 0.9497045506F, + 0.9503088323F, 0.9509081888F, 0.9515026365F, 0.9520921921F, + 0.9526768723F, 0.9532566940F, 0.9538316742F, 0.9544018300F, + 0.9549671786F, 0.9555277375F, 0.9560835241F, 0.9566345562F, + 0.9571808513F, 0.9577224275F, 0.9582593027F, 0.9587914949F, + 0.9593190225F, 0.9598419038F, 0.9603601571F, 0.9608738012F, + 0.9613828546F, 0.9618873361F, 0.9623872646F, 0.9628826591F, + 0.9633735388F, 0.9638599227F, 0.9643418303F, 0.9648192808F, + 0.9652922939F, 0.9657608890F, 0.9662250860F, 0.9666849046F, + 0.9671403646F, 0.9675914861F, 0.9680382891F, 0.9684807937F, + 0.9689190202F, 0.9693529890F, 0.9697827203F, 0.9702082347F, + 0.9706295529F, 0.9710466953F, 0.9714596828F, 0.9718685362F, + 0.9722732762F, 0.9726739240F, 0.9730705005F, 0.9734630267F, + 0.9738515239F, 0.9742360134F, 0.9746165163F, 0.9749930540F, + 0.9753656481F, 0.9757343198F, 0.9760990909F, 0.9764599829F, + 0.9768170175F, 0.9771702164F, 0.9775196013F, 0.9778651941F, + 0.9782070167F, 0.9785450909F, 0.9788794388F, 0.9792100824F, + 0.9795370437F, 0.9798603449F, 0.9801800080F, 0.9804960554F, + 0.9808085092F, 0.9811173916F, 0.9814227251F, 0.9817245318F, + 0.9820228343F, 0.9823176549F, 0.9826090160F, 0.9828969402F, + 0.9831814498F, 0.9834625674F, 0.9837403156F, 0.9840147169F, + 0.9842857939F, 0.9845535692F, 0.9848180654F, 0.9850793052F, + 0.9853373113F, 0.9855921062F, 0.9858437127F, 0.9860921535F, + 0.9863374512F, 0.9865796287F, 0.9868187085F, 0.9870547136F, + 0.9872876664F, 0.9875175899F, 0.9877445067F, 0.9879684396F, + 0.9881894112F, 0.9884074444F, 0.9886225619F, 0.9888347863F, + 0.9890441404F, 0.9892506468F, 0.9894543284F, 0.9896552077F, + 0.9898533074F, 0.9900486502F, 0.9902412587F, 0.9904311555F, + 0.9906183633F, 0.9908029045F, 0.9909848019F, 0.9911640779F, + 0.9913407550F, 0.9915148557F, 0.9916864025F, 0.9918554179F, + 0.9920219241F, 0.9921859437F, 0.9923474989F, 0.9925066120F, + 0.9926633054F, 0.9928176012F, 0.9929695218F, 0.9931190891F, + 0.9932663254F, 0.9934112527F, 0.9935538932F, 0.9936942686F, + 0.9938324012F, 0.9939683126F, 0.9941020248F, 0.9942335597F, + 0.9943629388F, 0.9944901841F, 0.9946153170F, 0.9947383593F, + 0.9948593325F, 0.9949782579F, 0.9950951572F, 0.9952100516F, + 0.9953229625F, 0.9954339111F, 0.9955429186F, 0.9956500062F, + 0.9957551948F, 0.9958585056F, 0.9959599593F, 0.9960595769F, + 0.9961573792F, 0.9962533869F, 0.9963476206F, 0.9964401009F, + 0.9965308483F, 0.9966198833F, 0.9967072261F, 0.9967928971F, + 0.9968769164F, 0.9969593041F, 0.9970400804F, 0.9971192651F, + 0.9971968781F, 0.9972729391F, 0.9973474680F, 0.9974204842F, + 0.9974920074F, 0.9975620569F, 0.9976306521F, 0.9976978122F, + 0.9977635565F, 0.9978279039F, 0.9978908736F, 0.9979524842F, + 0.9980127547F, 0.9980717037F, 0.9981293499F, 0.9981857116F, + 0.9982408073F, 0.9982946554F, 0.9983472739F, 0.9983986810F, + 0.9984488947F, 0.9984979328F, 0.9985458132F, 0.9985925534F, + 0.9986381711F, 0.9986826838F, 0.9987261086F, 0.9987684630F, + 0.9988097640F, 0.9988500286F, 0.9988892738F, 0.9989275163F, + 0.9989647727F, 0.9990010597F, 0.9990363938F, 0.9990707911F, + 0.9991042679F, 0.9991368404F, 0.9991685244F, 0.9991993358F, + 0.9992292905F, 0.9992584038F, 0.9992866914F, 0.9993141686F, + 0.9993408506F, 0.9993667526F, 0.9993918895F, 0.9994162761F, + 0.9994399273F, 0.9994628576F, 0.9994850815F, 0.9995066133F, + 0.9995274672F, 0.9995476574F, 0.9995671978F, 0.9995861021F, + 0.9996043841F, 0.9996220573F, 0.9996391352F, 0.9996556310F, + 0.9996715579F, 0.9996869288F, 0.9997017568F, 0.9997160543F, + 0.9997298342F, 0.9997431088F, 0.9997558905F, 0.9997681914F, + 0.9997800236F, 0.9997913990F, 0.9998023292F, 0.9998128261F, + 0.9998229009F, 0.9998325650F, 0.9998418296F, 0.9998507058F, + 0.9998592044F, 0.9998673362F, 0.9998751117F, 0.9998825415F, + 0.9998896358F, 0.9998964047F, 0.9999028584F, 0.9999090066F, + 0.9999148590F, 0.9999204253F, 0.9999257148F, 0.9999307368F, + 0.9999355003F, 0.9999400144F, 0.9999442878F, 0.9999483293F, + 0.9999521472F, 0.9999557499F, 0.9999591457F, 0.9999623426F, + 0.9999653483F, 0.9999681708F, 0.9999708175F, 0.9999732959F, + 0.9999756132F, 0.9999777765F, 0.9999797928F, 0.9999816688F, + 0.9999834113F, 0.9999850266F, 0.9999865211F, 0.9999879009F, + 0.9999891721F, 0.9999903405F, 0.9999914118F, 0.9999923914F, + 0.9999932849F, 0.9999940972F, 0.9999948336F, 0.9999954989F, + 0.9999960978F, 0.9999966349F, 0.9999971146F, 0.9999975411F, + 0.9999979185F, 0.9999982507F, 0.9999985414F, 0.9999987944F, + 0.9999990129F, 0.9999992003F, 0.9999993596F, 0.9999994939F, + 0.9999996059F, 0.9999996981F, 0.9999997732F, 0.9999998333F, + 0.9999998805F, 0.9999999170F, 0.9999999444F, 0.9999999643F, + 0.9999999784F, 0.9999999878F, 0.9999999937F, 0.9999999972F, + 0.9999999990F, 0.9999999997F, 1.0000000000F, 1.0000000000F, +};*/ + +static float vwin4096[2048]; /* = { + 0.0000002310F, 0.0000020791F, 0.0000057754F, 0.0000113197F, + 0.0000187121F, 0.0000279526F, 0.0000390412F, 0.0000519777F, + 0.0000667623F, 0.0000833949F, 0.0001018753F, 0.0001222036F, + 0.0001443798F, 0.0001684037F, 0.0001942754F, 0.0002219947F, + 0.0002515616F, 0.0002829761F, 0.0003162380F, 0.0003513472F, + 0.0003883038F, 0.0004271076F, 0.0004677584F, 0.0005102563F, + 0.0005546011F, 0.0006007928F, 0.0006488311F, 0.0006987160F, + 0.0007504474F, 0.0008040251F, 0.0008594490F, 0.0009167191F, + 0.0009758351F, 0.0010367969F, 0.0010996044F, 0.0011642574F, + 0.0012307558F, 0.0012990994F, 0.0013692880F, 0.0014413216F, + 0.0015151998F, 0.0015909226F, 0.0016684898F, 0.0017479011F, + 0.0018291565F, 0.0019122556F, 0.0019971983F, 0.0020839845F, + 0.0021726138F, 0.0022630861F, 0.0023554012F, 0.0024495588F, + 0.0025455588F, 0.0026434008F, 0.0027430847F, 0.0028446103F, + 0.0029479772F, 0.0030531853F, 0.0031602342F, 0.0032691238F, + 0.0033798538F, 0.0034924239F, 0.0036068338F, 0.0037230833F, + 0.0038411721F, 0.0039610999F, 0.0040828664F, 0.0042064714F, + 0.0043319145F, 0.0044591954F, 0.0045883139F, 0.0047192696F, + 0.0048520622F, 0.0049866914F, 0.0051231569F, 0.0052614583F, + 0.0054015953F, 0.0055435676F, 0.0056873748F, 0.0058330166F, + 0.0059804926F, 0.0061298026F, 0.0062809460F, 0.0064339226F, + 0.0065887320F, 0.0067453738F, 0.0069038476F, 0.0070641531F, + 0.0072262899F, 0.0073902575F, 0.0075560556F, 0.0077236838F, + 0.0078931417F, 0.0080644288F, 0.0082375447F, 0.0084124891F, + 0.0085892615F, 0.0087678614F, 0.0089482885F, 0.0091305422F, + 0.0093146223F, 0.0095005281F, 0.0096882592F, 0.0098778153F, + 0.0100691958F, 0.0102624002F, 0.0104574281F, 0.0106542791F, + 0.0108529525F, 0.0110534480F, 0.0112557651F, 0.0114599032F, + 0.0116658618F, 0.0118736405F, 0.0120832387F, 0.0122946560F, + 0.0125078917F, 0.0127229454F, 0.0129398166F, 0.0131585046F, + 0.0133790090F, 0.0136013292F, 0.0138254647F, 0.0140514149F, + 0.0142791792F, 0.0145087572F, 0.0147401481F, 0.0149733515F, + 0.0152083667F, 0.0154451932F, 0.0156838304F, 0.0159242777F, + 0.0161665345F, 0.0164106001F, 0.0166564741F, 0.0169041557F, + 0.0171536443F, 0.0174049393F, 0.0176580401F, 0.0179129461F, + 0.0181696565F, 0.0184281708F, 0.0186884883F, 0.0189506084F, + 0.0192145303F, 0.0194802535F, 0.0197477772F, 0.0200171008F, + 0.0202882236F, 0.0205611449F, 0.0208358639F, 0.0211123801F, + 0.0213906927F, 0.0216708011F, 0.0219527043F, 0.0222364019F, + 0.0225218930F, 0.0228091769F, 0.0230982529F, 0.0233891203F, + 0.0236817782F, 0.0239762259F, 0.0242724628F, 0.0245704880F, + 0.0248703007F, 0.0251719002F, 0.0254752858F, 0.0257804565F, + 0.0260874117F, 0.0263961506F, 0.0267066722F, 0.0270189760F, + 0.0273330609F, 0.0276489263F, 0.0279665712F, 0.0282859949F, + 0.0286071966F, 0.0289301753F, 0.0292549303F, 0.0295814607F, + 0.0299097656F, 0.0302398442F, 0.0305716957F, 0.0309053191F, + 0.0312407135F, 0.0315778782F, 0.0319168122F, 0.0322575145F, + 0.0325999844F, 0.0329442209F, 0.0332902231F, 0.0336379900F, + 0.0339875208F, 0.0343388146F, 0.0346918703F, 0.0350466871F, + 0.0354032640F, 0.0357616000F, 0.0361216943F, 0.0364835458F, + 0.0368471535F, 0.0372125166F, 0.0375796339F, 0.0379485046F, + 0.0383191276F, 0.0386915020F, 0.0390656267F, 0.0394415008F, + 0.0398191231F, 0.0401984927F, 0.0405796086F, 0.0409624698F, + 0.0413470751F, 0.0417334235F, 0.0421215141F, 0.0425113457F, + 0.0429029172F, 0.0432962277F, 0.0436912760F, 0.0440880610F, + 0.0444865817F, 0.0448868370F, 0.0452888257F, 0.0456925468F, + 0.0460979992F, 0.0465051816F, 0.0469140931F, 0.0473247325F, + 0.0477370986F, 0.0481511902F, 0.0485670064F, 0.0489845458F, + 0.0494038074F, 0.0498247899F, 0.0502474922F, 0.0506719131F, + 0.0510980514F, 0.0515259060F, 0.0519554756F, 0.0523867590F, + 0.0528197550F, 0.0532544624F, 0.0536908800F, 0.0541290066F, + 0.0545688408F, 0.0550103815F, 0.0554536274F, 0.0558985772F, + 0.0563452297F, 0.0567935837F, 0.0572436377F, 0.0576953907F, + 0.0581488412F, 0.0586039880F, 0.0590608297F, 0.0595193651F, + 0.0599795929F, 0.0604415117F, 0.0609051202F, 0.0613704170F, + 0.0618374009F, 0.0623060704F, 0.0627764243F, 0.0632484611F, + 0.0637221795F, 0.0641975781F, 0.0646746555F, 0.0651534104F, + 0.0656338413F, 0.0661159469F, 0.0665997257F, 0.0670851763F, + 0.0675722973F, 0.0680610873F, 0.0685515448F, 0.0690436684F, + 0.0695374567F, 0.0700329081F, 0.0705300213F, 0.0710287947F, + 0.0715292269F, 0.0720313163F, 0.0725350616F, 0.0730404612F, + 0.0735475136F, 0.0740562172F, 0.0745665707F, 0.0750785723F, + 0.0755922207F, 0.0761075143F, 0.0766244515F, 0.0771430307F, + 0.0776632505F, 0.0781851092F, 0.0787086052F, 0.0792337371F, + 0.0797605032F, 0.0802889018F, 0.0808189315F, 0.0813505905F, + 0.0818838773F, 0.0824187903F, 0.0829553277F, 0.0834934881F, + 0.0840332697F, 0.0845746708F, 0.0851176899F, 0.0856623252F, + 0.0862085751F, 0.0867564379F, 0.0873059119F, 0.0878569954F, + 0.0884096867F, 0.0889639840F, 0.0895198858F, 0.0900773902F, + 0.0906364955F, 0.0911972000F, 0.0917595019F, 0.0923233995F, + 0.0928888909F, 0.0934559745F, 0.0940246485F, 0.0945949110F, + 0.0951667604F, 0.0957401946F, 0.0963152121F, 0.0968918109F, + 0.0974699893F, 0.0980497454F, 0.0986310773F, 0.0992139832F, + 0.0997984614F, 0.1003845098F, 0.1009721267F, 0.1015613101F, + 0.1021520582F, 0.1027443692F, 0.1033382410F, 0.1039336718F, + 0.1045306597F, 0.1051292027F, 0.1057292990F, 0.1063309466F, + 0.1069341435F, 0.1075388878F, 0.1081451776F, 0.1087530108F, + 0.1093623856F, 0.1099732998F, 0.1105857516F, 0.1111997389F, + 0.1118152597F, 0.1124323121F, 0.1130508939F, 0.1136710032F, + 0.1142926379F, 0.1149157960F, 0.1155404755F, 0.1161666742F, + 0.1167943901F, 0.1174236211F, 0.1180543652F, 0.1186866202F, + 0.1193203841F, 0.1199556548F, 0.1205924300F, 0.1212307078F, + 0.1218704860F, 0.1225117624F, 0.1231545349F, 0.1237988013F, + 0.1244445596F, 0.1250918074F, 0.1257405427F, 0.1263907632F, + 0.1270424667F, 0.1276956512F, 0.1283503142F, 0.1290064537F, + 0.1296640674F, 0.1303231530F, 0.1309837084F, 0.1316457312F, + 0.1323092193F, 0.1329741703F, 0.1336405820F, 0.1343084520F, + 0.1349777782F, 0.1356485582F, 0.1363207897F, 0.1369944704F, + 0.1376695979F, 0.1383461700F, 0.1390241842F, 0.1397036384F, + 0.1403845300F, 0.1410668567F, 0.1417506162F, 0.1424358061F, + 0.1431224240F, 0.1438104674F, 0.1444999341F, 0.1451908216F, + 0.1458831274F, 0.1465768492F, 0.1472719844F, 0.1479685308F, + 0.1486664857F, 0.1493658468F, 0.1500666115F, 0.1507687775F, + 0.1514723422F, 0.1521773031F, 0.1528836577F, 0.1535914035F, + 0.1543005380F, 0.1550110587F, 0.1557229631F, 0.1564362485F, + 0.1571509124F, 0.1578669524F, 0.1585843657F, 0.1593031499F, + 0.1600233024F, 0.1607448205F, 0.1614677017F, 0.1621919433F, + 0.1629175428F, 0.1636444975F, 0.1643728047F, 0.1651024619F, + 0.1658334665F, 0.1665658156F, 0.1672995067F, 0.1680345371F, + 0.1687709041F, 0.1695086050F, 0.1702476372F, 0.1709879978F, + 0.1717296843F, 0.1724726938F, 0.1732170237F, 0.1739626711F, + 0.1747096335F, 0.1754579079F, 0.1762074916F, 0.1769583819F, + 0.1777105760F, 0.1784640710F, 0.1792188642F, 0.1799749529F, + 0.1807323340F, 0.1814910049F, 0.1822509628F, 0.1830122046F, + 0.1837747277F, 0.1845385292F, 0.1853036062F, 0.1860699558F, + 0.1868375751F, 0.1876064613F, 0.1883766114F, 0.1891480226F, + 0.1899206919F, 0.1906946164F, 0.1914697932F, 0.1922462194F, + 0.1930238919F, 0.1938028079F, 0.1945829643F, 0.1953643583F, + 0.1961469868F, 0.1969308468F, 0.1977159353F, 0.1985022494F, + 0.1992897859F, 0.2000785420F, 0.2008685145F, 0.2016597005F, + 0.2024520968F, 0.2032457005F, 0.2040405084F, 0.2048365175F, + 0.2056337247F, 0.2064321269F, 0.2072317211F, 0.2080325041F, + 0.2088344727F, 0.2096376240F, 0.2104419547F, 0.2112474618F, + 0.2120541420F, 0.2128619923F, 0.2136710094F, 0.2144811902F, + 0.2152925315F, 0.2161050301F, 0.2169186829F, 0.2177334866F, + 0.2185494381F, 0.2193665340F, 0.2201847712F, 0.2210041465F, + 0.2218246565F, 0.2226462981F, 0.2234690680F, 0.2242929629F, + 0.2251179796F, 0.2259441147F, 0.2267713650F, 0.2275997272F, + 0.2284291979F, 0.2292597739F, 0.2300914518F, 0.2309242283F, + 0.2317581001F, 0.2325930638F, 0.2334291160F, 0.2342662534F, + 0.2351044727F, 0.2359437703F, 0.2367841431F, 0.2376255875F, + 0.2384681001F, 0.2393116776F, 0.2401563165F, 0.2410020134F, + 0.2418487649F, 0.2426965675F, 0.2435454178F, 0.2443953122F, + 0.2452462474F, 0.2460982199F, 0.2469512262F, 0.2478052628F, + 0.2486603262F, 0.2495164129F, 0.2503735194F, 0.2512316421F, + 0.2520907776F, 0.2529509222F, 0.2538120726F, 0.2546742250F, + 0.2555373760F, 0.2564015219F, 0.2572666593F, 0.2581327845F, + 0.2589998939F, 0.2598679840F, 0.2607370510F, 0.2616070916F, + 0.2624781019F, 0.2633500783F, 0.2642230173F, 0.2650969152F, + 0.2659717684F, 0.2668475731F, 0.2677243257F, 0.2686020226F, + 0.2694806601F, 0.2703602344F, 0.2712407419F, 0.2721221789F, + 0.2730045417F, 0.2738878265F, 0.2747720297F, 0.2756571474F, + 0.2765431760F, 0.2774301117F, 0.2783179508F, 0.2792066895F, + 0.2800963240F, 0.2809868505F, 0.2818782654F, 0.2827705647F, + 0.2836637447F, 0.2845578016F, 0.2854527315F, 0.2863485307F, + 0.2872451953F, 0.2881427215F, 0.2890411055F, 0.2899403433F, + 0.2908404312F, 0.2917413654F, 0.2926431418F, 0.2935457567F, + 0.2944492061F, 0.2953534863F, 0.2962585932F, 0.2971645230F, + 0.2980712717F, 0.2989788356F, 0.2998872105F, 0.3007963927F, + 0.3017063781F, 0.3026171629F, 0.3035287430F, 0.3044411145F, + 0.3053542736F, 0.3062682161F, 0.3071829381F, 0.3080984356F, + 0.3090147047F, 0.3099317413F, 0.3108495414F, 0.3117681011F, + 0.3126874163F, 0.3136074830F, 0.3145282972F, 0.3154498548F, + 0.3163721517F, 0.3172951841F, 0.3182189477F, 0.3191434385F, + 0.3200686525F, 0.3209945856F, 0.3219212336F, 0.3228485927F, + 0.3237766585F, 0.3247054271F, 0.3256348943F, 0.3265650560F, + 0.3274959081F, 0.3284274465F, 0.3293596671F, 0.3302925657F, + 0.3312261382F, 0.3321603804F, 0.3330952882F, 0.3340308574F, + 0.3349670838F, 0.3359039634F, 0.3368414919F, 0.3377796651F, + 0.3387184789F, 0.3396579290F, 0.3405980113F, 0.3415387216F, + 0.3424800556F, 0.3434220091F, 0.3443645779F, 0.3453077578F, + 0.3462515446F, 0.3471959340F, 0.3481409217F, 0.3490865036F, + 0.3500326754F, 0.3509794328F, 0.3519267715F, 0.3528746873F, + 0.3538231759F, 0.3547722330F, 0.3557218544F, 0.3566720357F, + 0.3576227727F, 0.3585740610F, 0.3595258964F, 0.3604782745F, + 0.3614311910F, 0.3623846417F, 0.3633386221F, 0.3642931280F, + 0.3652481549F, 0.3662036987F, 0.3671597548F, 0.3681163191F, + 0.3690733870F, 0.3700309544F, 0.3709890167F, 0.3719475696F, + 0.3729066089F, 0.3738661299F, 0.3748261285F, 0.3757866002F, + 0.3767475406F, 0.3777089453F, 0.3786708100F, 0.3796331302F, + 0.3805959014F, 0.3815591194F, 0.3825227796F, 0.3834868777F, + 0.3844514093F, 0.3854163698F, 0.3863817549F, 0.3873475601F, + 0.3883137810F, 0.3892804131F, 0.3902474521F, 0.3912148933F, + 0.3921827325F, 0.3931509650F, 0.3941195865F, 0.3950885925F, + 0.3960579785F, 0.3970277400F, 0.3979978725F, 0.3989683716F, + 0.3999392328F, 0.4009104516F, 0.4018820234F, 0.4028539438F, + 0.4038262084F, 0.4047988125F, 0.4057717516F, 0.4067450214F, + 0.4077186172F, 0.4086925345F, 0.4096667688F, 0.4106413155F, + 0.4116161703F, 0.4125913284F, 0.4135667854F, 0.4145425368F, + 0.4155185780F, 0.4164949044F, 0.4174715116F, 0.4184483949F, + 0.4194255498F, 0.4204029718F, 0.4213806563F, 0.4223585987F, + 0.4233367946F, 0.4243152392F, 0.4252939281F, 0.4262728566F, + 0.4272520202F, 0.4282314144F, 0.4292110345F, 0.4301908760F, + 0.4311709343F, 0.4321512047F, 0.4331316828F, 0.4341123639F, + 0.4350932435F, 0.4360743168F, 0.4370555794F, 0.4380370267F, + 0.4390186540F, 0.4400004567F, 0.4409824303F, 0.4419645701F, + 0.4429468716F, 0.4439293300F, 0.4449119409F, 0.4458946996F, + 0.4468776014F, 0.4478606418F, 0.4488438162F, 0.4498271199F, + 0.4508105483F, 0.4517940967F, 0.4527777607F, 0.4537615355F, + 0.4547454165F, 0.4557293991F, 0.4567134786F, 0.4576976505F, + 0.4586819101F, 0.4596662527F, 0.4606506738F, 0.4616351687F, + 0.4626197328F, 0.4636043614F, 0.4645890499F, 0.4655737936F, + 0.4665585880F, 0.4675434284F, 0.4685283101F, 0.4695132286F, + 0.4704981791F, 0.4714831570F, 0.4724681577F, 0.4734531766F, + 0.4744382089F, 0.4754232501F, 0.4764082956F, 0.4773933406F, + 0.4783783806F, 0.4793634108F, 0.4803484267F, 0.4813334237F, + 0.4823183969F, 0.4833033419F, 0.4842882540F, 0.4852731285F, + 0.4862579608F, 0.4872427462F, 0.4882274802F, 0.4892121580F, + 0.4901967751F, 0.4911813267F, 0.4921658083F, 0.4931502151F, + 0.4941345427F, 0.4951187863F, 0.4961029412F, 0.4970870029F, + 0.4980709667F, 0.4990548280F, 0.5000385822F, 0.5010222245F, + 0.5020057505F, 0.5029891553F, 0.5039724345F, 0.5049555834F, + 0.5059385973F, 0.5069214716F, 0.5079042018F, 0.5088867831F, + 0.5098692110F, 0.5108514808F, 0.5118335879F, 0.5128155277F, + 0.5137972956F, 0.5147788869F, 0.5157602971F, 0.5167415215F, + 0.5177225555F, 0.5187033945F, 0.5196840339F, 0.5206644692F, + 0.5216446956F, 0.5226247086F, 0.5236045035F, 0.5245840759F, + 0.5255634211F, 0.5265425344F, 0.5275214114F, 0.5285000474F, + 0.5294784378F, 0.5304565781F, 0.5314344637F, 0.5324120899F, + 0.5333894522F, 0.5343665461F, 0.5353433670F, 0.5363199102F, + 0.5372961713F, 0.5382721457F, 0.5392478287F, 0.5402232159F, + 0.5411983027F, 0.5421730845F, 0.5431475569F, 0.5441217151F, + 0.5450955548F, 0.5460690714F, 0.5470422602F, 0.5480151169F, + 0.5489876368F, 0.5499598155F, 0.5509316484F, 0.5519031310F, + 0.5528742587F, 0.5538450271F, 0.5548154317F, 0.5557854680F, + 0.5567551314F, 0.5577244174F, 0.5586933216F, 0.5596618395F, + 0.5606299665F, 0.5615976983F, 0.5625650302F, 0.5635319580F, + 0.5644984770F, 0.5654645828F, 0.5664302709F, 0.5673955370F, + 0.5683603765F, 0.5693247850F, 0.5702887580F, 0.5712522912F, + 0.5722153800F, 0.5731780200F, 0.5741402069F, 0.5751019362F, + 0.5760632034F, 0.5770240042F, 0.5779843341F, 0.5789441889F, + 0.5799035639F, 0.5808624549F, 0.5818208575F, 0.5827787673F, + 0.5837361800F, 0.5846930910F, 0.5856494961F, 0.5866053910F, + 0.5875607712F, 0.5885156324F, 0.5894699703F, 0.5904237804F, + 0.5913770586F, 0.5923298004F, 0.5932820016F, 0.5942336578F, + 0.5951847646F, 0.5961353179F, 0.5970853132F, 0.5980347464F, + 0.5989836131F, 0.5999319090F, 0.6008796298F, 0.6018267713F, + 0.6027733292F, 0.6037192993F, 0.6046646773F, 0.6056094589F, + 0.6065536400F, 0.6074972162F, 0.6084401833F, 0.6093825372F, + 0.6103242736F, 0.6112653884F, 0.6122058772F, 0.6131457359F, + 0.6140849604F, 0.6150235464F, 0.6159614897F, 0.6168987862F, + 0.6178354318F, 0.6187714223F, 0.6197067535F, 0.6206414213F, + 0.6215754215F, 0.6225087501F, 0.6234414028F, 0.6243733757F, + 0.6253046646F, 0.6262352654F, 0.6271651739F, 0.6280943862F, + 0.6290228982F, 0.6299507057F, 0.6308778048F, 0.6318041913F, + 0.6327298612F, 0.6336548105F, 0.6345790352F, 0.6355025312F, + 0.6364252945F, 0.6373473211F, 0.6382686070F, 0.6391891483F, + 0.6401089409F, 0.6410279808F, 0.6419462642F, 0.6428637869F, + 0.6437805452F, 0.6446965350F, 0.6456117524F, 0.6465261935F, + 0.6474398544F, 0.6483527311F, 0.6492648197F, 0.6501761165F, + 0.6510866174F, 0.6519963186F, 0.6529052162F, 0.6538133064F, + 0.6547205854F, 0.6556270492F, 0.6565326941F, 0.6574375162F, + 0.6583415117F, 0.6592446769F, 0.6601470079F, 0.6610485009F, + 0.6619491521F, 0.6628489578F, 0.6637479143F, 0.6646460177F, + 0.6655432643F, 0.6664396505F, 0.6673351724F, 0.6682298264F, + 0.6691236087F, 0.6700165157F, 0.6709085436F, 0.6717996889F, + 0.6726899478F, 0.6735793167F, 0.6744677918F, 0.6753553697F, + 0.6762420466F, 0.6771278190F, 0.6780126832F, 0.6788966357F, + 0.6797796728F, 0.6806617909F, 0.6815429866F, 0.6824232562F, + 0.6833025961F, 0.6841810030F, 0.6850584731F, 0.6859350031F, + 0.6868105894F, 0.6876852284F, 0.6885589168F, 0.6894316510F, + 0.6903034275F, 0.6911742430F, 0.6920440939F, 0.6929129769F, + 0.6937808884F, 0.6946478251F, 0.6955137837F, 0.6963787606F, + 0.6972427525F, 0.6981057560F, 0.6989677678F, 0.6998287845F, + 0.7006888028F, 0.7015478194F, 0.7024058309F, 0.7032628340F, + 0.7041188254F, 0.7049738019F, 0.7058277601F, 0.7066806969F, + 0.7075326089F, 0.7083834929F, 0.7092333457F, 0.7100821640F, + 0.7109299447F, 0.7117766846F, 0.7126223804F, 0.7134670291F, + 0.7143106273F, 0.7151531721F, 0.7159946602F, 0.7168350885F, + 0.7176744539F, 0.7185127534F, 0.7193499837F, 0.7201861418F, + 0.7210212247F, 0.7218552293F, 0.7226881526F, 0.7235199914F, + 0.7243507428F, 0.7251804039F, 0.7260089715F, 0.7268364426F, + 0.7276628144F, 0.7284880839F, 0.7293122481F, 0.7301353040F, + 0.7309572487F, 0.7317780794F, 0.7325977930F, 0.7334163868F, + 0.7342338579F, 0.7350502033F, 0.7358654202F, 0.7366795059F, + 0.7374924573F, 0.7383042718F, 0.7391149465F, 0.7399244787F, + 0.7407328655F, 0.7415401041F, 0.7423461920F, 0.7431511261F, + 0.7439549040F, 0.7447575227F, 0.7455589797F, 0.7463592723F, + 0.7471583976F, 0.7479563532F, 0.7487531363F, 0.7495487443F, + 0.7503431745F, 0.7511364244F, 0.7519284913F, 0.7527193726F, + 0.7535090658F, 0.7542975683F, 0.7550848776F, 0.7558709910F, + 0.7566559062F, 0.7574396205F, 0.7582221314F, 0.7590034366F, + 0.7597835334F, 0.7605624194F, 0.7613400923F, 0.7621165495F, + 0.7628917886F, 0.7636658072F, 0.7644386030F, 0.7652101735F, + 0.7659805164F, 0.7667496292F, 0.7675175098F, 0.7682841556F, + 0.7690495645F, 0.7698137341F, 0.7705766622F, 0.7713383463F, + 0.7720987844F, 0.7728579741F, 0.7736159132F, 0.7743725994F, + 0.7751280306F, 0.7758822046F, 0.7766351192F, 0.7773867722F, + 0.7781371614F, 0.7788862848F, 0.7796341401F, 0.7803807253F, + 0.7811260383F, 0.7818700769F, 0.7826128392F, 0.7833543230F, + 0.7840945263F, 0.7848334471F, 0.7855710833F, 0.7863074330F, + 0.7870424941F, 0.7877762647F, 0.7885087428F, 0.7892399264F, + 0.7899698137F, 0.7906984026F, 0.7914256914F, 0.7921516780F, + 0.7928763607F, 0.7935997375F, 0.7943218065F, 0.7950425661F, + 0.7957620142F, 0.7964801492F, 0.7971969692F, 0.7979124724F, + 0.7986266570F, 0.7993395214F, 0.8000510638F, 0.8007612823F, + 0.8014701754F, 0.8021777413F, 0.8028839784F, 0.8035888849F, + 0.8042924592F, 0.8049946997F, 0.8056956048F, 0.8063951727F, + 0.8070934020F, 0.8077902910F, 0.8084858381F, 0.8091800419F, + 0.8098729007F, 0.8105644130F, 0.8112545774F, 0.8119433922F, + 0.8126308561F, 0.8133169676F, 0.8140017251F, 0.8146851272F, + 0.8153671726F, 0.8160478598F, 0.8167271874F, 0.8174051539F, + 0.8180817582F, 0.8187569986F, 0.8194308741F, 0.8201033831F, + 0.8207745244F, 0.8214442966F, 0.8221126986F, 0.8227797290F, + 0.8234453865F, 0.8241096700F, 0.8247725781F, 0.8254341097F, + 0.8260942636F, 0.8267530385F, 0.8274104334F, 0.8280664470F, + 0.8287210782F, 0.8293743259F, 0.8300261889F, 0.8306766662F, + 0.8313257566F, 0.8319734591F, 0.8326197727F, 0.8332646963F, + 0.8339082288F, 0.8345503692F, 0.8351911167F, 0.8358304700F, + 0.8364684284F, 0.8371049907F, 0.8377401562F, 0.8383739238F, + 0.8390062927F, 0.8396372618F, 0.8402668305F, 0.8408949977F, + 0.8415217626F, 0.8421471245F, 0.8427710823F, 0.8433936354F, + 0.8440147830F, 0.8446345242F, 0.8452528582F, 0.8458697844F, + 0.8464853020F, 0.8470994102F, 0.8477121084F, 0.8483233958F, + 0.8489332718F, 0.8495417356F, 0.8501487866F, 0.8507544243F, + 0.8513586479F, 0.8519614568F, 0.8525628505F, 0.8531628283F, + 0.8537613897F, 0.8543585341F, 0.8549542611F, 0.8555485699F, + 0.8561414603F, 0.8567329315F, 0.8573229832F, 0.8579116149F, + 0.8584988262F, 0.8590846165F, 0.8596689855F, 0.8602519327F, + 0.8608334577F, 0.8614135603F, 0.8619922399F, 0.8625694962F, + 0.8631453289F, 0.8637197377F, 0.8642927222F, 0.8648642821F, + 0.8654344172F, 0.8660031272F, 0.8665704118F, 0.8671362708F, + 0.8677007039F, 0.8682637109F, 0.8688252917F, 0.8693854460F, + 0.8699441737F, 0.8705014745F, 0.8710573485F, 0.8716117953F, + 0.8721648150F, 0.8727164073F, 0.8732665723F, 0.8738153098F, + 0.8743626197F, 0.8749085021F, 0.8754529569F, 0.8759959840F, + 0.8765375835F, 0.8770777553F, 0.8776164996F, 0.8781538162F, + 0.8786897054F, 0.8792241670F, 0.8797572013F, 0.8802888082F, + 0.8808189880F, 0.8813477407F, 0.8818750664F, 0.8824009653F, + 0.8829254375F, 0.8834484833F, 0.8839701028F, 0.8844902961F, + 0.8850090636F, 0.8855264054F, 0.8860423218F, 0.8865568131F, + 0.8870698794F, 0.8875815212F, 0.8880917386F, 0.8886005319F, + 0.8891079016F, 0.8896138479F, 0.8901183712F, 0.8906214719F, + 0.8911231503F, 0.8916234067F, 0.8921222417F, 0.8926196556F, + 0.8931156489F, 0.8936102219F, 0.8941033752F, 0.8945951092F, + 0.8950854244F, 0.8955743212F, 0.8960618003F, 0.8965478621F, + 0.8970325071F, 0.8975157359F, 0.8979975490F, 0.8984779471F, + 0.8989569307F, 0.8994345004F, 0.8999106568F, 0.9003854005F, + 0.9008587323F, 0.9013306526F, 0.9018011623F, 0.9022702619F, + 0.9027379521F, 0.9032042337F, 0.9036691074F, 0.9041325739F, + 0.9045946339F, 0.9050552882F, 0.9055145376F, 0.9059723828F, + 0.9064288246F, 0.9068838638F, 0.9073375013F, 0.9077897379F, + 0.9082405743F, 0.9086900115F, 0.9091380503F, 0.9095846917F, + 0.9100299364F, 0.9104737854F, 0.9109162397F, 0.9113573001F, + 0.9117969675F, 0.9122352430F, 0.9126721275F, 0.9131076219F, + 0.9135417273F, 0.9139744447F, 0.9144057750F, 0.9148357194F, + 0.9152642787F, 0.9156914542F, 0.9161172468F, 0.9165416576F, + 0.9169646877F, 0.9173863382F, 0.9178066102F, 0.9182255048F, + 0.9186430232F, 0.9190591665F, 0.9194739359F, 0.9198873324F, + 0.9202993574F, 0.9207100120F, 0.9211192973F, 0.9215272147F, + 0.9219337653F, 0.9223389504F, 0.9227427713F, 0.9231452290F, + 0.9235463251F, 0.9239460607F, 0.9243444371F, 0.9247414557F, + 0.9251371177F, 0.9255314245F, 0.9259243774F, 0.9263159778F, + 0.9267062270F, 0.9270951264F, 0.9274826774F, 0.9278688814F, + 0.9282537398F, 0.9286372540F, 0.9290194254F, 0.9294002555F, + 0.9297797458F, 0.9301578976F, 0.9305347125F, 0.9309101919F, + 0.9312843373F, 0.9316571503F, 0.9320286323F, 0.9323987849F, + 0.9327676097F, 0.9331351080F, 0.9335012816F, 0.9338661320F, + 0.9342296607F, 0.9345918694F, 0.9349527596F, 0.9353123330F, + 0.9356705911F, 0.9360275357F, 0.9363831683F, 0.9367374905F, + 0.9370905042F, 0.9374422108F, 0.9377926122F, 0.9381417099F, + 0.9384895057F, 0.9388360014F, 0.9391811985F, 0.9395250989F, + 0.9398677043F, 0.9402090165F, 0.9405490371F, 0.9408877680F, + 0.9412252110F, 0.9415613678F, 0.9418962402F, 0.9422298301F, + 0.9425621392F, 0.9428931695F, 0.9432229226F, 0.9435514005F, + 0.9438786050F, 0.9442045381F, 0.9445292014F, 0.9448525971F, + 0.9451747268F, 0.9454955926F, 0.9458151963F, 0.9461335399F, + 0.9464506253F, 0.9467664545F, 0.9470810293F, 0.9473943517F, + 0.9477064238F, 0.9480172474F, 0.9483268246F, 0.9486351573F, + 0.9489422475F, 0.9492480973F, 0.9495527087F, 0.9498560837F, + 0.9501582243F, 0.9504591325F, 0.9507588105F, 0.9510572603F, + 0.9513544839F, 0.9516504834F, 0.9519452609F, 0.9522388186F, + 0.9525311584F, 0.9528222826F, 0.9531121932F, 0.9534008923F, + 0.9536883821F, 0.9539746647F, 0.9542597424F, 0.9545436171F, + 0.9548262912F, 0.9551077667F, 0.9553880459F, 0.9556671309F, + 0.9559450239F, 0.9562217272F, 0.9564972429F, 0.9567715733F, + 0.9570447206F, 0.9573166871F, 0.9575874749F, 0.9578570863F, + 0.9581255236F, 0.9583927890F, 0.9586588849F, 0.9589238134F, + 0.9591875769F, 0.9594501777F, 0.9597116180F, 0.9599719003F, + 0.9602310267F, 0.9604889995F, 0.9607458213F, 0.9610014942F, + 0.9612560206F, 0.9615094028F, 0.9617616433F, 0.9620127443F, + 0.9622627083F, 0.9625115376F, 0.9627592345F, 0.9630058016F, + 0.9632512411F, 0.9634955555F, 0.9637387471F, 0.9639808185F, + 0.9642217720F, 0.9644616100F, 0.9647003349F, 0.9649379493F, + 0.9651744556F, 0.9654098561F, 0.9656441534F, 0.9658773499F, + 0.9661094480F, 0.9663404504F, 0.9665703593F, 0.9667991774F, + 0.9670269071F, 0.9672535509F, 0.9674791114F, 0.9677035909F, + 0.9679269921F, 0.9681493174F, 0.9683705694F, 0.9685907506F, + 0.9688098636F, 0.9690279108F, 0.9692448948F, 0.9694608182F, + 0.9696756836F, 0.9698894934F, 0.9701022503F, 0.9703139569F, + 0.9705246156F, 0.9707342291F, 0.9709428000F, 0.9711503309F, + 0.9713568243F, 0.9715622829F, 0.9717667093F, 0.9719701060F, + 0.9721724757F, 0.9723738210F, 0.9725741446F, 0.9727734490F, + 0.9729717369F, 0.9731690109F, 0.9733652737F, 0.9735605279F, + 0.9737547762F, 0.9739480212F, 0.9741402656F, 0.9743315120F, + 0.9745217631F, 0.9747110216F, 0.9748992901F, 0.9750865714F, + 0.9752728681F, 0.9754581829F, 0.9756425184F, 0.9758258775F, + 0.9760082627F, 0.9761896768F, 0.9763701224F, 0.9765496024F, + 0.9767281193F, 0.9769056760F, 0.9770822751F, 0.9772579193F, + 0.9774326114F, 0.9776063542F, 0.9777791502F, 0.9779510023F, + 0.9781219133F, 0.9782918858F, 0.9784609226F, 0.9786290264F, + 0.9787962000F, 0.9789624461F, 0.9791277676F, 0.9792921671F, + 0.9794556474F, 0.9796182113F, 0.9797798615F, 0.9799406009F, + 0.9801004321F, 0.9802593580F, 0.9804173813F, 0.9805745049F, + 0.9807307314F, 0.9808860637F, 0.9810405046F, 0.9811940568F, + 0.9813467232F, 0.9814985065F, 0.9816494095F, 0.9817994351F, + 0.9819485860F, 0.9820968650F, 0.9822442750F, 0.9823908186F, + 0.9825364988F, 0.9826813184F, 0.9828252801F, 0.9829683868F, + 0.9831106413F, 0.9832520463F, 0.9833926048F, 0.9835323195F, + 0.9836711932F, 0.9838092288F, 0.9839464291F, 0.9840827969F, + 0.9842183351F, 0.9843530464F, 0.9844869337F, 0.9846199998F, + 0.9847522475F, 0.9848836798F, 0.9850142993F, 0.9851441090F, + 0.9852731117F, 0.9854013101F, 0.9855287073F, 0.9856553058F, + 0.9857811087F, 0.9859061188F, 0.9860303388F, 0.9861537717F, + 0.9862764202F, 0.9863982872F, 0.9865193756F, 0.9866396882F, + 0.9867592277F, 0.9868779972F, 0.9869959993F, 0.9871132370F, + 0.9872297131F, 0.9873454304F, 0.9874603918F, 0.9875746001F, + 0.9876880581F, 0.9878007688F, 0.9879127348F, 0.9880239592F, + 0.9881344447F, 0.9882441941F, 0.9883532104F, 0.9884614962F, + 0.9885690546F, 0.9886758883F, 0.9887820001F, 0.9888873930F, + 0.9889920697F, 0.9890960331F, 0.9891992859F, 0.9893018312F, + 0.9894036716F, 0.9895048100F, 0.9896052493F, 0.9897049923F, + 0.9898040418F, 0.9899024006F, 0.9900000717F, 0.9900970577F, + 0.9901933616F, 0.9902889862F, 0.9903839343F, 0.9904782087F, + 0.9905718122F, 0.9906647477F, 0.9907570180F, 0.9908486259F, + 0.9909395742F, 0.9910298658F, 0.9911195034F, 0.9912084899F, + 0.9912968281F, 0.9913845208F, 0.9914715708F, 0.9915579810F, + 0.9916437540F, 0.9917288928F, 0.9918134001F, 0.9918972788F, + 0.9919805316F, 0.9920631613F, 0.9921451707F, 0.9922265626F, + 0.9923073399F, 0.9923875052F, 0.9924670615F, 0.9925460114F, + 0.9926243577F, 0.9927021033F, 0.9927792508F, 0.9928558032F, + 0.9929317631F, 0.9930071333F, 0.9930819167F, 0.9931561158F, + 0.9932297337F, 0.9933027728F, 0.9933752362F, 0.9934471264F, + 0.9935184462F, 0.9935891985F, 0.9936593859F, 0.9937290112F, + 0.9937980771F, 0.9938665864F, 0.9939345418F, 0.9940019460F, + 0.9940688018F, 0.9941351118F, 0.9942008789F, 0.9942661057F, + 0.9943307950F, 0.9943949494F, 0.9944585717F, 0.9945216645F, + 0.9945842307F, 0.9946462728F, 0.9947077936F, 0.9947687957F, + 0.9948292820F, 0.9948892550F, 0.9949487174F, 0.9950076719F, + 0.9950661212F, 0.9951240679F, 0.9951815148F, 0.9952384645F, + 0.9952949196F, 0.9953508828F, 0.9954063568F, 0.9954613442F, + 0.9955158476F, 0.9955698697F, 0.9956234132F, 0.9956764806F, + 0.9957290746F, 0.9957811978F, 0.9958328528F, 0.9958840423F, + 0.9959347688F, 0.9959850351F, 0.9960348435F, 0.9960841969F, + 0.9961330977F, 0.9961815486F, 0.9962295521F, 0.9962771108F, + 0.9963242274F, 0.9963709043F, 0.9964171441F, 0.9964629494F, + 0.9965083228F, 0.9965532668F, 0.9965977840F, 0.9966418768F, + 0.9966855479F, 0.9967287998F, 0.9967716350F, 0.9968140559F, + 0.9968560653F, 0.9968976655F, 0.9969388591F, 0.9969796485F, + 0.9970200363F, 0.9970600250F, 0.9970996170F, 0.9971388149F, + 0.9971776211F, 0.9972160380F, 0.9972540683F, 0.9972917142F, + 0.9973289783F, 0.9973658631F, 0.9974023709F, 0.9974385042F, + 0.9974742655F, 0.9975096571F, 0.9975446816F, 0.9975793413F, + 0.9976136386F, 0.9976475759F, 0.9976811557F, 0.9977143803F, + 0.9977472521F, 0.9977797736F, 0.9978119470F, 0.9978437748F, + 0.9978752593F, 0.9979064029F, 0.9979372079F, 0.9979676768F, + 0.9979978117F, 0.9980276151F, 0.9980570893F, 0.9980862367F, + 0.9981150595F, 0.9981435600F, 0.9981717406F, 0.9981996035F, + 0.9982271511F, 0.9982543856F, 0.9982813093F, 0.9983079246F, + 0.9983342336F, 0.9983602386F, 0.9983859418F, 0.9984113456F, + 0.9984364522F, 0.9984612638F, 0.9984857825F, 0.9985100108F, + 0.9985339507F, 0.9985576044F, 0.9985809743F, 0.9986040624F, + 0.9986268710F, 0.9986494022F, 0.9986716583F, 0.9986936413F, + 0.9987153535F, 0.9987367969F, 0.9987579738F, 0.9987788864F, + 0.9987995366F, 0.9988199267F, 0.9988400587F, 0.9988599348F, + 0.9988795572F, 0.9988989278F, 0.9989180487F, 0.9989369222F, + 0.9989555501F, 0.9989739347F, 0.9989920780F, 0.9990099820F, + 0.9990276487F, 0.9990450803F, 0.9990622787F, 0.9990792460F, + 0.9990959841F, 0.9991124952F, 0.9991287812F, 0.9991448440F, + 0.9991606858F, 0.9991763084F, 0.9991917139F, 0.9992069042F, + 0.9992218813F, 0.9992366471F, 0.9992512035F, 0.9992655525F, + 0.9992796961F, 0.9992936361F, 0.9993073744F, 0.9993209131F, + 0.9993342538F, 0.9993473987F, 0.9993603494F, 0.9993731080F, + 0.9993856762F, 0.9993980559F, 0.9994102490F, 0.9994222573F, + 0.9994340827F, 0.9994457269F, 0.9994571918F, 0.9994684793F, + 0.9994795910F, 0.9994905288F, 0.9995012945F, 0.9995118898F, + 0.9995223165F, 0.9995325765F, 0.9995426713F, 0.9995526029F, + 0.9995623728F, 0.9995719829F, 0.9995814349F, 0.9995907304F, + 0.9995998712F, 0.9996088590F, 0.9996176954F, 0.9996263821F, + 0.9996349208F, 0.9996433132F, 0.9996515609F, 0.9996596656F, + 0.9996676288F, 0.9996754522F, 0.9996831375F, 0.9996906862F, + 0.9996981000F, 0.9997053804F, 0.9997125290F, 0.9997195474F, + 0.9997264371F, 0.9997331998F, 0.9997398369F, 0.9997463500F, + 0.9997527406F, 0.9997590103F, 0.9997651606F, 0.9997711930F, + 0.9997771089F, 0.9997829098F, 0.9997885973F, 0.9997941728F, + 0.9997996378F, 0.9998049936F, 0.9998102419F, 0.9998153839F, + 0.9998204211F, 0.9998253550F, 0.9998301868F, 0.9998349182F, + 0.9998395503F, 0.9998440847F, 0.9998485226F, 0.9998528654F, + 0.9998571146F, 0.9998612713F, 0.9998653370F, 0.9998693130F, + 0.9998732007F, 0.9998770012F, 0.9998807159F, 0.9998843461F, + 0.9998878931F, 0.9998913581F, 0.9998947424F, 0.9998980473F, + 0.9999012740F, 0.9999044237F, 0.9999074976F, 0.9999104971F, + 0.9999134231F, 0.9999162771F, 0.9999190601F, 0.9999217733F, + 0.9999244179F, 0.9999269950F, 0.9999295058F, 0.9999319515F, + 0.9999343332F, 0.9999366519F, 0.9999389088F, 0.9999411050F, + 0.9999432416F, 0.9999453196F, 0.9999473402F, 0.9999493044F, + 0.9999512132F, 0.9999530677F, 0.9999548690F, 0.9999566180F, + 0.9999583157F, 0.9999599633F, 0.9999615616F, 0.9999631116F, + 0.9999646144F, 0.9999660709F, 0.9999674820F, 0.9999688487F, + 0.9999701719F, 0.9999714526F, 0.9999726917F, 0.9999738900F, + 0.9999750486F, 0.9999761682F, 0.9999772497F, 0.9999782941F, + 0.9999793021F, 0.9999802747F, 0.9999812126F, 0.9999821167F, + 0.9999829878F, 0.9999838268F, 0.9999846343F, 0.9999854113F, + 0.9999861584F, 0.9999868765F, 0.9999875664F, 0.9999882287F, + 0.9999888642F, 0.9999894736F, 0.9999900577F, 0.9999906172F, + 0.9999911528F, 0.9999916651F, 0.9999921548F, 0.9999926227F, + 0.9999930693F, 0.9999934954F, 0.9999939015F, 0.9999942883F, + 0.9999946564F, 0.9999950064F, 0.9999953390F, 0.9999956547F, + 0.9999959541F, 0.9999962377F, 0.9999965062F, 0.9999967601F, + 0.9999969998F, 0.9999972260F, 0.9999974392F, 0.9999976399F, + 0.9999978285F, 0.9999980056F, 0.9999981716F, 0.9999983271F, + 0.9999984724F, 0.9999986081F, 0.9999987345F, 0.9999988521F, + 0.9999989613F, 0.9999990625F, 0.9999991562F, 0.9999992426F, + 0.9999993223F, 0.9999993954F, 0.9999994625F, 0.9999995239F, + 0.9999995798F, 0.9999996307F, 0.9999996768F, 0.9999997184F, + 0.9999997559F, 0.9999997895F, 0.9999998195F, 0.9999998462F, + 0.9999998698F, 0.9999998906F, 0.9999999088F, 0.9999999246F, + 0.9999999383F, 0.9999999500F, 0.9999999600F, 0.9999999684F, + 0.9999999754F, 0.9999999811F, 0.9999999858F, 0.9999999896F, + 0.9999999925F, 0.9999999948F, 0.9999999965F, 0.9999999978F, + 0.9999999986F, 0.9999999992F, 0.9999999996F, 0.9999999998F, + 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, +};*/ + +static float vwin8192[4096]; /* = { + 0.0000000578F, 0.0000005198F, 0.0000014438F, 0.0000028299F, + 0.0000046780F, 0.0000069882F, 0.0000097604F, 0.0000129945F, + 0.0000166908F, 0.0000208490F, 0.0000254692F, 0.0000305515F, + 0.0000360958F, 0.0000421021F, 0.0000485704F, 0.0000555006F, + 0.0000628929F, 0.0000707472F, 0.0000790635F, 0.0000878417F, + 0.0000970820F, 0.0001067842F, 0.0001169483F, 0.0001275744F, + 0.0001386625F, 0.0001502126F, 0.0001622245F, 0.0001746984F, + 0.0001876343F, 0.0002010320F, 0.0002148917F, 0.0002292132F, + 0.0002439967F, 0.0002592421F, 0.0002749493F, 0.0002911184F, + 0.0003077493F, 0.0003248421F, 0.0003423967F, 0.0003604132F, + 0.0003788915F, 0.0003978316F, 0.0004172335F, 0.0004370971F, + 0.0004574226F, 0.0004782098F, 0.0004994587F, 0.0005211694F, + 0.0005433418F, 0.0005659759F, 0.0005890717F, 0.0006126292F, + 0.0006366484F, 0.0006611292F, 0.0006860716F, 0.0007114757F, + 0.0007373414F, 0.0007636687F, 0.0007904576F, 0.0008177080F, + 0.0008454200F, 0.0008735935F, 0.0009022285F, 0.0009313250F, + 0.0009608830F, 0.0009909025F, 0.0010213834F, 0.0010523257F, + 0.0010837295F, 0.0011155946F, 0.0011479211F, 0.0011807090F, + 0.0012139582F, 0.0012476687F, 0.0012818405F, 0.0013164736F, + 0.0013515679F, 0.0013871235F, 0.0014231402F, 0.0014596182F, + 0.0014965573F, 0.0015339576F, 0.0015718190F, 0.0016101415F, + 0.0016489251F, 0.0016881698F, 0.0017278754F, 0.0017680421F, + 0.0018086698F, 0.0018497584F, 0.0018913080F, 0.0019333185F, + 0.0019757898F, 0.0020187221F, 0.0020621151F, 0.0021059690F, + 0.0021502837F, 0.0021950591F, 0.0022402953F, 0.0022859921F, + 0.0023321497F, 0.0023787679F, 0.0024258467F, 0.0024733861F, + 0.0025213861F, 0.0025698466F, 0.0026187676F, 0.0026681491F, + 0.0027179911F, 0.0027682935F, 0.0028190562F, 0.0028702794F, + 0.0029219628F, 0.0029741066F, 0.0030267107F, 0.0030797749F, + 0.0031332994F, 0.0031872841F, 0.0032417289F, 0.0032966338F, + 0.0033519988F, 0.0034078238F, 0.0034641089F, 0.0035208539F, + 0.0035780589F, 0.0036357237F, 0.0036938485F, 0.0037524331F, + 0.0038114775F, 0.0038709817F, 0.0039309456F, 0.0039913692F, + 0.0040522524F, 0.0041135953F, 0.0041753978F, 0.0042376599F, + 0.0043003814F, 0.0043635624F, 0.0044272029F, 0.0044913028F, + 0.0045558620F, 0.0046208806F, 0.0046863585F, 0.0047522955F, + 0.0048186919F, 0.0048855473F, 0.0049528619F, 0.0050206356F, + 0.0050888684F, 0.0051575601F, 0.0052267108F, 0.0052963204F, + 0.0053663890F, 0.0054369163F, 0.0055079025F, 0.0055793474F, + 0.0056512510F, 0.0057236133F, 0.0057964342F, 0.0058697137F, + 0.0059434517F, 0.0060176482F, 0.0060923032F, 0.0061674166F, + 0.0062429883F, 0.0063190183F, 0.0063955066F, 0.0064724532F, + 0.0065498579F, 0.0066277207F, 0.0067060416F, 0.0067848205F, + 0.0068640575F, 0.0069437523F, 0.0070239051F, 0.0071045157F, + 0.0071855840F, 0.0072671102F, 0.0073490940F, 0.0074315355F, + 0.0075144345F, 0.0075977911F, 0.0076816052F, 0.0077658768F, + 0.0078506057F, 0.0079357920F, 0.0080214355F, 0.0081075363F, + 0.0081940943F, 0.0082811094F, 0.0083685816F, 0.0084565108F, + 0.0085448970F, 0.0086337401F, 0.0087230401F, 0.0088127969F, + 0.0089030104F, 0.0089936807F, 0.0090848076F, 0.0091763911F, + 0.0092684311F, 0.0093609276F, 0.0094538805F, 0.0095472898F, + 0.0096411554F, 0.0097354772F, 0.0098302552F, 0.0099254894F, + 0.0100211796F, 0.0101173259F, 0.0102139281F, 0.0103109863F, + 0.0104085002F, 0.0105064700F, 0.0106048955F, 0.0107037766F, + 0.0108031133F, 0.0109029056F, 0.0110031534F, 0.0111038565F, + 0.0112050151F, 0.0113066289F, 0.0114086980F, 0.0115112222F, + 0.0116142015F, 0.0117176359F, 0.0118215252F, 0.0119258695F, + 0.0120306686F, 0.0121359225F, 0.0122416312F, 0.0123477944F, + 0.0124544123F, 0.0125614847F, 0.0126690116F, 0.0127769928F, + 0.0128854284F, 0.0129943182F, 0.0131036623F, 0.0132134604F, + 0.0133237126F, 0.0134344188F, 0.0135455790F, 0.0136571929F, + 0.0137692607F, 0.0138817821F, 0.0139947572F, 0.0141081859F, + 0.0142220681F, 0.0143364037F, 0.0144511927F, 0.0145664350F, + 0.0146821304F, 0.0147982791F, 0.0149148808F, 0.0150319355F, + 0.0151494431F, 0.0152674036F, 0.0153858168F, 0.0155046828F, + 0.0156240014F, 0.0157437726F, 0.0158639962F, 0.0159846723F, + 0.0161058007F, 0.0162273814F, 0.0163494142F, 0.0164718991F, + 0.0165948361F, 0.0167182250F, 0.0168420658F, 0.0169663584F, + 0.0170911027F, 0.0172162987F, 0.0173419462F, 0.0174680452F, + 0.0175945956F, 0.0177215974F, 0.0178490504F, 0.0179769545F, + 0.0181053098F, 0.0182341160F, 0.0183633732F, 0.0184930812F, + 0.0186232399F, 0.0187538494F, 0.0188849094F, 0.0190164200F, + 0.0191483809F, 0.0192807923F, 0.0194136539F, 0.0195469656F, + 0.0196807275F, 0.0198149394F, 0.0199496012F, 0.0200847128F, + 0.0202202742F, 0.0203562853F, 0.0204927460F, 0.0206296561F, + 0.0207670157F, 0.0209048245F, 0.0210430826F, 0.0211817899F, + 0.0213209462F, 0.0214605515F, 0.0216006057F, 0.0217411086F, + 0.0218820603F, 0.0220234605F, 0.0221653093F, 0.0223076066F, + 0.0224503521F, 0.0225935459F, 0.0227371879F, 0.0228812779F, + 0.0230258160F, 0.0231708018F, 0.0233162355F, 0.0234621169F, + 0.0236084459F, 0.0237552224F, 0.0239024462F, 0.0240501175F, + 0.0241982359F, 0.0243468015F, 0.0244958141F, 0.0246452736F, + 0.0247951800F, 0.0249455331F, 0.0250963329F, 0.0252475792F, + 0.0253992720F, 0.0255514111F, 0.0257039965F, 0.0258570281F, + 0.0260105057F, 0.0261644293F, 0.0263187987F, 0.0264736139F, + 0.0266288747F, 0.0267845811F, 0.0269407330F, 0.0270973302F, + 0.0272543727F, 0.0274118604F, 0.0275697930F, 0.0277281707F, + 0.0278869932F, 0.0280462604F, 0.0282059723F, 0.0283661287F, + 0.0285267295F, 0.0286877747F, 0.0288492641F, 0.0290111976F, + 0.0291735751F, 0.0293363965F, 0.0294996617F, 0.0296633706F, + 0.0298275231F, 0.0299921190F, 0.0301571583F, 0.0303226409F, + 0.0304885667F, 0.0306549354F, 0.0308217472F, 0.0309890017F, + 0.0311566989F, 0.0313248388F, 0.0314934211F, 0.0316624459F, + 0.0318319128F, 0.0320018220F, 0.0321721732F, 0.0323429663F, + 0.0325142013F, 0.0326858779F, 0.0328579962F, 0.0330305559F, + 0.0332035570F, 0.0333769994F, 0.0335508829F, 0.0337252074F, + 0.0338999728F, 0.0340751790F, 0.0342508259F, 0.0344269134F, + 0.0346034412F, 0.0347804094F, 0.0349578178F, 0.0351356663F, + 0.0353139548F, 0.0354926831F, 0.0356718511F, 0.0358514588F, + 0.0360315059F, 0.0362119924F, 0.0363929182F, 0.0365742831F, + 0.0367560870F, 0.0369383297F, 0.0371210113F, 0.0373041315F, + 0.0374876902F, 0.0376716873F, 0.0378561226F, 0.0380409961F, + 0.0382263077F, 0.0384120571F, 0.0385982443F, 0.0387848691F, + 0.0389719315F, 0.0391594313F, 0.0393473683F, 0.0395357425F, + 0.0397245537F, 0.0399138017F, 0.0401034866F, 0.0402936080F, + 0.0404841660F, 0.0406751603F, 0.0408665909F, 0.0410584576F, + 0.0412507603F, 0.0414434988F, 0.0416366731F, 0.0418302829F, + 0.0420243282F, 0.0422188088F, 0.0424137246F, 0.0426090755F, + 0.0428048613F, 0.0430010819F, 0.0431977371F, 0.0433948269F, + 0.0435923511F, 0.0437903095F, 0.0439887020F, 0.0441875285F, + 0.0443867889F, 0.0445864830F, 0.0447866106F, 0.0449871717F, + 0.0451881661F, 0.0453895936F, 0.0455914542F, 0.0457937477F, + 0.0459964738F, 0.0461996326F, 0.0464032239F, 0.0466072475F, + 0.0468117032F, 0.0470165910F, 0.0472219107F, 0.0474276622F, + 0.0476338452F, 0.0478404597F, 0.0480475056F, 0.0482549827F, + 0.0484628907F, 0.0486712297F, 0.0488799994F, 0.0490891998F, + 0.0492988306F, 0.0495088917F, 0.0497193830F, 0.0499303043F, + 0.0501416554F, 0.0503534363F, 0.0505656468F, 0.0507782867F, + 0.0509913559F, 0.0512048542F, 0.0514187815F, 0.0516331376F, + 0.0518479225F, 0.0520631358F, 0.0522787775F, 0.0524948475F, + 0.0527113455F, 0.0529282715F, 0.0531456252F, 0.0533634066F, + 0.0535816154F, 0.0538002515F, 0.0540193148F, 0.0542388051F, + 0.0544587222F, 0.0546790660F, 0.0548998364F, 0.0551210331F, + 0.0553426561F, 0.0555647051F, 0.0557871801F, 0.0560100807F, + 0.0562334070F, 0.0564571587F, 0.0566813357F, 0.0569059378F, + 0.0571309649F, 0.0573564168F, 0.0575822933F, 0.0578085942F, + 0.0580353195F, 0.0582624689F, 0.0584900423F, 0.0587180396F, + 0.0589464605F, 0.0591753049F, 0.0594045726F, 0.0596342635F, + 0.0598643774F, 0.0600949141F, 0.0603258735F, 0.0605572555F, + 0.0607890597F, 0.0610212862F, 0.0612539346F, 0.0614870049F, + 0.0617204968F, 0.0619544103F, 0.0621887451F, 0.0624235010F, + 0.0626586780F, 0.0628942758F, 0.0631302942F, 0.0633667331F, + 0.0636035923F, 0.0638408717F, 0.0640785710F, 0.0643166901F, + 0.0645552288F, 0.0647941870F, 0.0650335645F, 0.0652733610F, + 0.0655135765F, 0.0657542108F, 0.0659952636F, 0.0662367348F, + 0.0664786242F, 0.0667209316F, 0.0669636570F, 0.0672068000F, + 0.0674503605F, 0.0676943384F, 0.0679387334F, 0.0681835454F, + 0.0684287742F, 0.0686744196F, 0.0689204814F, 0.0691669595F, + 0.0694138536F, 0.0696611637F, 0.0699088894F, 0.0701570307F, + 0.0704055873F, 0.0706545590F, 0.0709039458F, 0.0711537473F, + 0.0714039634F, 0.0716545939F, 0.0719056387F, 0.0721570975F, + 0.0724089702F, 0.0726612565F, 0.0729139563F, 0.0731670694F, + 0.0734205956F, 0.0736745347F, 0.0739288866F, 0.0741836510F, + 0.0744388277F, 0.0746944166F, 0.0749504175F, 0.0752068301F, + 0.0754636543F, 0.0757208899F, 0.0759785367F, 0.0762365946F, + 0.0764950632F, 0.0767539424F, 0.0770132320F, 0.0772729319F, + 0.0775330418F, 0.0777935616F, 0.0780544909F, 0.0783158298F, + 0.0785775778F, 0.0788397349F, 0.0791023009F, 0.0793652755F, + 0.0796286585F, 0.0798924498F, 0.0801566492F, 0.0804212564F, + 0.0806862712F, 0.0809516935F, 0.0812175231F, 0.0814837597F, + 0.0817504031F, 0.0820174532F, 0.0822849097F, 0.0825527724F, + 0.0828210412F, 0.0830897158F, 0.0833587960F, 0.0836282816F, + 0.0838981724F, 0.0841684682F, 0.0844391688F, 0.0847102740F, + 0.0849817835F, 0.0852536973F, 0.0855260150F, 0.0857987364F, + 0.0860718614F, 0.0863453897F, 0.0866193211F, 0.0868936554F, + 0.0871683924F, 0.0874435319F, 0.0877190737F, 0.0879950175F, + 0.0882713632F, 0.0885481105F, 0.0888252592F, 0.0891028091F, + 0.0893807600F, 0.0896591117F, 0.0899378639F, 0.0902170165F, + 0.0904965692F, 0.0907765218F, 0.0910568740F, 0.0913376258F, + 0.0916187767F, 0.0919003268F, 0.0921822756F, 0.0924646230F, + 0.0927473687F, 0.0930305126F, 0.0933140545F, 0.0935979940F, + 0.0938823310F, 0.0941670653F, 0.0944521966F, 0.0947377247F, + 0.0950236494F, 0.0953099704F, 0.0955966876F, 0.0958838007F, + 0.0961713094F, 0.0964592136F, 0.0967475131F, 0.0970362075F, + 0.0973252967F, 0.0976147805F, 0.0979046585F, 0.0981949307F, + 0.0984855967F, 0.0987766563F, 0.0990681093F, 0.0993599555F, + 0.0996521945F, 0.0999448263F, 0.1002378506F, 0.1005312671F, + 0.1008250755F, 0.1011192757F, 0.1014138675F, 0.1017088505F, + 0.1020042246F, 0.1022999895F, 0.1025961450F, 0.1028926909F, + 0.1031896268F, 0.1034869526F, 0.1037846680F, 0.1040827729F, + 0.1043812668F, 0.1046801497F, 0.1049794213F, 0.1052790813F, + 0.1055791294F, 0.1058795656F, 0.1061803894F, 0.1064816006F, + 0.1067831991F, 0.1070851846F, 0.1073875568F, 0.1076903155F, + 0.1079934604F, 0.1082969913F, 0.1086009079F, 0.1089052101F, + 0.1092098975F, 0.1095149699F, 0.1098204270F, 0.1101262687F, + 0.1104324946F, 0.1107391045F, 0.1110460982F, 0.1113534754F, + 0.1116612359F, 0.1119693793F, 0.1122779055F, 0.1125868142F, + 0.1128961052F, 0.1132057781F, 0.1135158328F, 0.1138262690F, + 0.1141370863F, 0.1144482847F, 0.1147598638F, 0.1150718233F, + 0.1153841631F, 0.1156968828F, 0.1160099822F, 0.1163234610F, + 0.1166373190F, 0.1169515559F, 0.1172661714F, 0.1175811654F, + 0.1178965374F, 0.1182122874F, 0.1185284149F, 0.1188449198F, + 0.1191618018F, 0.1194790606F, 0.1197966960F, 0.1201147076F, + 0.1204330953F, 0.1207518587F, 0.1210709976F, 0.1213905118F, + 0.1217104009F, 0.1220306647F, 0.1223513029F, 0.1226723153F, + 0.1229937016F, 0.1233154615F, 0.1236375948F, 0.1239601011F, + 0.1242829803F, 0.1246062319F, 0.1249298559F, 0.1252538518F, + 0.1255782195F, 0.1259029586F, 0.1262280689F, 0.1265535501F, + 0.1268794019F, 0.1272056241F, 0.1275322163F, 0.1278591784F, + 0.1281865099F, 0.1285142108F, 0.1288422805F, 0.1291707190F, + 0.1294995259F, 0.1298287009F, 0.1301582437F, 0.1304881542F, + 0.1308184319F, 0.1311490766F, 0.1314800881F, 0.1318114660F, + 0.1321432100F, 0.1324753200F, 0.1328077955F, 0.1331406364F, + 0.1334738422F, 0.1338074129F, 0.1341413479F, 0.1344756472F, + 0.1348103103F, 0.1351453370F, 0.1354807270F, 0.1358164801F, + 0.1361525959F, 0.1364890741F, 0.1368259145F, 0.1371631167F, + 0.1375006805F, 0.1378386056F, 0.1381768917F, 0.1385155384F, + 0.1388545456F, 0.1391939129F, 0.1395336400F, 0.1398737266F, + 0.1402141724F, 0.1405549772F, 0.1408961406F, 0.1412376623F, + 0.1415795421F, 0.1419217797F, 0.1422643746F, 0.1426073268F, + 0.1429506358F, 0.1432943013F, 0.1436383231F, 0.1439827008F, + 0.1443274342F, 0.1446725229F, 0.1450179667F, 0.1453637652F, + 0.1457099181F, 0.1460564252F, 0.1464032861F, 0.1467505006F, + 0.1470980682F, 0.1474459888F, 0.1477942620F, 0.1481428875F, + 0.1484918651F, 0.1488411942F, 0.1491908748F, 0.1495409065F, + 0.1498912889F, 0.1502420218F, 0.1505931048F, 0.1509445376F, + 0.1512963200F, 0.1516484516F, 0.1520009321F, 0.1523537612F, + 0.1527069385F, 0.1530604638F, 0.1534143368F, 0.1537685571F, + 0.1541231244F, 0.1544780384F, 0.1548332987F, 0.1551889052F, + 0.1555448574F, 0.1559011550F, 0.1562577978F, 0.1566147853F, + 0.1569721173F, 0.1573297935F, 0.1576878135F, 0.1580461771F, + 0.1584048838F, 0.1587639334F, 0.1591233255F, 0.1594830599F, + 0.1598431361F, 0.1602035540F, 0.1605643131F, 0.1609254131F, + 0.1612868537F, 0.1616486346F, 0.1620107555F, 0.1623732160F, + 0.1627360158F, 0.1630991545F, 0.1634626319F, 0.1638264476F, + 0.1641906013F, 0.1645550926F, 0.1649199212F, 0.1652850869F, + 0.1656505892F, 0.1660164278F, 0.1663826024F, 0.1667491127F, + 0.1671159583F, 0.1674831388F, 0.1678506541F, 0.1682185036F, + 0.1685866872F, 0.1689552044F, 0.1693240549F, 0.1696932384F, + 0.1700627545F, 0.1704326029F, 0.1708027833F, 0.1711732952F, + 0.1715441385F, 0.1719153127F, 0.1722868175F, 0.1726586526F, + 0.1730308176F, 0.1734033121F, 0.1737761359F, 0.1741492886F, + 0.1745227698F, 0.1748965792F, 0.1752707164F, 0.1756451812F, + 0.1760199731F, 0.1763950918F, 0.1767705370F, 0.1771463083F, + 0.1775224054F, 0.1778988279F, 0.1782755754F, 0.1786526477F, + 0.1790300444F, 0.1794077651F, 0.1797858094F, 0.1801641771F, + 0.1805428677F, 0.1809218810F, 0.1813012165F, 0.1816808739F, + 0.1820608528F, 0.1824411530F, 0.1828217739F, 0.1832027154F, + 0.1835839770F, 0.1839655584F, 0.1843474592F, 0.1847296790F, + 0.1851122175F, 0.1854950744F, 0.1858782492F, 0.1862617417F, + 0.1866455514F, 0.1870296780F, 0.1874141211F, 0.1877988804F, + 0.1881839555F, 0.1885693461F, 0.1889550517F, 0.1893410721F, + 0.1897274068F, 0.1901140555F, 0.1905010178F, 0.1908882933F, + 0.1912758818F, 0.1916637828F, 0.1920519959F, 0.1924405208F, + 0.1928293571F, 0.1932185044F, 0.1936079625F, 0.1939977308F, + 0.1943878091F, 0.1947781969F, 0.1951688939F, 0.1955598998F, + 0.1959512141F, 0.1963428364F, 0.1967347665F, 0.1971270038F, + 0.1975195482F, 0.1979123990F, 0.1983055561F, 0.1986990190F, + 0.1990927873F, 0.1994868607F, 0.1998812388F, 0.2002759212F, + 0.2006709075F, 0.2010661974F, 0.2014617904F, 0.2018576862F, + 0.2022538844F, 0.2026503847F, 0.2030471865F, 0.2034442897F, + 0.2038416937F, 0.2042393982F, 0.2046374028F, 0.2050357071F, + 0.2054343107F, 0.2058332133F, 0.2062324145F, 0.2066319138F, + 0.2070317110F, 0.2074318055F, 0.2078321970F, 0.2082328852F, + 0.2086338696F, 0.2090351498F, 0.2094367255F, 0.2098385962F, + 0.2102407617F, 0.2106432213F, 0.2110459749F, 0.2114490220F, + 0.2118523621F, 0.2122559950F, 0.2126599202F, 0.2130641373F, + 0.2134686459F, 0.2138734456F, 0.2142785361F, 0.2146839168F, + 0.2150895875F, 0.2154955478F, 0.2159017972F, 0.2163083353F, + 0.2167151617F, 0.2171222761F, 0.2175296780F, 0.2179373670F, + 0.2183453428F, 0.2187536049F, 0.2191621529F, 0.2195709864F, + 0.2199801051F, 0.2203895085F, 0.2207991961F, 0.2212091677F, + 0.2216194228F, 0.2220299610F, 0.2224407818F, 0.2228518850F, + 0.2232632699F, 0.2236749364F, 0.2240868839F, 0.2244991121F, + 0.2249116204F, 0.2253244086F, 0.2257374763F, 0.2261508229F, + 0.2265644481F, 0.2269783514F, 0.2273925326F, 0.2278069911F, + 0.2282217265F, 0.2286367384F, 0.2290520265F, 0.2294675902F, + 0.2298834292F, 0.2302995431F, 0.2307159314F, 0.2311325937F, + 0.2315495297F, 0.2319667388F, 0.2323842207F, 0.2328019749F, + 0.2332200011F, 0.2336382988F, 0.2340568675F, 0.2344757070F, + 0.2348948166F, 0.2353141961F, 0.2357338450F, 0.2361537629F, + 0.2365739493F, 0.2369944038F, 0.2374151261F, 0.2378361156F, + 0.2382573720F, 0.2386788948F, 0.2391006836F, 0.2395227380F, + 0.2399450575F, 0.2403676417F, 0.2407904902F, 0.2412136026F, + 0.2416369783F, 0.2420606171F, 0.2424845185F, 0.2429086820F, + 0.2433331072F, 0.2437577936F, 0.2441827409F, 0.2446079486F, + 0.2450334163F, 0.2454591435F, 0.2458851298F, 0.2463113747F, + 0.2467378779F, 0.2471646389F, 0.2475916573F, 0.2480189325F, + 0.2484464643F, 0.2488742521F, 0.2493022955F, 0.2497305940F, + 0.2501591473F, 0.2505879549F, 0.2510170163F, 0.2514463311F, + 0.2518758989F, 0.2523057193F, 0.2527357916F, 0.2531661157F, + 0.2535966909F, 0.2540275169F, 0.2544585931F, 0.2548899193F, + 0.2553214948F, 0.2557533193F, 0.2561853924F, 0.2566177135F, + 0.2570502822F, 0.2574830981F, 0.2579161608F, 0.2583494697F, + 0.2587830245F, 0.2592168246F, 0.2596508697F, 0.2600851593F, + 0.2605196929F, 0.2609544701F, 0.2613894904F, 0.2618247534F, + 0.2622602586F, 0.2626960055F, 0.2631319938F, 0.2635682230F, + 0.2640046925F, 0.2644414021F, 0.2648783511F, 0.2653155391F, + 0.2657529657F, 0.2661906305F, 0.2666285329F, 0.2670666725F, + 0.2675050489F, 0.2679436616F, 0.2683825101F, 0.2688215940F, + 0.2692609127F, 0.2697004660F, 0.2701402532F, 0.2705802739F, + 0.2710205278F, 0.2714610142F, 0.2719017327F, 0.2723426830F, + 0.2727838644F, 0.2732252766F, 0.2736669191F, 0.2741087914F, + 0.2745508930F, 0.2749932235F, 0.2754357824F, 0.2758785693F, + 0.2763215837F, 0.2767648251F, 0.2772082930F, 0.2776519870F, + 0.2780959066F, 0.2785400513F, 0.2789844207F, 0.2794290143F, + 0.2798738316F, 0.2803188722F, 0.2807641355F, 0.2812096211F, + 0.2816553286F, 0.2821012574F, 0.2825474071F, 0.2829937773F, + 0.2834403673F, 0.2838871768F, 0.2843342053F, 0.2847814523F, + 0.2852289174F, 0.2856765999F, 0.2861244996F, 0.2865726159F, + 0.2870209482F, 0.2874694962F, 0.2879182594F, 0.2883672372F, + 0.2888164293F, 0.2892658350F, 0.2897154540F, 0.2901652858F, + 0.2906153298F, 0.2910655856F, 0.2915160527F, 0.2919667306F, + 0.2924176189F, 0.2928687171F, 0.2933200246F, 0.2937715409F, + 0.2942232657F, 0.2946751984F, 0.2951273386F, 0.2955796856F, + 0.2960322391F, 0.2964849986F, 0.2969379636F, 0.2973911335F, + 0.2978445080F, 0.2982980864F, 0.2987518684F, 0.2992058534F, + 0.2996600409F, 0.3001144305F, 0.3005690217F, 0.3010238139F, + 0.3014788067F, 0.3019339995F, 0.3023893920F, 0.3028449835F, + 0.3033007736F, 0.3037567618F, 0.3042129477F, 0.3046693306F, + 0.3051259102F, 0.3055826859F, 0.3060396572F, 0.3064968236F, + 0.3069541847F, 0.3074117399F, 0.3078694887F, 0.3083274307F, + 0.3087855653F, 0.3092438920F, 0.3097024104F, 0.3101611199F, + 0.3106200200F, 0.3110791103F, 0.3115383902F, 0.3119978592F, + 0.3124575169F, 0.3129173627F, 0.3133773961F, 0.3138376166F, + 0.3142980238F, 0.3147586170F, 0.3152193959F, 0.3156803598F, + 0.3161415084F, 0.3166028410F, 0.3170643573F, 0.3175260566F, + 0.3179879384F, 0.3184500023F, 0.3189122478F, 0.3193746743F, + 0.3198372814F, 0.3203000685F, 0.3207630351F, 0.3212261807F, + 0.3216895048F, 0.3221530069F, 0.3226166865F, 0.3230805430F, + 0.3235445760F, 0.3240087849F, 0.3244731693F, 0.3249377285F, + 0.3254024622F, 0.3258673698F, 0.3263324507F, 0.3267977045F, + 0.3272631306F, 0.3277287286F, 0.3281944978F, 0.3286604379F, + 0.3291265482F, 0.3295928284F, 0.3300592777F, 0.3305258958F, + 0.3309926821F, 0.3314596361F, 0.3319267573F, 0.3323940451F, + 0.3328614990F, 0.3333291186F, 0.3337969033F, 0.3342648525F, + 0.3347329658F, 0.3352012427F, 0.3356696825F, 0.3361382849F, + 0.3366070492F, 0.3370759749F, 0.3375450616F, 0.3380143087F, + 0.3384837156F, 0.3389532819F, 0.3394230071F, 0.3398928905F, + 0.3403629317F, 0.3408331302F, 0.3413034854F, 0.3417739967F, + 0.3422446638F, 0.3427154860F, 0.3431864628F, 0.3436575938F, + 0.3441288782F, 0.3446003158F, 0.3450719058F, 0.3455436478F, + 0.3460155412F, 0.3464875856F, 0.3469597804F, 0.3474321250F, + 0.3479046189F, 0.3483772617F, 0.3488500527F, 0.3493229914F, + 0.3497960774F, 0.3502693100F, 0.3507426887F, 0.3512162131F, + 0.3516898825F, 0.3521636965F, 0.3526376545F, 0.3531117559F, + 0.3535860003F, 0.3540603870F, 0.3545349157F, 0.3550095856F, + 0.3554843964F, 0.3559593474F, 0.3564344381F, 0.3569096680F, + 0.3573850366F, 0.3578605432F, 0.3583361875F, 0.3588119687F, + 0.3592878865F, 0.3597639402F, 0.3602401293F, 0.3607164533F, + 0.3611929117F, 0.3616695038F, 0.3621462292F, 0.3626230873F, + 0.3631000776F, 0.3635771995F, 0.3640544525F, 0.3645318360F, + 0.3650093496F, 0.3654869926F, 0.3659647645F, 0.3664426648F, + 0.3669206930F, 0.3673988484F, 0.3678771306F, 0.3683555390F, + 0.3688340731F, 0.3693127322F, 0.3697915160F, 0.3702704237F, + 0.3707494549F, 0.3712286091F, 0.3717078857F, 0.3721872840F, + 0.3726668037F, 0.3731464441F, 0.3736262047F, 0.3741060850F, + 0.3745860843F, 0.3750662023F, 0.3755464382F, 0.3760267915F, + 0.3765072618F, 0.3769878484F, 0.3774685509F, 0.3779493686F, + 0.3784303010F, 0.3789113475F, 0.3793925076F, 0.3798737809F, + 0.3803551666F, 0.3808366642F, 0.3813182733F, 0.3817999932F, + 0.3822818234F, 0.3827637633F, 0.3832458124F, 0.3837279702F, + 0.3842102360F, 0.3846926093F, 0.3851750897F, 0.3856576764F, + 0.3861403690F, 0.3866231670F, 0.3871060696F, 0.3875890765F, + 0.3880721870F, 0.3885554007F, 0.3890387168F, 0.3895221349F, + 0.3900056544F, 0.3904892748F, 0.3909729955F, 0.3914568160F, + 0.3919407356F, 0.3924247539F, 0.3929088702F, 0.3933930841F, + 0.3938773949F, 0.3943618021F, 0.3948463052F, 0.3953309035F, + 0.3958155966F, 0.3963003838F, 0.3967852646F, 0.3972702385F, + 0.3977553048F, 0.3982404631F, 0.3987257127F, 0.3992110531F, + 0.3996964838F, 0.4001820041F, 0.4006676136F, 0.4011533116F, + 0.4016390976F, 0.4021249710F, 0.4026109313F, 0.4030969779F, + 0.4035831102F, 0.4040693277F, 0.4045556299F, 0.4050420160F, + 0.4055284857F, 0.4060150383F, 0.4065016732F, 0.4069883899F, + 0.4074751879F, 0.4079620665F, 0.4084490252F, 0.4089360635F, + 0.4094231807F, 0.4099103763F, 0.4103976498F, 0.4108850005F, + 0.4113724280F, 0.4118599315F, 0.4123475107F, 0.4128351648F, + 0.4133228934F, 0.4138106959F, 0.4142985716F, 0.4147865201F, + 0.4152745408F, 0.4157626330F, 0.4162507963F, 0.4167390301F, + 0.4172273337F, 0.4177157067F, 0.4182041484F, 0.4186926583F, + 0.4191812359F, 0.4196698805F, 0.4201585915F, 0.4206473685F, + 0.4211362108F, 0.4216251179F, 0.4221140892F, 0.4226031241F, + 0.4230922221F, 0.4235813826F, 0.4240706050F, 0.4245598887F, + 0.4250492332F, 0.4255386379F, 0.4260281022F, 0.4265176256F, + 0.4270072075F, 0.4274968473F, 0.4279865445F, 0.4284762984F, + 0.4289661086F, 0.4294559743F, 0.4299458951F, 0.4304358704F, + 0.4309258996F, 0.4314159822F, 0.4319061175F, 0.4323963050F, + 0.4328865441F, 0.4333768342F, 0.4338671749F, 0.4343575654F, + 0.4348480052F, 0.4353384938F, 0.4358290306F, 0.4363196149F, + 0.4368102463F, 0.4373009241F, 0.4377916478F, 0.4382824168F, + 0.4387732305F, 0.4392640884F, 0.4397549899F, 0.4402459343F, + 0.4407369212F, 0.4412279499F, 0.4417190198F, 0.4422101305F, + 0.4427012813F, 0.4431924717F, 0.4436837010F, 0.4441749686F, + 0.4446662742F, 0.4451576169F, 0.4456489963F, 0.4461404118F, + 0.4466318628F, 0.4471233487F, 0.4476148690F, 0.4481064230F, + 0.4485980103F, 0.4490896302F, 0.4495812821F, 0.4500729654F, + 0.4505646797F, 0.4510564243F, 0.4515481986F, 0.4520400021F, + 0.4525318341F, 0.4530236942F, 0.4535155816F, 0.4540074959F, + 0.4544994365F, 0.4549914028F, 0.4554833941F, 0.4559754100F, + 0.4564674499F, 0.4569595131F, 0.4574515991F, 0.4579437074F, + 0.4584358372F, 0.4589279881F, 0.4594201595F, 0.4599123508F, + 0.4604045615F, 0.4608967908F, 0.4613890383F, 0.4618813034F, + 0.4623735855F, 0.4628658841F, 0.4633581984F, 0.4638505281F, + 0.4643428724F, 0.4648352308F, 0.4653276028F, 0.4658199877F, + 0.4663123849F, 0.4668047940F, 0.4672972143F, 0.4677896451F, + 0.4682820861F, 0.4687745365F, 0.4692669958F, 0.4697594634F, + 0.4702519387F, 0.4707444211F, 0.4712369102F, 0.4717294052F, + 0.4722219056F, 0.4727144109F, 0.4732069204F, 0.4736994336F, + 0.4741919498F, 0.4746844686F, 0.4751769893F, 0.4756695113F, + 0.4761620341F, 0.4766545571F, 0.4771470797F, 0.4776396013F, + 0.4781321213F, 0.4786246392F, 0.4791171544F, 0.4796096663F, + 0.4801021744F, 0.4805946779F, 0.4810871765F, 0.4815796694F, + 0.4820721561F, 0.4825646360F, 0.4830571086F, 0.4835495732F, + 0.4840420293F, 0.4845344763F, 0.4850269136F, 0.4855193407F, + 0.4860117569F, 0.4865041617F, 0.4869965545F, 0.4874889347F, + 0.4879813018F, 0.4884736551F, 0.4889659941F, 0.4894583182F, + 0.4899506268F, 0.4904429193F, 0.4909351952F, 0.4914274538F, + 0.4919196947F, 0.4924119172F, 0.4929041207F, 0.4933963046F, + 0.4938884685F, 0.4943806116F, 0.4948727335F, 0.4953648335F, + 0.4958569110F, 0.4963489656F, 0.4968409965F, 0.4973330032F, + 0.4978249852F, 0.4983169419F, 0.4988088726F, 0.4993007768F, + 0.4997926539F, 0.5002845034F, 0.5007763247F, 0.5012681171F, + 0.5017598801F, 0.5022516132F, 0.5027433157F, 0.5032349871F, + 0.5037266268F, 0.5042182341F, 0.5047098086F, 0.5052013497F, + 0.5056928567F, 0.5061843292F, 0.5066757664F, 0.5071671679F, + 0.5076585330F, 0.5081498613F, 0.5086411520F, 0.5091324047F, + 0.5096236187F, 0.5101147934F, 0.5106059284F, 0.5110970230F, + 0.5115880766F, 0.5120790887F, 0.5125700587F, 0.5130609860F, + 0.5135518700F, 0.5140427102F, 0.5145335059F, 0.5150242566F, + 0.5155149618F, 0.5160056208F, 0.5164962331F, 0.5169867980F, + 0.5174773151F, 0.5179677837F, 0.5184582033F, 0.5189485733F, + 0.5194388931F, 0.5199291621F, 0.5204193798F, 0.5209095455F, + 0.5213996588F, 0.5218897190F, 0.5223797256F, 0.5228696779F, + 0.5233595755F, 0.5238494177F, 0.5243392039F, 0.5248289337F, + 0.5253186063F, 0.5258082213F, 0.5262977781F, 0.5267872760F, + 0.5272767146F, 0.5277660932F, 0.5282554112F, 0.5287446682F, + 0.5292338635F, 0.5297229965F, 0.5302120667F, 0.5307010736F, + 0.5311900164F, 0.5316788947F, 0.5321677079F, 0.5326564554F, + 0.5331451366F, 0.5336337511F, 0.5341222981F, 0.5346107771F, + 0.5350991876F, 0.5355875290F, 0.5360758007F, 0.5365640021F, + 0.5370521327F, 0.5375401920F, 0.5380281792F, 0.5385160939F, + 0.5390039355F, 0.5394917034F, 0.5399793971F, 0.5404670159F, + 0.5409545594F, 0.5414420269F, 0.5419294179F, 0.5424167318F, + 0.5429039680F, 0.5433911261F, 0.5438782053F, 0.5443652051F, + 0.5448521250F, 0.5453389644F, 0.5458257228F, 0.5463123995F, + 0.5467989940F, 0.5472855057F, 0.5477719341F, 0.5482582786F, + 0.5487445387F, 0.5492307137F, 0.5497168031F, 0.5502028063F, + 0.5506887228F, 0.5511745520F, 0.5516602934F, 0.5521459463F, + 0.5526315103F, 0.5531169847F, 0.5536023690F, 0.5540876626F, + 0.5545728649F, 0.5550579755F, 0.5555429937F, 0.5560279189F, + 0.5565127507F, 0.5569974884F, 0.5574821315F, 0.5579666794F, + 0.5584511316F, 0.5589354875F, 0.5594197465F, 0.5599039080F, + 0.5603879716F, 0.5608719367F, 0.5613558026F, 0.5618395689F, + 0.5623232350F, 0.5628068002F, 0.5632902642F, 0.5637736262F, + 0.5642568858F, 0.5647400423F, 0.5652230953F, 0.5657060442F, + 0.5661888883F, 0.5666716272F, 0.5671542603F, 0.5676367870F, + 0.5681192069F, 0.5686015192F, 0.5690837235F, 0.5695658192F, + 0.5700478058F, 0.5705296827F, 0.5710114494F, 0.5714931052F, + 0.5719746497F, 0.5724560822F, 0.5729374023F, 0.5734186094F, + 0.5738997029F, 0.5743806823F, 0.5748615470F, 0.5753422965F, + 0.5758229301F, 0.5763034475F, 0.5767838480F, 0.5772641310F, + 0.5777442960F, 0.5782243426F, 0.5787042700F, 0.5791840778F, + 0.5796637654F, 0.5801433322F, 0.5806227778F, 0.5811021016F, + 0.5815813029F, 0.5820603814F, 0.5825393363F, 0.5830181673F, + 0.5834968737F, 0.5839754549F, 0.5844539105F, 0.5849322399F, + 0.5854104425F, 0.5858885179F, 0.5863664653F, 0.5868442844F, + 0.5873219746F, 0.5877995353F, 0.5882769660F, 0.5887542661F, + 0.5892314351F, 0.5897084724F, 0.5901853776F, 0.5906621500F, + 0.5911387892F, 0.5916152945F, 0.5920916655F, 0.5925679016F, + 0.5930440022F, 0.5935199669F, 0.5939957950F, 0.5944714861F, + 0.5949470396F, 0.5954224550F, 0.5958977317F, 0.5963728692F, + 0.5968478669F, 0.5973227244F, 0.5977974411F, 0.5982720163F, + 0.5987464497F, 0.5992207407F, 0.5996948887F, 0.6001688932F, + 0.6006427537F, 0.6011164696F, 0.6015900405F, 0.6020634657F, + 0.6025367447F, 0.6030098770F, 0.6034828621F, 0.6039556995F, + 0.6044283885F, 0.6049009288F, 0.6053733196F, 0.6058455606F, + 0.6063176512F, 0.6067895909F, 0.6072613790F, 0.6077330152F, + 0.6082044989F, 0.6086758295F, 0.6091470065F, 0.6096180294F, + 0.6100888977F, 0.6105596108F, 0.6110301682F, 0.6115005694F, + 0.6119708139F, 0.6124409011F, 0.6129108305F, 0.6133806017F, + 0.6138502139F, 0.6143196669F, 0.6147889599F, 0.6152580926F, + 0.6157270643F, 0.6161958746F, 0.6166645230F, 0.6171330088F, + 0.6176013317F, 0.6180694910F, 0.6185374863F, 0.6190053171F, + 0.6194729827F, 0.6199404828F, 0.6204078167F, 0.6208749841F, + 0.6213419842F, 0.6218088168F, 0.6222754811F, 0.6227419768F, + 0.6232083032F, 0.6236744600F, 0.6241404465F, 0.6246062622F, + 0.6250719067F, 0.6255373795F, 0.6260026799F, 0.6264678076F, + 0.6269327619F, 0.6273975425F, 0.6278621487F, 0.6283265800F, + 0.6287908361F, 0.6292549163F, 0.6297188201F, 0.6301825471F, + 0.6306460966F, 0.6311094683F, 0.6315726617F, 0.6320356761F, + 0.6324985111F, 0.6329611662F, 0.6334236410F, 0.6338859348F, + 0.6343480472F, 0.6348099777F, 0.6352717257F, 0.6357332909F, + 0.6361946726F, 0.6366558704F, 0.6371168837F, 0.6375777122F, + 0.6380383552F, 0.6384988123F, 0.6389590830F, 0.6394191668F, + 0.6398790631F, 0.6403387716F, 0.6407982916F, 0.6412576228F, + 0.6417167645F, 0.6421757163F, 0.6426344778F, 0.6430930483F, + 0.6435514275F, 0.6440096149F, 0.6444676098F, 0.6449254119F, + 0.6453830207F, 0.6458404356F, 0.6462976562F, 0.6467546820F, + 0.6472115125F, 0.6476681472F, 0.6481245856F, 0.6485808273F, + 0.6490368717F, 0.6494927183F, 0.6499483667F, 0.6504038164F, + 0.6508590670F, 0.6513141178F, 0.6517689684F, 0.6522236185F, + 0.6526780673F, 0.6531323146F, 0.6535863598F, 0.6540402024F, + 0.6544938419F, 0.6549472779F, 0.6554005099F, 0.6558535373F, + 0.6563063598F, 0.6567589769F, 0.6572113880F, 0.6576635927F, + 0.6581155906F, 0.6585673810F, 0.6590189637F, 0.6594703380F, + 0.6599215035F, 0.6603724598F, 0.6608232064F, 0.6612737427F, + 0.6617240684F, 0.6621741829F, 0.6626240859F, 0.6630737767F, + 0.6635232550F, 0.6639725202F, 0.6644215720F, 0.6648704098F, + 0.6653190332F, 0.6657674417F, 0.6662156348F, 0.6666636121F, + 0.6671113731F, 0.6675589174F, 0.6680062445F, 0.6684533538F, + 0.6689002450F, 0.6693469177F, 0.6697933712F, 0.6702396052F, + 0.6706856193F, 0.6711314129F, 0.6715769855F, 0.6720223369F, + 0.6724674664F, 0.6729123736F, 0.6733570581F, 0.6738015194F, + 0.6742457570F, 0.6746897706F, 0.6751335596F, 0.6755771236F, + 0.6760204621F, 0.6764635747F, 0.6769064609F, 0.6773491204F, + 0.6777915525F, 0.6782337570F, 0.6786757332F, 0.6791174809F, + 0.6795589995F, 0.6800002886F, 0.6804413477F, 0.6808821765F, + 0.6813227743F, 0.6817631409F, 0.6822032758F, 0.6826431785F, + 0.6830828485F, 0.6835222855F, 0.6839614890F, 0.6844004585F, + 0.6848391936F, 0.6852776939F, 0.6857159589F, 0.6861539883F, + 0.6865917815F, 0.6870293381F, 0.6874666576F, 0.6879037398F, + 0.6883405840F, 0.6887771899F, 0.6892135571F, 0.6896496850F, + 0.6900855733F, 0.6905212216F, 0.6909566294F, 0.6913917963F, + 0.6918267218F, 0.6922614055F, 0.6926958471F, 0.6931300459F, + 0.6935640018F, 0.6939977141F, 0.6944311825F, 0.6948644066F, + 0.6952973859F, 0.6957301200F, 0.6961626085F, 0.6965948510F, + 0.6970268470F, 0.6974585961F, 0.6978900980F, 0.6983213521F, + 0.6987523580F, 0.6991831154F, 0.6996136238F, 0.7000438828F, + 0.7004738921F, 0.7009036510F, 0.7013331594F, 0.7017624166F, + 0.7021914224F, 0.7026201763F, 0.7030486779F, 0.7034769268F, + 0.7039049226F, 0.7043326648F, 0.7047601531F, 0.7051873870F, + 0.7056143662F, 0.7060410902F, 0.7064675586F, 0.7068937711F, + 0.7073197271F, 0.7077454264F, 0.7081708684F, 0.7085960529F, + 0.7090209793F, 0.7094456474F, 0.7098700566F, 0.7102942066F, + 0.7107180970F, 0.7111417274F, 0.7115650974F, 0.7119882066F, + 0.7124110545F, 0.7128336409F, 0.7132559653F, 0.7136780272F, + 0.7140998264F, 0.7145213624F, 0.7149426348F, 0.7153636433F, + 0.7157843874F, 0.7162048668F, 0.7166250810F, 0.7170450296F, + 0.7174647124F, 0.7178841289F, 0.7183032786F, 0.7187221613F, + 0.7191407765F, 0.7195591239F, 0.7199772030F, 0.7203950135F, + 0.7208125550F, 0.7212298271F, 0.7216468294F, 0.7220635616F, + 0.7224800233F, 0.7228962140F, 0.7233121335F, 0.7237277813F, + 0.7241431571F, 0.7245582604F, 0.7249730910F, 0.7253876484F, + 0.7258019322F, 0.7262159422F, 0.7266296778F, 0.7270431388F, + 0.7274563247F, 0.7278692353F, 0.7282818700F, 0.7286942287F, + 0.7291063108F, 0.7295181160F, 0.7299296440F, 0.7303408944F, + 0.7307518669F, 0.7311625609F, 0.7315729763F, 0.7319831126F, + 0.7323929695F, 0.7328025466F, 0.7332118435F, 0.7336208600F, + 0.7340295955F, 0.7344380499F, 0.7348462226F, 0.7352541134F, + 0.7356617220F, 0.7360690478F, 0.7364760907F, 0.7368828502F, + 0.7372893259F, 0.7376955176F, 0.7381014249F, 0.7385070475F, + 0.7389123849F, 0.7393174368F, 0.7397222029F, 0.7401266829F, + 0.7405308763F, 0.7409347829F, 0.7413384023F, 0.7417417341F, + 0.7421447780F, 0.7425475338F, 0.7429500009F, 0.7433521791F, + 0.7437540681F, 0.7441556674F, 0.7445569769F, 0.7449579960F, + 0.7453587245F, 0.7457591621F, 0.7461593084F, 0.7465591631F, + 0.7469587259F, 0.7473579963F, 0.7477569741F, 0.7481556590F, + 0.7485540506F, 0.7489521486F, 0.7493499526F, 0.7497474623F, + 0.7501446775F, 0.7505415977F, 0.7509382227F, 0.7513345521F, + 0.7517305856F, 0.7521263229F, 0.7525217636F, 0.7529169074F, + 0.7533117541F, 0.7537063032F, 0.7541005545F, 0.7544945076F, + 0.7548881623F, 0.7552815182F, 0.7556745749F, 0.7560673323F, + 0.7564597899F, 0.7568519474F, 0.7572438046F, 0.7576353611F, + 0.7580266166F, 0.7584175708F, 0.7588082235F, 0.7591985741F, + 0.7595886226F, 0.7599783685F, 0.7603678116F, 0.7607569515F, + 0.7611457879F, 0.7615343206F, 0.7619225493F, 0.7623104735F, + 0.7626980931F, 0.7630854078F, 0.7634724171F, 0.7638591209F, + 0.7642455188F, 0.7646316106F, 0.7650173959F, 0.7654028744F, + 0.7657880459F, 0.7661729100F, 0.7665574664F, 0.7669417150F, + 0.7673256553F, 0.7677092871F, 0.7680926100F, 0.7684756239F, + 0.7688583284F, 0.7692407232F, 0.7696228080F, 0.7700045826F, + 0.7703860467F, 0.7707671999F, 0.7711480420F, 0.7715285728F, + 0.7719087918F, 0.7722886989F, 0.7726682938F, 0.7730475762F, + 0.7734265458F, 0.7738052023F, 0.7741835454F, 0.7745615750F, + 0.7749392906F, 0.7753166921F, 0.7756937791F, 0.7760705514F, + 0.7764470087F, 0.7768231508F, 0.7771989773F, 0.7775744880F, + 0.7779496827F, 0.7783245610F, 0.7786991227F, 0.7790733676F, + 0.7794472953F, 0.7798209056F, 0.7801941982F, 0.7805671729F, + 0.7809398294F, 0.7813121675F, 0.7816841869F, 0.7820558873F, + 0.7824272684F, 0.7827983301F, 0.7831690720F, 0.7835394940F, + 0.7839095957F, 0.7842793768F, 0.7846488373F, 0.7850179767F, + 0.7853867948F, 0.7857552914F, 0.7861234663F, 0.7864913191F, + 0.7868588497F, 0.7872260578F, 0.7875929431F, 0.7879595055F, + 0.7883257445F, 0.7886916601F, 0.7890572520F, 0.7894225198F, + 0.7897874635F, 0.7901520827F, 0.7905163772F, 0.7908803468F, + 0.7912439912F, 0.7916073102F, 0.7919703035F, 0.7923329710F, + 0.7926953124F, 0.7930573274F, 0.7934190158F, 0.7937803774F, + 0.7941414120F, 0.7945021193F, 0.7948624991F, 0.7952225511F, + 0.7955822752F, 0.7959416711F, 0.7963007387F, 0.7966594775F, + 0.7970178875F, 0.7973759685F, 0.7977337201F, 0.7980911422F, + 0.7984482346F, 0.7988049970F, 0.7991614292F, 0.7995175310F, + 0.7998733022F, 0.8002287426F, 0.8005838519F, 0.8009386299F, + 0.8012930765F, 0.8016471914F, 0.8020009744F, 0.8023544253F, + 0.8027075438F, 0.8030603298F, 0.8034127831F, 0.8037649035F, + 0.8041166906F, 0.8044681445F, 0.8048192647F, 0.8051700512F, + 0.8055205038F, 0.8058706222F, 0.8062204062F, 0.8065698556F, + 0.8069189702F, 0.8072677499F, 0.8076161944F, 0.8079643036F, + 0.8083120772F, 0.8086595151F, 0.8090066170F, 0.8093533827F, + 0.8096998122F, 0.8100459051F, 0.8103916613F, 0.8107370806F, + 0.8110821628F, 0.8114269077F, 0.8117713151F, 0.8121153849F, + 0.8124591169F, 0.8128025108F, 0.8131455666F, 0.8134882839F, + 0.8138306627F, 0.8141727027F, 0.8145144038F, 0.8148557658F, + 0.8151967886F, 0.8155374718F, 0.8158778154F, 0.8162178192F, + 0.8165574830F, 0.8168968067F, 0.8172357900F, 0.8175744328F, + 0.8179127349F, 0.8182506962F, 0.8185883164F, 0.8189255955F, + 0.8192625332F, 0.8195991295F, 0.8199353840F, 0.8202712967F, + 0.8206068673F, 0.8209420958F, 0.8212769820F, 0.8216115256F, + 0.8219457266F, 0.8222795848F, 0.8226131000F, 0.8229462721F, + 0.8232791009F, 0.8236115863F, 0.8239437280F, 0.8242755260F, + 0.8246069801F, 0.8249380901F, 0.8252688559F, 0.8255992774F, + 0.8259293544F, 0.8262590867F, 0.8265884741F, 0.8269175167F, + 0.8272462141F, 0.8275745663F, 0.8279025732F, 0.8282302344F, + 0.8285575501F, 0.8288845199F, 0.8292111437F, 0.8295374215F, + 0.8298633530F, 0.8301889382F, 0.8305141768F, 0.8308390688F, + 0.8311636141F, 0.8314878124F, 0.8318116637F, 0.8321351678F, + 0.8324583246F, 0.8327811340F, 0.8331035957F, 0.8334257098F, + 0.8337474761F, 0.8340688944F, 0.8343899647F, 0.8347106867F, + 0.8350310605F, 0.8353510857F, 0.8356707624F, 0.8359900904F, + 0.8363090696F, 0.8366276999F, 0.8369459811F, 0.8372639131F, + 0.8375814958F, 0.8378987292F, 0.8382156130F, 0.8385321472F, + 0.8388483316F, 0.8391641662F, 0.8394796508F, 0.8397947853F, + 0.8401095697F, 0.8404240037F, 0.8407380873F, 0.8410518204F, + 0.8413652029F, 0.8416782347F, 0.8419909156F, 0.8423032456F, + 0.8426152245F, 0.8429268523F, 0.8432381289F, 0.8435490541F, + 0.8438596279F, 0.8441698502F, 0.8444797208F, 0.8447892396F, + 0.8450984067F, 0.8454072218F, 0.8457156849F, 0.8460237959F, + 0.8463315547F, 0.8466389612F, 0.8469460154F, 0.8472527170F, + 0.8475590661F, 0.8478650625F, 0.8481707063F, 0.8484759971F, + 0.8487809351F, 0.8490855201F, 0.8493897521F, 0.8496936308F, + 0.8499971564F, 0.8503003286F, 0.8506031474F, 0.8509056128F, + 0.8512077246F, 0.8515094828F, 0.8518108872F, 0.8521119379F, + 0.8524126348F, 0.8527129777F, 0.8530129666F, 0.8533126015F, + 0.8536118822F, 0.8539108087F, 0.8542093809F, 0.8545075988F, + 0.8548054623F, 0.8551029712F, 0.8554001257F, 0.8556969255F, + 0.8559933707F, 0.8562894611F, 0.8565851968F, 0.8568805775F, + 0.8571756034F, 0.8574702743F, 0.8577645902F, 0.8580585509F, + 0.8583521566F, 0.8586454070F, 0.8589383021F, 0.8592308420F, + 0.8595230265F, 0.8598148556F, 0.8601063292F, 0.8603974473F, + 0.8606882098F, 0.8609786167F, 0.8612686680F, 0.8615583636F, + 0.8618477034F, 0.8621366874F, 0.8624253156F, 0.8627135878F, + 0.8630015042F, 0.8632890646F, 0.8635762690F, 0.8638631173F, + 0.8641496096F, 0.8644357457F, 0.8647215257F, 0.8650069495F, + 0.8652920171F, 0.8655767283F, 0.8658610833F, 0.8661450820F, + 0.8664287243F, 0.8667120102F, 0.8669949397F, 0.8672775127F, + 0.8675597293F, 0.8678415894F, 0.8681230929F, 0.8684042398F, + 0.8686850302F, 0.8689654640F, 0.8692455412F, 0.8695252617F, + 0.8698046255F, 0.8700836327F, 0.8703622831F, 0.8706405768F, + 0.8709185138F, 0.8711960940F, 0.8714733174F, 0.8717501840F, + 0.8720266939F, 0.8723028469F, 0.8725786430F, 0.8728540824F, + 0.8731291648F, 0.8734038905F, 0.8736782592F, 0.8739522711F, + 0.8742259261F, 0.8744992242F, 0.8747721653F, 0.8750447496F, + 0.8753169770F, 0.8755888475F, 0.8758603611F, 0.8761315177F, + 0.8764023175F, 0.8766727603F, 0.8769428462F, 0.8772125752F, + 0.8774819474F, 0.8777509626F, 0.8780196209F, 0.8782879224F, + 0.8785558669F, 0.8788234546F, 0.8790906854F, 0.8793575594F, + 0.8796240765F, 0.8798902368F, 0.8801560403F, 0.8804214870F, + 0.8806865768F, 0.8809513099F, 0.8812156863F, 0.8814797059F, + 0.8817433687F, 0.8820066749F, 0.8822696243F, 0.8825322171F, + 0.8827944532F, 0.8830563327F, 0.8833178556F, 0.8835790219F, + 0.8838398316F, 0.8841002848F, 0.8843603815F, 0.8846201217F, + 0.8848795054F, 0.8851385327F, 0.8853972036F, 0.8856555182F, + 0.8859134764F, 0.8861710783F, 0.8864283239F, 0.8866852133F, + 0.8869417464F, 0.8871979234F, 0.8874537443F, 0.8877092090F, + 0.8879643177F, 0.8882190704F, 0.8884734671F, 0.8887275078F, + 0.8889811927F, 0.8892345216F, 0.8894874948F, 0.8897401122F, + 0.8899923738F, 0.8902442798F, 0.8904958301F, 0.8907470248F, + 0.8909978640F, 0.8912483477F, 0.8914984759F, 0.8917482487F, + 0.8919976662F, 0.8922467284F, 0.8924954353F, 0.8927437871F, + 0.8929917837F, 0.8932394252F, 0.8934867118F, 0.8937336433F, + 0.8939802199F, 0.8942264417F, 0.8944723087F, 0.8947178210F, + 0.8949629785F, 0.8952077815F, 0.8954522299F, 0.8956963239F, + 0.8959400634F, 0.8961834486F, 0.8964264795F, 0.8966691561F, + 0.8969114786F, 0.8971534470F, 0.8973950614F, 0.8976363219F, + 0.8978772284F, 0.8981177812F, 0.8983579802F, 0.8985978256F, + 0.8988373174F, 0.8990764556F, 0.8993152405F, 0.8995536720F, + 0.8997917502F, 0.9000294751F, 0.9002668470F, 0.9005038658F, + 0.9007405317F, 0.9009768446F, 0.9012128048F, 0.9014484123F, + 0.9016836671F, 0.9019185693F, 0.9021531191F, 0.9023873165F, + 0.9026211616F, 0.9028546546F, 0.9030877954F, 0.9033205841F, + 0.9035530210F, 0.9037851059F, 0.9040168392F, 0.9042482207F, + 0.9044792507F, 0.9047099293F, 0.9049402564F, 0.9051702323F, + 0.9053998569F, 0.9056291305F, 0.9058580531F, 0.9060866248F, + 0.9063148457F, 0.9065427159F, 0.9067702355F, 0.9069974046F, + 0.9072242233F, 0.9074506917F, 0.9076768100F, 0.9079025782F, + 0.9081279964F, 0.9083530647F, 0.9085777833F, 0.9088021523F, + 0.9090261717F, 0.9092498417F, 0.9094731623F, 0.9096961338F, + 0.9099187561F, 0.9101410295F, 0.9103629540F, 0.9105845297F, + 0.9108057568F, 0.9110266354F, 0.9112471656F, 0.9114673475F, + 0.9116871812F, 0.9119066668F, 0.9121258046F, 0.9123445945F, + 0.9125630367F, 0.9127811314F, 0.9129988786F, 0.9132162785F, + 0.9134333312F, 0.9136500368F, 0.9138663954F, 0.9140824073F, + 0.9142980724F, 0.9145133910F, 0.9147283632F, 0.9149429890F, + 0.9151572687F, 0.9153712023F, 0.9155847900F, 0.9157980319F, + 0.9160109282F, 0.9162234790F, 0.9164356844F, 0.9166475445F, + 0.9168590595F, 0.9170702296F, 0.9172810548F, 0.9174915354F, + 0.9177016714F, 0.9179114629F, 0.9181209102F, 0.9183300134F, + 0.9185387726F, 0.9187471879F, 0.9189552595F, 0.9191629876F, + 0.9193703723F, 0.9195774136F, 0.9197841119F, 0.9199904672F, + 0.9201964797F, 0.9204021495F, 0.9206074767F, 0.9208124616F, + 0.9210171043F, 0.9212214049F, 0.9214253636F, 0.9216289805F, + 0.9218322558F, 0.9220351896F, 0.9222377821F, 0.9224400335F, + 0.9226419439F, 0.9228435134F, 0.9230447423F, 0.9232456307F, + 0.9234461787F, 0.9236463865F, 0.9238462543F, 0.9240457822F, + 0.9242449704F, 0.9244438190F, 0.9246423282F, 0.9248404983F, + 0.9250383293F, 0.9252358214F, 0.9254329747F, 0.9256297896F, + 0.9258262660F, 0.9260224042F, 0.9262182044F, 0.9264136667F, + 0.9266087913F, 0.9268035783F, 0.9269980280F, 0.9271921405F, + 0.9273859160F, 0.9275793546F, 0.9277724566F, 0.9279652221F, + 0.9281576513F, 0.9283497443F, 0.9285415014F, 0.9287329227F, + 0.9289240084F, 0.9291147586F, 0.9293051737F, 0.9294952536F, + 0.9296849987F, 0.9298744091F, 0.9300634850F, 0.9302522266F, + 0.9304406340F, 0.9306287074F, 0.9308164471F, 0.9310038532F, + 0.9311909259F, 0.9313776654F, 0.9315640719F, 0.9317501455F, + 0.9319358865F, 0.9321212951F, 0.9323063713F, 0.9324911155F, + 0.9326755279F, 0.9328596085F, 0.9330433577F, 0.9332267756F, + 0.9334098623F, 0.9335926182F, 0.9337750434F, 0.9339571380F, + 0.9341389023F, 0.9343203366F, 0.9345014409F, 0.9346822155F, + 0.9348626606F, 0.9350427763F, 0.9352225630F, 0.9354020207F, + 0.9355811498F, 0.9357599503F, 0.9359384226F, 0.9361165667F, + 0.9362943830F, 0.9364718716F, 0.9366490327F, 0.9368258666F, + 0.9370023733F, 0.9371785533F, 0.9373544066F, 0.9375299335F, + 0.9377051341F, 0.9378800087F, 0.9380545576F, 0.9382287809F, + 0.9384026787F, 0.9385762515F, 0.9387494993F, 0.9389224223F, + 0.9390950209F, 0.9392672951F, 0.9394392453F, 0.9396108716F, + 0.9397821743F, 0.9399531536F, 0.9401238096F, 0.9402941427F, + 0.9404641530F, 0.9406338407F, 0.9408032061F, 0.9409722495F, + 0.9411409709F, 0.9413093707F, 0.9414774491F, 0.9416452062F, + 0.9418126424F, 0.9419797579F, 0.9421465528F, 0.9423130274F, + 0.9424791819F, 0.9426450166F, 0.9428105317F, 0.9429757274F, + 0.9431406039F, 0.9433051616F, 0.9434694005F, 0.9436333209F, + 0.9437969232F, 0.9439602074F, 0.9441231739F, 0.9442858229F, + 0.9444481545F, 0.9446101691F, 0.9447718669F, 0.9449332481F, + 0.9450943129F, 0.9452550617F, 0.9454154945F, 0.9455756118F, + 0.9457354136F, 0.9458949003F, 0.9460540721F, 0.9462129292F, + 0.9463714719F, 0.9465297003F, 0.9466876149F, 0.9468452157F, + 0.9470025031F, 0.9471594772F, 0.9473161384F, 0.9474724869F, + 0.9476285229F, 0.9477842466F, 0.9479396584F, 0.9480947585F, + 0.9482495470F, 0.9484040243F, 0.9485581906F, 0.9487120462F, + 0.9488655913F, 0.9490188262F, 0.9491717511F, 0.9493243662F, + 0.9494766718F, 0.9496286683F, 0.9497803557F, 0.9499317345F, + 0.9500828047F, 0.9502335668F, 0.9503840209F, 0.9505341673F, + 0.9506840062F, 0.9508335380F, 0.9509827629F, 0.9511316810F, + 0.9512802928F, 0.9514285984F, 0.9515765982F, 0.9517242923F, + 0.9518716810F, 0.9520187646F, 0.9521655434F, 0.9523120176F, + 0.9524581875F, 0.9526040534F, 0.9527496154F, 0.9528948739F, + 0.9530398292F, 0.9531844814F, 0.9533288310F, 0.9534728780F, + 0.9536166229F, 0.9537600659F, 0.9539032071F, 0.9540460470F, + 0.9541885858F, 0.9543308237F, 0.9544727611F, 0.9546143981F, + 0.9547557351F, 0.9548967723F, 0.9550375100F, 0.9551779485F, + 0.9553180881F, 0.9554579290F, 0.9555974714F, 0.9557367158F, + 0.9558756623F, 0.9560143112F, 0.9561526628F, 0.9562907174F, + 0.9564284752F, 0.9565659366F, 0.9567031017F, 0.9568399710F, + 0.9569765446F, 0.9571128229F, 0.9572488061F, 0.9573844944F, + 0.9575198883F, 0.9576549879F, 0.9577897936F, 0.9579243056F, + 0.9580585242F, 0.9581924497F, 0.9583260824F, 0.9584594226F, + 0.9585924705F, 0.9587252264F, 0.9588576906F, 0.9589898634F, + 0.9591217452F, 0.9592533360F, 0.9593846364F, 0.9595156465F, + 0.9596463666F, 0.9597767971F, 0.9599069382F, 0.9600367901F, + 0.9601663533F, 0.9602956279F, 0.9604246143F, 0.9605533128F, + 0.9606817236F, 0.9608098471F, 0.9609376835F, 0.9610652332F, + 0.9611924963F, 0.9613194733F, 0.9614461644F, 0.9615725699F, + 0.9616986901F, 0.9618245253F, 0.9619500757F, 0.9620753418F, + 0.9622003238F, 0.9623250219F, 0.9624494365F, 0.9625735679F, + 0.9626974163F, 0.9628209821F, 0.9629442656F, 0.9630672671F, + 0.9631899868F, 0.9633124251F, 0.9634345822F, 0.9635564585F, + 0.9636780543F, 0.9637993699F, 0.9639204056F, 0.9640411616F, + 0.9641616383F, 0.9642818359F, 0.9644017549F, 0.9645213955F, + 0.9646407579F, 0.9647598426F, 0.9648786497F, 0.9649971797F, + 0.9651154328F, 0.9652334092F, 0.9653511095F, 0.9654685337F, + 0.9655856823F, 0.9657025556F, 0.9658191538F, 0.9659354773F, + 0.9660515263F, 0.9661673013F, 0.9662828024F, 0.9663980300F, + 0.9665129845F, 0.9666276660F, 0.9667420750F, 0.9668562118F, + 0.9669700766F, 0.9670836698F, 0.9671969917F, 0.9673100425F, + 0.9674228227F, 0.9675353325F, 0.9676475722F, 0.9677595422F, + 0.9678712428F, 0.9679826742F, 0.9680938368F, 0.9682047309F, + 0.9683153569F, 0.9684257150F, 0.9685358056F, 0.9686456289F, + 0.9687551853F, 0.9688644752F, 0.9689734987F, 0.9690822564F, + 0.9691907483F, 0.9692989750F, 0.9694069367F, 0.9695146337F, + 0.9696220663F, 0.9697292349F, 0.9698361398F, 0.9699427813F, + 0.9700491597F, 0.9701552754F, 0.9702611286F, 0.9703667197F, + 0.9704720490F, 0.9705771169F, 0.9706819236F, 0.9707864695F, + 0.9708907549F, 0.9709947802F, 0.9710985456F, 0.9712020514F, + 0.9713052981F, 0.9714082859F, 0.9715110151F, 0.9716134862F, + 0.9717156993F, 0.9718176549F, 0.9719193532F, 0.9720207946F, + 0.9721219794F, 0.9722229080F, 0.9723235806F, 0.9724239976F, + 0.9725241593F, 0.9726240661F, 0.9727237183F, 0.9728231161F, + 0.9729222601F, 0.9730211503F, 0.9731197873F, 0.9732181713F, + 0.9733163027F, 0.9734141817F, 0.9735118088F, 0.9736091842F, + 0.9737063083F, 0.9738031814F, 0.9738998039F, 0.9739961760F, + 0.9740922981F, 0.9741881706F, 0.9742837938F, 0.9743791680F, + 0.9744742935F, 0.9745691707F, 0.9746637999F, 0.9747581814F, + 0.9748523157F, 0.9749462029F, 0.9750398435F, 0.9751332378F, + 0.9752263861F, 0.9753192887F, 0.9754119461F, 0.9755043585F, + 0.9755965262F, 0.9756884496F, 0.9757801291F, 0.9758715650F, + 0.9759627575F, 0.9760537071F, 0.9761444141F, 0.9762348789F, + 0.9763251016F, 0.9764150828F, 0.9765048228F, 0.9765943218F, + 0.9766835802F, 0.9767725984F, 0.9768613767F, 0.9769499154F, + 0.9770382149F, 0.9771262755F, 0.9772140976F, 0.9773016815F, + 0.9773890275F, 0.9774761360F, 0.9775630073F, 0.9776496418F, + 0.9777360398F, 0.9778222016F, 0.9779081277F, 0.9779938182F, + 0.9780792736F, 0.9781644943F, 0.9782494805F, 0.9783342326F, + 0.9784187509F, 0.9785030359F, 0.9785870877F, 0.9786709069F, + 0.9787544936F, 0.9788378484F, 0.9789209714F, 0.9790038631F, + 0.9790865238F, 0.9791689538F, 0.9792511535F, 0.9793331232F, + 0.9794148633F, 0.9794963742F, 0.9795776561F, 0.9796587094F, + 0.9797395345F, 0.9798201316F, 0.9799005013F, 0.9799806437F, + 0.9800605593F, 0.9801402483F, 0.9802197112F, 0.9802989483F, + 0.9803779600F, 0.9804567465F, 0.9805353082F, 0.9806136455F, + 0.9806917587F, 0.9807696482F, 0.9808473143F, 0.9809247574F, + 0.9810019778F, 0.9810789759F, 0.9811557519F, 0.9812323064F, + 0.9813086395F, 0.9813847517F, 0.9814606433F, 0.9815363147F, + 0.9816117662F, 0.9816869981F, 0.9817620108F, 0.9818368047F, + 0.9819113801F, 0.9819857374F, 0.9820598769F, 0.9821337989F, + 0.9822075038F, 0.9822809920F, 0.9823542638F, 0.9824273195F, + 0.9825001596F, 0.9825727843F, 0.9826451940F, 0.9827173891F, + 0.9827893700F, 0.9828611368F, 0.9829326901F, 0.9830040302F, + 0.9830751574F, 0.9831460720F, 0.9832167745F, 0.9832872652F, + 0.9833575444F, 0.9834276124F, 0.9834974697F, 0.9835671166F, + 0.9836365535F, 0.9837057806F, 0.9837747983F, 0.9838436071F, + 0.9839122072F, 0.9839805990F, 0.9840487829F, 0.9841167591F, + 0.9841845282F, 0.9842520903F, 0.9843194459F, 0.9843865953F, + 0.9844535389F, 0.9845202771F, 0.9845868101F, 0.9846531383F, + 0.9847192622F, 0.9847851820F, 0.9848508980F, 0.9849164108F, + 0.9849817205F, 0.9850468276F, 0.9851117324F, 0.9851764352F, + 0.9852409365F, 0.9853052366F, 0.9853693358F, 0.9854332344F, + 0.9854969330F, 0.9855604317F, 0.9856237309F, 0.9856868310F, + 0.9857497325F, 0.9858124355F, 0.9858749404F, 0.9859372477F, + 0.9859993577F, 0.9860612707F, 0.9861229871F, 0.9861845072F, + 0.9862458315F, 0.9863069601F, 0.9863678936F, 0.9864286322F, + 0.9864891764F, 0.9865495264F, 0.9866096826F, 0.9866696454F, + 0.9867294152F, 0.9867889922F, 0.9868483769F, 0.9869075695F, + 0.9869665706F, 0.9870253803F, 0.9870839991F, 0.9871424273F, + 0.9872006653F, 0.9872587135F, 0.9873165721F, 0.9873742415F, + 0.9874317222F, 0.9874890144F, 0.9875461185F, 0.9876030348F, + 0.9876597638F, 0.9877163057F, 0.9877726610F, 0.9878288300F, + 0.9878848130F, 0.9879406104F, 0.9879962225F, 0.9880516497F, + 0.9881068924F, 0.9881619509F, 0.9882168256F, 0.9882715168F, + 0.9883260249F, 0.9883803502F, 0.9884344931F, 0.9884884539F, + 0.9885422331F, 0.9885958309F, 0.9886492477F, 0.9887024838F, + 0.9887555397F, 0.9888084157F, 0.9888611120F, 0.9889136292F, + 0.9889659675F, 0.9890181273F, 0.9890701089F, 0.9891219128F, + 0.9891735392F, 0.9892249885F, 0.9892762610F, 0.9893273572F, + 0.9893782774F, 0.9894290219F, 0.9894795911F, 0.9895299853F, + 0.9895802049F, 0.9896302502F, 0.9896801217F, 0.9897298196F, + 0.9897793443F, 0.9898286961F, 0.9898778755F, 0.9899268828F, + 0.9899757183F, 0.9900243823F, 0.9900728753F, 0.9901211976F, + 0.9901693495F, 0.9902173314F, 0.9902651436F, 0.9903127865F, + 0.9903602605F, 0.9904075659F, 0.9904547031F, 0.9905016723F, + 0.9905484740F, 0.9905951086F, 0.9906415763F, 0.9906878775F, + 0.9907340126F, 0.9907799819F, 0.9908257858F, 0.9908714247F, + 0.9909168988F, 0.9909622086F, 0.9910073543F, 0.9910523364F, + 0.9910971552F, 0.9911418110F, 0.9911863042F, 0.9912306351F, + 0.9912748042F, 0.9913188117F, 0.9913626580F, 0.9914063435F, + 0.9914498684F, 0.9914932333F, 0.9915364383F, 0.9915794839F, + 0.9916223703F, 0.9916650981F, 0.9917076674F, 0.9917500787F, + 0.9917923323F, 0.9918344286F, 0.9918763679F, 0.9919181505F, + 0.9919597769F, 0.9920012473F, 0.9920425621F, 0.9920837217F, + 0.9921247263F, 0.9921655765F, 0.9922062724F, 0.9922468145F, + 0.9922872030F, 0.9923274385F, 0.9923675211F, 0.9924074513F, + 0.9924472294F, 0.9924868557F, 0.9925263306F, 0.9925656544F, + 0.9926048275F, 0.9926438503F, 0.9926827230F, 0.9927214461F, + 0.9927600199F, 0.9927984446F, 0.9928367208F, 0.9928748486F, + 0.9929128285F, 0.9929506608F, 0.9929883459F, 0.9930258841F, + 0.9930632757F, 0.9931005211F, 0.9931376207F, 0.9931745747F, + 0.9932113836F, 0.9932480476F, 0.9932845671F, 0.9933209425F, + 0.9933571742F, 0.9933932623F, 0.9934292074F, 0.9934650097F, + 0.9935006696F, 0.9935361874F, 0.9935715635F, 0.9936067982F, + 0.9936418919F, 0.9936768448F, 0.9937116574F, 0.9937463300F, + 0.9937808629F, 0.9938152565F, 0.9938495111F, 0.9938836271F, + 0.9939176047F, 0.9939514444F, 0.9939851465F, 0.9940187112F, + 0.9940521391F, 0.9940854303F, 0.9941185853F, 0.9941516044F, + 0.9941844879F, 0.9942172361F, 0.9942498495F, 0.9942823283F, + 0.9943146729F, 0.9943468836F, 0.9943789608F, 0.9944109047F, + 0.9944427158F, 0.9944743944F, 0.9945059408F, 0.9945373553F, + 0.9945686384F, 0.9945997902F, 0.9946308112F, 0.9946617017F, + 0.9946924621F, 0.9947230926F, 0.9947535937F, 0.9947839656F, + 0.9948142086F, 0.9948443232F, 0.9948743097F, 0.9949041683F, + 0.9949338995F, 0.9949635035F, 0.9949929807F, 0.9950223315F, + 0.9950515561F, 0.9950806549F, 0.9951096282F, 0.9951384764F, + 0.9951671998F, 0.9951957987F, 0.9952242735F, 0.9952526245F, + 0.9952808520F, 0.9953089564F, 0.9953369380F, 0.9953647971F, + 0.9953925340F, 0.9954201491F, 0.9954476428F, 0.9954750153F, + 0.9955022670F, 0.9955293981F, 0.9955564092F, 0.9955833003F, + 0.9956100720F, 0.9956367245F, 0.9956632582F, 0.9956896733F, + 0.9957159703F, 0.9957421494F, 0.9957682110F, 0.9957941553F, + 0.9958199828F, 0.9958456937F, 0.9958712884F, 0.9958967672F, + 0.9959221305F, 0.9959473784F, 0.9959725115F, 0.9959975300F, + 0.9960224342F, 0.9960472244F, 0.9960719011F, 0.9960964644F, + 0.9961209148F, 0.9961452525F, 0.9961694779F, 0.9961935913F, + 0.9962175930F, 0.9962414834F, 0.9962652627F, 0.9962889313F, + 0.9963124895F, 0.9963359377F, 0.9963592761F, 0.9963825051F, + 0.9964056250F, 0.9964286361F, 0.9964515387F, 0.9964743332F, + 0.9964970198F, 0.9965195990F, 0.9965420709F, 0.9965644360F, + 0.9965866946F, 0.9966088469F, 0.9966308932F, 0.9966528340F, + 0.9966746695F, 0.9966964001F, 0.9967180260F, 0.9967395475F, + 0.9967609651F, 0.9967822789F, 0.9968034894F, 0.9968245968F, + 0.9968456014F, 0.9968665036F, 0.9968873037F, 0.9969080019F, + 0.9969285987F, 0.9969490942F, 0.9969694889F, 0.9969897830F, + 0.9970099769F, 0.9970300708F, 0.9970500651F, 0.9970699601F, + 0.9970897561F, 0.9971094533F, 0.9971290522F, 0.9971485531F, + 0.9971679561F, 0.9971872617F, 0.9972064702F, 0.9972255818F, + 0.9972445968F, 0.9972635157F, 0.9972823386F, 0.9973010659F, + 0.9973196980F, 0.9973382350F, 0.9973566773F, 0.9973750253F, + 0.9973932791F, 0.9974114392F, 0.9974295059F, 0.9974474793F, + 0.9974653599F, 0.9974831480F, 0.9975008438F, 0.9975184476F, + 0.9975359598F, 0.9975533806F, 0.9975707104F, 0.9975879495F, + 0.9976050981F, 0.9976221566F, 0.9976391252F, 0.9976560043F, + 0.9976727941F, 0.9976894950F, 0.9977061073F, 0.9977226312F, + 0.9977390671F, 0.9977554152F, 0.9977716759F, 0.9977878495F, + 0.9978039361F, 0.9978199363F, 0.9978358501F, 0.9978516780F, + 0.9978674202F, 0.9978830771F, 0.9978986488F, 0.9979141358F, + 0.9979295383F, 0.9979448566F, 0.9979600909F, 0.9979752417F, + 0.9979903091F, 0.9980052936F, 0.9980201952F, 0.9980350145F, + 0.9980497515F, 0.9980644067F, 0.9980789804F, 0.9980934727F, + 0.9981078841F, 0.9981222147F, 0.9981364649F, 0.9981506350F, + 0.9981647253F, 0.9981787360F, 0.9981926674F, 0.9982065199F, + 0.9982202936F, 0.9982339890F, 0.9982476062F, 0.9982611456F, + 0.9982746074F, 0.9982879920F, 0.9983012996F, 0.9983145304F, + 0.9983276849F, 0.9983407632F, 0.9983537657F, 0.9983666926F, + 0.9983795442F, 0.9983923208F, 0.9984050226F, 0.9984176501F, + 0.9984302033F, 0.9984426827F, 0.9984550884F, 0.9984674208F, + 0.9984796802F, 0.9984918667F, 0.9985039808F, 0.9985160227F, + 0.9985279926F, 0.9985398909F, 0.9985517177F, 0.9985634734F, + 0.9985751583F, 0.9985867727F, 0.9985983167F, 0.9986097907F, + 0.9986211949F, 0.9986325297F, 0.9986437953F, 0.9986549919F, + 0.9986661199F, 0.9986771795F, 0.9986881710F, 0.9986990946F, + 0.9987099507F, 0.9987207394F, 0.9987314611F, 0.9987421161F, + 0.9987527045F, 0.9987632267F, 0.9987736829F, 0.9987840734F, + 0.9987943985F, 0.9988046584F, 0.9988148534F, 0.9988249838F, + 0.9988350498F, 0.9988450516F, 0.9988549897F, 0.9988648641F, + 0.9988746753F, 0.9988844233F, 0.9988941086F, 0.9989037313F, + 0.9989132918F, 0.9989227902F, 0.9989322269F, 0.9989416021F, + 0.9989509160F, 0.9989601690F, 0.9989693613F, 0.9989784931F, + 0.9989875647F, 0.9989965763F, 0.9990055283F, 0.9990144208F, + 0.9990232541F, 0.9990320286F, 0.9990407443F, 0.9990494016F, + 0.9990580008F, 0.9990665421F, 0.9990750257F, 0.9990834519F, + 0.9990918209F, 0.9991001331F, 0.9991083886F, 0.9991165877F, + 0.9991247307F, 0.9991328177F, 0.9991408491F, 0.9991488251F, + 0.9991567460F, 0.9991646119F, 0.9991724232F, 0.9991801801F, + 0.9991878828F, 0.9991955316F, 0.9992031267F, 0.9992106684F, + 0.9992181569F, 0.9992255925F, 0.9992329753F, 0.9992403057F, + 0.9992475839F, 0.9992548101F, 0.9992619846F, 0.9992691076F, + 0.9992761793F, 0.9992832001F, 0.9992901701F, 0.9992970895F, + 0.9993039587F, 0.9993107777F, 0.9993175470F, 0.9993242667F, + 0.9993309371F, 0.9993375583F, 0.9993441307F, 0.9993506545F, + 0.9993571298F, 0.9993635570F, 0.9993699362F, 0.9993762678F, + 0.9993825519F, 0.9993887887F, 0.9993949785F, 0.9994011216F, + 0.9994072181F, 0.9994132683F, 0.9994192725F, 0.9994252307F, + 0.9994311434F, 0.9994370107F, 0.9994428327F, 0.9994486099F, + 0.9994543423F, 0.9994600303F, 0.9994656739F, 0.9994712736F, + 0.9994768294F, 0.9994823417F, 0.9994878105F, 0.9994932363F, + 0.9994986191F, 0.9995039592F, 0.9995092568F, 0.9995145122F, + 0.9995197256F, 0.9995248971F, 0.9995300270F, 0.9995351156F, + 0.9995401630F, 0.9995451695F, 0.9995501352F, 0.9995550604F, + 0.9995599454F, 0.9995647903F, 0.9995695953F, 0.9995743607F, + 0.9995790866F, 0.9995837734F, 0.9995884211F, 0.9995930300F, + 0.9995976004F, 0.9996021324F, 0.9996066263F, 0.9996110822F, + 0.9996155004F, 0.9996198810F, 0.9996242244F, 0.9996285306F, + 0.9996327999F, 0.9996370326F, 0.9996412287F, 0.9996453886F, + 0.9996495125F, 0.9996536004F, 0.9996576527F, 0.9996616696F, + 0.9996656512F, 0.9996695977F, 0.9996735094F, 0.9996773865F, + 0.9996812291F, 0.9996850374F, 0.9996888118F, 0.9996925523F, + 0.9996962591F, 0.9996999325F, 0.9997035727F, 0.9997071798F, + 0.9997107541F, 0.9997142957F, 0.9997178049F, 0.9997212818F, + 0.9997247266F, 0.9997281396F, 0.9997315209F, 0.9997348708F, + 0.9997381893F, 0.9997414767F, 0.9997447333F, 0.9997479591F, + 0.9997511544F, 0.9997543194F, 0.9997574542F, 0.9997605591F, + 0.9997636342F, 0.9997666797F, 0.9997696958F, 0.9997726828F, + 0.9997756407F, 0.9997785698F, 0.9997814703F, 0.9997843423F, + 0.9997871860F, 0.9997900016F, 0.9997927894F, 0.9997955494F, + 0.9997982818F, 0.9998009869F, 0.9998036648F, 0.9998063157F, + 0.9998089398F, 0.9998115373F, 0.9998141082F, 0.9998166529F, + 0.9998191715F, 0.9998216642F, 0.9998241311F, 0.9998265724F, + 0.9998289884F, 0.9998313790F, 0.9998337447F, 0.9998360854F, + 0.9998384015F, 0.9998406930F, 0.9998429602F, 0.9998452031F, + 0.9998474221F, 0.9998496171F, 0.9998517885F, 0.9998539364F, + 0.9998560610F, 0.9998581624F, 0.9998602407F, 0.9998622962F, + 0.9998643291F, 0.9998663394F, 0.9998683274F, 0.9998702932F, + 0.9998722370F, 0.9998741589F, 0.9998760591F, 0.9998779378F, + 0.9998797952F, 0.9998816313F, 0.9998834464F, 0.9998852406F, + 0.9998870141F, 0.9998887670F, 0.9998904995F, 0.9998922117F, + 0.9998939039F, 0.9998955761F, 0.9998972285F, 0.9998988613F, + 0.9999004746F, 0.9999020686F, 0.9999036434F, 0.9999051992F, + 0.9999067362F, 0.9999082544F, 0.9999097541F, 0.9999112354F, + 0.9999126984F, 0.9999141433F, 0.9999155703F, 0.9999169794F, + 0.9999183709F, 0.9999197449F, 0.9999211014F, 0.9999224408F, + 0.9999237631F, 0.9999250684F, 0.9999263570F, 0.9999276289F, + 0.9999288843F, 0.9999301233F, 0.9999313461F, 0.9999325529F, + 0.9999337437F, 0.9999349187F, 0.9999360780F, 0.9999372218F, + 0.9999383503F, 0.9999394635F, 0.9999405616F, 0.9999416447F, + 0.9999427129F, 0.9999437665F, 0.9999448055F, 0.9999458301F, + 0.9999468404F, 0.9999478365F, 0.9999488185F, 0.9999497867F, + 0.9999507411F, 0.9999516819F, 0.9999526091F, 0.9999535230F, + 0.9999544236F, 0.9999553111F, 0.9999561856F, 0.9999570472F, + 0.9999578960F, 0.9999587323F, 0.9999595560F, 0.9999603674F, + 0.9999611666F, 0.9999619536F, 0.9999627286F, 0.9999634917F, + 0.9999642431F, 0.9999649828F, 0.9999657110F, 0.9999664278F, + 0.9999671334F, 0.9999678278F, 0.9999685111F, 0.9999691835F, + 0.9999698451F, 0.9999704960F, 0.9999711364F, 0.9999717662F, + 0.9999723858F, 0.9999729950F, 0.9999735942F, 0.9999741834F, + 0.9999747626F, 0.9999753321F, 0.9999758919F, 0.9999764421F, + 0.9999769828F, 0.9999775143F, 0.9999780364F, 0.9999785495F, + 0.9999790535F, 0.9999795485F, 0.9999800348F, 0.9999805124F, + 0.9999809813F, 0.9999814417F, 0.9999818938F, 0.9999823375F, + 0.9999827731F, 0.9999832005F, 0.9999836200F, 0.9999840316F, + 0.9999844353F, 0.9999848314F, 0.9999852199F, 0.9999856008F, + 0.9999859744F, 0.9999863407F, 0.9999866997F, 0.9999870516F, + 0.9999873965F, 0.9999877345F, 0.9999880656F, 0.9999883900F, + 0.9999887078F, 0.9999890190F, 0.9999893237F, 0.9999896220F, + 0.9999899140F, 0.9999901999F, 0.9999904796F, 0.9999907533F, + 0.9999910211F, 0.9999912830F, 0.9999915391F, 0.9999917896F, + 0.9999920345F, 0.9999922738F, 0.9999925077F, 0.9999927363F, + 0.9999929596F, 0.9999931777F, 0.9999933907F, 0.9999935987F, + 0.9999938018F, 0.9999940000F, 0.9999941934F, 0.9999943820F, + 0.9999945661F, 0.9999947456F, 0.9999949206F, 0.9999950912F, + 0.9999952575F, 0.9999954195F, 0.9999955773F, 0.9999957311F, + 0.9999958807F, 0.9999960265F, 0.9999961683F, 0.9999963063F, + 0.9999964405F, 0.9999965710F, 0.9999966979F, 0.9999968213F, + 0.9999969412F, 0.9999970576F, 0.9999971707F, 0.9999972805F, + 0.9999973871F, 0.9999974905F, 0.9999975909F, 0.9999976881F, + 0.9999977824F, 0.9999978738F, 0.9999979624F, 0.9999980481F, + 0.9999981311F, 0.9999982115F, 0.9999982892F, 0.9999983644F, + 0.9999984370F, 0.9999985072F, 0.9999985750F, 0.9999986405F, + 0.9999987037F, 0.9999987647F, 0.9999988235F, 0.9999988802F, + 0.9999989348F, 0.9999989873F, 0.9999990379F, 0.9999990866F, + 0.9999991334F, 0.9999991784F, 0.9999992217F, 0.9999992632F, + 0.9999993030F, 0.9999993411F, 0.9999993777F, 0.9999994128F, + 0.9999994463F, 0.9999994784F, 0.9999995091F, 0.9999995384F, + 0.9999995663F, 0.9999995930F, 0.9999996184F, 0.9999996426F, + 0.9999996657F, 0.9999996876F, 0.9999997084F, 0.9999997282F, + 0.9999997469F, 0.9999997647F, 0.9999997815F, 0.9999997973F, + 0.9999998123F, 0.9999998265F, 0.9999998398F, 0.9999998524F, + 0.9999998642F, 0.9999998753F, 0.9999998857F, 0.9999998954F, + 0.9999999045F, 0.9999999130F, 0.9999999209F, 0.9999999282F, + 0.9999999351F, 0.9999999414F, 0.9999999472F, 0.9999999526F, + 0.9999999576F, 0.9999999622F, 0.9999999664F, 0.9999999702F, + 0.9999999737F, 0.9999999769F, 0.9999999798F, 0.9999999824F, + 0.9999999847F, 0.9999999868F, 0.9999999887F, 0.9999999904F, + 0.9999999919F, 0.9999999932F, 0.9999999943F, 0.9999999953F, + 0.9999999961F, 0.9999999969F, 0.9999999975F, 0.9999999980F, + 0.9999999985F, 0.9999999988F, 0.9999999991F, 0.9999999993F, + 0.9999999995F, 0.9999999997F, 0.9999999998F, 0.9999999999F, + 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, + 1.0000000000F, 1.0000000000F, 1.0000000000F, 1.0000000000F, +};*/ + +static float *vwin[8] = { + vwin64, + vwin128, + vwin256, + vwin512, + vwin1024, + vwin2048, + vwin4096, + vwin8192, +}; + +/* + Added by brett. +*/ +void _FMOD_vorbis_window_init() +{ + int i; + int left = 32; + + while (left <= 4096) + { + float *tab = 0; + + /* The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*pi*.5) */ + switch(left) + { + case 32: + tab = vwin64; + break; + case 64: + tab = vwin128; + break; + case 128: + tab = vwin256; + break; + case 256: + tab = vwin512; + break; + case 512: + tab = vwin1024; + break; + case 1024: + tab = vwin2048; + break; + case 2048: + tab = vwin4096; + break; + case 4096: + tab = vwin8192; + break; + }; + + if (tab) + { + for(i=0;i Hz + */ +#define ALC_FREQUENCY 0x1007 + +/** + * followed by Hz + */ +#define ALC_REFRESH 0x1008 + +/** + * followed by AL_TRUE, AL_FALSE + */ +#define ALC_SYNC 0x1009 + +/** + * followed by Num of requested Mono (3D) Sources + */ +#define ALC_MONO_SOURCES 0x1010 + +/** + * followed by Num of requested Stereo Sources + */ +#define ALC_STEREO_SOURCES 0x1011 + +/** + * errors + */ + +/** + * No error + */ +#define ALC_NO_ERROR ALC_FALSE + +/** + * No device + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * invalid context ID + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * bad enum + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * bad value + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * Out of memory. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + + +/** + * The Specifier string for default device + */ +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 + +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +/** + * Capture extension + */ +#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 +#define ALC_CAPTURE_SAMPLES 0x312 + + +#if !defined(ALC_NO_PROTOTYPES) + +/* + * Context Management + */ +ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); + +ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( ALCvoid ); + +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); + + +/* + * Device Management + */ +ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename ); + +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device ); + + +/* + * Error support. + * Obtain the most recent Context error + */ +ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); + +ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); + +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); + + +/* + * Query functions + */ +ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); + +ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); + + +/* + * Capture functions + */ +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); + +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#else /* ALC_NO_PROTOTYPES */ + +typedef ALCcontext * (ALC_APIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)( ALCvoid ); +typedef ALCdevice * (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALC_APIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); +typedef ALCdevice * (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#endif /* ALC_NO_PROTOTYPES */ + +#if TARGET_OS_MAC + #pragma export off +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* AL_ALC_H */ diff --git a/lib/openal/include/al/alut.h b/lib/openal/include/al/alut.h new file mode 100755 index 0000000..7a33ccb --- /dev/null +++ b/lib/openal/include/al/alut.h @@ -0,0 +1,68 @@ +#ifndef _AL_ALUT_H +#define _AL_ALUT_H + +#include + +#if defined(_WIN32) && !defined(_XBOX) && 0 + #if defined (_OPENAL32LIB) + #define ALUTAPI __declspec(dllexport) + #else + #define ALUTAPI __declspec(dllimport) + #endif +#else + #define ALUTAPI extern +#endif + +#if defined(_WIN32) + #define ALUTAPIENTRY __cdecl +#else + #define ALUTAPIENTRY +#endif + +#if TARGET_OS_MAC + #pragma export on +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !defined(ALUT_NO_PROTOTYPES) + +ALUTAPI void ALUTAPIENTRY alutInit( int *argc, char *argv[] ); +ALUTAPI void ALUTAPIENTRY alutExit( void ); + +#if defined(MACINTOSH_AL) +/* Windows and Linux versions have a loop parameter, Macintosh doesn't */ +ALUTAPI void ALUTAPIENTRY alutLoadWAVFile( const ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq ); +ALUTAPI void ALUTAPIENTRY alutLoadWAVMemory( const ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq ); +#else +ALUTAPI void ALUTAPIENTRY alutLoadWAVFile( const ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop ); +ALUTAPI void ALUTAPIENTRY alutLoadWAVMemory( const ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop ); +#endif +ALUTAPI void ALUTAPIENTRY alutUnloadWAV( ALenum format, ALvoid *data, ALsizei size, ALsizei freq ); + +#else /* ALUT_NO_PROTOTYPES */ + +ALUTAPI void (ALUTAPIENTRY *alutInit)( int *argc, char *argv[] ); +ALUTAPI void (ALUTAPIENTRY *alutExit)( void ); +#if defined(MACINTOSH_AL) +ALUTAPI void (ALUTAPIENTRY *alutLoadWAVFile)( const ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq ); +ALUTAPI void (ALUTAPIENTRY *alutLoadWAVMemory)( const ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq ); +#else +ALUTAPI void (ALUTAPIENTRY *alutLoadWAVFile)( const ALbyte *file, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop ); +ALUTAPI void (ALUTAPIENTRY *alutLoadWAVMemory)( const ALbyte *memory, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *freq, ALboolean *loop ); +#endif +ALUTAPI void (ALUTAPIENTRY *alutUnloadWAV)( ALenum format, ALvoid *data, ALsizei size, ALsizei freq ); + +#endif /* ALUT_NO_PROTOTYPES */ + +#if TARGET_OS_MAC + #pragma export off +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/lib/openal/utils/LoadOAL.cpp b/lib/openal/utils/LoadOAL.cpp new file mode 100755 index 0000000..186a441 --- /dev/null +++ b/lib/openal/utils/LoadOAL.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2005, Creative Labs Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and + * the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "windows.h" +#include "LoadOAL.h" +#include "../../../src/fmod_debug.h" +#include "../../../src/fmod_types.h" + +HINSTANCE g_hOpenALDLL = NULL; + +ALboolean LoadOALLibrary(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable) +{ + if (!lpOALFnTable) + return AL_FALSE; + + if (szOALFullPathName) + g_hOpenALDLL = LoadLibrary(szOALFullPathName); + else + g_hOpenALDLL = LoadLibrary("openal32.dll"); + + if (!g_hOpenALDLL) + return AL_FALSE; + + FMOD_memset(lpOALFnTable, 0, sizeof(OPENALFNTABLE)); + + // Get function pointers + lpOALFnTable->alEnable = (LPALENABLE)GetProcAddress(g_hOpenALDLL, "alEnable"); + if (lpOALFnTable->alEnable == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alEnable' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alDisable = (LPALDISABLE)GetProcAddress(g_hOpenALDLL, "alDisable"); + if (lpOALFnTable->alDisable == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alDisable' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alIsEnabled = (LPALISENABLED)GetProcAddress(g_hOpenALDLL, "alIsEnabled"); + if (lpOALFnTable->alIsEnabled == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alIsEnabled' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetBoolean = (LPALGETBOOLEAN)GetProcAddress(g_hOpenALDLL, "alGetBoolean"); + if (lpOALFnTable->alGetBoolean == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetBoolean' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetInteger = (LPALGETINTEGER)GetProcAddress(g_hOpenALDLL, "alGetInteger"); + if (lpOALFnTable->alGetInteger == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetInteger' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetFloat = (LPALGETFLOAT)GetProcAddress(g_hOpenALDLL, "alGetFloat"); + if (lpOALFnTable->alGetFloat == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetFloat' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetDouble = (LPALGETDOUBLE)GetProcAddress(g_hOpenALDLL, "alGetDouble"); + if (lpOALFnTable->alGetDouble == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetDouble' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetBooleanv = (LPALGETBOOLEANV)GetProcAddress(g_hOpenALDLL, "alGetBooleanv"); + if (lpOALFnTable->alGetBooleanv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetBooleanv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetIntegerv = (LPALGETINTEGERV)GetProcAddress(g_hOpenALDLL, "alGetIntegerv"); + if (lpOALFnTable->alGetIntegerv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetIntegerv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetFloatv = (LPALGETFLOATV)GetProcAddress(g_hOpenALDLL, "alGetFloatv"); + if (lpOALFnTable->alGetFloatv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetFloatv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetDoublev = (LPALGETDOUBLEV)GetProcAddress(g_hOpenALDLL, "alGetDoublev"); + if (lpOALFnTable->alGetDoublev == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetDoublev' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetString = (LPALGETSTRING)GetProcAddress(g_hOpenALDLL, "alGetString"); + if (lpOALFnTable->alGetString == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetString' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetError = (LPALGETERROR)GetProcAddress(g_hOpenALDLL, "alGetError"); + if (lpOALFnTable->alGetError == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetError' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alIsExtensionPresent = (LPALISEXTENSIONPRESENT)GetProcAddress(g_hOpenALDLL, "alIsExtensionPresent"); + if (lpOALFnTable->alIsExtensionPresent == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alIsExtensionPresent' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetProcAddress = (LPALGETPROCADDRESS)GetProcAddress(g_hOpenALDLL, "alGetProcAddress"); + if (lpOALFnTable->alGetProcAddress == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetProcAddress' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetEnumValue = (LPALGETENUMVALUE)GetProcAddress(g_hOpenALDLL, "alGetEnumValue"); + if (lpOALFnTable->alGetEnumValue == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetEnumValue' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alListeneri = (LPALLISTENERI)GetProcAddress(g_hOpenALDLL, "alListeneri"); + if (lpOALFnTable->alListeneri == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alListeneri' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alListenerf = (LPALLISTENERF)GetProcAddress(g_hOpenALDLL, "alListenerf"); + if (lpOALFnTable->alListenerf == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alListenerf' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alListener3f = (LPALLISTENER3F)GetProcAddress(g_hOpenALDLL, "alListener3f"); + if (lpOALFnTable->alListener3f == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alListener3f' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alListenerfv = (LPALLISTENERFV)GetProcAddress(g_hOpenALDLL, "alListenerfv"); + if (lpOALFnTable->alListenerfv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alListenerfv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetListeneri = (LPALGETLISTENERI)GetProcAddress(g_hOpenALDLL, "alGetListeneri"); + if (lpOALFnTable->alGetListeneri == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetListeneri' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetListenerf =(LPALGETLISTENERF)GetProcAddress(g_hOpenALDLL, "alGetListenerf"); + if (lpOALFnTable->alGetListenerf == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetListenerf' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetListener3f = (LPALGETLISTENER3F)GetProcAddress(g_hOpenALDLL, "alGetListener3f"); + if (lpOALFnTable->alGetListener3f == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetListener3f' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetListenerfv = (LPALGETLISTENERFV)GetProcAddress(g_hOpenALDLL, "alGetListenerfv"); + if (lpOALFnTable->alGetListenerfv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetListenerfv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGenSources = (LPALGENSOURCES)GetProcAddress(g_hOpenALDLL, "alGenSources"); + if (lpOALFnTable->alGenSources == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGenSources' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alDeleteSources = (LPALDELETESOURCES)GetProcAddress(g_hOpenALDLL, "alDeleteSources"); + if (lpOALFnTable->alDeleteSources == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alDeleteSources' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alIsSource = (LPALISSOURCE)GetProcAddress(g_hOpenALDLL, "alIsSource"); + if (lpOALFnTable->alIsSource == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alIsSource' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcei = (LPALSOURCEI)GetProcAddress(g_hOpenALDLL, "alSourcei"); + if (lpOALFnTable->alSourcei == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcei' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcef = (LPALSOURCEF)GetProcAddress(g_hOpenALDLL, "alSourcef"); + if (lpOALFnTable->alSourcef == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcef' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSource3f = (LPALSOURCE3F)GetProcAddress(g_hOpenALDLL, "alSource3f"); + if (lpOALFnTable->alSource3f == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSource3f' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcefv = (LPALSOURCEFV)GetProcAddress(g_hOpenALDLL, "alSourcefv"); + if (lpOALFnTable->alSourcefv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcefv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetSourcei = (LPALGETSOURCEI)GetProcAddress(g_hOpenALDLL, "alGetSourcei"); + if (lpOALFnTable->alGetSourcei == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetSourcei' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetSourcef = (LPALGETSOURCEF)GetProcAddress(g_hOpenALDLL, "alGetSourcef"); + if (lpOALFnTable->alGetSourcef == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetSourcef' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetSourcefv = (LPALGETSOURCEFV)GetProcAddress(g_hOpenALDLL, "alGetSourcefv"); + if (lpOALFnTable->alGetSourcefv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetSourcefv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcePlayv = (LPALSOURCEPLAYV)GetProcAddress(g_hOpenALDLL, "alSourcePlayv"); + if (lpOALFnTable->alSourcePlayv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcePlayv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcePausev = (LPALSOURCEPAUSEV)GetProcAddress(g_hOpenALDLL, "alSourcePausev"); + if (lpOALFnTable->alSourcePausev == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcePausev' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourceStopv = (LPALSOURCESTOPV)GetProcAddress(g_hOpenALDLL, "alSourceStopv"); + if (lpOALFnTable->alSourceStopv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourceStopv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcePlay = (LPALSOURCEPLAY)GetProcAddress(g_hOpenALDLL, "alSourcePlay"); + if (lpOALFnTable->alSourcePlay == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcePlay' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourcePause = (LPALSOURCEPAUSE)GetProcAddress(g_hOpenALDLL, "alSourcePause"); + if (lpOALFnTable->alSourcePause == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourcePause' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourceStop = (LPALSOURCESTOP)GetProcAddress(g_hOpenALDLL, "alSourceStop"); + if (lpOALFnTable->alSourceStop == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourceStop' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGenBuffers = (LPALGENBUFFERS)GetProcAddress(g_hOpenALDLL, "alGenBuffers"); + if (lpOALFnTable->alGenBuffers == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGenBuffers' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alDeleteBuffers = (LPALDELETEBUFFERS)GetProcAddress(g_hOpenALDLL, "alDeleteBuffers"); + if (lpOALFnTable->alDeleteBuffers == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alDeleteBuffers' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alIsBuffer = (LPALISBUFFER)GetProcAddress(g_hOpenALDLL, "alIsBuffer"); + if (lpOALFnTable->alIsBuffer == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alIsBuffer' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alBufferData = (LPALBUFFERDATA)GetProcAddress(g_hOpenALDLL, "alBufferData"); + if (lpOALFnTable->alBufferData == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alBufferData' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetBufferi = (LPALGETBUFFERI)GetProcAddress(g_hOpenALDLL, "alGetBufferi"); + if (lpOALFnTable->alGetBufferi == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetBufferi' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alGetBufferf = (LPALGETBUFFERF)GetProcAddress(g_hOpenALDLL, "alGetBufferf"); + if (lpOALFnTable->alGetBufferf == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alGetBufferf' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourceQueueBuffers = (LPALSOURCEQUEUEBUFFERS)GetProcAddress(g_hOpenALDLL, "alSourceQueueBuffers"); + if (lpOALFnTable->alSourceQueueBuffers == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourceQueueBuffers' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSourceUnqueueBuffers = (LPALSOURCEUNQUEUEBUFFERS)GetProcAddress(g_hOpenALDLL, "alSourceUnqueueBuffers"); + if (lpOALFnTable->alSourceUnqueueBuffers == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSourceUnqueueBuffers' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alDistanceModel = (LPALDISTANCEMODEL)GetProcAddress(g_hOpenALDLL, "alDistanceModel"); + if (lpOALFnTable->alDistanceModel == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alDistanceModel' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alDopplerFactor = (LPALDOPPLERFACTOR)GetProcAddress(g_hOpenALDLL, "alDopplerFactor"); + if (lpOALFnTable->alDopplerFactor == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alDopplerFactor' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alDopplerVelocity = (LPALDOPPLERVELOCITY)GetProcAddress(g_hOpenALDLL, "alDopplerVelocity"); + if (lpOALFnTable->alDopplerVelocity == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alDopplerVelocity' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetString = (LPALCGETSTRING)GetProcAddress(g_hOpenALDLL, "alcGetString"); + if (lpOALFnTable->alcGetString == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetString' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetIntegerv = (LPALCGETINTEGERV)GetProcAddress(g_hOpenALDLL, "alcGetIntegerv"); + if (lpOALFnTable->alcGetIntegerv == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetIntegerv' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcOpenDevice = (LPALCOPENDEVICE)GetProcAddress(g_hOpenALDLL, "alcOpenDevice"); + if (lpOALFnTable->alcOpenDevice == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcOpenDevice' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcCloseDevice = (LPALCCLOSEDEVICE)GetProcAddress(g_hOpenALDLL, "alcCloseDevice"); + if (lpOALFnTable->alcCloseDevice == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcCloseDevice' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcCreateContext = (LPALCCREATECONTEXT)GetProcAddress(g_hOpenALDLL, "alcCreateContext"); + if (lpOALFnTable->alcCreateContext == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcCreateContext' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcMakeContextCurrent = (LPALCMAKECONTEXTCURRENT)GetProcAddress(g_hOpenALDLL, "alcMakeContextCurrent"); + if (lpOALFnTable->alcMakeContextCurrent == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcMakeContextCurrent' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcProcessContext = (LPALCPROCESSCONTEXT)GetProcAddress(g_hOpenALDLL, "alcProcessContext"); + if (lpOALFnTable->alcProcessContext == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcProcessContext' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetCurrentContext = (LPALCGETCURRENTCONTEXT)GetProcAddress(g_hOpenALDLL, "alcGetCurrentContext"); + if (lpOALFnTable->alcGetCurrentContext == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetCurrentContext' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetContextsDevice = (LPALCGETCONTEXTSDEVICE)GetProcAddress(g_hOpenALDLL, "alcGetContextsDevice"); + if (lpOALFnTable->alcGetContextsDevice == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetContextsDevice' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcSuspendContext = (LPALCSUSPENDCONTEXT)GetProcAddress(g_hOpenALDLL, "alcSuspendContext"); + if (lpOALFnTable->alcSuspendContext == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcSuspendContext' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcDestroyContext = (LPALCDESTROYCONTEXT)GetProcAddress(g_hOpenALDLL, "alcDestroyContext"); + if (lpOALFnTable->alcDestroyContext == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcDestroyContext' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetError = (LPALCGETERROR)GetProcAddress(g_hOpenALDLL, "alcGetError"); + if (lpOALFnTable->alcGetError == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetError' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcIsExtensionPresent = (LPALCISEXTENSIONPRESENT)GetProcAddress(g_hOpenALDLL, "alcIsExtensionPresent"); + if (lpOALFnTable->alcIsExtensionPresent == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcIsExtensionPresent' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetProcAddress = (LPALCGETPROCADDRESS)GetProcAddress(g_hOpenALDLL, "alcGetProcAddress"); + if (lpOALFnTable->alcGetProcAddress == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetProcAddress' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alcGetEnumValue = (LPALCGETENUMVALUE)GetProcAddress(g_hOpenALDLL, "alcGetEnumValue"); + if (lpOALFnTable->alcGetEnumValue == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alcGetEnumValue' function address\n")); + return AL_FALSE; + } + lpOALFnTable->alSpeedOfSound = (LPALSPEEDOFSOUND)GetProcAddress(g_hOpenALDLL, "alSpeedOfSound"); + if (lpOALFnTable->alSpeedOfSound == NULL) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "LoadOAL10Library", "Failed to retrieve 'alSpeedOfSound' function address, this is not OpenAL version 1.1\n")); + return AL_FALSE; + } + + return AL_TRUE; +} + +ALvoid UnloadOALLibrary() +{ + // Unload the dll + if (g_hOpenALDLL) + { + FreeLibrary(g_hOpenALDLL); + g_hOpenALDLL = NULL; + } +} \ No newline at end of file diff --git a/lib/openal/utils/LoadOAL.h b/lib/openal/utils/LoadOAL.h new file mode 100755 index 0000000..8877a66 --- /dev/null +++ b/lib/openal/utils/LoadOAL.h @@ -0,0 +1,173 @@ +#include "../include/al/al.h" +#include "../include/al/alc.h" + +// Open AL Function table definition + +#ifndef _OPENALFNTABLE +#define _OPENALFNTABLE + +// AL 1.0 did not define the ALchar and ALCchar types, so define them here +// if they don't exist + +#ifndef ALchar +#define ALchar char +#endif + +#ifndef ALCchar +#define ALCchar char +#endif + +// Complete list of functions available in AL 1.0 implementations + +typedef void (ALAPIENTRY *LPALENABLE)( ALenum capability ); +typedef void (ALAPIENTRY *LPALDISABLE)( ALenum capability ); +typedef ALboolean (ALAPIENTRY *LPALISENABLED)( ALenum capability ); +typedef const ALchar* (ALAPIENTRY *LPALGETSTRING)( ALenum param ); +typedef void (ALAPIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data ); +typedef void (ALAPIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data ); +typedef void (ALAPIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data ); +typedef void (ALAPIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data ); +typedef ALboolean (ALAPIENTRY *LPALGETBOOLEAN)( ALenum param ); +typedef ALint (ALAPIENTRY *LPALGETINTEGER)( ALenum param ); +typedef ALfloat (ALAPIENTRY *LPALGETFLOAT)( ALenum param ); +typedef ALdouble (ALAPIENTRY *LPALGETDOUBLE)( ALenum param ); +typedef ALenum (ALAPIENTRY *LPALGETERROR)( void ); +typedef ALboolean (ALAPIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname ); +typedef void* (ALAPIENTRY *LPALGETPROCADDRESS)( const ALchar* fname ); +typedef ALenum (ALAPIENTRY *LPALGETENUMVALUE)( const ALchar* ename ); +typedef void (ALAPIENTRY *LPALLISTENERF)( ALenum param, ALfloat value ); +typedef void (ALAPIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALLISTENERI)( ALenum param, ALint value ); +typedef void (ALAPIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +typedef void (ALAPIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources ); +typedef void (ALAPIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources ); +typedef ALboolean (ALAPIENTRY *LPALISSOURCE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value); +typedef void (ALAPIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value); +typedef void (ALAPIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (ALAPIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPLAY)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCESTOP)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEREWIND)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids ); +typedef void (ALAPIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids ); +typedef void (ALAPIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers ); +typedef void (ALAPIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers ); +typedef ALboolean (ALAPIENTRY *LPALISBUFFER)( ALuint bid ); +typedef void (ALAPIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +typedef void (ALAPIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALDOPPLERFACTOR)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDOPPLERVELOCITY)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel ); + +typedef ALCcontext * (ALCAPIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALCAPIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALCAPIENTRY *LPALCGETCURRENTCONTEXT)( ALCvoid ); +typedef ALCdevice * (ALCAPIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALCAPIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALCAPIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALCAPIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALCAPIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALCAPIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALCAPIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALCAPIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALCAPIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); + +/* + This is in OAL1.1 +*/ +typedef void (ALAPIENTRY *LPALSPEEDOFSOUND)( ALfloat value ); + + +typedef struct +{ + LPALENABLE alEnable; + LPALDISABLE alDisable; + LPALISENABLED alIsEnabled; + LPALGETBOOLEAN alGetBoolean; + LPALGETINTEGER alGetInteger; + LPALGETFLOAT alGetFloat; + LPALGETDOUBLE alGetDouble; + LPALGETBOOLEANV alGetBooleanv; + LPALGETINTEGERV alGetIntegerv; + LPALGETFLOATV alGetFloatv; + LPALGETDOUBLEV alGetDoublev; + LPALGETSTRING alGetString; + LPALGETERROR alGetError; + LPALISEXTENSIONPRESENT alIsExtensionPresent; + LPALGETPROCADDRESS alGetProcAddress; + LPALGETENUMVALUE alGetEnumValue; + LPALLISTENERI alListeneri; + LPALLISTENERF alListenerf; + LPALLISTENER3F alListener3f; + LPALLISTENERFV alListenerfv; + LPALGETLISTENERI alGetListeneri; + LPALGETLISTENERF alGetListenerf; + LPALGETLISTENER3F alGetListener3f; + LPALGETLISTENERFV alGetListenerfv; + LPALGENSOURCES alGenSources; + LPALDELETESOURCES alDeleteSources; + LPALISSOURCE alIsSource; + LPALSOURCEI alSourcei; + LPALSOURCEF alSourcef; + LPALSOURCE3F alSource3f; + LPALSOURCEFV alSourcefv; + LPALGETSOURCEI alGetSourcei; + LPALGETSOURCEF alGetSourcef; + LPALGETSOURCEFV alGetSourcefv; + LPALSOURCEPLAYV alSourcePlayv; + LPALSOURCEPAUSEV alSourcePausev; + LPALSOURCESTOPV alSourceStopv; + LPALSOURCEPLAY alSourcePlay; + LPALSOURCEPAUSE alSourcePause; + LPALSOURCESTOP alSourceStop; + LPALGENBUFFERS alGenBuffers; + LPALDELETEBUFFERS alDeleteBuffers; + LPALISBUFFER alIsBuffer; + LPALBUFFERDATA alBufferData; + LPALGETBUFFERI alGetBufferi; + LPALGETBUFFERF alGetBufferf; + LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers; + LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers; + LPALDISTANCEMODEL alDistanceModel; + LPALDOPPLERFACTOR alDopplerFactor; + LPALDOPPLERVELOCITY alDopplerVelocity; + LPALCGETSTRING alcGetString; + LPALCGETINTEGERV alcGetIntegerv; + LPALCOPENDEVICE alcOpenDevice; + LPALCCLOSEDEVICE alcCloseDevice; + LPALCCREATECONTEXT alcCreateContext; + LPALCMAKECONTEXTCURRENT alcMakeContextCurrent; + LPALCPROCESSCONTEXT alcProcessContext; + LPALCGETCURRENTCONTEXT alcGetCurrentContext; + LPALCGETCONTEXTSDEVICE alcGetContextsDevice; + LPALCSUSPENDCONTEXT alcSuspendContext; + LPALCDESTROYCONTEXT alcDestroyContext; + LPALCGETERROR alcGetError; + LPALCISEXTENSIONPRESENT alcIsExtensionPresent; + LPALCGETPROCADDRESS alcGetProcAddress; + LPALCGETENUMVALUE alcGetEnumValue; + + LPALSPEEDOFSOUND alSpeedOfSound; +} OPENALFNTABLE, *LPOPENALFNTABLE; +#endif + +ALboolean LoadOALLibrary(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable); +ALvoid UnloadOALLibrary(); diff --git a/lib/sfx/foreverb/3dl2.h b/lib/sfx/foreverb/3dl2.h new file mode 100755 index 0000000..57a5a45 --- /dev/null +++ b/lib/sfx/foreverb/3dl2.h @@ -0,0 +1,218 @@ +// 3DL2.H +// + +// #ifndef 3DL2_H_INCLUDED +// #define 3DL2_H_INCLUDED + +#ifndef __3DL2_H +#define __3DL2_H + +// #include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/* +#pragma pack(push, 4) + +// I3DL2 listener property set {DA0F0520-300A-11D3-8A2B-0060970DB011} +DEFINE_GUID(DSPROPSETID_I3DL2_ListenerProperties, + 0xDA0F0520, + 0x300A, + 0x11D3, + 0x8A2B, + 0x0060970DB011); +*/ + +typedef enum +{ + DSPROPERTY_I3DL2LISTENER_ALL, // sets all I3DL2 listener properties + DSPROPERTY_I3DL2LISTENER_ROOM, // room effect level at low frequencies + DSPROPERTY_I3DL2LISTENER_ROOMHF, // room effect high-frequency level re. low frequency level + DSPROPERTY_I3DL2LISTENER_ROOMROLLOFFFACTOR, // like DS3D flRolloffFactor but for room effect + DSPROPERTY_I3DL2LISTENER_DECAYTIME, // reverberation decay time at low-frequencies + DSPROPERTY_I3DL2LISTENER_DECAYHFRATIO, // high-frequency to low-frequency decay time ratio + DSPROPERTY_I3DL2LISTENER_REFLECTIONS, // early reflections level relative to room effect + DSPROPERTY_I3DL2LISTENER_REFLECTIONSDELAY, // delay time of first reflection + DSPROPERTY_I3DL2LISTENER_REVERB, // late reverberation level relative to room effect + DSPROPERTY_I3DL2LISTENER_REVERBDELAY, // late reverberation delay time relative to first reflection + DSPROPERTY_I3DL2LISTENER_DIFFUSION, // reverberation diffusion (echo density) + DSPROPERTY_I3DL2LISTENER_DENSITY, // reverberation density (modal density) + DSPROPERTY_I3DL2LISTENER_HFREFERENCE // reference high frequency +} DSPROPERTY_I3DL2_LISTENERPROPERTY; + +// use this structure for DSPROPERTY_I3DL2LISTENER_ALL +// - all levels are hundredths of decibels (mB) +// - all times are in seconds (s) +typedef struct _I3DL2_LISTENERPROPERTIES +{ + long lRoom; // [-10000, 0] default: -10000 mB + long lRoomHF; // [-10000, 0] default: 0 mB + float flRoomRolloffFactor; // [0.0, 10.0] default: 0.0 + float flDecayTime; // [0.1, 20.0] default: 1.0 s + float flDecayHFRatio; // [0.1, 2.0] default: 0.5 + long lReflections; // [-10000, 1000] default: -10000 mB + float flReflectionsDelay; // [0.0, 0.3] default: 0.02 s + long lReverb; // [-10000, 2000] default: -10000 mB + float flReverbDelay; // [0.0, 0.1] default: 0.04 s + float flDiffusion ; // [0.0, 100.0] default: 100.0 % + float flDensity; // [0.0, 100.0] default: 100.0 % + float flHFReference; // [20.0, 20000.0] default: 5000.0 Hz +} I3DL2_LISTENERPROPERTIES, *LPI3DL2_LISTENERPROPERTIES; + +// property ranges and defaults: + +#define I3DL2LISTENER_MINROOM -10000 +#define I3DL2LISTENER_MAXROOM 0 +#define I3DL2LISTENER_DEFAULTROOM -10000 + +#define I3DL2LISTENER_MINROOMHF -10000 +#define I3DL2LISTENER_MAXROOMHF 0 +#define I3DL2LISTENER_DEFAULTROOMHF 0 + +#define I3DL2LISTENER_MINROOMROLLOFFFACTOR 0.0f +#define I3DL2LISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define I3DL2LISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define I3DL2LISTENER_MINDECAYTIME 0.1f +#define I3DL2LISTENER_MAXDECAYTIME 20.0f +#define I3DL2LISTENER_DEFAULTDECAYTIME 1.0f + +#define I3DL2LISTENER_MINDECAYHFRATIO 0.1f +#define I3DL2LISTENER_MAXDECAYHFRATIO 2.0f +#define I3DL2LISTENER_DEFAULTDECAYHFRATIO 0.5f + +#define I3DL2LISTENER_MINREFLECTIONS -10000 +#define I3DL2LISTENER_MAXREFLECTIONS 1000 +#define I3DL2LISTENER_DEFAULTREFLECTIONS -10000 + +#define I3DL2LISTENER_MINREFLECTIONSDELAY 0.0f +#define I3DL2LISTENER_MAXREFLECTIONSDELAY 0.3f +#define I3DL2LISTENER_DEFAULTREFLECTIONSDELAY 0.02f + +#define I3DL2LISTENER_MINREVERB -10000 +#define I3DL2LISTENER_MAXREVERB 2000 +#define I3DL2LISTENER_DEFAULTREVERB 0 // -10000 + +#define I3DL2LISTENER_MINREVERBDELAY 0.0f +#define I3DL2LISTENER_MAXREVERBDELAY 0.1f +#define I3DL2LISTENER_DEFAULTREVERBDELAY 0.04f + +#define I3DL2LISTENER_MINDIFFUSION 0.0f +#define I3DL2LISTENER_MAXDIFFUSION 100.0f +#define I3DL2LISTENER_DEFAULTDIFFUSION 100.0f + +#define I3DL2LISTENER_MINDENSITY 0.0f +#define I3DL2LISTENER_MAXDENSITY 100.0f +#define I3DL2LISTENER_DEFAULTDENSITY 100.0f + +#define I3DL2LISTENER_MINHFREFERENCE 20.0f +#define I3DL2LISTENER_MAXHFREFERENCE 20000.0f +#define I3DL2LISTENER_DEFAULTHFREFERENCE 5000.0f + +// I3DL2 buffer property set {DA0F0521-300A-11D3-8A2B-0060970DB011} +/* +DEFINE_GUID(DSPROPSETID_I3DL2_BufferProperties, + 0xDA0F0521, + 0x300A, + 0x11D3, + 0x8A2B, + 0x0060970DB011); +*/ + +/* +typedef enum +{ + DSPROPERTY_I3DL2BUFFER_ALL, // sets all I3DL2 buffer properties + DSPROPERTY_I3DL2BUFFER_OBSTRUCTIONALL, // sets both obstruction properties + DSPROPERTY_I3DL2BUFFER_OCCLUSIONALL, // sets both occlusion properties + DSPROPERTY_I3DL2BUFFER_DIRECT, // additional direct path level correction + DSPROPERTY_I3DL2BUFFER_DIRECTHF, // additional direct path high-frequency re. low-frequency level correction + DSPROPERTY_I3DL2BUFFER_ROOM, // additional room effect level correction + DSPROPERTY_I3DL2BUFFER_ROOMHF, // additional room effect high-frequency re. low-frequency level correction + DSPROPERTY_I3DL2BUFFER_ROOMROLLOFFFACTOR, // like DS3D flRolloffFactor but for room effect + DSPROPERTY_I3DL2BUFFER_OBSTRUCTION, // main obstruction control (attenuation at high frequencies) + DSPROPERTY_I3DL2BUFFER_OBSTRUCTIONLFRATIO, // obstruction low-frequency re. high-frequency ratio + DSPROPERTY_I3DL2BUFFER_OCCLUSION, // main occlusion control (attenuation at high frequencies) + DSPROPERTY_I3DL2BUFFER_OCCLUSIONLFRATIO // occlusion low-frequency re. high-frequency ratio +} DSPROPERTY_I3DL2_BUFFERPROPERTY; +*/ + +/* +// use this structure for DSPROPERTY_I3DL2BUFFER_OBSTRUCTIONALL +// - all levels are hundredths of decibels (mB) +typedef struct _I3DL2_OBSTRUCTIONPROPERTIES +{ + LONG lHFLevel; // [-10000, 0] default: 0 mB + FLOAT flLFRatio; // [0.0, 1.0] default: 0.0 +} I3DL2_OBSTRUCTIONPROPERTIES, *LPI3DL2_OBSTRUCTIONPROPERTIES; + +// use this structure for DSPROPERTY_I3DL2BUFFER_OCCLUSIONALL +// - all levels are hundredths of decibels (mB) +typedef struct _I3DL2_OCCLUSIONPROPERTIES +{ + LONG lHFLevel; // [-10000, 0] default: 0 mB + FLOAT flLFRatio; // [0.0, 1.0] default: 0.25 +} I3DL2_OCCLUSIONPROPERTIES, *LPI3DL2_OCCLUSIONPROPERTIES; + +// use this structure for DSPROPERTY_I3DL2BUFFER_ALL +// - all levels are hundredths of decibels (mB) +typedef struct _I3DL2_BUFFERPROPERTIES +{ + LONG lDirect; // [-10000, 1000] default: 0 mB + LONG lDirectHF; // [-10000, 0] default: 0 mB + LONG lRoom; // [-10000, 1000] default: 0 mB + LONG lRoomHF; // [-10000, 0] default: 0 mB + FLOAT flRoomRolloffFactor; // [0.0, 10.0] default: 0.0 + I3DL2BUFFER_OBSTRUCTIONPROPERTIES Obstruction; + I3DL2BUFFER_OCCLUSIONPROPERTIES Occlusion; +} I3DL2_BUFFERPROPERTIES, *LPI3DL2_BUFFERPROPERTIES; + +// property ranges and defaults: + +#define I3DL2BUFFER_MINDIRECT -10000 +#define I3DL2BUFFER_MAXDIRECT 1000 +#define I3DL2BUFFER_DEFAULTDIRECT 0 + +#define I3DL2BUFFER_MINDIRECTHF -10000 +#define I3DL2BUFFER_MAXDIRECTHF 0 +#define I3DL2BUFFER_DEFAULTDIRECTHF 0 + +#define I3DL2BUFFER_MINROOM -10000 +#define I3DL2BUFFER_MAXROOM 1000 +#define I3DL2BUFFER_DEFAULTROOM 0 + +#define I3DL2BUFFER_MINROOMHF -10000 +#define I3DL2BUFFER_MAXROOMHF 0 +#define I3DL2BUFFER_DEFAULTROOMHF 0 + +#define I3DL2BUFFER_MINROOMROLLOFFFACTOR 0.0f +#define I3DL2BUFFER_MAXROOMROLLOFFFACTOR 10.f +#define I3DL2BUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define I3DL2BUFFER_MINOBSTRUCTION -10000 +#define I3DL2BUFFER_MAXOBSTRUCTION 0 +#define I3DL2BUFFER_DEFAULTOBSTRUCTION 0 + +#define I3DL2BUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define I3DL2BUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define I3DL2BUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define I3DL2BUFFER_MINOCCLUSION -10000 +#define I3DL2BUFFER_MAXOCCLUSION 0 +#define I3DL2BUFFER_DEFAULTOCCLUSION 0 + +#define I3DL2BUFFER_MINOCCLUSIONLFRATIO 0.0f +#define I3DL2BUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define I3DL2BUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f +*/ + +// #pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif diff --git a/lib/sfx/foreverb/aSfxDsp.cpp b/lib/sfx/foreverb/aSfxDsp.cpp new file mode 100755 index 0000000..eab2136 --- /dev/null +++ b/lib/sfx/foreverb/aSfxDsp.cpp @@ -0,0 +1,967 @@ +#include + +#include "aSfxDsp.hpp" +#include "3dl2.h" + +#include +#include // for memmove and FMOD_memcpy + +#include "../../../src/fmod_memory.h" +#include "../../../src/fmod_types.h" + +#define ADD_ANTI_DENORMALS_TO_INPUT + +#define SfxDataMove(source, dest, bytes) memmove(dest, source, (unsigned)bytes) + +//----------------------------------------------------------------------------- + +int ASfxDsp::init(float rate) +{ + int delayLine; + int error = 0; + + mEarlyLateDelay = NULL; + mEarlyDelay = NULL; + + ZeroWritePointers(); + + mEarlyLateSec[0] = 0.040f; + mEarlyLateSamples[0] = (int)(mEarlyLateSec[0] * rate); + SetLate_EarlyLateDelayTaps(0.060f, kEarlyLateNextLengthSec, kEarlyLateDelayRatio, rate); + + + for (delayLine=0; delayLine + +struct DspValues +{ + short reinitialize; // 0 means in progress. 1 means settings have changed, start over +}; + +struct coeff2ndorder +{ + float a0, a1, a2, b1, b2; +}; + +#define kNumLateReverbDelays 8 +// Only feed the first 8 or so late reverb inputs at first? +#define kNumMatrixStages 3 // 2 ^ kNumMatrixStages should equal kNumLateReverbDelays +#define kNumEarly_EarlyLateDelayTaps 1 +#define kNumLate_EarlyLateDelayTaps 8 // Should equal kNumLateReverbDelays +#define kNumEarlyLateDelayTaps (kNumEarly_EarlyLateDelayTaps + kNumLate_EarlyLateDelayTaps) +#define kNumEarlyDelayTaps 7 +#define kNumAllpassDelays 2 + +#define kSFXREVERB_MAXCHANNELS 8 + +#define kEarlyLateDelayLen 65536 // Delete +#define kEarlyDelayLenSamples 16384 // Delete +#define kAllpassLengthSamples 2048 // Delete + +// Main late delays +#define LOWEST_DELAY_LINE_LENGTH_SEC (0.061f) +#define DELAY_LINE_RATIO (1.32f) +#define LOWEST_DELAY_LINE_LENGTH_B_SEC (0.0015f) +#define DELAY_LINE_RATIO_B (1.47f) + +// Allpass delays +#define kAllpassDelayLenSec0 (0.0059f) +#define kAllpassDelayLenSec1 (0.0077f) + +// EarlyLate delays +#define kEarlyLateNextLengthSec (0.0187f) +#define kEarlyLateDelayRatio (1.29f) + +// Early delays +#define kEarlyFirstDelaySec (0.005f) +#define kEarlyDelayRatio (1.59f) + +// Error codes +#define REVERB_ALLPASS_ALLOCATION_ERR 4500 +#define REVERB_EARLYLATE_ALLOCATION_ERR 4501 +#define REVERB_EARLY_ALLOCATION_ERR 4502 +#define REVERB_LATE_ALLOCATION_ERR 4503 +#define REVERB_PARAMETER_ERROR 4504 +#define REVERB_INBUFF_ALLOCATION_ERR 4505 + +namespace FMOD +{ + class SystemI; +} + +class ASfxDsp +{ +public: + int init(float rate); + void close(); + + int UpdateBufferSize(int newMaxBlockSize); // possibly reallocate mInBuff buffers to new MaxBlockSize + void ClearBuffers(); // zero the audio buffers + void ClearInBuff(); + void ClearReverbInternalBuffers(); + int AllocateEarlyLateDelay(float *delaySec, float sampleRate); + int AllocateAllpassDelays(int numDelays, float *delaySec, float sampleRate); + int AllocateEarlyDelay(float delaySec, float sampleRate); + int AllocateLateDelays(int numDelays, float *delaySec, float sampleRate); + int NextPowerOf2(int delaySamples); + + void DoDSPProcessing (float *inAudio, float *outAudio, int channels, unsigned int sampleframes, float rate, float drymix, unsigned short speakermask); + void BlockProcessInput(unsigned int sampframes, int channels, float *inAudio, float rate); + +public: + void SetAllpassDelays(float rate); + void SetLate_EarlyLateDelayTaps(float refPlusRevDelaySec, float nextLengthSec, float delayRatio, float rate); + void SetEarlyDelay(float nextLengthSec, float delayRatio, float rate); + void SetLateDelays(float nextLengthSec, float delayRatio, float nextLengthBSec, float delayRatioB, float rate); + void DeallocateLateDelays(); + void DeallocateAllpassDelays(); + void DeallocateEarlyLateDelay(); + void DeallocateEarlyDelay(); + void ZeroWritePointers(); + + #if defined(PLATFORM_PS3) //|| defined(PLATFORM_WINDOWS_PS3MODE) + float mInBuffMemory[256 + 4]; /* +4 = 16bytes extra for alignment */ + #else + float *mInBuffMemory; + #endif + float *mInBuff; + + int mNumAllocatedInBuffSamples; // the number of samples per mInBuff currently allocated + // (varies according to MaxBlockSize) + + #if defined(PLATFORM_PS3) //|| defined(PLATFORM_WINDOWS_PS3MODE) + float FMOD_PPCALIGN16(mTempbuff[(9*256) + 4]); // 8k + #endif + + FMOD::SystemI *mSystem; + + float mRoomHF; + bool mRoomHFChanged; + float mLatchRoomHF; // Latch it for safety + float mPrevRoomHF; // For our interpolation + float mOldRoomHF; // For next time + float mzRoomHF0, mzRoomHF1; + + struct coeff2ndorder mRoomLFcoeffs; + float mRoomLF; + float mzRoomLF0, mzRoomLF1; + + float FMOD_PPCALIGN16(mDecayHF[kNumLateReverbDelays]); + bool FMOD_PPCALIGN16(mDecayHFChanged[kNumLateReverbDelays]); + float FMOD_PPCALIGN16(mLatchDecayHF[kNumLateReverbDelays]); // Latch it for safety. + float FMOD_PPCALIGN16(mLatchOneMinusDecayHF[kNumLateReverbDelays]); + float FMOD_PPCALIGN16(mPrevDecayHF[kNumLateReverbDelays]); // Latch previous one + float FMOD_PPCALIGN16(mOldDecayHF[kNumLateReverbDelays]); // Save current one for next time... + float FMOD_PPCALIGN16(mMatrixMem[kNumMatrixStages * kNumLateReverbDelays + 16]); + float FMOD_PPCALIGN16(mzDecayHFMem[kNumLateReverbDelays + 16]); + float *FMOD_PPCALIGN16(mzDecayHF); + float *FMOD_PPCALIGN16(mMatrix); + float FMOD_PPCALIGN16(mFeedback[kNumLateReverbDelays]); + float FMOD_PPCALIGN16(mLatchFeedback[kNumLateReverbDelays]); // Latch it for safety + float FMOD_PPCALIGN16(mOldFeedback[kNumLateReverbDelays]); // Save for next buffer + float FMOD_PPCALIGN16(mPrevFeedback[kNumLateReverbDelays]); // Use previous value for crossfade + + float mHadamard; + bool mHadamardChanged; + float mLatchHadamard; // Latch it for safety + float mPrevHadamard; // For our interpolation + float mOldHadamard; // For next time + + float mERgain, mLRgain; + bool mLRgainChanged; + float mLatchLRgain; // Latch it for safety + float mPrevLRgain; // For our interpolation + float mOldLRgain; // For next time + bool mERgainChanged; + float mLatchERgain; // Latch it for safety + float mPrevERgain; // For our interpolation + float mOldERgain; // For next time + + float mDiffusionScale; + bool mDiffusionScaleChanged; + float mLatchDiffusionScale; // Latch it for safety + float mPrevDiffusionScale; // For our interpolation + float mOldDiffusionScale; // For next time + + float mAllpassGain; + int mNumLateReverbDelays; + + // Late delays + #if defined(PLATFORM_PS3) //|| defined(PLATFORM_WINDOWS_PS3MODE) + float *FMOD_PPCALIGN16(mLateDelaysMemory[kNumLateReverbDelays]); + #endif + float *FMOD_PPCALIGN16(mLateDelays[kNumLateReverbDelays]); // Array of delay lines + float FMOD_PPCALIGN16(mLateDelayLenSec[kNumLateReverbDelays]); // Seconds + int FMOD_PPCALIGN16(mLateDelayLenSamples[kNumLateReverbDelays]); // Samples used + int FMOD_PPCALIGN16(mOldLateDelayLenSamples[kNumLateReverbDelays]); + int FMOD_PPCALIGN16(mLateDelayLenBSamples[kNumLateReverbDelays]); // Samples used + int FMOD_PPCALIGN16(mLateDelaySamplesAllocated[kNumLateReverbDelays]); // Samples allocated + bool FMOD_PPCALIGN16(mLateChanged[kNumLateReverbDelays]); // Either the delay lengths or mFeedback changed + int FMOD_PPCALIGN16(mLateMask[kNumLateReverbDelays]); + int FMOD_PPCALIGN16(mLateWritePointer[kNumLateReverbDelays]); + int FMOD_PPCALIGN16(mLateReadPointer[kNumLateReverbDelays]); + int FMOD_PPCALIGN16(mOldLateReadPointer[kNumLateReverbDelays]); + int FMOD_PPCALIGN16(mLateReadPointerB[kNumLateReverbDelays]); // Short (~10 ms) reads to avoid cancellations + + // EarlyLate delay + float *FMOD_PPCALIGN16(mEarlyLateDelay); + float *FMOD_PPCALIGN16(mEarlyLateDelayMemory); + float FMOD_PPCALIGN16(mEarlyLateSec[kNumEarlyLateDelayTaps]); + int FMOD_PPCALIGN16(mEarlyLateSamples[kNumEarlyLateDelayTaps]); + int FMOD_PPCALIGN16(mOldEarlyLateSamples[kNumEarlyLateDelayTaps]); + bool FMOD_PPCALIGN16(mEarlyLateChanged[kNumEarlyLateDelayTaps]); + int FMOD_PPCALIGN16(mEarlyLateSamplesAllocated); + int FMOD_PPCALIGN16(mEarlyLateMask); + int FMOD_PPCALIGN16(mEarlyLateWritePointer); + int FMOD_PPCALIGN16(mEarlyLateReadPointer[kNumEarlyLateDelayTaps]); + int FMOD_PPCALIGN16(mOldEarlyLateReadPointer[kNumEarlyLateDelayTaps]); + + // Early delay + #if defined(PLATFORM_PS3) //|| defined(PLATFORM_WINDOWS_PS3MODE) + float FMOD_PPCALIGN16(mEarlyDelay[4096]); + #else + float *mEarlyDelay; + #endif + float FMOD_PPCALIGN16(mEarlyDelayLenSec[kNumEarlyDelayTaps]); + int FMOD_PPCALIGN16(mEarlyDelayLenSamples[kNumEarlyDelayTaps]); + int FMOD_PPCALIGN16(mEarlyDelaySamplesAllocated); + int FMOD_PPCALIGN16(mEarlyMask); + int FMOD_PPCALIGN16(mEarlyWritePointer); + int FMOD_PPCALIGN16(mEarlyReadPointer[kNumEarlyDelayTaps]); + + // Allpass delays + #if defined(PLATFORM_PS3) //|| defined(PLATFORM_WINDOWS_PS3MODE) + float FMOD_PPCALIGN16(mAllpassDelays[kNumAllpassDelays][512]); + #else + float *mAllpassDelays[kNumAllpassDelays]; + #endif + float FMOD_PPCALIGN16(mAllpassDelayLenSec[kNumAllpassDelays]); + int FMOD_PPCALIGN16(mAllpassDelayLenSamples[kNumAllpassDelays]); + int FMOD_PPCALIGN16(mAllpassSamplesAllocated[kNumAllpassDelays]); + int FMOD_PPCALIGN16(mAllpassMask[kNumAllpassDelays]); + int FMOD_PPCALIGN16(mAllpassWritePointer[kNumAllpassDelays]); + int FMOD_PPCALIGN16(mAllpassReadPointer[kNumAllpassDelays]); + + int mNumMatrixStages; +}; + + +#endif // __ASFXDSP_H + diff --git a/lib/vst/AEffect.h b/lib/vst/AEffect.h new file mode 100755 index 0000000..936f08c --- /dev/null +++ b/lib/vst/AEffect.h @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------------------------------- +// VST Plug-Ins SDK +// Version 1.0 +// © 2003, Steinberg Media Technologies, All Rights Reserved +//------------------------------------------------------------------------------------------------------- + +#ifndef __AEffect__ +#define __AEffect__ + +/* to create an Audio Effect for power pc's, create a + code resource + file type: 'aPcs' + resource type: 'aEff' + ppc header: none (raw pef) + + for windows, it's a .dll + + the only symbol searched for is: + AEffect *main(float (*audioMaster)(AEffect *effect, long opcode, long index, + long value, void *ptr, float opt)); +*/ + +#if CARBON +#if PRAGMA_STRUCT_ALIGN || __MWERKS__ + #pragma options align=mac68k +#endif +#else +#if PRAGMA_ALIGN_SUPPORTED || __MWERKS__ + #pragma options align=mac68k +#endif +#endif +#if defined __BORLANDC__ + #pragma -a8 +#elif defined(WIN32) || defined(__FLAT__) || defined CBUILDER + #pragma pack(push) + #pragma pack(8) + #define VSTCALLBACK __cdecl +#else + #define VSTCALLBACK +#endif + + +//------------------------------------------------- +// Misc. Definition +//------------------------------------------------- + +typedef struct AEffect AEffect; +typedef long (VSTCALLBACK *audioMasterCallback)(AEffect *effect, long opcode, long index, + long value, void *ptr, float opt); + +// prototype for plug-in main +// AEffect *main(audioMasterCallback audioMaster); + +// Four Character Constant +#define CCONST(a, b, c, d) \ + ((((long)a) << 24) | (((long)b) << 16) | (((long)c) << 8) | (((long)d) << 0)) + +// Magic Number +#define kEffectMagic CCONST ('V', 's', 't', 'P') + + +//------------------------------------------------- +// AEffect Structure +//------------------------------------------------- +struct AEffect +{ + long magic; // must be kEffectMagic ('VstP') + + long (VSTCALLBACK *dispatcher)(AEffect *effect, long opCode, long index, long value, + void *ptr, float opt); + + void (VSTCALLBACK *process)(AEffect *effect, float **inputs, float **outputs, long sampleframes); + + void (VSTCALLBACK *setParameter)(AEffect *effect, long index, float parameter); + float (VSTCALLBACK *getParameter)(AEffect *effect, long index); + + long numPrograms; // number of Programs + long numParams; // all programs are assumed to have numParams parameters + long numInputs; // number of Audio Inputs + long numOutputs; // number of Audio Outputs + + long flags; // see constants (Flags Bits) + + long resvd1; // reserved for Host, must be 0 (Dont use it) + long resvd2; // reserved for Host, must be 0 (Dont use it) + + long initialDelay; // for algorithms which need input in the first place + + long realQualities; // number of realtime qualities (0: realtime) + long offQualities; // number of offline qualities (0: realtime only) + float ioRatio; // input samplerate to output samplerate ratio, not used yet + + void *object; // for class access (see AudioEffect.hpp), MUST be 0 else! + void *user; // user access + + long uniqueID; // pls choose 4 character as unique as possible. (register it at Steinberg Web) + // this is used to identify an effect for save+load + long version; // (example 1100 for version 1.1.0.0) + + void (VSTCALLBACK *processReplacing)(AEffect *effect, float **inputs, float **outputs, long sampleframes); + + char future[60]; // pls zero +}; + +//------------------------------------------------- +// Flags Bits +//------------------------------------------------- + +#define effFlagsHasEditor 1 // if set, is expected to react to editor messages +#define effFlagsHasClip 2 // return > 1. in getVu() if clipped +#define effFlagsHasVu 4 // return vu value in getVu(); > 1. means clipped +#define effFlagsCanMono 8 // if numInputs == 2, makes sense to be used for mono in +#define effFlagsCanReplacing 16 // supports in place output (processReplacing() exsists) +#define effFlagsProgramChunks 32 // program data are handled in formatless chunks + +//------------------------------------------------- +// Dispatcher OpCodes +//------------------------------------------------- + +enum +{ + effOpen = 0, // initialise + effClose, // exit, release all memory and other resources! + + effSetProgram, // program no in + effGetProgram, // return current program no. + effSetProgramName, // user changed program name (max 24 char + 0) to as passed in string + effGetProgramName, // stuff program name (max 24 char + 0) into string + + effGetParamLabel, // stuff parameter label (max 8 char + 0) into string + // (examples: sec, dB, type) + effGetParamDisplay, // stuff parameter textual representation into string + // (examples: 0.5, -3, PLATE) + effGetParamName, // stuff parameter label (max 8 char + 0) into string + // (examples: Time, Gain, RoomType) + effGetVu, // called if (flags & (effFlagsHasClip | effFlagsHasVu)) + + // system + effSetSampleRate, // in opt (float value in Hz; for example 44100.0Hz) + effSetBlockSize, // in value (this is the maximun size of an audio block, + // pls check sampleframes in process call) + effMainsChanged, // the user has switched the 'power on' button to + // value (0 off, else on). This only switches audio + // processing; you should flush delay buffers etc. + + // editor + effEditGetRect, // stuff rect (top, left, bottom, right) into ptr + effEditOpen, // system dependant Window pointer in ptr + effEditClose, // no arguments + effEditDraw, // draw method, ptr points to rect (MAC Only) + effEditMouse, // index: x, value: y (MAC Only) + effEditKey, // system keycode in value + effEditIdle, // no arguments. Be gentle! + effEditTop, // window has topped, no arguments + effEditSleep, // window goes to background + + effIdentify, // returns 'NvEf' + effGetChunk, // host requests pointer to chunk into (void**)ptr, byteSize returned + effSetChunk, // plug-in receives saved chunk, byteSize passed + + effNumOpcodes +}; + +//------------------------------------------------- +// AudioMaster OpCodes +//------------------------------------------------- + +enum +{ + audioMasterAutomate = 0, // index, value, returns 0 + audioMasterVersion, // VST Version supported (for example 2200 for VST 2.2) + audioMasterCurrentId, // Returns the unique id of a plug that's currently + // loading + audioMasterIdle, // Call application idle routine (this will + // call effEditIdle for all open editors too) + audioMasterPinConnected // Inquire if an input or output is beeing connected; + // index enumerates input or output counting from zero, + // value is 0 for input and != 0 otherwise. note: the + // return value is 0 for such that older versions + // will always return true. +}; + +#if CARBON +#if PRAGMA_STRUCT_ALIGN || __MWERKS__ + #pragma options align=reset +#endif +#else +#if PRAGMA_ALIGN_SUPPORTED || __MWERKS__ + #pragma options align=reset +#elif defined(WIN32) || defined(__FLAT__) + #pragma pack(pop) +#elif defined __BORLANDC__ + #pragma -a- +#endif +#endif + +#endif // __AEffect__ diff --git a/lib/vst/AudioEffect.hpp b/lib/vst/AudioEffect.hpp new file mode 100755 index 0000000..d21efcb --- /dev/null +++ b/lib/vst/AudioEffect.hpp @@ -0,0 +1,131 @@ +//------------------------------------------------------------------------------------------------------- +// VST Plug-Ins SDK +// Version 1.0 +// © 2003, Steinberg Media Technologies, All Rights Reserved +//------------------------------------------------------------------------------------------------------- + +#ifndef __AudioEffect__ +#define __AudioEffect__ + +#ifndef __AEffect__ +#include "AEffect.h" // "c" interface +#endif + +class AEffEditor; +class AudioEffect; + +//------------------------------------------------------------------------------------------------------- +// Needs to be defined by the audio effect and is +// called to create the audio effect object instance. +AudioEffect* createEffectInstance (audioMasterCallback audioMaster); + +long dispatchEffectClass (AEffect *e, long opCode, long index, long value, void *ptr, float opt); +float getParameterClass (long index); +void setParameterClass (long index, float value); +void processClass (AEffect *e, float **inputs, float **outputs, long sampleFrames); +void processClassReplacing (AEffect *e, float **inputs, float **outputs, long sampleFrames); + +//------------------------------------------------------------------------------------------------------- +class AudioEffect +{ +friend class AEffEditor; +friend long dispatchEffectClass (AEffect *e, long opCode, long index, long value, void *ptr, float opt); +friend float getParameterClass (AEffect *e, long index); +friend void setParameterClass (AEffect *e, long index, float value); +friend void processClass (AEffect *e, float **inputs, float **outputs, long sampleFrames); +friend void processClassReplacing (AEffect *e, float **inputs, float **outputs, long sampleFrames); + +public: + AudioEffect (audioMasterCallback audioMaster, long numPrograms, long numParams); + virtual ~AudioEffect (); + + virtual void setParameter (long index, float value) { index = index; value = value; } + virtual float getParameter (long index) { index = index; return 0; } + virtual void setParameterAutomated (long index, float value); + + AEffect *getAeffect () { return &cEffect; } // Returns the AEffect Structure + + void setEditor (AEffEditor *editor) + { this->editor = editor; + if (editor) cEffect.flags |= effFlagsHasEditor; + else cEffect.flags &= ~effFlagsHasEditor; } // Should be called if you want to define your own editor + + //---Called from audio master (Host -> Plug)--------------- + virtual void process (float **inputs, float **outputs, long sampleFrames) = 0; + virtual void processReplacing (float **inputs, float **outputs, long sampleFrames) + { inputs = inputs; outputs = outputs; sampleFrames = sampleFrames; } + + virtual long dispatcher (long opCode, long index, long value, void *ptr, float opt); // Opcodes dispatcher + + virtual void open () {} // Called when Plugin is initialized + virtual void close () {} // Called when Plugin will be released + + //---Program---------------------------- + virtual long getProgram () { return curProgram; } + virtual void setProgram (long program) { curProgram = program; }// Don't forget to set curProgram + virtual void setProgramName (char *name) { *name = 0; } // All following refer to curProgram + virtual void getProgramName (char *name) { *name = 0; } + + virtual void getParameterLabel (long index, char *label) { index = index; *label = 0; } // example: "dB" + virtual void getParameterDisplay (long index, char *text) { index = index; *text = 0; } // example: "6.01" + virtual void getParameterName (long index, char *text) { index = index; *text = 0; } // example: "Volume" + + virtual float getVu () { return 0; } + + virtual long getChunk (void** data, bool isPreset = false) { return 0; } // Returns the Size in bytes of the chunk (Plugin allocates the data array) + virtual long setChunk (void* data, long byteSize, bool isPreset = false) { return 0; } + + virtual void setSampleRate (float sampleRate) { this->sampleRate = sampleRate; } + virtual void setBlockSize (long blockSize) { this->blockSize = blockSize; } + + virtual void suspend () {} // Called when Plugin is switched to Off + virtual void resume () {} // Called when Plugin is switched to On + + //---Setup--------------------------- + virtual void setUniqueID (long iD) { cEffect.uniqueID = iD; } // must call this! + virtual void setNumInputs (long inputs) { cEffect.numInputs = inputs; } + virtual void setNumOutputs (long outputs) { cEffect.numOutputs = outputs; } + + virtual void hasVu (bool state = true); + virtual void hasClip (bool state = true); + virtual void canMono (bool state = true); + virtual void canProcessReplacing (bool state = true); // Tells that the processReplacing () could be used + virtual void programsAreChunks (bool state = true); + virtual void setRealtimeQualities (long qualities); + virtual void setOfflineQualities (long qualities); + virtual void setInitialDelay (long delay); // Uses to report the Plugin's latency (Group Delay) + + //---Inquiry------------------------- + virtual float getSampleRate () { return sampleRate; } + virtual long getBlockSize () { return blockSize; } + + //---Host communication-------------- + virtual long getMasterVersion (); + virtual long getCurrentUniqueId (); + virtual void masterIdle (); + virtual bool isInputConnected (long input); + virtual bool isOutputConnected (long output); + + //---Tools--------------------------- + virtual void dB2string (float value, char *text); + virtual void Hz2string (float samples, char *text); + virtual void ms2string (float samples, char *text); + virtual void float2string (float value, char *string); + virtual void long2string (long value, char *text); + +protected: + //---Members------------------------- + float sampleRate; + AEffEditor *editor; + audioMasterCallback audioMaster; + long numPrograms; + long numParams; + long curProgram; + long blockSize; + AEffect cEffect; +}; + +#endif // __AudioEffect__ +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- + diff --git a/lib/vst/aeffectx.h b/lib/vst/aeffectx.h new file mode 100755 index 0000000..9b2d168 --- /dev/null +++ b/lib/vst/aeffectx.h @@ -0,0 +1,1004 @@ +//------------------------------------------------------------------------------------------------------- +// VST Plug-Ins SDK +// Version 2.3 Extension +// © 2003, Steinberg Media Technologies, All Rights Reserved +//------------------------------------------------------------------------------------------------------- + +#ifndef __aeffectx__ +#define __aeffectx__ + +#ifndef __AEffect__ +#include "AEffect.h" +#endif + +#include // for strcpy + +#if PRAGMA_STRUCT_ALIGN || __MWERKS__ + #pragma options align=mac68k +#elif defined __BORLANDC__ + #pragma -a8 +#elif defined(WIN32) || defined(__FLAT__) + #pragma pack(push) + #pragma pack(8) +#endif + +//------------------------------------------------- +// VstEvent +//------------------------------------------------- + +typedef struct VstEvent VstEvent; +typedef struct VstMidiEvent VstMidiEvent; +typedef struct VstEvents VstEvents; + +struct VstEvent // a generic timestamped event +{ + long type; // see enum below + long byteSize; // of this event, excl. type and byteSize + long deltaFrames; // sample frames related to the current block start sample position + long flags; // generic flags, none defined yet (0) + + char data[16]; // size may vary but is usually 16 +}; + +//----VstEvent Types------------------------------- +enum +{ + kVstMidiType = 1, // midi event, can be cast as VstMidiEvent (see below) + kVstAudioType, // audio + kVstVideoType, // video + kVstParameterType, // parameter + kVstTriggerType, // trigger + kVstSysExType // midi system exclusive + // ...etc +}; + +struct VstEvents // a block of events for the current audio block +{ + long numEvents; + long reserved; // zero + VstEvent* events[2]; // variable +}; + +//---Defined Events-------------------------------- +struct VstMidiEvent // to be casted from a VstEvent +{ + long type; // kVstMidiType + long byteSize; // 24 + long deltaFrames; // sample frames related to the current block start sample position + long flags; // none defined yet + + long noteLength; // (in sample frames) of entire note, if available, else 0 + long noteOffset; // offset into note from note start if available, else 0 + + char midiData[4]; // 1 thru 3 midi bytes; midiData[3] is reserved (zero) + char detune; // -64 to +63 cents; for scales other than 'well-tempered' ('microtuning') + char noteOffVelocity; + char reserved1; // zero + char reserved2; // zero +}; + + +//------------------------------------------------- +// VstTimeInfo +//------------------------------------------------- + +typedef struct VstTimeInfo VstTimeInfo; + +// VstTimeInfo as requested via audioMasterGetTime (getTimeInfo()) +// refers to the current time slice. note the new slice is +// already started when processEvents() is called + +struct VstTimeInfo +{ + double samplePos; // current location + double sampleRate; + double nanoSeconds; // system time + double ppqPos; // 1 ppq + double tempo; // in bpm + double barStartPos; // last bar start, in 1 ppq + double cycleStartPos; // 1 ppq + double cycleEndPos; // 1 ppq + long timeSigNumerator; // time signature + long timeSigDenominator; + long smpteOffset; + long smpteFrameRate; // 0:24, 1:25, 2:29.97, 3:30, 4:29.97 df, 5:30 df + long samplesToNextClock; // midi clock resolution (24 ppq), can be negative + long flags; // see below +}; + +enum +{ + kVstTransportChanged = 1, // Indicates that Playing, Cycle or Recording has changed + kVstTransportPlaying = 1 << 1, + kVstTransportCycleActive = 1 << 2, + kVstTransportRecording = 1 << 3, + + kVstAutomationWriting = 1 << 6, + kVstAutomationReading = 1 << 7, + + // flags which indicate which of the fields in this VstTimeInfo + // are valid; samplePos and sampleRate are always valid + kVstNanosValid = 1 << 8, + kVstPpqPosValid = 1 << 9, + kVstTempoValid = 1 << 10, + kVstBarsValid = 1 << 11, + kVstCyclePosValid = 1 << 12, // start and end + kVstTimeSigValid = 1 << 13, + kVstSmpteValid = 1 << 14, + kVstClockValid = 1 << 15 +}; + +//------------------------------------------------- +// Variable IO for Offline Processing +//------------------------------------------------- + +typedef struct VstVariableIo VstVariableIo; + +struct VstVariableIo +{ + float **inputs; + float **outputs; + long numSamplesInput; + long numSamplesOutput; + long *numSamplesInputProcessed; + long *numSamplesOutputProcessed; +}; + +//------------------------------------------------- +// AudioMaster OpCodes +//------------------------------------------------- + +enum +{ + //---from here VST 2.0 extension opcodes------------------------------------------------------ + // VstEvents + VstTimeInfo + audioMasterWantMidi = audioMasterPinConnected + 2, // is a filter which is currently ignored + audioMasterGetTime, // returns const VstTimeInfo* (or 0 if not supported) + // should contain a mask indicating which fields are required + // (see valid masks above), as some items may require extensive + // conversions + audioMasterProcessEvents, // VstEvents* in + audioMasterSetTime, // VstTimenfo* in , filter in , not supported + audioMasterTempoAt, // returns tempo (in bpm * 10000) at sample frame location passed in + + // parameters + audioMasterGetNumAutomatableParameters, + audioMasterGetParameterQuantization, // returns the integer value for +1.0 representation, + // or 1 if full single float precision is maintained + // in automation. parameter index in (-1: all, any) + // connections, configuration + audioMasterIOChanged, // numInputs and/or numOutputs has changed + audioMasterNeedIdle, // plug needs idle calls (outside its editor window) + audioMasterSizeWindow, // index: width, value: height + audioMasterGetSampleRate, + audioMasterGetBlockSize, + audioMasterGetInputLatency, + audioMasterGetOutputLatency, + audioMasterGetPreviousPlug, // input pin in (-1: first to come), returns cEffect* + audioMasterGetNextPlug, // output pin in (-1: first to come), returns cEffect* + + // realtime info + audioMasterWillReplaceOrAccumulate, // returns: 0: not supported, 1: replace, 2: accumulate + audioMasterGetCurrentProcessLevel, // returns: 0: not supported, + // 1: currently in user thread (gui) + // 2: currently in audio thread (where process is called) + // 3: currently in 'sequencer' thread (midi, timer etc) + // 4: currently offline processing and thus in user thread + // other: not defined, but probably pre-empting user thread. + audioMasterGetAutomationState, // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + + // offline + audioMasterOfflineStart, + audioMasterOfflineRead, // ptr points to offline structure, see below. return 0: error, 1 ok + audioMasterOfflineWrite, // same as read + audioMasterOfflineGetCurrentPass, + audioMasterOfflineGetCurrentMetaPass, + + // other + audioMasterSetOutputSampleRate, // for variable i/o, sample rate in + audioMasterGetSpeakerArrangement, // result in ret + audioMasterGetOutputSpeakerArrangement = audioMasterGetSpeakerArrangement, + audioMasterGetVendorString, // fills with a string identifying the vendor (max 64 char) + audioMasterGetProductString, // fills with a string with product name (max 64 char) + audioMasterGetVendorVersion, // returns vendor-specific version + audioMasterVendorSpecific, // no definition, vendor specific handling + audioMasterSetIcon, // void* in , format not defined yet + audioMasterCanDo, // string in ptr, see below + audioMasterGetLanguage, // see enum + audioMasterOpenWindow, // returns platform specific ptr + audioMasterCloseWindow, // close window, platform specific handle in + audioMasterGetDirectory, // get plug directory, FSSpec on MAC, else char* + audioMasterUpdateDisplay, // something has changed, update 'multi-fx' display + + //---from here VST 2.1 extension opcodes------------------------------------------------------ + audioMasterBeginEdit, // begin of automation session (when mouse down), parameter index in + audioMasterEndEdit, // end of automation session (when mouse up), parameter index in + audioMasterOpenFileSelector, // open a fileselector window with VstFileSelect* in + + //---from here VST 2.2 extension opcodes------------------------------------------------------ + audioMasterCloseFileSelector, // close a fileselector operation with VstFileSelect* in : Must be always called after an open ! + audioMasterEditFile, // open an editor for audio (defined by XML text in ptr) + audioMasterGetChunkFile, // get the native path of currently loading bank or project + // (called from writeChunk) void* in (char[2048], or sizeof(FSSpec)) + + //---from here VST 2.3 extension opcodes------------------------------------------------------ + audioMasterGetInputSpeakerArrangement // result a VstSpeakerArrangement in ret +}; + +//------------------------------------------------- +// Language +//------------------------------------------------- + +enum VstHostLanguage +{ + kVstLangEnglish = 1, + kVstLangGerman, + kVstLangFrench, + kVstLangItalian, + kVstLangSpanish, + kVstLangJapanese +}; + +//------------------------------------------------- +// Dispatcher OpCodes +//------------------------------------------------- + +enum +{ + //---from here VST 2.0 extension opcodes--------------------------------------------------------- + // VstEvents + effProcessEvents = effSetChunk + 1, // VstEvents* in + + // parameters and programs + effCanBeAutomated, // parameter index in + effString2Parameter, // parameter index in , string in + effGetNumProgramCategories, // no arguments. this is for dividing programs into groups (like GM) + effGetProgramNameIndexed, // get program name of category , program into . + // category (that is, ) may be -1, in which case program indices + // are enumerated linearily (as usual); otherwise, each category starts + // over with index 0. + effCopyProgram, // copy current program to destination + // note: implies setParameter + // connections, configuration + effConnectInput, // input at has been (dis-)connected; + // == 0: disconnected, else connected + effConnectOutput, // same as input + effGetInputProperties, // , VstPinProperties* in ptr, return != 0 => true + effGetOutputProperties, // dto + effGetPlugCategory, // no parameter, return value is category + + // realtime + effGetCurrentPosition, // for external dsp, see flag bits below + effGetDestinationBuffer, // for external dsp, see flag bits below. returns float* + + // offline + effOfflineNotify, // ptr = VstAudioFile array, value = count, index = start flag + effOfflinePrepare, // ptr = VstOfflineTask array, value = count + effOfflineRun, // dto + + // other + effProcessVarIo, // VstVariableIo* in + effSetSpeakerArrangement, // VstSpeakerArrangement* pluginInput in + // VstSpeakerArrangement* pluginOutput in + effSetBlockSizeAndSampleRate, // block size in , sampleRate in + effSetBypass, // onOff in (0 = off) + effGetEffectName, // char* name (max 32 bytes) in + effGetErrorText, // char* text (max 256 bytes) in + effGetVendorString, // fills with a string identifying the vendor (max 64 char) + effGetProductString, // fills with a string with product name (max 64 char) + effGetVendorVersion, // returns vendor-specific version + effVendorSpecific, // no definition, vendor specific handling + effCanDo, // + effGetTailSize, // returns tail size; 0 is default (return 1 for 'no tail') + effIdle, // idle call in response to audioMasterneedIdle. must + // return 1 to keep idle calls beeing issued + + // gui + effGetIcon, // void* in , not yet defined + effSetViewPosition, // set view position (in window) to x y + + // and... + effGetParameterProperties, // of param , VstParameterProperties* in + effKeysRequired, // returns 0: needs keys (default for 1.0 plugs), 1: don't need + effGetVstVersion, // returns 2 for VST 2; older versions return 0; 2100 for VST 2.1 + + effNumV2Opcodes, + // note that effNumOpcodes doesn't apply anymore + + + //---from here VST 2.1 extension opcodes--------------------------------------------------------- + effEditKeyDown = effNumV2Opcodes, // Character in , virtual in , modifiers in , + // return -1 if not used, return 1 if used + effEditKeyUp, // Character in , virtual in , modifiers in + // return -1 if not used, return 1 if used + effSetEditKnobMode, // Mode in : 0: circular, 1:circular relativ, 2:linear + + // midi plugins channeldependent programs + effGetMidiProgramName, // Passed points to MidiProgramName struct. + // struct will be filled with information for 'thisProgramIndex'. + // returns number of used programIndexes. + // if 0 is returned, no MidiProgramNames supported. + + effGetCurrentMidiProgram, // Returns the programIndex of the current program. + // passed points to MidiProgramName struct. + // struct will be filled with information for the current program. + + effGetMidiProgramCategory, // Passed points to MidiProgramCategory struct. + // struct will be filled with information for 'thisCategoryIndex'. + // returns number of used categoryIndexes. + // if 0 is returned, no MidiProgramCategories supported. + + effHasMidiProgramsChanged, // Returns 1 if the MidiProgramNames or MidiKeyNames + // had changed on this channel, 0 otherwise. ignored. + + effGetMidiKeyName, // Passed points to MidiKeyName struct. + // struct will be filled with information for 'thisProgramIndex' and + // 'thisKeyNumber'. If keyName is "" the standard name of the key + // will be displayed. If 0 is returned, no MidiKeyNames are + // defined for 'thisProgramIndex'. + + effBeginSetProgram, // Called before a new program is loaded + effEndSetProgram, // Called when the program is loaded + + effNumV2_1Opcodes, + + //---from here VST 2.3 extension opcodes--------------------------------------------------------- + effGetSpeakerArrangement = effNumV2_1Opcodes, // VstSpeakerArrangement** pluginInput in + // VstSpeakerArrangement** pluginOutput in + + effShellGetNextPlugin, // This opcode is only called, if plugin is of type kPlugCategShell. + // returns the next plugin's uniqueID. + // points to a char buffer of size 64, which is to be filled + // with the name of the plugin including the terminating zero. + + effStartProcess, // Called before the start of process call + effStopProcess, // Called after the stop of process call + effSetTotalSampleToProcess, // Called in offline (non RealTime) Process before process is called, indicates how many sample will be processed + + effSetPanLaw, // PanLaw : Type (Linear, Equal Power,.. see enum PanLaw Type) in , + // Gain in : for Linear : [1.0 => 0dB PanLaw], [~0.58 => -4.5dB], [0.5 => -6.02dB] + effBeginLoadBank, // Called before a Bank is loaded, points to VstPatchChunkInfo structure + // return -1 if the Bank can not be loaded, return 1 if it can be loaded else 0 (for compatibility) + effBeginLoadProgram, // Called before a Program is loaded, points to VstPatchChunkInfo structure + // return -1 if the Program can not be loaded, return 1 if it can be loaded else 0 (for compatibility) + + effNumV2_3Opcodes +}; + +//------------------------------------------------- +// Parameter Properties +//------------------------------------------------- + +typedef struct VstParameterProperties VstParameterProperties; + +struct VstParameterProperties +{ + float stepFloat; + float smallStepFloat; + float largeStepFloat; + char label[64]; + long flags; // see below + long minInteger; + long maxInteger; + long stepInteger; + long largeStepInteger; + char shortLabel[8]; // recommended: 6 + delimiter + + // the following are for remote controller display purposes. + // note that the kVstParameterSupportsDisplayIndex flag must be set. + // host can scan all parameters, and find out in what order + // to display them: + + short displayIndex; // for remote controllers, the index where this parameter + // should be displayed (starting with 0) + + // host can also possibly display the parameter group (category), such as + // --------------------------- + // Osc 1 + // Wave Detune Octave Mod + // --------------------------- + // if the plug supports it (flag kVstParameterSupportsDisplayCategory) + short category; // 0: no category, else group index + 1 + short numParametersInCategory; + short reserved; + char categoryLabel[24]; // for instance, "Osc 1" + + char future[16]; +}; + +//---Parameter Properties Flags-------------------- +enum +{ + kVstParameterIsSwitch = 1 << 0, + kVstParameterUsesIntegerMinMax = 1 << 1, + kVstParameterUsesFloatStep = 1 << 2, + kVstParameterUsesIntStep = 1 << 3, + kVstParameterSupportsDisplayIndex = 1 << 4, + kVstParameterSupportsDisplayCategory = 1 << 5, + kVstParameterCanRamp = 1 << 6 +}; + +//------------------------------------------------- +// Pin Properties +//------------------------------------------------- + +typedef struct VstPinProperties VstPinProperties; + +struct VstPinProperties +{ + char label[64]; + long flags; // see pin properties flags + long arrangementType; + char shortLabel[8]; // recommended: 6 + delimiter + char future[48]; +}; + +//---Pin Properties Flags-------------------------- +enum +{ + kVstPinIsActive = 1 << 0, + kVstPinIsStereo = 1 << 1, + kVstPinUseSpeaker = 1 << 2 +}; + +//------------------------------------------------- +// Plugin Category +//------------------------------------------------- + +enum VstPlugCategory +{ + kPlugCategUnknown = 0, + kPlugCategEffect, + kPlugCategSynth, + kPlugCategAnalysis, + kPlugCategMastering, + kPlugCategSpacializer, // 'panners' + kPlugCategRoomFx, // delays and reverbs + kPlugSurroundFx, // dedicated surround processor + kPlugCategRestoration, + kPlugCategOfflineProcess, + kPlugCategShell, // plugin which is only a container of plugins. + kPlugCategGenerator +}; + +//------------------------------------------------- +// Midi Plugins Channel Dependent Programs +//------------------------------------------------- + +typedef struct MidiProgramName MidiProgramName; +typedef struct MidiProgramCategory MidiProgramCategory; +typedef struct MidiKeyName MidiKeyName; + +struct MidiProgramName +{ + long thisProgramIndex; // >= 0. fill struct for this program index. + char name[64]; + char midiProgram; // -1:off, 0-127 + char midiBankMsb; // -1:off, 0-127 + char midiBankLsb; // -1:off, 0-127 + char reserved; // zero + long parentCategoryIndex; // -1:no parent category + long flags; // omni etc, see below +}; + +//---MidiProgramName Flags------------------------- +enum +{ + kMidiIsOmni = 1 // default is multi. for omni mode, channel 0 + // is used for inquiries and program changes +}; + +//---MidiProgramName------------------------------- +struct MidiProgramCategory +{ + long thisCategoryIndex; // >= 0. fill struct for this category index. + char name[64]; + long parentCategoryIndex; // -1:no parent category + long flags; // reserved, none defined yet, zero. +}; + +//---MidiKeyName----------------------------------- +struct MidiKeyName +{ + long thisProgramIndex; // >= 0. fill struct for this program index. + long thisKeyNumber; // 0 - 127. fill struct for this key number. + char keyName[64]; + long reserved; // zero + long flags; // reserved, none defined yet, zero. +}; + +//------------------------------------------------- +// Flags Bits +//------------------------------------------------- + +enum +{ + effFlagsIsSynth = 1 << 8, // host may assign mixer channels for its outputs + effFlagsNoSoundInStop = 1 << 9, // does not produce sound when input is all silence + effFlagsExtIsAsync = 1 << 10, // for external dsp; plug returns immedeately from process() + // host polls plug position (current block) via effGetCurrentPosition + effFlagsExtHasBuffer = 1 << 11 // external dsp, may have their own output buffe (32 bit float) + // host then requests this via effGetDestinationBuffer +}; + +//------------------------------------------------- +// Surround Setup +//------------------------------------------------- + +typedef struct VstSpeakerProperties VstSpeakerProperties; +typedef struct VstSpeakerArrangement VstSpeakerArrangement; + +//---Speaker Properties---------------------------- +struct VstSpeakerProperties +{ // units: range: except: + float azimuth; // rad -PI...PI 10.f for LFE channel + float elevation; // rad -PI/2...PI/2 10.f for LFE channel + float radius; // meter 0.f for LFE channel + float reserved; // 0. + char name[64]; // for new setups, new names should be given (L/R/C... won't do) + long type; // speaker type + char future[28]; +}; + +// note: the origin for azimuth is right (as by math conventions dealing with radians); +// the elevation origin is also right, visualizing a rotation of a circle across the +// -pi/pi axis of the horizontal circle. thus, an elevation of -pi/2 corresponds +// to bottom, and a speaker standing on the left, and 'beaming' upwards would have +// an azimuth of -pi, and an elevation of pi/2. +// for user interface representation, grads are more likely to be used, and the +// origins will obviously 'shift' accordingly. + +//---Speaker Arrangement--------------------------- +struct VstSpeakerArrangement +{ + long type; // (was float lfeGain; // LFE channel gain is adjusted [dB] higher than other channels) + long numChannels; // number of channels in this speaker arrangement + VstSpeakerProperties speakers[8]; // variable +}; + +//---Speaker Types--------------------------------- +enum +{ + kSpeakerUndefined = 0x7fffffff, // Undefinded + kSpeakerM = 0, // Mono (M) + kSpeakerL, // Left (L) + kSpeakerR, // Right (R) + kSpeakerC, // Center (C) + kSpeakerLfe, // Subbass (Lfe) + kSpeakerLs, // Left Surround (Ls) + kSpeakerRs, // Right Surround (Rs) + kSpeakerLc, // Left of Center (Lc) + kSpeakerRc, // Right of Center (Rc) + kSpeakerS, // Surround (S) + kSpeakerCs = kSpeakerS, // Center of Surround (Cs) = Surround (S) + kSpeakerSl, // Side Left (Sl) + kSpeakerSr, // Side Right (Sr) + kSpeakerTm, // Top Middle (Tm) + kSpeakerTfl, // Top Front Left (Tfl) + kSpeakerTfc, // Top Front Center (Tfc) + kSpeakerTfr, // Top Front Right (Tfr) + kSpeakerTrl, // Top Rear Left (Trl) + kSpeakerTrc, // Top Rear Center (Trc) + kSpeakerTrr, // Top Rear Right (Trr) + kSpeakerLfe2 // Subbass 2 (Lfe2) +}; + +// user-defined speaker types (to be extended in the negative range) +// (will be handled as their corresponding speaker types with abs values: +// e.g abs(kSpeakerU1) == kSpeakerL, abs(kSpeakerU2) == kSpeakerR) +enum +{ + kSpeakerU32 = -32, + kSpeakerU31, + kSpeakerU30, + kSpeakerU29, + kSpeakerU28, + kSpeakerU27, + kSpeakerU26, + kSpeakerU25, + kSpeakerU24, + kSpeakerU23, + kSpeakerU22, + kSpeakerU21, + kSpeakerU20, // == kSpeakerLfe2 + kSpeakerU19, // == kSpeakerTrr + kSpeakerU18, // == kSpeakerTrc + kSpeakerU17, // == kSpeakerTrl + kSpeakerU16, // == kSpeakerTfr + kSpeakerU15, // == kSpeakerTfc + kSpeakerU14, // == kSpeakerTfl + kSpeakerU13, // == kSpeakerTm + kSpeakerU12, // == kSpeakerSr + kSpeakerU11, // == kSpeakerSl + kSpeakerU10, // == kSpeakerCs + kSpeakerU9, // == kSpeakerS + kSpeakerU8, // == kSpeakerRc + kSpeakerU7, // == kSpeakerLc + kSpeakerU6, // == kSpeakerRs + kSpeakerU5, // == kSpeakerLs + kSpeakerU4, // == kSpeakerLfe + kSpeakerU3, // == kSpeakerC + kSpeakerU2, // == kSpeakerR + kSpeakerU1 // == kSpeakerL +}; + +//---Speaker Arrangement Types--------------------- +enum +{ + kSpeakerArrUserDefined = -2, + kSpeakerArrEmpty = -1, + + kSpeakerArrMono = 0, // M + + kSpeakerArrStereo, // L R + kSpeakerArrStereoSurround, // Ls Rs + kSpeakerArrStereoCenter, // Lc Rc + kSpeakerArrStereoSide, // Sl Sr + kSpeakerArrStereoCLfe, // C Lfe + + kSpeakerArr30Cine, // L R C + kSpeakerArr30Music, // L R S + kSpeakerArr31Cine, // L R C Lfe + kSpeakerArr31Music, // L R Lfe S + + kSpeakerArr40Cine, // L R C S (LCRS) + kSpeakerArr40Music, // L R Ls Rs (Quadro) + kSpeakerArr41Cine, // L R C Lfe S (LCRS+Lfe) + kSpeakerArr41Music, // L R Lfe Ls Rs (Quadro+Lfe) + + kSpeakerArr50, // L R C Ls Rs + kSpeakerArr51, // L R C Lfe Ls Rs + + kSpeakerArr60Cine, // L R C Ls Rs Cs + kSpeakerArr60Music, // L R Ls Rs Sl Sr + kSpeakerArr61Cine, // L R C Lfe Ls Rs Cs + kSpeakerArr61Music, // L R Lfe Ls Rs Sl Sr + + kSpeakerArr70Cine, // L R C Ls Rs Lc Rc + kSpeakerArr70Music, // L R C Ls Rs Sl Sr + kSpeakerArr71Cine, // L R C Lfe Ls Rs Lc Rc + kSpeakerArr71Music, // L R C Lfe Ls Rs Sl Sr + + kSpeakerArr80Cine, // L R C Ls Rs Lc Rc Cs + kSpeakerArr80Music, // L R C Ls Rs Cs Sl Sr + kSpeakerArr81Cine, // L R C Lfe Ls Rs Lc Rc Cs + kSpeakerArr81Music, // L R C Lfe Ls Rs Cs Sl Sr + + kSpeakerArr102, // L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2 + + kNumSpeakerArr +}; + +//------------------------------------------------- +// Offline Processing +//------------------------------------------------- + +typedef struct VstOfflineTask VstOfflineTask; +typedef struct VstAudioFile VstAudioFile; +typedef struct VstAudioFileMarker VstAudioFileMarker; + +struct VstOfflineTask +{ + char processName[96]; // set by plug + + // audio access + double readPosition; // set by plug/host + double writePosition; // set by plug/host + long readCount; // set by plug/host + long writeCount; // set by plug + long sizeInputBuffer; // set by host + long sizeOutputBuffer; // set by host + void* inputBuffer; // set by host + void* outputBuffer; // set by host + double positionToProcessFrom; // set by host + double numFramesToProcess; // set by host + double maxFramesToWrite; // set by plug + + // other data access + void* extraBuffer; // set by plug + long value; // set by host or plug + long index; // set by host or plug + + // file attributes + double numFramesInSourceFile; // set by host + double sourceSampleRate; // set by host or plug + double destinationSampleRate; // set by host or plug + long numSourceChannels; // set by host or plug + long numDestinationChannels; // set by host or plug + long sourceFormat; // set by host + long destinationFormat; // set by plug + char outputText[512]; // set by plug or host + + // progress notification + double progress; // set by plug + long progressMode; // reserved for future + char progressText[100]; // set by plug + + long flags; // set by host and plug; see VstOfflineTaskFlags + long returnValue; // reserved for future + void* hostOwned; // set by host + void* plugOwned; // set by plug + + char future[1024]; +}; + +//---VstOfflineTask Flags-------------------------- +enum VstOfflineTaskFlags +{ + // set by host + kVstOfflineUnvalidParameter = 1 << 0, + kVstOfflineNewFile = 1 << 1, + + // set by plug + kVstOfflinePlugError = 1 << 10, + kVstOfflineInterleavedAudio = 1 << 11, + kVstOfflineTempOutputFile = 1 << 12, + kVstOfflineFloatOutputFile = 1 << 13, + kVstOfflineRandomWrite = 1 << 14, + kVstOfflineStretch = 1 << 15, + kVstOfflineNoThread = 1 << 16 +}; + +//---Option passed to offlineRead/offlineWrite----- +enum VstOfflineOption +{ + kVstOfflineAudio, // reading/writing audio samples + kVstOfflinePeaks, // reading graphic representation + kVstOfflineParameter, // reading/writing parameters + kVstOfflineMarker, // reading/writing marker + kVstOfflineCursor, // reading/moving edit cursor + kVstOfflineSelection, // reading/changing selection + kVstOfflineQueryFiles // to request the host to call asynchronously offlineNotify +}; + +//---Structure passed to offlineNotify and offlineStart +struct VstAudioFile +{ + long flags; // see enum VstAudioFileFlags + void* hostOwned; // any data private to host + void* plugOwned; // any data private to plugin + char name[100]; // file title + long uniqueId; // uniquely identify a file during a session + double sampleRate; // file sample rate + long numChannels; // number of channels (1 for mono, 2 for stereo...) + double numFrames; // number of frames in the audio file + long format; // reserved for future + double editCursorPosition; // -1 if no such cursor + double selectionStart; // frame index of first selected frame, or -1 + double selectionSize; // number of frames in selection, or 0 + long selectedChannelsMask; // 1 bit per channel + long numMarkers; // number of markers in the file + long timeRulerUnit; // see doc for possible values + double timeRulerOffset; // offset in time ruler (positive or negative) + double tempo; // as bpm + long timeSigNumerator; // time signature numerator + long timeSigDenominator; // time signature denominator + long ticksPerBlackNote; // resolution + long smpteFrameRate; // smpte rate (set as in VstTimeInfo) + + char future[64]; +}; + +//---VstAudioFile Flags---------------------------- +enum VstAudioFileFlags +{ + // set by host (in call offlineNotify) + kVstOfflineReadOnly = 1 << 0, + kVstOfflineNoRateConversion = 1 << 1, + kVstOfflineNoChannelChange = 1 << 2, + + // Set by plug (in function offlineStart) + kVstOfflineCanProcessSelection = 1 << 10, + kVstOfflineNoCrossfade = 1 << 11, + kVstOfflineWantRead = 1 << 12, + kVstOfflineWantWrite = 1 << 13, + kVstOfflineWantWriteMarker = 1 << 14, + kVstOfflineWantMoveCursor = 1 << 15, + kVstOfflineWantSelect = 1 << 16 +}; + +//---VstAudioFileMarker---------------------------- +struct VstAudioFileMarker +{ + double position; + char name[32]; + long type; + long id; + long reserved; +}; + +//------------------------------------------------- +// Others +//------------------------------------------------- + +//---Structure used for openWindow and closeWindow +struct VstWindow +{ + char title[128]; // title + short xPos; // position and size + short yPos; + short width; + short height; + long style; // 0: with title, 1: without title + + void *parent; // parent of this window + void *userHandle; // reserved + void *winHandle; // reserved + + char future[104]; +}; + +//---Structure and enum used for keyUp/keyDown----- +struct VstKeyCode +{ + long character; + unsigned char virt; // see enum VstVirtualKey + unsigned char modifier; // see enum VstModifierKey +}; + +//---Used by member virt of VstKeyCode------------- +enum VstVirtualKey +{ + VKEY_BACK = 1, + VKEY_TAB, + VKEY_CLEAR, + VKEY_RETURN, + VKEY_PAUSE, + VKEY_ESCAPE, + VKEY_SPACE, + VKEY_NEXT, + VKEY_END, + VKEY_HOME, + + VKEY_LEFT, + VKEY_UP, + VKEY_RIGHT, + VKEY_DOWN, + VKEY_PAGEUP, + VKEY_PAGEDOWN, + + VKEY_SELECT, + VKEY_PRINT, + VKEY_ENTER, + VKEY_SNAPSHOT, + VKEY_INSERT, + VKEY_DELETE, + VKEY_HELP, + VKEY_NUMPAD0, + VKEY_NUMPAD1, + VKEY_NUMPAD2, + VKEY_NUMPAD3, + VKEY_NUMPAD4, + VKEY_NUMPAD5, + VKEY_NUMPAD6, + VKEY_NUMPAD7, + VKEY_NUMPAD8, + VKEY_NUMPAD9, + VKEY_MULTIPLY, + VKEY_ADD, + VKEY_SEPARATOR, + VKEY_SUBTRACT, + VKEY_DECIMAL, + VKEY_DIVIDE, + VKEY_F1, + VKEY_F2, + VKEY_F3, + VKEY_F4, + VKEY_F5, + VKEY_F6, + VKEY_F7, + VKEY_F8, + VKEY_F9, + VKEY_F10, + VKEY_F11, + VKEY_F12, + VKEY_NUMLOCK, + VKEY_SCROLL, + + VKEY_SHIFT, + VKEY_CONTROL, + VKEY_ALT, + + VKEY_EQUALS +}; + +//---Used by member modifier of VstKeyCode--------- +enum VstModifierKey +{ + MODIFIER_SHIFT = 1<<0, // Shift + MODIFIER_ALTERNATE = 1<<1, // Alt + MODIFIER_COMMAND = 1<<2, // Control on Mac + MODIFIER_CONTROL = 1<<3 // Ctrl on PC, Apple on Mac +}; + + +//---Used by audioMasterOpenFileSelector----------- +struct VstFileType +{ + VstFileType (char* _name, char *_macType, char *_dosType, char *_unixType = 0, char *_mimeType1 = 0, char *_mimeType2 = 0) + { + if (_name) + strcpy (name, _name); + if (_macType) + strcpy (macType, _macType); + if (_dosType) + strcpy (dosType, _dosType); + if (_unixType) + strcpy (unixType, _unixType); + if (_mimeType1) + strcpy (mimeType1, _mimeType1); + if (_mimeType2) + strcpy (mimeType2, _mimeType2); + } + char name[128]; + char macType[8]; + char dosType[8]; + char unixType[8]; + char mimeType1[128]; + char mimeType2[128]; +}; + +struct VstFileSelect +{ + long command; // see enum kVstFileLoad.... + long type; // see enum kVstFileType... + + long macCreator; // optional: 0 = no creator + + long nbFileTypes; // nb of fileTypes to used + VstFileType *fileTypes; // list of fileTypes + + char title[1024]; // text display in the file selector's title + + char *initialPath; // initial path + + char *returnPath; // use with kVstFileLoad and kVstDirectorySelect + // if null is passed, the host will allocated memory + // the plugin should then called closeOpenFileSelector for freeing memory + long sizeReturnPath; + + char **returnMultiplePaths; // use with kVstMultipleFilesLoad + // the host allocates this array. The plugin should then called closeOpenFileSelector for freeing memory + long nbReturnPath; // number of selected paths + + long reserved; // reserved for host application + char future[116]; // future use +}; + +enum { + kVstFileLoad = 0, + kVstFileSave, + kVstMultipleFilesLoad, + kVstDirectorySelect, + + kVstFileType = 0 +}; + +//---Structure used for effBeginLoadBank/effBeginLoadProgram-- +struct VstPatchChunkInfo +{ + long version; // Format Version (should be 1) + long pluginUniqueID;// UniqueID of the plugin + long pluginVersion; // Plugin Version + long numElements; // Number of Programs (Bank) or Parameters (Program) + char future[48]; +}; + + +//---PanLaw Type----------------------------------- +enum +{ + kLinearPanLaw = 0, // L = pan * M; R = (1 - pan) * M; + kEqualPowerPanLaw // L = pow (pan, 0.5) * M; R = pow ((1 - pan), 0.5) * M; +}; + + +#if PRAGMA_STRUCT_ALIGN || __MWERKS__ + #pragma options align=reset +#elif defined(WIN32) || defined(__FLAT__) + #pragma pack(pop) +#elif defined __BORLANDC__ + #pragma -a- +#endif + +#endif diff --git a/lib/vst/audioeffectx.h b/lib/vst/audioeffectx.h new file mode 100755 index 0000000..8de24bd --- /dev/null +++ b/lib/vst/audioeffectx.h @@ -0,0 +1,295 @@ +//------------------------------------------------------------------------------------------------------- +// VST Plug-Ins SDK +// Version 2.3 Extension +// © 2003, Steinberg Media Technologies, All Rights Reserved +//------------------------------------------------------------------------------------------------------- + +#ifndef __audioeffectx__ +#define __audioeffectx__ + +#ifndef __AudioEffect__ +#include "AudioEffect.hpp" // Version 1.0 base class AudioEffect +#endif + +#ifndef __aeffectx__ +#include "aeffectx.h" // Version 2.0 'C' extensions and structures +#endif + +#define VST_2_1_EXTENSIONS 1 // Version 2.1 extensions +#define VST_2_2_EXTENSIONS 1 // Version 2.2 extensions +#define VST_2_3_EXTENSIONS 1 // Version 2.3 extensions + +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +// AudioEffectX extends AudioEffect with new features. So you should derive +// your Plugin from AudioEffectX +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- + +class AudioEffectX : public AudioEffect +{ +public: + // Constructor + AudioEffectX (audioMasterCallback audioMaster, long numPrograms, long numParams); + + // Destructor + virtual ~AudioEffectX (); + + // Dispatcher + virtual long dispatcher (long opCode, long index, long value, void *ptr, float opt); + + virtual AEffEditor* getEditor () { return editor; } // Returns the attached editor + + // 'Plug -> Host' are methods which go from Plugin to Host, and are usually not overridden + // 'Host -> Plug' are methods which you may override to implement the according functionality (to Host) + +//------------------------------------------------------------------------------------------------------- +// Events + Time +//------------------------------------------------------------------------------------------------------- + + // Plug -> Host + virtual void wantEvents (long filter = 1); // Filter is currently ignored, midi channel data only (default) + virtual VstTimeInfo* getTimeInfo (long filter); // Returns const VstTimeInfo* (or 0 if not supported) + // filter should contain a mask indicating which fields are requested + // (see valid masks in aeffectx.h), as some items may require extensive conversions + virtual long tempoAt (long pos); // Returns tempo (in bpm * 10000) at sample frame location + bool sendVstEventsToHost (VstEvents* events); // Returns true when success + + // Host -> Plug + virtual long processEvents (VstEvents* events) { return 0; }// 0 means 'wants no more'...else returns 1! + // VstEvents and VstMidiEvents are declared in aeffectx.h + +//------------------------------------------------------------------------------------------------------- +// Parameters and Programs +//------------------------------------------------------------------------------------------------------- + + // Plug -> Host + virtual long getNumAutomatableParameters (); // Returns the number of automatable Parameters (should be <= than numParams) + virtual long getParameterQuantization (); // Returns the integer value for +1.0 representation, + // or 1 if full single float precision is maintained + // in automation. parameter index in (-1: all, any) + // Host -> Plug + virtual bool canParameterBeAutomated (long index) { return true; } + virtual bool string2parameter (long index, char* text) { return false; } // Note: implies setParameter. text==0 is to be + // expected to check the capability (returns true). + virtual float getChannelParameter (long channel, long index) { return 0.f; } + virtual long getNumCategories () { return 1L; } + virtual bool getProgramNameIndexed (long category, long index, char* text) { return false; } + virtual bool copyProgram (long destination) { return false; } + +//------------------------------------------------------------------------------------------------------- +// Connections, Configuration +//------------------------------------------------------------------------------------------------------- + + // Plug -> Host + virtual bool ioChanged (); // Tell Host numInputs and/or numOutputs and/or initialDelay and/or numParameters has changed + virtual bool needIdle (); // Plugin needs idle calls (outside its editor window), will call fxIdle () + virtual bool sizeWindow (long width, long height); + + virtual double updateSampleRate (); // Returns sample rate from Host (may issue setSampleRate() ) + virtual long updateBlockSize (); // Same for block size + virtual long getInputLatency (); // Returns input Latency + virtual long getOutputLatency (); // Returns output Latency + virtual AEffect* getPreviousPlug (long input); // Input can be -1 in which case the first found is returned + virtual AEffect* getNextPlug (long output); // Output can be -1 in which case the first found is returned + + // Host -> Plug + virtual void inputConnected (long index, bool state) {} // Input at has been (dis-)connected, + virtual void outputConnected (long index, bool state) {} // Same as input; state == true: connected + virtual bool getInputProperties (long index, VstPinProperties* properties) { return false; } + virtual bool getOutputProperties (long index, VstPinProperties* properties) { return false; } + + virtual VstPlugCategory getPlugCategory () + { if (cEffect.flags & effFlagsIsSynth) return kPlugCategSynth; return kPlugCategUnknown; } // See aeffects.h for Category + +//------------------------------------------------------------------------------------------------------- +// Realtime +//------------------------------------------------------------------------------------------------------- + + // Plug -> Host + virtual long willProcessReplacing (); // Returns 0: not implemented, 1: replacing, 2: accumulating + virtual long getCurrentProcessLevel (); // Returns 0: not supported, + // 1: currently in user thread (gui) + // 2: currently in audio thread or irq (where process is called) + // 3: currently in 'sequencer' thread or irq (midi, timer etc) + // 4: currently offline processing and thus in user thread + // other: not defined, but probably pre-empting user thread. + virtual long getAutomationState (); // Returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + virtual void wantAsyncOperation (bool state = true); // Notify Host that we want to operate asynchronously. + // process () will return immediately; Host will poll getCurrentPosition + // to see if data are available in time. + virtual void hasExternalBuffer (bool state = true); // For external DSP, may have their own output buffer (32 bit float) + // Host then requests this via effGetDestinationBuffer + + // Host -> Plug + virtual long reportCurrentPosition () { return 0; } // For external DSP, see wantAsyncOperation () + virtual float* reportDestinationBuffer () { return 0; } // For external DSP (dma option) + +//------------------------------------------------------------------------------------------------------- +// Offline +//------------------------------------------------------------------------------------------------------- + + // Plug -> Host + virtual bool offlineRead (VstOfflineTask* offline, VstOfflineOption option, bool readSource = true); + virtual bool offlineWrite (VstOfflineTask* offline, VstOfflineOption option); + virtual bool offlineStart (VstAudioFile* ptr, long numAudioFiles, long numNewAudioFiles); + virtual long offlineGetCurrentPass (); + virtual long offlineGetCurrentMetaPass (); + + // Host -> Plug + virtual bool offlineNotify (VstAudioFile* ptr, long numAudioFiles, bool start) { return false; } + virtual bool offlinePrepare (VstOfflineTask* offline, long count) { return false; } + virtual bool offlineRun (VstOfflineTask* offline, long count) { return false; } + + virtual long offlineGetNumPasses () { return 0; } + virtual long offlineGetNumMetaPasses () { return 0; } + +//------------------------------------------------------------------------------------------------------- +// Other +//------------------------------------------------------------------------------------------------------- + + // Plug -> Host + virtual void setOutputSamplerate (float samplerate); + virtual VstSpeakerArrangement* getInputSpeakerArrangement (); + virtual VstSpeakerArrangement* getOutputSpeakerArrangement (); + + virtual bool getHostVendorString (char* text); // Fills with a string identifying the vendor (max 64 char) + virtual bool getHostProductString (char* text); // Fills with a string with product name (max 64 char) + virtual long getHostVendorVersion (); // Returns vendor-specific version + virtual long hostVendorSpecific (long lArg1, long lArg2, void* ptrArg, float floatArg); // No specific definition + virtual long canHostDo (char* text); // See 'hostCanDos' in audioeffectx.cpp + // returns 0: don't know (default), 1: yes, -1: no + + virtual void isSynth (bool state = true); // Will call wantEvents if true + virtual void noTail (bool state = true); // true: tells Host we produce no output when silence comes in + // enables Host to omit process() when no data are present on any one input. + + virtual long getHostLanguage (); // Returns VstHostLanguage (see aeffectx.h) + virtual void* openWindow (VstWindow*); // Create new window + virtual bool closeWindow (VstWindow*); // Close a newly created window + virtual void* getDirectory (); // Get the Plugin's directory, FSSpec on MAC, else char* + virtual bool updateDisplay (); // Something has changed, update display like program list, parameter list + // returns true if supported + + // Host -> Plug + virtual bool processVariableIo (VstVariableIo* varIo) { return false; } // If called with varIo == NULL, returning true indicates that this call is supported by the Plugin + virtual bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, VstSpeakerArrangement* pluginOutput) { return false; } + virtual bool getSpeakerArrangement (VstSpeakerArrangement** pluginInput, VstSpeakerArrangement** pluginOutput) { *pluginInput = 0; *pluginOutput = 0; return false; } + virtual void setBlockSizeAndSampleRate (long blockSize, float sampleRate) + { this->blockSize = blockSize; this->sampleRate = sampleRate; } + virtual bool setBypass (bool onOff) { return false; } // For 'soft-bypass; process () still called + + virtual bool getEffectName (char* name) { return false; } // Name max 32 char + virtual bool getErrorText (char* text) { return false; } // Text max 256 char + virtual bool getVendorString (char* text) { return false; } // Fill text with a string identifying the vendor (max 64 char) + virtual bool getProductString (char* text) { return false; }// Fill text with a string identifying the product name (max 64 char) // fills with a string with product name (max 64 char) + virtual long getVendorVersion () { return 0; } // Return vendor-specific version + virtual long vendorSpecific (long lArg, long lArg2, void* ptrArg, float floatArg) { return 0; } + // No definition, vendor specific handling + virtual long canDo (char* text) { return 0; } // See 'plugCanDos' in audioeffectx.cpp + // returns 0: don't know (default), 1: yes, -1: no + virtual void* getIcon () { return 0; } // Not yet defined + virtual bool setViewPosition (long x, long y) { return false; } + virtual long getGetTailSize () { return 0; } + virtual long fxIdle () { return 0; } // Called when NeedIdle () was called + virtual bool getParameterProperties (long index, VstParameterProperties* p) { return false; } + virtual bool keysRequired () { return false; } // Version 1 Plugins will return true + + #if VST_2_3_EXTENSIONS + virtual long getVstVersion () { return 2300; } // Returns the current VST Version + #elif VST_2_2_EXTENSIONS + virtual long getVstVersion () { return 2200; } + #elif VST_2_1_EXTENSIONS + virtual long getVstVersion () { return 2100; } + #else + virtual long getVstVersion () { return 2; } + #endif + +//--------------------------------------------------------- +#if VST_2_1_EXTENSIONS +//------------------------------------------------------------------------------------------------------- +// Midi Program Names, are always defined per channel, valid channels are 0 - 15 +//------------------------------------------------------------------------------------------------------- + + // Host -> Plug + virtual long getMidiProgramName (long channel, MidiProgramName* midiProgramName) { return 0; } + // Struct will be filled with information for 'thisProgramIndex'. + // returns number of used programIndexes. + // If 0 is returned, no MidiProgramNames supported. + virtual long getCurrentMidiProgram (long channel, MidiProgramName* currentProgram) { return -1; } + // Struct will be filled with information for the current program. + // Returns the programIndex of the current program. -1 means not supported. + virtual long getMidiProgramCategory (long channel, MidiProgramCategory* category) { return 0; } + // Struct will be filled with information for 'thisCategoryIndex'. + // returns number of used categoryIndexes. + // if 0 is returned, no MidiProgramCategories supported/used. + virtual bool hasMidiProgramsChanged (long channel) { return false; } + // Returns true if the MidiProgramNames, MidiKeyNames or + // MidiControllerNames had changed on this channel. + virtual bool getMidiKeyName (long channel, MidiKeyName* keyName) { return false; } + // Struct will be filled with information for 'thisProgramIndex' and 'thisKeyNumber' + // if keyName is "" the standard name of the key will be displayed. + // if false is returned, no MidiKeyNames defined for 'thisProgramIndex'. + + virtual bool beginSetProgram () { return false; } // Called before a program is loaded + virtual bool endSetProgram () { return false; } // Called after... + + // Plug -> Host + virtual bool beginEdit (long index); // To be called before a setParameterAutomated with mouse move (one per Mouse Down) + virtual bool endEdit (long index); // To be called after a setParameterAutomated (on Mouse Up) + + virtual bool openFileSelector (VstFileSelect *ptr); // Open a Host File selector (see aeffectx.h for VstFileSelect definition) + +#endif // VST_2_1_EXTENSIONS + +//--------------------------------------------------------- +#if VST_2_2_EXTENSIONS + + bool closeFileSelector (VstFileSelect *ptr); // Close the Host File selector which was opened by openFileSelector + bool getChunkFile (void* nativePath); // Returns in platform format the path of the current chunk (could be called in setChunk ()) (FSSpec on MAC else char*) + +#endif // VST_2_2_EXTENSIONS + +//--------------------------------------------------------- +#if VST_2_3_EXTENSIONS + + // Host -> Plug + virtual long setTotalSampleToProcess (long value) { return value; } // Called in offline (non RealTime) Process before process is called, indicates how many sample will be processed + + virtual long getNextShellPlugin (char* name) { return 0; } + // Tthis opcode is only called, if Plugin is of type kPlugCategShell. + // should return the next plugin's uniqueID. + // name points to a char buffer of size 64, which is to be filled + // with the name of the plugin including the terminating zero. + virtual long startProcess () { return 0; } // Called one time before the start of process call + virtual long stopProcess () { return 0; } // Called after the stop of process call + + virtual bool setPanLaw (long type, float val) { return false; } // Set the Panning Law used by the Host + + virtual long beginLoadBank (VstPatchChunkInfo* ptr) { return 0; } + // Called before a Bank is loaded. + // returns -1 if the Bank cannot be loaded, returns 1 if it can be loaded else 0 (for compatibility) + virtual long beginLoadProgram (VstPatchChunkInfo* ptr) { return 0; } + // Called before a Program is loaded. (called before beginSetProgram) + // returns -1 if the Program cannot be loaded, returns 1 if it can be loaded else 0 (for compatibility) + + // Tools + virtual bool allocateArrangement (VstSpeakerArrangement** arrangement, long nbChannels); + // Allocate memory for a VstSpeakerArrangement containing the given + // number of channels + virtual bool deallocateArrangement (VstSpeakerArrangement** arrangement); + // Delete/free memory for a speaker arrangement + virtual bool copySpeaker (VstSpeakerProperties* to, VstSpeakerProperties* from); + // Feed the "to" speaker properties with the same values than "from"'s ones. + // It is assumed here that "to" exists yet, ie this function won't + // allocate memory for the speaker (this will prevent from having + // a difference between an Arrangement's number of channels and + // its actual speakers...) + virtual bool matchArrangement (VstSpeakerArrangement** to, VstSpeakerArrangement* from); + // "to" is deleted, then created and initialized with the same values as "from" ones ("from" must exist). +#endif // VST_2_3_EXTENSIONS +}; + +#endif //__audioeffectx__ +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- diff --git a/lib/winamp/dsp.h b/lib/winamp/dsp.h new file mode 100755 index 0000000..6c264b4 --- /dev/null +++ b/lib/winamp/dsp.h @@ -0,0 +1,38 @@ +// DSP plugin interface + +// notes: +// any window that remains in foreground should optimally pass unused +// keystrokes to the parent (winamp's) window, so that the user +// can still control it. As for storing configuration, +// Configuration data should be stored in \plugin.ini +// (look at the vis plugin for configuration code) + +typedef struct winampDSPModule { + char *description; // description + void *hwndParent; // parent window (filled in by calling app) + void *hDllInstance; // instance handle to this DLL (filled in by calling app) + + void (*Config)(struct winampDSPModule *this_mod); // configuration dialog (if needed) + int (*Init)(struct winampDSPModule *this_mod); // 0 on success, creates window, etc (if needed) + + // modify waveform samples: returns number of samples to actually write + // (typically numsamples, but no more than twice numsamples, and no less than half numsamples) + // numsamples should always be at least 128. should, but I'm not sure + int (*ModifySamples)(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate); + + void (*Quit)(struct winampDSPModule *this_mod); // called when unloading + + void *userData; // user data, optional +} winampDSPModule; + +typedef struct { + int version; // DSP_HDRVER + char *description; // description of library + winampDSPModule* (*getModule)(int); // module retrieval function +} winampDSPHeader; + +// exported symbols +typedef winampDSPHeader* (*winampDSPGetHeaderType)(); + +// header version: 0x20 == 0.20 == winamp 2.0 +#define DSP_HDRVER 0x20 \ No newline at end of file diff --git a/src/fmod.cpp b/src/fmod.cpp new file mode 100755 index 0000000..775f706 --- /dev/null +++ b/src/fmod.cpp @@ -0,0 +1,13755 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod.hpp" +#include "fmod_channeli.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_memory.h" +#include "fmod_linkedlist.h" +#include "fmod_listener.h" +#include "fmod_pluginfactory.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" +#include "fmod_cmdlog.h" + + +#if 0 //def DEBUG + +#include + +#if !defined(__MWERKS__) && !defined(__SN__) +void * operator new(unsigned int size) +{ + assert(!"ERROR : tried to alloc a pointer using global new"); + return 0; +} + +void operator delete(void *ptr) +{ + assert(!"ERROR : tried to delete a pointer using global delete\n"); +} +#endif + +#endif + + +/* +[API] +[ + [DESCRIPTION] + Specifies a method for FMOD to allocate memory, either through callbacks or its own internal memory management. You can also supply a pool of memory for FMOD to work with and it will do so with no extra calls to malloc or free. + + [PARAMETERS] + 'poolmem' If you want a fixed block of memory for FMOD to use, pass it in here. Specify the length in poollen. Specifying NULL doesn't use internal management and it relies on callbacks. + 'poollen' Length in bytes of the pool of memory for FMOD to use specified in. Specifying 0 turns off internal memory management and relies purely on callbacks. Length must be a multiple of 512. + 'useralloc' Only supported if pool is NULL. Otherwise it overrides the FMOD internal calls to alloc. Compatible with ansi malloc(). + 'userrealloc' Only supported if pool is NULL. Otherwise it overrides the FMOD internal calls to realloc. Compatible with ansi realloc(). + 'userfree' Only supported if pool is NULL. Otherwise it overrides the FMOD internal calls to free. Compatible with ansi free(). + 'memtypeflags' FMOD_MEMORY_TYPE flags you wish to receive through your memory callbacks. See FMOD_MEMORY_TYPE. + + [RETURN_VALUE] + + [REMARKS] + This function is useful for systems that want FMOD to use their own memory management or for fixed memory devices such as Xbox, Xbox360, PS2 and GameCube that don't want any allocations occurring out of their control causing fragmentation or unpredictable overflows in a tight memory space. +

+ FMOD only does allocation when creating streams, music or samples and the System::init stage. It never allocates or deallocates memory during the course of runtime processing. To find out the required fixed size the user can call Memory_Initialize with an overly large pool size (or no pool) and find out the maximum RAM usage at any one time with Memory_GetStats. +

+ FMOD behaves differently based on what you pass into this function in 3 different combinations. For example : +

+

+    FMOD::Memory_Initialize(NULL, 0,   NULL,    NULL,      NULL);   // Falls back purely to ansi C malloc, realloc and free.
+    FMOD::Memory_Initialize(NULL, 0,   myalloc, myrealloc, myfree); // Calls user supplied callbacks every time FMOD does a memory allocation or deallocation.
+    FMOD::Memory_Initialize(ptr,  len, NULL,    NULL,      NULL);   // Uses "ptr" and manages memory internally.  NO extra mallocs or frees are performed from this point.
+    
+

+ Callbacks and memory pools cannot be combined. If a memory pool is provided by the user, FMOD accesses that pool using its own memory management scheme. FMOD's internal memory management scheme is extremely efficient and also faster than the standard C malloc and free. +

+ When running on Xbox 1, you MUST specify a pointer and length. The memory provided must be enough to store all sample data. +

+ NOTE! Your memory callbacks must be threadsafe otherwise unexpected behaviour may occur. FMOD calls memory allocation functions from other threads (such as the asynchronous loading thread used when you specify FMOD_NONBLOCKING) and sometimes from the mixer thread. +

+ NOTE! If you specify a fixed size pool that is too small, FMOD will return FMOD_ERR_MEMORY when the limit of the fixed size pool is exceeded. At this point, it's possible that FMOD may become unstable. To maintain stability, do not allow FMOD to run out of memory.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMORY_ALLOCCALLBACK + FMOD_MEMORY_REALLOCCALLBACK + FMOD_MEMORY_FREECALLBACK + Memory_GetStats + System::close +] +*/ +FMOD_RESULT F_API FMOD_Memory_Initialize(void *poolmem, int poollen, FMOD_MEMORY_ALLOCCALLBACK useralloc, FMOD_MEMORY_REALLOCCALLBACK userrealloc, FMOD_MEMORY_FREECALLBACK userfree, FMOD_MEMORY_TYPE memtypeflags) +{ + if (!FMOD::gGlobal->gSystemHead->isEmpty()) + { + return FMOD_ERR_INITIALIZED; + } + + if (poollen % FMOD_MEMORY_DEFAULTBLOCKSIZE) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "FMOD_Memory_Initialize", "Please pass a pool size aligned to a %d byte boundary\n", FMOD_MEMORY_DEFAULTBLOCKSIZE)); + return FMOD_ERR_INVALID_PARAM; + } + + #ifdef PLATFORM_XENON + // The CPU's virtual addresses for 'physical memory' allocations are divided + // into three sections: + // + // 0xA0000000 - 0xBFFFFFF 64 KB Pages + // 0xC0000000 - 0xDFFFFFF 16 MB Pages (Address range also used for cached-read-only pages, see below) + // 0xE0000000 - 0xFFFFFFF 4 KB Pages + if (poolmem && (unsigned int)poolmem < 0xA0000000) + { + FLOGC((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "FMOD_Memory_Initialize", "Memory must be physical not virtual.\n")); + return FMOD_ERR_INVALID_ADDRESS; + } + #endif + + FMOD::gGlobal->gMemoryTypeFlags = memtypeflags; + FMOD::gGlobal->gMemoryTypeFlags |= FMOD_MEMORY_XBOX360_PHYSICAL; /* Make sure FMOD_MEMORY_XBOX360_PHYSICAL is always on by default. */ + + if (poollen && poolmem) + { + FMOD_RESULT result; + + if (useralloc || userrealloc || userfree) + { + return FMOD_ERR_INVALID_PARAM; + } + if (poollen < FMOD_MEMORY_DEFAULTBLOCKSIZE) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = FMOD::gGlobal->gSystemPool->init(poolmem, poollen, FMOD_MEMORY_DEFAULTBLOCKSIZE); + if (result == FMOD_OK) + { + FMOD::gGlobal->gSystemPool->setCallbacks(0,0,0); + } + + return result; + } + else + { + if (poolmem || poollen) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (useralloc && userrealloc && userfree) + { + FMOD::gGlobal->gSystemPool->setCallbacks(useralloc, userrealloc, userfree); + } + else if (!useralloc && !userrealloc && !userfree) + { + FMOD::gGlobal->gSystemPool->setCallbacks(FMOD::Memory_DefaultMalloc, FMOD::Memory_DefaultRealloc, FMOD::Memory_DefaultFree); + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + } + + return FMOD_OK; +} + + +/* +[API] +[ + [DESCRIPTION] + Returns information on the memory usage of FMOD. This is useful for determining a fixed memory size to make FMOD work within for fixed memory machines such as consoles. + + [PARAMETERS] + 'currentalloced' Address of a variable that receives the currently allocated memory at time of call. Optional. Specify 0 or NULL to ignore. + 'maxalloced' Address of a variable that receives the maximum allocated memory since System::init or Memory_Initialize. Optional. Specify 0 or NULL to ignore. + 'blocking' Boolean indicating whether to favour speed or accuracy. Specifying true for this parameter will flush the DSP network to make sure all queued allocations happen immediately, which can be costly. + + [RETURN_VALUE] + + [REMARKS] + Note that if using FMOD::Memory_Initialize, the memory usage will be slightly higher than without it, as FMOD has to have a small amount of memory overhead to manage the available memory. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init + Memory_Initialize +] +*/ +FMOD_RESULT F_API FMOD_Memory_GetStats(int *currentalloced, int *maxalloced, FMOD_BOOL blocking) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD::SystemI *current; + + /* + First flush out any queued threaded mallocs. + */ + if (blocking) + { + for (current = (FMOD::SystemI *)(FMOD::gGlobal->gSystemHead->getNext()); current != FMOD::gGlobal->gSystemHead; current = (FMOD::SystemI *)(current->getNext())) + { + current->flushDSPConnectionRequests(); + } + } +#endif + + if (currentalloced) + { + *currentalloced = FMOD::gGlobal->gSystemPool->getCurrentAllocated(); + } + if (maxalloced) + { + *maxalloced = FMOD::gGlobal->gSystemPool->getMaxAllocated(); + } + + return FMOD_OK; +} + + +/* +[API] +[ + [DESCRIPTION] + FMOD System creation function. This must be called to create an FMOD System object before you can do anything else. + Use this function to create 1, or multiple instances of FMOD System objects. + + [PARAMETERS] + 'system' Address of a pointer that receives the new FMOD System object. + + [RETURN_VALUE] + + [REMARKS] + Use System::release to free a system object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init + System::release +] +*/ +FMOD_RESULT F_API FMOD_System_Create(FMOD_SYSTEM **system) +{ + FMOD::SystemI *sys, *current; + int count; + bool usedsys[1 << FMOD::SYSTEMID_BITS]; + + if (!system) + { + return FMOD_ERR_INVALID_PARAM; + } + + sys = FMOD_Object_Calloc(FMOD::SystemI); + + *system = (FMOD_SYSTEM *)sys; + if (!*system) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memset(usedsys, 0, sizeof(bool) * (1 << FMOD::SYSTEMID_BITS)); + for (current = (FMOD::SystemI *)(FMOD::gGlobal->gSystemHead->getNext()); current != FMOD::gGlobal->gSystemHead; current = (FMOD::SystemI *)(current->getNext())) + { + usedsys[current->mIndex - 1] = true; + } + + for (count = 0; count < (1 << FMOD::SYSTEMID_BITS) - 1; count++) + { + if (!usedsys[count]) + { + sys->mIndex = count + 1; + break; + } + } + + if (count == (1 << FMOD::SYSTEMID_BITS) - 1) + { + FMOD_Memory_Free(sys); + return FMOD_ERR_MEMORY; + } + + sys->addAfter(FMOD::gGlobal->gSystemHead); + +#ifdef FMOD_SUPPORT_CMDLOG + FMOD_RESULT result = FMOD_CmdLog_Init(); + if (result != FMOD_OK) + { + return result; + } +#endif + + return FMOD_OK; +} + + +/* +[API] +[ + [DESCRIPTION] + Closes and frees a system object and its resources. + + [PARAMETERS] + 'system' Address of variable that receives an FMOD::System object. + + [RETURN_VALUE] + + [REMARKS] + This function also calls System::close, so calling close before this function is not necessary.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System_Create + System::init + System::close +] +*/ +FMOD_RESULT F_API FMOD_System_Release(FMOD_SYSTEM *system) +{ + FMOD::System *sys = (FMOD::System *)system; + + FMOD_RESULT result = sys->release(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/*$ preserve end $*/ +/* +[API] +[ + [DESCRIPTION] + This function selects the output mode for the platform. This is for selecting different OS specific APIs which might have different features. + + [PARAMETERS] + 'output' Output type to select. See type list for different output types you can select. + + [RETURN_VALUE] + + [REMARKS] + This function is not necessary to call. It is only if you want to specifically switch away from the default output mode for the operating system. + The most optimal mode is selected by default for the operating system. For example FMOD_OUTPUTTYPE_DSOUND is selected on all operating systems except for Windows NT, where FMOD_OUTPUTTYPE_WINMM is selected because it is lower latency / faster. +
+
+ This function cannot be called after FMOD is already activated with System::init.
+ It must be called before System::init, or after System::close. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OUTPUTTYPE + System::init + System::close +] +*/ +FMOD_RESULT F_API FMOD_System_SetOutput(FMOD_SYSTEM *system, FMOD_OUTPUTTYPE output) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setOutput(output); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current output system FMOD is using to address the hardware. + + [PARAMETERS] + 'output' Address of a variable that receives the current output type. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OUTPUTTYPE +] +*/ +FMOD_RESULT F_API FMOD_System_GetOutput(FMOD_SYSTEM *system, FMOD_OUTPUTTYPE *output) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getOutput(output); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of soundcard devices on the machine, specific to the output mode set with System::setOutput. + + [PARAMETERS] + 'numdrivers' Address of a variable that receives the number of output drivers. + + [RETURN_VALUE] + + [REMARKS] + If System::setOutput is not called it will return the number of drivers available for the default output type. + Use this for enumerating sound devices. Use System::getDriverInfo to get the device's name. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getDriver + System::getDriverInfo + System::setOutput + System::getOutput +] +*/ +FMOD_RESULT F_API FMOD_System_GetNumDrivers(FMOD_SYSTEM *system, int *numdrivers) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getNumDrivers(numdrivers); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves identification information about a sound device specified by its index, and specific to the output mode set with System::setOutput. + + [PARAMETERS] + 'id' Index of the sound driver device. The total number of devices can be found with System::getNumDrivers. + 'name' Address of a variable that receives the name of the device. Optional. Specify 0 or NULL to ignore. + 'namelen' Length in bytes of the target buffer to receieve the string. Required if name parameter is not NULL. + 'guid' Address of a variable that receives the GUID that uniquely identifies the device. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumDrivers + System::setOutput +] +*/ +FMOD_RESULT F_API FMOD_System_GetDriverInfo(FMOD_SYSTEM *system, int id, char *name, int namelen, FMOD_GUID *guid) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDriverInfo(id, name, namelen, guid); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves identification information about a sound device specified by its index, and specific to the output mode set with System::setOutput. + + [PARAMETERS] + 'id' Index of the sound driver device. The total number of devices can be found with System::getNumDrivers. + 'name' Address of a variable that receives the name of the device in wide chars. Optional. Specify 0 or NULL to ignore. + 'namelen' Length in bytes of the target buffer to receieve the string. Required if name parameter is not NULL. + 'guid' Address of a variable that receives the GUID that uniquely identifies the device. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] + System::getNumDrivers + System::setOutput +] +*/ +FMOD_RESULT F_API FMOD_System_GetDriverInfoW(FMOD_SYSTEM *system, int id, short *name, int namelen, FMOD_GUID *guid) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDriverInfoW(id, name, namelen, guid); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns information on capabilities of the current output mode for the selected sound device. + + [PARAMETERS] + 'id' Enumerated driver ID. This must be in a valid range delimited by System::getNumDrivers. + 'caps' Address of a variable that receives the capabilities of the device. Optional. Specify 0 or NULL to ignore. + 'minfrequency' Address of a variable that receives the minimum frequency allowed with sounds created with FMOD_HARDWARE. If Channel::setFrequency is used FMOD will clamp the frequency to this minimum. Optional. Specify 0 or NULL to ignore. + 'maxfrequency' Address of a variable that receives the maximum frequency allowed with sounds created with FMOD_HARDWARE. If Channel::setFrequency is used FMOD will clamp the frequency to this maximum. Optional. Specify 0 or NULL to ignore. + 'controlpanelspeakermode' Address of a variable that receives the speaker mode set by the operating system control panel. Use this to pass to System::setSpeakerMode if you want to set up FMOD's software mixing engine to match. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + This function cannot be called after FMOD is already activated with System::init.
+ It must be called before System::init, or after System::close.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CAPS + System::init + System::close + System::getNumDrivers + System::getHardwareChannels + System::setSpeakerMode + Channel::setFrequency +] +*/ +FMOD_RESULT F_API FMOD_System_GetDriverCaps(FMOD_SYSTEM *system, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDriverCaps(id, caps, minfrequency, maxfrequency, controlpanelspeakermode); +} + + +/* +[API] +[ + [DESCRIPTION] + Selects a soundcard driver. + This function is used when an output mode has enumerated more than one output device, and you need to select between them. + + [PARAMETERS] + 'driver' Driver number to select. 0 = primary or main sound device as selected by the operating system settings. Use System::getNumDrivers to select a specific device. + + [RETURN_VALUE] + + [REMARKS] + If this function is called after FMOD is already initialized with System::init, the current driver will be shutdown and the newly selected driver will be initialized / started.
+
+ When switching output driver after System::init there are a few considerations to make:
+
+ All sounds must be created with FMOD_SOFTWARE, creating even one FMOD_HARDWARE sound will cause this function to return FMOD_ERR_NEEDSSOFTWARE.
+
+ The driver that you wish to change to must support the current output format, sample rate, and number of channels. If it does not, FMOD_ERR_OUTPUT_INIT is returned and driver state is cleared. You should now call System::setDriver with your original driver index to restore driver state (providing that driver is still available / connected) or make another selection.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getDriver + System::getNumDrivers + System::getDriverInfo + System::setOutput + System::init + System::close +] +*/ +FMOD_RESULT F_API FMOD_System_SetDriver(FMOD_SYSTEM *system, int driver) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setDriver(driver); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the currently selected driver number. Drivers are enumerated when selecting a driver with System::setDriver or other driver related functions such as System::getNumDrivers or System::getDriverInfo + + [PARAMETERS] + 'driver' Address of a variable that receives the currently selected driver ID. 0 = primary or main sound device as selected by the operating system settings. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setDriver + System::getNumDrivers + System::getDriverInfo +] +*/ +FMOD_RESULT F_API FMOD_System_GetDriver(FMOD_SYSTEM *system, int *driver) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDriver(driver); +} + + +/* +[API] +[ + [DESCRIPTION] + This function allows the user to request a minimum number of hardware voices to be present on the soundcard to allow hardware 3D sound acceleration, or clamp the number of hardware 3D voices to a maximum value. + + [PARAMETERS] + 'min2d' Minimum number of hardware voices on a soundcard required to actually support hardware 2D sound. If the soundcard does not match this value for number of hardware voices possible, FMOD will place the sound into software mixed buffers instead hardware mixed buffers to guarantee the number of sounds playable at once is guaranteed. + 'max2d' Maximum number of hardware voices to be used by FMOD. This clamps the polyphony of hardware 2D voices to a user specified number. This could be used to limit the number of 2D hardware voices possible at once so that it doesn't sound noisy, or the user might want to limit the number of channels used for 2D hardware support to avoid problems with certain buggy soundcard drivers that report they have many channels but actually don't. + 'min3d' Minimum number of hardware voices on a soundcard required to actually support hardware 3D sound. If the soundcard does not match this value for number of hardware voices possible, FMOD will place the sound into software mixed buffers instead hardware mixed buffers to guarantee the number of sounds playable at once is guaranteed. + 'max3d' Maximum number of hardware voices to be used by FMOD. This clamps the polyphony of hardware 3D voices to a user specified number. This could be used to limit the number of 3D hardware voices possible at once so that it doesn't sound noisy, or the user might want to limit the number of channels used for 3D hardware support to avoid problems with certain buggy soundcard drivers that report they have many channels but actually don't. + + [RETURN_VALUE] + + [REMARKS] + The 'min' value sets the minimum allowable hardware channels before FMOD drops back to 100 percent software based buffers for sounds even if they are allocated with FMOD_HARDWARE.
+ This is helpful for minimum spec cards, and not having to 'guess' how many hardware channels they might have. This way you can guarantee and assume a certain number of channels for your application and always allocate with FMOD_HARDWARE | FMOD_3D without fear of the playsound failing.
+
+
+ The 'max' value function has nothing to do with the 'min' value, in that this is not a function that forces FMOD channels into software mode if a card has less than or more than a certain number of channels.
+ This parameter only sets a limit on hardware channels playable at once, so if your card has 96 hardware channels, and you set max to 10, then you will only have 10 hardware 3D channels to use.
+ The 'buggy soundcard driver' issue in the description for the 'max' parameter is to do with one known sound card driver in particular, the default Windows XP SoundBlaster Live drivers. They report over 32 possible voices, but actually only support 32, and when you use the extra voices the driver can act unpredictably causing either sound dropouts or a crash.
+
+
+ This function cannot be called after FMOD is already activated with System::init.
+ It must be called before System::init, or after System::close.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getHardwareChannels + System::init + System::close +] +*/ +FMOD_RESULT F_API FMOD_System_SetHardwareChannels(FMOD_SYSTEM *system, int min2d, int max2d, int min3d, int max3d) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setHardwareChannels(min2d, max2d, min3d, max3d); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the maximum number of software mixed channels possible. Software mixed voices are used by sounds loaded with FMOD_SOFTWARE. + + [PARAMETERS] + 'numsoftwarechannels' The maximum number of FMOD_SOFTWARE mixable voices to be allocated by FMOD. If you don't require software mixed voices specify 0. Default = 32. + + [RETURN_VALUE] + + [REMARKS] + 32 voices are allocated by default to be played simultaneously in software.
+ To turn off the software mixer completely including hardware resources used for the software mixer, specify FMOD_INIT_SOFTWARE_DISABLE in System::init. +
+
+ This function cannot be called after FMOD is already activated with System::init.
+ It must be called before System::init, or after System::close. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + FMOD_INITFLAGS + System::init + System::close + System::getSoftwareChannels +] +*/ +FMOD_RESULT F_API FMOD_System_SetSoftwareChannels(FMOD_SYSTEM *system, int numsoftwarechannels) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setSoftwareChannels(numsoftwarechannels); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the maximum number of software mixed channels possible. Software mixed voices are used by sounds loaded with FMOD_SOFTWARE. + + [PARAMETERS] + 'numsoftwarechannels' Address of a variable that receives the current maximum number of software voices available. Default = 32. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setSoftwareChannels +] +*/ +FMOD_RESULT F_API FMOD_System_GetSoftwareChannels(FMOD_SYSTEM *system, int *numsoftwarechannels) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getSoftwareChannels(numsoftwarechannels); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the output format for the software mixer. This includes the bitdepth, sample rate and number of output channels.
+ Do not call this unless you explicity want to change something. Calling this could have adverse impact on the performance and panning behaviour.
+ + [PARAMETERS] + 'samplerate' The soundcard's output rate. default = 48000. + 'format' The soundcard's output format. default = FMOD_SOUND_FORMAT_PCM16. + 'numoutputchannels' The number of output channels / speakers to initialize the soundcard to. 0 = keep speakermode setting (set with System::setSpeakerMode). If anything else than 0 is specified then the speakermode will be overriden and will become FMOD_SPEAKERMODE_RAW, meaning logical speaker assignments (as defined in FMOD_SPEAKER) become innefective and cannot be used. Channel::setPan will also fail. Default = 2 (FMOD_SPEAKERMODE_STEREO). + 'maxinputchannels' Optional. Specify 0 to ignore. Default = 6. Maximum channel count in loaded/created sounds to be supported. This is here purely for memory considerations and affects how much memory is used in the software mixer when allocating matrices for panning. Do not confuse this with recording, or anything to do with how many voices you can play at once. This is purely for setting the largest type of sound you can play (ie 1 = mono, 2 = stereo, etc.). Most of the time the user will not play sounds any larger than mono or stereo, so setting this to 2 would save memory and cover most sounds that are playable. + 'resamplemethod' Software engine resampling method. default = FMOD_DSP_RESAMPLER_LINEAR. See FMOD_DSP_RESAMPLER for different types. + + [RETURN_VALUE] + + [REMARKS] + Note! The settings in this function may be overriden by the output mode.
+ FMOD_OUTPUTTYPE_ASIO will always change the output mode to FMOD_SOUND_FORMAT_PCMFLOAT to be compatible with the output formats selectable by the ASIO control panel.
+ FMOD_OUTPUTTYPE_ASIO will also change the samplerate specified by the user to the one selected in the ASIO control panel.
+ Use System::getSoftwareFormat after System::init to determine what the output has possibly changed the format to. Call it after System::init.
+
+ It is dependant on the output whether it will force a format change and override these settings or not.
+
+ If the output does not support the output mode specified System::init will fail, and you will have to try another setting.
+
+ Note! When this function is called with a output channel count greater than 0, the speaker mode is set to FMOD_SPEAKERMODE_RAW. FMOD does not know when you specify a number of output channels what type of speaker system it is connected to, so Channel::setPan or Channel::setSpeakerMix will then fail to work.
+ Calling System::setSpeakerMode will override the output channel speaker count.
+
+ This function cannot be called after FMOD is already activated with System::init.
+ It must be called before System::init, or after System::close.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getSoftwareFormat + System::setSpeakerMode + System::init + System::close + Channel::setPan + Channel::setSpeakerMix + FMOD_SPEAKER + FMOD_SPEAKERMODE + FMOD_SOUND_FORMAT + FMOD_DSP_RESAMPLER +] +*/ +FMOD_RESULT F_API FMOD_System_SetSoftwareFormat(FMOD_SYSTEM *system, int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplemethod) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setSoftwareFormat(samplerate, format, numoutputchannels, maxinputchannels, resamplemethod); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the output format for the software mixer. + + [PARAMETERS] + 'samplerate' Address of a variable that receives the mixer's output rate. Optional. Specify 0 or NULL to ignore. + 'format' Address of a variable that receives the mixer's output format. Optional. Specify 0 or NULL to ignore. + 'numoutputchannels' Address of a variable that receives the number of output channels to initialize the mixer to, for example 1 = mono, 2 = stereo. 8 is the maximum for soundcards that can handle it. Optional. Specify 0 or NULL to ignore. + 'maxinputchannels' Address of a variable that receives the maximum channel depth on sounds that are loadable or creatable. Specify 0 or NULL to ignore. + 'resamplemethod' Address of a variable that receives the current resampling (frequency conversion) method for software mixed sounds. Specify 0 or NULL to ignore. + 'bits' Address of a variable that receives the number of bits per sample. Useful for byte->sample conversions. for example FMOD_SOUND_FORMAT_PCM16 is 16. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + Note that the settings returned here may differ from the settings provided by the user with System::setSoftwareFormat. This is because the driver may have changed it because it will not initialize to anything else. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setSoftwareFormat + FMOD_SOUND_FORMAT + FMOD_DSP_RESAMPLER +] +*/ +FMOD_RESULT F_API FMOD_System_GetSoftwareFormat(FMOD_SYSTEM *system, int *samplerate, FMOD_SOUND_FORMAT *format, int *numoutputchannels, int *maxinputchannels, FMOD_DSP_RESAMPLER *resamplemethod, int *bits) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getSoftwareFormat(samplerate, format, numoutputchannels, maxinputchannels, resamplemethod, bits); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the FMOD internal mixing buffer size. This function is used if you need to control mixer latency or granularity. + Smaller buffersizes lead to smaller latency, but can lead to stuttering/skipping/instable sound on slower machines or soundcards with bad drivers. + + [PARAMETERS] + 'bufferlength' The mixer engine block size in samples. Use this to adjust mixer update granularity. Default = 1024. (milliseconds = 1024 at 48khz = 1024 / 48000 * 1000 = 21.33ms). This means the mixer updates every 21.33ms. + 'numbuffers' The mixer engine number of buffers used. Use this to adjust mixer latency. Default = 4. To get the total buffersize multiply the bufferlength by the numbuffers value. By default this would be 4*1024. + + [RETURN_VALUE] + + [REMARKS] + The FMOD software mixer mixes to a ringbuffer. The size of this ringbuffer is determined here. It mixes a block of sound data every 'bufferlength' number of samples, and there are 'numbuffers' number of these blocks that make up the entire ringbuffer.
+ Adjusting these values can lead to extremely low latency performance (smaller values), or greater stability in sound output (larger values). +
+
+ Warning! The 'buffersize' is generally best left alone. Making the granularity smaller will just increase CPU usage (cache misses and DSP network overhead). + Making it larger affects how often you hear commands update such as volume/pitch/pan changes. Anything above 20ms will be noticable and sound parameter changes will be obvious instead of smooth. +
+
+ FMOD chooses the most optimal size by default for best stability, depending on the output type, and if the drivers are emulated or not (for example DirectSound is emulated using waveOut on NT). + It is not recommended changing this value unless you really need to. You may get worse performance than the default settings chosen by FMOD. +
+
+ To convert from milliseconds to 'samples', simply multiply the value in milliseconds by the sample rate of the output (ie 48000 if that is what it is set to), then divide by 1000. +
+
+ The values in milliseconds and average latency expected from the settings can be calculated using the following code.
+
+

+    FMOD_RESULT result;
+    unsigned int blocksize;
+    int numblocks;
+    float ms;
+    
+ result = system->getDSPBufferSize(&blocksize, &numblocks); + result = system->getSoftwareFormat(&frequency, 0, 0, 0, 0); +
+ ms = (float)blocksize * 1000.0f / (float)frequency; +
+ printf("Mixer blocksize = %.02f ms\n", ms); + printf("Mixer Total buffersize = %.02f ms\n", ms * numblocks); + printf("Mixer Average Latency = %.02f ms\n", ms * ((float)numblocks - 1.5f)); +
+
+ Platform notes: Some output modes (such as FMOD_OUTPUTTYPE_ASIO) will change the buffer size to match their own internal optimal buffer size. Use System::getDSPBufferSize after calling System::init to see if this is the case.
+ Linux output modes will ignore numbuffers and just write the buffer size to the output every time it can. It does not use a ringbuffer.
+ Xbox 360 defaults to 256 sample buffersize and 4 for numblocks. This gives a 5.333ms granularity with roughly a 10-15ms latency.
+ PS3 ignores this function. Check FMOD_PS3_EXTRADRIVERDATA to control output latency. +
+ This function cannot be called after FMOD is already activated with System::init.
+ It must be called before System::init, or after System::close.
+ + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getDSPBufferSize + System::getSoftwareFormat + System::init + System::close +] +*/ +FMOD_RESULT F_API FMOD_System_SetDSPBufferSize(FMOD_SYSTEM *system, unsigned int bufferlength, int numbuffers) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setDSPBufferSize(bufferlength, numbuffers); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the buffer size settings for the FMOD software mixing engine. + + [PARAMETERS] + 'bufferlength' Address of a variable that receives the mixer engine block size in samples. Default = 1024. (milliseconds = 1024 at 48khz = 1024 / 48000 * 1000 = 10.66ms). This means the mixer updates every 21.3ms. Optional. Specify 0 or NULL to ignore. + 'numbuffers' Address of a variable that receives the mixer engine number of buffers used. Default = 4. To get the total buffersize multiply the bufferlength by the numbuffers value. By default this would be 4*1024. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + See documentation on System::setDSPBufferSize for more information about these values. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setDSPBufferSize +] +*/ +FMOD_RESULT F_API FMOD_System_GetDSPBufferSize(FMOD_SYSTEM *system, unsigned int *bufferlength, int *numbuffers) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDSPBufferSize(bufferlength, numbuffers); +} + + +/* +[API] +[ + [DESCRIPTION] + Specify user callbacks for FMOD's internal file manipulation functions.
+ If ANY of the callback functions are set to 0/ NULL, then FMOD will switch back to its own file routines.
+ This function is useful for replacing FMOD's file system with a game system's own file reading API.
+ + [PARAMETERS] + 'useropen' Callback for opening a file. Specifying 0 / null will disable file callbacks. + 'userclose' Callback for closing a file. Specifying 0 / null will disable file callbacks. + 'userread' Callback for reading from a file. Specifying 0 / null will disable file callbacks. + 'userseek' Callback for seeking within a file. Specifying 0 / null will disable file callbacks. + 'blockalign' Internal minimum file block alignment. FMOD will read data in at least chunks of this size if you ask it to. Specifying 0 means there is no file buffering at all (this could adversely affect streaming). Do NOT make this a large value, it is purely a setting for minimum sector size alignment to aid seeking and reading on certain media. It is not for stream buffer sizes, that is what System::setStreamBufferSize is for. It is recommened just to pass -1. Large values just mean large memory usage with no benefit. Specify -1 to not set this value. Default = 2048. + + [RETURN_VALUE] + + [REMARKS] + This has no effect on sounds loaded with FMOD_OPENMEMORY or FMOD_CREATEUSER.
+
+ This function can be used to set user file callbacks, or if required, they can be turned off by specifying 0.
+ This function can be used purely to set the 'buffersize' parameter, and ignore the callback aspect of the function.
+
+ Warning : This function can cause unpredictable behaviour if not used properly. You must return the right values, and each command must work properly, or FMOD will not function, or it may even crash if you give it invalid data.
+ You must also return FMOD_ERR_FILE_EOF from a read callback if the number of bytes read is smaller than the number of bytes requested.
+
+ FMOD's default filsystem buffers reads every 2048 bytes by default. This means every time fmod reads one byte from the API (say if it was parsing a file format), it simply mem copies the byte from the 2k memory buffer, and every time it needs to, refreshes the 2k buffer resulting in a drastic reduction in file I/O. Large reads go straight to the pointer instead of the 2k buffer if it is buffer aligned. This value can be increased or decreased by the user. A buffer of 0 means all reads go directly to the pointer specified. 2048 bytes is the size of a CD sector on most CD ISO formats so it is chosen as the default, for optimal reading speed from CD media.
+
+ NOTE! Do not force a cast from your function pointer to the FMOD_FILE_xxxCALLBACK type! Never try to 'force' fmod to accept your function. If there is an error then find out what it is. Remember to include F_CALLBACK between the return type and the function name, this equates to stdcall which you must include otherwise (besides not compiling) it will cause problems such as crashing and callbacks not being called. +
+ NOTE! Your file callbacks must be thread safe. If not unexpected behaviour may occur. FMOD calls file functions from asynchronous threads, such as the streaming thread, and thread related to FMOD_NONBLOCKING flag. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init + System::attachFileSystem + FMOD_FILE_OPENCALLBACK + FMOD_FILE_CLOSECALLBACK + FMOD_FILE_READCALLBACK + FMOD_FILE_SEEKCALLBACK +] +*/ +FMOD_RESULT F_API FMOD_System_SetFileSystem(FMOD_SYSTEM *system, FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setFileSystem(useropen, userclose, userread, userseek, blockalign); +} + + +/* +[API] +[ + [DESCRIPTION] + Function to allow a user to 'piggyback' on FMOD's file reading routines. This allows users to capture data as FMOD reads it, which may be useful for ripping the raw data that FMOD reads for hard to support sources (for example internet streams or cdda streams). + + [PARAMETERS] + 'useropen' Pointer to an open callback which is called after a file is opened by FMOD. + 'userclose' Pointer to a close callback which is called after a file is closed by FMOD. + 'userread' Pointer to a read callback which is called after a file is read by FMOD. + 'userseek' Pointer to a seek callback which is called after a file is seeked into by FMOD. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setFileSystem + FMOD_FILE_OPENCALLBACK + FMOD_FILE_CLOSECALLBACK + FMOD_FILE_READCALLBACK + FMOD_FILE_SEEKCALLBACK + +] +*/ +FMOD_RESULT F_API FMOD_System_AttachFileSystem(FMOD_SYSTEM *system, FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->attachFileSystem(useropen, userclose, userread, userseek); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets advanced features like configuring memory and cpu usage for FMOD_CREATECOMPRESSEDSAMPLE usage. + + [PARAMETERS] + 'settings' Pointer to FMOD_ADVANCEDSETTINGS structure. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_ADVANCEDSETTINGS + System::getAdvancedSettings + FMOD_MODE +] +*/ +FMOD_RESULT F_API FMOD_System_SetAdvancedSettings(FMOD_SYSTEM *system, FMOD_ADVANCEDSETTINGS *settings) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setAdvancedSettings(settings); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the advanced settings value set for the system object. + + [PARAMETERS] + 'settings' Address of a variable to receive the contents of the FMOD_ADVANCEDSETTINGS structure specified by the user. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_ADVANCEDSETTINGS + System::setAdvancedSettings +] +*/ +FMOD_RESULT F_API FMOD_System_GetAdvancedSettings(FMOD_SYSTEM *system, FMOD_ADVANCEDSETTINGS *settings) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getAdvancedSettings(settings); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the speaker mode in the hardware and FMOD software mixing engine. + + [PARAMETERS] + 'speakermode' Speaker mode specified from the list in FMOD_SPEAKERMODE. + + [RETURN_VALUE] + + [REMARKS] + Speaker modes that are supported on each platform are as follows.
+
  • Win32/Win64 - All. +
  • Linux/Linux64 - All. +
  • Solaris - All. +
  • Macintosh - All. +
  • iPhone - FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_MONO, FMOD_SPEAKERMODE_STEREO, FMOD_SPEAKERMODE_PROLOGIC. +
  • Wii - FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_MONO, FMOD_SPEAKERMODE_STEREO, FMOD_SPEAKERMODE_PROLOGIC. +
  • PS2 - FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_MONO, FMOD_SPEAKERMODE_STEREO, FMOD_SPEAKERMODE_PROLOGIC. +
  • PSP - FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_MONO, FMOD_SPEAKERMODE_STEREO, FMOD_SPEAKERMODE_PROLOGIC. +
  • PS3 - FMOD_SPEAKERMODE_7POINT1 only. The user cannot change the speaker mode on this platform. +
  • Xbox 360 - FMOD_SPEAKERMODE_5POINT1 only. The user cannot change the speaker mode on this platform. +
    +
    + NOTE! Calling this function resets any speaker positions set with System::set3DSpeakerPosition, therefore this function must be called before calling System::set3DSpeakerPosition.
    + If System::setSoftwareFormat is called after this function with a valid output channel count, the speakermode is set to FMOD_SPEAKERMODE_RAW.
    + If this function is called after System::setSoftwareFormat, then it will overwrite the channel count specified in that function.
    + The channel count that is overwritten for each speaker mode is as follows:
    +
  • FMOD_SPEAKERMODE_RAW - Channel count is unaffected. +
  • FMOD_SPEAKERMODE_MONO - Channel count is set to 1. +
  • FMOD_SPEAKERMODE_STEREO - Channel count is set to 2. +
  • FMOD_SPEAKERMODE_QUAD - Channel count is set to 4. +
  • FMOD_SPEAKERMODE_SURROUND - Channel count is set to 5. +
  • FMOD_SPEAKERMODE_5POINT1 - Channel count is set to 6. +
  • FMOD_SPEAKERMODE_7POINT1 - Channel count is set to 8. +
  • FMOD_SPEAKERMODE_PROLOGIC - Channel count is set to 2. +
    +
    + These channel counts are the channel width of the FMOD DSP system, and affect software mixed sounds (sounds created with FMOD_SOFTWARE flag) only.
    + Hardware sounds are not affected, but will still have the speaker mode appropriately set if possible. (On Windows the speaker mode is set by the user in the control panel, not by FMOD).
    +
    + NOTE! (ProLogic only) Software 3D sounds will still be panned as though they are in FMOD_SPEAKERMODE_STEREO.
    +
    + NOTE! (Windows only) Sound will not behave correctly unless your control panel has set the speaker mode to the correct setup. For example if FMOD_SPEAKERMODE_7POINT1 is set on a speaker system that has been set to 'stereo' in the windows control panel, sounds can dissapear and come out of the wrong speaker. Make sure your users know about this.
    + If using WinMM output, note that some soundcard drivers do not support multichannel output correctly (i.e. Creative cards).
    + If using WASAPI the speaker mode will be forced to the control panel setting, you can call System::getSpeakerMode after System::init to verify.
    + Only DirectSound, WASAPI and ASIO have reliably working multichannel output.
    + If the speaker mode is not actually supported (even though the user set the speaker mode to 7.1 in Windows) the soundcard might not be able to handle it. You will get FMOD_ERR_OUTPUT_CREATEBUFFER error. Change the speaker mode to FMOD_SPEAKERMODE_STEREO and re-initialize if this happens.
    +
    + To set the speaker mode to that of the windows control panel, use System::getDriverCaps. For example:
    +
    +    FMOD_SPEAKERMODE speakermode;
    +    FMOD_RESULT      result;
    +
    +    result = system->getDriverCaps(0,0,0,0,&speakermode);   // Get speaker mode for default driver.
    +    ERRCHECK(result);
    +
    +    result = system->setSpeakerMode(speakermode);
    +    ERRCHECK(result);
    +    
    +
    + This function cannot be called after FMOD is already activated with System::init.
    + It must be called before System::init, or after System::close.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getSpeakerMode + FMOD_SPEAKERMODE + System::init + System::close + System::setSoftwareFormat + System::set3DSpeakerPosition + System::getDriverCaps + FMOD_RESULT +] +*/ +FMOD_RESULT F_API FMOD_System_SetSpeakerMode(FMOD_SYSTEM *system, FMOD_SPEAKERMODE speakermode) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setSpeakerMode(speakermode); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current speaker mode. + + [PARAMETERS] + 'speakermode' Address of a variable that receives the current speaker mode. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setSpeakerMode + FMOD_SPEAKERMODE +] +*/ +FMOD_RESULT F_API FMOD_System_GetSpeakerMode(FMOD_SYSTEM *system, FMOD_SPEAKERMODE *speakermode) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getSpeakerMode(speakermode); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a system callback to catch various fatal or informational events. + + [PARAMETERS] + 'callback' Pointer to a callback to receive the event callback when it happens. + + [RETURN_VALUE] + + [REMARKS] + System callbacks are not asynchronous and are bound by the latency caused by the rate the user calls the update command.
    +
    + Callbacks are stdcall. Use F_CALLBACK inbetween your return type and function name.
    + Example: +
    +
    +    FMOD_RESULT F_CALLBACK systemcallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void *commanddata1, void *commanddata2)
    +    {
    +    
      FMOD::System *sys = (FMOD::System *)system; +
      + switch (type) + { +
        case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED: + { +
          int numdrivers; +
          + printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED occured.\n"); +
          + sys->getNumDrivers(&numdrivers); +
          + printf("Numdevices = %d\n", numdrivers); + break; +
        } + case FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED: + { +
          printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED occured.\n"); + printf("%s.\n", commanddata1); + printf("%d bytes.\n", commanddata2); + break; +
        } + case FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED: + { +
          printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED occured.\n"); + printf("Thread ID = %d\n", (int)commanddata1); + printf("Thread Name = %s\n", (char *)commanddata2); + break; +
        } + case FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION: + { +
          FMOD::DSP *source = (FMOD::DSP *)commanddata1; + FMOD::DSP *dest = (FMOD::DSP *)commanddata2; +
          + printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION occured.\n"); + if (source) + { +
            char name[256]; + source->getInfo(name, 0,0,0,0); + printf("SOURCE = %s\n", name); +
          } + if (dest) + { +
            char name[256]; + dest->getInfo(name, 0,0,0,0); + printf("DEST = %s\n", name); +
          } + break; +
        } +
      } +
      + return FMOD_OK; +
    } +
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::update + FMOD_SYSTEM_CALLBACK + FMOD_SYSTEM_CALLBACKTYPE +] +*/ +FMOD_RESULT F_API FMOD_System_SetCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK callback) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setCallback(callback); +} + + +/* +[API] +[ + [DESCRIPTION] + Specify a base search path for plugins so they can be placed somewhere else than the directory of the main executable. + + [PARAMETERS] + 'path' A character string containing a correctly formatted path to load plugins from. + + [RETURN_VALUE] + + [REMARKS] + The 'plugin' version of FMOD relies on plugins, so when System::init is called it tries to load all FMOD registered plugins.
    + This path is where it will attempt to load from. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::loadPlugin + System::init +] +*/ +FMOD_RESULT F_API FMOD_System_SetPluginPath(FMOD_SYSTEM *system, const char *path) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setPluginPath(path); +} + + +/* +[API] +[ + [DESCRIPTION] + Loads an FMOD plugin. This could be a DSP, file format or output plugin. + + [PARAMETERS] + 'filename' Filename of the plugin to be loaded. + 'handle' Pointer to an unsigned int to receive the plugin handle, for later use. + 'priority' (FMOD_PLUGINTYPE_CODEC only) Priority of the codec compared to other codecs. 0 = most important. higher numbers = less importance. + + [RETURN_VALUE] + + [REMARKS] + Once the plugin is loaded, it can be enumerated and used.
    + For file format plugins, FMOD will automatically try to use them when System::createSound is used.
    + For DSP plugins, you can enumerate them with System::getNumPlugins, System::getPluginHandle and System::getPluginInfo.
    + Plugins can be created for FMOD by the user. See the relevant section in the documentation on creating plugins.
    + The format of the plugin is dependant on the operating system.
    + On Win32 and Win64 the .dll format is used
    + On Linux, the .so format is used.
    + On Macintosh, the .shlib format is used
    +
    + The codecs internal to FMOD have the following priorities.
    +
    +
    +    Tag         100
    +    CDDA        200
    +    FSB         300
    +    DSP         400
    +    VAG         500
    +    Wav         600 
    +    AT3         700
    +    OggVorbis   800
    +    Tremor      900
    +    AIFF        1000
    +    FLAC        1100
    +    MOD         1200
    +    S3M         1300
    +    XM          1400
    +    IT          1500
    +    MIDI        1600
    +    DLS         1700
    +    SF2         1800
    +    ASF         1900
    +    XMA         2000
    +    XWMA        2100
    +    Playlist    2200
    +    MPEGPSP     2300
    +    MPEG        2400
    +    Raw         2500
    +    
    + Note: Some codecs are only for certain platforms, ie XMA is xbox 360 only. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris + + [SEE_ALSO] + System::setPluginPath + System::unloadPlugin + System::getNumPlugins + System::getPluginHandle + System::getPluginInfo + System::setOutputByPlugin + System::getOutputByPlugin + System::createDSPByPlugin + System::createSound +] +*/ +FMOD_RESULT F_API FMOD_System_LoadPlugin(FMOD_SYSTEM *system, const char *filename, unsigned int *handle, unsigned int priority) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->loadPlugin(filename, handle, priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Unloads a plugin from memory. + + [PARAMETERS] + 'handle' Handle to a pre-existing plugin. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::loadPlugin +] +*/ +FMOD_RESULT F_API FMOD_System_UnloadPlugin(FMOD_SYSTEM *system, unsigned int handle) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->unloadPlugin(handle); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of available plugins loaded into FMOD at the current time. + + [PARAMETERS] + 'plugintype' The type of plugin type such as FMOD_PLUGINTYPE_OUTPUT, FMOD_PLUGINTYPE_CODEC or FMOD_PLUGINTYPE_DSP. + 'numplugins' Address of a variable that receives the number of available plugins for the selected type. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_PLUGINTYPE + System::getPluginHandle +] +*/ +FMOD_RESULT F_API FMOD_System_GetNumPlugins(FMOD_SYSTEM *system, FMOD_PLUGINTYPE plugintype, int *numplugins) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getNumPlugins(plugintype, numplugins); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the handle of a plugin based on its type and relative index. Use System::getNumPlugins to enumerate plugins. + + [PARAMETERS] + 'plugintype' The type of plugin type such as FMOD_PLUGINTYPE_OUTPUT, FMOD_PLUGINTYPE_CODEC or FMOD_PLUGINTYPE_DSP. + 'index' The relative index for the type of plugin. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumPlugins +] +*/ +FMOD_RESULT F_API FMOD_System_GetPluginHandle(FMOD_SYSTEM *system, FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getPluginHandle(plugintype, index, handle); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves information to display for the selected plugin. + + [PARAMETERS] + 'handle' Handle to a pre-existing plugin. + 'plugintype' Address of a variable that receives the type of the plugin, FMOD_PLUGINTYPE_OUTPUT, FMOD_PLUGINTYPE_CODEC or FMOD_PLUGINTYPE_DSP. + 'name' Address of a variable that receives the name of the plugin. + 'namelen' Length in bytes of the target buffer to receieve the string. + 'version' Version number set by the plugin. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumPlugins +] +*/ +FMOD_RESULT F_API FMOD_System_GetPluginInfo(FMOD_SYSTEM *system, unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getPluginInfo(handle, plugintype, name, namelen, version); +} + + +/* +[API] +[ + [DESCRIPTION] + Selects an output type based on the enumerated list of outputs including FMOD and 3rd party output plugins. + + [PARAMETERS] + 'handle' Handle to a pre-existing output plugin. + + [RETURN_VALUE] + + [REMARKS] + This function cannot be called after FMOD is already activated with System::init.
    + It must be called before System::init.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumPlugins + System::getOutputByPlugin + System::setOutput + System::init + System::close +] +*/ +FMOD_RESULT F_API FMOD_System_SetOutputByPlugin(FMOD_SYSTEM *system, unsigned int handle) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setOutputByPlugin(handle); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the currently selected output as an id in the list of output plugins. + + [PARAMETERS] + 'handle' Handle to a pre-existing output plugin. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumPlugins + System::setOutputByPlugin + System::setOutput +] +*/ +FMOD_RESULT F_API FMOD_System_GetOutputByPlugin(FMOD_SYSTEM *system, unsigned int *handle) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getOutputByPlugin(handle); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates a DSP unit object which is either built in or loaded as a plugin, to be inserted into a DSP network, for the purposes of sound filtering or sound generation.
    + This function creates a DSP unit that can be enumerated by using System::getNumPlugins and System::getPluginInfo. + + [PARAMETERS] + 'handle' Handle to a pre-existing DSP plugin. + 'dsp' Address of a variable to receive a newly created FMOD::DSP object. + + [RETURN_VALUE] + + [REMARKS] + A DSP unit can generate or filter incoming data.
    + To be active, a unit must be inserted into the FMOD DSP network to be heard. Use functions such as System::addDSP, Channel::addDSP or DSP::addInput to do this.
    + For more information and a detailed description (with diagrams) see the tutorial on the DSP system in the documentation.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumPlugins + System::getPluginInfo + System::createDSPByType + System::createDSP + System::addDSP + Channel::addDSP + DSP::addInput + DSP::setActive +] +*/ +FMOD_RESULT F_API FMOD_System_CreateDSPByPlugin(FMOD_SYSTEM *system, unsigned int handle, FMOD_DSP **dsp) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createDSPByPlugin(handle, (FMOD::DSP **)dsp); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates an in memory file format codec to be used by FMOD by passing in a codec description structure.
    + Once this is created, FMOD will use it to open user defined file formats. + + [PARAMETERS] + 'description' Address of a FMOD_CODEC_DESCRIPTION structure, containing information about the codec. + 'priority' Priority of the codec compared to other codecs. 0 = most important. higher numbers = less importance. + + [RETURN_VALUE] + + [REMARKS] + The codecs internal to FMOD have the following priorities.
    +
    +
    +    Tag         100
    +    CDDA        200
    +    FSB         300
    +    DSP         400
    +    VAG         500
    +    Wav         600 
    +    AT3         700
    +    OggVorbis   800
    +    Tremor      900
    +    AIFF        1000
    +    FLAC        1100
    +    MOD         1200
    +    S3M         1300
    +    XM          1400
    +    IT          1500
    +    MIDI        1600
    +    DLS         1700
    +    SF2         1800
    +    ASF         1900
    +    XMA         2000
    +    XWMA        2100
    +    Playlist    2200
    +    MPEGPSP     2300
    +    MPEG        2400
    +    Raw         2500
    +    
    + Note: Some codecs are only for certain platforms, ie XMA is xbox 360 only. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CODEC_DESCRIPTION +] +*/ +FMOD_RESULT F_API FMOD_System_CreateCodec(FMOD_SYSTEM *system, FMOD_CODEC_DESCRIPTION *description, unsigned int priority) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createCodec(description, priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Initializes the system object, and the sound device. This has to be called at the start of the user's program.
    + You must create a system object with FMOD::System_create. + + [PARAMETERS] + 'maxchannels' The maximum number of channels to be used in FMOD. They are also called 'virtual channels' as you can play as many of these as you want, even if you only have a small number of hardware or software voices. See remarks for more. + 'flags' See FMOD_INITFLAGS. This can be a selection of flags bitwise OR'ed together to change the behaviour of FMOD at initialization time. + 'extradriverdata' Driver specific data that can be passed to the output plugin. For example the filename for the wav writer plugin. See FMOD_OUTPUTTYPE for what each output mode might take here. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + Virtual channels.
    + These types of voices are the ones you work with using the FMOD::Channel API.
    + The advantage of virtual channels are, unlike older versions of FMOD, you can now play as many sounds as you like without fear of ever running out of voices, or playsound failing.
    + You can also avoid 'channel stealing' if you specify enough virtual voices.
    +
    + As an example, you can play 1000 sounds at once, even on a 32 channel soundcard.
    + FMOD will only play the most important/closest/loudest (determined by volume/distance/geometry and priority settings) voices, and the other 968 voices will be virtualized without expense to the CPU. The voice's cursor positions are updated.
    + When the priority of sounds change or emulated sounds get louder than audible ones, they will swap the actual voice resource over (ie hardware or software buffer) and play the voice from its correct position in time as it should be heard.
    + What this means is you can play all 1000 sounds, if they are scattered around the game world, and as you move around the world you will hear the closest or most important 32, and they will automatically swap in and out as you move.
    +
    + Currently the maximum channel limit is 4093.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_INITFLAGS + System::close + System_Create + FMOD_OUTPUTTYPE +] +*/ +FMOD_RESULT F_API FMOD_System_Init(FMOD_SYSTEM *system, int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->init(maxchannels, flags, extradriverdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Closes the system object without freeing the object's memory, so the system handle will still be valid.
    + Closing the output renders objects created with this system object invalid. Make sure any sounds, channelgroups, geometry and dsp objects are released before closing the system object.
    + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init + System::release +] +*/ +FMOD_RESULT F_API FMOD_System_Close(FMOD_SYSTEM *system) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->close(); +} + + +/* +[API] +[ + [DESCRIPTION] + Updates the FMOD system. This should be called once per 'game' tick, or once per frame in your application. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + This updates the following things.
    +
  • 3D Sound. System::update must be called to get 3D positioning. +
  • Virtual voices. If more voices are played than there are real hardware/software voices, System::update must be called to handle the virtualization. +
  • *_NRT output modes. System::update must be called to drive the output for these output modes. +
  • FMOD_INIT_STREAM_FROM_UPDATE. System::update must be called to update the streamer if this flag has been used. +
  • Callbacks. System::update must be called to fire callbacks if they are specified. +
  • FMOD_NONBLOCKING. System::update must be called to make sounds opened with FMOD_NONBLOCKING flag to work properly. +
  • FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED callback. System::update must be called for this callback to trigger. +
    + If FMOD_OUTPUTTYPE_NOSOUND_NRT or FMOD_OUTPUTTYPE_WAVWRITER_NRT output modes are used, this function also drives the software / DSP engine, instead of it running asynchronously in a thread as is the default behaviour.
    + This can be used for faster than realtime updates to the decoding or DSP engine which might be useful if the output is the wav writer for example.
    +
    + If FMOD_INIT_STREAM_FROM_UPDATE is used, this function will update the stream engine. Combining this with the non realtime output will mean smoother captured output.
    +
    + Warning! Do not be tempted to call this function from a different thread to other FMOD commands! This is dangerous and will cause corruption/crashes. This function is not thread safe, and should be called from the same thread as the rest of the FMOD commands. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init + FMOD_INITFLAGS + FMOD_OUTPUTTYPE + FMOD_MODE +] +*/ +FMOD_RESULT F_API FMOD_System_Update(FMOD_SYSTEM *system) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->update(); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the global doppler scale, distance factor and log rolloff scale for all 3D sound in FMOD. + + [PARAMETERS] + 'dopplerscale' Scaling factor for doppler shift. Default = 1.0. + 'distancefactor' Relative distance factor to FMOD's units. Default = 1.0. (1.0 = 1 metre). + 'rolloffscale' Scaling factor for 3D sound rolloff or attenuation for FMOD_3D_LOGROLLOFF based sounds only (which is the default type). Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + The doppler scale is a general scaling factor for how much the pitch varies due to doppler shifting in 3D sound. Doppler is the pitch + bending effect when a sound comes towards the listener or moves away from it, much like the effect you hear when a train goes past you with + its horn sounding. With "dopplerscale" you can exaggerate or diminish the effect. FMOD's effective speed of sound at a doppler factor of 1.0 is 340 m/s. +

    + The distance factor is the FMOD 3D engine relative distance factor, compared to 1.0 meters. Another way to put it is that it equates to "how many units per meter does your engine have". + For example, if you are using feet then "scale" would equal 3.28. +

    + Note! This only affects doppler! If you keep your min/max distance, custom rolloff curves and positions in scale relative to each other the volume rolloff will not change. + If you set this, the mindistance of a sound will automatically set itself to this value when it is created in case the user forgets to set the mindistance to match the new distancefactor. +

    + The rolloff scale sets the global attenuation rolloff factor for FMOD_3D_LOGROLLOFF based sounds only (which is the default). Normally volume for a + sound will scale at mindistance / distance. This gives a logarithmic attenuation of volume as the source gets further away (or closer). + Setting this value makes the sound drop off faster or slower. The higher the value, the faster volume will attenuate, and conversely the lower + the value, the slower it will attenuate. For example a rolloff factor of 1 will simulate the real world, where as a value of 2 will make sounds attenuate 2 times quicker. +

    + Note! "rolloffscale" has no effect when using FMOD_3D_LINEARROLLOFF or FMOD_3D_CUSTOMROLLOFF. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::get3DSettings + Sound::set3DMinMaxDistance + Sound::get3DMinMaxDistance + Channel::set3DAttributes + Channel::get3DAttributes +] +*/ +FMOD_RESULT F_API FMOD_System_Set3DSettings(FMOD_SYSTEM *system, float dopplerscale, float distancefactor, float rolloffscale) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->set3DSettings(dopplerscale, distancefactor, rolloffscale); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the global doppler scale, distance factor and rolloff scale for all 3D sound in FMOD. + + [PARAMETERS] + 'dopplerscale' Address of a variable that receives the scaling factor for doppler shift. Optional. Specify 0 or NULL to ignore. + 'distancefactor' Address of a variable that receives the relative distance factor to FMOD's units. Optional. Specify 0 or NULL to ignore. + 'rolloffscale' Address of a variable that receives the scaling factor for 3D sound rolloff or attenuation. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::set3DSettings +] +*/ +FMOD_RESULT F_API FMOD_System_Get3DSettings(FMOD_SYSTEM *system, float *dopplerscale, float *distancefactor, float *rolloffscale) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->get3DSettings(dopplerscale, distancefactor, rolloffscale); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the number of 3D 'listeners' in the 3D sound scene. This function is useful mainly for split-screen game purposes. + + [PARAMETERS] + 'numlisteners' Number of listeners in the scene. Valid values are from 1-4 inclusive. Default = 1. + + [RETURN_VALUE] + + [REMARKS] + If the number of listeners is set to more than 1, then panning and doppler are turned off. All sound effects will be mono.
    + FMOD uses a 'closest sound to the listener' method to determine what should be heard in this case.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::get3DNumListeners + System::set3DListenerAttributes +] +*/ +FMOD_RESULT F_API FMOD_System_Set3DNumListeners(FMOD_SYSTEM *system, int numlisteners) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->set3DNumListeners(numlisteners); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of 3D listeners. + + [PARAMETERS] + 'numlisteners' Address of a variable that receives the current number of 3D listeners in the 3D scene. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::set3DNumListeners +] +*/ +FMOD_RESULT F_API FMOD_System_Get3DNumListeners(FMOD_SYSTEM *system, int *numlisteners) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->get3DNumListeners(numlisteners); +} + + +/* +[API] +[ + [DESCRIPTION] + This updates the position, velocity and orientation of the specified 3D sound listener. + + [PARAMETERS] + 'listener' Listener ID in a multi-listener environment. Specify 0 if there is only 1 listener. + 'pos' The position of the listener in world space, measured in distance units. You can specify 0 or NULL to not update the position. + 'vel' The velocity of the listener measured in distance units per second. You can specify 0 or NULL to not update the velocity of the listener. + 'forward' The forwards orientation of the listener. This vector must be of unit length and perpendicular to the up vector. You can specify 0 or NULL to not update the forwards orientation of the listener. + 'up' The upwards orientation of the listener. This vector must be of unit length and perpendicular to the forwards vector. You can specify 0 or NULL to not update the upwards orientation of the listener. + + [RETURN_VALUE] + + [REMARKS] + By default, FMOD uses a left-handed co-ordinate system. This means +X is right, +Y is up, and +Z is forwards.
    + To change this to a right-handed coordinate system, use FMOD_INIT_3D_RIGHTHANDED. This means +X is left, +Y is up, and +Z is forwards.
    +
    + To map to another coordinate system, flip/negate and exchange these values.
    +
    + Orientation vectors are expected to be of UNIT length. This means the magnitude of the vector should be 1.0.
    +
    + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    +
    + Always remember to use units per second, not units per frame as this is a common mistake and will make the doppler effect sound wrong.
    + For example, Do not just use (pos - lastpos) from the last frame's data for velocity, as this is not correct. You need to time compensate it so it is given in units per second.
    + You could alter your pos - lastpos calculation to something like this.
    +

    +	vel = (pos-lastpos) / time_taken_since_last_frame_in_seconds.
    +    
    + I.e. at 60fps the formula would look like this vel = (pos-lastpos) / 0.0166667.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::get3DListenerAttributes + FMOD_INITFLAGS + System::set3DSettings + System::get3DSettings + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_System_Set3DListenerAttributes(FMOD_SYSTEM *system, int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->set3DListenerAttributes(listener, pos, vel, forward, up); +} + + +/* +[API] +[ + [DESCRIPTION] + This retrieves the position, velocity and orientation of the specified 3D sound listener. + + [PARAMETERS] + 'listener' Listener ID in a multi-listener environment. Specify 0 if there is only 1 listener. + 'pos' Address of a variable that receives the position of the listener in world space, measured in distance units. Optional. Specify 0 or NULL to ignore. + 'vel' Address of a variable that receives the velocity of the listener measured in distance units per second. Optional. Specify 0 or NULL to ignore. + 'forward' Address of a variable that receives the forwards orientation of the listener. Optional. Specify 0 or NULL to ignore. + 'up' Address of a variable that receives the upwards orientation of the listener. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::set3DListenerAttributes + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_System_Get3DListenerAttributes(FMOD_SYSTEM *system, int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->get3DListenerAttributes(listener, pos, vel, forward, up); +} + + +/* +[API] +[ + [DESCRIPTION] + When FMOD wants to calculate 3d volume for a channel, this callback can be used to override the internal volume calculation based on distance. + + [PARAMETERS] + 'callback' Pointer to a C function of type FMOD_3D_ROLLOFFCALLBACK, that is used to override the FMOD volume calculation. Default is 0 or NULL. Setting the callback to null will return 3d calculation back to FMOD. + + [RETURN_VALUE] + + [REMARKS] + This function overrides FMOD_3D_LOGROLLOFF, FMOD_3D_LINEARROLLOFF, FMOD_3D_CUSTOMROLLOFF. To allow FMOD to calculate the 3d volume again, use 0 or NULL as the callback.

    + NOTE: When using the event system, call Channel::getUserData from your FMOD_3D_ROLLOFFCALLBACK to get the event instance handle of the event that spawned the channel in question. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_3D_ROLLOFFCALLBACK + System::set3DListenerAttributes + System::get3DListenerAttributes + Channel::getUserData +] +*/ +FMOD_RESULT F_API FMOD_System_Set3DRolloffCallback(FMOD_SYSTEM *system, FMOD_3D_ROLLOFFCALLBACK callback) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->set3DRolloffCallback(callback); +} + + +/* +[API] +[ + [DESCRIPTION] + This function allows the user to specify the position of their actual physical speaker to account for non standard setups.
    + It also allows the user to disable speakers from 3D consideration in a game.
    + The funtion is for describing the 'real world' speaker placement to provide a more natural panning solution for 3d sound. Graphical configuration screens in an application could draw icons for speaker placement that the user could position at their will. + + [PARAMETERS] + 'speaker' The selected speaker of interest to position. + 'x' The 2D X offset in relation to the listening position. For example -1.0 would mean the speaker is on the left, and +1.0 would mean the speaker is on the right. 0.0 is the speaker is in the middle. + 'y' The 2D Y offset in relation to the listening position. For example -1.0 would mean the speaker is behind the listener, and +1 would mean the speaker is in front of the listener. + 'active' Enables or disables speaker from 3D consideration. Useful for disabling center speaker for vocals for example, or the LFE. x and y can be anything in this case. + + [RETURN_VALUE] + + [REMARKS] + Note! This only affects software mixed 3d sounds, created with FMOD_SOFTWARE and FMOD_3D.
    +
    + A typical 7.1 setup would look like this.
    +

    +    system->set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT,    -1.0f,  1.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT,    1.0f,  1.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER,   0.0f,  1.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_LOW_FREQUENCY,  0.0f,  0.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT,     -1.0f, -1.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT,     1.0f, -1.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT,     -1.0f,  0.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT,     1.0f,  0.0f, true);
    +    
    + A typical stereo setup would look like this.
    +
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT,    -1.0f,  0.0f, true);
    +    system->set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT,    1.0f,  0.0f, true);
    +    
    + You could use this function to make sounds in front of your come out of different physical speakers. If you specified for example that FMOD_SPEAKER_SIDE_RIGHT was in front of you at <0.0, 1.0> and you organized the other speakers accordingly the 3d audio would come out of the side right speaker when it was in front instead of the default which is only to the side.
    + This function is also useful if speakers are not 'perfectly symmetrical'. For example if the center speaker was closer to the front left than the front right, this function could be used to position that center speaker accordingly and FMOD would skew the panning appropriately to make it sound correct again.
    +
    + The 2d coordinates used are only used to generate angle information. Size / distance does not matter in FMOD's implementation because it is not FMOD's job to attenuate or amplify the signal based on speaker distance. If it amplified the signal in the digital domain the audio could clip/become distorted. It is better to use the amplifier's analogue level capabilities to balance speaker volumes. +
    + Calling System::setSpeakerMode overrides these values, so this function must be called after this.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::get3DSpeakerPosition + System::setSpeakerMode + FMOD_SPEAKERMODE + FMOD_SPEAKER + +] +*/ +FMOD_RESULT F_API FMOD_System_Set3DSpeakerPosition(FMOD_SYSTEM *system, FMOD_SPEAKER speaker, float x, float y, FMOD_BOOL active) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->set3DSpeakerPosition(speaker, x, y, active ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current speaker position information for the selected speaker. + + [PARAMETERS] + 'speaker' The selected speaker of interest to return the x and y position. + 'x' Address of a variable that receives the 2D X position relative to the listener. Optional. Specify 0 or NULL to ignore. + 'y' Address of a variable that receives the 2D Y position relative to the listener. Optional. Specify 0 or NULL to ignore. + 'active' Address of a variable that receives the active state of a speaker. + + [RETURN_VALUE] + + [REMARKS] + See the System::set3DSpeakerPosition for more information on speaker positioning. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::set3DSpeakerPosition + FMOD_SPEAKERMODE + FMOD_SPEAKER +] +*/ +FMOD_RESULT F_API FMOD_System_Get3DSpeakerPosition(FMOD_SYSTEM *system, FMOD_SPEAKER speaker, float *x, float *y, FMOD_BOOL *active) +{ + FMOD_RESULT result; + bool active2; + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _system->get3DSpeakerPosition(speaker, x, y, &active2); + if (result == FMOD_OK) + { + if (active) + { + *active = active2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the internal buffersize for streams opened after this call.
    + Larger values will consume more memory (see remarks), whereas smaller values may cause buffer under-run/starvation/stuttering caused by large delays in disk access (ie CDROM or netstream), or cpu usage in slow machines, or by trying to play too many streams at once.
    + + [PARAMETERS] + 'filebuffersize' Size of stream file buffer. Default is 16384 (FMOD_TIMEUNIT_RAWBYTES). + 'filebuffersizetype' Type of unit for stream file buffer size. Must be FMOD_TIMEUNIT_MS, FMOD_TIMEUNIT_PCM, FMOD_TIMEUNIT_PCMBYTES or FMOD_TIMEUNIT_RAWBYTES. Default is FMOD_TIMEUNIT_RAWBYTES. + + [RETURN_VALUE] + + [REMARKS] + Note this function does not affect streams created with FMOD_OPENUSER, as the buffer size is specified in System::createSound.
    + This function does not affect latency of playback. All streams are pre-buffered (unless opened with FMOD_OPENONLY), so they will always start immediately.
    + Seek and Play operations can sometimes cause a reflush of this buffer.
    +
    + If FMOD_TIMEUNIT_RAWBYTES is used, the memory allocated is 2 * the size passed in, because fmod allocates a double buffer.
    + If FMOD_TIMEUNIT_MS, FMOD_TIMEUNIT_PCM or FMOD_TIMEUNIT_PCMBYTES is used, and the stream is infinite (such as a shoutcast netstream), or VBR, then FMOD cannot calculate an accurate compression ratio to work with when the file is opened. This means it will then base the buffersize on FMOD_TIMEUNIT_PCMBYTES, or in other words the number of PCM bytes, but this will be incorrect for some compressed formats.
    + Use FMOD_TIMEUNIT_RAWBYTES for these type (infinite / undetermined length) of streams for more accurate read sizes.
    +
    + Note to determine the actual memory usage of a stream, including sound buffer and other overhead, use Memory_GetStats before and after creating a sound.
    +
    + Note that the stream may still stutter if the codec uses a large amount of cpu time, which impacts the smaller, internal 'decode' buffer.
    + The decode buffer size is changeable via FMOD_CREATESOUNDEXINFO.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + System::createSound + System::getStreamBufferSize + Sound::getOpenState + Channel::setMute + Memory_GetStats + FMOD_CREATESOUNDEXINFO +] +*/ +FMOD_RESULT F_API FMOD_System_SetStreamBufferSize(FMOD_SYSTEM *system, unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setStreamBufferSize(filebuffersize, filebuffersizetype); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the current internal buffersize settings for streamable sounds. + + [PARAMETERS] + 'filebuffersize' Address of a variable that receives the current stream file buffer size setting. Default is 16384 (FMOD_TIMEUNIT_RAWBYTES). Optional. Specify 0 or NULL to ignore. + 'filebuffersizetype' Address of a variable that receives the type of unit for the current stream file buffer size setting. Can be FMOD_TIMEUNIT_MS, FMOD_TIMEUNIT_PCM, FMOD_TIMEUNIT_PCMBYTES or FMOD_TIMEUNIT_RAWBYTES. Default is FMOD_TIMEUNIT_RAWBYTES. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + System::setStreamBufferSize +] +*/ +FMOD_RESULT F_API FMOD_System_GetStreamBufferSize(FMOD_SYSTEM *system, unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getStreamBufferSize(filebuffersize, filebuffersizetype); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the current version of FMOD Ex being used. + + [PARAMETERS] + 'version' Address of a variable that receives the current FMOD Ex version. + + [RETURN_VALUE] + + [REMARKS] + The version is a 32bit hexadecimal value formated as 16:8:8, with the upper 16bits being the major version, the middle 8bits being the minor version and the bottom 8bits being the development version. For example a value of 00040106h is equal to 4.01.06. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init +] +*/ +FMOD_RESULT F_API FMOD_System_GetVersion(FMOD_SYSTEM *system, unsigned int *version) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getVersion(version); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to the system level output device module. This means a pointer to a DirectX "LPDIRECTSOUND", or a WINMM handle, or with something like with FMOD_OUTPUTTYPE_NOSOUND output, the handle will be null or 0. + + [PARAMETERS] + 'handle' Address of a variable that receives the handle to the output mode's native hardware API object (see remarks). + + [RETURN_VALUE] + + [REMARKS] + Must be called after System::init.
    + Cast the resulting pointer depending on what output system pointer you are after.
    +
    + FMOD_OUTPUTTYPE_DSOUND Pointer to type DIRECTSOUND is returned.
    + FMOD_OUTPUTTYPE_WINMM Pointer to type HWAVEOUT is returned.
    + FMOD_OUTPUTTYPE_OPENAL Pointer to type ALCdevice is returned.
    + FMOD_OUTPUTTYPE_WASAPI Pointer to type IAudioRenderClient is returned.
    + FMOD_OUTPUTTYPE_ASIO Pointer to type AsioDrivers is returned.
    + FMOD_OUTPUTTYPE_OSS File handle is returned, (cast to int).
    + FMOD_OUTPUTTYPE_ALSA Pointer to type snd_pcm_t is returned.
    + FMOD_OUTPUTTYPE_ESD Handle of type int is returned, as returned by so_esd_open_sound (cast to int).
    + FMOD_OUTPUTTYPE_COREAUDIO Handle of type AudioUnit is returned.
    + FMOD_OUTPUTTYPE_PS2 NULL / 0 is returned.
    + FMOD_OUTPUTTYPE_PS3 NULL / 0 is returned.
    + FMOD_OUTPUTTYPE_XBOX360 Pointer to type IXAudio2 is returned.
    + FMOD_OUTPUTTYPE_PSP NULL / 0 is returned.
    + FMOD_OUTPUTTYPE_WII NULL / 0 is returned.
    + FMOD_OUTPUTTYPE_NOSOUND NULL / 0 is returned.
    + FMOD_OUTPUTTYPE_WAVWRITER NULL / 0 is returned.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OUTPUTTYPE + System::setOutput + System::init +] +*/ +FMOD_RESULT F_API FMOD_System_GetOutputHandle(FMOD_SYSTEM *system, void **handle) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getOutputHandle(handle); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of currently playing channels. + + [PARAMETERS] + 'channels' Address of a variable that receives the number of currently playing channels. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_API FMOD_System_GetChannelsPlaying(FMOD_SYSTEM *system, int *channels) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getChannelsPlaying(channels); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the number of available hardware mixed 2d and 3d channels. + + [PARAMETERS] + 'num2d' Address of a variable that receives the number of available hardware mixed 2d channels. Optional. Specify 0 or NULL to ignore. + 'num3d' Address of a variable that receives the number of available hardware mixed 3d channels. Optional. Specify 0 or NULL to ignore. + 'total' Address of a variable that receives the total number of available hardware mixed channels. Usually total = num2d + num3d, but on some platforms like PS2 and GameCube, 2D and 3D voices share the same channel pool so total, num2d and num3d will all be the same number. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + If total doesn't equal num2d + num3d, it usually means the 2d and 3d hardware voices share the same actual hardware voice pool.
    + For example if it said 32 for each value, then if 30 3d voices were playing, then only 2 voices total would be available, for 2d or 3d playback. They are not separate.
    +
    + NOTE: If this is called before System::init, you must call System::getDriverCaps first. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::init + System::getChannelsPlaying + System::setHardwareChannels + System::getDriverCaps +] +*/ +FMOD_RESULT F_API FMOD_System_GetHardwareChannels(FMOD_SYSTEM *system, int *num2d, int *num3d, int *total) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getHardwareChannels(num2d, num3d, total); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves in percent of CPU time - the amount of cpu usage that FMOD is taking for streaming/mixing and System::update combined. + + [PARAMETERS] + 'dsp' Address of a variable that receives the current dsp mixing engine cpu usage. Result will be from 0 to 100.0f. Optional. Specify 0 or NULL to ignore. + 'stream' Address of a variable that receives the current streaming engine cpu usage. Result will be from 0 to 100.0f. Optional. Specify 0 or NULL to ignore. + 'update' Address of a variable that receives the current System::update cpu usage. Result will be from 0 to 100.0f. Optional. Specify 0 or NULL to ignore. + 'geometry' Address of a variable that receives the current geometry engine cpu usage. Result will be from 0 to 100.0f. Optional. Specify 0 or NULL to ignore. + 'total' Address of a variable that receives the current total cpu usage. Result will be from 0 to 100.0f. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + This value is slightly smoothed to provide more stable readout (and to round off spikes that occur due to multitasking/operating system issues).
    +
    + NOTE! On ps3 and xbox360, the dsp and stream figures are NOT main cpu/main thread usage. On PS3 this is the percentage of SPU being used. On Xbox 360 it is the percentage of a hardware thread being used which is on a totally different CPU than the main one.
    + Do not be alarmed if the usage for these platforms reaches over 50%, this is normal and should be ignored if you are playing a lot of compressed sounds and are using effects. The only value on the main cpu / main thread to take note of here that will impact your framerate is the update value, and this is typically very low (ie less than 1%).
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::update +] +*/ +FMOD_RESULT F_API FMOD_System_GetCPUUsage(FMOD_SYSTEM *system, float *dsp, float *stream, float *geometry, float *update, float *total) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getCPUUsage(dsp, stream, geometry, update, total); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the amount of dedicated sound ram available if the platform supports it.
    + Most platforms use main ram to store audio data, so this function usually isn't necessary. + + [PARAMETERS] + 'currentalloced' Address of a variable that receives the currently allocated sound ram memory at time of call. Optional. Specify 0 or NULL to ignore. + 'maxalloced' Address of a variable that receives the maximum allocated sound ram memory since System::init. Optional. Specify 0 or NULL to ignore. + 'total' Address of a variable that receives the total amount of sound ram available on this device. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, PlayStation 2, PlayStation 3 + + [SEE_ALSO] + Memory_GetStats +] +*/ +FMOD_RESULT F_API FMOD_System_GetSoundRAM(FMOD_SYSTEM *system, int *currentalloced, int *maxalloced, int *total) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getSoundRAM(currentalloced, maxalloced, total); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of available CDROM drives on the user's machine. + + [PARAMETERS] + 'numdrives' Address of a variable that receives the number of CDROM drives. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris + + [SEE_ALSO] + System::getCDROMDriveName +] +*/ +FMOD_RESULT F_API FMOD_System_GetNumCDROMDrives(FMOD_SYSTEM *system, int *numdrives) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getNumCDROMDrives(numdrives); +} + + +/* +[API] +[ + [DESCRIPTION] + Gets information on the selected cdrom drive. + + [PARAMETERS] + 'drive' The enumerated number of the CDROM drive to query. 0 based. + 'drivename' Address of a variable that receives the name of the drive letter or name depending on the operating system. + 'drivenamelen' Length in bytes of the target buffer to receieve the string. + 'scsiname' Address of a variable that receives the SCSI address of the drive. This could also be used to pass to System::createSound, or just used for information purposes. + 'scsinamelen' Length in bytes of the target buffer to receieve the string. + 'devicename' Address of a variable that receives the name of the physical device. This is usually a string defined by the manufacturer. It also contains the drive's vendor ID, product ID and version number. + 'devicenamelen' Length in bytes of the target buffer to receieve the string. + + [RETURN_VALUE] + + [REMARKS] + Enumerate CDROM drives by finding out how many there are with System::getNumCDROMDrives. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris + + [SEE_ALSO] + System::getNumCDROMDrives + System::createSound +] +*/ +FMOD_RESULT F_API FMOD_System_GetCDROMDriveName(FMOD_SYSTEM *system, int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getCDROMDriveName(drive, drivename, drivenamelen, scsiname, scsinamelen, devicename, devicenamelen); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal. + + [PARAMETERS] + 'spectrumarray' Address of a variable that receives the spectrum data. This is an array of floating point values. Data will range is 0.0 to 1.0. Decibels = 10.0f * (float)log10(val) * 2.0f; See remarks for what the data represents. + 'numvalues' Size of array in floating point values being passed to the function. Must be a power of 2. (ie 128/256/512 etc). Min = 64. Max = 8192. + 'channeloffset' Channel of the signal to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' "Pre-FFT" window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the numvalues, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.
    + The larger the numvalues, the more 'lag' the spectrum will seem to inherit. This is because the FFT window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.
    + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).
    + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.
    +
    + What the data represents.
    + To work out what each entry in the array represents, use this formula
    +
    +    entry_hz = (output_rate / 2) / numvalues
    +    
    + The array represents amplitudes of each frequency band from 0hz to the nyquist rate. The nyquist rate is equal to the output rate divided by 2.
    + For example when FMOD is set to 44100hz output, the range of represented frequencies will be 0hz to 22049hz, a total of 22050hz represented.
    + If in the same example, 1024 was passed to this function as the numvalues, each entry's contribution would be as follows. +
    +    entry_hz = (44100 / 2) / 1024
    +    entry_hz = 21.53 hz
    +    
    +
    + Note: This function only displays data for sounds playing that were created with FMOD_SOFTWARE. FMOD_HARDWARE based sounds are played using the sound card driver and are not accessable. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW + Channel::getSpectrum + ChannelGroup::getSpectrum + System::getWaveData +] +*/ +FMOD_RESULT F_API FMOD_System_GetSpectrum(FMOD_SYSTEM *system, float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to a block of PCM data that represents the currently playing audio mix.
    + This function is useful for a very easy way to plot an oscilliscope.
    + + [PARAMETERS] + 'wavearray' Address of a variable that receives the currently playing waveform data. This is an array of floating point values. + 'numvalues' Number of floats to write to the array. Maximum value = 16384. + 'channeloffset' Offset into multichannel data. For mono output use 0. Stereo output will use 0 = left, 1 = right. More than stereo output - use the appropriate index. + + [RETURN_VALUE] + + [REMARKS] + This is the actual resampled, filtered and volume scaled data of the final output, at the time this function is called.
    +
    + Do not use this function to try and display the whole waveform of the sound, as this is more of a 'snapshot' of the current waveform at the time it is called, and could return the same data if it is called very quickly in succession.
    + See the DSP API to capture a continual stream of wave data as it plays, or see Sound::lock / Sound::unlock if you want to simply display the waveform of a sound.
    +
    + This function allows retrieval of left and right data for a stereo sound individually. To combine them into one signal, simply add the entries of each seperate buffer together and then divide them by 2.
    +
    + Note: This function only displays data for sounds playing that were created with FMOD_SOFTWARE. FMOD_HARDWARE based sounds are played using the sound card driver and are not accessable. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getSpectrum + Channel::getWaveData + ChannelGroup::getWaveData + Sound::lock + Sound::unlock +] +*/ +FMOD_RESULT F_API FMOD_System_GetWaveData(FMOD_SYSTEM *system, float *wavearray, int numvalues, int channeloffset) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getWaveData(wavearray, numvalues, channeloffset); +} + + +/* +[API] +[ + [DESCRIPTION] + Loads a sound into memory, or opens it for streaming. + + [PARAMETERS] + 'name_or_data' Name of the file or URL to open, or a pointer to a preloaded sound memory block if FMOD_OPENMEMORY/FMOD_OPENMEMORY_POINT is used. For CD playback the name should be a drive letter with a colon, example "D:" (windows only). + 'mode' Behaviour modifier for opening the sound. See FMOD_MODE. Also see remarks for more. + 'exinfo' Pointer to a FMOD_CREATESOUNDEXINFO which lets the user provide extended information while playing the sound. Optional. Specify 0 or NULL to ignore. + 'sound' Address of a variable to receive a newly created FMOD::Sound object. + + [RETURN_VALUE] + + [REMARKS] + Important! By default (FMOD_CREATESAMPLE) FMOD will try to load and decompress the whole sound into memory! Use FMOD_CREATESTREAM to open it as a stream and have it play back in realtime! FMOD_CREATECOMPRESSEDSAMPLE can also be used for certain formats.
    +
    +
  • To open a file or URL as a stream, so that it decompresses / reads at runtime, instead of loading / decompressing into memory all at the time of this call, use the FMOD_CREATESTREAM flag. This is like a 'stream' in FMOD 3.
    +
  • To open a file or URL as a compressed sound effect that is not streamed and is not decompressed into memory at load time, use FMOD_CREATECOMPRESSEDSAMPLE. This is supported with MPEG (mp2/mp3), ADPCM (wav on all platforms and vag on ps2/psp) and XMA files only. This is useful for those who want realtime compressed soundeffects, but not the overhead of disk access.
    +
  • To open a CD drive, use the drive as the name, for example on the windows platform, use "D:"
    +
  • To open a sound as 2D, so that it is not affected by 3D processing, use the FMOD_2D flag. 3D sound commands will be ignored on these types of sounds.
    +
  • To open a sound as 3D, so that it is treated as a 3D sound, use the FMOD_3D flag. Calls to Channel::setPan will be ignored on these types of sounds.
    +
  • To use FMOD software mixing buffers, use the FMOD_SOFTWARE flag. This gives certain benefits, such as DSP processing, spectrum analysis, loop points, 5.1 mix levels, 2d/3d morphing, and more.
    +
  • To use the soundcard's hardware to play the sound, use the FMOD_HARDWARE flag. This gives certain benefits such as EAX reverb, dolby digital output on some devices, and better 3d sound virtualization using headphones.
    +
    + Note that FMOD_OPENRAW, FMOD_OPENMEMORY, FMOD_OPENMEMORY_POINT and FMOD_OPENUSER will not work here without the exinfo structure present, as more information is needed.
    +
    + Use FMOD_NONBLOCKING to have the sound open or load in the background. You can use Sound::getOpenState to determine if it has finished loading / opening or not. While it is loading (not ready), sound functions are not accessable for that sound.
    +
    + To account for slow devices or computers that might cause buffer underrun (skipping/stuttering/repeating blocks of audio), use System::setStreamBufferSize. +
    + To play WMA files on Windows, the user must have the latest Windows media player codecs installed (Windows Media Player 9). The user can download this as an installer (wmfdist.exe) from www.fmod.org download page if they desire or you may wish to redistribute it with your application (this is allowed). This installer does NOT install windows media player, just the necessary WMA codecs needed. +
    + PlayStation 2 Note: You can pre-pend "host0:" or "cdrom0:" if you like. FMOD will automatically add "host0:" to the filename if it is not found. +
    + Specifying FMOD_OPENMEMORY_POINT will POINT to your memory rather allocating its own sound buffers and duplicating it internally
    + This means you cannot free the memory while FMOD is using it, until after Sound::release is called. + With FMOD_OPENMEMORY_POINT, for PCM formats, only WAV, FSB and RAW are supported. For compressed formats, only those formats supported by FMOD_CREATECOMPRESSEDSAMPLE are supported.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + FMOD_CREATESOUNDEXINFO + Sound::getOpenState + System::setStreamBufferSize + Channel::setPan +] +*/ +FMOD_RESULT F_API FMOD_System_CreateSound(FMOD_SYSTEM *system, const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, FMOD_SOUND **sound) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createSound(name_or_data, mode, exinfo, (FMOD::Sound **)sound); +} + + +/* +[API] +[ + [DESCRIPTION] + Opens a sound for streaming. This function is a helper function that is the same as System::createSound but has the FMOD_CREATESTREAM flag added internally. + + [PARAMETERS] + 'name_or_data' Name of the file or URL to open. For CD playback this may be a drive letter with a colon, example "D:". + 'mode' Behaviour modifier for opening the sound. See FMOD_MODE. Also see remarks for more. + 'exinfo' Pointer to a FMOD_CREATESOUNDEXINFO which lets the user provide extended information while playing the sound. Optional. Specify 0 or NULL to ignore. + 'sound' Address of a variable to receive a newly created FMOD::Sound object. + + [RETURN_VALUE] + + [REMARKS] + Note that a stream only has 1 decode buffer and file handle, and therefore can only be played once. It cannot play multiple times at once because it cannot share a stream buffer if the stream is playing at different positions.
    + Open multiple streams to have them play concurrently.
    +
    +
  • To open a file or URL as a stream, so that it decompresses / reads at runtime, instead of loading / decompressing into memory all at the time of this call, use the FMOD_CREATESTREAM flag. This is like a 'stream' in FMOD 3.
    +
  • To open a file or URL as a compressed sound effect that is not streamed and is not decompressed into memory at load time, use FMOD_CREATECOMPRESSEDSAMPLE. This is supported with MPEG (mp2/mp3), ADPCM (wav on all platforms and vag on ps2/psp) and XMA files only. This is useful for those who want realtime compressed soundeffects, but not the overhead of disk access.
    +
  • To open a CD drive, use the drive as the name, for example on the windows platform, use "D:"
    +
  • To open a sound as 2D, so that it is not affected by 3D processing, use the FMOD_2D flag. 3D sound commands will be ignored on these types of sounds.
    +
  • To open a sound as 3D, so that it is treated as a 3D sound, use the FMOD_3D flag. Calls to Channel::setPan will be ignored on these types of sounds.
    +
  • To use FMOD software mixing buffers, use the FMOD_SOFTWARE flag. This gives certain benefits, such as DSP processing, spectrum analysis, loop points, 5.1 mix levels, 2d/3d morphing, and more.
    +
  • To use the soundcard's hardware to play the sound, use the FMOD_HARDWARE flag. This gives certain benefits such as EAX reverb, dolby digital output on some devices, and better 3d sound virtualization using headphones.
    +
    + Note that FMOD_OPENRAW, FMOD_OPENMEMORY, FMOD_OPENMEMORY_POINT and FMOD_OPENUSER will not work here without the exinfo structure present, as more information is needed.
    +
    + Use FMOD_NONBLOCKING to have the sound open or load in the background. You can use Sound::getOpenState to determine if it has finished loading / opening or not. While it is loading (not ready), sound functions are not accessable for that sound.
    +
    + To account for slow devices or computers that might cause buffer underrun (skipping/stuttering/repeating blocks of audio), use System::setStreamBufferSize. +
    + Note that FMOD_CREATESAMPLE will be ignored, overriden by this function because this is simply a wrapper to System::createSound that provides the FMOD_CREATESTREAM flag. The FMOD_CREATESTREAM flag overrides FMOD_CREATESAMPLE. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + FMOD_CREATESOUNDEXINFO + Sound::getOpenState + System::setStreamBufferSize + System::createSound + Channel::setPan +] +*/ +FMOD_RESULT F_API FMOD_System_CreateStream(FMOD_SYSTEM *system, const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, FMOD_SOUND **sound) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createStream(name_or_data, mode, exinfo, (FMOD::Sound **)sound); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates a user defined DSP unit object to be inserted into a DSP network, for the purposes of sound filtering or sound generation. + + [PARAMETERS] + 'description' Address of an FMOD_DSP_DESCRIPTION structure containing information about the unit to be created. + 'dsp' Address of a variable to receive a newly created FMOD::DSP object. + + [RETURN_VALUE] + + [REMARKS] + A DSP unit can generate or filter incoming data.
    + The data is created or filtered through use of the read callback that is defined by the user.
    + See the definition for the FMOD_DSP_DESCRIPTION structure to find out what each member means.
    + To be active, a unit must be inserted into the FMOD DSP network to be heard. Use functions such as System::addDSP, Channel::addDSP or DSP::addInput to do this.
    + For more information and a detailed description (with diagrams) see the tutorial on the DSP system in the documentation.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_DESCRIPTION + System::createDSPByType + System::createDSPByPlugin + System::addDSP + Channel::addDSP + DSP::addInput + DSP::setActive +] +*/ +FMOD_RESULT F_API FMOD_System_CreateDSP(FMOD_SYSTEM *system, FMOD_DSP_DESCRIPTION *description, FMOD_DSP **dsp) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createDSP(description, (FMOD::DSP **)dsp); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates an FMOD defined built in DSP unit object to be inserted into a DSP network, for the purposes of sound filtering or sound generation.
    + This function is used to create special effects that come built into FMOD. + + [PARAMETERS] + 'type' A pre-defined DSP effect or sound generator described by a FMOD_DSP_TYPE. + 'dsp' Address of a variable to receive a newly created FMOD::DSP object. + + [RETURN_VALUE] + + [REMARKS] + A DSP unit can generate or filter incoming data.
    + To be active, a unit must be inserted into the FMOD DSP network to be heard. Use functions such as System::addDSP, Channel::addDSP , ChannelGroup::addDSP or DSP::addInput to do this.
    + For more information and a detailed description (with diagrams) see the tutorial on the DSP system in the documentation.
    +
    + Note! Winamp DSP and VST plugins will only return the first plugin of this type that was loaded!
    + To access all VST or Winamp DSP plugins the System::createDSPByPlugin function! Use the index returned by System::loadPlugin if you don't want to enumerate them all.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_TYPE + System::createDSP + System::createDSPByPlugin + System::addDSP + System::loadPlugin + Channel::addDSP + ChannelGroup::addDSP + DSP::addInput + DSP::setActive +] +*/ +FMOD_RESULT F_API FMOD_System_CreateDSPByType(FMOD_SYSTEM *system, FMOD_DSP_TYPE type, FMOD_DSP **dsp) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createDSPByType(type, (FMOD::DSP **)dsp); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates a channel group object. These objects can be used to assign channels to for group channel settings, such as volume.
    + Channel groups are also used for sub-mixing. Any channels that are assigned to a channel group get submixed into that channel group's DSP. + + [PARAMETERS] + 'name' Label to give to the channel group for identification purposes. Optional (can be null). + 'channelgroup' Address of a variable to receive a newly created FMOD::ChannelGroup object. + + [RETURN_VALUE] + + [REMARKS] + See the channel group class definition for the types of operations that can be performed on 'groups' of channels.
    + The channel group can for example be used to have 2 seperate groups of master volume, instead of one global master volume.
    + A channel group can be used for sub-mixing, ie so that a set of channels can be mixed into a channel group, then can have effects applied to it without affecting other channels.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getMasterChannelGroup + Channel::setChannelGroup + ChannelGroup::release +] +*/ +FMOD_RESULT F_API FMOD_System_CreateChannelGroup(FMOD_SYSTEM *system, const char *name, FMOD_CHANNELGROUP **channelgroup) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createChannelGroup(name, (FMOD::ChannelGroup **)channelgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates a sound group, which can store handles to multiple Sound pointers. + + [PARAMETERS] + 'name' Name of sound group. + 'soundgroup' Address of a variable to recieve a pointer to a sound group. + + [RETURN_VALUE] + + [REMARKS] + Once a SoundGroup is created, Sound::setSoundGroup is used to put a sound in a SoundGroup. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::release + Sound::setSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_System_CreateSoundGroup(FMOD_SYSTEM *system, const char *name, FMOD_SOUNDGROUP **soundgroup) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createSoundGroup(name, (FMOD::SoundGroup **)soundgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates a 'virtual reverb' object. This object reacts to 3d location and morphs the reverb environment based on how close it is to the reverb object's center.
    + Multiple reverb objects can be created to achieve a multi-reverb environment. + + [PARAMETERS] + 'reverb' Address of a pointer to a Reverb object to receive the newly created virtual reverb object. + + [RETURN_VALUE] + + [REMARKS] + The 3D reverb object is a sphere having 3D attributes (position, minimum distance, maximum distance) and reverb properties.
    + The properties and 3D attributes of all reverb objects collectively determine, along with the listener's position, the settings of and input gains into a single 3D reverb DSP.
    + Please note that this only applies to software channels. When the listener is within the sphere of effect of one or more 3d reverbs, the listener's 3D reverb properties are + a weighted combination of such 3d reverbs. When the listener is outside all of the reverbs, the 3D reverb setting is set to the default ambient reverb setting.
    +
    + Use System::setReverbAmbientProperties to set a 'background' default reverb environment. This is a reverb that will be morphed to if the listener is not within any virtual reverb zones.
    + By default the ambient reverb is set to 'off'. +
    + Creating multiple reverb objects does not impact performance. These are 'virtual reverbs'. There will still be only 1 physical reverb DSP running that just morphs between the different virtual reverbs. +
    + System::setReverbProperties can still be used in conjunction with the 3d based virtual reverb system. This allows 2d sounds to have reverb. If this call is used at the same time virtual reverb objects are active, 2 physical reverb dsps will be used, incurring a small memory and cpu hit. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::release + System::setReverbAmbientProperties + System::getReverbAmbientProperties + System::setReverbProperties + System::getReverbProperties +] +*/ +FMOD_RESULT F_API FMOD_System_CreateReverb(FMOD_SYSTEM *system, FMOD_REVERB **reverb) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createReverb((FMOD::Reverb **)reverb); +} + + +/* +[API] +[ + [DESCRIPTION] + Plays a sound object on a particular channel. + + [PARAMETERS] + 'channelid' Use the value FMOD_CHANNEL_FREE to get FMOD to pick a free channel. Otherwise specify a channel number from 0 to the 'maxchannels' value specified in System::init minus 1. + 'sound' Pointer to the sound to play. This is opened with System::createSound. + 'paused' True or false flag to specify whether to start the channel paused or not. Starting a channel paused allows the user to alter its attributes without it being audible, and unpausing with Channel::setPaused actually starts the sound. + 'channel' Address of a channel handle pointer that receives the newly playing channel. If FMOD_CHANNEL_REUSE is used, this can contain a previously used channel handle and FMOD will re-use it to play a sound on. + + [RETURN_VALUE] + + [REMARKS] + When a sound is played, it will use the sound's default frequency, volume, pan, levels and priority.
    +
    + A sound defined as FMOD_3D will by default play at the position of the listener.
    +
    + To change channel attributes before the sound is audible, start the channel paused by setting the paused flag to true, and calling the relevant channel based functions. Following that, unpause the channel with Channel::setPaused.
    +
    + If FMOD_CHANNEL_FREE is used as the channel index, it will pick an arbitrary free channel and use channel management. (As described below).
    + If FMOD_CHANNEL_REUSE is used as the channel index, FMOD Ex will re-use the channel handle that is passed in as the 'channel' parameter. If NULL or 0 is passed in as the channel handle it will use the same logic as FMOD_CHANNEL_FREE and pick an arbitrary channel.
    +
    + Channels are reference counted. If a channel is stolen by the FMOD priority system, then the handle to the stolen voice becomes invalid, and Channel based commands will not affect the new sound playing in its place.
    + If all channels are currently full playing a sound, FMOD will steal a channel with the lowest priority sound.
    + If more channels are playing than are currently available on the soundcard/sound device or software mixer, then FMOD will 'virtualize' the channel. This type of channel is not heard, but it is updated as if it was playing. When its priority becomes high enough or another sound stops that was using a real hardware/software channel, it will start playing from where it should be. This technique saves CPU time (thousands of sounds can be played at once without actually being mixed or taking up resources), and also removes the need for the user to manage voices themselves.
    + An example of virtual channel usage is a dungeon with 100 torches burning, all with a looping crackling sound, but with a soundcard that only supports 32 hardware voices. If the 3D positions and priorities for each torch are set correctly, FMOD will play all 100 sounds without any 'out of channels' errors, and swap the real voices in and out according to which torches are closest in 3D space.
    + Priority for virtual channels can be changed in the sound's defaults, or at runtime with Channel::setPriority. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CHANNELINDEX + System::createSound + Channel::setPaused + Channel::setPriority + Sound::setDefaults + Sound::setVariations + System::init +] +*/ +FMOD_RESULT F_API FMOD_System_PlaySound(FMOD_SYSTEM *system, FMOD_CHANNELINDEX channelid, FMOD_SOUND *sound, FMOD_BOOL paused, FMOD_CHANNEL **channel) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->playSound(channelid, (FMOD::Sound *)sound, paused ? true : false, (FMOD::Channel **)channel); +} + + +/* +[API] +[ + [DESCRIPTION] + Plays a DSP unit object and its input network on a particular channel. + + [PARAMETERS] + 'channelid' Use the value FMOD_CHANNEL_FREE to get FMOD to pick a free channel. Otherwise specify a channel number from 0 to the 'maxchannels' value specified in System::init minus 1. + 'dsp' Pointer to the dsp unit to play. This is opened with System::createDSP, System::createDSPByType, System::createDSPByPlugin. + 'paused' True or false flag to specify whether to start the channel paused or not. Starting a channel paused allows the user to alter its attributes without it being audible, and unpausing with Channel::setPaused actually starts the dsp running. + 'channel' Address of a channel handle pointer that receives the newly playing channel. If FMOD_CHANNEL_REUSE is used, this can contain a previously used channel handle and FMOD will re-use it to play a dsp on. + + [RETURN_VALUE] + + [REMARKS] + When a dsp is played, it will use the dsp's default frequency, volume, pan, levels and priority.
    +
    + A dsp defined as FMOD_3D will by default play at the position of the listener.
    +
    + To change channel attributes before the dsp is audible, start the channel paused by setting the paused flag to true, and calling the relevant channel based functions. Following that, unpause the channel with Channel::setPaused.
    +
    + If FMOD_CHANNEL_FREE is used as the channel index, it will pick an arbitrary free channel and use channel management. (As described below).
    + If FMOD_CHANNEL_REUSE is used as the channel index, FMOD Ex will re-use the channel handle that is passed in as the 'channel' parameter. If NULL or 0 is passed in as the channel handle it will use the same logic as FMOD_CHANNEL_FREE and pick an arbitrary channel.
    +
    + Channels are reference counted. If a channel is stolen by the FMOD priority system, then the handle to the stolen voice becomes invalid, and Channel based commands will not affect the new channel playing in its place.
    + If all channels are currently full playing a dsp or sound, FMOD will steal a channel with the lowest priority dsp or sound.
    + If more channels are playing than are currently available on the soundcard/sound device or software mixer, then FMOD will 'virtualize' the channel. This type of channel is not heard, but it is updated as if it was playing. When its priority becomes high enough or another sound stops that was using a real hardware/software channel, it will start playing from where it should be. This technique saves CPU time (thousands of sounds can be played at once without actually being mixed or taking up resources), and also removes the need for the user to manage voices themselves.
    + An example of virtual channel usage is a dungeon with 100 torches burning, all with a looping crackling sound, but with a soundcard that only supports 32 hardware voices. If the 3D positions and priorities for each torch are set correctly, FMOD will play all 100 sounds without any 'out of channels' errors, and swap the real voices in and out according to which torches are closest in 3D space.
    + Priority for virtual channels can be changed in the sound's defaults, or at runtime with Channel::setPriority. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CHANNELINDEX + System::createDSP + System::createDSPByType + System::createDSPByPlugin + Channel::setPaused + Channel::setPriority + DSP::setDefaults + System::init +] +*/ +FMOD_RESULT F_API FMOD_System_PlayDSP(FMOD_SYSTEM *system, FMOD_CHANNELINDEX channelid, FMOD_DSP *dsp, FMOD_BOOL paused, FMOD_CHANNEL **channel) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->playDSP(channelid, (FMOD::DSP *)dsp, paused ? true : false, (FMOD::Channel **)channel); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a handle to a channel by ID. + + [PARAMETERS] + 'channelid' Index in the FMOD channel pool. Specify a channel number from 0 to the 'maxchannels' value specified in System::init minus 1. + 'channel' Address of a variable that receives a pointer to the requested channel. + + [RETURN_VALUE] + + [REMARKS] + This function is mainly for getting handles to existing (playing) channels and setting their attributes. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + System::init +] +*/ +FMOD_RESULT F_API FMOD_System_GetChannel(FMOD_SYSTEM *system, int channelid, FMOD_CHANNEL **channel) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getChannel(channelid, (FMOD::Channel **)channel); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a handle to the internal master channel group. This is the default channel group that all channels play on.
    + This channel group can be used to do things like set the master volume for all playing sounds. See the ChannelGroup API for more functionality. + + [PARAMETERS] + 'channelgroup' Address of a variable that receives a pointer to the master System object channel group. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createChannelGroup + ChannelGroup::setVolume + ChannelGroup::getVolume +] +*/ +FMOD_RESULT F_API FMOD_System_GetMasterChannelGroup(FMOD_SYSTEM *system, FMOD_CHANNELGROUP **channelgroup) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getMasterChannelGroup((FMOD::ChannelGroup **)channelgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the default sound group, where all sounds are placed when they are created. + + [PARAMETERS] + 'soundgroup' Address of a pointer to a SoundGroup object to receive the master sound group. + + [RETURN_VALUE] + + [REMARKS] + If a user based soundgroup is deleted/released, the sounds will be put back into this sound group. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::release + SoundGroup::getSystemObject + SoundGroup::setMaxAudible + SoundGroup::getMaxAudible + SoundGroup::getName + SoundGroup::getNumSounds + SoundGroup::getSound + SoundGroup::getNumPlaying + SoundGroup::setUserData + SoundGroup::getUserData +] +*/ +FMOD_RESULT F_API FMOD_System_GetMasterSoundGroup(FMOD_SYSTEM *system, FMOD_SOUNDGROUP **soundgroup) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getMasterSoundGroup((FMOD::SoundGroup **)soundgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets parameters for the global reverb environment.
    + Reverb parameters can be set manually, or automatically using the pre-defined presets given in the fmod.h header. + + [PARAMETERS] + 'prop' Address of an FMOD_REVERB_PROPERTIES structure which defines the attributes for the reverb. + + [RETURN_VALUE] + + [REMARKS] + With FMOD_HARDWARE on Windows using EAX, the reverb will only work on FMOD_3D based sounds. FMOD_SOFTWARE does not have this problem and works on FMOD_2D and FMOD_3D based sounds.
    +
    + On PlayStation 2, the reverb is limited to only a few reverb types that are not configurable. Use the FMOD_PRESET_PS2_xxx presets. +
    + On Xbox, it is possible to apply reverb to FMOD_2D and FMOD_HARDWARE based voices using this function. By default reverb is turned off for FMOD_2D hardware based voices.
    +
    + Note! It is important to specify the 'Instance' value in the FMOD_REVERB_PROPERTIES structure correctly, otherwise you will get an FMOD_ERR_REVERB_INSTANCE error. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_REVERB_PROPERTIES + System::getReverbProperties + Channel::setReverbProperties + Channel::getReverbProperties +] +*/ +FMOD_RESULT F_API FMOD_System_SetReverbProperties(FMOD_SYSTEM *system, const FMOD_REVERB_PROPERTIES *prop) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setReverbProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current reverb environment for the specified reverb instance. You must specify the 'Instance' value (usually 0 unless you are using multiple reverbs) before calling this function. + + [PARAMETERS] + 'prop' Address of a variable that receives the current reverb environment description. Make sure the 'Instance' value is specified. + + [RETURN_VALUE] + + [REMARKS] + Note! It is important to specify the 'Instance' value in the FMOD_REVERB_PROPERTIES structure correctly, otherwise you will get an FMOD_ERR_REVERB_INSTANCE error. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_REVERB_PROPERTIES + System::setReverbProperties + Channel::setReverbProperties + Channel::getReverbProperties +] +*/ +FMOD_RESULT F_API FMOD_System_GetReverbProperties(FMOD_SYSTEM *system, FMOD_REVERB_PROPERTIES *prop) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getReverbProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a 'background' default reverb environment for the virtual reverb system. This is a reverb preset that will be morphed to if the listener is not within any virtual reverb zones.
    + By default the ambient reverb is set to 'off'. + + [PARAMETERS] + 'prop' Address of a FMOD_REVERB_PROPERTIES structure containing the settings for the desired ambient reverb setting. + + [RETURN_VALUE] + + [REMARKS] + There is one reverb DSP dedicated to providing a 3D reverb effect. This DSP's properties are a weighted sum of all the contributing virtual reverbs.
    + The default 3d reverb properties specify the reverb properties in the 3D volumes which has no virtual reverbs defined. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_REVERB_PROPERTIES + System::getReverbAmbientProperties + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_System_SetReverbAmbientProperties(FMOD_SYSTEM *system, FMOD_REVERB_PROPERTIES *prop) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setReverbAmbientProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the default reverb envrionment for the virtual reverb system. + + [PARAMETERS] + 'prop' Address of a pointer to a FMOD_REVERB_PROPERTIES to receieve the settings for the current ambient reverb setting. + + [RETURN_VALUE] + + [REMARKS] + By default the ambient reverb is set to 'off'. This is the same as FMOD_REVERB_PRESET_OFF. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_REVERB_PROPERTIES + System::setReverbAmbientProperties + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_System_GetReverbAmbientProperties(FMOD_SYSTEM *system, FMOD_REVERB_PROPERTIES *prop) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getReverbAmbientProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns a pointer to the head DSP unit of the DSP network. This unit is the closest unit to the soundcard and all sound data comes through this unit. + + [PARAMETERS] + 'dsp' Address of a variable that receives the pointer to the head DSP unit. + + [RETURN_VALUE] + + [REMARKS] + Use this unit if you wish to connect custom DSP units to the output or filter the global mix by inserting filter units between this one and the incoming channel mixer unit.
    + Read the tutorial on DSP if you wish to know more about this. It is not recommended using this if you do not understand how the FMOD Ex DSP network is connected. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getDSPHead + ChannelGroup::getDSPHead +] +*/ +FMOD_RESULT F_API FMOD_System_GetDSPHead(FMOD_SYSTEM *system, FMOD_DSP **dsp) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDSPHead((FMOD::DSP **)dsp); +} + + +/* +[API] +[ + [DESCRIPTION] + This function adds a pre-created DSP unit or effect to the head of the System DSP chain. + + [PARAMETERS] + 'dsp' A pointer to a pre-created DSP unit to be inserted at the head of the System DSP chain. + 'connection' A pointer to the connection involved between the System DSP head and the specified dsp unit. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + This function is a wrapper function to insert a DSP unit at the top of the System DSP chain.
    + It disconnects the head unit from its input, then inserts the unit at the head and reconnects the previously disconnected input back as as an input to the new unit.
    +
    + Note: The connection pointer retrieved here will become invalid if you disconnect the 2 dsp units that use it.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getDSPHead + System::createDSP + System::createDSPByType + System::createDSPByPlugin + Channel::addDSP + ChannelGroup::addDSP + DSP::remove +] +*/ +FMOD_RESULT F_API FMOD_System_AddDSP(FMOD_SYSTEM *system, FMOD_DSP *dsp, FMOD_DSPCONNECTION **connection) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->addDSP((FMOD::DSP *)dsp, (FMOD::DSPConnection **)connection); +} + + +/* +[API] +[ + [DESCRIPTION] + Mutual exclusion function to lock the FMOD DSP engine (which runs asynchronously in another thread), so that it will not execute. If the FMOD DSP engine is already executing, this function will block until it has completed.
    + The function may be used to synchronize DSP network operations carried out by the user.
    + An example of using this function may be for when the user wants to construct a DSP sub-network, without the DSP engine executing in the background while the sub-network is still under construction.
    + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Once the user no longer needs the DSP engine locked, it must be unlocked with System::unlockDSP.
    + Note that the DSP engine should not be locked for a significant amount of time, otherwise inconsistency in the audio output may result. (audio skipping/stuttering).
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::unlockDSP +] +*/ +FMOD_RESULT F_API FMOD_System_LockDSP(FMOD_SYSTEM *system) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->lockDSP(); +} + + +/* +[API] +[ + [DESCRIPTION] + Mutual exclusion function to unlock the FMOD DSP engine (which runs asynchronously in another thread) and let it continue executing. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + The DSP engine must be locked with System::lockDSP before this function is called. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::lockDSP +] +*/ +FMOD_RESULT F_API FMOD_System_UnlockDSP(FMOD_SYSTEM *system) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->unlockDSP(); +} + + +/* +[API] +[ + [DESCRIPTION] + Return the current 64bit DSP clock value which counts up by the number of samples per second in the software mixer, every second. + Ie if the default sample rate is 48khz, the DSP clock increments by 48000 per second. + + [PARAMETERS] + 'hi' The most significant 32bits of the 64bit DSP clock value. + 'lo' The least significant 32bits of the 64bit DSP clock value. + + [RETURN_VALUE] + + [REMARKS] + Use result with Channel::setDelay to play a sound on an exact tick in the future.
    + Use Channel::getDelay after playing a sound to work out what DSP clock value a sound started on.
    +
    + Use FMOD_64BIT_ADD or FMOD_64BIT_SUB helper macros from fmod.h to add a hi/lo combination together and cope with wraparound.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setDelay + Channel::getDelay +] +*/ +FMOD_RESULT F_API FMOD_System_GetDSPClock(FMOD_SYSTEM *system, unsigned int *hi, unsigned int *lo) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getDSPClock(hi, lo); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of recording devices available for this output mode. Use this to enumerate all recording devices possible so that the user can select one. + + [PARAMETERS] + 'numdrivers' Address of a variable that receives the number of recording drivers available for this output mode. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] + System::GetRecordDriverInfo +] +*/ +FMOD_RESULT F_API FMOD_System_GetRecordNumDrivers(FMOD_SYSTEM *system, int *numdrivers) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getRecordNumDrivers(numdrivers); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves identification information about a sound device specified by its index, and specific to the output mode set with System::setOutput. + + [PARAMETERS] + 'id' Index into the enumerated list of record devices up to the value returned by System::getRecordNumDrivers. + 'name' Address of a variable that receives the name of the recording device. Optional. Specify 0 or NULL to ignore. + 'namelen' Length in bytes of the target buffer to receieve the string. Required if name parameter is not NULL. + 'guid' Address of a variable that receives the GUID that uniquely identifies the device. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] + System::setOutput + System::getRecordNumDrivers +] +*/ +FMOD_RESULT F_API FMOD_System_GetRecordDriverInfo(FMOD_SYSTEM *system, int id, char *name, int namelen, FMOD_GUID *guid) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getRecordDriverInfo(id, name, namelen, guid); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves identification information about a sound device specified by its index, and specific to the output mode set with System::setOutput. + + [PARAMETERS] + 'id' Index into the enumerated list of record devices up to the value returned by System::getRecordNumDrivers. + 'name' Address of a variable that receives the name of the recording device in wide chars. Optional. Specify 0 or NULL to ignore. + 'namelen' Length in bytes of the target buffer to receieve the string. Required if name parameter is not NULL. + 'guid' Address of a variable that receives the GUID that uniquely identifies the device. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] + System::setOutput + System::getRecordNumDrivers +] +*/ +FMOD_RESULT F_API FMOD_System_GetRecordDriverInfoW(FMOD_SYSTEM *system, int id, short *name, int namelen, FMOD_GUID *guid) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getRecordDriverInfoW(id, name, namelen, guid); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns information on capabilities of the current output mode for the selected recording sound device. + + [PARAMETERS] + 'id' Enumerated driver ID. This must be in a valid range delimited by System::getRecordNumDrivers. + 'caps' Address of a variable that receives the capabilities of the device. Optional. Specify 0 or NULL to ignore. + 'minfrequency' Address of a variable that receives the minimum frequency allowed for sounds used with recording. Optional. Specify 0 or NULL to ignore. + 'maxfrequency' Address of a variable that receives the maximum frequency allowed for sounds used with recording. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] + FMOD_CAPS + System::getRecordNumDrivers +] +*/ +FMOD_RESULT F_API FMOD_System_GetRecordDriverCaps(FMOD_SYSTEM *system, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getRecordDriverCaps(id, caps, minfrequency, maxfrequency); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current recording position of the record buffer in PCM samples. + + [PARAMETERS] + 'id' Enumerated driver ID. This must be in a valid range delimited by System::getRecordNumDrivers. + 'position' Address of a variable to receieve the current recording position in PCM samples. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_API FMOD_System_GetRecordPosition(FMOD_SYSTEM *system, int id, unsigned int *position) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getRecordPosition(id, position); +} + + +/* +[API] +[ + [DESCRIPTION] + Starts the recording engine recording to the specified recording sound. + + [PARAMETERS] + 'id' Enumerated driver ID. This must be in a valid range delimited by System::getRecordNumDrivers. + 'sound' User created sound for the user to record to. + 'loop' Boolean flag to tell the recording engine whether to continue recording to the provided sound from the start again, after it has reached the end. If this is set to true the data will be continually be overwritten once every loop. See remarks. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] + System::recordStop +] +*/ +FMOD_RESULT F_API FMOD_System_RecordStart(FMOD_SYSTEM *system, int id, FMOD_SOUND *sound, FMOD_BOOL loop) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->recordStart(id, (FMOD::Sound *)sound, loop ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Stops the recording engine from recording to the specified recording sound. + + [PARAMETERS] + 'id' Enumerated driver ID. This must be in a valid range delimited by System::getRecordNumDrivers. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] + System::recordStart +] +*/ +FMOD_RESULT F_API FMOD_System_RecordStop(FMOD_SYSTEM *system, int id) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->recordStop(id); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the state of the FMOD recording API, ie if it is currently recording or not. + + [PARAMETERS] + 'id' Enumerated driver ID. This must be in a valid range delimited by System::getRecordNumDrivers. + 'recording' Address of a variable to receive the current recording state. True or non zero if the FMOD recording api is currently in the middle of recording, false or zero if the recording api is stopped / not recording. + + [RETURN_VALUE] + + [REMARKS] + Recording can be started with System::recordStart. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh360, PlayStation 3, Solaris, iPhone + + [SEE_ALSO] + System::recordStart + System::recordStop +] +*/ +FMOD_RESULT F_API FMOD_System_IsRecording(FMOD_SYSTEM *system, int id, FMOD_BOOL *recording) +{ + FMOD_RESULT result; + bool recording2; + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _system->isRecording(id, &recording2); + if (result == FMOD_OK) + { + if (recording) + { + *recording = recording2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Geometry creation function. This function will create a base geometry object which can then have polygons added to it. + + [PARAMETERS] + 'maxpolygons' Maximum number of polygons within this object. + 'maxvertices' Maximum number of vertices within this object. + 'geometry' Address of a variable to receive a newly created FMOD::Geometry object. + + [RETURN_VALUE] + + [REMARKS] + Polygons can be added to a geometry object using Geometry::AddPolygon.
    +
    + A geometry object stores its list of polygons in a structure optimized for quick line intersection testing and efficient insertion and updating.
    + The structure works best with regularly shaped polygons with minimal overlap.
    + Many overlapping polygons, or clusters of long thin polygons may not be handled efficiently.
    + Axis aligned polygons are handled most efficiently.
    +
    + The same type of structure is used to optimize line intersection testing with multiple geometry objects.
    +
    + It is important to set the value of maxworldsize to an appropriate value using System::setGeometrySettings.
    + Objects or polygons outside the range of maxworldsize will not be handled efficiently.
    + Conversely, if maxworldsize is excessively large, the structure may lose precision and efficiency may drop.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setGeometrySettings + System::loadGeometry + Geometry::AddPolygon +] +*/ +FMOD_RESULT F_API FMOD_System_CreateGeometry(FMOD_SYSTEM *system, int maxpolygons, int maxvertices, FMOD_GEOMETRY **geometry) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->createGeometry(maxpolygons, maxvertices, (FMOD::Geometry **)geometry); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the maximum world size for the geometry engine for performance / precision reasons. + + [PARAMETERS] + 'maxworldsize' Maximum size of the world from the centerpoint to the edge using the same units used in other 3D functions. + + [RETURN_VALUE] + + [REMARKS] + Setting maxworldsize should be done first before creating any geometry.
    + It can be done any time afterwards but may be slow in this case.
    +
    + Objects or polygons outside the range of maxworldsize will not be handled efficiently.
    + Conversely, if maxworldsize is excessively large, the structure may loose precision and efficiency may drop.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createGeometry + System::getGeometrySettings +] +*/ +FMOD_RESULT F_API FMOD_System_SetGeometrySettings(FMOD_SYSTEM *system, float maxworldsize) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setGeometrySettings(maxworldsize); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the maximum world size for the geometry engine. + + [PARAMETERS] + 'maxworldsize' Pointer to a float to receieve the maximum world size. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setGeometrySettings +] +*/ +FMOD_RESULT F_API FMOD_System_GetGeometrySettings(FMOD_SYSTEM *system, float *maxworldsize) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getGeometrySettings(maxworldsize); +} + + +/* +[API] +[ + [DESCRIPTION] + Creates a geometry object from a block of memory which contains pre-saved geometry data, saved by Geometry::save. + + [PARAMETERS] + 'data' Address of data containing pre-saved geometry data. + 'datasize' Size of geometry data block in bytes. + 'geometry' Address of a variable to receive a newly created FMOD::Geometry object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::save + System::createGeometry +] +*/ +FMOD_RESULT F_API FMOD_System_LoadGeometry(FMOD_SYSTEM *system, const void *data, int datasize, FMOD_GEOMETRY **geometry) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->loadGeometry(data, datasize, (FMOD::Geometry **)geometry); +} + + +/* +[API] +[ + [DESCRIPTION] + Calculates geometry occlusion between a listener and a sound source. + + [PARAMETERS] + 'listener' The listener position. + 'source' The source position. + 'direct' Optional. Specify 0 to ignore. Address of a variable to receive the direct occlusion value. + 'reverb' Optional. Specify 0 to ignore. Address of a variable to receive the reverb occlusion value. + + [RETURN_VALUE] + + [REMARKS] + If single sided polygons have been created, it is important to get the source + and listener positions round the right way, as the occlusion from point A to + point B may not be the same as the occlusion from point B to point A. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createGeometry +] +*/ +FMOD_RESULT F_API FMOD_System_GetGeometryOcclusion(FMOD_SYSTEM *system, const FMOD_VECTOR *listener, const FMOD_VECTOR *source, float *direct, float *reverb) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getGeometryOcclusion(listener, source, direct, reverb); +} + + +/* +[API] +[ + [DESCRIPTION] + Set a proxy server to use for all subsequent internet connections. + + [PARAMETERS] + 'proxy' The name of a proxy server in host:port format e.g. www.fmod.org:8888 (defaults to port 80 if no port is specified). + + [RETURN_VALUE] + + [REMARKS] + Basic authentication is supported. To use it, this parameter must be in user:password@host:port format e.g. bob:sekrit123@www.fmod.org:8888 + Set this parameter to 0 / NULL if no proxy is required. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris, iPhone + + [SEE_ALSO] + System::getNetworkProxy +] +*/ +FMOD_RESULT F_API FMOD_System_SetNetworkProxy(FMOD_SYSTEM *system, const char *proxy) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setNetworkProxy(proxy); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the URL of the proxy server used in internet streaming. + + [PARAMETERS] + 'proxy' Address of a variable that receives the proxy server URL. + 'proxylen' Size of the buffer in bytes to receive the string. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris, iPhone + + [SEE_ALSO] + System::setNetworkProxy +] +*/ +FMOD_RESULT F_API FMOD_System_GetNetworkProxy(FMOD_SYSTEM *system, char *proxy, int proxylen) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getNetworkProxy(proxy, proxylen); +} + + +/* +[API] +[ + [DESCRIPTION] + Set the timeout for network streams. + + [PARAMETERS] + 'timeout' The timeout value in ms. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris, iPhone + + [SEE_ALSO] + System::getNetworkTimeout +] +*/ +FMOD_RESULT F_API FMOD_System_SetNetworkTimeout(FMOD_SYSTEM *system, int timeout) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setNetworkTimeout(timeout); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve the timeout value for network streams + + [PARAMETERS] + 'timeout' The timeout value in ms. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Solaris, iPhone + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_API FMOD_System_GetNetworkTimeout(FMOD_SYSTEM *system, int *timeout) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getNetworkTimeout(timeout); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the System object will store internally. Can be retrieved with System::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the System object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using System::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getUserData +] +*/ +FMOD_RESULT F_API FMOD_System_SetUserData(FMOD_SYSTEM *system, void *userdata) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the System::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the data specified with the System::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setUserData +] +*/ +FMOD_RESULT F_API FMOD_System_GetUserData(FMOD_SYSTEM *system, void **userdata) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + Every public FMOD class has a getMemoryInfo function which can be used to get detailed information on what memory resources are associated with the object in question. + The getMemoryInfo function can be used in two different ways : +

     

    + +

  • Use "memorybits" and "event_memorybits" to specify what memory usage you'd like to query and receive one number back in "memoryused". +
  • Provide an FMOD_MEMORY_USAGE_DETAILS structure for FMOD to fill with memory usage values for all types of memory usage. +

     

    + + + You can use the FMOD_MEMBITS_xxx and FMOD_EVENT_MEMBITS_xxx defines to get FMOD to add up the memory usage numbers you're interested in : + +

    +        FMOD::ChannelGroup *channelgroup;
    +        unsigned int        usedvalue;
    +        

    + (create channelgroup here...) +

    + // By specifying FMOD_MEMBITS_DSPI and FMOD_MEMBITS_CHANNELGROUP here we're asking that "usedvalue" + // only counts FMOD_MEMBITS_DSPI and FMOD_MEMBITS_CHANNELGROUP type memory usage. + result = channelgroup->getMemoryInfo(FMOD_MEMBITS_DSPI | FMOD_MEMBITS_CHANNELGROUP, 0, &usedvalue, 0); + if (result != FMOD_OK) + { + (handle error...) + } +

    + printf("This FMOD::ChannelGroup is currently using %d bytes for DSP units and the ChannelGroup object itself\n", usedvalue); +

    +

     

    + + + Alternatively, the FMOD_MEMORY_USAGE_DETAILS structure can be used to find out the memory usage of each type within a object : + +

    +        FMOD::ChannelGroup *channelgroup;
    +        FMOD_MEMORY_USAGE_DETAILS memused_details;
    +        unsigned int        channelgroupused;
    +        

    + (create channelgroup here...) +

    + // By specitying a "FMOD_MEMORY_USAGE_DETAILS" struct here, we're asking FMOD to fill in the structure + // with memory usage values for all types of memory associated with this object. + result = channelgroup->getMemoryInfo(0, 0, 0, &memused_details); + if (result != FMOD_OK) + { + (handle error...) + } +

    + channelgroupused = memused_details.dsp + memused_details.channelgroup; +

    + printf("This FMOD::ChannelGroup is currently using %d bytes for DSP units and the ChannelGroup object itself\n", channelgroupused); +

    + + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS +] +*/ +FMOD_RESULT F_API FMOD_System_GetMemoryInfo(FMOD_SYSTEM *system, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::System *_system = (FMOD::System *)system; + + if (!FMOD::gGlobal->gSystemHead->exists((FMOD::SystemI *)_system)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _system->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Frees a sound object. + + [PARAMETERS] + 'sound' Pointer to an FMOD::Sound object. + + [RETURN_VALUE] + + [REMARKS] + This will free the sound object and everything created under it.
    +
    + If this is a stream that is playing as a subsound of another parent stream, then if this is the currently playing subsound (be it a normal subsound playback, or as part of a sentence), the whole stream will stop. +
    + Note - This function will block if it was opened with FMOD_NONBLOCKING and hasn't finished opening yet. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSound + Sound::getSubSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_Release(FMOD_SOUND *sound) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->release(); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the parent System object that was used to create this object. + + [PARAMETERS] + 'system' Address of a pointer that receives the System object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetSystemObject(FMOD_SOUND *sound, FMOD_SYSTEM **system) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getSystemObject((FMOD::System **)system); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns a pointer to the beginning of the sample data for a sound.
    + + [PARAMETERS] + 'offset' Offset in bytes to the position you want to lock in the sample buffer. + 'length' Number of bytes you want to lock in the sample buffer. + 'ptr1' Address of a pointer that will point to the first part of the locked data. + 'ptr2' Address of a pointer that will point to the second part of the locked data. This will be null if the data locked hasn't wrapped at the end of the buffer. + 'len1' Length of data in bytes that was locked for ptr1 + 'len2' Length of data in bytes that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer. + + [RETURN_VALUE] + + [REMARKS] + You must always unlock the data again after you have finished with it, using Sound::unlock.
    + With this function you get access to the RAW audio data, for example 8, 16, 24 or 32bit PCM data, mono or stereo data, and on consoles, vag, xadpcm or gcadpcm compressed data. You must take this into consideration when processing the data within the pointer. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::unlock + System::createSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_Lock(FMOD_SOUND *sound, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->lock(offset, length, ptr1, ptr2, len1, len2); +} + + +/* +[API] +[ + [DESCRIPTION] + Releases previous sample data lock from Sound::lock. + + [PARAMETERS] + 'ptr1' Pointer to the 1st locked portion of sample data, from Sound::lock. + 'ptr2' Pointer to the 2nd locked portion of sample data, from Sound::lock. + 'len1' Length of data in bytes that was locked for ptr1 + 'len2' Length of data in bytes that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer. + + [RETURN_VALUE] + Call this function after data has been read/written to from Sound::lock. This function will do any post processing necessary and if needed, send it to sound ram. + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::lock + System::createSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_Unlock(FMOD_SOUND *sound, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->unlock(ptr1, ptr2, len1, len2); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a sounds's default attributes, so when it is played it uses these values without having to specify them later for each channel each time the sound is played. + + [PARAMETERS] + 'frequency' Default playback frequency for the sound, in hz. (ie 44100hz). + 'volume' Default volume for the sound. 0.0 to 1.0. 0.0 = Silent, 1.0 = full volume. Default = 1.0. + 'pan' Default pan for the sound. -1.0 to +1.0. -1.0 = Full left, 0.0 = center, 1.0 = full right. Default = 0.0. + 'priority' Default priority for the sound when played on a channel. 0 to 256. 0 = most important, 256 = least important. Default = 128. + + [RETURN_VALUE] + + [REMARKS] + There are no 'ignore' values for these parameters. Use Sound::getDefaults if you want to change only 1 and leave others unaltered. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getDefaults + System::playSound + System::createSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetDefaults(FMOD_SOUND *sound, float frequency, float volume, float pan, int priority) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setDefaults(frequency, volume, pan, priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a sound's default attributes for when it is played on a channel with System::playSound. + + [PARAMETERS] + 'frequency' Address of a variable that receives the default frequency for the sound. Optional. Specify 0 or NULL to ignore. + 'volume' Address of a variable that receives the default volume for the sound. Result will be from 0.0 to 1.0. 0.0 = Silent, 1.0 = full volume. Default = 1.0. Optional. Specify 0 or NULL to ignore. + 'pan' Address of a variable that receives the default pan for the sound. Result will be from -1.0 to +1.0. -1.0 = Full left, 0.0 = center, 1.0 = full right. Default = 0.0. Optional. Specify 0 or NULL to ignore. + 'priority' Address of a variable that receives the default priority for the sound when played on a channel. Result will be from 0 to 256. 0 = most important, 256 = least important. Default = 128. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setDefaults + System::createSound + System::playSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetDefaults(FMOD_SOUND *sound, float *frequency, float *volume, float *pan, int *priority) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getDefaults(frequency, volume, pan, priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Changes the playback behaviour of a sound by allowing random variations to playback parameters to be set. + + [PARAMETERS] + 'frequencyvar' Frequency variation in hz. Frequency will play at its default frequency, plus or minus a random value within this range. Default = 0.0. + 'volumevar' Volume variation. 0.0 to 1.0. Sound will play at its default volume, plus or minus a random value within this range. Default = 0.0. + 'panvar' Pan variation. 0.0 to 2.0. Sound will play at its default pan, plus or minus a random value within this range. Pan is from -1.0 to +1.0 normally so the range can be a maximum of 2.0 in this case. Default = 0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getVariations +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetVariations(FMOD_SOUND *sound, float frequencyvar, float volumevar, float panvar) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setVariations(frequencyvar, volumevar, panvar); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current playback behaviour variations of a sound. + + [PARAMETERS] + 'frequencyvar' Address of a variable to receive the frequency variation in hz. Frequency will play at its default frequency, plus or minus a random value within this range. Default = 0.0. Specify 0 or NULL to ignore. + 'volumevar' Address of a variable to receive the volume variation. 0.0 to 1.0. Sound will play at its default volume, plus or minus a random value within this range. Default = 0.0. Specify 0 or NULL to ignore. + 'panvar' Address of a variable to receive the pan variation. 0.0 to 2.0. Sound will play at its default pan, plus or minus a random value within this range. Pan is from -1.0 to +1.0 normally so the range can be a maximum of 2.0 in this case. Default = 0. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setVariations +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetVariations(FMOD_SOUND *sound, float *frequencyvar, float *volumevar, float *panvar) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getVariations(frequencyvar, volumevar, panvar); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the minimum and maximum audible distance for a sound.
    +
    + MinDistance is the minimum distance that the sound emitter will cease to continue growing louder at (as it approaches the listener).
    + Within the mindistance it stays at the constant loudest volume possible. Outside of this mindistance it begins to attenuate.
    + MaxDistance is the distance a sound stops attenuating at. Beyond this point it will stay at the volume it would be at maxdistance units from the listener and will not attenuate any more.
    + MinDistance is useful to give the impression that the sound is loud or soft in 3d space. An example of this is a small quiet object, such as a bumblebee, which you could set a mindistance of to 0.1 for example, which would cause it to attenuate quickly and dissapear when only a few meters away from the listener.
    + Another example is a jumbo jet, which you could set to a mindistance of 100.0, which would keep the sound volume at max until the listener was 100 meters away, then it would be hundreds of meters more before it would fade out.
    +
    + In summary, increase the mindistance of a sound to make it 'louder' in a 3d world, and decrease it to make it 'quieter' in a 3d world.
    + Maxdistance is effectively obsolete unless you need the sound to stop fading out at a certain point. Do not adjust this from the default if you dont need to.
    + Some people have the confusion that maxdistance is the point the sound will fade out to, this is not the case.
    + + [PARAMETERS] + 'min' The sound's minimum volume distance in "units". See remarks for more on units. + 'max' The sound's maximum volume distance in "units". See remarks for more on units. + + [RETURN_VALUE] + + [REMARKS] + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    + See System::set3DSettings for more on this.
    + The default units for minimum and maximum distances are 1.0 and 10,000.0f.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::get3DMinMaxDistance + Channel::set3DMinMaxDistance + Channel::get3DMinMaxDistance + System::set3DSettings +] +*/ +FMOD_RESULT F_API FMOD_Sound_Set3DMinMaxDistance(FMOD_SOUND *sound, float min, float max) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->set3DMinMaxDistance(min, max); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve the minimum and maximum audible distance for a sound. + + [PARAMETERS] + 'min' Pointer to value to be filled with the minimum volume distance for the sound. See remarks for more on units. Optional. Specify 0 or NULL to ignore. + 'max' Pointer to value to be filled with the maximum volume distance for the sound. See remarks for more on units. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    + See System::set3DSettings for more on this.
    + The default units for minimum and maximum distances are 1.0 and 10,000.0f.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::set3DMinMaxDistance + Channel::set3DMinMaxDistance + Channel::get3DMinMaxDistance + System::set3DSettings +] +*/ +FMOD_RESULT F_API FMOD_Sound_Get3DMinMaxDistance(FMOD_SOUND *sound, float *min, float *max) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->get3DMinMaxDistance(min, max); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the inside and outside angles of the sound projection cone, as well as the volume of the sound outside the outside angle of the sound projection cone. + + [PARAMETERS] + 'insideconeangle' Inside cone angle, in degrees, from 0 to 360. This is the angle within which the sound is at its normal volume. Must not be greater than outsideconeangle. Default = 360. + 'outsideconeangle' Outside cone angle, in degrees, from 0 to 360. This is the angle outside of which the sound is at its outside volume. Must not be less than insideconeangle. Default = 360. + 'outsidevolume' Cone outside volume, from 0 to 1.0. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::get3DConeSettings + Channel::set3DConeSettings +] +*/ +FMOD_RESULT F_API FMOD_Sound_Set3DConeSettings(FMOD_SOUND *sound, float insideconeangle, float outsideconeangle, float outsidevolume) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->set3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the inside and outside angles of the sound projection cone. + + [PARAMETERS] + 'insideconeangle' Address of a variable that receives the inside angle of the sound projection cone, in degrees. This is the angle within which the sound is at its normal volume. Optional. Specify 0 or NULL to ignore. + 'outsideconeangle' Address of a variable that receives the outside angle of the sound projection cone, in degrees. This is the angle outside of which the sound is at its outside volume. Optional. Specify 0 or NULL to ignore. + 'outsidevolume' Address of a variable that receives the cone outside volume for this sound. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::set3DConeSettings + Channel::set3DConeSettings +] +*/ +FMOD_RESULT F_API FMOD_Sound_Get3DConeSettings(FMOD_SOUND *sound, float *insideconeangle, float *outsideconeangle, float *outsidevolume) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->get3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); +} + + +/* +[API] +[ + [DESCRIPTION] + Point a sound to use a custom rolloff curve. Must be used in conjunction with FMOD_3D_CUSTOMROLLOFF flag to be activated. + + [PARAMETERS] + 'points' An array of FMOD_VECTOR structures where x = distance and y = volume from 0.0 to 1.0. z should be set to 0. + 'numpoints' The number of points in the array. + + [RETURN_VALUE] + + [REMARKS] + Note! This function does not duplicate the memory for the points internally. The pointer you pass to FMOD must remain valid until there is no more use for it.
    + Do not free the memory while in use, or use a local variable that goes out of scope while in use.
    +
    + Points must be sorted by distance! Passing an unsorted list to FMOD will result in an error.
    +
    + Set the points parameter to 0 or NULL to disable the points. If FMOD_3D_CUSTOMROLLOFF is set and the rolloff curve is 0, FMOD will revert to logarithmic curve rolloff.
    +
    + Min and maxdistance are meaningless when FMOD_3D_CUSTOMROLLOFF is used and the values are ignored.
    +
    + Here is an example of a custom array of points.
    +

    +    FMOD_VECTOR curve[3] = 
    +    {
    +    
      { 0.0f, 1.0f, 0.0f }, + { 2.0f, 0.2f, 0.0f }, + { 20.0f, 0.0f, 0.0f } + /ul>}; +
    + x represents the distance, y represents the volume. z is always 0.
    + Distances between points are linearly interpolated.
    + Note that after the highest distance specified, the volume in the last entry is used from that distance onwards.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + FMOD_VECTOR + Sound::get3DCustomRolloff + Channel::set3DCustomRolloff + Channel::get3DCustomRolloff +] +*/ +FMOD_RESULT F_API FMOD_Sound_Set3DCustomRolloff(FMOD_SOUND *sound, FMOD_VECTOR *points, int numpoints) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->set3DCustomRolloff(points, numpoints); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to the sound's current custom rolloff curve. + + [PARAMETERS] + 'points' Address of a variable to receive the pointer to the current custom rolloff point list. Optional. Specify 0 or NULL to ignore. + 'numpoints' Address of a variable to receive the number of points int he current custom rolloff point list. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_VECTOR + Sound::set3DCustomRolloff + Channel::set3DCustomRolloff + Channel::get3DCustomRolloff +] +*/ +FMOD_RESULT F_API FMOD_Sound_Get3DCustomRolloff(FMOD_SOUND *sound, FMOD_VECTOR **points, int *numpoints) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->get3DCustomRolloff(points, numpoints); +} + + +/* +[API] +[ + + [DESCRIPTION] + Assigns a sound as a 'subsound' of another sound. A sound can contain other sounds. The sound object that is issuing the command will be the 'parent' sound. + + [PARAMETERS] + 'index' Index within the sound to set the new sound to as a 'subsound'. + 'subsound' Sound object to set as a subsound within this sound. + + [RETURN_VALUE] + + [REMARKS] + If a subsound is set which is a member of a sentence (See Sound::setSubSoundSentence), the loop points of the sentence will be reset to a loopstart of 0 and a loopend of the length of the sentence minus 1. + This means any sentence must have its loop points re-set by the user if this function is called in this case. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumSubSounds + Sound::getSubSound + Sound::setSubSoundSentence +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetSubSound(FMOD_SOUND *sound, int index, FMOD_SOUND *subsound) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setSubSound(index, (FMOD::Sound *)subsound); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a handle to a Sound object that is contained within the parent sound. + + [PARAMETERS] + 'index' Index of the subsound to retrieve within this sound. + 'subsound' Address of a variable that receives the sound object specified. + + [RETURN_VALUE] + + [REMARKS] + If the sound is a stream and FMOD_NONBLOCKING was not used, then this call will perform a blocking seek/flush to the specified subsound.
    +
    + If FMOD_NONBLOCKING was used to open this sound and the sound is a stream, FMOD will do a non blocking seek/flush and set the state of the subsound to FMOD_OPENSTATE_SEEKING.
    + The sound won't be ready to be used in this case until the state of the sound becomes FMOD_OPENSTATE_READY (or FMOD_OPENSTATE_ERROR). + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumSubSounds + Sound::setSubSound + System::createSound + FMOD_MODE + FMOD_OPENSTATE +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetSubSound(FMOD_SOUND *sound, int index, FMOD_SOUND **subsound) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getSubSound(index, (FMOD::Sound **)subsound); +} + + +/* +[API] +[ + [DESCRIPTION] + For any sound that has subsounds, this function will determine the order of playback of these subsounds, and it will play / stitch together the subsounds without gaps.
    + This is a very useful feature for those users wanting to do seamless / gapless stream playback. (ie sports commentary, gapless playback media players etc). + + [PARAMETERS] + 'subsoundlist' Pointer to an array of indicies which are the subsounds to play. One subsound can be included in this list multiple times if required. + 'numsubsounds' Number of indicies inside the subsoundlist array. + + [RETURN_VALUE] + + [REMARKS] + The currently playing subsound in a sentence can be found with Channel::getPosition and the timeunit FMOD_TIMEUNIT_SENTENCE_SUBSOUND. + This is useful for displaying the currently playing track of a cd in a whole CD sentence for example. +
    + For realtime stitching purposes, it is better to know the buffered ahead of time subsound index. This can be done by adding the flag (using bitwise OR) FMOD_TIMEUNIT_BUFFERED. +
    + If FMOD_ERR_SUBSOUND_MODE is returned, then FMOD_CREATECOMPRESSEDSAMPLE has been used on a child sound, but not on the parent sound, or vice versa. Another cause of this is trying to mix a static sample with a streaming sound. Mode bits to do with this must match. +
    + A user can change subsounds that are not playing at the time, to do dynamic stitching/sentencing of sounds.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + Sound::getSubSound + Channel::getPosition + FMOD_TIMEUNIT +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetSubSoundSentence(FMOD_SOUND *sound, int *subsoundlist, int numsubsounds) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setSubSoundSentence(subsoundlist, numsubsounds); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the name of a sound. + + [PARAMETERS] + 'name' Address of a variable that receives the name of the sound. + 'namelen' Length in bytes of the target buffer to receieve the string. + + [RETURN_VALUE] + + [REMARKS] + if FMOD_LOWMEM has been specified in System::createSound, this function will return "(null)". + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSound + FMOD_MODE +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetName(FMOD_SOUND *sound, char *name, int namelen) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getName(name, namelen); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the length of the sound using the specified time unit. + + [PARAMETERS] + 'length' Address of a variable that receives the length of the sound. + 'lengthtype' Time unit retrieve into the length parameter. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + Certain timeunits do not work depending on the file format. For example FMOD_TIMEUNIT_MODORDER will not work with an mp3 file.
    + A length of 0xFFFFFFFF usually means it is of unlimited length, such as an internet radio stream or MOD/S3M/XM/IT file which may loop forever.
    +
    + Warning! Using a VBR source that does not have an associated length information in milliseconds or pcm samples (such as MP3 or MOD/S3M/XM/IT) may return inaccurate lengths specify FMOD_TIMEUNIT_MS or FMOD_TIMEUNIT_PCM.
    + If you want FMOD to retrieve an accurate length it will have to pre-scan the file first in this case. You will have to specify FMOD_ACCURATETIME when loading or opening the sound. This means there is a slight delay as FMOD scans the whole file when loading the sound to find the right length in millseconds or pcm samples, and this also creates a seek table as it does this for seeking purposes.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetLength(FMOD_SOUND *sound, unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getLength(length, lengthtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns format information about the sound. + + [PARAMETERS] + 'type' Address of a variable that receives the type of sound. Optional. Specify 0 or NULL to ignore. + 'format' Address of a variable that receives the format of the sound. Optional. Specify 0 or NULL to ignore. + 'channels' Address of a variable that receives the number of channels for the sound. Optional. Specify 0 or NULL to ignore. + 'bits' Address of a variable that receives the number of bits per sample for the sound. This corresponds to FMOD_SOUND_FORMAT but is provided as an integer format for convenience. Hardware compressed formats such as VAG, XADPCM, GCADPCM that stay compressed in memory will return 0. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_SOUND_TYPE + FMOD_SOUND_FORMAT +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetFormat(FMOD_SOUND *sound, FMOD_SOUND_TYPE *type, FMOD_SOUND_FORMAT *format, int *channels, int *bits) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getFormat(type, format, channels, bits); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of subsounds stored within a sound. + + [PARAMETERS] + 'numsubsounds' Address of a variable that receives the number of subsounds stored within this sound. + + [RETURN_VALUE] + + [REMARKS] + A format that has subsounds is usually a container format, such as FSB, DLS, MOD, S3M, XM, IT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getSubSound +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetNumSubSounds(FMOD_SOUND *sound, int *numsubsounds) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getNumSubSounds(numsubsounds); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of tags belonging to a sound. + + [PARAMETERS] + 'numtags' Address of a variable that receives the number of tags in the sound. Optional. Specify 0 or NULL to ignore. + 'numtagsupdated' Address of a variable that receives the number of tags updated since this function was last called. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + The 'numtagsupdated' parameter can be used to check if any tags have been updated since last calling this function.
    + This can be useful to update tag fields, for example from internet based streams, such as shoutcast or icecast where the name of the song might change.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getTag +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetNumTags(FMOD_SOUND *sound, int *numtags, int *numtagsupdated) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getNumTags(numtags, numtagsupdated); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a descriptive tag stored by the sound, to describe things like the song name, author etc. + + [PARAMETERS] + 'name' Optional. Name of a tag to retrieve. Used to specify a particular tag if the user requires it. To get all types of tags leave this parameter as 0 or NULL. + 'index' Index into the tag list. If the name parameter is null, then the index is the index into all tags present, from 0 up to but not including the numtags value returned by Sound::getNumTags.
    If name is not null, then index is the index from 0 up to the number of tags with the same name. For example if there were 2 tags with the name "TITLE" then you could use 0 and 1 to reference them.
    Specifying an index of -1 returns new or updated tags. This can be used to pull tags out as they are added or updated. + 'tag' Pointer to a tag structure. This will receive + + [RETURN_VALUE] + + [REMARKS] + The number of tags available can be found with Sound::getNumTags. + The way to display or retrieve tags can be done in 3 different ways.
    + All tags can be continuously retrieved by looping from 0 to the numtags value in Sound::getNumTags - 1. Updated tags will refresh automatically, and the 'updated' member of the FMOD_TAG structure will be set to true if a tag has been updated, due to something like a netstream changing the song name for example.
    + Tags could also be retrieved by specifying -1 as the index and only updating tags that are returned. If all tags are retrieved and this function is called the function will return an error of FMOD_ERR_TAGNOTFOUND.
    + Specific tags can be retrieved by specifying a name parameter. The index can be 0 based or -1 in the same fashion as described previously. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumTags + FMOD_TAG +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetTag(FMOD_SOUND *sound, const char *name, int index, FMOD_TAG *tag) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getTag(name, index, tag); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the state a sound is in after FMOD_NONBLOCKING has been used to open it, or the state of the streaming buffer. + + [PARAMETERS] + 'openstate' Address of a variable that receives the open state of a sound. Optional. Specify 0 or NULL to ignore. + 'percentbuffered' Address of a variable that receives the percentage of the file buffer filled progress of a stream. Optional. Specify 0 or NULL to ignore. + 'starving' Address of a variable that receives the starving state of a sound. If a stream has decoded more than the stream file buffer has ready for it, it will return TRUE. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + Note: The return value will be the result of the asynchronous sound create. Use this to determine what happened if a sound failed to open. + Note: Always check 'openstate' to determine the state of the sound. Do not assume that if this function returns FMOD_OK then the sound has finished loading. + + [REMARKS] + When a sound is opened with FMOD_NONBLOCKING, it is opened and prepared in the background, or asynchronously.
    + This allows the main application to execute without stalling on audio loads.
    + This function will describe the state of the asynchronous load routine i.e. whether it has succeeded, failed or is still in progress.
    +
    + If 'starving' is true, then you will most likely hear a stuttering/repeating sound as the decode buffer loops on itself and replays old data.
    + Now that this variable exists, you can detect buffer underrun and use something like Channel::setMute to keep it quiet until it is not starving any more. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OPENSTATE + FMOD_MODE + Channel::setMute +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetOpenState(FMOD_SOUND *sound, FMOD_OPENSTATE *openstate, unsigned int *percentbuffered, FMOD_BOOL *starving) +{ + FMOD_RESULT result; + bool starving2; + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _sound->getOpenState(openstate, percentbuffered, &starving2); + if (result == FMOD_OK) + { + if (starving) + { + *starving = starving2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Reads data from an opened sound to a specified pointer, using the FMOD codec created internally.
    + This can be used for decoding data offline in small pieces (or big pieces), rather than playing and capturing it, or loading the whole file at once and having to lock / unlock the data. + + [PARAMETERS] + 'buffer' Address of a buffer that receives the decoded data from the sound. + 'lenbytes' Number of bytes to read into the buffer. + 'read' Number of bytes actually read. + + [RETURN_VALUE] + + [REMARKS] + If too much data is read, it is possible FMOD_ERR_FILE_EOF will be returned, meaning it is out of data. The 'read' parameter will reflect this by returning a smaller number of bytes read than was requested.
    + As a sound already reads the whole file then closes it upon calling System::createSound (unless System::createStream or FMOD_CREATESTREAM is used), this function will not work because the file is no longer open.
    + Note that opening a stream makes it read a chunk of data and this will advance the read cursor. You need to either use FMOD_OPENONLY to stop the stream pre-buffering or call Sound::seekData to reset the read cursor.
    + If FMOD_OPENONLY flag is used when opening a sound, it will leave the file handle open, and FMOD will not read any data internally, so the read cursor will be at position 0. This will allow the user to read the data from the start.
    + As noted previously, if a sound is opened as a stream and this function is called to read some data, then you will 'miss the start' of the sound.
    + Channel::setPosition will have the same result. These function will flush the stream buffer and read in a chunk of audio internally. This is why if you want to read from an absolute position you should use Sound::seekData and not the previously mentioned functions.
    + Remember if you are calling readData and seekData on a stream it is up to you to cope with the side effects that may occur. Information functions such as Channel::getPosition may give misleading results. Calling Channel::setPosition will reset and flush the stream, leading to the time values returning to their correct position.
    +
    + NOTE! Thread safety. If you call this from another stream callback, or any other thread besides the main thread, make sure to put a criticalsection around the call, and another around Sound::release in case the sound is still being read from while releasing.
    + This function is thread safe to call from a stream callback or different thread as long as it doesnt conflict with a call to Sound::release. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::seekData + FMOD_MODE + Channel::setPosition + System::createSound + System::createStream + Sound::release +] +*/ +FMOD_RESULT F_API FMOD_Sound_ReadData(FMOD_SOUND *sound, void *buffer, unsigned int lenbytes, unsigned int *read) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->readData(buffer, lenbytes, read); +} + + +/* +[API] +[ + [DESCRIPTION] + Seeks a sound for use with data reading. This is not a function to 'seek a sound' for normal use. This is for use in conjunction with Sound::readData. + + [PARAMETERS] + 'pcm' Offset to seek to in PCM samples. + + [RETURN_VALUE] + + [REMARKS] + Note. If a stream is opened and this function is called to read some data, then it will advance the internal file pointer, so data will be skipped if you play the stream. Also calling position / time information functions will lead to misleading results.
    + A stream can be reset before playing by setting the position of the channel (ie using Channel::setPosition), which will make it seek, reset and flush the stream buffer. This will make it sound correct again.
    + Remember if you are calling readData and seekData on a stream it is up to you to cope with the side effects that may occur. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::readData + Channel::setPosition +] +*/ +FMOD_RESULT F_API FMOD_Sound_SeekData(FMOD_SOUND *sound, unsigned int pcm) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->seekData(pcm); +} + + +/* +[API] +[ + [DESCRIPTION] + Moves the sound from its existing SoundGroup to the specified sound group. + + [PARAMETERS] + 'soundgroup' Address of a SoundGroup object to move the sound to. + + [RETURN_VALUE] + + [REMARKS] + By default a sound is located in the 'master sound group'. This can be retrieved with System::getMasterSoundGroup.
    + Putting a sound in a sound group (or just using the master sound group) allows for functionality like limiting a group of sounds to a certain number of playbacks (see SoundGroup::setMaxAudible). + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getSoundGroup + System::getMasterSoundGroup + System::createSoundGroup + SoundGroup::setMaxAudible +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetSoundGroup(FMOD_SOUND *sound, FMOD_SOUNDGROUP *soundgroup) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setSoundGroup((FMOD::SoundGroup *)soundgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the sound's current soundgroup. + + [PARAMETERS] + 'soundgroup' Address of a pointer to a SoundGroup to receive the sound's current soundgroup. + + [RETURN_VALUE] + By default a sound is located in the 'master sound group'. This can be retrieved with System::getMasterSoundGroup.
    + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetSoundGroup(FMOD_SOUND *sound, FMOD_SOUNDGROUP **soundgroup) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getSoundGroup((FMOD::SoundGroup **)soundgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of sync points stored within a sound. These points can be user generated or can come from a wav file with embedded markers. + + [PARAMETERS] + 'numsyncpoints' Address of a variable to receive the number of sync points within this sound. + + [RETURN_VALUE] + + [REMARKS] + In sound forge, a marker can be added a wave file by clicking on the timeline / ruler, and right clicking then selecting 'Insert Marker/Region'.
    + Riff wrapped mp3 files are also supported. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getSyncPoint + Sound::getSyncPointInfo + Sound::addSyncPoint + Sound::deleteSyncPoint +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetNumSyncPoints(FMOD_SOUND *sound, int *numsyncpoints) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getNumSyncPoints(numsyncpoints); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve a handle to a sync point. These points can be user generated or can come from a wav file with embedded markers. + + [PARAMETERS] + 'index' Index of the sync point to retrieve. Use Sound::getNumSyncPoints to determine the number of syncpoints. + 'point' Address of a variable to receive a pointer to a sync point. + + [RETURN_VALUE] + + [REMARKS] + In sound forge, a marker can be added a wave file by clicking on the timeline / ruler, and right clicking then selecting 'Insert Marker/Region'.
    + Riff wrapped mp3 files are also supported. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumSyncPoints + Sound::getSyncPointInfo + Sound::addSyncPoint + Sound::deleteSyncPoint +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetSyncPoint(FMOD_SOUND *sound, int index, FMOD_SYNCPOINT **point) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getSyncPoint(index, point); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves information on an embedded sync point. These points can be user generated or can come from a wav file with embedded markers. + + [PARAMETERS] + 'point' Pointer to a sync point. Use Sound::getSyncPoint to retrieve a syncpoint or Sound::addSyncPoint to create one. + 'name' Address of a variable to receive the name of the syncpoint. Optional. Specify 0 or NULL to ignore. + 'namelen' Size of buffer in bytes for name parameter. FMOD will only copy to this point if the string is bigger than the buffer passed in. Specify 0 to ignore name parameter. + 'offset' Address of a variable to receieve the offset of the syncpoint in a format determined by the offsettype parameter. Optional. Specify 0 or NULL to ignore. + 'offsettype' A timeunit parameter to determine a desired format for the offset parameter. For example the offset can be specified as pcm samples, or milliseconds. + + [RETURN_VALUE] + + [REMARKS] + In sound forge, a marker can be added a wave file by clicking on the timeline / ruler, and right clicking then selecting 'Insert Marker/Region'.
    + Riff wrapped mp3 files are also supported. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumSyncPoints + Sound::getSyncPoint + Sound::addSyncPoint + Sound::deleteSyncPoint +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetSyncPointInfo(FMOD_SOUND *sound, FMOD_SYNCPOINT *point, char *name, int namelen, unsigned int *offset, FMOD_TIMEUNIT offsettype) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getSyncPointInfo(point, name, namelen, offset, offsettype); +} + + +/* +[API] +[ + [DESCRIPTION] + Adds a sync point at a specific time within the sound. These points can be user generated or can come from a wav file with embedded markers. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + In sound forge, a marker can be added a wave file by clicking on the timeline / ruler, and right clicking then selecting 'Insert Marker/Region'.
    + Riff wrapped mp3 files are also supported. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumSyncPoints + Sound::getSyncPoint + Sound::getSyncPointInfo + Sound::deleteSyncPoint +] +*/ +FMOD_RESULT F_API FMOD_Sound_AddSyncPoint(FMOD_SOUND *sound, unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **point) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->addSyncPoint(offset, offsettype, name, point); +} + + +/* +[API] +[ + [DESCRIPTION] + Deletes a syncpoint within the sound. These points can be user generated or can come from a wav file with embedded markers. + + [PARAMETERS] + 'point' Address of an FMOD_SYNCPOINT object. + + [RETURN_VALUE] + + [REMARKS] + In sound forge, a marker can be added a wave file by clicking on the timeline / ruler, and right clicking then selecting 'Insert Marker/Region'.
    + Riff wrapped mp3 files are also supported. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getNumSyncPoints + Sound::getSyncPoint + Sound::getSyncPointInfo + Sound::addSyncPoint +] +*/ +FMOD_RESULT F_API FMOD_Sound_DeleteSyncPoint(FMOD_SOUND *sound, FMOD_SYNCPOINT *point) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->deleteSyncPoint(point); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets or alters the mode of a sound. + + [PARAMETERS] + 'mode' Mode bits to set. + + [RETURN_VALUE] + + [REMARKS] + When calling this function, note that it will only take effect when the sound is played again with System::playSound. Consider this mode the 'default mode' for when the sound plays, not a mode that will suddenly change all currently playing instances of this sound. +
    + Flags supported:
    + FMOD_LOOP_OFF
    + FMOD_LOOP_NORMAL
    + FMOD_LOOP_BIDI (only works with sounds created with FMOD_SOFTWARE. Otherwise it will behave as FMOD_LOOP_NORMAL)
    + FMOD_3D_HEADRELATIVE
    + FMOD_3D_WORLDRELATIVE
    + FMOD_2D (see notes for win32 hardware voices)
    + FMOD_3D (see notes for win32 hardware voices)
    + FMOD_3D_LOGROLLOFF
    + FMOD_3D_LINEARROLLOFF
    + FMOD_3D_CUSTOMROLLOFF
    + FMOD_3D_IGNOREGEOMETRY
    + FMOD_DONTRESTOREVIRTUAL
    +
    + Issues with streamed audio. (Sounds created with with System::createStream or FMOD_CREATESTREAM). + When changing the loop mode, sounds created with System::createStream or FMOD_CREATESTREAM may already have been pre-buffered and executed their loop logic ahead of time, before this call was even made.
    + This is dependant on the size of the sound versus the size of the stream decode buffer. See FMOD_CREATESOUNDEXINFO.
    + If this happens, you may need to reflush the stream buffer. To do this, you can call Channel::setPosition which forces a reflush of the stream buffer.
    + Note this will usually only happen if you have sounds or looppoints that are smaller than the stream decode buffer size. Otherwise you will not normally encounter any problems.
    +
    + Win32 FMOD_HARDWARE note. Under DirectSound, you cannot change the mode of a sound between FMOD_2D and FMOD_3D. If this is a problem create the sound as FMOD_3D initially, and use FMOD_3D_HEADRELATIVE and FMOD_3D_WORLDRELATIVE. Alternatively just use FMOD_SOFTWARE.
    +
    + If FMOD_3D_IGNOREGEOMETRY is not specified, the flag will be cleared if it was specified previously.
    + + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + Sound::getMode + System::setStreamBufferSize + System::playSound + System::createStream + Channel::setPosition + FMOD_CREATESOUNDEXINFO +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetMode(FMOD_SOUND *sound, FMOD_MODE mode) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setMode(mode); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the mode bits set by the codec and the user when opening the sound. + + [PARAMETERS] + 'mode' Address of a variable that receives the current mode for this sound. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setMode + System::createSound + Channel::setMode + Channel::getMode +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetMode(FMOD_SOUND *sound, FMOD_MODE *mode) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getMode(mode); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a sound, by default, to loop a specified number of times before stopping if its mode is set to FMOD_LOOP_NORMAL or FMOD_LOOP_BIDI. + + [PARAMETERS] + 'loopcount' Number of times to loop before stopping. 0 = oneshot. 1 = loop once then stop. -1 = loop forever. Default = -1 + + [RETURN_VALUE] + + [REMARKS] + This function does not affect FMOD_HARDWARE based sounds that are not streamable.
    + FMOD_SOFTWARE based sounds or any type of sound created with System::CreateStream or FMOD_CREATESTREAM will support this function.
    +
    + Issues with streamed audio. (Sounds created with with System::createStream or FMOD_CREATESTREAM). + When changing the loop count, sounds created with System::createStream or FMOD_CREATESTREAM may already have been pre-buffered and executed their loop logic ahead of time, before this call was even made.
    + This is dependant on the size of the sound versus the size of the stream decode buffer. See FMOD_CREATESOUNDEXINFO.
    + If this happens, you may need to reflush the stream buffer. To do this, you can call Channel::setPosition which forces a reflush of the stream buffer.
    + Note this will usually only happen if you have sounds or looppoints that are smaller than the stream decode buffer size. Otherwise you will not normally encounter any problems.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getLoopCount + System::setStreamBufferSize + FMOD_CREATESOUNDEXINFO +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetLoopCount(FMOD_SOUND *sound, int loopcount) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setLoopCount(loopcount); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current loop count value for the specified sound. + + [PARAMETERS] + 'loopcount' Address of a variable that receives the number of times a sound will loop by default before stopping. 0 = oneshot. 1 = loop once then stop. -1 = loop forever. Default = -1 + + [RETURN_VALUE] + + [REMARKS] + Unlike the channel loop count function, this function simply returns the value set with Sound::setLoopCount. It does not decrement as it plays (especially seeing as one sound can be played multiple times). + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setLoopCount +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetLoopCount(FMOD_SOUND *sound, int *loopcount) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getLoopCount(loopcount); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the loop points within a sound. + + [PARAMETERS] + 'loopstart' The loop start point. This point in time is played, so it is inclusive. + 'loopstarttype' The time format used for the loop start point. See FMOD_TIMEUNIT. + 'loopend' The loop end point. This point in time is played, so it is inclusive. + 'loopendtype' The time format used for the loop end point. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + Not supported by static sounds created with FMOD_HARDWARE.
    + Supported by sounds created with FMOD_SOFTWARE, or sounds of any type (hardware or software) created with System::createStream or FMOD_CREATESTREAM. +
    + If a sound was 44100 samples long and you wanted to loop the whole sound, loopstart would be 0, and loopend would be 44099,
    not 44100. You wouldn't use milliseconds in this case because they are not sample accurate.
    + If loop end is smaller or equal to loop start, it will result in an error.
    + If loop start or loop end is larger than the length of the sound, it will result in an error.
    +
    + Issues with streamed audio. (Sounds created with with System::createStream or FMOD_CREATESTREAM).
    + When changing the loop points, sounds created with System::createStream or FMOD_CREATESTREAM may already have been pre-buffered and executed their loop logic ahead of time, before this call was even made.
    + This is dependant on the size of the sound versus the size of the stream decode buffer. See FMOD_CREATESOUNDEXINFO.
    + If this happens, you may need to reflush the stream buffer. To do this, you can call Channel::setPosition which forces a reflush of the stream buffer.
    + Note this will usually only happen if you have sounds or looppoints that are smaller than the stream decode buffer size. Otherwise you will not normally encounter any problems.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + FMOD_MODE + Sound::getLoopPoints + Sound::setLoopCount + System::createStream + System::setStreamBufferSize + Channel::setPosition + FMOD_CREATESOUNDEXINFO +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetLoopPoints(FMOD_SOUND *sound, unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setLoopPoints(loopstart, loopstarttype, loopend, loopendtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the loop points for a sound. + + [PARAMETERS] + 'loopstart' Address of a variable to receive the loop start point. This point in time is played, so it is inclusive. Optional. Specify 0 or NULL to ignore. + 'loopstarttype' The time format used for the returned loop start point. See FMOD_TIMEUNIT. + 'loopend' Address of a variable to receive the loop end point. This point in time is played, so it is inclusive. Optional. Specify 0 or NULL to ignore. + 'loopendtype' The time format used for the returned loop end point. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + Sound::setLoopPoints +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetLoopPoints(FMOD_SOUND *sound, unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getLoopPoints(loopstart, loopstarttype, loopend, loopendtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Gets the number of music channels inside a MOD/S3M/XM/IT/MIDI file. + + [PARAMETERS] + 'numchannels' Number of music channels used in the song. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setMusicChannelVolume + Sound::getMusicChannelVolume +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetMusicNumChannels(FMOD_SOUND *sound, int *numchannels) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getMusicNumChannels(numchannels); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the volume of a MOD/S3M/XM/IT/MIDI music channel volume. + + [PARAMETERS] + 'channel' MOD/S3M/XM/IT/MIDI music subchannel to set a linear volume for. + 'volume' Volume of the channel from 0.0 to 1.0. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + Use Sound::getMusicNumChannels to get the maximum number of music channels in the song. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getMusicNumChannels + Sound::getMusicChannelVolume +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetMusicChannelVolume(FMOD_SOUND *sound, int channel, float volume) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setMusicChannelVolume(channel, volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the volume of a MOD/S3M/XM/IT/MIDI music channel volume. + + [PARAMETERS] + 'channel' MOD/S3M/XM/IT/MIDI music subchannel to retrieve the volume for. + 'volume' Address of a variable to receive the volume of the channel from 0.0 to 1.0. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + Use Sound::getMusicNumChannels to get the maximum number of music channels in the song. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getMusicNumChannels + Sound::setMusicChannelVolume +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetMusicChannelVolume(FMOD_SOUND *sound, int channel, float *volume) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getMusicChannelVolume(channel, volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the Sound object will store internally. Can be retrieved with Sound::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the Sound object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using Sound::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getUserData +] +*/ +FMOD_RESULT F_API FMOD_Sound_SetUserData(FMOD_SOUND *sound, void *userdata) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the Sound::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the data specified with the Sound::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::setUserData +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetUserData(FMOD_SOUND *sound, void **userdata) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_Sound_GetMemoryInfo(FMOD_SOUND *sound, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::Sound *_sound = (FMOD::Sound *)sound; + + if (!_sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _sound->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the parent System object that was used to create this object. + + [PARAMETERS] + 'system' Address of a variable that receives the System object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetSystemObject(FMOD_CHANNEL *channel, FMOD_SYSTEM **system) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getSystemObject((FMOD::System **)system); +} + + +/* +[API] +[ + [DESCRIPTION] + Stops the channel from playing. Makes it available for re-use by the priority system. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound +] +*/ +FMOD_RESULT F_API FMOD_Channel_Stop(FMOD_CHANNEL *channel) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->stop(); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the paused state of the channel. + + [PARAMETERS] + 'paused' Paused state to set. true = channel is paused. false = channel is unpaused. + + [RETURN_VALUE] + + [REMARKS] + If a channel belongs to a paused channelgroup, it will stay paused regardless of the channel pause state. The channel pause state will still be reflected internally though, ie Channel::getPaused will still return the value you set. If the channelgroup has paused set to false, this function will become effective again. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getPaused + ChannelGroup::setPaused +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetPaused(FMOD_CHANNEL *channel, FMOD_BOOL paused) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setPaused(paused ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the paused state of the channel. + + [PARAMETERS] + 'paused' Address of a variable that receives the current paused state. true = the sound is paused. false = the sound is not paused. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setPaused +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetPaused(FMOD_CHANNEL *channel, FMOD_BOOL *paused) +{ + FMOD_RESULT result; + bool paused2; + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _channel->getPaused(&paused2); + if (paused) + { + *paused = paused2 ? 1 : 0; + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the volume for the channel linearly. + + [PARAMETERS] + 'volume' A linear volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + When a sound is played, it plays at the default volume of the sound which can be set by Sound::setDefaults.
    + For most file formats, the volume is determined by the audio format. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getVolume + ChannelGroup::setVolume + Sound::setDefaults +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetVolume(FMOD_CHANNEL *channel, float volume) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the volume level for the channel. + + [PARAMETERS] + 'volume' Address of a variable to receive the channel volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setVolume +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetVolume(FMOD_CHANNEL *channel, float *volume) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the channel's frequency or playback rate, in HZ. + + [PARAMETERS] + 'frequency' A frequency value in HZ. This value can also be negative to play the sound backwards (negative frequencies allowed with FMOD_SOFTWARE based non-stream sounds only). DirectSound hardware voices have limited frequency range on some soundcards. Please see remarks for more on this. + + [RETURN_VALUE] + + [REMARKS] + When a sound is played, it plays at the default frequency of the sound which can be set by Sound::setDefaults.
    + For most file formats, the volume is determined by the audio format.
    +
    + Frequency limitations for sounds created with FMOD_HARDWARE in DirectSound.
    + Every hardware device has a minimum and maximum frequency. This means setting the frequency above the maximum and below the minimum will have no effect.
    + FMOD clamps frequencies to these values when playing back on hardware, so if you are setting the frequency outside of this range, the frequency will stay at either the minimum or maximum.
    + Note that FMOD_SOFTWARE based sounds do not have this limitation.
    + To find out the minimum and maximum value before initializing FMOD (maybe to decide whether to use a different soundcard, output mode, or drop back fully to software mixing), you can use the System::getDriverCaps function. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getFrequency + System::getDriverCaps + Sound::setDefaults +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetFrequency(FMOD_CHANNEL *channel, float frequency) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setFrequency(frequency); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the frequency in HZ of the channel. + + [PARAMETERS] + 'frequency' Address of a variable that receives the current frequency of the channel in HZ. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setFrequency +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetFrequency(FMOD_CHANNEL *channel, float *frequency) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getFrequency(frequency); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a channels pan position linearly. + + [PARAMETERS] + 'pan' A left/right pan level, from -1.0 to 1.0 inclusive. -1.0 = Full left, 0.0 = center, 1.0 = full right. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + This function only works on sounds created with FMOD_2D. 3D sounds are not pannable and will return FMOD_ERR_NEEDS2D.
    +
    + Only sounds that are mono or stereo can be panned. Multichannel sounds (ie >2 channels) cannot be panned.
    + Mono sounds are panned from left to right using constant power panning (non linear fade). This means when pan = 0.0, the balance for the sound in each speaker is 71% left and 71% right, not 50% left and 50% right. This gives (audibly) smoother pans.
    + Stereo sounds heave each left/right value faded up and down according to the specified pan position. This means when pan = 0.0, the balance for the sound in each speaker is 100% left and 100% right. When pan = -1.0, only the left channel of the stereo sound is audible, when pan = 1.0, only the right channel of the stereo sound is audible.
    +
    + Panning does not work if the speaker mode is FMOD_SPEAKERMODE_RAW.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getPan + FMOD_SPEAKERMODE +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetPan(FMOD_CHANNEL *channel, float pan) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setPan(pan); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the pan position of the channel. + + [PARAMETERS] + 'pan' Address of a variable to receive the left/right pan level for the channel, from -1.0 to 1.0 inclusive. -1.0 = Full left, 1.0 = full right. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setPan +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetPan(FMOD_CHANNEL *channel, float *pan) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getPan(pan); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets an end delay for a sound (so that dsp can continue to process the finished sound), set the start of the sound according to the global DSP clock value which represents the time in the mixer timeline. + + [PARAMETERS] + 'delaytype' See FMOD_DELAYTYPE. This determines what delayhi and delaylo will represent. + 'delayhi' Top (most significant) 32 bits of a 64bit number representing the time. + 'delaylo' Bottom (least significant) 32 bits of a 64bit number representing the time. + + [RETURN_VALUE] + + [REMARKS] + Note! Only works with sounds created with FMOD_SOFTWARE.
    +
    + Using FMOD_DELAYTYPE_END_MS : Setting a delay after a sound ends is sometimes useful to prolong the sound, even though it has stopped, so that DSP effects can trail out, or render the last of their tails. (for example an echo or reverb effect). Remember the delayhi parameter is the only parameter used here, and it is representing millseconds.
    + Using FMOD_DELAYTYPE_DSPCLOCK_START : Note! Works with sounds created with FMOD_SOFTWARE only. This allows a sound to be played in the future on an exact sample accurate boundary or DSP clock value. This can be used for synchronizing sounds to start at an exact time in the overall timeline.
    +
    + What is the 'DSP clock'? The DSP clock represents the output stream to the soundcard, and is incremented by the output rate every second (though of course with much finer granularity than this). So if your output rate is 48khz, the DSP clock will increment by 48000 per second.
    + The hi and lo values represent this 64bit number, with the delaylo representing the least significant 32bits and the delayhi value representing the most significant 32bits.
    +
    + Use FMOD_64BIT_ADD or FMOD_64BIT_SUB to add a hi/lo combination together and cope with wraparound. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DELAYTYPE + Channel::getDelay + Channel::isPlaying +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetDelay(FMOD_CHANNEL *channel, FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setDelay(delaytype, delayhi, delaylo); +} + + +/* +[API] +[ + [DESCRIPTION] + Gets the currently set delay values. + + [PARAMETERS] + 'delaytype' See FMOD_DELAYTYPE. This determines what delayhi and delaylo will represent. + 'delayhi' Address of a variable to receive the top (most significant) 32 bits of a 64bit number representing the time. + 'delaylo' Address of a variable to receive the bottom (least significant) 32 bits of a 64bit number representing the time. + + [RETURN_VALUE] + + [REMARKS] + Note! Only works with sounds created with FMOD_SOFTWARE.
    +
    + If FMOD_DELAYTYPE_DSPCLOCK_START is used, this will be the value of the DSP clock time at the time System::playSound was called, if the user has not called Channel::setDelay.. +
    + What is the 'dsp clock'? The DSP clock represents the output stream to the soundcard, and is incremented by the output rate every second (though of course with much finer granularity than this). So if your output rate is 48khz, the DSP clock will increment by 48000 per second.
    + The hi and lo values represent this 64bit number, with the delaylo representing the least significant 32bits and the delayhi value representing the most significant 32bits.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DELAYTYPE + Channel::setDelay + System::playSound +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetDelay(FMOD_CHANNEL *channel, FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getDelay(delaytype, delayhi, delaylo); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the channel's speaker volume levels for each speaker individually. + + [PARAMETERS] + 'frontleft' Volume level for this channel in the front left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + 'frontright' Volume level for this channel in the front right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, up to 5.0 = 5x amplification. + 'center' Volume level for this channel in the center speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + 'lfe' Volume level for this channel in the subwoofer speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + 'backleft' Volume level for this channel in the back left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + 'backright' Volume level for this channel in the back right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + 'sideleft' Volume level for this channel in the side left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + 'sideright' Volume level for this channel in the side right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = normal volume, 5.0 = 5x amplification. + + + [RETURN_VALUE] + + [REMARKS] + This function only fully works on sounds created with FMOD_2D and FMOD_SOFTWARE. FMOD_3D based sounds only allow setting of LFE channel, as all other speaker levels are calculated by FMOD's 3D engine.
    +
    + Speakers specified that don't exist will simply be ignored.
    +
    + For more advanced speaker control, including sending the different channels of a stereo sound to arbitrary speakers, see Channel::setSpeakerLevels.
    +
    + This function allows amplification! You can go up to 5 times the volume of a normal sound, but warning this may cause clipping/distortion! Useful for LFE boosting. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getSpeakerMix + Channel::setSpeakerLevels + FMOD_SPEAKERMODE +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetSpeakerMix(FMOD_CHANNEL *channel, float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the channel's speaker volume levels for each speaker individually. + + [PARAMETERS] + 'frontleft' Address of a variable to receive the current volume level for this channel in the front left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'frontright' Address of a variable to receive the current volume level for this channel in the front right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'center' Address of a variable to receive the current volume level for this channel in the center speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'lfe' Address of a variable to receive the current volume level for this channel in the subwoofer speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'backleft' Address of a variable to receive the current volume level for this channel in the back left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'backright' Address of a variable to receive the current volume level for this channel in the back right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'sideleft' Address of a variable to receive the current volume level for this channel in the side left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + 'sideright' Address of a variable to receive the current volume level for this channel in the side right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume, up to 5.0 = 5x amplification. + + [RETURN_VALUE] + + [REMARKS] + For 3D sound, the values set here are not representative of the 3d mix. For 3D sound this function is mainly for retrieving the LFE value if it was set by the user. + This function is not affected by Channel::setSpeakerLevels. This function only returns the levels set by Channel::setSpeakerMix. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setSpeakerMix + Channel::setSpeakerLevels +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetSpeakerMix(FMOD_CHANNEL *channel, float *frontleft, float *frontright, float *center, float *lfe, float *backleft, float *backright, float *sideleft, float *sideright) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the incoming sound levels for a particular speaker. This is a literal setting of channel's internal pan matrix and does NOT perform downmixing depending on speaker mode. + + [PARAMETERS] + 'speaker' The target speaker to modify the levels for. This can be cast to an integer if you are using FMOD_SPEAKERMODE_RAW and want to access up to 15 speakers (output channels). + 'levels' An array of floating point numbers from 0.0 to 1.0 representing the volume of each input channel of a sound. See remarks for more. + 'numlevels' The number of floats within the levels parameter being passed to this function. In the case of the above mono or stereo sound, 1 or 2 could be used respectively. If the sound being played was an 8 channel multichannel sound then 8 levels would be used. + + [RETURN_VALUE] + + [REMARKS] + As an example of usage of this function, if the sound played on this speaker was mono, only 1 level would be needed.
    + If the sound played on this channel was stereo, then an array of 2 floats could be specified. For example { 0, 1 } on a channel playing a stereo sound would mute the left part of the stereo sound when it is played on this speaker.
    +
    + Note! This function requires the sound be created with FMOD_SOFTWARE in FMOD_OUTPUTTYPE_DSOUND. Directsound hardware voices are not capable of this functionality.
    +
    + Only speakers that are usable with the current speaker mode will be accepted. Anything else will return FMOD_ERR_INVALID_SPEAKER.
    +
    + When using FMOD_SPEAKERMODE_MONO it is preferable to use the alias FMOD_SPEAKER_MONO (or index 0), as FMOD_SPEAKER_FRONT_LEFT doesn't really make sense in that setting.
    + When using FMOD_SPEAKERMODE_RAW, the 'speaker' parameter can be cast to an integer and used as a raw speaker index, disregarding FMOD's speaker mappings.
    +
    + Warning. This function will allocate memory for the speaker level matrix and attach it to the channel. If you prefer not to have a dynamic memory allocation done at this point use Channel::setSpeakerMix instead. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getSpeakerLevels + Channel::setSpeakerMix + FMOD_SPEAKERMODE + FMOD_SPEAKER + FMOD_OUTPUTTYPE +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetSpeakerLevels(FMOD_CHANNEL *channel, FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setSpeakerLevels(speaker, levels, numlevels); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current pan matrix level settings from Channel::setSpeakerLevels. + + [PARAMETERS] + 'speaker' The speaker id to get the levels for. This can be cast to an integer if you are using a device with more than the pre-defined speaker range. + 'levels' Address of a variable that receives the current levels for the channel. This is an array of floating point values. The destination array size can be specified with the numlevels parameter. + 'numlevels' Number of floats in the destination array. + + [RETURN_VALUE] + + [REMARKS] + This function does not return level values reflecting Channel::setVolume. Volume is a separate scalar to the pan matrix levels. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setSpeakerLevels + Channel::setPan + Channel::setVolume +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetSpeakerLevels(FMOD_CHANNEL *channel, FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getSpeakerLevels(speaker, levels, numlevels); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the incoming levels in a sound. This means if you have a multichannel sound you can turn channels on and off.
    + A mono sound has 1 input channel, a stereo has 2, etc. It depends on what type of sound is playing on the channel at the time. + + [PARAMETERS] + 'levels' Array of float volume levels, from 0.0 to 1.0. These represent the incoming channels for the sound playing on the channel at the time. + 'numlevels' Number of floats in the array. Maximum = the maximum number of input channels specified in System::setSoftwareFormat. + + [RETURN_VALUE] + + [REMARKS] + Note! This function requires the sound be created with FMOD_SOFTWARE in FMOD_OUTPUTTYPE_DSOUND. DirectSound hardware voices are not capable of this functionality.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getInputChannelMix + Channel::setPan + Channel::setSpeakerMix + Channel::setSpeakerLevels + System::setSoftwareFormat +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetInputChannelMix(FMOD_CHANNEL *channel, float *levels, int numlevels) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setInputChannelMix(levels, numlevels); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the incoming levels for a channel in a sound.
    + A mono sound has 1 input channel, a stereo has 2, etc. It depends on what type of sound is playing on the channel at the time. + + [PARAMETERS] + 'levels' Address of an array of float volume levels, from 0.0 to 1.0. These represent the incoming channels for the sound playing on the channel at the time. + 'numlevels' Number of floats to receive into the array. Maximum = the maximum number of input channels specified in System::setSoftwareFormat. + + [RETURN_VALUE] + + [REMARKS] + This does not affect which speakers the sound is routed to. This can be used in conjunction with functions like Channel::setPan, Channel::setSpeakerMix, Channel::setSpeakerLevels.
    + This function only scales the input channels from the sound. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setInputChannelMix + Channel::setPan + Channel::setSpeakerMix + Channel::setSpeakerLevels + System::setSoftwareFormat +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetInputChannelMix(FMOD_CHANNEL *channel, float *levels, int numlevels) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getInputChannelMix(levels, numlevels); +} + + +/* +[API] +[ + [DESCRIPTION] + Mutes / un-mutes a channel, effectively silencing it or returning it to its normal volume. + + [PARAMETERS] + 'mute' true = channel becomes muted (silent), false = channel returns to normal volume. + + [RETURN_VALUE] + + [REMARKS] + If a channel belongs to a muted channelgroup, it will stay muted regardless of the channel mute state. The channel mute state will still be reflected internally though, ie Channel::getMute will still return the value you set. If the channelgroup has mute set to false, this function will become effective again. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getMute + ChannelGroup::setMute +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetMute(FMOD_CHANNEL *channel, FMOD_BOOL mute) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setMute(mute ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the current mute status of the channel. + + [PARAMETERS] + 'mute' true = channel is muted (silent), false = channel is at normal volume. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setMute +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetMute(FMOD_CHANNEL *channel, FMOD_BOOL *mute) +{ + FMOD_RESULT result; + bool mute2; + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _channel->getMute(&mute2); + if (mute) + { + *mute = mute2 ? 1 : 0; + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the priority for a channel after it has been played. A sound with a higher priority than another sound will not be stolen or made virtual by that sound. + + [PARAMETERS] + 'priority' priority for the channel. 0 to 256 inclusive. 0 = most important. 256 = least important. Default = 128. + + [RETURN_VALUE] + + [REMARKS] + Priority will make a channel more important or less important than its counterparts. When virtual channels are in place, by default the importance of the sound (whether it is audible or not when more channels are playing than exist) is based on the volume, or audiblity of the sound. This is determined by distance from the listener in 3d, the volume set with Channel::setVolume, channel group volume, and geometry occlusion factors. To make a quiet sound more important, so that it isn't made virtual by louder sounds, you can use this function to increase its importance, and keep it audible. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getPriority + Channel::setVolume +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetPriority(FMOD_CHANNEL *channel, int priority) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setPriority(priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current priority for this channel. + + [PARAMETERS] + 'priority' Address of a variable that receives the current channel priority. 0 to 256 inclusive. 0 = most important. 256 = least important. Default = 128. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setPriority +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetPriority(FMOD_CHANNEL *channel, int *priority) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getPriority(priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the current playback position for the currently playing sound to the specified PCM offset. + + [PARAMETERS] + 'position' Position of the channel to set in units specified in the postype parameter. + 'postype' Time unit to set the channel position by. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + Certain timeunits do not work depending on the file format. For example FMOD_TIMEUNIT_MODORDER will not work with an mp3 file.
    +
    + Note that if you are calling this function on a stream, it has to possibly reflush its buffer to get zero latency playback when it resumes playing, therefore it could potentially cause a stall or take a small amount of time to do this. +
    + Warning! Using a VBR source that does not have an associated seek table or seek information (such as MP3 or MOD/S3M/XM/IT) may cause inaccurate seeking if you specify FMOD_TIMEUNIT_MS or FMOD_TIMEUNIT_PCM.
    + If you want FMOD to create a pcm vs bytes seek table so that seeking is accurate, you will have to specify FMOD_ACCURATETIME when loading or opening the sound. This means there is a slight delay as FMOD scans the whole file when loading the sound to create this table.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getPosition + FMOD_TIMEUNIT + FMOD_MODE + Sound::getLength +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetPosition(FMOD_CHANNEL *channel, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setPosition(position, postype); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the current PCM offset or playback position for the specified channel. + + [PARAMETERS] + 'position' Address of a variable that receives the position of the sound. + 'postype' Time unit to retrieve into the position parameter. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + Certain timeunits do not work depending on the file format. For example FMOD_TIMEUNIT_MODORDER will not work with an mp3 file.
    + A PCM sample is a unit of measurement in audio that contains the data for one audible element of sound. 1 sample might be 16bit stereo, so 1 sample contains 4 bytes. 44,100 samples of a 44khz sound would represent 1 second of data. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setPosition + FMOD_TIMEUNIT + Sound::getLength +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetPosition(FMOD_CHANNEL *channel, unsigned int *position, FMOD_TIMEUNIT postype) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getPosition(position, postype); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the channel specific reverb properties, including things like wet/dry mix. + + [PARAMETERS] + 'prop' A pointer to the a FMOD_REVERB_CHANNELPROPERTIES structure, with the relevant reverb instance specified in FMOD_REVERB_CHANNELFLAGS. + + [RETURN_VALUE] + + [REMARKS] + With FMOD_HARDWARE on Windows using EAX, the reverb will only work on FMOD_3D based sounds. FMOD_SOFTWARE does not have this problem and works on FMOD_2D and FMOD_3D based sounds.
    +
    + On PlayStation 2, the 'Room' parameter is the only parameter supported. The hardware only allows 'on' or 'off', so the reverb will be off when 'Room' is -10000 and on for every other value.
    +
    + On Xbox, it is possible to apply reverb to FMOD_2D and FMOD_HARDWARE based voices using this function. By default reverb is turned off for FMOD_2D hardware based voices.
    +
    + Note! It is important to clear this structure to 0 before calling, or at least specify which reverb instance you are talking about by using the Flags member of FMOD_REVERB_CHANNELPROPERTIES, otherwise you may get an FMOD_ERR_REVERB_INSTANCE error. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getReverbProperties + FMOD_REVERB_CHANNELPROPERTIES + FMOD_REVERB_CHANNELFLAGS +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetReverbProperties(FMOD_CHANNEL *channel, const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setReverbProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current reverb properties for this channel. + + [PARAMETERS] + 'prop' Address of a variable to receive the FMOD_REVERB_CHANNELPROPERTIES information. Remember to clear this structure before calling this function, or specify the correct reverb instance with FMOD_REVERB_CHANNELFLAGS. + + [RETURN_VALUE] + + [REMARKS] + Note! It is important to clear this structure to 0 before calling, or at least specify which reverb instance you are talking about by using the Flags member of FMOD_REVERB_CHANNELPROPERTIES, otherwise you may get an FMOD_ERR_REVERB_INSTANCE error. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setReverbProperties + FMOD_REVERB_CHANNELPROPERTIES + FMOD_REVERB_CHANNELFLAGS +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetReverbProperties(FMOD_CHANNEL *channel, FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getReverbProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the gain of the dry signal when lowpass filtering is applied. + + [PARAMETERS] + 'gain' A linear gain level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getLowPassGain +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetLowPassGain(FMOD_CHANNEL *channel, float gain) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setLowPassGain(gain); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the gain of the dry signal when lowpass filtering is applied. + + [PARAMETERS] + 'gain' Address of a variable to receive the gain level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setLowPassGain +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetLowPassGain(FMOD_CHANNEL *channel, float *gain) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getLowPassGain(gain); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a channel to belong to a specified channel group. A channelgroup can contain many channels.
    + + [PARAMETERS] + 'channelgroup' Pointer to a ChannelGroup object. + + [RETURN_VALUE] + + [REMARKS] + Setting a channel to a channel group removes it from any previous group, it does not allow sharing of channel groups. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetChannelGroup(FMOD_CHANNEL *channel, FMOD_CHANNELGROUP *channelgroup) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setChannelGroup((FMOD::ChannelGroup *)channelgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the currently assigned channel group for the channel. + + [PARAMETERS] + 'channelgroup' Address of a variable to receive a pointer to the currently assigned channel group. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetChannelGroup(FMOD_CHANNEL *channel, FMOD_CHANNELGROUP **channelgroup) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getChannelGroup((FMOD::ChannelGroup **)channelgroup); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a callback for a channel for a specific event. + + [PARAMETERS] + 'type' The callback type, for example an 'end of sound' callback. + 'callback' Pointer to a callback to receive the event when it happens. + + [RETURN_VALUE] + + [REMARKS] + Currently callbacks are driven by System::update and will only occur when this function is called. This has the main advantage of far less complication due to thread issues, and allows all FMOD commands, including loading sounds and playing new sounds from the callback.
    + It also allows any type of sound to have an end callback, no matter what it is. The only disadvantage is that callbacks are not asynchronous and are bound by the latency caused by the rate the user calls the update command.
    + Callbacks are stdcall. Use F_CALLBACK inbetween your return type and function name.
    + Example: +
    +
    +    FMOD_RESULT F_CALLBACK mycallback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2)
    +    { 
    +    
      FMOD::Channel *cppchannel = (FMOD::Channel *)channel; +
      + // More code goes here. +
      + return FMOD_OK; +
    } +
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::update + FMOD_CHANNEL_CALLBACK + FMOD_CHANNEL_CALLBACKTYPE +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetCallback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACK callback) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setCallback(callback); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the position and velocity of a 3d channel. + + [PARAMETERS] + 'pos' Position in 3D space of the channel. Specifying 0 / null will ignore this parameter. + 'vel' Velocity in 'distance units per second' in 3D space of the channel. See remarks. Specifying 0 / null will ignore this parameter. + + [RETURN_VALUE] + + [REMARKS] + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    +
    + For a stereo 3d sound, you can set the spread of the left/right parts in speaker space by using Channel::set3DSpread. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DAttributes + FMOD_VECTOR + System::set3DSettings + Channel::set3DSpread +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DAttributes(FMOD_CHANNEL *channel, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DAttributes(pos, vel); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the position and velocity of a 3d channel. + + [PARAMETERS] + 'pos' Address of a variable that receives the position in 3D space of the channel. Optional. Specify 0 or NULL to ignore. + 'vel' Address of a variable that receives the velocity in 'distance units per second' in 3D space of the channel. See remarks. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DAttributes + FMOD_VECTOR + System::set3DSettings +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DAttributes(FMOD_CHANNEL *channel, FMOD_VECTOR *pos, FMOD_VECTOR *vel) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DAttributes(pos, vel); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the minimum and maximum audible distance for a channel.
    + + [PARAMETERS] + 'mindistance' The channel's minimum volume distance in "units". See remarks for more on units. + 'maxdistance' The channel's maximum volume distance in "units". See remarks for more on units. + + [RETURN_VALUE] + + [REMARKS] +
    + MinDistance is the minimum distance that the sound emitter will cease to continue growing louder at (as it approaches the listener).
    + Within the mindistance it stays at the constant loudest volume possible. Outside of this mindistance it begins to attenuate.
    + MaxDistance is the distance a sound stops attenuating at. Beyond this point it will stay at the volume it would be at maxdistance units from the listener and will not attenuate any more.
    + MinDistance is useful to give the impression that the sound is loud or soft in 3d space. An example of this is a small quiet object, such as a bumblebee, which you could set a mindistance of to 0.1 for example, which would cause it to attenuate quickly and dissapear when only a few meters away from the listener.
    + Another example is a jumbo jet, which you could set to a mindistance of 100.0, which would keep the sound volume at max until the listener was 100 meters away, then it would be hundreds of meters more before it would fade out.
    +
    + In summary, increase the mindistance of a sound to make it 'louder' in a 3d world, and decrease it to make it 'quieter' in a 3d world.
    + maxdistance is effectively obsolete unless you need the sound to stop fading out at a certain point. Do not adjust this from the default if you dont need to.
    + Some people have the confusion that maxdistance is the point the sound will fade out to, this is not the case.
    +
    + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    + The default units for minimum and maximum distances are 1.0 and 10000.0f.
    + Volume drops off at mindistance / distance.
    + To define the min and max distance per sound and not per channel use Sound::set3DMinMaxDistance.
    +
    + If FMOD_3D_CUSTOMROLLOFF is used, then these values are stored, but ignored in 3d processing. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DMinMaxDistance + System::set3DSettings + Sound::set3DMinMaxDistance +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DMinMaxDistance(FMOD_CHANNEL *channel, float mindistance, float maxdistance) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DMinMaxDistance(mindistance, maxdistance); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current minimum and maximum audible distance for a channel. + + [PARAMETERS] + 'mindistance' Pointer to a floating point value to store mindistance. Optional. Specify 0 or NULL to ignore. + 'maxdistance' Pointer to a floating point value to store maxdistance. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DMinMaxDistance + System::set3DSettings + Sound::set3DMinMaxDistance +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DMinMaxDistance(FMOD_CHANNEL *channel, float *mindistance, float *maxdistance) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DMinMaxDistance(mindistance, maxdistance); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the inside and outside angles of the sound projection cone, as well as the volume of the sound outside the outside angle of the sound projection cone. + + [PARAMETERS] + 'insideconeangle' Inside cone angle, in degrees. This is the angle within which the sound is at its normal volume. Must not be greater than outsideconeangle. Default = 360. + 'outsideconeangle' Outside cone angle, in degrees. This is the angle outside of which the sound is at its outside volume. Must not be less than insideconeangle. Default = 360. + 'outsidevolume' Cone outside volume, from 0 to 1.0. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DConeSettings + Channel::set3DConeOrientation + Sound::set3DConeSettings +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DConeSettings(FMOD_CHANNEL *channel, float insideconeangle, float outsideconeangle, float outsidevolume) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the inside and outside angles of the sound projection cone. + + [PARAMETERS] + 'insideconeangle' Address of a variable that receives the inside angle of the sound projection cone, in degrees. This is the angle within which the sound is at its normal volume. Optional. Specify 0 or NULL to ignore. + 'outsideconeangle' Address of a variable that receives the outside angle of the sound projection cone, in degrees. This is the angle outside of which the sound is at its outside volume. Optional. Specify 0 or NULL to ignore. + 'outsidevolume' Address of a variable that receives the cone outside volume for this channel. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DConeSettings + Sound::get3DConeSettings +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DConeSettings(FMOD_CHANNEL *channel, float *insideconeangle, float *outsideconeangle, float *outsidevolume) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the orientation of the sound projection cone. + + [PARAMETERS] + 'orientation' Pointer to an FMOD_VECTOR defining the coordinates of the sound cone orientation vector. + + [RETURN_VALUE] + + [REMARKS] + This function has no effect unless the cone angle and cone outside volume have also been set to values other than the default. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DConeOrientation + Channel::set3DConeSettings + Sound::set3DConeSettings + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DConeOrientation(FMOD_CHANNEL *channel, FMOD_VECTOR *orientation) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DConeOrientation(orientation); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the orientation of the sound projection cone for this channel. + + [PARAMETERS] + 'orientation' Address of a variable that receives the orientation of the sound projection cone. The vector information represents the center of the sound cone. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DConeOrientation +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DConeOrientation(FMOD_CHANNEL *channel, FMOD_VECTOR *orientation) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DConeOrientation(orientation); +} + + +/* +[API] +[ + [DESCRIPTION] + Point a channel to use a custom rolloff curve. Must be used in conjunction with FMOD_3D_CUSTOMROLLOFF flag to be activated. + + [PARAMETERS] + 'points' An array of FMOD_VECTOR structures where x = distance and y = volume from 0.0 to 1.0. z should be set to 0. + 'numpoints' The number of points in the array. + + [RETURN_VALUE] + + [REMARKS] + Note! This function does not duplicate the memory for the points internally. The pointer you pass to FMOD must remain valid until there is no more use for it.
    + Do not free the memory while in use, or use a local variable that goes out of scope while in use.
    +
    + Points must be sorted by distance! Passing an unsorted list to FMOD will result in an error.
    +
    + Set the points parameter to 0 or NULL to disable the points. If FMOD_3D_CUSTOMROLLOFF is set and the rolloff curve is 0, FMOD will revert to logarithmic curve rolloff.
    +
    + Min and maxdistance are meaningless when FMOD_3D_CUSTOMROLLOFF is used and the values are ignored.
    +
    + Here is an example of a custom array of points.
    +
    +    FMOD_VECTOR curve[3] = 
    +    {
    +    
      { 0.0f, 1.0f, 0.0f }, + { 2.0f, 0.2f, 0.0f }, + { 20.0f, 0.0f, 0.0f } +
    }; +
    + x represents the distance, y represents the volume. z is always 0.
    + Distances between points are linearly interpolated.
    + Note that after the highest distance specified, the volume in the last entry is used from that distance onwards.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + FMOD_VECTOR + Channel::get3DCustomRolloff + Sound::set3DCustomRolloff + Sound::get3DCustomRolloff +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DCustomRolloff(FMOD_CHANNEL *channel, FMOD_VECTOR *points, int numpoints) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DCustomRolloff(points, numpoints); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to the sound's current custom rolloff curve. + + [PARAMETERS] + 'points' Address of a variable to receive the pointer to the current custom rolloff point list. Optional. Specify 0 or NULL to ignore. + 'numpoints' Address of a variable to receive the number of points int he current custom rolloff point list. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_VECTOR + Channel::set3DCustomRolloff + Sound::set3DCustomRolloff + Sound::get3DCustomRolloff +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DCustomRolloff(FMOD_CHANNEL *channel, FMOD_VECTOR **points, int *numpoints) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DCustomRolloff(points, numpoints); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the EAX or software based occlusion factors for a channel. If the FMOD geometry engine is not being used, this function can be called to produce the same audible effects, just without the built in polygon processing. FMOD's internal geometry engine calls this function. + + [PARAMETERS] + 'directocclusion' Occlusion factor for a voice for the direct path. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. + 'reverbocclusion' Occlusion factor for a voice for the reverb mix. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + With EAX based sound cards and FMOD_HARDWARE based sounds, this will attenuate the sound using frequency filtering.
    + With non EAX sounds, then the volume is simply attenuated by the directOcclusion factor.
    + If FMOD_INIT_SOFTWARE_OCCLUSION is specified, FMOD_SOFTWARE based sounds will also use frequency filtering, with a small CPU hit.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DOcclusion + ChannelGroup::set3DOcclusion + FMOD_INITFLAGS +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DOcclusion(FMOD_CHANNEL *channel, float directocclusion, float reverbocclusion) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DOcclusion(directocclusion, reverbocclusion); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the the EAX or software based occlusion factors for a channel. + + [PARAMETERS] + 'directocclusion' Address of a variable that receives the occlusion factor for a voice for the direct path. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. Optional. Specify 0 or NULL to ignore. + 'reverbocclusion' Address of a variable that receives the occlusion factor for a voice for the reverb mix. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DOcclusion + ChannelGroup::get3DOcclusion +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DOcclusion(FMOD_CHANNEL *channel, float *directocclusion, float *reverbocclusion) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DOcclusion(directocclusion, reverbocclusion); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the spread of a 3d sound in speaker space.
    + Normally a 3d sound is aimed at one position in a speaker array depending on the 3d position, to give it direction. Left and right parts of a stereo sound for example are consequently summed together and become 'mono'.
    + When increasing the 'spread' of a sound, the left and right parts of a stereo sound rotate away from their original position, to give it more 'stereoness'. The rotation of the sound channels are done in 'speaker space'.
    + Mono sounds are also able to be 'spread' with this function. + + [PARAMETERS] + 'angle' Speaker spread angle. 0 = all sound channels are located at the same speaker location and is 'mono'. 360 = all subchannels are located at the opposite speaker location to the speaker location that it should be according to 3D position. Default = 0. + + [RETURN_VALUE] + + [REMARKS] + Only affects sounds created with FMOD_SOFTWARE.
    +
    + By default, if a stereo sound was played in 3d, and it was directly in front of you, the left and right part of the stereo sound would be summed into the center speaker (on a 5.1 setup), making it sound mono.
    + This function lets you control the speaker spread of a stereo (and above) sound within the speaker array, to separate the left right part of a stereo sound for example.
    + In the above case, in a 5.1 setup, specifying a spread of 90 degrees would put the left part of the sound in the front left speaker, and the right part of the sound in the front right speaker. This stereo separation remains intact as the listener rotates and the sound moves around the speakers.
    + To summarize (for a stereo sound).
    + 1. A spread angle of 0 makes the stereo sound mono at the point of the 3d emitter.
    + 2. A spread angle of 90 makes the left part of the stereo sound place itself at 45 degrees to the left and the right part 45 degrees to the right.
    + 3. A spread angle of 180 makes the left part of the stero sound place itself at 90 degrees to the left and the right part 90 degrees to the right.
    + 4. A spread angle of 360 makes the stereo sound mono at the opposite speaker location to where the 3d emitter should be located (by moving the left part 180 degrees left and the right part 180 degrees right). So in this case, behind you when the sound should be in front of you!
    +
    + Multichannel sounds with channel counts greater than stereo have their sub-channels spread evently through the specified angle. For example a 6 channel sound over a 90 degree spread has each subchannel located 15 degrees apart from each other in the speaker array.
    +
    + Mono sounds are spread as if they were a stereo signal, ie the signal is split into 2. The power of the same will remain the same as it spreads around the speakers. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DSpread + FMOD_MODE +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DSpread(FMOD_CHANNEL *channel, float angle) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DSpread(angle); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the stereo (and above) spread angle specified by Channel::set3DSpread. + + [PARAMETERS] + 'angle' Address of a variable that receives the spread angle for subchannels. 0 = all subchannels are located at the same position. 360 = all subchannels are located at the opposite position. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DSpread +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DSpread(FMOD_CHANNEL *channel, float *angle) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DSpread(angle); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets how much the 3d engine has an effect on the channel, versus that set by Channel::setPan, Channel::setSpeakerMix, Channel::setSpeakerLevels.
    + + [PARAMETERS] + 'level' 1 = Sound pans and attenuates according to 3d position. 0 = Attenuation is ignored and pan/speaker levels are defined by Channel::setPan, Channel::setSpeakerMix, Channel::setSpeakerLevels. Default = 1 (all by 3D position). + + [RETURN_VALUE] + + [REMARKS] + Only affects sounds created FMOD_3D.
    +
    + Useful for morhping a sound between 3D and 2D. This is most common in volumetric sound, when the sound goes from directional, to 'all around you' (and doesn't pan according to listener position/direction).
    + FMOD_INIT_SOFTWARE_HRTF is also interpolated to be 'off' if level = 0, so that you do not get a muffling effect based on location when the sound is supposed to be effectively 2D. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DPanLevel + Channel::setSpeakerMix + Channel::setPan + Channel::setSpeakerLevels +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DPanLevel(FMOD_CHANNEL *channel, float level) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DPanLevel(level); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current 3D mix level for the channel set by Channel::set3DPanLevel. + + [PARAMETERS] + 'level' 0 = Sound pans according to Channel::setSpeakerMix. 1 = Sound pans according to 3d position. Default = 1 (all by 3d position). + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DPanLevel + Channel::setSpeakerMix +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DPanLevel(FMOD_CHANNEL *channel, float *level) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DPanLevel(level); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the channel specific doppler scale for the channel. + + [PARAMETERS] + 'level' 0 = No doppler. 1 = Normal doppler. 5 = max. Default = 1. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::get3DDopplerLevel +] +*/ +FMOD_RESULT F_API FMOD_Channel_Set3DDopplerLevel(FMOD_CHANNEL *channel, float level) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->set3DDopplerLevel(level); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current 3D doppler level for the channel set by Channel::set3DDopplerLevel. + + [PARAMETERS] + 'level' Address of a variable to receives the current doppler scale for this channel. 0 = No doppler. 1 = Normal doppler. 5 = max. Default = 1. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DDopplerLevel +] +*/ +FMOD_RESULT F_API FMOD_Channel_Get3DDopplerLevel(FMOD_CHANNEL *channel, float *level) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->get3DDopplerLevel(level); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns a pointer to the DSP unit head node that handles software mixing for this channel.
    + Only applicable to channels playing sounds created with FMOD_SOFTWARE. + + [PARAMETERS] + 'dsp' Address of a variable that receives pointer to the current head DSP unit for this channel. + + [RETURN_VALUE] + + [REMARKS] + By default a channel DSP unit usually contains 1 input, which is the wavetable input.
    + If System::playDSP has been used then the input to the channel head unit will be the unit that was specified in the call.
    + See the tutorials for more information on DSP networks and how to manipulate them. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createDSP + System::createDSPByType + System::playDSP +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetDSPHead(FMOD_CHANNEL *channel, FMOD_DSP **dsp) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getDSPHead((FMOD::DSP **)dsp); +} + + +/* +[API] +[ + [DESCRIPTION] + This function adds a pre-created DSP unit or effect to the head of the Channel DSP chain. + + [PARAMETERS] + 'dsp' A pointer to a pre-created DSP unit to be inserted at the head of the Channel DSP chain. + 'connection' A pointer to the connection involved between the Channel DSP head and the specified dsp unit. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + This function is a wrapper function to insert a DSP unit at the top of the Channel DSP chain.
    + It disconnects the head unit from its input, then inserts the unit at the head and reconnects the previously disconnected input back as as an input to the new unit.
    +
    + Note: The connection pointer retrieved here will become invalid if you disconnect the 2 dsp units that use it.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getDSPHead + System::createDSP + System::createDSPByType + System::createDSPByPlugin + System::addDSP + ChannelGroup::addDSP + DSP::remove +] +*/ +FMOD_RESULT F_API FMOD_Channel_AddDSP(FMOD_CHANNEL *channel, FMOD_DSP *dsp, FMOD_DSPCONNECTION **connection) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->addDSP((FMOD::DSP *)dsp, (FMOD::DSPConnection **)connection); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the playing state for the current channel. + + [PARAMETERS] + 'isplaying' Address of a variable that receives the current channel's playing status. true = the channel is currently playing a sound. false = the channel is not playing a sound. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + System::playDSP +] +*/ +FMOD_RESULT F_API FMOD_Channel_IsPlaying(FMOD_CHANNEL *channel, FMOD_BOOL *isplaying) +{ + FMOD_RESULT result; + bool isplaying2; + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _channel->isPlaying(&isplaying2); + if (isplaying) + { + *isplaying = isplaying2 ? 1 : 0; + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the current channel's status of whether it is virtual (emulated) or not due to FMOD Ex's virtual channel management system. + + [PARAMETERS] + 'isvirtual' Address of a variable that receives the current channel's virtual status. true = the channel is inaudible and currently being emulated at no cpu cost. false = the channel is a real hardware or software voice and should be audible. + + [RETURN_VALUE] + + [REMARKS] + Virtual channels are not audible, because there are no more real hardware or software channels available.
    + If you are plotting virtual voices vs real voices graphically, and wondering why FMOD sometimes chooses seemingly random channels to be virtual that are usually far away, that is because they are probably silent. It doesn't matter which are virtual and which are not if they are silent. Virtual voices are not calculation on 'closest to listener' calculation, they are based on audibility. + See the tutorial in the FMOD Ex documentation for more information on virtual channels. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound +] +*/ +FMOD_RESULT F_API FMOD_Channel_IsVirtual(FMOD_CHANNEL *channel, FMOD_BOOL *isvirtual) +{ + FMOD_RESULT result; + bool isvirtual2; + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _channel->isVirtual(&isvirtual2); + if (isvirtual) + { + *isvirtual = isvirtual2 ? 1 : 0; + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the combined volume of the channel after 3d sound, volume, channel group volume and geometry occlusion calculations have been performed on it. + + [PARAMETERS] + 'audibility' Address of a variable that receives the channel audibility value. + + [RETURN_VALUE] + + [REMARKS] + This does not represent the waveform, just the calculated volume based on 3d distance, occlusion, volume and channel group volume. + This value is used by the FMOD Ex virtual channel system to order its channels between real and virtual. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setVolume + Channel::getVolume + ChannelGroup::setVolume + ChannelGroup::getVolume + Channel::set3DOcclusion + Channel::get3DOcclusion + Channel::set3DAttributes + Channel::get3DAttributes +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetAudibility(FMOD_CHANNEL *channel, float *audibility) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getAudibility(audibility); +} + + +/* +[API] +[ + [DESCRIPTION] + Returns the currently playing sound for this channel. + + [PARAMETERS] + 'sound' Address of a variable that receives the pointer to the currently playing sound for this channel. + + [RETURN_VALUE] + + [REMARKS] + If a sound is not playing the returned pointer will be 0 or NULL. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + System::playDSP +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetCurrentSound(FMOD_CHANNEL *channel, FMOD_SOUND **sound) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getCurrentSound((FMOD::Sound **)sound); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal for the current channel only. + + [PARAMETERS] + 'spectrumarray' Address of a variable that receives the spectrum data. This is an array of floating point values. Data will range is 0.0 to 1.0. Decibels = 10.0f * (float)log10(val) * 2.0f; See remarks for what the data represents. + 'numvalues' Size of array in floating point values being passed to the function. Must be a power of 2. (ie 128/256/512 etc). Min = 64. Max = 8192. + 'channeloffset' Channel of the signal to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' "Pre-FFT" window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the numvalues, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.
    + The larger the numvalues, the more 'lag' the spectrum will seem to inherit. This is because the FFT window size stretches the analysis back in time to what was already played. For example if the numvalues size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.
    + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).
    + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.
    +
    + What the data represents.
    + To work out what each entry in the array represents, use this formula
    +
    +    entry_hz = (output_rate / 2) / numvalues
    +    
    + The array represents amplitudes of each frequency band from 0hz to the nyquist rate. The nyquist rate is equal to the output rate divided by 2.
    + For example when FMOD is set to 44100hz output, the range of represented frequencies will be 0hz to 22049hz, a total of 22050hz represented.
    + If in the same example, 1024 was passed to this function as the numvalues, each entry's contribution would be as follows. +
    +    entry_hz = (44100 / 2) / 1024
    +    entry_hz = 21.53 hz
    +    
    +
    + Note: This function only displays data for sounds playing that were created with FMOD_SOFTWARE. FMOD_HARDWARE based sounds are played using the sound card driver and are not accessable. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW + System::getSpectrum + ChannelGroup::getSpectrum + System::getWaveData +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetSpectrum(FMOD_CHANNEL *channel, float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to a block of PCM data that represents the currently playing waveform on this channel.
    + This function is useful for a very easy way to plot an oscilliscope. + + [PARAMETERS] + 'wavearray' Address of a variable that receives the currently playing waveform data. This is an array of floating point values. + 'numvalues' Number of floats to write to the array. Maximum value = 16384. + 'channeloffset' Offset into multichannel data. Mono channels use 0. Stereo channels use 0 = left, 1 = right. More than stereo use the appropriate index. + + [RETURN_VALUE] + + [REMARKS] + This is the actual resampled pcm data window at the time the function is called.
    +
    + Do not use this function to try and display the whole waveform of the sound, as this is more of a 'snapshot' of the current waveform at the time it is called, and could return the same data if it is called very quickly in succession.
    + See the DSP API to capture a continual stream of wave data as it plays, or see Sound::lock / Sound::unlock if you want to simply display the waveform of a sound.
    +
    + This function allows retrieval of left and right data for a stereo sound individually. To combine them into one signal, simply add the entries of each seperate buffer together and then divide them by 2. +
    + Note: This function only displays data for sounds playing that were created with FMOD_SOFTWARE. FMOD_HARDWARE based sounds are played using the sound card driver and are not accessable. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getSpectrum + ChannelGroup::getWaveData + System::getWaveData + Sound::lock + Sound::unlock +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetWaveData(FMOD_CHANNEL *channel, float *wavearray, int numvalues, int channeloffset) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getWaveData(wavearray, numvalues, channeloffset); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the internal channel index for a channel. + + [PARAMETERS] + 'index' Address of a variable to receive the channel index. This will be from 0 to the value specified in System::init minus 1. + + [RETURN_VALUE] + + [REMARKS] + Note that working with channel indicies directly is not recommended. It is recommended that you use FMOD_CHANNEL_FREE for the index in System::playSound to use FMOD's channel manager. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + System::init +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetIndex(FMOD_CHANNEL *channel, int *index) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getIndex(index); +} + + +/* +[API] +[ + [DESCRIPTION] + Changes some attributes for a channel based on the mode passed in. + + [PARAMETERS] + 'mode' Mode bits to set. + + [RETURN_VALUE] + + [REMARKS] + Flags supported:
    + FMOD_LOOP_OFF
    + FMOD_LOOP_NORMAL
    + FMOD_LOOP_BIDI (only works with sounds created with FMOD_SOFTWARE. Otherwise it will behave as FMOD_LOOP_NORMAL)
    + FMOD_3D_HEADRELATIVE
    + FMOD_3D_WORLDRELATIVE
    + FMOD_2D (see notes for win32 hardware voices)
    + FMOD_3D (see notes for win32 hardware voices)
    + FMOD_3D_LOGROLLOFF
    + FMOD_3D_LINEARROLLOFF
    + FMOD_3D_CUSTOMROLLOFF
    + FMOD_3D_IGNOREGEOMETRY
    + FMOD_DONTRESTOREVIRTUAL
    +
    + Issues with streamed audio. (Sounds created with with System::createStream or FMOD_CREATESTREAM).
    + When changing the loop mode, sounds created with System::createStream or FMOD_CREATESTREAM may already have been pre-buffered and executed their loop logic ahead of time, before this call was even made.
    + This is dependant on the size of the sound versus the size of the stream decode buffer. See FMOD_CREATESOUNDEXINFO.
    + If this happens, you may need to reflush the stream buffer. To do this, you can call Channel::setPosition which forces a reflush of the stream buffer.
    + Note this will usually only happen if you have sounds or looppoints that are smaller than the stream decode buffer size. Otherwise you will not normally encounter any problems.
    +
    + Issues with PCM samples. (Sounds created with with System::createSound or FMOD_CREATESAMPLE).
    + When changing the loop mode, if the sound was set up as FMOD_LOOP_OFF, then set to FMOD_LOOP_NORMAL with this function, the sound may click when playing the end of the sound. This is because the sound needs to be pre-prepared for looping using Sound::setMode, by modifying the content of the pcm data (ie data past the end of the actual sample data) to allow the interpolators to read ahead without clicking. If you use Channel::setMode it will not do this (because different channels may have different loop modes for the same sound) and may click if you try to set it to looping on an unprepared sound. If you want to change the loop mode at runtime it may be better to load the sound as looping first (or use Sound::setMode), to let it pre-prepare the data as if it was looping so that it does not click whenever Channel::setMode is used to turn looping on.
    +
    + Win32 FMOD_HARDWARE note. Under DirectSound, you cannot change the loop mode of a channel while it is playing. You must use Sound::setMode or pause the channel to get this to work.
    + Win32 FMOD_HARDWARE note. Under DirectSound, you cannot change the mode of a channel between FMOD_2D and FMOD_3D. If this is a problem create the sound as FMOD_3D initially, and use FMOD_3D_HEADRELATIVE and FMOD_3D_WORLDRELATIVE. Alternatively just use FMOD_SOFTWARE.
    +
    + If FMOD_3D_IGNOREGEOMETRY is not specified, the flag will be cleared if it was specified previously.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MODE + Channel::getMode + Channel::setPosition + Sound::setMode + System::createStream + System::createSound + System::setStreamBufferSize + FMOD_CREATESOUNDEXINFO +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetMode(FMOD_CHANNEL *channel, FMOD_MODE mode) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setMode(mode); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current mode bit flags for the current channel. + + [PARAMETERS] + 'mode' Address of a an FMOD_MODE variable that receives the current mode for this channel. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setMode +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetMode(FMOD_CHANNEL *channel, FMOD_MODE *mode) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getMode(mode); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a channel to loop a specified number of times before stopping. + + [PARAMETERS] + 'loopcount' Number of times to loop before stopping. 0 = oneshot. 1 = loop once then stop. -1 = loop forever. Default = -1 + + [RETURN_VALUE] + + [REMARKS] + This function does not affect FMOD_HARDWARE based sounds that are not streamable.
    + FMOD_SOFTWARE based sounds or any type of sound created with System::CreateStream or FMOD_CREATESTREAM will support this function.
    +
    + Issues with streamed audio. (Sounds created with with System::createStream or FMOD_CREATESTREAM). + When changing the loop count, sounds created with System::createStream or FMOD_CREATESTREAM may already have been pre-buffered and executed their loop logic ahead of time, before this call was even made.
    + This is dependant on the size of the sound versus the size of the stream decode buffer. See FMOD_CREATESOUNDEXINFO.
    + If this happens, you may need to reflush the stream buffer. To do this, you can call Channel::setPosition which forces a reflush of the stream buffer.
    + Note this will usually only happen if you have sounds or looppoints that are smaller than the stream decode buffer size. Otherwise you will not normally encounter any problems.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getLoopCount + Channel::setPosition + System::createStream + FMOD_CREATESOUNDEXINFO + FMOD_MODE +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetLoopCount(FMOD_CHANNEL *channel, int loopcount) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setLoopCount(loopcount); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current loop count for the specified channel. + + [PARAMETERS] + 'loopcount' Address of a variable that receives the number of times a channel will loop before stopping. 0 = oneshot. 1 = loop once then stop. -1 = loop forever. Default = -1 + + [RETURN_VALUE] + + [REMARKS] + This function retrieves the current loop countdown value for the channel being played.
    + This means it will decrement until reaching 0, as it plays. To reset the value, use Channel::setLoopCount.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setLoopCount +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetLoopCount(FMOD_CHANNEL *channel, int *loopcount) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getLoopCount(loopcount); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the loop points within a channel. + + [PARAMETERS] + 'loopstart' The loop start point. This point in time is played, so it is inclusive. + 'loopstarttype' The time format used for the loop start point. See FMOD_TIMEUNIT. + 'loopend' The loop end point. This point in time is played, so it is inclusive. + 'loopendtype' The time format used for the loop end point. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + Not supported by static sounds created with FMOD_HARDWARE.
    + Supported by sounds created with FMOD_SOFTWARE, or sounds of any type (hardware or software) created with System::createStream or FMOD_CREATESTREAM. +
    + If a sound was 44100 samples long and you wanted to loop the whole sound, loopstart would be 0, and loopend would be 44099,
    not
    44100. You wouldn't use milliseconds in this case because they are not sample accurate.
    + If loop end is smaller or equal to loop start, it will result in an error.
    + If loop start or loop end is larger than the length of the sound, it will result in an error.
    +
    + Issues with streamed audio. (Sounds created with with System::createStream or FMOD_CREATESTREAM).
    + When changing the loop points, sounds created with System::createStream or FMOD_CREATESTREAM may already have been pre-buffered and executed their loop logic ahead of time, before this call was even made.
    + This is dependant on the size of the sound versus the size of the stream decode buffer. See FMOD_CREATESOUNDEXINFO.
    + If this happens, you may need to reflush the stream buffer. To do this, you can call Channel::setPosition which forces a reflush of the stream buffer.
    + Note this will usually only happen if you have sounds or looppoints that are smaller than the stream decode buffer size. Otherwise you will not normally encounter any problems.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + FMOD_MODE + Channel::getLoopPoints + Channel::setLoopCount + System::createStream + System::setStreamBufferSize + FMOD_CREATESOUNDEXINFO +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetLoopPoints(FMOD_CHANNEL *channel, unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setLoopPoints(loopstart, loopstarttype, loopend, loopendtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the loop points for a channel. + + [PARAMETERS] + 'loopstart' Address of a variable to receive the loop start point. This point in time is played, so it is inclusive. Optional. Specify 0 or NULL to ignore. + 'loopstarttype' The time format used for the returned loop start point. See FMOD_TIMEUNIT. + 'loopend' Address of a variable to receive the loop end point. This point in time is played, so it is inclusive. Optional. Specify 0 or NULL to ignore. + 'loopendtype' The time format used for the returned loop end point. See FMOD_TIMEUNIT. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + Channel::setLoopPoints +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetLoopPoints(FMOD_CHANNEL *channel, unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getLoopPoints(loopstart, loopstarttype, loopend, loopendtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the Channel object will store internally. Can be retrieved with Channel::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the Channel object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using Channel::getUserData would help in the identification of the object.

    + NOTE: If this channel was spawned by the event system then its user data field will be set, by the event system, to the event instance handle that spawned it and this function should NOT be called. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getUserData +] +*/ +FMOD_RESULT F_API FMOD_Channel_SetUserData(FMOD_CHANNEL *channel, void *userdata) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the Channel::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the data specified with the Channel::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + NOTE: If this channel was spawned by the event system then its user data field will be set, by the event system, to the event instance handle that spawned it. Use this function to go from an arbitrary channel back up to the event that owns it. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setUserData +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetUserData(FMOD_CHANNEL *channel, void **userdata) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_Channel_GetMemoryInfo(FMOD_CHANNEL *channel, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::Channel *_channel = (FMOD::Channel *)channel; + + if (!_channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _channel->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Frees a channel group. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + All channels assigned to this group are returned back to the master channel group owned by the System object. See System::getMasterChannelGroup.
    + All child groups assigned to this group are returned back to the master channel group owned by the System object. See System::getMasterChannelGroup.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createChannelGroup + System::getMasterChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_Release(FMOD_CHANNELGROUP *channelgroup) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->release(); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the parent System object that created this channel group. + + [PARAMETERS] + 'system' Address of a variable that receives the System object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createChannelGroup + System::getMasterChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetSystemObject(FMOD_CHANNELGROUP *channelgroup, FMOD_SYSTEM **system) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getSystemObject((FMOD::System **)system); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the master volume for the channel group linearly. + + [PARAMETERS] + 'volume' A linear volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + This function does not go through and overwrite the channel volumes. It scales them by the channel group's volume.
    + That way when Channel::setVolume / Channel::getVolume is called the respective individual channel volumes will still be preserved. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::setVolume + Channel::setVolume + Channel::getVolume + ChannelGroup::overrideVolume +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_SetVolume(FMOD_CHANNELGROUP *channelgroup, float volume) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the master volume level for the channel group. + + [PARAMETERS] + 'volume' Address of a variable to receive the channel group volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::setVolume + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetVolume(FMOD_CHANNELGROUP *channelgroup, float *volume) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the master pitch for the channel group. + + [PARAMETERS] + 'pitch' A pitch level, from 0.0 to 10.0 inclusive. 0.5 = half pitch, 2.0 = double pitch. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + This function does not go through and overwrite the channel frequencies. It scales them by the channel group's pitch.
    + That way when Channel::setFrequency / Channel::getFrequency is called the respective individual channel frequencies will still be preserved. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::overrideFrequency + System::getMasterChannelGroup + ChannelGroup::getPitch + Channel::setFrequency + Channel::getFrequency + ChannelGroup::overrideFrequency +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_SetPitch(FMOD_CHANNELGROUP *channelgroup, float pitch) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setPitch(pitch); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the master pitch level for the channel group. + + [PARAMETERS] + 'pitch' Address of a variable to receive the channel group pitch value, from 0.0 to 10.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::setPitch + ChannelGroup::overrideFrequency + System::getMasterChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetPitch(FMOD_CHANNELGROUP *channelgroup, float *pitch) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getPitch(pitch); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the master occlusion factors for the channel group. + + [PARAMETERS] + 'directocclusion' Occlusion factor for the direct path. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. + 'reverbocclusion' Occlusion factor for the reverb mix. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + This function does not go through and overwrite the channel occlusion factors. It scales them by the channel group's occlusion factors.
    + That way when Channel::set3DOcclusion / Channel::get3DOcclusion is called the respective individual channel occlusion factors will still be preserved. + This means that final Channel occlusion values will be affected by both ChannelGroup occlusion and geometry (if any). + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::get3DOcclusion + Channel::set3DOcclusion + Channel::get3DOcclusion + System::getMasterChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_Set3DOcclusion(FMOD_CHANNELGROUP *channelgroup, float directocclusion, float reverbocclusion) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->set3DOcclusion(directocclusion, reverbocclusion); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the master occlusion factors for the channel group. + + [PARAMETERS] + 'directocclusion' Address of a variable that receives the occlusion factor for the direct path. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. Optional. Specify 0 or NULL to ignore. + 'reverbocclusion' Address of a variable that receives the occlusion factor for the reverb mix. 0.0 = not occluded. 1.0 = fully occluded. Default = 0.0. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::set3DOcclusion + Channel::set3DOcclusion + Channel::get3DOcclusion + System::getMasterChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_Get3DOcclusion(FMOD_CHANNELGROUP *channelgroup, float *directocclusion, float *reverbocclusion) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->get3DOcclusion(directocclusion, reverbocclusion); +} + + +/* +[API] +[ + [DESCRIPTION] + Pauses a channelgroup, and the channels within it, or unpauses any unpaused channels if set to false. + + [PARAMETERS] + 'paused' Paused state to set. true = channelgroup state is set to paused. false = channelgroup state is set to unpaused. + + [RETURN_VALUE] + + [REMARKS] + A channelgroup maintains a paused state, that affects channelgroups and channels within it. If a channelgroup is paused, all channelgroups and channels below it will become paused.
    + Channels will not have their per channel pause state overwritten, so that when a channelgroup is unpaused, the paused state of the channels will correct as they were set on a per channel basis.
    + This means even though a channel is paused, it can return false when you call Channel::getPaused on that channel, because that was the state of the channel at the time before the ChannelGroup was paused.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getPaused + Channel::setPaused + Channel::getPaused + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_SetPaused(FMOD_CHANNELGROUP *channelgroup, FMOD_BOOL paused) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setPaused(paused ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the pause state of a ChannelGroup. + + [PARAMETERS] + 'paused' Address of a variable to receive the pause state of the channelgroup. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::setPaused + Channel::setPaused + Channel::getPaused + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetPaused(FMOD_CHANNELGROUP *channelgroup, FMOD_BOOL *paused) +{ + FMOD_RESULT result; + bool paused2; + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _group->getPaused(&paused2); + if (result == FMOD_OK) + { + if (paused) + { + *paused = paused2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Mutes a channelgroup, and the channels within it, or unmutes any unmuted channels if set to false. + + [PARAMETERS] + 'mute' Mute state to set. true = channelgroup state is set to muted. false = channelgroup state is set to unmuted. + + [RETURN_VALUE] + + [REMARKS] + A channelgroup maintains a mute state, that affects channelgroups and channels within it. If a channelgroup is muted, all channelgroups and channels below it will become muted.
    + Channels will not have their per channel mute state overwritten, so that when a channelgroup is unmuted, the muted state of the channels will correct as they were set on a per channel basis.
    + This means even though a channel is muted, it can return false when you call Channel::getMute on that channel, because that was the state of the channel at the time before the ChannelGroup was muted.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getMute + Channel::setMute + Channel::getMute + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_SetMute(FMOD_CHANNELGROUP *channelgroup, FMOD_BOOL mute) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setMute(mute ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the mute state of a ChannelGroup. + + [PARAMETERS] + 'mute' Address of a variable to receive the pause state of the channelgroup. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::setMute + Channel::setMute + Channel::getMute + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetMute(FMOD_CHANNELGROUP *channelgroup, FMOD_BOOL *mute) +{ + FMOD_RESULT result; + bool mute2; + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _group->getMute(&mute2); + if (result == FMOD_OK) + { + if (mute) + { + *mute = mute2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Stops all channels within the channelgroup. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_Stop(FMOD_CHANNELGROUP *channelgroup) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->stop(); +} + + +/* +[API] +[ + [DESCRIPTION] + Overrides the volume of all channels within this channel group and those of any sub channelgroups. + + [PARAMETERS] + 'volume' A linear volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + This is not to be used as a master volume for the group, as it will modify the volumes of the channels themselves.
    + If you want to scale the volume of the group, use ChannelGroup::setVolume. + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getMasterChannelGroup + System::createChannelGroup + ChannelGroup::setVolume +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_OverrideVolume(FMOD_CHANNELGROUP *channelgroup, float volume) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->overrideVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Overrides the frequency or playback rate, in HZ of all channels within this channel group and those of any sub channelgroups. + + [PARAMETERS] + 'frequency' A frequency value in HZ. This value can also be negative to play the sound backwards (negative frequencies allowed with FMOD_SOFTWARE based non-stream sounds only). DirectSound hardware voices have limited frequency range on some soundcards. Please see remarks for more on this. + + [RETURN_VALUE] + + [REMARKS] + When a sound is played, it plays at the default frequency of the sound which can be set by Sound::setDefaults.
    + For most file formats, the volume is determined by the audio format.
    +
    + Frequency limitations for sounds created with FMOD_HARDWARE in DirectSound.
    + Every hardware device has a minimum and maximum frequency. This means setting the frequency above the maximum and below the minimum will have no effect.
    + FMOD clamps frequencies to these values when playing back on hardware, so if you are setting the frequency outside of this range, the frequency will stay at either the minimum or maximum.
    + Note that FMOD_SOFTWARE based sounds do not have this limitation.
    + To find out the minimum and maximum value before initializing FMOD (maybe to decide whether to use a different soundcard, output mode, or drop back fully to software mixing), you can use the System::getDriverCaps function. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setFrequency + Channel::getFrequency + System::getDriverCaps + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_OverrideFrequency(FMOD_CHANNELGROUP *channelgroup, float frequency) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->overrideFrequency(frequency); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets pan position linearly of all channels within this channel group and those of any sub channelgroups. + + [PARAMETERS] + 'pan' A left/right pan level, from -1.0 to 1.0 inclusive. -1.0 = Full left, 0.0 = center, 1.0 = full right. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + Panning only works on sounds created with FMOD_2D. 3D sounds are not pannable.
    + Only sounds that are mono or stereo can be panned. Multichannel sounds (ie >2 channels) cannot be panned.
    +
    + Mono sounds are panned from left to right using constant power panning. This means when pan = 0.0, the balance for the sound in each speaker is 71% left and 71% right, not 50% left and 50% right. This gives (audibly) smoother pans.
    + Stereo sounds heave each left/right value faded up and down according to the specified pan position. This means when pan = 0.0, the balance for the sound in each speaker is 100% left and 100% right. When pan = -1.0, only the left channel of the stereo sound is audible, when pan = 1.0, only the right channel of the stereo sound is audible.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getMasterChannelGroup + System::createChannelGroup + Channel::setPan + Channel::getPan +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_OverridePan(FMOD_CHANNELGROUP *channelgroup, float pan) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->overridePan(pan); +} + + +/* +[API] +[ + [DESCRIPTION] + Overrides the reverb properties of all channels within this channel group and those of any sub channelgroups. + + [PARAMETERS] + 'prop' Pointer to a FMOD_REVERB_CHANNELPROPERTIES structure definition. + + [RETURN_VALUE] + + [REMARKS] + With FMOD_HARDWARE on Windows using EAX, the reverb will only work on FMOD_3D based sounds. FMOD_SOFTWARE does not have this problem and works on FMOD_2D and FMOD_3D based sounds.
    +
    + On PlayStation 2, the 'Room' parameter is the only parameter supported. The hardware only allows 'on' or 'off', so the reverb will be off when 'Room' is -10000 and on for every other value.
    +
    + On Xbox, it is possible to apply reverb to FMOD_2D and FMOD_HARDWARE based voices using this function. By default reverb is turned off for FMOD_2D hardware based voices, to make it compatible with EAX.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_REVERB_CHANNELPROPERTIES + System::setReverbProperties + System::getReverbProperties + Channel::setReverbProperties + Channel::getReverbProperties + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_OverrideReverbProperties(FMOD_CHANNELGROUP *channelgroup, const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->overrideReverbProperties(prop); +} + + +/* +[API] +[ + [DESCRIPTION] + Overrides the position and velocity of all channels within this channel group and those of any sub channelgroups. + + [PARAMETERS] + 'pos' Position in 3D space of the channels in the group. Specifying 0 / null will ignore this parameter. + 'vel' Velocity in 'distance units per second' in 3D space of the group of channels. See remarks. Specifying 0 / null will ignore this parameter. + + [RETURN_VALUE] + + [REMARKS] + A 'distance unit' is specified by System::set3DSettings. By default this is set to meters which is a distance scale of 1.0.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::set3DAttributes + Channel::get3DAttributes + FMOD_VECTOR + System::set3DSettings +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_Override3DAttributes(FMOD_CHANNELGROUP *channelgroup, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->override3DAttributes(pos, vel); +} + + +/* +[API] +[ + [DESCRIPTION] + Overrides all channel speaker levels for each speaker individually. + + [PARAMETERS] + 'frontleft' Level for this channel in the front left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'frontright' Level for this channel in the front right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'center' Level for this channel in the center speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'lfe' Level for this channel in the subwoofer speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'backleft' Level for this channel in the back left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'backright' Level for this channel in the back right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'sideleft' Level for this channel in the side left speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + 'sideright' Level for this channel in the side right speaker of a multichannel speaker setup. 0.0 = silent, 1.0 = full volume. + + [RETURN_VALUE] + + [REMARKS] + This function only works on sounds created with FMOD_2D. 3D sounds are not pannable and will return FMOD_ERR_NEEDS2D.
    +
    + Only sounds create with FMOD_SOFTWARE playing on this channel will allow this functionality.
    +
    + Speakers specified that don't exist will simply be ignored.
    +
    + For more advanced speaker control, including sending the different channels of a stereo sound to arbitrary speakers, see Channel::setSpeakerLevels.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setSpeakerMix + Channel::getSpeakerMix + Channel::setSpeakerLevels +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_OverrideSpeakerMix(FMOD_CHANNELGROUP *channelgroup, float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->overrideSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); +} + + +/* +[API] +[ + [DESCRIPTION] + Adds a channel group as a child of the current channel group. + + [PARAMETERS] + 'group' channel group to add as a child. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getNumGroups + ChannelGroup::getGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_AddGroup(FMOD_CHANNELGROUP *channelgroup, FMOD_CHANNELGROUP *group) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->addGroup((FMOD::ChannelGroup *)group); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of sub groups under this channel group. + + [PARAMETERS] + 'numgroups' Address of a variable to receive the number of channel groups within this channel group. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getGroup + ChannelGroup::addGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetNumGroups(FMOD_CHANNELGROUP *channelgroup, int *numgroups) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getNumGroups(numgroups); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a handle to a specified sub channelgroup. + + [PARAMETERS] + 'index' Index to specify which sub channelgroup to receieve. + 'group' Address of a variable to receieve a pointer to a channelgroup. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getNumGroups + ChannelGroup::getParentGroup + ChannelGroup::addGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetGroup(FMOD_CHANNELGROUP *channelgroup, int index, FMOD_CHANNELGROUP **group) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getGroup(index, (FMOD::ChannelGroup **)group); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a handle to this channelgroup's parent channelgroup. + + [PARAMETERS] + 'group' Address of a variable to recieve a pointer to a channelgroup. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getNumGroups + ChannelGroup::getGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetParentGroup(FMOD_CHANNELGROUP *channelgroup, FMOD_CHANNELGROUP **group) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getParentGroup((FMOD::ChannelGroup **)group); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the DSP unit responsible for this channel group. When channels are submixed to this channel group, this is the DSP unit they target. + + [PARAMETERS] + 'dsp' Address of a variable to receive the pointer to the head DSP unit for this channel group. + + [RETURN_VALUE] + + [REMARKS] + Use this unit if you wish to connect custom DSP units to the channelgroup or filter the channels in the channel group by inserting filter units between this one and the incoming channel mixer unit.
    + Read the tutorial on DSP if you wish to know more about this. It is not recommended using this if you do not understand how the FMOD Ex DSP network is connected.
    + Alternatively you can simply add effects by using ChannelGroup::addDSP which does the connection / disconnection work for you.
    +
    + Note: If this function returns FMOD_ERR_DSP_NOTFOUND when called on an event's channelgroup, + the channelgroup's DSP unit may have been optimized away by the event system. + Use FMOD_EVENT_USERDSP when getting the event to force the channelgroup to contain a DSP unit. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::addDSP + System::createDSP + System::createDSPByType + System::createDSPByPlugin + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetDSPHead(FMOD_CHANNELGROUP *channelgroup, FMOD_DSP **dsp) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getDSPHead((FMOD::DSP **)dsp); +} + + +/* +[API] +[ + [DESCRIPTION] + Adds a DSP effect to this channelgroup, affecting all channels that belong to it. Because it is a submix, only one instance of the effect is added, and all subsequent channels are affected. + + [PARAMETERS] + 'dsp' Pointer to the dsp effect to add. This can be created with System::createDSP, System::createDSPByType, System::createDSPByPlugin. + 'connection' A pointer to the connection involved between the ChannelGroup DSP head and the specified dsp unit. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + This function is a wrapper function to insert a DSP unit at the top of the channel group DSP chain.
    + It disconnects the head unit from its input, then inserts the unit at the head and reconnects the previously disconnected input back as as an input to the new unit.
    +
    + Note: The connection pointer retrieved here will become invalid if you disconnect the 2 dsp units that use it.
    +
    + Note: If this function returns FMOD_ERR_DSP_NOTFOUND when called on an event's channelgroup, + the channelgroup's DSP unit may have been optimized away by the event system. + Use FMOD_EVENT_USERDSP when getting the event to force the channelgroup to contain a DSP unit. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getDSPHead + System::createDSP + System::createDSPByType + System::createDSPByPlugin + System::getMasterChannelGroup + System::createChannelGroup + System::addDSP + Channel::addDSP + DSP::remove +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_AddDSP(FMOD_CHANNELGROUP *channelgroup, FMOD_DSP *dsp, FMOD_DSPCONNECTION **connection) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->addDSP((FMOD::DSP *)dsp, (FMOD::DSPConnection **)connection); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the name of the channelgroup. The name is set when the group is created. + + [PARAMETERS] + 'name' Address of a variable that receives the name of the channel group. + 'namelen' Length in bytes of the target buffer to receieve the string. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetName(FMOD_CHANNELGROUP *channelgroup, char *name, int namelen) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getName(name, namelen); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current number of assigned channels to this channel group. + + [PARAMETERS] + 'numchannels' Address of a variable to receive the current number of assigned channels in this channel group. + + [RETURN_VALUE] + + [REMARKS] + Use this function to enumerate the channels within the channel group. You can then use ChannelGroup::getChannel to retrieve each individual channel. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getChannel + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetNumChannels(FMOD_CHANNELGROUP *channelgroup, int *numchannels) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getNumChannels(numchannels); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the a handle to a channel from the current channel group. + + [PARAMETERS] + 'index' Index of the channel inside the channel group, from 0 to the number of channels returned by ChannelGroup::getNumChannels. + 'channel' Address of a variable to receieve a pointer to a Channel object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getNumChannels + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetChannel(FMOD_CHANNELGROUP *channelgroup, int index, FMOD_CHANNEL **channel) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getChannel(index, (FMOD::Channel **)channel); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing channels assigned to this channel group. + + [PARAMETERS] + 'spectrumarray' Address of a variable that receives the spectrum data. This is an array of floating point values. Data will range is 0.0 to 1.0. Decibels = 10.0f * (float)log10(val) * 2.0f; See remarks for what the data represents. + 'numvalues' Size of array in floating point values being passed to the function. Must be a power of 2. (ie 128/256/512 etc). Min = 64. Max = 8192. + 'channeloffset' Channel of the signal to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' "Pre-FFT" window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the numvalues, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.
    + The larger the numvalues, the more 'lag' the spectrum will seem to inherit. This is because the FFT window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.
    + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).
    + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.
    +
    + What the data represents.
    + To work out what each entry in the array represents, use this formula
    +

    +    entry_hz = (output_rate / 2) / numvalues
    +    
    + The array represents amplitudes of each frequency band from 0hz to the nyquist rate. The nyquist rate is equal to the output rate divided by 2.
    + For example when FMOD is set to 44100hz output, the range of represented frequencies will be 0hz to 22049hz, a total of 22050hz represented.
    + If in the same example, 1024 was passed to this function as the numvalues, each entry's contribution would be as follows. +
    +    entry_hz = (44100 / 2) / 1024
    +    entry_hz = 21.53 hz
    +    
    +
    + Note: This function only displays data for sounds playing that were created with FMOD_SOFTWARE. FMOD_HARDWARE based sounds are played using the sound card driver and are not accessable. +
    + With the Event system, events are now optimized to remove the DSP unit from a channelgroup to save memory. This will result in the error FMOD_ERR_DSP_NOTFOUND. If you wish to force the DSP node to be created with an event, use FMOD_EVENT_USERDSP flag when getting an event. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW + System::getSpectrum + Channel::getSpectrum + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetSpectrum(FMOD_CHANNELGROUP *channelgroup, float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to a block of PCM data that represents the currently playing waveform for this channel group.
    + This function is useful for a very easy way to plot an oscilliscope. + + [PARAMETERS] + 'wavearray' Address of a variable that receives the currently playing waveform data. This is an array of floating point values. + 'numvalues' Number of floats to write to the array. Maximum value = 16384. + 'channeloffset' Offset into multichannel data. Mono channels use 0. Stereo channels use 0 = left, 1 = right. More than stereo use the appropriate index. + + [RETURN_VALUE] + + [REMARKS] + This is the actual resampled, filtered and volume scaled data, at the time this function is called.
    +
    + Do not use this function to try and display the whole waveform of the sound, as this is more of a 'snapshot' of the current waveform at the time it is called, and could return the same data if it is called very quickly in succession.
    + See the DSP API to capture a continual stream of wave data as it plays, or see Sound::lock / Sound::unlock if you want to simply display the waveform of a sound.
    +
    + This function allows retrieval of left and right data for a stereo sound individually. To combine them into one signal, simply add the entries of each seperate buffer together and then divide them by 2. +
    + Note: This function only displays data for sounds playing that were created with FMOD_SOFTWARE. FMOD_HARDWARE based sounds are played using the sound card driver and are not accessable. +
    + With the Event system, events are now optimized to remove the DSP unit from a channelgroup to save memory. This will result in the error FMOD_ERR_DSP_NOTFOUND. If you wish to force the DSP node to be created with an event, use FMOD_EVENT_USERDSP flag when getting an event. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getMasterChannelGroup + System::createChannelGroup + Sound::lock + Sound::unlock +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetWaveData(FMOD_CHANNELGROUP *channelgroup, float *wavearray, int numvalues, int channeloffset) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getWaveData(wavearray, numvalues, channeloffset); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the ChannelGroup object will store internally. Can be retrieved with ChannelGroup::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the ChannelGroup object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using ChannelGroup::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::getUserData + System::getMasterChannelGroup + System::createChannelGroup +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_SetUserData(FMOD_CHANNELGROUP *channelgroup, void *userdata) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the ChannelGroup::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the to user data specified with the ChannelGroup::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + ChannelGroup::setUserData +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetUserData(FMOD_CHANNELGROUP *channelgroup, void **userdata) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_ChannelGroup_GetMemoryInfo(FMOD_CHANNELGROUP *channelgroup, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::ChannelGroup *_group = (FMOD::ChannelGroup *)channelgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Releases a soundgroup object and returns all sounds back to the master sound group. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + You cannot release the master sound group. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_Release(FMOD_SOUNDGROUP *soundgroup) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->release(); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the parent System object that was used to create this object. + + [PARAMETERS] + 'system' Address of a pointer that receives the System object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetSystemObject(FMOD_SOUNDGROUP *soundgroup, FMOD_SYSTEM **system) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getSystemObject((FMOD::System **)system); +} + + +/* +[API] +[ + [DESCRIPTION] + Limits the number of concurrent playbacks of sounds in a sound group to the specified value.
    + After this, if the sounds in the sound group are playing this many times, any attepts to play more of the sounds in the sound group will by default fail with FMOD_ERR_MAXAUDIBLE.
    + Use SoundGroup::setMaxAudibleBehavior to change the way the sound playback behaves when too many sounds are playing. Muting, failing and stealing behaviors can be specified.
    + + [PARAMETERS] + 'maxaudible' Number of playbacks to be audible at once. -1 = unlimited. 0 means no sounds in this group will succeed. Default = -1. + + [RETURN_VALUE] + + [REMARKS] + SoundGroup::getNumPlaying can be used to determine how many instances of the sounds in the sound group are currently playing.
    + + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + SoundGroup::getMaxAudible + SoundGroup::getNumPlaying + SoundGroup::setMaxAudibleBehavior + SoundGroup::getMaxAudibleBehavior + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_SetMaxAudible(FMOD_SOUNDGROUP *soundgroup, int maxaudible) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setMaxAudible(maxaudible); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of concurrent playbacks of sounds in a sound group to the specified value.
    + If the sounds in the sound group are playing this many times, any attepts to play more of the sounds in the sound group will fail with FMOD_ERR_MAXAUDIBLE. + + [PARAMETERS] + 'maxaudible' Address of a variable to recieve the number of playbacks to be audible at once. -1 = unlimited. 0 means no sounds in this group will succeed. Default = -1. + + [RETURN_VALUE] + + [REMARKS] + SoundGroup::getNumPlaying can be used to determine how many instances of the sounds in the sound group are playing. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::setMaxAudible + SoundGroup::getNumPlaying + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetMaxAudible(FMOD_SOUNDGROUP *soundgroup, int *maxaudible) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getMaxAudible(maxaudible); +} + + +/* +[API] +[ + [DESCRIPTION] + This function changes the way the sound playback behaves when too many sounds are playing in a soundgroup. Muting, failing and stealing behaviors can be specified.
    + + [PARAMETERS] + 'behavior' Specify a behavior determined with a FMOD_SOUNDGROUP_BEHAVIOR flag. Default is FMOD_SOUNDGROUP_BEHAVIOR_FAIL. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_SOUNDGROUP_BEHAVIOR + SoundGroup::getMaxAudibleBehavior + SoundGroup::setMaxAudible + SoundGroup::getMaxAudible + SoundGroup::setMuteFadeSpeed + SoundGroup::getMuteFadeSpeed + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_SetMaxAudibleBehavior(FMOD_SOUNDGROUP *soundgroup, FMOD_SOUNDGROUP_BEHAVIOR behavior) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setMaxAudibleBehavior(behavior); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current max audible behavior method. + + [PARAMETERS] + 'behavior' Address of a variable to recieve the current sound group max playbacks behavior. Default is FMOD_SOUNDGROUP_BEHAVIOR_FAIL. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_SOUNDGROUP_BEHAVIOR + SoundGroup::setMaxAudibleBehavior + SoundGroup::setMaxAudible + SoundGroup::getMaxAudible + SoundGroup::setMuteFadeSpeed + SoundGroup::getMuteFadeSpeed + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetMaxAudibleBehavior(FMOD_SOUNDGROUP *soundgroup, FMOD_SOUNDGROUP_BEHAVIOR *behavior) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getMaxAudibleBehavior(behavior); +} + + +/* +[API] +[ + [DESCRIPTION] + Specify a time in seconds for FMOD_SOUNDGROUP_BEHAVIOR_MUTE behavior to fade with. By default there is no fade.
    + When more sounds are playing in a SoundGroup than are specified with SoundGroup::setMaxAudible, the least important sound (ie lowest priority / lowest audible volume due to 3d position, volume etc) will fade to silence if FMOD_SOUNDGROUP_BEHAVIOR_MUTE is used, and any previous sounds that were silent because of this rule will fade in if they are more important.
    + + [PARAMETERS] + 'speed' Fade time in seconds (1.0 = 1 second). Default = 0.0. (no fade). + + [RETURN_VALUE] + + [REMARKS] + If a mode besides FMOD_SOUNDGROUP_BEHAVIOR_MUTE is used, the fade speed is ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::getMuteFadeSpeed + SoundGroup::setMaxAudibleBehavior + SoundGroup::getMaxAudibleBehavior + SoundGroup::setMaxAudible + SoundGroup::getMaxAudible + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_SetMuteFadeSpeed(FMOD_SOUNDGROUP *soundgroup, float speed) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setMuteFadeSpeed(speed); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current time in seconds for FMOD_SOUNDGROUP_BEHAVIOR_MUTE behavior to fade with. + + [PARAMETERS] + 'speed' Address of a variable to receive the fade time in seconds (1.0 = 1 second). Default = 0.0. (no fade). + + [RETURN_VALUE] + + [REMARKS] + If a mode besides FMOD_SOUNDGROUP_BEHAVIOR_MUTE is used, the fade speed is ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::setMuteFadeSpeed + SoundGroup::setMaxAudibleBehavior + SoundGroup::getMaxAudibleBehavior + SoundGroup::setMaxAudible + SoundGroup::getMaxAudible + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetMuteFadeSpeed(FMOD_SOUNDGROUP *soundgroup, float *speed) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getMuteFadeSpeed(speed); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the volume for a sound group, affecting all channels playing the sounds in this soundgroup. + + [PARAMETERS] + 'volume' A linear volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::getVolume + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_SetVolume(FMOD_SOUNDGROUP *soundgroup, float volume) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the volume for the sounds within a soundgroup. + + [PARAMETERS] + 'volume' Address of a variable to receive the soundgroup volume level, from 0.0 to 1.0 inclusive. 0.0 = silent, 1.0 = full volume. Default = 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::setVolume + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetVolume(FMOD_SOUNDGROUP *soundgroup, float *volume) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getVolume(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Stops all sounds within this soundgroup. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playSound + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_Stop(FMOD_SOUNDGROUP *soundgroup) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->stop(); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the name of the sound group. + + [PARAMETERS] + 'name' Address of a variable that receives the name of the sound group. + 'namelen' Length in bytes of the target buffer to receieve the string. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetName(FMOD_SOUNDGROUP *soundgroup, char *name, int namelen) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getName(name, namelen); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current number of sounds in this sound group. + + [PARAMETERS] + 'numsounds' Address of a variable to receive the number of sounds in this sound group. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + System::getMasterSoundGroup + SoundGroup::setMaxAudible + SoundGroup::getSound +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetNumSounds(FMOD_SOUNDGROUP *soundgroup, int *numsounds) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getNumSounds(numsounds); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to a sound from within a sound group. + + [PARAMETERS] + 'index' Index of the sound that is to be retrieved. + 'sound' Address of a variable to receieve a pointer to a Sound object. + + [RETURN_VALUE] + + [REMARKS] + Use SoundGroup::getNumSounds in conjunction with this function to enumerate all sounds in a sound group. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + System::createSound + SoundGroup::getNumSounds + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetSound(FMOD_SOUNDGROUP *soundgroup, int index, FMOD_SOUND **sound) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getSound(index, (FMOD::Sound **)sound); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of currently playing channels for the sound group. + + [PARAMETERS] + 'numplaying' Address of a variable to receive the number of actively playing channels from sounds in this sound group. + + [RETURN_VALUE] + + [REMARKS] + This routine returns the number of channels playing. If the sound group only has 1 sound, and that sound is playing twice, the figure returned will be 2. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetNumPlaying(FMOD_SOUNDGROUP *soundgroup, int *numplaying) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getNumPlaying(numplaying); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the SoundGroup object will store internally. Can be retrieved with SoundGroup::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the sound group object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using SoundGroup::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::getUserData + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_SetUserData(FMOD_SOUNDGROUP *soundgroup, void *userdata) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the SoundGroup::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the data specified with the SoundGroup::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + SoundGroup::setUserData + System::createSoundGroup + System::getMasterSoundGroup +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetUserData(FMOD_SOUNDGROUP *soundgroup, void **userdata) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_SoundGroup_GetMemoryInfo(FMOD_SOUNDGROUP *soundgroup, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::SoundGroup *_group = (FMOD::SoundGroup *)soundgroup; + + if (!_group) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _group->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Frees a DSP object. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + This will free the DSP object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createDSP + System::createDSPByType + System::getDSPHead + Channel::getDSPHead + ChannelGroup::getDSPHead + +] +*/ +FMOD_RESULT F_API FMOD_DSP_Release(FMOD_DSP *dsp) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->release(); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the parent System object that was used to create this object. + + [PARAMETERS] + 'system' Address of a variable that receives the System object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createDSP + System::createDSPByType + System::getDSPHead + Channel::getDSPHead + ChannelGroup::getDSPHead +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetSystemObject(FMOD_DSP *dsp, FMOD_SYSTEM **system) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getSystemObject((FMOD::System **)system); +} + + +/* +[API] +[ + [DESCRIPTION] + Adds the specified DSP unit as an input of the DSP object. + + [PARAMETERS] + 'target' The DSP unit to add as an input of the current unit. + 'connection' The connection between the 2 units. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + Adding a unit as an input means that there can be multiple units added to the target.
    + Inputs are automatically mixed together, then the mixed data is sent to the unit's output(s).
    + To find the number of inputs or outputs a unit has use DSP::getNumInputs or DSP::getNumOutputs.
    +
    + Note: The connection pointer retrieved here will become invalid if you disconnect the 2 dsp units that use it.
    + + [REMARKS] + If you want to add a unit as an output of another unit, then add 'this' unit as an input of that unit instead. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getNumInputs + DSP::getInput + DSP::getNumOutputs + DSP::disconnectFrom +] +*/ +FMOD_RESULT F_API FMOD_DSP_AddInput(FMOD_DSP *dsp, FMOD_DSP *target, FMOD_DSPCONNECTION **connection) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->addInput((FMOD::DSP *)target, (FMOD::DSPConnection **)connection); +} + + +/* +[API] +[ + [DESCRIPTION] + Disconnect the DSP unit from the specified target. + + [PARAMETERS] + 'target' The unit that this unit is to be removed from. Specify 0 or NULL to disconnect the unit from all outputs and inputs. + + [RETURN_VALUE] + + [REMARKS] + Note that when you disconnect a unit, it is up to you to reconnect the network so that data flow can continue.
    +
    + Important note: If you have a handle to the connection pointer that binds these 2 DSP units, then it will become invalid. The connection is then sent back to a freelist to be re-used again by a later addInput command.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::addInput + DSP::disconnectAll +] +*/ +FMOD_RESULT F_API FMOD_DSP_DisconnectFrom(FMOD_DSP *dsp, FMOD_DSP *target) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->disconnectFrom((FMOD::DSP *)target); +} + + +/* +[API] +[ + [DESCRIPTION] + Helper function to disconnect either all inputs or all outputs of a dsp unit. + + [PARAMETERS] + 'inputs' true = disconnect all inputs to this DSP unit. false = leave input connections alone. + 'outputs' true = disconnect all outputs to this DSP unit. false = leave output connections alone. + + [RETURN_VALUE] + + [REMARKS] + This function is optimized to be faster than disconnecting inputs and outputs manually one by one.
    +
    + Important note: If you have a handle to DSPConnection pointers that bind any of the inputs or outputs to this DSP unit, then they will become invalid. The connections are sent back to a freelist to be re-used again by a later addInput command.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::disconnectFrom +] +*/ +FMOD_RESULT F_API FMOD_DSP_DisconnectAll(FMOD_DSP *dsp, FMOD_BOOL inputs, FMOD_BOOL outputs) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->disconnectAll(inputs ? true : false, outputs ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Removes a unit from a DSP chain and connects the unit's input and output together after it is gone. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + This function is generally only used with units that have been added with System::addDSP or Channel::addDSP.
    + A unit that has been added in this way generally only has one input and one output, so this function assumes this and takes input 0 and connects it with output 0 after it has been removed, so that the data flow is not broken.
    +
    + Important note: If you have a handle to DSPConnection pointers that bind any of the inputs or outputs to this DSP unit, then they will become invalid. The connections are sent back to a freelist to be re-used again by a later addInput command.
    +
    + Note: If the unit has not been added with addDSP it will not restore links, and will just disconnect all inputs and outputs, making it equivalent to +
    +    disconnectAll(true, true).
    +    
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::addDSP + Channel::addDSP + ChannelGroup::addDSP +] +*/ +FMOD_RESULT F_API FMOD_DSP_Remove(FMOD_DSP *dsp) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->remove(); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of inputs connected to the DSP unit. + + [PARAMETERS] + 'numinputs' Address of a variable that receives the number of inputs connected to this unit. + + [RETURN_VALUE] + + [REMARKS] + Inputs are units that feed data to this unit. When there are multiple inputs, they are mixed together.
    +
    + Performance warning! Because this function needs to flush the dsp queue before it can determine how many units are available, this function may block significantly while the background mixer thread operates. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getNumOutputs + DSP::getInput +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetNumInputs(FMOD_DSP *dsp, int *numinputs) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getNumInputs(numinputs); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of outputs connected to the DSP unit. + + [PARAMETERS] + 'numoutputs' Address of a variable that receives the number of outputs connected to this unit. + + [RETURN_VALUE] + + [REMARKS] + Outputs are units that this unit feeds data to. When there are multiple outputs, the data is split and sent to each unit individually.
    +
    + Performance warning! Because this function needs to flush the dsp queue before it can determine how many units are available, this function may block significantly while the background mixer thread operates. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getNumInputs + DSP::getOutput +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetNumOutputs(FMOD_DSP *dsp, int *numoutputs) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getNumOutputs(numoutputs); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to a DSP unit which is acting as an input to this unit. + + [PARAMETERS] + 'index' Index of the input unit to retrieve. + 'input' Address of a variable that receieves the pointer to the desired input unit. + 'inputconnection' The connection between the 2 units. Optional. Specify 0 or NULL to ignore. + + + [RETURN_VALUE] + + [REMARKS] + An input is a unit which feeds audio data to this unit.
    + If there are more than 1 input to this unit, the inputs will be mixed, and the current unit processes the mixed result.
    + Find out the number of input units to this unit by calling DSP::getNumInputs.
    +
    + Performance warning! Because this function needs to flush the dsp queue before it can determine if the specified numerical input is available or not, this function may block significantly while the background mixer thread operates.
    +
    + Note: The connection pointer retrieved here will become invalid if you disconnect the 2 dsp units that use it.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getNumInputs + DSP::addInput + DSP::getOutput + DSPConnection::getMix + DSPConnection::setMix +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetInput(FMOD_DSP *dsp, int index, FMOD_DSP **input, FMOD_DSPCONNECTION **inputconnection) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getInput(index, (FMOD::DSP **)input, (FMOD::DSPConnection **)inputconnection); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a pointer to a DSP unit which is acting as an output to this unit. + + [PARAMETERS] + 'index' Index of the output unit to retrieve. + 'output' Address of a variable that receieves the pointer to the desired output unit. + 'outputconnection' The connection between the 2 units. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + An output is a unit which this unit will feed data too once it has processed its data.
    + Find out the number of output units to this unit by calling DSP::getNumOutputs.
    +
    + Performance warning! Because this function needs to flush the dsp queue before it can determine if the specified numerical output is available or not, this function may block significantly while the background mixer thread operates.
    +
    + Note: The connection pointer retrieved here will become invalid if you disconnect the 2 dsp units that use it.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getNumOutputs + DSP::addInput + DSP::getInput + DSPConnection::getMix + DSPConnection::setMix +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetOutput(FMOD_DSP *dsp, int index, FMOD_DSP **output, FMOD_DSPCONNECTION **outputconnection) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getOutput(index, (FMOD::DSP **)output, (FMOD::DSPConnection **)outputconnection); +} + + +/* +[API] +[ + [DESCRIPTION] + Enables or disables a unit for being processed. + + [PARAMETERS] + 'active' true = unit is activated, false = unit is deactivated. + + [RETURN_VALUE] + + [REMARKS] + This does not connect or disconnect a unit in any way, it just disables it so that it is not processed.
    + If a unit is disabled, and has inputs, they will also cease to be processed.
    + To disable a unit but allow the inputs of the unit to continue being processed, use DSP::setBypass instead. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getActive + DSP::setBypass +] +*/ +FMOD_RESULT F_API FMOD_DSP_SetActive(FMOD_DSP *dsp, FMOD_BOOL active) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->setActive(active ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the active state of a DSP unit. + + [PARAMETERS] + 'active' Address of a variable that receives the active state of the unit. true = unit is activated, false = unit is deactivated. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setActive + DSP::setBypass +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetActive(FMOD_DSP *dsp, FMOD_BOOL *active) +{ + FMOD_RESULT result; + bool active2; + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _dsp->getActive(&active2); + if (result == FMOD_OK) + { + if (active) + { + *active = active2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Enables or disables the read callback of a DSP unit so that it does or doesn't process the data coming into it.
    + A DSP unit that is disabled still processes its inputs, it will just be 'dry'. + + [PARAMETERS] + 'bypass' Boolean to cause the read callback of the DSP unit to be bypassed or not. Default = false. + + [RETURN_VALUE] + + [REMARKS] + If a unit is bypassed, it will still process its inputs.
    + To disable the unit and all of its inputs, use DSP::setActive instead.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getBypass + DSP::setActive +] +*/ +FMOD_RESULT F_API FMOD_DSP_SetBypass(FMOD_DSP *dsp, FMOD_BOOL bypass) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->setBypass(bypass ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the bypass state of the DSP unit. + + [PARAMETERS] + 'bypass' Address of a variable that receieves the bypass state for a DSP unit. true = unit is not processing audio data, false = unit is processing audio data. Default = false. + + [RETURN_VALUE] + + [REMARKS] + If a unit is bypassed, it will still process its inputs, unlike DSP::setActive (when set to false) which causes inputs to stop processing as well. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setBypass + DSP::setActive +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetBypass(FMOD_DSP *dsp, FMOD_BOOL *bypass) +{ + FMOD_RESULT result; + bool bypass2; + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _dsp->getBypass(&bypass2); + if (result == FMOD_OK) + { + if (bypass) + { + *bypass = bypass2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Enables or disables the DSP effect on the given speaker channel. This is used to reduce the overhead of DSP effect
    + by only applying the effect to the speaker channels where it is needed. + + [PARAMETERS] + 'speaker' The speaker channel that is being set. + 'active' Boolean to cause the DSP to be active/inactive on a given speaker channel. Default = true. + + [RETURN_VALUE] + + [REMARKS] + If a speaker channel is deactivated it will not have the DSP effect applied to it. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getSpeakerActive + DSP::setActive +] +*/ +FMOD_RESULT F_API FMOD_DSP_SetSpeakerActive(FMOD_DSP *dsp, FMOD_SPEAKER speaker, FMOD_BOOL active) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->setSpeakerActive(speaker, active ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the active state of the effect on the given speaker. + + [PARAMETERS] + 'speaker' The speaker channel that is being checked + 'active' Address of a variable that receieves the active state for a DSP unit. true = DSP unit is active on that speakerchannel, false = DSP unit is not active on that speaker channel. Default = true. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setSpeakerActive + DSP::getActive +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetSpeakerActive(FMOD_DSP *dsp, FMOD_SPEAKER speaker, FMOD_BOOL *active) +{ + FMOD_RESULT result; + bool active2; + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _dsp->getSpeakerActive(speaker, &active2); + if (result == FMOD_OK) + { + if (active) + { + *active = active2 ? 1 : 0; + } + } + + return result; +} + + +/* +[API] +[ + [DESCRIPTION] + Calls the DSP unit's reset function, which will clear internal buffers and reset the unit back to an initial state. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Calling this function is useful if the DSP unit relies on a history to process itself (ie an echo filter).
    + If you disconnected the unit and reconnected it to a different part of the network with a different sound, you would want to call this to reset the units state (ie clear and reset the echo filter) so that you dont get left over artifacts from the place it used to be connected. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_API FMOD_DSP_Reset(FMOD_DSP *dsp) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->reset(); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a DSP unit's parameter by index. To find out the parameter names and range, see the see also field. + + [PARAMETERS] + 'index' Parameter index for this unit. Find the number of parameters with DSP::getNumParameters. + 'value' Parameter value. The parameter properties can be retrieved with DSP::getParameterInfo. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getParameterInfo + DSP::getNumParameters + DSP::getParameter +] +*/ +FMOD_RESULT F_API FMOD_DSP_SetParameter(FMOD_DSP *dsp, int index, float value) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->setParameter(index, value); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves a DSP unit's parameter by index. To find out the parameter names and range, see the see also field. + + [PARAMETERS] + 'index' Parameter index for this unit. Find the number of parameters with DSP::getNumParameters. + 'value' Address of a variable that receives the parameter value. The parameter properties can be retrieved with DSP::getParameterInfo. + 'valuestr' Address of a variable that receives the string containing a formatted or more meaningful representation of the DSP parameter's value. For example if a switch parameter has on and off (0.0 or 1.0) it will display "ON" or "OFF" by using this parameter. + 'valuestrlen' Length of the user supplied memory in bytes that valuestr will write to. This will not exceed 16 bytes. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getParameterInfo + DSP::getNumParameters + DSP::setParameter +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetParameter(FMOD_DSP *dsp, int index, float *value, char *valuestr, int valuestrlen) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getParameter(index, value, valuestr, valuestrlen); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of parameters a DSP unit has to control its behaviour. + + [PARAMETERS] + 'numparams' Address of a variable that receives the number of parameters contained within this DSP unit. + + [RETURN_VALUE] + + [REMARKS] + Use this to enumerate all parameters of a DSP unit with DSP::getParameter and DSP::getParameterInfo. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setParameter + DSP::getParameter + DSP::getParameterInfo +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetNumParameters(FMOD_DSP *dsp, int *numparams) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getNumParameters(numparams); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve information about a specified parameter within the DSP unit. + + [PARAMETERS] + 'index' Parameter index for this unit. Find the number of parameters with DSP::getNumParameters. + 'name' Address of a variable that receives the name of the parameter. An example is "Gain". This is a maximum string length of 16bytes (append \0 in case the plugin has used all 16 bytes for the string). + 'label' Address of a variable that receives the label of the parameter (ie a parameter type that might go next to the parameter). An example is "dB". This is a maximum string length of 16bytes (append \0 in case the plugin has used all 16 bytes for the string). + 'description' Address of a variable that receives the more descriptive text about the parameter (ie for a tooltip). An example is "Controls the input level for the effect in decibels". + 'descriptionlen' Maximum length of user supplied description string in bytes that FMOD will write to. + 'min' Minimum range of the parameter. + 'max' Maximum range of the parameter. + + [RETURN_VALUE] + + [REMARKS] + Use DSP::getNumParameters to find out the number of parameters for this DSP unit. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setParameter + DSP::getParameter + DSP::getNumParameters +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetParameterInfo(FMOD_DSP *dsp, int index, char *name, char *label, char *description, int descriptionlen, float *min, float *max) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getParameterInfo(index, name, label, description, descriptionlen, min, max); +} + + +/* +[API] +[ + [DESCRIPTION] + Display or hide a DSP unit configuration dialog box inside the target window. + + [PARAMETERS] + 'hwnd' Target HWND in windows to display configuration dialog. + 'show' true = show dialog box inside target hwnd. false = remove dialog from target hwnd. + + [RETURN_VALUE] + + [REMARKS] + Dialog boxes are used by DSP plugins that prefer to use a graphical user interface to modify their parameters rather than using the other method of enumerating the parameters and using DSP::setParameter.
    + These are usually VST plugins. FMOD Ex plugins do not have configuration dialog boxes. + To find out what size window to create to store the configuration screen, use DSP::getInfo where you can get the width and height. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getInfo + DSP::setParameter + DSP::getParameter +] +*/ +FMOD_RESULT F_API FMOD_DSP_ShowConfigDialog(FMOD_DSP *dsp, void *hwnd, FMOD_BOOL show) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->showConfigDialog(hwnd, show ? true : false); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves information about the current DSP unit, including name, version, default channels and width and height of configuration dialog box if it exists. + + [PARAMETERS] + 'name' Address of a variable that receives the name of the unit. This will be a maximum of 32bytes. If the DSP unit has filled all 32 bytes with the name with no terminating \0 null character it is up to the caller to append a null character. Optional. Specify 0 or NULL to ignore. + 'version' Address of a variable that receives the version number of the DSP unit. Version number is usually formated as hex AAAABBBB where the AAAA is the major version number and the BBBB is the minor version number. Optional. Specify 0 or NULL to ignore. + 'channels' Address of a variable that receives the number of channels the unit was initialized with. 0 means the plugin will process whatever number of channels is currently in the network. >0 would be mostly used if the unit is a unit that only generates sound, or is not flexible enough to take any number of input channels. Optional. Specify 0 or NULL to ignore. + 'configwidth' Address of a variable that receives the width of an optional configuration dialog box that can be displayed with DSP::showConfigDialog. 0 means the dialog is not present. Optional. Specify 0 or NULL to ignore. + 'configheight' Address of a variable that receives the height of an optional configuration dialog box that can be displayed with DSP::showConfigDialog. 0 means the dialog is not present. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::showConfigDialog +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetInfo(FMOD_DSP *dsp, char *name, unsigned int *version, int *channels, int *configwidth, int *configheight) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getInfo(name, version, channels, configwidth, configheight); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the pre-defined type of a FMOD registered DSP unit. + + [PARAMETERS] + 'type' Address of a variable to recieve the FMOD dsp type. + + [RETURN_VALUE] + + [REMARKS] + This is only valid for built in FMOD effects. Any user plugins will simply return FMOD_DSP_TYPE_UNKNOWN. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_TYPE +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetType(FMOD_DSP *dsp, FMOD_DSP_TYPE *type) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getType(type); +} + + +/* +[API] +[ + [DESCRIPTION] + If a DSP unit is to be played on a channel with System::playDSP, this will set the defaults for frequency, volume, pan and more for the channel. + + [PARAMETERS] + 'frequency' Default playback frequency for the DSP unit, in hz. (ie 44100hz). + 'volume' Default volume for the DSP unit. 0.0 to 1.0. 0.0 = Silent, 1.0 = full volume. Default = 1.0. + 'pan' Default pan for the DSP unit. -1.0 to +1.0. -1.0 = Full left, 0.0 = center, 1.0 = full right. Default = 0.0. + 'priority' Default priority for the DSP unit when played on a channel. 0 to 256. 0 = most important, 256 = least important. Default = 128. + + [RETURN_VALUE] + + [REMARKS] + There are no 'ignore' values for these parameters. Use DSP::getDefaults if you want to change only 1 and leave others unaltered. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::playDSP + DSP::getDefaults +] +*/ +FMOD_RESULT F_API FMOD_DSP_SetDefaults(FMOD_DSP *dsp, float frequency, float volume, float pan, int priority) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->setDefaults(frequency, volume, pan, priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the default frequency, volume, pan and more for this DSP unit if it was to ever be played on a channel using System::playDSP. + + [PARAMETERS] + 'frequency' Address of a variable that receives the default frequency for the DSP unit. Optional. Specify 0 or NULL to ignore. + 'volume' Address of a variable that receives the default volume for the DSP unit. Result will be from 0.0 to 1.0. 0.0 = Silent, 1.0 = full volume. Default = 1.0. Optional. Specify 0 or NULL to ignore. + 'pan' Address of a variable that receives the default pan for the DSP unit. Result will be from -1.0 to +1.0. -1.0 = Full left, 0.0 = center, 1.0 = full right. Default = 0.0. Optional. Specify 0 or NULL to ignore. + 'priority' Address of a variable that receives the default priority for the DSP unit when played on a channel. Result will be from 0 to 256. 0 = most important, 256 = least important. Default = 128. Optional. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setDefaults + System::playDSP +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetDefaults(FMOD_DSP *dsp, float *frequency, float *volume, float *pan, int *priority) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getDefaults(frequency, volume, pan, priority); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the DSP object will store internally. Can be retrieved with DSP::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the DSP object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using DSP::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::getUserData +] +*/ +FMOD_RESULT F_API FMOD_DSP_SetUserData(FMOD_DSP *dsp, void *userdata) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the DSP::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the user data specified with the DSP::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSP::setUserData +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetUserData(FMOD_DSP *dsp, void **userdata) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_DSP_GetMemoryInfo(FMOD_DSP *dsp, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::DSP *_dsp = (FMOD::DSP *)dsp; + + if (!_dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dsp->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the DSP unit that is the input of this connection. + + [PARAMETERS] + 'input' Address of a pointer that receives the pointer to the DSP unit that is the input of this connection. + + [RETURN_VALUE] + + [REMARKS] + A DSPConnection joins 2 DSP units together (think of it as the line between 2 circles).
    + Each DSPConnection has 1 input and 1 output.
    +
    + Note! If a DSP::addInput just occured, the connection might not be ready because the DSP system is still queued to connect in the background. If so the function will return FMOD_ERR_NOTREADY and the input will be null. Poll until it is ready.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::getOutput + DSP::addInput +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_GetInput(FMOD_DSPCONNECTION *dspconnection, FMOD_DSP **input) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->getInput((FMOD::DSP **)input); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the DSP unit that is the output of this connection. + + [PARAMETERS] + 'output' Address of a pointer that receives the pointer to the DSP unit that is the output of this connection. + + [RETURN_VALUE] + + [REMARKS] + A DSPConnection joins 2 DSP units together (think of it as the line between 2 circles).
    + Each DSPConnection has 1 input and 1 output.
    +
    + Note! If a DSP::addInput just occured, the connection might not be ready because the DSP system is still queued to connect in the background. If so the function will return FMOD_ERR_NOTREADY and the input will be null. Poll until it is ready.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::getInput + DSP::addInput +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_GetOutput(FMOD_DSPCONNECTION *dspconnection, FMOD_DSP **output) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->getOutput((FMOD::DSP **)output); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the volume of the connection so that the input is scaled by this value before being passed to the output. + + [PARAMETERS] + 'volume' Volume or mix level of the connection. 0.0 = silent, 1.0 = full volume. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::getMix + DSP::getInput + DSP::getOutput +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_SetMix(FMOD_DSPCONNECTION *dspconnection, float volume) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->setMix(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the volume of the connection - the scale level of the input before being passed to the output. + + [PARAMETERS] + 'volume' Address of a variable to receive the volume or mix level of the specified input. 0.0 = silent, 1.0 = full volume. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::setMix + DSP::getInput + DSP::getOutput +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_GetMix(FMOD_DSPCONNECTION *dspconnection, float *volume) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->getMix(volume); +} + + +/* +[API] +[ + [DESCRIPTION] + For a particular speaker, the levels of the incoming channels of the connection are set so that they will be scaled before being passed to the output. + + [PARAMETERS] + 'speaker' The target speaker to modify the levels for. This can be cast to an integer if you are using a device with more than the pre-defined speaker range. + 'levels' An array of floating point numbers from 0.0 to 1.0 representing the volume of each input channel of a sound. See remarks for more. + 'numlevels' The number of floats within the levels parameter being passed to this function. In the case of the above mono or stereo sound, 1 or 2 could be used respectively. If the sound being played was an 8 channel multichannel sound then 8 levels would be used. + + [RETURN_VALUE] + + [REMARKS] + As an example of usage of this function, if the sound played on this speaker was mono, only 1 level would be needed.
    + If the sound played on this channel was stereo, then an array of 2 floats could be specified. For example { 0, 1 } on a channel playing a stereo sound would mute the left part of the stereo sound when it is played on this speaker.
    +
    + Note! To conserve memory the levels are converted from floating point to 16bit integers (4.12 fixed point). This means when using DSPConnection::getLevels the values may not come back exactly as they were set. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::getLevels + DSPConnection::getInput + DSPConnection::getOutput + DSPConnection::setMix +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_SetLevels(FMOD_DSPCONNECTION *dspconnection, FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->setLevels(speaker, levels, numlevels); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the speaker mix for a DSP connection for a particular output speaker. + + [PARAMETERS] + 'speaker' The target speaker to get the levels from. This can be cast to an integer if you are using a device with more than the pre-defined speaker range. + 'levels' Address of an array of floating point numbers to get the speaker levels of an input. + 'numlevels' The number of floats within the levels parameter being passed to this function. In the case of the above mono or stereo sound, 1 or 2 could be used respectively. If the sound being played was an 8 channel multichannel sound then 8 levels would be used. + + [RETURN_VALUE] + + [REMARKS] + Note! To conserve memory the stored levels are converted from floating point to 16bit integers (4.12 fixed point). This means when using DSP::getInputLevels the values may not come back exactly as they were set. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::setLevels + DSPConnection::getInput + DSPConnection::getOutput + DSPConnection::setMix +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_GetLevels(FMOD_DSPCONNECTION *dspconnection, FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->getLevels(speaker, levels, numlevels); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the DSPConnection object will store internally. Can be retrieved with DSPConnection::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the DSPConnection object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::getUserData +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_SetUserData(FMOD_DSPCONNECTION *dspconnection, void *userdata) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->setUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the DSPConnection object will store internally. Can be retrieved with DSPConnection::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the DSPConnection object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + DSPConnection::getUserData +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_GetUserData(FMOD_DSPCONNECTION *dspconnection, void **userdata) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->getUserData(userdata); +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_DSPConnection_GetMemoryInfo(FMOD_DSPCONNECTION *dspconnection, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD::DSPConnection *_dspconnection = (FMOD::DSPConnection *)dspconnection; + + if (!_dspconnection) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _dspconnection->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +} + + +/* +[API] +[ + [DESCRIPTION] + Frees a geometry object and releases its memory. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_API FMOD_Geometry_Release(FMOD_GEOMETRY *geometry) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->release(); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Adds a polygon to an existing geometry object. + + [PARAMETERS] + 'directocclusion' Occlusion value from 0.0 to 1.0 which affects volume or audible frequencies. 0.0 = The polygon does not occlude volume or audible frequencies (sound will be fully audible), 1.0 = The polygon fully occludes (sound will be silent). + 'reverbocclusion' Occlusion value from 0.0 to 1.0 which affects the reverb mix. 0.0 = The polygon does not occlude reverb (reverb reflections still travel through this polygon), 1.0 = The polyfully fully occludes reverb (reverb reflections will be silent through this polygon). + 'doublesided' Description of polygon if it is double sided or single sided. true = polygon is double sided, false = polygon is single sided, and the winding of the polygon (which determines the polygon's normal) determines which side of the polygon will cause occlusion. + 'numvertices' Number of vertices in this polygon. This must be at least 3. Polygons (more than 3 sides) are supported. + 'vertices' A pointer to an array of vertices located in object space, with the count being the number of vertices described using the numvertices parameter. + 'polygonindex' Address of a variable to receieve the polygon index for this object. This index can be used later with other per polygon based geometry functions. + + [RETURN_VALUE] + + [REMARKS] + Note!
    + - All vertices must lay in the same plane otherwise behaviour may be unpredictable.
    + - The polygon is assumed to be convex. A non convex polygon will produce unpredictable behaviour.
    + - Polygons with zero area will be ignored.
    +
    + Vertices of an object are in object space, not world space, and so are relative to the position, or center of the object. See Geometry::setPosition. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getNumPolygons + Geometry::setPosition + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_AddPolygon(FMOD_GEOMETRY *geometry, float directocclusion, float reverbocclusion, FMOD_BOOL doublesided, int numvertices, const FMOD_VECTOR *vertices, int *polygonindex) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->addPolygon(directocclusion, reverbocclusion, doublesided ? true : false, numvertices, vertices, polygonindex); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the number of polygons stored within this geometry object. + + [PARAMETERS] + 'numpolygons' Address of a variable to receive the number of polygons within this object. + + [RETURN_VALUE] + + [REMARKS] + Polygons are added to a geometry object via Geometry::addPolygon. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::AddPolygon +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetNumPolygons(FMOD_GEOMETRY *geometry, int *numpolygons) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getNumPolygons(numpolygons); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the maximum number of polygons and vertices allocatable for this object. This is not the number of polygons or vertices currently present.
    + The maximum number was set with System::createGeometry. + + [PARAMETERS] + 'maxpolygons' Address of a variable to receieve the maximum possible number of polygons in this object. + 'maxvertices' Address of a variable to receieve the maximum possible number of vertices in this object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createGeometry + System::loadGeometry +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetMaxPolygons(FMOD_GEOMETRY *geometry, int *maxpolygons, int *maxvertices) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getMaxPolygons(maxpolygons, maxvertices); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Gets the number of vertices in a polygon which is part of the geometry object. + + [PARAMETERS] + 'index' Polygon index. This must be in the range of 0 to Geometry::getNumPolygons minus 1. + 'numvertices' Address of a variable to receive the number of vertices for the selected polygon. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getNumPolygons +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetPolygonNumVertices(FMOD_GEOMETRY *geometry, int index, int *numvertices) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getPolygonNumVertices(index, numvertices); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Alters the position of a polygon's vertex inside a geometry object. + + [PARAMETERS] + 'index' Polygon index. This must be in the range of 0 to Geometry::getNumPolygons minus 1. + 'vertexindex' Vertex index inside the polygon. This must be in the range of 0 to Geometry::getPolygonNumVertices minus 1. + 'vertex' Address of an FMOD_VECTOR which holds the new vertex location. + + [RETURN_VALUE] + + [REMARKS] + Note! There may be some significant overhead with this function as it may cause some reconfiguration of internal data structures used to speed up sound-ray testing.
    + You may get better results if you want to modify your object by using Geometry::setPosition, Geometry::setScale and Geometry::setRotation.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getPolygonNumVertices + Geometry::getPolygonNumVertices + Geometry::setPosition + Geometry::setScale + Geometry::setRotation + Geometry::getNumPolygons + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetPolygonVertex(FMOD_GEOMETRY *geometry, int index, int vertexindex, const FMOD_VECTOR *vertex) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setPolygonVertex(index, vertexindex, vertex); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the position of the vertex inside a geometry object. + + [PARAMETERS] + 'index' Polygon index. This must be in the range of 0 to Geometry::getNumPolygons minus 1. + 'vertexindex' Vertex index inside the polygon. This must be in the range of 0 to Geometry::getPolygonNumVertices minus 1. + 'vertex' Address of an FMOD_VECTOR structure which will receive the new vertex location in object space. + + [RETURN_VALUE] + + [REMARKS] + Vertices are relative to the position of the object. See Geometry::setPosition. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getPolygonNumVertices + Geometry::setPosition + Geometry::getNumPolygons + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetPolygonVertex(FMOD_GEOMETRY *geometry, int index, int vertexindex, FMOD_VECTOR *vertex) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getPolygonVertex(index, vertexindex, vertex); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets individual attributes for each polygon inside a geometry object. + + [PARAMETERS] + 'index' Polygon index inside the object. + 'directocclusion' Occlusion value from 0.0 to 1.0 which affects volume or audible frequencies. 0.0 = The polygon does not occlude volume or audible frequencies (sound will be fully audible), 1.0 = The polygon fully occludes (sound will be silent). + 'reverbocclusion' Occlusion value from 0.0 to 1.0 which affects the reverb mix. 0.0 = The polygon does not occlude reverb (reverb reflections still travel through this polygon), 1.0 = The polyfully fully occludes reverb (reverb reflections will be silent through this polygon). + 'doublesided' Description of polygon if it is double sided or single sided. true = polygon is double sided, false = polygon is single sided, and the winding of the polygon (which determines the polygon's normal) determines which side of the polygon will cause occlusion. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getPolygonAttributes + Geometry::getNumPolygons +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetPolygonAttributes(FMOD_GEOMETRY *geometry, int index, float directocclusion, float reverbocclusion, FMOD_BOOL doublesided) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setPolygonAttributes(index, directocclusion, reverbocclusion, doublesided ? true : false); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the attributes for a particular polygon inside a geometry object. + + [PARAMETERS] + 'index' Polygon index inside the object. + 'directocclusion' Address of a variable to receieve the occlusion value from 0.0 to 1.0 which affects volume or audible frequencies. 0.0 = The polygon does not occlude volume or audible frequencies (sound will be fully audible), 1.0 = The polygon fully occludes (sound will be silent). + 'reverbocclusion' Address of a variable to receieve the occlusion value from 0.0 to 1.0 which affects the reverb mix. 0.0 = The polygon does not occlude reverb (reverb reflections still travel through this polygon), 1.0 = The polyfully fully occludes reverb (reverb reflections will be silent through this polygon). + 'doublesided' Address of a variable to receieve the description of polygon if it is double sided or single sided. true = polygon is double sided, false = polygon is single sided, and the winding of the polygon (which determines the polygon's normal) determines which side of the polygon will cause occlusion. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getPolygonAttributes + Geometry::getNumPolygons +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetPolygonAttributes(FMOD_GEOMETRY *geometry, int index, float *directocclusion, float *reverbocclusion, FMOD_BOOL *doublesided) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD_RESULT result; + bool doublesided2; + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _geometry->getPolygonAttributes(index, directocclusion, reverbocclusion, &doublesided2); + if (result == FMOD_OK) + { + if (doublesided) + { + *doublesided = doublesided2 ? 1 : 0; + } + } + + return result; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Enables or disables an object from being processed in the geometry engine. + + [PARAMETERS] + 'active' true = active, false = not active. Default = true. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getActive +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetActive(FMOD_GEOMETRY *geometry, FMOD_BOOL active) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setActive(active ? true : false); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user set active state of the geometry object. + + [PARAMETERS] + 'active' Address of a variable to receive the active state of the object. true = active, false = not active. Default = true. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::setActive +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetActive(FMOD_GEOMETRY *geometry, FMOD_BOOL *active) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD_RESULT result; + bool active2; + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _geometry->getActive(&active2); + if (result == FMOD_OK) + { + if (active) + { + *active = active2 ? 1 : 0; + } + } + + return result; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the orientation of the geometry object. + + [PARAMETERS] + 'forward' The forwards orientation of the geometry object. This vector must be of unit length and perpendicular to the up vector. You can specify 0 or NULL to not update the forwards orientation of the geometry object. + 'up' The upwards orientation of the geometry object. This vector must be of unit length and perpendicular to the forwards vector. You can specify 0 or NULL to not update the upwards orientation of the geometry object. + + [RETURN_VALUE] + + [REMARKS] + See remarks in System::set3DListenerAttributes for more description on forward and up vectors. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getRotation + System::set3DListenerAttributes + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetRotation(FMOD_GEOMETRY *geometry, const FMOD_VECTOR *forward, const FMOD_VECTOR *up) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setRotation(forward, up); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the orientation of the geometry object. + + [PARAMETERS] + 'forward' Address of a variable that receives the forwards orientation of the geometry object. Specify 0 or NULL to ignore. + 'up' Address of a variable that receives the upwards orientation of the geometry object. Specify 0 or NULL to ignore. + + [RETURN_VALUE] + + [REMARKS] + See remarks in System::set3DListenerAttributes for more description on forward and up vectors. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::setRotation + System::set3DListenerAttributes + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetRotation(FMOD_GEOMETRY *geometry, FMOD_VECTOR *forward, FMOD_VECTOR *up) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getRotation(forward, up); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the position of the object in world space, which is the same space FMOD sounds and listeners reside in. + + [PARAMETERS] + 'position' Pointer to a vector containing the 3d position of the object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getPosition + Geometry::setRotation + Geometry::setScale + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetPosition(FMOD_GEOMETRY *geometry, const FMOD_VECTOR *position) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setPosition(position); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the position of the object in 3D world space. + + [PARAMETERS] + 'position' Address of a variable to receive the 3d position of the object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::setPosition + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetPosition(FMOD_GEOMETRY *geometry, FMOD_VECTOR *position) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getPosition(position); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the relative scale vector of the geometry object. An object can be scaled/warped in all 3 dimensions separately using the vector without having to modify polygon data. + + [PARAMETERS] + 'scale' The scale vector of the object. Default = 1.0, 1.0, 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getScale + Geometry::setRotation + Geometry::setPosition + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetScale(FMOD_GEOMETRY *geometry, const FMOD_VECTOR *scale) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setScale(scale); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the relative scale vector of the geometry object. An object can be scaled/warped in all 3 dimensions separately using the vector. + + [PARAMETERS] + 'scale' Address of a variable to receieve the scale vector of the object. Default = 1.0, 1.0, 1.0. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::setScale + FMOD_VECTOR +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetScale(FMOD_GEOMETRY *geometry, FMOD_VECTOR *scale) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getScale(scale); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Saves the geometry object as a serialized binary block, to a user memory buffer. This can then be saved to a file if required and loaded later with System::loadGeometry. + + [PARAMETERS] + 'data' Address of a variable to receive the serialized geometry object. Specify 0 or NULL to have the datasize parameter return the size of the memory required for this saved object. + 'datasize' Address of a variable to receive the size in bytes required to save this object when 'data' parameter is 0 or NULL. + + [RETURN_VALUE] + + [REMARKS] + To use this function you will normally need to call it twice. Once to get the size of the data, then again to write the data to your pointer. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::loadGeometry + System::createGeometry +] +*/ +FMOD_RESULT F_API FMOD_Geometry_Save(FMOD_GEOMETRY *geometry, void *data, int *datasize) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->save(data, datasize); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the Geometry object will store internally. Can be retrieved with Geometry::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the Geometry object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using Geometry::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::getUserData +] +*/ +FMOD_RESULT F_API FMOD_Geometry_SetUserData(FMOD_GEOMETRY *geometry, void *userdata) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->setUserData(userdata); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the Geometry::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the data specified with the Geometry::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Geometry::setUserData +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetUserData(FMOD_GEOMETRY *geometry, void **userdata) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getUserData(userdata); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_Geometry_GetMemoryInfo(FMOD_GEOMETRY *geometry, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD::Geometry *_geometry = (FMOD::Geometry *)geometry; + + if (!_geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _geometry->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Releases the memory for a reverb object and makes it inactive. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + If no reverb objects are created, the ambient reverb will be the only audible reverb. By default this ambient reverb setting is set to OFF. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createReverb + System::setReverbAmbientProperties +] +*/ +FMOD_RESULT F_API FMOD_Reverb_Release(FMOD_REVERB *reverb) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->release(); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets the 3d properties of a 'virtual' reverb object. + + [PARAMETERS] + 'position' Pointer to a vector containing the 3d position of the center of the reverb in 3d space. Default = { 0,0,0 }. + 'mindistance' The distance from the centerpoint that the reverb will have full effect at. Default = 0.0. + 'maxdistance' The distance from the centerpoint that the reverb will not have any effect. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + The 3D reverb object is a sphere having 3D attributes (position, minimum distance, maximum distance) and reverb properties.
    + The properties and 3D attributes of all reverb objects collectively determine, along with the listener's position, the settings of and input gains into a single 3D reverb DSP.
    + Please note that this only applies to software channels. When the listener is within the sphere of effect of one or more 3d reverbs, the listener's 3D reverb properties are + a weighted combination of such 3d reverbs. When the listener is outside all of the reverbs, the 3D reverb setting is set to the default ambient reverb setting.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::get3DAttributes + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_Reverb_Set3DAttributes(FMOD_REVERB *reverb, const FMOD_VECTOR *position, float mindistance, float maxdistance) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->set3DAttributes(position, mindistance, maxdistance); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the 3d attributes of a Reverb object. + + [PARAMETERS] + 'position' Address of a variable that will receive the 3d position of the center of the reverb in 3d space. Default = { 0,0,0 }. + 'mindistance' Address of a variable that will receive the distance from the centerpoint that the reverb will have full effect at. Default = 0.0. + 'maxdistance' Address of a variable that will receive the distance from the centerpoint that the reverb will not have any effect. Default = 0.0. + + [RETURN_VALUE] + + [REMARKS] + The 3D reverb object is a sphere having 3D attributes (position, minimum distance, maximum distance) and reverb properties.
    + The properties and 3D attributes of all reverb objects collectively determine, along with the listener's position, the settings of and input gains into a single 3D reverb DSP.
    + Please note that this only applies to software channels. When the listener is within the sphere of effect of one or more 3d reverbs, the listener's 3D reverb properties are + a weighted combination of such 3d reverbs. When the listener is outside all of the reverbs, the 3D reverb setting is set to the default ambient reverb setting.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::set3DAttributes + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_Reverb_Get3DAttributes(FMOD_REVERB *reverb, FMOD_VECTOR *position, float *mindistance, float *maxdistance) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->get3DAttributes(position, mindistance, maxdistance); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets reverb parameters for the current reverb object.
    + Reverb parameters can be set manually, or automatically using the pre-defined presets given in the fmod.h header. + + [PARAMETERS] + 'properties' Address of an FMOD_REVERB_PROPERTIES structure which defines the attributes for the reverb. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_REVERB_PROPERTIES + Reverb::getProperties + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_Reverb_SetProperties(FMOD_REVERB *reverb, const FMOD_REVERB_PROPERTIES *properties) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->setProperties(properties); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current reverb environment. + + [PARAMETERS] + 'properties' Address of a variable that receives the current reverb environment description. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::setProperties + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_Reverb_GetProperties(FMOD_REVERB *reverb, FMOD_REVERB_PROPERTIES *properties) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->getProperties(properties); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Disables or enables a reverb object so that it does or does not contribute to the 3d scene. + + [PARAMETERS] + 'active' true = active, false = not active. Default = true. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::setActive + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_Reverb_SetActive(FMOD_REVERB *reverb, FMOD_BOOL active) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->setActive(active ? true : false); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the active state of the reverb object. + + [PARAMETERS] + 'active' Address of a variable to receive the current active state of the reverb object. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::setActive + System::createReverb +] +*/ +FMOD_RESULT F_API FMOD_Reverb_GetActive(FMOD_REVERB *reverb, FMOD_BOOL *active) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD_RESULT result; + bool active2; + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = _reverb->getActive(&active2); + if (result == FMOD_OK) + { + if (active) + { + *active = active2 ? 1 : 0; + } + } + + return result; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Sets a user value that the Reverb object will store internally. Can be retrieved with Reverb::getUserData. + + [PARAMETERS] + 'userdata' Address of user data that the user wishes stored within the Reverb object. + + [RETURN_VALUE] + + [REMARKS] + This function is primarily used in case the user wishes to 'attach' data to an FMOD object.
    + It can be useful if an FMOD callback passes an object of this type as a parameter, and the user does not know which object it is (if many of these types of objects exist). Using Reverb::getUserData would help in the identification of the object. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::getUserData +] +*/ +FMOD_RESULT F_API FMOD_Reverb_SetUserData(FMOD_REVERB *reverb, void *userdata) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->setUserData(userdata); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieves the user value that that was set by calling the Reverb::setUserData function. + + [PARAMETERS] + 'userdata' Address of a pointer that receives the data specified with the Reverb::setUserData function. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Reverb::setUserData +] +*/ +FMOD_RESULT F_API FMOD_Reverb_GetUserData(FMOD_REVERB *reverb, void **userdata) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->getUserData(userdata); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Retrieve detailed memory usage information about this object. + + [PARAMETERS] + 'memorybits' Memory usage bits for FMOD Ex. See FMOD_MEMBITS. + 'event_memorybits' Memory usage bits for FMOD Event System. See FMOD_EVENT_MEMBITS. + 'memoryused' Optional. Specify 0 to ignore. Address of a variable to receive how much memory is being used by this object given the specified "memorybits" and "event_memorybits". + 'memoryused_details' Optional. Specify 0 to ignore. Address of a user-allocated FMOD_MEMORY_USAGE_DETAILS structure to be filled with detailed memory usage information about this object. + + [RETURN_VALUE] + + [REMARKS] + See System::getMemoryInfo for more details. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_MEMBITS + FMOD_EVENT_MEMBITS + FMOD_MEMORY_USAGE_DETAILS + System::getMemoryInfo +] +*/ +FMOD_RESULT F_API FMOD_Reverb_GetMemoryInfo(FMOD_REVERB *reverb, unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_SFXREVERB + FMOD::Reverb *_reverb = (FMOD::Reverb *)reverb; + + if (!_reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + return _reverb->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + diff --git a/src/fmod_3d.h b/src/fmod_3d.h new file mode 100755 index 0000000..e7563a7 --- /dev/null +++ b/src/fmod_3d.h @@ -0,0 +1,285 @@ +#ifndef _FMOD_3D_H +#define _FMOD_3D_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +#include + +/* + CONSTANTS +*/ + +#define FMOD_3D_MAXLISTENERS 4 + +/* + MACROS +*/ + +#define FMOD_Vector_CrossProduct(_a, _b, _r) \ + (_r)->x = ((_a)->y * (_b)->z) - ((_a)->z * (_b)->y); \ + (_r)->y = ((_a)->z * (_b)->x) - ((_a)->x * (_b)->z); \ + (_r)->z = ((_a)->x * (_b)->y) - ((_a)->y * (_b)->x); + +#define FMOD_Vector_Subtract(_a, _b, _r) \ + (_r)->x = (_a)->x - (_b)->x; \ + (_r)->y = (_a)->y - (_b)->y; \ + (_r)->z = (_a)->z - (_b)->z; + +#define FMOD_Vector_Add(_a, _b, _r) \ + (_r)->x = (_a)->x + (_b)->x; \ + (_r)->y = (_a)->y + (_b)->y; \ + (_r)->z = (_a)->z + (_b)->z; + +#define FMOD_Vector_Set(_r, _x, _y, _z) \ + (_r)->x = (_x); \ + (_r)->y = (_y); \ + (_r)->z = (_z); + +#define FMOD_Vector_Copy(_a, _r) \ + (_r)->x = (_a)->x; \ + (_r)->y = (_a)->y; \ + (_r)->z = (_a)->z; + +#define FMOD_Vector_DotProduct(_a, _b) \ + (((_a)->x * (_b)->x) + ((_a)->y * (_b)->y) + ((_a)->z * (_b)->z)) + +#define FMOD_Vector_DotProduct64(_a, _b) \ + FMOD_SCALEDOWN64( ((FMOD_SINT64)((_a)->x) * (FMOD_SINT64)((_b)->x)) + ((FMOD_SINT64)((_a)->y) * (FMOD_SINT64)((_b)->y)) + ((FMOD_SINT64)((_a)->z) * (FMOD_SINT64)((_b)->z)) ) + +#define FMOD_Vector_Scale(_a, _s, _r) \ + (_r)->x = ((_a)->x * (_s)); \ + (_r)->y = ((_a)->y * (_s)); \ + (_r)->z = ((_a)->z * (_s)); + +static FMOD_INLINE void FMOD_Vector_AxisRotate(FMOD_VECTOR *in,FMOD_VECTOR *at,FMOD_VECTOR *up,float theta) +{ + float a,b,c,u,v,w,x,y,z; + float cost,sint; + + a = at->x; + b = at->y; + c = at->z; + + u = up->x; + v = up->y; + w = up->z; + + x = in->x; + y = in->y; + z = in->z; + + cost = FMOD_COS(theta); + sint = FMOD_SIN(theta); + + in->x=(a*(v*v+w*w)+u*(-b*v-c*w+u*x+v*y+w*z)+((x-a)*(v*v+w*w)+u*(b*v+c*w-v*y-w*z))*cost+FMOD_SQRT(u*u+v*v+w*w)*(b*w-c*v-w*y+v*z)*sint)/(u*u+v*v+w*w); + in->y=(b*(u*u+w*w)+v*(-a*u-c*w+u*x+v*y+w*z)+((y-b)*(u*u+w*w)+v*(a*u+c*w-u*x-w*z))*cost+FMOD_SQRT(u*u+v*v+w*w)*(-a*w+c*u+w*x-u*z)*sint)/(u*u+v*v+w*w); + in->z=(c*(u*u+v*v)+w*(-a*u-b*v+u*x+v*y+w*z)+((z-c)*(u*u+v*v)+w*(a*u+b*v-u*x-v*y))*cost+FMOD_SQRT(u*u+v*v+w*w)*(a*v-b*u-v*x+u*y)*sint)/(u*u+v*v+w*w); +} + +static FMOD_INLINE int FMOD_Cart2Angle(int y, int x) +{ + int coeff_1, coeff_2, abs_y; + int r, angle; + + #define SHIFT 10 + #define M_PII (int)(3.1415926535897932384626433832795f * (float)(1<= 0) + { + r = (x - abs_y) / ((x + abs_y) >> SHIFT); + angle = coeff_1 - ((coeff_1 * r) >> SHIFT); + } + else + { + r = (x + abs_y) / ((abs_y - x) >> SHIFT); + angle = coeff_2 - ((coeff_1 * r) >> SHIFT); + } + + if (y < 0) + { + angle = -angle; /* negate if in quad III or IV */ + } + + angle *= 180; + angle /= M_PII; + angle = (angle < 0) ? angle + 360 : (angle >= 360) ? angle - 360 : angle; + + return angle; +} + + +/* + Angle Sort Functions + ==================== + Functions for quick sorting and comparison of 2D vector angles. + Values are in the range [0,8) and correspond to the angles shown below: + + y + 0.0 | 2.0 + .-+-. + ---+ + +--- x + .-+-. + 6.0 | 4.0 +*/ + +#define FMOD_ANGLESORT_REVOLUTION 8.0f +#define FMOD_ANGLESORT_STRAIGHT_TOLERANCE 0.002f /* ~0.1 degrees */ + +/* + Return an angle in the range [0,8) from cartesian coordinates. (vector doesn't need to be normalised) +*/ +static FMOD_INLINE float FMOD_AngleSort_GetValue(float x, float y) +{ + if(x == 0.0f && y == 0.0f) + { + return 0.0f; + } + + float abs_y = FMOD_ABS(y); + float abs_x = FMOD_ABS(x); + float value; + + if(abs_x <= abs_y) + { + /* + Top and bottom sides of square. + The angle is calculated for positive 'y' and then mirrored if y is negative. + (-1, 1) => 0; (1, 1) => 2 + */ + value = x / abs_y + 1.0f; + + if(y < 0.0f) + { + /* values 0 -> 2 (front) map to values 6 -> 4 (back) */ + return 6.0f - value; + } + else + { + return value; + } + } + else + { + /* + Left and right sides of square. + The angle is calculated for positive 'x' and then mirrored if x is negative. + (1, -1) => 4; (1, 1) => 2 + */ + value = 3.0f - y / abs_x; + + if(x < 0.0f) + { + /* values 2 -> 4 (front) map to values 8 -> 6 (back) */ + return 10.0f - value; + } + else + { + return value; + } + } +} + +/* + Check that values in the range [0,8) represent angles sorted in a clockwise order. + (or anti-clockwise for left-handed coordinate systems) +*/ +static FMOD_INLINE bool FMOD_AngleSort_IsClockwise(float a, float b, float c) +{ + float c_unwrapped = (c >= a) ? c : c + FMOD_ANGLESORT_REVOLUTION; + + return (a <= b && b < c_unwrapped) || (a <= b + FMOD_ANGLESORT_REVOLUTION && b + FMOD_ANGLESORT_REVOLUTION < c_unwrapped); +} + +/* + Checks if the clockwise (or anti-clockwise for left-handed coordinate systems) difference + between two angles is 180 degrees (FMOD_AngleSort_IsStraight) or more (FMOD_AngleSort_IsReflex). +*/ +#define FMOD_AngleSort_IsStraight(_a, _b) (FMOD_ABS(FMOD_ABS((_b) - (_a)) - FMOD_ANGLESORT_REVOLUTION * 0.5f) <= FMOD_ANGLESORT_STRAIGHT_TOLERANCE) +#define FMOD_AngleSort_IsReflex(_a, _b) (((_b) - (_a) > FMOD_ANGLESORT_REVOLUTION * 0.5f) || (((_a) > (_b)) && ((_a) - (_b) < FMOD_ANGLESORT_REVOLUTION * 0.5f))) + + +#include + +#define FMOD_Vector_GetLengthFast(_x) FMOD_SQRT(FMOD_Vector_DotProduct((_x), (_x))) +#define FMOD_Vector_GetLength(_x) FMOD_SQRT(FMOD_Vector_DotProduct((_x), (_x))) + + +static FMOD_INLINE void FMOD_Vector_Normalize(FMOD_VECTOR *v) +{ + float distance = FMOD_Vector_GetLength(v); + + if (distance <= 0) + { + v->x = 0; + v->y = 0; + v->z = 0; + } + else + { + /* + Normalize + */ + v->x = v->x / distance; + v->y = v->y / distance; + v->z = v->z / distance; + } +} + +static FMOD_INLINE FMOD_RESULT FMOD_CHECKFLOAT(float value) +{ + int s, e; + unsigned long src; + long f; + union { + float fval; + unsigned int ival; + } u; + u.fval = value; + + src = u.ival; + + s = (src & 0x80000000UL) >> 31; + e = (src & 0x7F800000UL) >> 23; + f = (src & 0x007FFFFFUL); + + if (e == 255 && f != 0) + { + return FMOD_ERR_INVALID_FLOAT; + } + else if (e == 255 && f == 0 && s == 1) + { + return FMOD_ERR_INVALID_FLOAT; + } + else if (e == 255 && f == 0 && s == 0) + { + return FMOD_ERR_INVALID_FLOAT; + } + else if (e == 0 && f != 0) + { + return FMOD_ERR_INVALID_FLOAT; + } + + return FMOD_OK; +} + +#endif + + + + diff --git a/src/fmod_async.cpp b/src/fmod_async.cpp new file mode 100755 index 0000000..98b7834 --- /dev/null +++ b/src/fmod_async.cpp @@ -0,0 +1,674 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NONBLOCKING + +#include "fmod_async.h" +#include "fmod_dspi.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +LinkedListNode AsyncThread::gAsyncHead; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void asyncThreadFunc(void *data) +{ + AsyncThread *asyncThread = (AsyncThread *)data; + asyncThread->threadFunc(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +AsyncThread::AsyncThread() +{ + mCrit = 0; + mThreadActive = false; + mBusy = false; + mDone = false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::shutDown() +{ + if (FMOD::gGlobal->gAsyncCrit) + { + AsyncThread *asyncthread; + LinkedListNode *current, *next; + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + { + current = gAsyncHead.getNext(); + while (current != &gAsyncHead) + { + next = current->getNext(); + + asyncthread = SAFE_CAST(AsyncThread, current); + asyncthread->reallyRelease(); + + current = next; + } + } + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::addCallback(FMOD_ASYNC_CALLBACK callback, AsyncThread **asyncthread) +{ + FMOD_RESULT result; + + if (asyncthread) + { + *asyncthread = 0; + } + + LinkedListNode *node = (LinkedListNode *)FMOD_Object_Alloc(LinkedListNode); + if (!node) + { + return FMOD_ERR_MEMORY; + } + + node->setData((void *)callback); + + /* + Make sure there's a thread for us + */ + result = getAsyncThread(0); + if (result != FMOD_OK) + { + return result; + } + + LinkedListNode *threadnode = gAsyncHead.getNext(); + if (threadnode != &gAsyncHead) + { + AsyncThread *thread = SAFE_CAST(AsyncThread, threadnode); + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + { + node->addBefore(&thread->mCallbackHead); + } + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + + if (asyncthread) + { + *asyncthread = thread; + } + } + else + { + return FMOD_ERR_INTERNAL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::removeCallback(FMOD_ASYNC_CALLBACK callback) +{ + LinkedListNode *threadnode = gAsyncHead.getNext(); + + if (threadnode != &gAsyncHead) + { + AsyncThread *asyncthread = SAFE_CAST(AsyncThread, threadnode); + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + + LinkedListNode *callbacknode = asyncthread->mCallbackHead.getNext(); + for (; callbacknode != &asyncthread->mCallbackHead; callbacknode = callbacknode->getNext()) + { + if (callbacknode->getData() == (void *)callback) + { + callbacknode->removeNode(); + FMOD_Memory_Free(callbacknode); + break; + } + } + + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + } + else + { + return FMOD_ERR_INTERNAL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::init(bool owned, SystemI *system) +{ + FMOD_RESULT result; + + mOwned = owned; + + result = FMOD_OS_CriticalSection_Create(&mCrit); + if (result != FMOD_OK) + { + return result; + } + + result = mThread.initThread("FMOD thread for FMOD_NONBLOCKING", asyncThreadFunc, this, ASYNC_THREADPRIORITY, 0, ASYNC_STACKSIZE, true, 0, system); + if (result != FMOD_OK) + { + return result; + } + + mThreadActive = true; + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + addBefore(&gAsyncHead); + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "AsyncThread::init", "created thread for %p %s\n", this, mOwned ? "(owned)" : "")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::release() +{ + if (mOwned) + { + if (mHead.getNext() != &mHead) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "AsyncThread::release", "%p queue not empty\n", this)); + } + + mDone = true; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::reallyRelease() +{ + FMOD_OS_CriticalSection_Enter(mCrit); + { + if (mHead.getNext() != &mHead) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "AsyncThread::reallyRelease", "%p queue not empty\n", this)); + } + if (mBusy) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "AsyncThread::reallyRelease", "%p still busy\n", this)); + } + + LinkedListNode *next, *current = mCallbackHead.getNext(); + while (current != &mCallbackHead) + { + next = current->getNext(); + current->removeNode(); + FMOD_Memory_Free(current); + current = next; + } + } + FMOD_OS_CriticalSection_Leave(mCrit); + + // FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + removeNode(); + // FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + + mThreadActive = false; + mThread.closeThread(); + + if (mCrit) + { + FMOD_OS_CriticalSection_Free(mCrit); + } + + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "AsyncThread::reallyRelease", "released thread for %p %s\n", this, mOwned ? "(owned)" : "")); + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::wakeupThread() +{ + return mThread.wakeupThread(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::update() +{ + if (FMOD::gGlobal->gAsyncCrit) + { + AsyncThread *asyncthread; + LinkedListNode *current, *next; + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + { + current = gAsyncHead.getNext(); + while (current != &gAsyncHead) + { + next = current->getNext(); + + asyncthread = SAFE_CAST(AsyncThread, current); + if (asyncthread->mDone) + { + asyncthread->reallyRelease(); + } + + current = next; + } + } + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::threadFunc() +{ + FMOD_RESULT result = FMOD_OK; + SoundI *sound = 0; + SystemI *system; + LinkedListNode *current; + + if (mThreadActive) + { + FMOD_OS_CriticalSection_Enter(mCrit); + { + current = mHead.getNext(); + if (current != &mHead) + { + sound = (SoundI *)current->getData(); + current->removeNode(); + mBusy = true; + } + else + { + current = 0; + } + } + FMOD_OS_CriticalSection_Leave(mCrit); + + if (sound) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "Starting Asynchronous operation on sound %p\n", sound)); + FLOG_INDENT(4); + + system = sound->mSystem; + + if (sound->mOpenState == FMOD_OPENSTATE_LOADING) + { + if (sound->mMode & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) + { + result = system->createSoundInternal((const char *)sound->mAsyncData->mNameData, sound->mMode, sound->mAsyncData->mBufferSize, sound->mAsyncData->mBufferSizeType, sound->mAsyncData->mExInfoExists ? &sound->mAsyncData->mExInfo : 0, true, &sound); + } + else + { + result = system->createSoundInternal(sound->mAsyncData->mName, sound->mMode, sound->mAsyncData->mBufferSize, sound->mAsyncData->mBufferSizeType, sound->mAsyncData->mExInfoExists ? &sound->mAsyncData->mExInfo : 0, true, &sound); + } + } +#ifdef FMOD_SUPPORT_STREAMING +#ifdef FMOD_SUPPORT_NONBLOCKSETPOS + else if (sound->mOpenState == FMOD_OPENSTATE_SETPOSITION) + { + Stream *stream = SAFE_CAST(Stream, sound); + + /* + Block here and wait for stream thread to stop doing stuff. + */ + while (!(stream->mFlags & (FMOD_SOUND_FLAG_SETPOS_SAFE | FMOD_SOUND_FLAG_THREADFINISHED))) + { + FMOD_OS_Time_Sleep(10); + } + + if (!(stream->mFlags & FMOD_SOUND_FLAG_THREADFINISHED)) + { + result = stream->mChannel->setPositionEx(sound->mAsyncData->mPosition, sound->mAsyncData->mPositionType, true); + if (result == FMOD_OK) + { + stream->mChannel->mFlags &= ~CHANNELREAL_FLAG_PAUSEDFORSETPOS; + + FMOD_OS_CriticalSection_Enter(sound->mSystem->mStreamRealchanCrit); + if (stream->mChannel->mRealChannel[0]) + { + stream->mChannel->setPaused(stream->mChannel->mFlags & CHANNELREAL_FLAG_PAUSED ? true : false); + } + FMOD_OS_CriticalSection_Leave(sound->mSystem->mStreamRealchanCrit); + } + else if (result == FMOD_ERR_INVALID_HANDLE) + { + result = FMOD_OK; + } + } + } +#endif + else if (sound->mOpenState == FMOD_OPENSTATE_SEEKING) + { + Stream *stream = SAFE_CAST(Stream, sound); + + if (stream->mSubSoundList) + { + result = FMOD_OK; + } + else + { + result = stream->updateSubSound(stream->mSubSoundIndex, false); + } + + if (result == FMOD_OK) + { + result = stream->setPosition(0, FMOD_TIMEUNIT_PCM); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "done setposition\n")); + if (result == FMOD_OK) + { + result = stream->flush(); + if (result == FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "done flush\n")); + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "stream->flush returned %d\n", result)); + } + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "stream->setPosition returned %d\n", result)); + } + } + } +#endif + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "AsyncThread::threadFunc: unexpected mOpenState (%d). Result = %d\n", sound->mOpenState, result)); + } + + sound->mAsyncData->mResult = result; + sound->mFlags |= FMOD_SOUND_FLAG_DONOTRELEASE; /* Dissalow release... */ + sound->mOpenState = (result == FMOD_OK) ? FMOD_OPENSTATE_READY : FMOD_OPENSTATE_ERROR; /* .. but allow everything else. */ + if (sound->mSubSoundParent) + { + sound->mSubSoundParent->mOpenState = sound->mOpenState; + } + if (sound->mSubSoundShared) + { + sound->mSubSoundShared->mOpenState = sound->mOpenState; + } + mBusy = false; + + if (sound->mAsyncData->mExInfoExists) + { + if (sound->mAsyncData->mExInfo.nonblockcallback) + { + sound->mUserData = sound->mAsyncData->mExInfo.userdata; + + sound->mAsyncData->mExInfo.nonblockcallback((FMOD_SOUND *)sound, result); + } + } + + sound->mFlags &= ~FMOD_SOUND_FLAG_DONOTRELEASE; + + /* + Release this thread if it's owned + */ + release(); + + FLOG_INDENT(-4); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "AsyncThread::threadFunc", "Finished Asynchronous operation on sound %p\n", sound)); + } + + /* + Call any callbacks that have been added + */ + LinkedListNode *node; + + FMOD_OS_CriticalSection_Enter(mCrit); + { + node = mCallbackHead.getNext(); + } + FMOD_OS_CriticalSection_Leave(mCrit); + + while (node != &mCallbackHead) + { + FMOD_ASYNC_CALLBACK callback = (FMOD_ASYNC_CALLBACK)node->getData(); + + result = callback(); + if (result != FMOD_OK) + { + return result; + } + + FMOD_OS_CriticalSection_Enter(mCrit); + { + node = node->getNext(); + } + FMOD_OS_CriticalSection_Leave(mCrit); + } + + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT AsyncThread::getAsyncThread(SoundI *sound) +{ + FMOD_RESULT result; + LinkedListNode *current; + bool found, owned; + AsyncThread *asyncthread = 0; + + owned = false; + found = false; + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + { + current = gAsyncHead.getNext(); + if (current != &gAsyncHead) + { + asyncthread = SAFE_CAST(AsyncThread, current); + + FMOD_OS_CriticalSection_Enter(asyncthread->mCrit); + { + if (0) //(asyncthread->mHead.getNext() != &asyncthread->mHead) || asyncthread->mBusy || asyncthread->mDone) + { + owned = true; /* The first asyncthread is or will be busy so make a new one */ + } + else + { + found = true; /* The first asyncthread isn't doing anything so use it */ + } + } + FMOD_OS_CriticalSection_Leave(asyncthread->mCrit); + } + } + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + + if (!found) + { + asyncthread = FMOD_Object_Alloc(AsyncThread); + if (!asyncthread) + { + return FMOD_ERR_MEMORY; + } + + result = asyncthread->init(owned, sound ? sound->mSystem : 0); + if (result != FMOD_OK) + { + return result; + } + } + + if (sound) + { + if (sound->mAsyncData->mThread) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "AsyncThread::getAsyncThread", "sound->mAsyncData->Thread not null == %p\n", sound->mAsyncData->mThread)); + } + + sound->mAsyncData->mThread = asyncthread; + } + + return FMOD_OK; +} + + +} + +#endif diff --git a/src/fmod_async.h b/src/fmod_async.h new file mode 100755 index 0000000..40e78be --- /dev/null +++ b/src/fmod_async.h @@ -0,0 +1,83 @@ +#ifndef _FMOD_ASYNC_H +#define _FMOD_ASYNC_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#ifndef _FMOD_LINKEDLIST_H +#include "fmod_linkedlist.h" +#endif +#ifndef _FMOD_THREAD_H +#include "fmod_thread.h" +#endif +#ifndef _FMOD_SOUND_STREAM_H +#include "fmod_sound_stream.h" +#endif + +typedef FMOD_RESULT (F_CALLBACK *FMOD_ASYNC_CALLBACK)(); + +namespace FMOD +{ + class SoundI; + + class AsyncThread : public LinkedListNode /* This linked list node entry is for AsyncThread::gAsyncHead */ + { + friend class SystemI; + friend class Stream; + friend class SoundI; + friend class ChannelStream; + friend void asyncThreadFunc(void *data); + + private: + + Thread mThread; + bool mThreadActive; + LinkedListNode mHead; + FMOD_OS_CRITICALSECTION *mCrit; + bool mOwned; + bool mBusy; + bool mDone; + LinkedListNode mCallbackHead; + + FMOD_RESULT threadFunc(); + FMOD_RESULT init(bool owned, SystemI *system); + FMOD_RESULT reallyRelease(); + + + public : + + static LinkedListNode gAsyncHead; + + AsyncThread(); + + FMOD_RESULT release(); + FMOD_RESULT F_API wakeupThread(); + + static FMOD_RESULT getAsyncThread(SoundI *sound); + static FMOD_RESULT update(); + static FMOD_RESULT shutDown(); + static FMOD_RESULT F_API addCallback(FMOD_ASYNC_CALLBACK callback, AsyncThread **asyncthread); + static FMOD_RESULT F_API removeCallback(FMOD_ASYNC_CALLBACK callback); + }; + + struct AsyncData + { + char mName[FMOD_STRING_MAXNAMELEN * 2]; // doubled to allow for Unicode strings + unsigned int mBufferSize; + FMOD_TIMEUNIT mBufferSizeType; + AsyncThread *mThread; + LinkedListNode mNode; + void *mNameData; + FMOD_CREATESOUNDEXINFO mExInfo; + bool mExInfoExists; +#ifdef FMOD_SUPPORT_NONBLOCKSETPOS + unsigned int mPosition; + FMOD_TIMEUNIT mPositionType; +#endif + + FMOD_RESULT mResult; + }; +} + +#endif diff --git a/src/fmod_autocleanup.h b/src/fmod_autocleanup.h new file mode 100755 index 0000000..63890d9 --- /dev/null +++ b/src/fmod_autocleanup.h @@ -0,0 +1,307 @@ +#ifndef _FMOD_AUTOCLEANUP_H +#define _FMOD_AUTOCLEANUP_H + +#include "fmod_os_misc.h" +#include "fmod_memory.h" +#include "fmod_linkedlist.h" + +#ifdef PLATFORM_WINDOWS +// disable "identifier was truncated in the debug info" warnings +#pragma warning(push) +#pragma warning(disable: 4786) +#endif + +namespace FMOD +{ + +/*! \brief Parameterised resource cleanup. + An object of this class stores a value of type \a T. \a T must provide a convesion + to bool. If the value is valid (converts to true), the destructor calls the + \a CleanupPolicy::cleanup function on it. + Copying is disabled for this class, as it doesn't make sense and could easily + lead to bugs. +*/ +template +class AutoCleanup : public CleanupPolicy +{ +public: + AutoCleanup(const T &val = T()) : m_val(val) { } + ~AutoCleanup() { if(m_val) CleanupPolicy::cleanup(m_val); } + + /*! Sets the internal pointer. + */ + AutoCleanup &operator=(T val) { m_val = val; return *this; } + bool operator!() { return !m_val; } + + /*! Releases the internal pointer, so the cleanup policy will not be invoked. + */ + T releasePtr() { T tmp = m_val; m_val = T(); return tmp; } +private: + AutoCleanup(const AutoCleanup &other); // disabled + void operator=(const AutoCleanup &other); // disabled + T m_val; +}; + + +/* + Convenience cleanup types + These types wrap the AutoCleanup class for common cleanup policies so we + have nicer template instantiation code everywhere. +*/ + +/*! Stores a value of type T*. + Cleanup: Calls T::close() on the stored pointer. +*/ +template +class AutoClose; + +/*! Stores a value of type T*. + Cleanup: Calls T::release() on the stored pointer. +*/ +template +class AutoRelease; + +/*! Stores a value of type T*, and an FMOD_OS_CRITICALSECTION*. + Cleanup: Enters the stored critical section, calls T::release() on the + stored pointer and then exits the critical section. +*/ +template +class AutoCritRelease; + +/*! Stores a value of type T*, and a cleanup function pointer. + Cleanup: Calls the stored function pointer with the stored pointer as its argument. +*/ +template +class AutoCleanupFunc; + +/*! Stores a value of type T**. + Cleanup: Calls T::release() on *value, then sets *value to 0, where value + is the stored T**. +*/ +template +class AutoReleaseClear; + +/*! Stores a pointer of type T*, and a value of type T. + Cleanup: Sets *pointer to the stored value. +*/ +template +class AutoSet; + +/*! Stores a value of type T**. + Cleanup: Calls FMOD_Memory_Free on *value, then sets *value to 0. +*/ +template +class AutoFreeClear; + +/*! \class AutoFree + Stores a value of type void*. + Cleanup: Calls FMOD_Memory_Free on the stored value. +*/ + +/*! \class AutoFreeCrit + Stores a value of type FMOD_OS_CRITICALSECTION*. + Cleanup: Calls FMOD_OS_CriticalSection_Free on the stored value. +*/ + + +/* + Cleanup policy and convenience class implementations +*/ + +/* + Cleanup policy classes +*/ + +template +class CleanupRelease +{ +public: + static void cleanup(T ptr) { ptr->release(); } +}; + +template +class CleanupClose +{ +public: + static void cleanup(T ptr) { ptr->close(); } +}; + +template +class CleanupClear : public CleanupPolicy +{ +public: + static void cleanup(T ptr) + { + if(*ptr) + { + CleanupPolicy::cleanup(*ptr); + *ptr = 0; + } + } +}; + +template +class CleanupSet +{ +public: + CleanupSet() : mSetValue() { } + + void setValue(const T &value) { mSetValue = value; } + void cleanup(T *ptr) { *ptr = mSetValue; } + +private: + T mSetValue; +}; + +template +class CleanupFree +{ +public: + static void cleanup(T ptr) { FMOD_Memory_Free(ptr); } +}; + +template <> +class CleanupFree +{ +public: + static void cleanup(FMOD_OS_CRITICALSECTION *ptr) + { FMOD_OS_CriticalSection_Free(ptr); } +}; + +template <> +class CleanupFree +{ +public: + static void cleanup(FMOD_OS_SEMAPHORE *ptr) + { FMOD_OS_Semaphore_Free(ptr); } +}; + +template +class CleanupCrit : public CleanupPolicy +{ +public: + CleanupCrit() : mCrit(0) { } + + void setCrit(FMOD_OS_CRITICALSECTION *crit) { mCrit = crit; } + + void cleanup(T ptr) + { + if(mCrit) FMOD_OS_CriticalSection_Enter(mCrit); + CleanupPolicy::cleanup(ptr); + if(mCrit) FMOD_OS_CriticalSection_Leave(mCrit); + } + +private: + FMOD_OS_CRITICALSECTION *mCrit; +}; + +template +class CleanupFuncPtr +{ +public: + typedef void (F_STDCALL *CleanupFunction)(T); + + CleanupFuncPtr() : mFunc(0) { } + + void setFunc(CleanupFunction func) { mFunc = func; } + + void cleanup(T val) + { + if(mFunc) mFunc(val); + } + +private: + CleanupFunction mFunc; +}; + +/* + Convenience cleanup type implementations +*/ + +template +class AutoClose : public AutoCleanup > +{ +public: + typedef AutoCleanup > Base; + + AutoClose(T *ptr = 0) : Base(ptr) { } + AutoClose &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +template +class AutoRelease : public AutoCleanup > +{ +public: + typedef AutoCleanup > Base; + + AutoRelease(T *ptr = 0) : Base(ptr) { } + AutoRelease &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +template +class AutoCritRelease : public AutoCleanup > > +{ +public: + typedef AutoCleanup > > Base; + + AutoCritRelease(FMOD_OS_CRITICALSECTION *crit, T *ptr = 0) : Base(ptr) + { Base::setCrit(crit); } + AutoCritRelease &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +template +class AutoCleanupFunc : public AutoCleanup > +{ +public: + typedef void (F_STDCALL *CleanupFunction)(T *ptr); + typedef AutoCleanup > Base; + + AutoCleanupFunc(CleanupFunction func, T *ptr = 0) : Base(ptr) + { setFunc(func); } + AutoCleanupFunc &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +template +class AutoReleaseClear : public AutoCleanup > > +{ +public: + typedef AutoCleanup > > Base; + + AutoReleaseClear(T *ptr = 0) : Base(ptr) { } + AutoReleaseClear &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +template +class AutoSet : public AutoCleanup > +{ +public: + typedef AutoCleanup > Base; + + AutoSet(const T &value, T *ptr = 0) : Base(ptr) + { setValue(value); } + AutoSet &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +template +class AutoFreeClear : public AutoCleanup > > +{ +public: + typedef AutoCleanup > > Base; + + AutoFreeClear(T *ptr = 0) : Base(ptr) { } + AutoFreeClear &operator=(T *ptr) { Base::operator=(ptr); return *this; } +}; + +typedef AutoCleanup > AutoFree; +typedef AutoCleanup > AutoFreeCrit; +typedef AutoCleanup > AutoFreeSema; + +} // namespace FMOD + +#ifdef PLATFORM_WINDOWS +#pragma warning(pop) +#endif + +#endif // _FMOD_AUTOCLEANUP_H diff --git a/src/fmod_channel.cpp b/src/fmod_channel.cpp new file mode 100755 index 0000000..d99fc67 --- /dev/null +++ b/src/fmod_channel.cpp @@ -0,0 +1,1363 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_channeli.h" +#include "fmod_systemi.h" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT Channel::getSystemObject(System **system) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (system) + { + *system = 0; + } + return result; + } + else + { + return channeli->getSystemObject(system); + } +} + + +FMOD_RESULT Channel::stop() +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->stop(); + } +} + + +FMOD_RESULT Channel::setPaused(bool paused) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setPaused(paused); + } +} + + +FMOD_RESULT Channel::getPaused(bool *paused) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (paused) + { + *paused = false; + } + return result; + } + else + { + return channeli->getPaused(paused); + } +} + + +FMOD_RESULT Channel::setVolume(float volume) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setVolume(volume); + } +} + + +FMOD_RESULT Channel::getVolume(float *volume) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (volume) + { + *volume = 0.0f; + } + return result; + } + else + { + return channeli->getVolume(volume); + } +} + + +FMOD_RESULT Channel::setFrequency(float frequency) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setFrequency(frequency); + } +} + + +FMOD_RESULT Channel::getFrequency(float *frequency) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (frequency) + { + *frequency = 0.0f; + } + return result; + } + else + { + return channeli->getFrequency(frequency); + } +} + + +FMOD_RESULT Channel::setPan(float pan) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setPan(pan); + } +} + + +FMOD_RESULT Channel::getPan(float *pan) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (pan) + { + *pan = 0.0f; + } + return result; + } + else + { + return channeli->getPan(pan); + } +} + + +FMOD_RESULT Channel::setDelay(FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setDelay(delaytype, delayhi, delaylo); + } +} + + +FMOD_RESULT Channel::getDelay(FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (delayhi) + { + *delayhi = 0; + } + if (delaylo) + { + *delaylo = 0; + } + return result; + } + else + { + return channeli->getDelay(delaytype, delayhi, delaylo); + } +} + + +FMOD_RESULT Channel::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); + } +} + + +FMOD_RESULT Channel::getSpeakerMix(float *frontleft, float *frontright, float *center, float *lfe, float *backleft, float *backright, float *sideleft, float *sideright) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (frontleft) + { + *frontleft = 0.0f; + } + if (frontright) + { + *frontright = 0.0f; + } + if (center) + { + *center = 0.0f; + } + if (lfe) + { + *lfe = 0.0f; + } + if (backleft) + { + *backleft = 0.0f; + } + if (backright) + { + *backright = 0.0f; + } + if (sideleft) + { + *sideleft = 0.0f; + } + if (sideright) + { + *sideright = 0.0f; + } + return result; + } + else + { + return channeli->getSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); + } +} + + +FMOD_RESULT Channel::setSpeakerLevels(FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setSpeakerLevels(speaker, levels, numlevels); + } +} + + +FMOD_RESULT Channel::getSpeakerLevels(FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (levels) + { + *levels = 0.0f; + } + return result; + } + else + { + return channeli->getSpeakerLevels(speaker, levels, numlevels); + } +} + + +FMOD_RESULT Channel::setInputChannelMix(float *levels, int numlevels) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setInputChannelMix(levels, numlevels); + } +} + + +FMOD_RESULT Channel::getInputChannelMix(float *levels, int numlevels) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (levels) + { + *levels = 0.0f; + } + return result; + } + else + { + return channeli->getInputChannelMix(levels, numlevels); + } +} + + +FMOD_RESULT Channel::setMute(bool mute) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setMute(mute); + } +} + + +FMOD_RESULT Channel::getMute(bool *mute) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (mute) + { + *mute = false; + } + return result; + } + else + { + return channeli->getMute(mute); + } +} + + +FMOD_RESULT Channel::setPriority(int priority) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setPriority(priority); + } +} + + +FMOD_RESULT Channel::getPriority(int *priority) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (priority) + { + *priority = 0; + } + return result; + } + else + { + return channeli->getPriority(priority); + } +} + + +FMOD_RESULT Channel::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setPosition(position, postype); + } +} + + +FMOD_RESULT Channel::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (position) + { + *position = 0; + } + return result; + } + else + { + return channeli->getPosition(position, postype); + } +} + + +FMOD_RESULT Channel::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setReverbProperties(prop); + } +} + + +FMOD_RESULT Channel::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->getReverbProperties(prop); + } +} + + +FMOD_RESULT Channel::setLowPassGain(float gain) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setLowPassGain(gain); + } +} + + +FMOD_RESULT Channel::getLowPassGain(float *gain) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (gain) + { + *gain = 0.0f; + } + return result; + } + else + { + return channeli->getLowPassGain(gain); + } +} + + +FMOD_RESULT Channel::setChannelGroup(ChannelGroup *channelgroup) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setChannelGroup((ChannelGroupI *)channelgroup); + } +} + + +FMOD_RESULT Channel::getChannelGroup(ChannelGroup **channelgroup) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (channelgroup) + { + *channelgroup = 0; + } + return result; + } + else + { + return channeli->getChannelGroup((ChannelGroupI **)channelgroup); + } +} + + +FMOD_RESULT Channel::setCallback(FMOD_CHANNEL_CALLBACK callback) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setCallback(callback); + } +} + + +FMOD_RESULT Channel::set3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DAttributes(pos, vel); + } +} + + +FMOD_RESULT Channel::get3DAttributes(FMOD_VECTOR *pos, FMOD_VECTOR *vel) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (pos) + { + pos->x = pos->y = pos->z = 0.0f; + } + if (vel) + { + vel->x = vel->y = vel->z = 0.0f; + } + return result; + } + else + { + return channeli->get3DAttributes(pos, vel); + } +} + + +FMOD_RESULT Channel::set3DMinMaxDistance(float mindistance, float maxdistance) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DMinMaxDistance(mindistance, maxdistance); + } +} + + +FMOD_RESULT Channel::get3DMinMaxDistance(float *mindistance, float *maxdistance) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (mindistance) + { + *mindistance = 0.0f; + } + if (maxdistance) + { + *maxdistance = 0.0f; + } + return result; + } + else + { + return channeli->get3DMinMaxDistance(mindistance, maxdistance); + } +} + + +FMOD_RESULT Channel::set3DConeSettings(float insideconeangle, float outsideconeangle, float outsidevolume) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); + } +} + + +FMOD_RESULT Channel::get3DConeSettings(float *insideconeangle, float *outsideconeangle, float *outsidevolume) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (insideconeangle) + { + *insideconeangle = 0.0f; + } + if (outsideconeangle) + { + *outsideconeangle = 0.0f; + } + if (outsidevolume) + { + *outsidevolume = 0.0f; + } + return result; + } + else + { + return channeli->get3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); + } +} + + +FMOD_RESULT Channel::set3DConeOrientation(FMOD_VECTOR *orientation) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DConeOrientation(orientation); + } +} + + +FMOD_RESULT Channel::get3DConeOrientation(FMOD_VECTOR *orientation) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (orientation) + { + orientation->x = orientation->y = orientation->z = 0.0f; + } + return result; + } + else + { + return channeli->get3DConeOrientation(orientation); + } +} + + +FMOD_RESULT Channel::set3DCustomRolloff(FMOD_VECTOR *points, int numpoints) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DCustomRolloff(points, numpoints); + } +} + + +FMOD_RESULT Channel::get3DCustomRolloff(FMOD_VECTOR **points, int *numpoints) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (points) + { + *points = 0; + } + if (numpoints) + { + *numpoints = 0; + } + return result; + } + else + { + return channeli->get3DCustomRolloff(points, numpoints); + } +} + + +FMOD_RESULT Channel::set3DOcclusion(float directocclusion, float reverbocclusion) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DOcclusion(directocclusion, reverbocclusion); + } +} + + +FMOD_RESULT Channel::get3DOcclusion(float *directocclusion, float *reverbocclusion) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (directocclusion) + { + *directocclusion = 0.0f; + } + if (reverbocclusion) + { + *reverbocclusion = 0.0f; + } + return result; + } + else + { + return channeli->get3DOcclusion(directocclusion, reverbocclusion); + } +} + + +FMOD_RESULT Channel::set3DSpread(float angle) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DSpread(angle); + } +} + + +FMOD_RESULT Channel::get3DSpread(float *angle) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (angle) + { + *angle = 0.0f; + } + return result; + } + else + { + return channeli->get3DSpread(angle); + } +} + + +FMOD_RESULT Channel::set3DPanLevel(float level) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DPanLevel(level); + } +} + + +FMOD_RESULT Channel::get3DPanLevel(float *level) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (level) + { + *level = 0.0f; + } + return result; + } + else + { + return channeli->get3DPanLevel(level); + } +} + + +FMOD_RESULT Channel::set3DDopplerLevel(float level) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->set3DDopplerLevel(level); + } +} + + +FMOD_RESULT Channel::get3DDopplerLevel(float *level) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (level) + { + *level = 0.0f; + } + return result; + } + else + { + return channeli->get3DDopplerLevel(level); + } +} + + +FMOD_RESULT Channel::getDSPHead(DSP **dsp) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (dsp) + { + *dsp = 0; + } + return result; + } + else + { + return channeli->getDSPHead((DSPI **)dsp); + } +} + + +FMOD_RESULT Channel::addDSP(DSP *dsp, DSPConnection **connection) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->addDSP((DSPI *)dsp, (DSPConnectionI **)connection); + } +} + + +FMOD_RESULT Channel::isPlaying(bool *isplaying) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (isplaying) + { + *isplaying = false; + } + return result; + } + else + { + return channeli->isPlaying(isplaying); + } +} + + +FMOD_RESULT Channel::isVirtual(bool *isvirtual) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (isvirtual) + { + *isvirtual = false; + } + return result; + } + else + { + return channeli->isVirtual(isvirtual); + } +} + + +FMOD_RESULT Channel::getAudibility(float *audibility) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (audibility) + { + *audibility = 0.0f; + } + return result; + } + else + { + return channeli->getAudibility(audibility); + } +} + + +FMOD_RESULT Channel::getCurrentSound(Sound **sound) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (sound) + { + *sound = 0; + } + return result; + } + else + { + return channeli->getCurrentSound((SoundI **)sound); + } +} + + +FMOD_RESULT Channel::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (spectrumarray) + { + *spectrumarray = 0.0f; + } + return result; + } + else + { + return channeli->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); + } +} + + +FMOD_RESULT Channel::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (wavearray) + { + *wavearray = 0.0f; + } + return result; + } + else + { + return channeli->getWaveData(wavearray, numvalues, channeloffset); + } +} + + +FMOD_RESULT Channel::getIndex(int *index) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (index) + { + *index = 0; + } + return result; + } + else + { + return channeli->getIndex(index); + } +} + + +FMOD_RESULT Channel::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setMode(mode); + } +} + + +FMOD_RESULT Channel::getMode(FMOD_MODE *mode) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (mode) + { + *mode = (FMOD_MODE)0; + } + return result; + } + else + { + return channeli->getMode(mode); + } +} + + +FMOD_RESULT Channel::setLoopCount(int loopcount) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setLoopCount(loopcount); + } +} + + +FMOD_RESULT Channel::getLoopCount(int *loopcount) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (loopcount) + { + *loopcount = 0; + } + return result; + } + else + { + return channeli->getLoopCount(loopcount); + } +} + + +FMOD_RESULT Channel::setLoopPoints(unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setLoopPoints(loopstart, loopstarttype, loopend, loopendtype); + } +} + + +FMOD_RESULT Channel::getLoopPoints(unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (loopstart) + { + *loopstart = 0; + } + if (loopend) + { + *loopend = 0; + } + return result; + } + else + { + return channeli->getLoopPoints(loopstart, loopstarttype, loopend, loopendtype); + } +} + + +FMOD_RESULT Channel::setUserData(void *_userdata) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + return result; + } + else + { + return channeli->setUserData(_userdata); + } +} + + +FMOD_RESULT Channel::getUserData(void **_userdata) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (_userdata) + { + *_userdata = 0; + } + return result; + } + else + { + return channeli->getUserData(_userdata); + } +} + + +FMOD_RESULT Channel::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + ChannelI *channeli; + + result = ChannelI::validate(this, &channeli); + if (result != FMOD_OK) + { + if (memoryused) + { + *memoryused = 0; + } + if (memoryused_details) + { + FMOD_memset(memoryused_details, 0, sizeof(FMOD_MEMORY_USAGE_DETAILS)); + } + return result; + } + else + { + return channeli->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_channel_emulated.cpp b/src/fmod_channel_emulated.cpp new file mode 100755 index 0000000..fab456a --- /dev/null +++ b/src/fmod_channel_emulated.cpp @@ -0,0 +1,539 @@ +#include "fmod_settings.h" + +#include "fmod_channeli.h" +#include "fmod_channel_emulated.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_sound_stream.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" +#include "fmod_os_misc.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelEmulated::ChannelEmulated() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + mDSPHead = 0; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::init(int index, SystemI *system, Output *output, DSPI *dspmixtarget) +{ + ChannelReal::init(index, system, output, dspmixtarget); + +#ifdef FMOD_SUPPORT_SOFTWARE + if (!(mSystem->mFlags & FMOD_INIT_SOFTWARE_DISABLE)) + { + FMOD_RESULT result; + FMOD_DSP_DESCRIPTION_EX descriptionex; + + /* + Create a head unit that things can connect to. + */ + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "EmulatedChannel DSPHead Unit"); + descriptionex.version = 0x00010100; + descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER; + descriptionex.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + + #ifdef PLATFORM_PS3 + mDSPHead = (DSPI *)FMOD_ALIGNPOINTER(&mDSPHeadMemory, 16); + mDSPHead = new (mDSPHead) (DSPFilter); + #else + mDSPHead = &mDSPHeadMemory; + #endif + + result = mSystem->createDSP(&descriptionex, &mDSPHead, false); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_PS3 + mDSPHead->mMramAddress = (unsigned int)mDSPHead; + mDSPHead->mDescription.mSize = (sizeof(DSPFilter) + 15) & ~15; + #endif + + mMinFrequency = -mMaxFrequency; + } +#endif + + return FMOD_OK; +} + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::alloc() +{ + FMOD_RESULT result; + + result = ChannelReal::alloc(); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mDSPHead) + { + result = mDSPHead->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + + result = mParent->mChannelGroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + } +#endif + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::stop() +{ +#ifdef FMOD_SUPPORT_STREAMING + if (mSound) + { + if (mSound->isStream()) + { + Stream *stream = SAFE_CAST(Stream, mSound); + if (stream->mChannel) + { + stream->mChannel->mFinished = true; + } + } + } +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mDSPHead) + { + mDSPHead->setActive(false); + mDSPHead->disconnectAll(false, true); + } +#endif + +#ifdef FMOD_SUPPORT_REVERB + if (mParent) + { + int instance; + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + mSystem->mReverbGlobal.resetConnectionPointer(instance, mParent->mIndex); + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + { + ReverbI *reverb_c; + + mSystem->mReverb3D.resetConnectionPointer(0, mParent->mIndex); + + reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + while (reverb_c != &(mSystem->mReverb3DHead)) + { + reverb_c->resetConnectionPointer(0, mParent->mIndex); + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()); + } + } +#endif + } +#endif + + return ChannelReal::stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::close() +{ + FMOD_RESULT result; + + result = ChannelReal::close(); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mDSPHead) + { + mDSPHead->release(false); /* false = dont free this, as it is not alloced. */ + mDSPHead= 0; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::update(int delta) +{ + FMOD_RESULT result; + + result = ChannelReal::update(delta); + if (result != FMOD_OK) + { + return result; + } + + if (mFlags & CHANNELREAL_FLAG_PAUSED || !(mFlags & CHANNELREAL_FLAG_PLAYING)) + { + return FMOD_OK; + } + + if ((mParent->mFlags & CHANNELI_FLAG_JUSTWENTVIRTUAL && !(mParent->mFlags & CHANNELI_FLAG_FORCEVIRTUAL)) || mMode & FMOD_VIRTUAL_PLAYFROMSTART) + { + return FMOD_OK; + } + + if (mSystem->mDSPClock.mValue < mParent->mDSPClockDelay.mValue) + { + return FMOD_OK; + } + + + /* + Convert from ms to samples + */ + delta = delta * (int)(mParent->mFrequency * mParent->mPitch3D) / 1000; + + if (mDirection == CHANNELREAL_PLAYDIR_BACKWARDS) + { + delta = -delta; + } + + if ((int)mPosition + delta > 0) + { + mPosition += delta; + } + else + { + mPosition = 0; + } + + if (mSound) + { + SoundI *soundi = SAFE_CAST(SoundI, mSound); + + if (mMode & FMOD_LOOP_NORMAL || mMode & FMOD_LOOP_BIDI && mLoopCount) + { + while ((mParent->mFrequency > 0 && mPosition >= mLoopStart + mLoopLength) || + (mParent->mFrequency < 0 && mPosition <= mLoopStart)) + { + if (!mLoopCount) + { + if (mDirection == CHANNELREAL_PLAYDIR_FORWARDS && mParent->mFrequency > 0) + { + mPosition = mLoopStart + mLoopLength; + } + else + { + mPosition = mLoopStart; + } + + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_PLAYING); + break; + } + else if (mMode & FMOD_LOOP_NORMAL) + { + if (mParent->mFrequency > 0) + { + mPosition -= mLoopLength; + } + else + { + mPosition += mLoopLength; + } + } + else if (mMode & FMOD_LOOP_BIDI) + { + if (mDirection == CHANNELREAL_PLAYDIR_FORWARDS) + { + mDirection = CHANNELREAL_PLAYDIR_BACKWARDS; + } + else + { + mDirection = CHANNELREAL_PLAYDIR_FORWARDS; + } + mPosition += -delta; + } + + if (mLoopCount >= 0) + { + mLoopCount--; + } + } + } + else + { + if (mPosition >= soundi->mLength) + { + mPosition = soundi->mLength; + + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_PLAYING); + } + } + + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::isVirtual(bool *isvirtual) +{ + if (!isvirtual) + { + return FMOD_ERR_INVALID_PARAM; + } + + *isvirtual = true; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::getDSPHead(DSPI **dsp) +{ + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + *dsp = mDSPHead; +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelEmulated::moveChannelGroup(ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result; + + if (oldchannelgroup == newchannelgroup || !mDSPHead) + { + return FMOD_OK; + } + + /* + 1. disconnect from previous channel group's head. + */ + if (oldchannelgroup && oldchannelgroup->mDSPMixTarget) + { + result = oldchannelgroup->mDSPMixTarget->disconnectFrom(mDSPHead); + if (result != FMOD_OK) + { + return result; + } + } + + /* + 2. reconnect to DSP group's head. + */ + result = newchannelgroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } +#endif + + return FMOD_OK; +} + + + +} + diff --git a/src/fmod_channel_emulated.h b/src/fmod_channel_emulated.h new file mode 100755 index 0000000..e4014c1 --- /dev/null +++ b/src/fmod_channel_emulated.h @@ -0,0 +1,38 @@ +#ifndef _FMOD_CHANNEL_EMULATED_H +#define _FMOD_CHANNEL_EMULATED_H + +#include "fmod_settings.h" + +#include "fmod_channel_real.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class ChannelEmulated : public ChannelReal + { + private: + +#ifdef FMOD_SUPPORT_SOFTWARE + DSPFilter mDSPHeadMemory; + DSPI *mDSPHead; +#endif + + public: + + ChannelEmulated(); + + FMOD_RESULT init(int index, SystemI *system, Output *output, DSPI *dspmixtarget); + FMOD_RESULT alloc(); + FMOD_RESULT stop(); + FMOD_RESULT close(); + FMOD_RESULT update(int delta); + FMOD_RESULT isVirtual(bool *isvirtual); + FMOD_RESULT getDSPHead(DSPI **dsp); + FMOD_RESULT setSpeakerLevels(int speaker, float *levels, int numlevels); + FMOD_RESULT setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + FMOD_RESULT moveChannelGroup(ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup); + }; +} + +#endif + diff --git a/src/fmod_channel_real.cpp b/src/fmod_channel_real.cpp new file mode 100755 index 0000000..2d68032 --- /dev/null +++ b/src/fmod_channel_real.cpp @@ -0,0 +1,1600 @@ +#include "fmod_settings.h" + +#include "fmod_3d.h" +#include "fmod_channel_real.h" +#include "fmod_channeli.h" +#include "fmod_channelpool.h" +#include "fmod_output.h" +#include "fmod_systemi.h" +#include "fmod_soundi.h" +#include "fmod_speakermap.h" + +#include + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelReal::ChannelReal() +{ + mSound = 0; + mSystem = 0; + mOutput = 0; + mPool = 0; + mLoopCount = -1; + mMinFrequency = 100.0f; + mMaxFrequency = 1000000.0f; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::init(int index, SystemI *system, Output *output, DSPI *dspmixtarget) +{ + mSound = 0; + mFlags = (CHANNELREAL_FLAG)0; + mMode = (FMOD_MODE)0; + mPosition = 0; + mDirection = CHANNELREAL_PLAYDIR_FORWARDS; + mLoopCount = -1; + mOutput = output; + mSystem = system; + mIndex = index; + + #ifdef PLATFORM_WII + mControllerSpeaker = 0; + mBiquadActive = false; + mBiquadB0 = 0; + mBiquadB1 = 0; + mBiquadB2 = 0; + mBiquadA1 = 0; + mBiquadA2 = 0; + mLPFCutoff = -1; + #endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::close() +{ + return stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::alloc() +{ + mPosition = 0; + + if (mPool) + { + mPool->mChannelsUsed++; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::alloc(DSPI *dsp) +{ + if (mPool) + { + mPool->mChannelsUsed++; + } + + mPosition = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::set2DFreqVolumePanFor3D() +{ + /* + Channel types that have their own 3d system (ie dsound3d or xbox) will call this, + software and others use the stereo panning method defined in ChannelRealManual3D. + */ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::update(int delta) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::updateStream() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::start() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::stop() +{ +// mSound = 0; /* ChannelI or ChannelStream will clear this out in its stop call. */ +// mDSP = 0; /* ChannelI or ChannelStream will clear this out in its stop call. */ + + if (mPool) /* Channel might not belong to a pool */ + { + mPool->mChannelsUsed--; + } + + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_ALLOCATED); + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_PLAYING); + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_PAUSED); + + mFlags = (CHANNELREAL_FLAG)(mFlags | CHANNELREAL_FLAG_STOPPED); + + #ifdef PLATFORM_WII + mControllerSpeaker = 0; + mBiquadActive = false; + mBiquadB0 = 0; + mBiquadB1 = 0; + mBiquadB2 = 0; + mBiquadA1 = 0; + mBiquadA2 = 0; + mLPFCutoff = -1; + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setPaused(bool paused) +{ + if (paused) + { + mFlags = (CHANNELREAL_FLAG)(mFlags | CHANNELREAL_FLAG_PAUSED); + } + else + { + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_PAUSED); + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getPaused(bool *paused) +{ + if (!paused) + { + return FMOD_ERR_INVALID_PARAM; + } + + *paused = mFlags & CHANNELREAL_FLAG_PAUSED ? true : false; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setVolume(float volume) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setFrequency(float frequency) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setPan(float pan, float fbpan) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setDSPClockDelay() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + int numsubchannels = 0; + + if (mDSP) + { + return FMOD_OK; /* Only ChannelSoftware supports DSP. */ + } + + if (mSound) + { + if (mSound->mSubSampleParent) + { + numsubchannels = mSound->mSubSampleParent->mChannels; + } + else + { + numsubchannels = mSound->mChannels; + } + } + + if (numsubchannels > 1) + { + if (mSound->mDefaultChannelMask & SPEAKER_ALLMONO) + { + setPan(0.0f, 1.0f); + setVolume(center * mParent->mVolume); + } + else if (mSound->mDefaultChannelMask & SPEAKER_ALLSTEREO) + { + switch (mSubChannelIndex % 2) + { + case 0: + { + setPan(-1.0f, 1.0f); + setVolume(frontleft * mParent->mVolume); + break; + } + case 1: + { + setPan(1.0f, 1.0f); + setVolume(frontright * mParent->mVolume); + break; + } + } + } + else + { + switch (mSubChannelIndex) + { + case 0: + { + setPan(-1.0f, 1.0f); + setVolume(frontleft * mParent->mVolume); + break; + } + case 1: + { + setPan(1.0f, 1.0f); + setVolume(frontright * mParent->mVolume); + break; + } + case 2: + { + setPan(0.0f, 1.0f); + setVolume(center * mParent->mVolume); + break; + } + case 3: + { + setPan(0.0f, 0.0f); + setVolume(lfe * mParent->mVolume); + break; + } + case 4: + { + setPan(-1.0f, -1.0f); + setVolume(backleft * mParent->mVolume); + break; + } + case 5: + { + setPan(1.0f, -1.0f); + setVolume(backright * mParent->mVolume); + break; + } + case 6: + { + setPan(-1.0f, 0.0f); + setVolume(sideleft * mParent->mVolume); + break; + } + case 7: + { + setPan(1.0f, 0.0f); + setVolume(sideright * mParent->mVolume); + break; + } + } + } + } + else + { + float vol = 0, lrpan = 0, fbpan = 0; + + vol += frontleft; + vol += frontright; + vol += center; + vol += lfe; + vol += backleft; + vol += backright; + vol += sideleft; + vol += sideright; + + #if 0 + + float sqrtsumsq; + /* + Normalise + */ + sqrtsumsq = frontleft*frontleft + frontright*frontright + backleft*backleft + backright*backright + sideleft*sideleft + sideright*sideright; + sqrtsumsq = FMOD_SQRT(sqrtsumsq); + + lrpan -= frontleft / sqrtsumsq; + lrpan += frontright / sqrtsumsq; + lrpan -= backleft / sqrtsumsq; + lrpan += backright / sqrtsumsq; + lrpan -= sideleft / sqrtsumsq; + lrpan += sideright / sqrtsumsq; + + /* + Normalise + */ + sqrtsumsq = frontleft*frontleft + frontright*frontright + backleft*backleft + backright*backright + center*center; + sqrtsumsq = FMOD_SQRT(sqrtsumsq); + + fbpan += frontleft / sqrtsumsq; + fbpan += frontright / sqrtsumsq; + fbpan += center / sqrtsumsq; + fbpan -= backleft / sqrtsumsq; + fbpan -= backright / sqrtsumsq; + + #else + + lrpan -= frontleft; + lrpan += frontright; + lrpan -= backleft; + lrpan += backright; + lrpan -= sideleft; + lrpan += sideright; + + fbpan += frontleft; + fbpan += frontright; + fbpan += center; + fbpan -= backleft; + fbpan -= backright; + + #endif + + vol = vol > 1.0f ? 1.0f : vol; + setVolume(vol * mParent->mVolume); + setPan(lrpan < -1.0f ? -1.0f : lrpan > 1.0f ? 1.0f : lrpan, + fbpan < -1.0f ? -1.0f : fbpan > 1.0f ? 1.0f : fbpan); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + if (!mParent) + { + return FMOD_OK; + } + + if (!mParent->mLevels) + { + mSystem->mSpeakerLevelsPool.alloc(&mParent->mLevels); + if (!mParent->mLevels) + { + return FMOD_ERR_MEMORY; + } + } + + for (int count = 0; count < numlevels; count++) + { + float volume = levels[count]; + + if (volume < 0) + { + volume = 0; + } + if (volume > 1.0f) + { + volume = 1.0f; + } + + mParent->mLevels[(speaker * mSystem->mMaxInputChannels) + count] = volume; + } + + + return updateSpeakerLevels(mParent->mVolume); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::updateSpeakerLevels(float volume) +{ + if (mParent && mParent->mLevels) + { + int count; + float vol = 0, pan = 0, fbpan = 0; + float sumsq = 0.0f, sqrtsumsq; + int outputchannels = mSystem->mMaxOutputChannels; + + if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC) + { + outputchannels = 6; + } + + for (count = 0; count < outputchannels; count++) + { + if (count == FMOD_SPEAKER_FRONT_CENTER || count == FMOD_SPEAKER_LOW_FREQUENCY) + { + continue; + } + + float val = FMOD_FABS(mParent->mLevels[(count * mSystem->mMaxInputChannels) + mSubChannelIndex]); + + sumsq += val*val; + } + + sqrtsumsq = FMOD_SQRT(sumsq); + vol = sqrtsumsq; + + for (count = 0; count < outputchannels; count++) + { + float val = FMOD_FABS(mParent->mLevels[(count * mSystem->mMaxInputChannels) + mSubChannelIndex]); + + if (sqrtsumsq) + { + val /= sqrtsumsq; + } + else + { + val = 0; + } + + if (count == FMOD_SPEAKER_FRONT_LEFT || count == FMOD_SPEAKER_BACK_LEFT || count == FMOD_SPEAKER_SIDE_LEFT) + { + pan -= val; + } + else if (count == FMOD_SPEAKER_FRONT_RIGHT || count == FMOD_SPEAKER_BACK_RIGHT || count == FMOD_SPEAKER_SIDE_RIGHT) + { + pan += val; + } + + if (count == FMOD_SPEAKER_FRONT_LEFT || count == FMOD_SPEAKER_FRONT_RIGHT ) + { + fbpan += val; + } + else if (count == FMOD_SPEAKER_BACK_LEFT || count == FMOD_SPEAKER_BACK_RIGHT) + { + fbpan -= val; + } + } + + vol = vol > 1.0f ? 1.0f : vol; + setVolume(vol * volume); + setPan(pan < -1.0f ? -1.0f : pan > 1.0f ? 1.0f : pan, fbpan < -1.0f ? -1.0f : fbpan > 1.0f ? 1.0f : fbpan); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } + + if (mSound) + { + FMOD_RESULT result; + unsigned int pcm = 0, lengthpcm; + + result = mSound->getLength(&lengthpcm, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + + if (postype == FMOD_TIMEUNIT_PCM) + { + pcm = position; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(position, &pcm, mSound->mChannels, mSound->mFormat); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + pcm = (unsigned int)((float)position / 1000.0f * mSound->mDefaultFrequency); + } + + if (pcm >= lengthpcm) + { + pcm = lengthpcm; + } + + mPosition = pcm; + } + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + bool getsubsoundtime = false; + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + postype = (FMOD_TIMEUNIT)(postype & ~FMOD_TIMEUNIT_BUFFERED); /* Not a stream, so buffering is irrelevant. */ + + /* + First check for sentence stuff. + */ + if (postype == FMOD_TIMEUNIT_SENTENCE_MS) + { + postype = FMOD_TIMEUNIT_MS; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCM) + { + postype = FMOD_TIMEUNIT_PCM; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCMBYTES) + { + postype = FMOD_TIMEUNIT_PCMBYTES; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE || postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + getsubsoundtime = true; + } + + if (getsubsoundtime +#ifdef FMOD_SUPPORT_SENTENCING + && !mSound->mSubSoundList +#endif + ) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (postype == FMOD_TIMEUNIT_MS || postype == FMOD_TIMEUNIT_PCM || postype == FMOD_TIMEUNIT_PCMBYTES || postype == FMOD_TIMEUNIT_SENTENCE || postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + int currentsubsoundid = 0; + int currentsentenceid = 0; + unsigned int pcmcurrent = mPosition; + +#ifdef FMOD_SUPPORT_SENTENCING + if (getsubsoundtime) + { + for (currentsubsoundid = 0; currentsubsoundid < mSound->mSubSoundListNum; currentsubsoundid++) + { + SoundI *sound = mSound->mSubSound[mSound->mSubSoundList[currentsubsoundid].mIndex]; + + if (sound && pcmcurrent >= sound->mLength) + { + pcmcurrent -= sound->mLength; + currentsentenceid++; + } + else + { + break; + } + } + } +#endif + + if (postype == FMOD_TIMEUNIT_SENTENCE) + { + *position = currentsentenceid; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + *position = currentsubsoundid; + } + else if (postype == FMOD_TIMEUNIT_PCM) + { + *position = pcmcurrent; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(pcmcurrent, position, mSound->mChannels, mSound->mFormat); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + *position = (unsigned int)((float)pcmcurrent / mSound->mDefaultFrequency * 1000.0f); + } + } + else + { + return FMOD_ERR_FORMAT; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setLoopPoints(unsigned int loopstart, unsigned int looplength) +{ + if (!mSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (loopstart >= mSound->mLength) + { + return FMOD_ERR_INVALID_PARAM; + } + if (loopstart + looplength > mSound->mLength) + { + return FMOD_ERR_INVALID_PARAM; + } + + mLoopStart = loopstart; + mLoopLength = looplength; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setLoopCount(int loopcount) +{ + mLoopCount = loopcount; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setLowPassGain(float gain) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::set3DAttributes() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::set3DMinMaxDistance() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::set3DOcclusion(float directOcclusion, float reverbOcclusion) +{ + if (!mParent) + { + return FMOD_OK; + } + + return setVolume(mParent->mVolume); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ +#ifndef FMOD_STATICFORPLUGINS + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Set the reverb's channel properties and update the reverb mix + */ + if (!mParent) + { + return FMOD_OK; + } + + FMOD_RESULT result; + int instance, numinstances = 0; + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (prop->Flags & (FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance)) + { + numinstances++; + } + } + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (prop->Flags & (FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance) || (!numinstances && !instance)) + { + result = mSystem->mReverbGlobal.setChanProperties(instance, mParent->mIndex, prop); + if (numinstances <= 1 && result != FMOD_OK) + { + return result; + } + } + else + { + // update direct mix on other instances (ignoring errors) + FMOD_REVERB_CHANNELPROPERTIES cprop; + result = mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, &cprop); + cprop.Direct = prop->Direct; + result = mSystem->mReverbGlobal.setChanProperties(instance, mParent->mIndex, &cprop); + } + } + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ +#ifndef FMOD_STATICFORPLUGINS + + if(!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mParent) + { + return FMOD_OK; + } + + int instance = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) ? 1 : + (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) ? 2 : + (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) ? 3 : + 0; + + return mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, prop, 0); + +#endif + + return FMOD_OK; +} +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::isPlaying(bool *isplaying, bool includethreadlatency) +{ + if (!isplaying) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFlags & CHANNELREAL_FLAG_PLAYING || mFlags & CHANNELREAL_FLAG_ALLOCATED) + { + *isplaying = true; + } + else + { + *isplaying = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::isVirtual(bool *isvirtual) +{ + if (!isvirtual) + { + return FMOD_ERR_INVALID_PARAM; + } + + *isvirtual = false; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getSpectrum(float *spectrumarray, int numentries, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + return FMOD_ERR_NEEDSSOFTWARE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + return FMOD_ERR_NEEDSSOFTWARE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getDSPHead(DSPI **dsp) +{ + *dsp = 0; + + return FMOD_ERR_NEEDSSOFTWARE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setMode(FMOD_MODE mode) +{ + /* + Allow switching between loop modes. + */ + if (mode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)) + { + mMode = mMode & ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI); + + if (mode & FMOD_LOOP_OFF) + { + mMode |= FMOD_LOOP_OFF; + } + else if (mode & FMOD_LOOP_NORMAL) + { + mMode |= FMOD_LOOP_NORMAL; + } + else if (mode & FMOD_LOOP_BIDI) + { + mMode |= FMOD_LOOP_BIDI; + } + } + + /* + Allow switching between head relative and world relative. + */ + if (mode & FMOD_3D_HEADRELATIVE) + { + mMode &= ~FMOD_3D_WORLDRELATIVE; + mMode |= FMOD_3D_HEADRELATIVE; + } + else if (mode & FMOD_3D_WORLDRELATIVE) + { + mMode &= ~FMOD_3D_HEADRELATIVE; + mMode |= FMOD_3D_WORLDRELATIVE; + } + + /* + Allow switching between linear/log/custom rolloff. + */ + if (mode & FMOD_3D_LOGROLLOFF) + { + mMode &= ~FMOD_3D_LINEARROLLOFF; + mMode &= ~FMOD_3D_CUSTOMROLLOFF; + mMode |= FMOD_3D_LOGROLLOFF; + } + else if (mode & FMOD_3D_LINEARROLLOFF) + { + mMode &= ~FMOD_3D_LOGROLLOFF; + mMode &= ~FMOD_3D_CUSTOMROLLOFF; + mMode |= FMOD_3D_LINEARROLLOFF; + } + else if (mode & FMOD_3D_CUSTOMROLLOFF) + { + mMode &= ~FMOD_3D_LOGROLLOFF; + mMode &= ~FMOD_3D_LINEARROLLOFF; + mMode |= FMOD_3D_CUSTOMROLLOFF; + } + + if (mode & FMOD_3D_IGNOREGEOMETRY) + { + mMode |= FMOD_3D_IGNOREGEOMETRY; + } + else + { + mMode &= ~FMOD_3D_IGNOREGEOMETRY; + } + + if (mode & FMOD_VIRTUAL_PLAYFROMSTART) + { + mMode |= FMOD_VIRTUAL_PLAYFROMSTART; + } + else + { + mMode &= ~FMOD_VIRTUAL_PLAYFROMSTART; + } + + /* + Allow switching between 2D and 3D only in software. + */ +#if !defined( PLATFORM_GC ) && !defined( PLATFORM_WII ) && !defined( PLATFORM_PSP ) && !defined( PLATFORM_PS2 ) + if (!(mMode & FMOD_HARDWARE)) +#endif + { + if (mode & FMOD_2D) + { + if (!mParent) + { + return FMOD_OK; + } + + mMode &= ~FMOD_3D; + mMode |= FMOD_2D; + mParent->mVolume3D = 1.0f; + mParent->mDirectOcclusion = 0.0f; + mParent->mReverbDryVolume = 1.0f; + mParent->mConeVolume3D = 1.0f; + mParent->mPitch3D = 1.0f; + } + else if (mode & FMOD_3D) + { + mMode &= ~FMOD_2D; + mMode |= FMOD_3D; + } + } + + return FMOD_OK; +} + + +#ifdef PLATFORM_WII +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setLowPassFilter(int cutoff) +{ + mLPFCutoff = cutoff; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getLowPassFilter(int *cutoff) +{ + *cutoff = mLPFCutoff; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setBiquadFilter(bool active, unsigned short b0, unsigned short b1, unsigned short b2, unsigned short a1, unsigned short a2) +{ + mBiquadActive = active; + mBiquadB0 = b0; + mBiquadB1 = b1; + mBiquadB2 = b2; + mBiquadA1 = a1; + mBiquadA2 = a2; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getBiquadFilter(bool *active, unsigned short *b0, unsigned short *b1, unsigned short *b2, unsigned short *a1, unsigned short *a2) +{ + *active = mBiquadActive; + *b0 = mBiquadB0; + *b1 = mBiquadB1; + *b2 = mBiquadB2; + *a1 = mBiquadA1; + *a2 = mBiquadA2; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::setControllerSpeaker(unsigned int controllerspeaker, int subchannel) +{ + mControllerSpeaker = controllerspeaker; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelReal::getControllerSpeaker(unsigned int *controllerspeaker) +{ + *controllerspeaker = mControllerSpeaker; + + return FMOD_OK; +} +#endif + +} + + diff --git a/src/fmod_channel_real.h b/src/fmod_channel_real.h new file mode 100755 index 0000000..5e3a8f3 --- /dev/null +++ b/src/fmod_channel_real.h @@ -0,0 +1,173 @@ +#ifndef _FMOD_CHANNEL_REAL_H +#define _FMOD_CHANNEL_REAL_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_channeli.h" +#include "fmod_types.h" + +namespace FMOD +{ + class ChannelPool; + class DSPI; + class Output; + class SoundI; + class SystemI; + struct ChannelRealData; + class ReverbI; + + typedef enum + { + CHANNELREAL_TYPE_HW3D, + CHANNELREAL_TYPE_HW2D, + CHANNELREAL_TYPE_SW, + CHANNELREAL_TYPE_MAX + } CHANNELREAL_TYPE; + + #define CHANNELREAL_TYPE_HW CHANNELREAL_TYPE_HW3D + + typedef unsigned int CHANNELREAL_FLAG; + + #define CHANNELREAL_FLAG_ALLOCATED 0x00000010 + #define CHANNELREAL_FLAG_PAUSED 0x00000020 + #define CHANNELREAL_FLAG_PLAYING 0x00000040 + #define CHANNELREAL_FLAG_STOPPED 0x00000080 + #define CHANNELREAL_FLAG_IN_USE 0x00000100 + #define CHANNELREAL_FLAG_NOREVERB 0x00000800 + #define CHANNELREAL_FLAG_RESERVED 0x00001000 + #define CHANNELREAL_FLAG_HASPLAYED 0x00002000 + #define CHANNELREAL_FLAG_PAUSEDFORSETPOS 0x00004000 + #define CHANNELREAL_FLAG_MAX 0xFFFFFFFF + + typedef enum + { + CHANNELREAL_PLAYDIR_FORWARDS, + CHANNELREAL_PLAYDIR_BACKWARDS, + CHANNELREAL_PLAYDIR_MAX = 0xFFFFFFFF + } CHANNELREAL_PLAYDIR; + + /* + ************************************************************************************* + DON'T IFDEF STUFF OUT THAT MIGHT BE INCLUDED IN FMOD.DLL BUT NOT IN A PLUGIN DLL! + (staticforplugins vs fmod.dll code sharing conflict) + ************************************************************************************* + */ + class ChannelReal + { + friend class ChannelPool; + friend class SystemI; + friend class ChannelI; + friend class ChannelStream; + friend class MusicSong; + friend class Stream; + friend class SoundI; + friend class DSPWaveTable; + friend class DSPResampler; + friend class DSPCodec; + friend class OutputOpenAL; + friend class AsyncThread; + + protected: + + SystemI *mSystem; + int mSubChannelIndex; + ChannelPool *mPool; + ChannelI *mParent; + + Output *mOutput; + SoundI *mSound; + DSPI *mDSP; + FMOD_MODE mMode; + CHANNELREAL_FLAG mFlags; + + int mIndex; + unsigned int mPosition; + int mDirection; + int mLoopCount; + unsigned int mLoopStart; + unsigned int mLoopLength; + unsigned int mLength; + float mMaxFrequency; + float mMinFrequency; + int mSubSoundListCurrent; + + #ifdef PLATFORM_WII + int mLPFCutoff; + bool mBiquadActive; + unsigned short mBiquadB0; + unsigned short mBiquadB1; + unsigned short mBiquadB2; + unsigned short mBiquadA1; + unsigned short mBiquadA2; + unsigned int mControllerSpeaker; + #endif + + FMOD_RESULT init(); + FMOD_RESULT calcVolumeAndPitchFor3D(); + virtual FMOD_RESULT set2DFreqVolumePanFor3D(); + virtual bool isStream() { return false; } + virtual FMOD_RESULT moveChannelGroup(ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup, bool forcedspreconnect) { return FMOD_OK; } + + public: + + ChannelReal(); + virtual ~ChannelReal() { } + + virtual FMOD_RESULT init (int index, SystemI *system, Output *output, DSPI *dspmixtarget); + virtual FMOD_RESULT close (); + virtual FMOD_RESULT alloc (); + virtual FMOD_RESULT alloc (DSPI *dsp); + virtual FMOD_RESULT start (); + virtual FMOD_RESULT update (int delta); + virtual FMOD_RESULT updateStream (); + virtual FMOD_RESULT stop (); + virtual FMOD_RESULT setPaused (bool paused); + virtual FMOD_RESULT getPaused (bool *paused); + virtual FMOD_RESULT setVolume (float volume); + virtual FMOD_RESULT setFrequency (float frequency); + virtual FMOD_RESULT setPan (float pan, float fbpan = 1); + virtual FMOD_RESULT setDSPClockDelay (); + virtual FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + virtual FMOD_RESULT setSpeakerLevels (int speaker, float *levels, int numlevels); + virtual FMOD_RESULT updateSpeakerLevels (float volume); + virtual FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype); + virtual FMOD_RESULT setPositionEx (unsigned int position, FMOD_TIMEUNIT postype, bool fromasync) { return setPosition(position, postype); } + virtual FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype); + virtual FMOD_RESULT setLoopPoints (unsigned int loopstart, unsigned int looplength); + virtual FMOD_RESULT setLoopCount (int loopcount); + virtual FMOD_RESULT setLowPassGain (float gain); + virtual FMOD_RESULT set3DAttributes (); + virtual FMOD_RESULT set3DMinMaxDistance (); + virtual FMOD_RESULT set3DOcclusion (float directOcclusion, float reverbOcclusion); + virtual FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + virtual FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + virtual FMOD_RESULT isPlaying (bool *isplaying, bool includethreadlatency = false); + virtual FMOD_RESULT isVirtual (bool *isvirtual); + virtual FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + virtual FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset); + virtual FMOD_RESULT getDSPHead (DSPI **dsp); + virtual FMOD_RESULT setMode (FMOD_MODE mode); + +#ifdef PLATFORM_WII + virtual FMOD_RESULT setLowPassFilter (int cutoff); + virtual FMOD_RESULT getLowPassFilter (int *cutoff); + virtual FMOD_RESULT setBiquadFilter (bool active, unsigned short b0, unsigned short b1, unsigned short b2, unsigned short a1, unsigned short a2); + virtual FMOD_RESULT getBiquadFilter (bool *active, unsigned short *b0, unsigned short *b1, unsigned short *b2, unsigned short *a1, unsigned short *a2); + virtual FMOD_RESULT setControllerSpeaker (unsigned int controllerspeaker, int subchannel = -1); + virtual FMOD_RESULT getControllerSpeaker (unsigned int *controllerspeaker); +#endif + FMOD_RESULT setReserved(bool reserved) + { + if (mFlags & (CHANNELREAL_FLAG_ALLOCATED | CHANNELREAL_FLAG_IN_USE)) return FMOD_ERR_CHANNEL_ALLOC; + if (reserved) mFlags |= CHANNELREAL_FLAG_RESERVED; else mFlags &= ~CHANNELREAL_FLAG_RESERVED; + return FMOD_OK; + } + FMOD_RESULT allowReverb(bool allow) { if (allow) mFlags &= ~CHANNELREAL_FLAG_NOREVERB; else mFlags |= CHANNELREAL_FLAG_NOREVERB; return FMOD_OK; } + FMOD_RESULT hasPlayed(bool *hasplayed) { if (hasplayed) *hasplayed = ((mFlags & CHANNELREAL_FLAG_HASPLAYED) ? true : false); return FMOD_OK; } + }; +} + +#endif + + diff --git a/src/fmod_channel_realmanual3d.cpp b/src/fmod_channel_realmanual3d.cpp new file mode 100755 index 0000000..303315f --- /dev/null +++ b/src/fmod_channel_realmanual3d.cpp @@ -0,0 +1,671 @@ +#include "fmod_settings.h" + +#include "fmod_3d.h" +#include "fmod_channeli.h" +#include "fmod_channel_realmanual3d.h" +#include "fmod_soundi.h" +#include "fmod_speakermap.h" +#include "fmod_systemi.h" + +#include + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelRealManual3D::ChannelRealManual3D() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + mAngleToListener = 0.0f; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelRealManual3D::alloc() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + mAngleToListener = 0.0f; +#endif + + return ChannelReal::alloc(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelRealManual3D::set2DFreqVolumePanFor3D() +{ +#ifdef FMOD_SUPPORT_3DSOUND + float lrpan[FMOD_CHANNEL_MAXINPUTCHANNELS] = { 0 }, fbpan[FMOD_CHANNEL_MAXINPUTCHANNELS] = { 0 }; + int subchannel, numsubchannels; + float lrpan_base = 0.0f, fbpan_base = 0.0f, angle = 0.0f; + bool monospread = false; + + if (!(mMode & FMOD_3D)) + { + return FMOD_OK; + } + if (mSound) + { + if (mSound->mSubSampleParent) + { + numsubchannels = mSound->mSubSampleParent->mChannels; + } + else + { + numsubchannels = mSound->mChannels; + } + } + else if (mDSP) + { + numsubchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + /* + CALCULATE 3D PAN + */ + if (mSystem->mNumListeners == 1) + { + FMOD_VECTOR currdiff; + FMOD_VECTOR front, right; + FMOD_VECTOR position; + float distance; + + position = mParent->mPosition3D; + + /* + Distance between emitter and listener this frame + */ + if (mMode & FMOD_3D_HEADRELATIVE) + { + front.x = 0.0f; + front.y = 0.0f; + front.z = 1.0f; + right.x = 1.0f; + right.y = 0.0f; + right.z = 0.0f; + + currdiff = position; + } + else + { + front = mSystem->mListener[0].mFront; + right = mSystem->mListener[0].mRight; + FMOD_Vector_Subtract(&position, &mSystem->mListener[0].mPosition, &currdiff); + } + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + front.z = -front.z; + currdiff.z = -currdiff.z; + } + + /* + Normalize difference vector + */ + distance = FMOD_Vector_GetLength(&currdiff); + + if (distance <= 0) + { + currdiff.x = 0; + currdiff.y = 0; + currdiff.z = 0; + } + else + { + /* + Normalize + */ + currdiff.x /= (float)distance; + currdiff.y /= (float)distance; + currdiff.z /= (float)distance; + } + + /* + Base L/R and F/B pan, and the base direction of the sound + */ + lrpan_base = FMOD_Vector_DotProduct(&currdiff, &right); + fbpan_base = FMOD_Vector_DotProduct(&currdiff, &front); + + if (lrpan_base > 1.01f || lrpan_base < -1.01f) + { + return FMOD_ERR_INVALID_VECTOR; + } + if (fbpan_base > 1.01f || fbpan_base < -1.01f) + { + return FMOD_ERR_INVALID_VECTOR; + } + + /* + Clamp (floating point accuracy issues on some platforms) + */ + lrpan_base = lrpan_base > 1.0f ? 1.0f : lrpan_base; + lrpan_base = lrpan_base < -1.0f ? -1.0f : lrpan_base; + fbpan_base = fbpan_base > 1.0f ? 1.0f : fbpan_base; + fbpan_base = fbpan_base < -1.0f ? -1.0f : fbpan_base; + + angle = FMOD_AngleSort_GetValue(lrpan_base, fbpan_base); + +#ifdef FMOD_SUPPORT_SOFTWARE + mAngleToListener = FMOD_ACOS(fbpan_base) * (180.0f / FMOD_PI); +#endif + + /* + Spread the pan field for each part of a sound using the spread angle. + If a stereo sound has a spread of 90 degrees, the pan will be rotated 45 degrees left for left channel, 45 degrees right for right channel. + */ + float radian_spread = mParent->mSpread * FMOD_PI / 180.0f; + + if (mParent->mSpread != 0.0f && numsubchannels == 1) + { + monospread = true; + numsubchannels = 2; + } + + for (subchannel = 0; subchannel < numsubchannels; subchannel++) + { + if (mParent->mSpread == 0.0f || numsubchannels == 1) + { + lrpan[subchannel] = lrpan_base; + fbpan[subchannel] = fbpan_base; + } + else + { + // + // Map input channel to speaker position index in spread + // We don't have access to the sound's channel format so make a guess + // For spread pos -1.0 for leftmost, 1.0 for rightmost + // + float spread_pos = 0.0f; + switch(numsubchannels) + { + case 4: // Could be either L/R/LR/RR or L/R/C/B so last 2 subs get centre + switch(subchannel) + { + case 0: spread_pos = -1.0f; break; // L + case 1: spread_pos = 1.0f; break; // R + case 2: spread_pos = 0.0f; break; // unknown + case 3: spread_pos = 0.0f; break; // unknown + } + break; + + case 6: + switch(subchannel) + { + case 0: spread_pos = -0.5f; break; // L + case 1: spread_pos = 0.5f; break; // R + case 2: spread_pos = 0.0f; break; // C + case 3: spread_pos = 0.0f; break; // LFE + case 4: spread_pos = -1.0f; break; // LR + case 5: spread_pos = 1.0f; break; // RR + } + break; + case 8: + switch(subchannel) + { + case 0: spread_pos = -0.333f; break; // L + case 1: spread_pos = 0.333f; break; // R + case 2: spread_pos = 0.0f; break; // C + case 3: spread_pos = 0.0f; break; // LFE + case 4: spread_pos = -1.0f; break; // LR + case 5: spread_pos = 1.0f; break; // RR + case 6: spread_pos = -0.667f; break; // LS + case 7: spread_pos = 0.667f; break; // RS + } + break; + default: + spread_pos = -1.0f + 2.0f * (float)subchannel / (float)(numsubchannels - 1); + break; + } + float theta = -0.5f * spread_pos * radian_spread; + float cost = FMOD_COS(theta); + float sint = FMOD_SIN(theta); + + lrpan[subchannel] = (cost * lrpan_base) - (sint * fbpan_base); + fbpan[subchannel] = (cost * fbpan_base) + (sint * lrpan_base); + } + } + } + + /* + CALL THE DRIVER TO SET THE VOLUME, FREQUENCY AND PAN + */ + + setFrequency(mParent->mFrequency); + + if (mParent->mSpeakerMode != FMOD_SPEAKERMODE_MONO && + mParent->mSpeakerMode != FMOD_SPEAKERMODE_RAW) + { + float speakerlevel[FMOD_SPEAKER_MAX][FMOD_CHANNEL_MAXINPUTCHANNELS]; /* 8 * 16 * sizeof(float) = 512 bytes */ + int numspeakers = 0, numrealspeakers = 0; + int subchannel; + + FMOD_memset(speakerlevel, 0, sizeof(float) * FMOD_SPEAKER_MAX * FMOD_CHANNEL_MAXINPUTCHANNELS); + + for (subchannel = 0; subchannel < numsubchannels; subchannel++) + { + speakerlevel[FMOD_SPEAKER_LOW_FREQUENCY][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_LOW_FREQUENCY].mActive ? mParent->mSpeakerLFE : 0; + } + + if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_STEREO) + { + numspeakers = 2; + numrealspeakers = 2; + } + else if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC) + { + numspeakers = 5; + numrealspeakers = 6; + } + else if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_QUAD) + { + numspeakers = 4; + numrealspeakers = 4; + } + else if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_SURROUND) + { + numspeakers = 5; + numrealspeakers = 5; + } + else if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_5POINT1) + { + numspeakers = 5; + numrealspeakers = 6; + } + else if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_7POINT1) + { + numspeakers = 7; + numrealspeakers = 8; + } + + if (mParent->m3DPanLevel > 0.0f) /* 0.0 = pure 2d. Less than this has some 3d component. 1 = pure 3d. */ + { + for (subchannel = 0; subchannel < numsubchannels; subchannel++) + { + if (fbpan[subchannel] == 0 && lrpan[subchannel] == 0) + { + float level = 1.4142135623730950488016887242097f / numspeakers; + + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_FRONT_LEFT].mActive ? level : 0; + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_FRONT_RIGHT].mActive ? level : 0; + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_FRONT_CENTER].mActive ? level : 0; + speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_BACK_LEFT].mActive ? level : 0; + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_BACK_RIGHT].mActive ? level : 0; + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_SIDE_LEFT].mActive ? level : 0; + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel] = mSystem->mSpeaker[FMOD_SPEAKER_SIDE_RIGHT].mActive ? level : 0; + } + else + { + int speaker; + float subchannelangle; + + if (lrpan[subchannel] == lrpan_base && fbpan[subchannel] == fbpan_base) + { + subchannelangle = angle; + } + else + { + subchannelangle = FMOD_AngleSort_GetValue(lrpan[subchannel], fbpan[subchannel]); + } + + if (!mSystem->mSpeakerList[0]) /* No speakers are active. */ + { + + } + else if (!mSystem->mSpeakerList[1]) /* Only 1 speaker is active. */ + { + speakerlevel[mSystem->mSpeakerList[0]->mSpeaker][subchannel] = 1.0f; + } + else + { + for (speaker = 1; speaker < numspeakers + 1; speaker++) + { + FMOD_SPEAKERCONFIG *spkA, *spkB; + + spkA = mSystem->mSpeakerList[speaker-1]; + if (mSystem->mSpeakerList[speaker]) + { + spkB = mSystem->mSpeakerList[speaker]; + } + else + { + spkB = mSystem->mSpeakerList[0]; + } + + if (spkA->mXZAngle == spkB->mXZAngle) /* 2 speakers are at the same location so skip to the next pair */ + { + continue; + } + + if (FMOD_AngleSort_IsClockwise(spkA->mXZAngle, subchannelangle, spkB->mXZAngle)) + { + /* + VBAP only makes sense if source direction is in the vector space spanned by positive gains. + So if the speaker angles are 180 deg apart then the best we can do is resolve the lateral + (side-to-side) component of the source's direction. + */ + if (spkA->mPairUseVBAP) + { + /* + Use Vector-Based Amplitude Panning by soving the equations: + 1. [x_src] = [x_spkA x_spkB] * [gainA'] + [y_src] [y_spkA y_spkB] [gainB'] + 2. gainA = k * gainA' + 3. gainB = k * gainB' + 4. gainA ^ 2 + gainB ^ 2 = 1 + Note: there is no need to evaluate the determinant when inverting the speaker matrix + in Eq.1 since gains will we normalised for constant power. + */ + float gainA = lrpan[subchannel] * spkB->mXZNormal.z - fbpan[subchannel] * spkB->mXZNormal.x; + float gainB = fbpan[subchannel] * spkA->mXZNormal.x - lrpan[subchannel] * spkA->mXZNormal.z; + float norm = spkA->mPairVBAPSign / FMOD_SQRT(gainA * gainA + gainB * gainB); + speakerlevel[spkA->mSpeaker][subchannel] = gainA * norm; + speakerlevel[spkB->mSpeaker][subchannel] = gainB * norm; + } + else + { + /* + Resolve lateral component using the dot product, solving the following equations: + 1. u_src = u_spkA * gainA + u_spkB * gainB + 2. gainA ^ 2 + gainB ^ 2 = 1 + where 'u_' denotes the lateral component of the source and speaker unit direction vectors + */ + float lateral = (lrpan[subchannel] * spkA->mXZNormal.x + fbpan[subchannel] * spkA->mXZNormal.z); + float d = FMOD_SQRT(2.0f - lateral * lateral); + speakerlevel[spkA->mSpeaker][subchannel] = (d + lateral) * 0.5f; + speakerlevel[spkB->mSpeaker][subchannel] = (d - lateral) * 0.5f; + } + break; + } + } + } + } + } + } + + if (numsubchannels > 1) + { + int speaker; + float levels[DSP_MAXLEVELS_OUT * DSP_MAXLEVELS_IN] = { 0 }; + int numinputlevels; + + if (mParent->m3DPanLevel < 1.0f) + { + float _3dmix = mParent->m3DPanLevel, _2dmix = 1.0f - mParent->m3DPanLevel; + + if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_PAN || mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + float fl = 0.0f, fr = 0.0f, c = 0.0f, lfe = 0.0f, bl = 0.0f, br = 0.0f, sl = 0.0f, sr = 0.0f; + + if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_PAN) + { + float pan = (mParent->mPan + 1.0f) / 2.0f; + float l, r; + + if (pan <= 0.5f) + { + l = 1.0f; + r = pan * 2.0f; + } + else + { + l = (1.0f - pan) * 2.0f; + r = 1.0f; + } + + if (numsubchannels == 2 && !(mSound->mDefaultChannelMask & SPEAKER_ALLMONO)) + { + fl = l; fr = r; c = 0; lfe = 0; bl = 0; br = 0; sl = 0; sr = 0; + } + else + { + fl = l; fr = r; c = 1.0f; lfe = 1.0f; bl = l; br = r; sl = l; sr = r; + } + } + else if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + fl = mParent->mSpeakerFL; + fr = mParent->mSpeakerFR; + c = mParent->mSpeakerC; + lfe = mParent->mSpeakerLFE; + bl = mParent->mSpeakerBL; + br = mParent->mSpeakerBR; + sl = mParent->mSpeakerSL; + sr = mParent->mSpeakerSR; + } + + FMOD_SPEAKERMAPTYPE speakermap = FMOD_SPEAKERMAPTYPE_DEFAULT; + + if (mSound) + { + if (mSound->mDefaultChannelMask & SPEAKER_ALLMONO) + { + speakermap = FMOD_SPEAKERMAPTYPE_ALLMONO; + } + else if (mSound->mDefaultChannelMask & SPEAKER_ALLSTEREO) + { + speakermap = FMOD_SPEAKERMAPTYPE_ALLSTEREO; + } + else if (mSound->mDefaultChannelMask & SPEAKER_PROTOOLS) + { + speakermap = FMOD_SPEAKERMAPTYPE_51_PROTOOLS; + } + } + + DSPI::calculateSpeakerLevels(fl, + fr, + c, + lfe, + bl, + br, + sl, + sr, + mParent->mSpeakerMode, + numsubchannels, + speakermap, + levels, + &numinputlevels); + + for (subchannel = 0; subchannel < numsubchannels; subchannel++) + { + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel] = (speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_FRONT_LEFT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel] = (speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_FRONT_RIGHT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel] = (speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_FRONT_CENTER * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel] = (speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_BACK_LEFT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel] = (speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_BACK_RIGHT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel] = (speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_SIDE_LEFT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel] = (speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel] * _3dmix) + (levels[(FMOD_SPEAKER_SIDE_RIGHT * numinputlevels) + subchannel] * _2dmix); + } + } + else if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS && mParent->mLevels) + { + numinputlevels = mSystem->mMaxInputChannels; + + for (subchannel = 0; subchannel < numsubchannels; subchannel++) + { + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel] = (speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_FRONT_LEFT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel] = (speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_FRONT_RIGHT * numinputlevels) + subchannel] * _2dmix); + + if (numspeakers > 2) + { + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel] = (speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_FRONT_CENTER * numinputlevels) + subchannel] * _2dmix); + + if (numspeakers > 3) + { + speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel] = (speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_BACK_LEFT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel] = (speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_BACK_RIGHT * numinputlevels) + subchannel] * _2dmix); + + if (numspeakers > 5) + { + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel] = (speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_SIDE_LEFT * numinputlevels) + subchannel] * _2dmix); + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel] = (speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel] * _3dmix) + (mParent->mLevels[(FMOD_SPEAKER_SIDE_RIGHT * numinputlevels) + subchannel] * _2dmix); + } + } + } + } + } + } + + if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC && (mSound->mMode & FMOD_SOFTWARE)) /* Downmix logic. */ + { + for (subchannel = 0; subchannel < numsubchannels; subchannel++) + { + DSPI::calculateSpeakerLevels(speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel], + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel], + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel], + speakerlevel[FMOD_SPEAKER_LOW_FREQUENCY][subchannel], + speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel], + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel], + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel], + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel], + mParent->mSpeakerMode, + 1, + FMOD_SPEAKERMAPTYPE_DEFAULT, + levels, + &numinputlevels); + + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][subchannel] = levels[0]; + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][subchannel] = levels[1]; + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][subchannel] = 0; + speakerlevel[FMOD_SPEAKER_BACK_LEFT][subchannel] = 0; + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][subchannel] = 0; + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][subchannel] = 0; + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][subchannel] = 0; + } + } + + if (monospread) + { + numsubchannels = 1; /* Put it back to normal */ + } + + for (speaker = 0; speaker < numrealspeakers; speaker++) + { + if (monospread) + { + speakerlevel[speaker][0] = (speakerlevel[speaker][0] + speakerlevel[speaker][1]) / 2.0f; + } + setSpeakerLevels(speaker, speakerlevel[speaker], numsubchannels); + } + } + else + { + if (mParent->m3DPanLevel < 1.0f) + { + float _3dmix = mParent->m3DPanLevel, _2dmix = 1.0f - mParent->m3DPanLevel; + + if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_PAN) + { + float pan = (mParent->mPan + 1.0f) / 2.0f; + float l = FMOD_SQRT(1.0f - pan); + float r = FMOD_SQRT(pan); + + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0] = (speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0] * _3dmix) + (l * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0] * _3dmix) + (r * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0] = (speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0] * _3dmix); + speakerlevel[FMOD_SPEAKER_BACK_LEFT][0] = (speakerlevel[FMOD_SPEAKER_BACK_LEFT][0] * _3dmix); + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0] * _3dmix); + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0] = (speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0] * _3dmix); + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0] * _3dmix); + } + else if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0] = (speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0] * _3dmix) + (mParent->mSpeakerFL * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0] * _3dmix) + (mParent->mSpeakerFR * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0] = (speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0] * _3dmix) + (mParent->mSpeakerC * _2dmix); + speakerlevel[FMOD_SPEAKER_BACK_LEFT][0] = (speakerlevel[FMOD_SPEAKER_BACK_LEFT][0] * _3dmix) + (mParent->mSpeakerBL * _2dmix); + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0] * _3dmix) + (mParent->mSpeakerBR * _2dmix); + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0] = (speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0] * _3dmix) + (mParent->mSpeakerSL * _2dmix); + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0] * _3dmix) + (mParent->mSpeakerSR * _2dmix); + } + else if (mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS && mParent->mLevels) + { + speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0] = (speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_FRONT_LEFT] * _2dmix); + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_FRONT_RIGHT] * _2dmix); + + if (numspeakers > 2) + { + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0] = (speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_FRONT_CENTER] * _2dmix); + + if (numspeakers > 3) + { + speakerlevel[FMOD_SPEAKER_BACK_LEFT][0] = (speakerlevel[FMOD_SPEAKER_BACK_LEFT][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_BACK_LEFT] * _2dmix); + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_BACK_RIGHT] * _2dmix); + + if (numspeakers > 5) + { + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0] = (speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_SIDE_LEFT] * _2dmix); + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0] = (speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0] * _3dmix) + (mParent->mLevels[mSystem->mMaxInputChannels * FMOD_SPEAKER_SIDE_RIGHT] * _2dmix); + } + } + } + } + } + + setSpeakerMix(speakerlevel[FMOD_SPEAKER_FRONT_LEFT][0], + speakerlevel[FMOD_SPEAKER_FRONT_RIGHT][0], + speakerlevel[FMOD_SPEAKER_FRONT_CENTER][0], + mParent->mSpeakerLFE, + speakerlevel[FMOD_SPEAKER_BACK_LEFT][0], + speakerlevel[FMOD_SPEAKER_BACK_RIGHT][0], + speakerlevel[FMOD_SPEAKER_SIDE_LEFT][0], + speakerlevel[FMOD_SPEAKER_SIDE_RIGHT][0]); + } + } + + setVolume(mParent->mVolume); +#endif + + return FMOD_OK; +} + + +} + diff --git a/src/fmod_channel_realmanual3d.h b/src/fmod_channel_realmanual3d.h new file mode 100755 index 0000000..910d997 --- /dev/null +++ b/src/fmod_channel_realmanual3d.h @@ -0,0 +1,27 @@ +#ifndef _FMOD_CHANNEL_REALMANUAL3D_H +#define _FMOD_CHANNEL_REALMANUAL3D_H + +#include "fmod_settings.h" + +#include "fmod_channel_real.h" + +namespace FMOD +{ + class ChannelRealManual3D : public ChannelReal + { + friend class ChannelSoftware; + +#ifdef FMOD_SUPPORT_SOFTWARE + float mAngleToListener; +#endif + + public: + + ChannelRealManual3D(); + + FMOD_RESULT alloc(); + FMOD_RESULT set2DFreqVolumePanFor3D(); + }; +} + +#endif diff --git a/src/fmod_channel_software.cpp b/src/fmod_channel_software.cpp new file mode 100755 index 0000000..6cda540 --- /dev/null +++ b/src/fmod_channel_software.cpp @@ -0,0 +1,3300 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_3d.h" +#include "fmod_channel_software.h" +#include "fmod_codeci.h" +#ifdef FMOD_SUPPORT_FSB +#include "fmod_codec_fsb.h" +#endif +#ifdef FMOD_SUPPORT_MPEG +#include "fmod_codec_mpeg.h" +#endif +#ifdef FMOD_SUPPORT_XMA +#include "fmod_codec_xma.h" +#endif +#if defined(FMOD_SUPPORT_WAV) && defined(FMOD_SUPPORT_IMAADPCM) +#include "fmod_codec_wav.h" +#include "fmod_codec_wav_imaadpcm.h" +#endif +#include "fmod_dsp_codec.h" +#include "fmod_dsp_fft.h" +#include "fmod_dsp_resampler.h" +#include "fmod_dsp_wavetable.h" +#include "fmod_dspi.h" +#include "fmod_sample_software.h" +#include "fmod_speakermap.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelSoftware::ChannelSoftware() +{ + mDSPWaveTable = 0; + mDSPHead = 0; + mDSPResampler = 0; + mDSPCodec = 0; + mDSPLowPass = 0; + mDSP = 0; + mDSPConnection = 0; + mDSPReverb = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::init(int index, SystemI *system, Output *output, DSPI *dspmixtarget) +{ + FMOD_RESULT result; + FMOD_DSP_DESCRIPTION_EX descriptionex; + + ChannelReal::init(index, system, output, dspmixtarget); + + /* + Create a head unit that things can connect to. + */ + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD Channel DSPHead Unit"); + descriptionex.version = 0x00010100; + descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER; + descriptionex.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + + #ifdef PLATFORM_PS3 + mDSPHead = (DSPI *)FMOD_ALIGNPOINTER(&mDSPHeadMemory, 16); + mDSPHead = new (mDSPHead) (DSPFilter); + #else + mDSPHead = &mDSPHeadMemory; + #endif + + result = mSystem->createDSP(&descriptionex, &mDSPHead, false); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_PS3 + mDSPHead->mMramAddress = (unsigned int)mDSPHead; + mDSPHead->mDescription.mSize = (sizeof(DSPFilter) + 15) & ~15; + #endif + + /* + Create a lowpass unit + */ + if (mSystem->mFlags & FMOD_INIT_SOFTWARE_OCCLUSION || mSystem->mFlags & FMOD_INIT_SOFTWARE_HRTF) + { + result = mSystem->createDSPByType(FMOD_DSP_TYPE_LOWPASS_SIMPLE, &mDSPLowPass); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Create a wave table unit + */ + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD WaveTable Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = dspmixtarget->mDescription.channels; + descriptionex.read = 0; /* DSPWavetable uses DSPWaveTable::execute not a read callback. */ + + descriptionex.setparameter = DSPWaveTable::setParameterCallback; + descriptionex.getparameter = DSPWaveTable::getParameterCallback; + descriptionex.setposition = DSPWaveTable::setPositionCallback; + descriptionex.reset = DSPWaveTable::resetCallback; + + descriptionex.mCategory = FMOD_DSP_CATEGORY_WAVETABLE; + descriptionex.mFormat = dspmixtarget->mDescription.mFormat; + descriptionex.mDSPSoundCard = dspmixtarget; + + mDSPWaveTable = &mDSPWaveTableMemory; + + result = mSystem->createDSP(&descriptionex, (DSPI **)&mDSPWaveTable, false); + if (result != FMOD_OK) + { + return result; + } + mDSPWaveTable->setFinished(true, true); + + result = mDSPWaveTable->setUserData((void **)this); + if (result != FMOD_OK) + { + return result; + } + result = mDSPWaveTable->setTargetFrequency((int)dspmixtarget->mDefaultFrequency); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = -mMaxFrequency; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::close() +{ + FMOD_RESULT result; + + if (mDSPWaveTable) + { + mDSPWaveTable->mDSPSoundCard = 0; + } + + if (mDSPResampler) + { + mDSPResampler->mDSPSoundCard = 0; + } + + result = ChannelReal::close(); + if (result != FMOD_OK) + { + return result; + } + + if (mDSPWaveTable) + { + mDSPWaveTable->release(false); /* false = dont free this, as it is not alloced. */ + mDSPWaveTable = 0; + } + + if (mDSPHead) + { + mDSPHead->release(false); /* false = dont free this, as it is not alloced. */ + mDSPHead= 0; + } + + if (mDSPResampler) + { + mDSPResampler->release(); + mDSPResampler = 0; + } + + if (mDSPLowPass) + { + mDSPLowPass->release(); + mDSPLowPass = 0; + } + + mDSPCodec = 0; /* Don't free, it points to a pool codec. */ + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setupDSPCodec(DSPI *dsp) +{ +#ifdef FMOD_SUPPORT_DSPCODEC + FMOD_RESULT result; + DSPCodec *dspcodec; + Codec *codec, *soundcodec; + SampleSoftware *sample; + +#ifdef FMOD_SUPPORT_SENTENCING + if (mSound->mSubSoundList) + { + sample = SAFE_CAST(SampleSoftware, mSound->mSubSound[mSound->mSubSoundList[mSubSoundListCurrent].mIndex]); + } + else +#endif + { + sample = SAFE_CAST(SampleSoftware, mSound); + } + + dspcodec = SAFE_CAST(DSPCodec, dsp); + codec = SAFE_CAST(Codec, dspcodec->mCodec); + soundcodec = sample->mCodec ? sample->mCodec : sample->mSubSoundParent->mCodec; + + if (!soundcodec) + { + return FMOD_ERR_INTERNAL; + } + + #ifdef PLATFORM_PS3 + /* These need to be 0 so Codec::read doesn't try and read into mPCMBuffer on the SPU. */ + codec->mPCMBufferLength = 0; //soundcodec->mPCMBufferLength; + codec->mPCMBufferLengthBytes = 0; //soundcodec->mPCMBufferLength * sizeof(short) * dsp->mDescription.channels; + #else + codec->mPCMBufferLength = soundcodec->mPCMBufferLength; + codec->mPCMBufferLengthBytes = soundcodec->mPCMBufferLength * sizeof(short) * dsp->mDescription.channels; + #endif + + if (!codec->waveformat) + { + return FMOD_ERR_INTERNAL; + } + + soundcodec->mDescription.getwaveformat(soundcodec, sample->mSubSoundIndex, codec->waveformat); + + codec->mFlags = soundcodec->mFlags; + dspcodec->mNewPosition = 0xFFFFFFFF; + dspcodec->mNoDMA->mNewPosition = 0xFFFFFFFF; + dspcodec->mCodec->mFile = &dspcodec->mMemoryFile; + + #ifdef PLATFORM_PS3 + dspcodec->mMemoryFile.init(mSystem, sample->mLengthBytes, PS3_DMAFILE_BLOCKSIZE); + dspcodec->mMemoryFile.mFlags |= FMOD_FILE_BIGENDIAN; + #else + dspcodec->mMemoryFile.init(mSystem, sample->mLengthBytes, 0); + #endif + + dspcodec->mMemoryFile.mPosition = 0; + dspcodec->mMemoryFile.mMem = sample->mBuffer; + + /* + Copy codec specific things from the source sound to the dspcodec used in this channel. + */ + if (0) + { + } + #if defined(FMOD_SUPPORT_WAV) && defined(FMOD_SUPPORT_IMAADPCM) + if (sample->mType == FMOD_SOUND_TYPE_WAV && sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + CodecWav *srcwav = (CodecWav *)soundcodec; + CodecWav *destwav = (CodecWav *)codec; + + destwav->mSamplesPerADPCMBlock = srcwav->mSamplesPerADPCMBlock; + destwav->mReadBufferLength = srcwav->mReadBufferLength; + } + #endif + #if defined(FMOD_SUPPORT_WAV) && defined(FMOD_SUPPORT_RAW) && defined(FMOD_SUPPORT_IMAADPCM) + else if (sample->mType == FMOD_SOUND_TYPE_RAW && sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + CodecRaw *srcwav = (CodecRaw *)soundcodec; + CodecWav *destwav = (CodecWav *)codec; + + destwav->mSamplesPerADPCMBlock = srcwav->mSamplesPerADPCMBlock; + destwav->mReadBufferLength = srcwav->mReadBufferLength; + } + #endif + #if defined(FMOD_SUPPORT_FSB) + else if (sample->mType == FMOD_SOUND_TYPE_FSB) + { + CodecFSB *fsb = (CodecFSB *)soundcodec; + + if (0) + { + } + #if defined(FMOD_SUPPORT_XMA) + else if (sample->mFormat == FMOD_SOUND_FORMAT_XMA) + { + CodecXMA *destxma = (CodecXMA *)codec; + int **seektable = (int **)fsb->plugindata; + + if (destxma->mXMASeekable) + { + destxma->mSeekTable = seektable[sample->mSubSoundIndex]; + } + #ifdef FMOD_SUPPORT_XMA_NEWHAL + destxma->mBlockSize = 32*1024; // FSBank always encodes to 32kb blocks now, using XMA2. + #endif + } + #endif + #if defined(FMOD_SUPPORT_MPEG) + else if (sample->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + CodecMPEG *destmpeg = (CodecMPEG *)codec; + + destmpeg->mPCMFrameLengthBytes = codec->waveformat[0].channels * 2304; + } + #endif + #if defined(FMOD_SUPPORT_IMAADPCM) + else if (sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + CodecWav *srcwav = fsb->mADPCM; + CodecWav *destwav = (CodecWav *)codec; + + destwav->mSamplesPerADPCMBlock = srcwav->mSamplesPerADPCMBlock; + destwav->mReadBufferLength = codec->waveformat[0].channels * 36; + } + #endif + #if defined(FMOD_SUPPORT_CELT) + else if (sample->mFormat == FMOD_SOUND_FORMAT_CELT) + { + CodecCELT *destcelt = (CodecCELT *)codec; + + destcelt->mPCMFrameLengthBytes = codec->waveformat[0].channels * FMOD_CELT_FRAMESIZESAMPLES * sizeof(short); + } + #endif + + fsb->mDescription.getwaveformat(fsb, sample->mSubSoundIndex, codec->waveformat); + } + #endif + #if defined(FMOD_SUPPORT_XMA) + else if (sample->mFormat == FMOD_SOUND_FORMAT_XMA) + { + CodecXMA *srcxma = (CodecXMA *)soundcodec; + CodecXMA *destxma = (CodecXMA *)codec; + if (destxma->mXMASeekable) + { + destxma->mSeekTable = srcxma->mSeekTable; + } + #ifdef FMOD_SUPPORT_XMA_NEWHAL + destxma->mBlockSize = srcxma->mBlockSize; + #endif + } + #endif + #if defined(FMOD_SUPPORT_MPEG) + else if (sample->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + CodecMPEG *srcmpeg = (CodecMPEG *)soundcodec; + CodecMPEG *destmpeg = (CodecMPEG *)codec; + + destmpeg->mPCMFrameLengthBytes = srcmpeg->mPCMFrameLengthBytes; + } + #endif + + /* + If the codec is a pcm codec, fix up the blockalign and compressed bytes values. + */ + #if defined(FMOD_SUPPORT_RAW) + if (sample->mFormat == FMOD_SOUND_FORMAT_PCM16) + { + mMinFrequency = -mMaxFrequency; + + dspcodec->mWaveFormat.blockalign = sizeof(short) * dspcodec->mWaveFormat.channels; + dspcodec->mWaveFormat.lengthbytes = dspcodec->mWaveFormat.lengthpcm * dspcodec->mWaveFormat.blockalign; + } + #endif + + dsp->mDescription.channels = sample->mChannels; + + result = dsp->setTargetFrequency((int)mParent->mChannelGroup->mDSPMixTarget->mDefaultFrequency); + if (result != FMOD_OK) + { + return result; + } + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + if (sample->mFormat != FMOD_SOUND_FORMAT_PCM16) +#endif + { + mMinFrequency = 0; + } + + mDSPCodec = dspcodec; + mDSPCodec->mLength = mSound->mLength; + mDSPCodec->mDefaultFrequency = sample->mDefaultFrequency; + mDSPCodec->mLoopCount = mLoopCount; + mDSPCodec->mPosition = 0; + mDSPCodec->DSPResampler::mPosition.mHi = 0; + mDSPCodec->DSPResampler::mPosition.mLo = 0; + mDSPCodec->mNoDMA->mLoopStart = mLoopStart; + mDSPCodec->mNoDMA->mLoopLength = mLoopLength; + mDSPCodec->mNoDMA->mMode = mMode; + mDSPCodec->mNoDMA->mDSPClockStart.mHi = 0; + mDSPCodec->mNoDMA->mDSPClockStart.mLo = 0; + mDSPCodec->mNoDMA->mDSPClockEnd.mHi = 0; + mDSPCodec->mNoDMA->mDSPClockEnd.mLo = 0; + mDSPCodec->mNoDMA->mDSPClockPause.mHi = 0; + mDSPCodec->mNoDMA->mDSPClockPause.mLo = 0; + mDSPCodec->mNoDMA->mNewLoopCount = -2; + +#ifdef FMOD_SUPPORT_SENTENCING + if (mSound->mSubSoundList) + { + result = mDSPCodec->setUserData((void **)mSound); + if (result != FMOD_OK) + { + return result; + } + + mDSPCodec->mSubSoundListCurrent = 0; + mDSPCodec->mSubSoundListNum = mSound->mSubSoundListNum; + } +#endif + + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::alloc() +{ + FMOD_RESULT result; + + result = ChannelRealManual3D::alloc(); + if (result != FMOD_OK) + { + return result; + } + + /* + Standard PCM *wavetable* playback. + */ + if (!(mMode & FMOD_CREATECOMPRESSEDSAMPLE) +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + && (mParent->mFlags & CHANNELI_FLAG_MUSICOWNED) +#endif + ) + { + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INTERNAL; + } + + mDSPCodec = 0; + + /* + Disconnect existing nodes first. + */ + result = mDSPHead->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + if (mDSPLowPass) + { + result = mDSPLowPass->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + } + result = mDSPWaveTable->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + + /* + Add to parent's channelgroup along with any lowpass. + */ + if (mDSPLowPass) + { + result = mParent->mChannelGroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, &mDSPConnection); + if (result != FMOD_OK) + { + return result; + } + result = mDSPHead->addInputQueued(mDSPLowPass, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + result = mDSPLowPass->addInputQueued(mDSPWaveTable, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mParent->mChannelGroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, &mDSPConnection); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPHead->addInputQueued(mDSPWaveTable, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + } + + if (!(mFlags & CHANNELREAL_FLAG_NOREVERB)) + { + mDSPReverb = mDSPWaveTable; + + result = addToReverbs(mDSPReverb); + if (result != FMOD_OK) + { + return result; + } + } + + result = setLoopPoints(mSound->mLoopStart, mSound->mLoopLength); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = -mMaxFrequency; + + dspwave->mSpeed.mHi = 0; + dspwave->mSpeed.mLo = 0; + dspwave->mPosition.mHi = 0; + dspwave->mPosition.mLo = 0; + dspwave->mDSPClockStart.mHi = 0; + dspwave->mDSPClockStart.mLo = 0; + dspwave->mDSPClockEnd.mHi = 0; + dspwave->mDSPClockEnd.mLo = 0; + dspwave->mDSPClockPause.mHi = 0; + dspwave->mDSPClockPause.mLo = 0; + + dspwave->mChannel = this; + dspwave->mSound = mSound; + + dspwave->mDirection = DSPWAVETABLE_SPEEDDIR_FORWARDS; + + mDSPHead->setActive(false); + if (mDSPLowPass) + { + mDSPLowPass->setActive(false); + } + mDSPWaveTable->setFinished(false); + mDSPWaveTable->setActive(false); + } + else + /* + Advanced - Compressed codec playback with external resampler unit. + */ + { +#ifdef FMOD_SUPPORT_DSPCODEC + DSPCodec *dsp; + + /* + Disconnect existing nodes first. + */ + result = mDSPHead->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + + if (mDSPLowPass) + { + result = mDSPLowPass->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + } + if (mDSPWaveTable) + { + result = mDSPWaveTable->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Grab a codec from the pool based on format. + */ + result = mSystem->allocateDSPCodec(mSound->mFormat, &dsp); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_XMA + /* + If its XMA, set whether there is a seektable or not + */ + if (mSound->mFormat == FMOD_SOUND_FORMAT_XMA) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, dsp); + CodecXMA *xma = (CodecXMA *)dspcodec->mCodec; + + xma->mXMASeekable = mSound->mCodec ? mSound->mCodec->mXMASeekable : mSound->mSubSoundParent->mCodec->mXMASeekable; + } +#endif + result = setupDSPCodec(dsp); + if (result != FMOD_OK) + { + return result; + } + + mDSPHead->setActive(false); + + dsp->setFinished(false); + dsp->setActive(false); + + if (mDSPLowPass) + { + result = mParent->mChannelGroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, &mDSPConnection); + if (result != FMOD_OK) + { + return result; + } + result = mDSPHead->addInputQueued(mDSPLowPass, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + result = mDSPLowPass->addInputQueued(dsp, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mParent->mChannelGroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, &mDSPConnection); + if (result != FMOD_OK) + { + return result; + } + result = mDSPHead->addInputQueued(dsp, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + } + + if (!(mFlags & CHANNELREAL_FLAG_NOREVERB)) + { + mDSPReverb = dsp; + + result = addToReverbs(mDSPReverb); + if (result != FMOD_OK) + { + return result; + } + } +#else + return FMOD_ERR_INTERNAL; +#endif + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::alloc(DSPI *dsp) +{ + FMOD_RESULT result; + FMOD_SOUND_FORMAT format; + int channels; + + result = ChannelReal::alloc(); + if (result != FMOD_OK) + { + return result; + } + + mDSPCodec = 0; + + format = dsp->mDescription.mFormat; + channels = dsp->mDescription.channels; + + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD Resampler Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = 0; + descriptionex.mCategory = FMOD_DSP_CATEGORY_RESAMPLER; + + result = mSystem->createDSP(&descriptionex, (DSPI **)&mDSPResampler); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPResampler->setUserData((void **)this); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPResampler->setTargetFrequency((int)mParent->mChannelGroup->mDSPMixTarget->mDefaultFrequency); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = 0; + } + + + /* + Connect the mixer to the resampler and the resampler to the reader + */ + result = mDSPHead->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + if (mDSPLowPass) + { + result = mDSPLowPass->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + } + if (mDSPWaveTable) + { + result = mDSPWaveTable->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + } + + result = mDSPHead->addInputQueued(mDSPResampler, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + result = mDSPResampler->addInputQueued(dsp, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + result = mParent->mChannelGroup->mDSPMixTarget->addInputQueued(mDSPHead, false, 0, &mDSPConnection); + if (result != FMOD_OK) + { + return result; + } + + mDSPReverb = mDSPResampler; + + result = addToReverbs(mDSPReverb); + if (result != FMOD_OK) + { + return result; + } + + { + DSPResampler *resampler = SAFE_CAST(DSPResampler, mDSPResampler); + + resampler->mLength = mLength; + resampler->mLoopCount = mLoopCount; + resampler->mNoDMA->mLoopStart = mLoopStart; + resampler->mNoDMA->mLoopLength = mLoopLength; + resampler->mNoDMA->mMode = mMode; + resampler->mNoDMA->mDSPClockStart.mHi = 0; + resampler->mNoDMA->mDSPClockStart.mLo = 0; + resampler->mNoDMA->mDSPClockEnd.mHi = 0; + resampler->mNoDMA->mDSPClockEnd.mLo = 0; + resampler->mNoDMA->mDSPClockPause.mHi = 0; + resampler->mNoDMA->mDSPClockPause.mLo = 0; + resampler->mNoDMA->mNewLoopCount = -2; + + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (dspwave) + { + dspwave->mSound = 0; + } + } + + mDSPHead->setActive(false); + + mDSPResampler->setFinished(false); + mDSPResampler->setActive(false); + + dsp->setActive(false); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::start() +{ + if (!(mFlags & CHANNELREAL_FLAG_PAUSED)) + { + mDSPHead->setActive(true); + + if (mSound && mDSPWaveTable) + { + mDSPWaveTable->setActive(true); + } + if (mDSPResampler) + { + mDSPResampler->setActive(true); + } + if (mDSPLowPass) + { + mDSPLowPass->setActive(true); + } + #ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->setActive(true); + } + #endif + if (mDSP) + { + mDSP->setActive(true); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::stop() +{ + FMOD_RESULT result; + int count; + + if (mDSPHead) + { + mDSPHead->setActive(false); + mDSPHead->disconnectAll(false, true); + mDSPHead->stopBuffering(); + } + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->setFinished(true, false); + mDSPCodec->setActive(false); + mDSPCodec->disconnectAll(false, true); /* Just outputs, there are no inputs. */ + + mDSPCodec->freeFromPool(); + mDSPCodec = 0; + } +#endif + +#ifndef FMOD_STATICFORPLUGINS + /* + Clear up reverb pointers. + */ + if (mParent && !(mParent->mFlags & CHANNELI_FLAG_MUSICOWNED) && mDSPReverb) + { + int instance; + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (mSystem->mReverbGlobal.mInstance[instance].mDSP) + { + DSPConnectionI *connection = 0; + + result = mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, 0, &connection); + + result = mSystem->mReverbGlobal.mInstance[instance].mDSP->disconnectFrom(mDSPReverb, connection); + } + + mSystem->mReverbGlobal.resetConnectionPointer(instance, mParent->mIndex); + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + mSystem->mReverb3D.mInstance[0].mDSP->disconnectFrom(mDSPReverb); + mSystem->mReverb3D.resetConnectionPointer(0, mParent->mIndex); + } + + ReverbI *reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + while (reverb_c != &(mSystem->mReverb3DHead)) + { + if (reverb_c->mInstance[0].mDSP) + { + reverb_c->mInstance[0].mDSP->disconnectFrom(mDSPReverb); + reverb_c->resetConnectionPointer(0, mParent->mIndex); + } + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()); + } +#endif + + mDSPReverb = 0; + } +#endif + + if (mDSPResampler) + { + mDSPResampler->setFinished(true, false); + mDSPResampler->setActive(false); + mDSPResampler->release(); + mDSPResampler = 0; + } + + if (mDSPWaveTable) + { + mDSPWaveTable->reset(); + mDSPWaveTable->setFinished(true, false); + mDSPWaveTable->setActive(false); + mDSPWaveTable->disconnectAll(false, true); /* Just outputs, there are no inputs. */ + } + + if (mDSP) + { + DSPI *prev; + int numoutputs; + + result = mDSP->getNumOutputs(&numoutputs); + if (result != FMOD_OK) + { + return result; + } + + for(count = 0; count < numoutputs; count++) + { + result = mDSP->getOutput(count, &prev); + if (result == FMOD_OK) + { + result = prev->disconnectFrom(mDSP); + if (result != FMOD_OK) + { + return result; + } + } + } + } + + ChannelReal::stop(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setPaused(bool paused) +{ + FMOD_RESULT result; + + result = mDSPHead->setActive(!paused); + if (result != FMOD_OK) + { + return result; + } + + if (mSound && mDSPWaveTable) + { + mDSPWaveTable->setActive(!paused); + } + if (mDSPResampler) + { + mDSPResampler->setActive(!paused); + } + if (mDSPLowPass) + { + mDSPLowPass->setActive(!paused); + } +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->setActive(!paused); + } +#endif + if (mDSP) + { + mDSP->setActive(!paused); + } + + return ChannelReal::setPaused(paused); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::getPaused(bool *paused) +{ + FMOD_RESULT result; + bool active; + + result = mDSPHead->getActive(&active); + if (result != FMOD_OK) + { + return result; + } + + if (!active) + { + *paused = true; + return FMOD_OK; + } + + if (mSound && mDSPWaveTable) + { + mDSPWaveTable->getActive(&active); + + if (!active) + { + *paused = true; + return FMOD_OK; + } + + } + if (mDSPResampler) + { + mDSPResampler->getActive(&active); + + if (!active) + { + *paused = true; + return FMOD_OK; + } + } + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->getActive(&active); + if (!active) + { + *paused = true; + return FMOD_OK; + } + } +#endif + + if (mDSP) + { + mDSP->getActive(&active); + if (!active) + { + *paused = true; + return FMOD_OK; + } + } + + return ChannelReal::getPaused(paused); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ +#ifdef FMOD_SUPPORT_REVERB + FMOD_RESULT result; + DSPConnectionI *connection; + float lindirect; + int instance; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (prop->ConnectionPoint && mDSPReverb != (DSPI *)prop->ConnectionPoint) + { + FMOD_RESULT result; + int instance; + + /* + Add an input to standard reverb + */ + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (mSystem->mReverbGlobal.mInstance[instance].mDSP) + { + DSPConnectionI *connection = 0; + + result = mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, 0, &connection); + + result = mSystem->mReverbGlobal.mInstance[instance].mDSP->disconnectFrom(mDSPReverb, connection); + if (result != FMOD_OK) + { + return result; + } + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + Add an input to 3d reverb + */ + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + result = mSystem->mReverb3D.mInstance[0].mDSP->disconnectFrom(mDSPReverb); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Traverse reverb list and add an input to each one that has a DSP + */ + ReverbI *reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + while (reverb_c != &(mSystem->mReverb3DHead)) + { + if (reverb_c->mInstance[0].mDSP) + { + result = reverb_c->mInstance[0].mDSP->disconnectFrom(mDSPReverb); + if (result != FMOD_OK) + { + return result; + } + } + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()); + } +#endif + mDSPReverb = (DSPI *)prop->ConnectionPoint; + + result = addToReverbs(mDSPReverb); + if (result != FMOD_OK) + { + return result; + } + } + + + lindirect = FMOD_POW(10.0f, prop->Direct / 2000.0f); // Convert to linear gain + + if (lindirect != mParent->mReverbDryVolume) + { + mParent->mReverbDryVolume = lindirect; + updateDirectMix(mParent->mVolume); + } + + /* + How many instances is the user trying to set. Just use this for error checking later. + Setting just one instance will return an error if it doesnt exist. If the user sets + something like 0xFFFFFFFF dont bail out as they probably want to set all instances. + */ + int numinstances = 0; + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (prop->Flags & (FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance)) + { + numinstances++; + } + } + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (prop->Flags & (FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance) || (!numinstances && !instance)) + { + /* + Set main reverb's channel properties and update the reverb mix + */ + result = mSystem->mReverbGlobal.setChanProperties(instance, mParent->mIndex, prop); + if (numinstances <= 1 && result != FMOD_OK) + { + return result; + } + + if (mSystem->mReverbGlobal.mInstance[instance].mDSP) + { + mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, 0, &connection); + if (!connection) + { + if (!mDSPReverb) + { + if (mDSPCodec) mDSPReverb = mDSPCodec; + else if (mDSPResampler) mDSPReverb = mDSPResampler; + else mDSPReverb = mDSPWaveTable; + } + + addToReverbs(mDSPReverb); + } + result = updateReverbMix(&mSystem->mReverbGlobal, mParent->mVolume); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + // update direct mix on other instances (ignoring errors) + FMOD_REVERB_CHANNELPROPERTIES cprop; + result = mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, &cprop); + cprop.Direct = prop->Direct; + result = mSystem->mReverbGlobal.setChanProperties(instance, mParent->mIndex, &cprop); + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + Also for 3D master reverb + */ + + /* + Only apply 3D reverb property to instance #0. If user specified nothing (default to instance 0), or instance 0 flag. + */ + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0 || !numinstances) + { + result = mSystem->mReverb3D.setChanProperties(0, mParent->mIndex, prop); + if (result != FMOD_OK) + { + return result; + } + } + + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + mSystem->mReverb3D.getChanProperties(0, mParent->mIndex, 0, &connection); + if (!connection) + { + if (!mDSPReverb) + { + if (mDSPCodec) mDSPReverb = mDSPCodec; + else if (mDSPResampler) mDSPReverb = mDSPResampler; + else mDSPReverb = mDSPWaveTable; + } + + addToReverbs(mDSPReverb); + } + result = updateReverbMix(&mSystem->mReverb3D, mParent->mVolume); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Also for physicals in 3D reverb list + */ + ReverbI *reverb_c; + for (reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); reverb_c != &mSystem->mReverb3DHead; reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + if(reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + reverb_c->setChanProperties(0, mParent->mIndex, prop); + + if( reverb_c->mInstance[0].mDSP) + { + reverb_c->getChanProperties(0, mParent->mIndex, 0, &connection); + if (!connection) + { + if (!mDSPReverb) + { + if (mDSPCodec) mDSPReverb = mDSPCodec; + else if (mDSPResampler) mDSPReverb = mDSPResampler; + else mDSPReverb = mDSPWaveTable; + } + + addToReverbs(mDSPReverb); + } + result = updateReverbMix(reverb_c, mParent->mVolume); + if (result != FMOD_OK) + { + return result; + } + } + } + } +#endif + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::updateDirectMix(float volume) +{ + FMOD_RESULT result; + float gainlin; + + if (mParent->mFlags & CHANNELI_FLAG_REALMUTE) + { + volume = 0; + } + + /* + Calculate level based on current state + */ + gainlin = volume; + gainlin *= mParent->mFadeVolume; + gainlin *= mParent->mReverbDryVolume; + gainlin *= ((1.0f - mParent->m3DPanLevel) + (mParent->mVolume3D * mParent->m3DPanLevel)); + gainlin *= mParent->mChannelGroup->mRealVolume; + gainlin *= ((1.0f - mParent->m3DPanLevel) + (mParent->mConeVolume3D * mParent->m3DPanLevel)); + if (mSound && mSound->mSoundGroup) + { + gainlin *= mSound->mSoundGroup->mVolume; + } + + float transmission = (1.0f - mParent->mDirectOcclusion) * + (1.0f - mParent->mUserDirectOcclusion) * + mParent->mChannelGroup->mRealDirectOcclusionVolume; + + if (mDSPLowPass) + { + float hrtf_cutoff = 22050.0f; + + transmission *= mParent->mLowPassGain; + + if (mSystem->mFlags & FMOD_INIT_SOFTWARE_HRTF) + { + float hrtf_min_angle = mSystem->mAdvancedSettings.HRTFMinAngle / 2.0f; + float hrtf_max_angle = mSystem->mAdvancedSettings.HRTFMaxAngle / 2.0f; + float abs_angle = (mAngleToListener <= 180.0f) ? mAngleToListener : 360.0f-mAngleToListener; + + if (abs_angle <= hrtf_min_angle) + { + hrtf_cutoff = 22050.0f; /* Max cutoff frequency - i.e. no filter. */ + } + else if (abs_angle >= hrtf_max_angle) + { + hrtf_cutoff = (float)mSystem->mAdvancedSettings.HRTFFreq; /* Min cutoff frequency - i.e. maximum filter */ + } + else + { + /* + Linear interpolation between min and max frequency with linear angle + */ + float filter_strength = (abs_angle - hrtf_min_angle) / (hrtf_max_angle - hrtf_min_angle); + float range = 22050.0f - mSystem->mAdvancedSettings.HRTFFreq; + + hrtf_cutoff = mSystem->mAdvancedSettings.HRTFFreq + range * (1.0f - filter_strength); + } + } + + hrtf_cutoff = (((1.0f - mParent->m3DPanLevel) * 22050.0f) + (hrtf_cutoff * mParent->m3DPanLevel)); + + if (transmission < 1.0f || hrtf_cutoff < 22050.0f) + { + float cutoff; + + mDSPLowPass->setBypass(false); + + cutoff = 22050.0f * transmission * transmission; + + /* + Try to override the occlusion + */ + if (hrtf_cutoff < cutoff) + { + cutoff = hrtf_cutoff; + } + + mDSPLowPass->setParameter(FMOD_DSP_LOWPASS_SIMPLE_CUTOFF, cutoff); + } + else + { + mDSPLowPass->setBypass(true); + } + } + else + { + gainlin *= transmission; + } + + result = mDSPConnection->setMix(gainlin); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::updateReverbMix(ReverbI* reverb, float volume) +{ +#ifdef FMOD_SUPPORT_REVERB + int instance; + + if (!reverb) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mParent->mFlags & CHANNELI_FLAG_REALMUTE) + { + volume = 0; + } + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (reverb->mInstance[instance].mDSP) + { + FMOD_RESULT result; + FMOD_REVERB_CHANNELPROPERTIES props; + DSPConnectionI *connection; + float gainlin = 0.0f; + bool morphing = false; + + /* + Get properties + */ + result = reverb->getChanProperties(instance, mParent->mIndex, &props, &connection); + if (result != FMOD_OK) + { + return result; + } + + if (!connection) + { + return FMOD_OK; + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + result = mSystem->get3DReverbActive(&morphing); + if (result != FMOD_OK) + { + return result; + } +#endif + + float transmission = (1.0f - mParent->mReverbOcclusion) * + (1.0f - mParent->mUserReverbOcclusion) * + mParent->mChannelGroup->mRealReverbOcclusionVolume; + + /* + Main reverb + */ + if (reverb == &mSystem->mReverbGlobal) + { + if (mMode & FMOD_3D) + { + if (morphing) + { + gainlin = 0.0f; + } + else + { + gainlin = (float)FMOD_POW(10.0f, props.Room / 2000.0f); // Convert to linear gain + + if (!props.ConnectionPoint) + { + gainlin *= transmission; + gainlin *= mParent->mFadeVolume; + gainlin *= mParent->mVolume3D; + gainlin *= mParent->mChannelGroup->mRealVolume; + if (mSound && mSound->mSoundGroup) + { + gainlin *= mSound->mSoundGroup->mVolume; + } + gainlin *= volume; + } + } + } + else + { + gainlin = (float)FMOD_POW(10.0f, props.Room / 2000.0f); + + if (!props.ConnectionPoint) + { + gainlin *= mParent->mChannelGroup->mRealVolume; + gainlin *= volume; + } + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + Main 3D reverb + */ + else if (reverb == &mSystem->mReverb3D ) + { + if (mMode & FMOD_3D) // 3D channel + { + gainlin = (float)FMOD_POW(10.0f, props.Room / 2000.0f); // Convert to linear gain + + if (!props.ConnectionPoint) + { + gainlin *= transmission; + gainlin *= mParent->mFadeVolume; + gainlin *= mParent->mVolume3D; + gainlin *= mParent->mChannelGroup->mRealVolume; + if (mSound && mSound->mSoundGroup) + { + gainlin *= mSound->mSoundGroup->mVolume; + } + + gainlin *= volume; + } + } + else // 2D channels contribute nothing + { + gainlin = 0.0f; + } + } + /* + Physical 3D reverbs + */ + else + { + // 2D channel : no contribution + // + if (mMode & FMOD_2D) + { + gainlin = 0.0f; + } + else + { + /* + Positioned 3D reverbs, including 3D main module if present + */ + + /* + Find listener and source components + */ + float source_gain; + reverb->getPresenceGain(0, mParent->mIndex, &source_gain); + + /* + Take the maximum of source presence and listener presence components + */ + float listener_gain = reverb->getGain(); + gainlin = (listener_gain > source_gain) ? listener_gain : source_gain; + + /* + Apply other gains + */ + gainlin *= (float)FMOD_POW(10.0f, props.Room / 2000.0f); /* Convert to linear gain */ + + if (!props.ConnectionPoint) + { + gainlin *= transmission; + gainlin *= mParent->mFadeVolume; + gainlin *= mParent->mVolume3D; + gainlin *= mParent->mChannelGroup->mRealVolume; + if (mSound && mSound->mSoundGroup) + { + gainlin *= mSound->mSoundGroup->mVolume; + } + + gainlin *= volume; + } + } + } +#endif + + /* + Find connection and set channel's reverb input level as appropriate + */ + result = connection->setMix(gainlin); + if (result != FMOD_OK) + { + return result; + } + } + } + +#endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ +#ifdef FMOD_SUPPORT_REVERB + FMOD_RESULT result; + int instance = 0; + + if(!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + The first instance set is always returned (so 0|1|2|3 will return 0, 2|3 will return 2 etc). + */ + instance = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) ? 0 : + (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) ? 1 : + (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) ? 2 : + (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) ? 3 : + 0; + + result = mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, prop); + if(result != FMOD_OK) + { + return result; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setLowPassGain(float gain) +{ + /* + Need to flush the mLowPassGain value set in parent (via updateDirectMix) + */ + return setVolume(mParent->mVolume); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::set3DOcclusion(float directOcclusion, float reverbOcclusion) +{ + FMOD_RESULT result; + + if (mSubChannelIndex > 0) + { + return FMOD_OK; + } + + /* + Modify Room property to be reverbVolume in mB + */ + mParent->mReverbOcclusion = reverbOcclusion; + + /* + Set direct occlusion factors + */ + mParent->mDirectOcclusion = directOcclusion; + + /* + Update direct and listener reverb mix levels + */ + result = updateDirectMix(mParent->mVolume); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + ReverbI *reverb_c; + for (reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); reverb_c != &mSystem->mReverb3DHead; reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + if (reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + result = updateReverbMix(reverb_c, mParent->mVolume); + if (result != FMOD_OK) + { + return result; + } + } + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setVolume(float volume) +{ + FMOD_RESULT result; + + if (mSubChannelIndex > 0) + { + return FMOD_OK; + } + + /* + Update direct and listener reverb mix levels + */ + result = updateDirectMix(volume); + if (result != FMOD_OK) + { + return result; + } + + if (!(mFlags & CHANNELREAL_FLAG_NOREVERB)) + { + result = updateReverbMix(&mSystem->mReverbGlobal, volume); + if (result != FMOD_OK) + { + return result; + + } + + #ifdef FMOD_SUPPORT_MULTIREVERB + ReverbI *reverb_c; + + result = updateReverbMix(&mSystem->mReverb3D, volume); + if (result != FMOD_OK) + { + return result; + } + + for (reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + reverb_c != &mSystem->mReverb3DHead; + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + if(reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + result = updateReverbMix(reverb_c, volume); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setFrequency(float frequency) +{ + if (mDSPResampler || mDSPCodec) + { + DSPResampler *dspresampler; + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + dspresampler = SAFE_CAST(DSPResampler, mDSPCodec); + } + else +#endif + { + dspresampler = SAFE_CAST(DSPResampler, mDSPResampler); + } + if (!dspresampler) + { + return FMOD_ERR_INVALID_PARAM; + } + + frequency *= ((1.0f - mParent->m3DPanLevel) + (mParent->mPitch3D * mParent->m3DPanLevel)); + frequency *= mParent->mChannelGroup->mRealPitch; + + if (frequency > mMaxFrequency) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ChannelSoftware::setFrequency", "Warning!!! Extreme frequency being set (%.02f hz). Possibly because of bad velocity in set3DAttributes call.\n", frequency)); + frequency = mMaxFrequency; + } + if (frequency < mMinFrequency) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ChannelSoftware::setFrequency", "Warning!!! Extreme frequency being set (%.02f hz). Possibly because of bad velocity in set3DAttributes call.\n", frequency)); + frequency = mMinFrequency; + } + + return dspresampler->setFrequency(frequency); + } + else if (mDSPWaveTable) + { + DSPWaveTable *dspwave; + + dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INTERNAL; + } + + frequency *= ((1.0f - mParent->m3DPanLevel) + (mParent->mPitch3D * mParent->m3DPanLevel)); + frequency *= mParent->mChannelGroup->mRealPitch; + + if (frequency > mMaxFrequency) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ChannelSoftware::setFrequency", "Warning!!! Extreme frequency being set (%.02f hz). Possibly because of bad velocity in set3DAttributes call.\n", frequency)); + frequency = mMaxFrequency; + } + if (frequency < mMinFrequency) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ChannelSoftware::setFrequency", "Warning!!! Extreme frequency being set (%.02f hz). Possibly because of bad velocity in set3DAttributes call.\n", frequency)); + frequency = mMinFrequency; + } + + return dspwave->setFrequency(frequency); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setPan(float lrpan, float fbpan) +{ + int numchannels; + float pan = (lrpan + 1.0f) / 2.0f; + float l,r; + unsigned int channelmask = 0; + + if (mSound) + { + numchannels = mSound->mChannels; + channelmask = mSound->mDefaultChannelMask; + } + else if (mDSP) + { + numchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (numchannels == 1) + { + if (mParent->mSpeakerMode == FMOD_SPEAKERMODE_STEREO_LINEAR) + { + l = 1.0f - pan; + r = pan; + } + else + { + l = FMOD_SQRT(1.0f - pan); + r = FMOD_SQRT(pan); + } + + return setSpeakerMix(l, r, 0, 0, 0, 0, 0, 0); + } + else + { + /* + Stereo source panning is 0 = 1.0:0.0. 0.5 = 1.0:1.0. 1.0 = 0:1.0. + */ + if (pan <= 0.5f) + { + l = 1.0f; + r = pan * 2.0f; + } + else + { + l = (1.0f - pan) * 2.0f; + r = 1.0f; + } + + if (numchannels == 2 && !(channelmask & SPEAKER_ALLMONO)) + { + return setSpeakerMix(l, r, 0, 0, 0, 0, 0, 0); + } + else + { + return setSpeakerMix(l, r, 1.0f, 1.0f, l, r, l, r); + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setDSPClockDelay() +{ +#ifdef FMOD_STATICFORPLUGINS + return FMOD_ERR_UNSUPPORTED; +#else + if (mDSPWaveTable) + { + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + + dspwave->mDSPClockStart.mHi = mParent->mDSPClockDelay.mHi; + dspwave->mDSPClockStart.mLo = mParent->mDSPClockDelay.mLo; + dspwave->mDSPClockEnd.mHi = mParent->mDSPClockEnd.mHi; + dspwave->mDSPClockEnd.mLo = mParent->mDSPClockEnd.mLo; + dspwave->mDSPClockPause.mHi = mParent->mDSPClockPause.mHi; + dspwave->mDSPClockPause.mLo = mParent->mDSPClockPause.mLo; + } + if (mDSPCodec || mDSPResampler) + { + DSPResampler *resampler = SAFE_CAST(DSPResampler, mDSPCodec ? mDSPCodec : mDSPResampler); + + resampler->mNoDMA->mDSPClockStart.mHi = mParent->mDSPClockDelay.mHi; + resampler->mNoDMA->mDSPClockStart.mLo = mParent->mDSPClockDelay.mLo; + resampler->mNoDMA->mDSPClockEnd.mHi = mParent->mDSPClockEnd.mHi; + resampler->mNoDMA->mDSPClockEnd.mLo = mParent->mDSPClockEnd.mLo; + resampler->mNoDMA->mDSPClockPause.mHi = mParent->mDSPClockPause.mHi; + resampler->mNoDMA->mDSPClockPause.mLo = mParent->mDSPClockPause.mLo; + } + + return FMOD_OK; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + FMOD_RESULT result; + int numinputlevels; + float levels[DSP_MAXLEVELS_OUT * DSP_MAXLEVELS_IN]; + int numchannels; + int count, count2; + FMOD_SPEAKERMAPTYPE speakermap = FMOD_SPEAKERMAPTYPE_DEFAULT; + + if (mSubChannelIndex > 0) + { + return FMOD_OK; + } + + if (mSound) + { + numchannels = mSound->mChannels; + + if (mSound->mDefaultChannelMask & SPEAKER_ALLMONO) + { + speakermap = FMOD_SPEAKERMAPTYPE_ALLMONO; + } + else if (mSound->mDefaultChannelMask & SPEAKER_ALLSTEREO) + { + speakermap = FMOD_SPEAKERMAPTYPE_ALLSTEREO; + } + else if (mSound->mDefaultChannelMask & SPEAKER_PROTOOLS) + { + speakermap = FMOD_SPEAKERMAPTYPE_51_PROTOOLS; + } + /* + Special case for quad sounds inside a 6 or 8 channel stream so that + DSPI::calculateSpeakerLevels() maps the channels in the correct order. + */ + else if (mSound->mDefaultChannelMask == SPEAKER_QUAD) + { + numchannels = 4; + } + } + else if (mDSP) + { + numchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if ((mSound && mSound->mMode & FMOD_3D) && (mParent->mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC)) + { + result = DSPI::calculateSpeakerLevels(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright, FMOD_SPEAKERMODE_STEREO, numchannels, speakermap, &levels[0], &numinputlevels); + } + else + { + result = DSPI::calculateSpeakerLevels(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright, mParent->mSpeakerMode, numchannels, speakermap, &levels[0], &numinputlevels); + } + if (result != FMOD_OK) + { + return result; + } + + if (mParent->mFlags & CHANNELI_FLAG_USEDINPUTMIX) + { + for (count = 0; count < mSystem->mMaxOutputChannels; count++) + { + for (count2 = 0; count2 < numinputlevels; count2++) + { + levels[(count * numinputlevels) + count2] *= mParent->mInputMix[count2]; + } + } + } + + result = mDSPConnection->setLevels(&levels[0], numinputlevels); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_REVERB + int instance; + FMOD_REVERB_CHANNELPROPERTIES props; + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (mSystem->mReverbGlobal.mInstance[instance].mDSP) + { + FMOD_RESULT result; + DSPConnectionI *connection; + + /* + Get connection + */ + mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, &props, &connection); + if (connection && (connection->mInputUnit == mDSPCodec || connection->mInputUnit == mDSPWaveTable || connection->mInputUnit == mDSPResampler) && !props.ConnectionPoint) + { + result = connection->setLevels(&levels[0], numinputlevels); + if (result != FMOD_OK) + { + return result; + } + } + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + DSPConnectionI *connection; + + mSystem->mReverb3D.getChanProperties(0, mParent->mIndex, &props, &connection); + if (connection && (connection->mInputUnit == mDSPCodec || connection->mInputUnit == mDSPWaveTable || connection->mInputUnit == mDSPResampler) && !props.ConnectionPoint) + { + result = connection->setLevels(&levels[0], numinputlevels); + if (result != FMOD_OK) + { + return result; + } + } + } + + /* + Also for physicals in 3D reverb list + */ + for (ReverbI* reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + reverb_c != &mSystem->mReverb3DHead; + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + if (reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + if (reverb_c->mInstance[0].mDSP) + { + DSPConnectionI *connection; + + reverb_c->getChanProperties(0, mParent->mIndex, &props, &connection); + if (connection && (connection->mInputUnit == mDSPCodec || connection->mInputUnit == mDSPWaveTable || connection->mInputUnit == mDSPResampler) && !props.ConnectionPoint) + { + result = connection->setLevels(&levels[0], numinputlevels); + if (result != FMOD_OK) + { + return result; + } + } + } + } + } +#endif + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + FMOD_RESULT result; + float clevels[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN]; + int count; + + if (mSubChannelIndex > 0) + { + return FMOD_OK; + } + + result = mDSPConnection->getLevels(&clevels[0][0], DSP_MAXLEVELS_IN); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < numlevels; count++) + { + clevels[speaker][count] = levels[count] * mParent->mInputMix[count]; + } + + result = mDSPConnection->setLevels(&clevels[0][0], DSP_MAXLEVELS_IN); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_REVERB + int instance; + FMOD_REVERB_CHANNELPROPERTIES props; + + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (mSystem->mReverbGlobal.mInstance[instance].mDSP) + { + FMOD_RESULT result; + DSPConnectionI *connection; + + /* + Get connection + */ + result = mSystem->mReverbGlobal.getChanProperties(instance, mParent->mIndex, &props, &connection); + if (connection && (connection->mInputUnit == mDSPCodec || connection->mInputUnit == mDSPWaveTable || connection->mInputUnit == mDSPResampler) && !props.ConnectionPoint) + { + result = connection->setLevels(&clevels[0][0], DSP_MAXLEVELS_IN); + if (result != FMOD_OK) + { + return result; + } + } + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + DSPConnectionI *connection; + + mSystem->mReverb3D.getChanProperties(0, mParent->mIndex, &props, &connection); + if (connection && (connection->mInputUnit == mDSPCodec || connection->mInputUnit == mDSPWaveTable || connection->mInputUnit == mDSPResampler) && !props.ConnectionPoint) + { + result = connection->setLevels(&clevels[0][0], DSP_MAXLEVELS_IN); + if (result != FMOD_OK) + { + return result; + } + } + } + + /* + Also for physicals in 3D reverb list + */ + ReverbI *reverb_c; + for (reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); reverb_c != &mSystem->mReverb3DHead; reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + if (reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + if (reverb_c->mInstance[0].mDSP) + { + DSPConnectionI *connection; + + reverb_c->getChanProperties(0, mParent->mIndex, &props, &connection); + if (connection && (connection->mInputUnit == mDSPCodec || connection->mInputUnit == mDSPWaveTable || connection->mInputUnit == mDSPResampler) && !props.ConnectionPoint) + { + result = connection->setLevels(&clevels[0][0], DSP_MAXLEVELS_IN); + if (result != FMOD_OK) + { + return result; + } + } + } + } + } +#endif + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + unsigned int pcm = 0, endpoint; + int channels; + FMOD_SOUND_FORMAT format; + float frequency; + + if (mSubChannelIndex > 0) + { + return FMOD_OK; + } + + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } +#ifdef FMOD_SUPPORT_DSPCODEC + else if (mDSPCodec) + { + channels = mDSPCodec->mDescription.channels; + format = mDSPCodec->mDescription.mFormat; + frequency = mDSPCodec->mDefaultFrequency; + } +#endif + else if (mSound) + { + channels = mSound->mChannels; + format = mSound->mFormat; + frequency = mSound->mDefaultFrequency; + } + else if (mDSPResampler) + { + channels = mDSPResampler->mDescription.channels; + format = FMOD_SOUND_FORMAT_PCMFLOAT; + frequency = mDSPResampler->mDefaultFrequency; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (postype == FMOD_TIMEUNIT_PCM) + { + pcm = position; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(position, &pcm, channels, format); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + pcm = (unsigned int)((float)position / 1000.0f * frequency); + } + + if (mSound) + { + if (mMode & FMOD_LOOP_OFF) + { + endpoint = mSound->mLength - 1; + } + else + { + endpoint = mLoopStart + mLoopLength - 1; + } + } + else + { + endpoint = (unsigned int)-1; + } + + if (pcm > endpoint) + { + return FMOD_ERR_INVALID_POSITION; + } + + /* + Recurse through channel head downwards calling setposition to all dsp units. + */ + if (0) + { + } +#ifdef FMOD_SUPPORT_DSPCODEC + else if (mDSPCodec) + { + return mDSPCodec->setPosition(pcm, false); + } +#endif + else if (mDSP) + { + return mDSP->setPosition(pcm, true); + } + else if (mDSPWaveTable) + { + return mDSPWaveTable->setPosition(pcm, false); + } + else if (mDSPResampler) + { + return mDSPResampler->setPosition(pcm, true); + } + else + { + return mDSPHead->setPosition(pcm, true); /* Channel based sound is always wavetable, dspcodec, or resampler. If this is called they must have done a dsp connection and it will flush. */ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + int channels; + FMOD_SOUND_FORMAT format; + float frequency; + bool getsubsoundtime = false; + unsigned int pcmcurrent; + int subsoundlistcurrent = mSubSoundListCurrent; + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + postype &= ~FMOD_TIMEUNIT_BUFFERED; /* Don't need that crap. */ + + if (postype == FMOD_TIMEUNIT_SENTENCE_MS) + { + postype = FMOD_TIMEUNIT_MS; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCM) + { + postype = FMOD_TIMEUNIT_PCM; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCMBYTES) + { + postype = FMOD_TIMEUNIT_PCMBYTES; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE || postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + getsubsoundtime = true; + } + else if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } + + if (getsubsoundtime +#ifdef FMOD_SUPPORT_SENTENCING + && !mSound->mSubSoundList +#endif + ) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + DSPResampler *dspcodec = SAFE_CAST(DSPResampler, mDSPCodec); + if (!dspcodec) + { + return FMOD_ERR_INVALID_PARAM; + } + + channels = mDSPCodec->mDescription.channels; + format = mDSPCodec->mDescription.mFormat; + frequency = mSound->mDefaultFrequency; + + mPosition = dspcodec->mPosition.mHi; + subsoundlistcurrent = mDSPCodec->mSubSoundListCurrent; + } + else +#endif + if (mSound && mDSPWaveTable) + { + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INTERNAL; + } + + channels = mSound->mChannels; + format = mSound->mFormat; + frequency = mSound->mDefaultFrequency; + + if (dspwave->mNewPosition != 0xFFFFFFFF) + { + mPosition = dspwave->mNewPosition; + } + else + { + mPosition = dspwave->mPosition.mHi; + } + } + else if (mDSPResampler) + { + channels = mDSPResampler->mDescription.channels; + format = FMOD_SOUND_FORMAT_PCMFLOAT; + frequency = mDSPResampler->mDefaultFrequency; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + pcmcurrent = mPosition; + +#ifdef FMOD_SUPPORT_SENTENCING + if (getsubsoundtime) + { + int count = 0; + + for (count = 0; count < mSound->mSubSoundListNum; count++) + { + unsigned int length = mSound->mSubSoundList[count].mLength; + + if (pcmcurrent >= length) + { + pcmcurrent -= length; + } + else + { + break; + } + } + } +#endif + + if (postype == FMOD_TIMEUNIT_PCM) + { + *position = pcmcurrent; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(pcmcurrent, position, channels, format); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + *position = (unsigned int)((float)pcmcurrent / frequency * 1000.0f); + } + else if (postype == FMOD_TIMEUNIT_SENTENCE) + { + *position = mSubSoundListCurrent; + } +#ifdef FMOD_SUPPORT_SENTENCING + else if (postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + *position = mSound->mSubSoundList[subsoundlistcurrent].mIndex; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setLoopPoints(unsigned int loopstart, unsigned int looplength) +{ + FMOD_RESULT result; + + result = ChannelReal::setLoopPoints(loopstart, looplength); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + + if (mDSPCodec) + { + mDSPCodec->mNoDMA->mLoopStart = mLoopStart; + mDSPCodec->mNoDMA->mLoopLength = mLoopLength; + } + +#endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setLoopCount(int loopcount) +{ + FMOD_RESULT result; + + result = ChannelReal::setLoopCount(loopcount); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + + if (mDSPCodec) + { + mDSPCodec->mNoDMA->mNewLoopCount = mLoopCount; + mDSPCodec->mNoDMA->mLoopCountIncrement++; + } + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + + result = ChannelReal::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + + if (mDSPCodec) + { + mDSPCodec->mNoDMA->mMode = mMode; + } + +#endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::isPlaying(bool *isplaying, bool includethreadlatency) +{ + if (!isplaying) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFlags & CHANNELREAL_FLAG_ALLOCATED) + { + *isplaying = true; + } + else + { +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->getFinished(isplaying); + *isplaying = !*isplaying; + } + else +#endif + if (mDSPResampler) + { + mDSPResampler->getFinished(isplaying); + *isplaying = !*isplaying; + } + else if (mDSPWaveTable) + { + if (!mSound) + { + *isplaying = false; + } + else + { + mDSPWaveTable->getFinished(isplaying); + *isplaying = !*isplaying; + } + } + else + { + *isplaying = false; + } + } + + if (!*isplaying) + { + mFlags &= ~(CHANNELREAL_FLAG_ALLOCATED | CHANNELREAL_FLAG_PLAYING); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal. + + [PARAMETERS] + 'spectrumarray' Pointer to an array of floats to receive spectrum data. Data range is 0-1. Decibels = 10.0f * (float)log10(val) * 2.0f; + 'windowsize' Number of PCM samples to analyze. The resulting spectrum placed in the spectrumarray parameter will be HALF the size of this value (ie 1024 will place 512 floats in spectrumarray). Must be a power of 2. (ie 128/256/512 etc). Min = 128. Max = 16384. + 'channeloffset' Channel to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' Pre-FFT window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the windowsize, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.
    + The larger the windowsize, the more 'lag' the spectrum will seem to inherit. This is because the window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.
    + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).
    + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.
    + + [PLATFORMS] + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW +] +*/ +FMOD_RESULT ChannelSoftware::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ +#ifdef FMOD_SUPPORT_GETSPECTRUM + FMOD_RESULT result; + +#ifdef FMOD_STATICFORPLUGINS + + result = FMOD_ERR_UNSUPPORTED; + +#else + + DSPFilter *dsphead; + float *buffer; + unsigned int position, length, bufferlength; + int numchannels, windowsize; + static DSPFFT fft; + + dsphead = (DSPFilter *)mDSPHead; + if (!dsphead) + { + return FMOD_ERR_INITIALIZATION; + } + + windowsize = numvalues * 2; + + if (windowsize != (1 << 7) && + windowsize != (1 << 8) && + windowsize != (1 << 9) && + windowsize != (1 << 10) && + windowsize != (1 << 11) && + windowsize != (1 << 12) && + windowsize != (1 << 13) && + windowsize != (1 << 14)) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mSound) + { + numchannels = mSound->mChannels; + } + else if (mDSP) + { + numchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = dsphead->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (windowsize > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mSystem->getDSPBufferSize(&bufferlength, 0); + + position -= windowsize; + if ((int)position < 0) + { + position += length; + } + + result = fft.getSpectrum(buffer, position, length, spectrumarray, windowsize, channeloffset, numchannels, windowtype); + +#endif + return result; +#else + return FMOD_ERR_UNSUPPORTED; +#endif // FMOD_SUPPORT_GETSPECTRUM +} + + +/* +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal. + + [PARAMETERS] + 'spectrumarray' Pointer to an array of floats to receive spectrum data. Data range is 0-1. Decibels = 10.0f * (float)log10(val) * 2.0f; + 'windowsize' Number of PCM samples to analyze. The resulting spectrum placed in the spectrumarray parameter will be HALF the size of this value (ie 1024 will place 512 floats in spectrumarray). Must be a power of 2. (ie 128/256/512 etc). Min = 128. Max = 16384. + 'channeloffset' Channel to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' Pre-FFT window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the windowsize, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.
    + The larger the windowsize, the more 'lag' the spectrum will seem to inherit. This is because the window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.
    + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).
    + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.
    + + [PLATFORMS] + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW +] +*/ +FMOD_RESULT ChannelSoftware::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + FMOD_RESULT result; + +#ifdef FMOD_STATICFORPLUGINS + + result = FMOD_ERR_UNSUPPORTED; + +#else + + DSPFilter *dsphead; + float *buffer; + unsigned int position, length; + int numchannels, count; + + dsphead = (DSPFilter *)mDSPHead; + if (!dsphead) + { + return FMOD_ERR_INITIALIZATION; + } + + if (mSound) + { + numchannels = mSound->mChannels; + } + else if (mDSP) + { + numchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = dsphead->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (numvalues > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + position -= numvalues; + if ((int)position < 0) + { + position += length; + } + + for (count = 0; count < numvalues; count++) + { + wavearray[count] = buffer[position*numchannels+channeloffset]; + position++; + if (position >= length) + { + position = 0; + } + } + +#endif + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::getDSPHead(DSPI **dsp) +{ + *dsp = mDSPHead; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::addToReverbs(DSPI *dsptarget) +{ +#ifdef FMOD_SUPPORT_REVERB + FMOD_RESULT result; + int instance; + + if (!dsptarget) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Add an input to standard reverb + */ + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + if (mSystem->mReverbGlobal.mInstance[instance].mDSP) + { + DSPConnectionI *connection; + + result = mSystem->mReverbGlobal.mInstance[instance].mDSP->addInputQueued(dsptarget, false, 0, &connection); + if (result != FMOD_OK) + { + return result; + } + + mSystem->mReverbGlobal.setChanProperties(instance, mParent->mIndex, 0, connection); + mSystem->mReverbGlobal.setPresenceGain(instance, mParent->mIndex, 1.0f); + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + Add an input to 3d reverb + */ + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + DSPConnectionI *connection; + + result = mSystem->mReverb3D.mInstance[0].mDSP->addInputQueued(dsptarget, false, 0, &connection); + if (result != FMOD_OK) + { + return result; + } + + mSystem->mReverb3D.setChanProperties(0, mParent->mIndex, 0, connection); + mSystem->mReverb3D.setPresenceGain(0, mParent->mIndex, 1.0f); + } + + /* + Traverse reverb list and add an input to each one that has a DSP + */ + ReverbI *reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + while (reverb_c != &(mSystem->mReverb3DHead)) + { + if (reverb_c->mInstance[0].mDSP) + { + DSPConnectionI *connection; + + result = reverb_c->mInstance[0].mDSP->addInputQueued(dsptarget, false, 0, &connection); + if (result != FMOD_OK) + { + return result; + } + + reverb_c->setChanProperties(0, mParent->mIndex, 0, connection); + reverb_c->setPresenceGain(0, mParent->mIndex, 1.0f); + } + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()); + } +#endif + +#endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelSoftware::moveChannelGroup(ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup, bool forcedspreconnect) +{ + FMOD_RESULT result; + + if (oldchannelgroup == newchannelgroup && !forcedspreconnect) + { + return FMOD_OK; + } + + /* + 1. disconnect from previous channel group's head. + */ + if (oldchannelgroup && oldchannelgroup->mDSPMixTarget) + { + result = oldchannelgroup->mDSPMixTarget->disconnectFrom(mDSPHead); + if (result != FMOD_OK) + { + return result; + } + } + + /* + 2. reconnect to DSP group's head. + */ + result = newchannelgroup->mDSPMixTarget->addInputQueued(mDSPHead, false, mDSPConnection, &mDSPConnection); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +} + +#endif diff --git a/src/fmod_channel_software.h b/src/fmod_channel_software.h new file mode 100755 index 0000000..cf56e09 --- /dev/null +++ b/src/fmod_channel_software.h @@ -0,0 +1,89 @@ +#ifndef _FMOD_CHANNEL_SOFTWARE_H +#define _FMOD_CHANNEL_SOFTWARE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_channel_realmanual3d.h" +#include "fmod_dsp_filter.h" +#include "fmod_dsp_wavetable.h" + +namespace FMOD +{ + class DSPCodec; + class ReverbI; + + class ChannelSoftware : public ChannelRealManual3D + { + friend class DSPCodecPool; + friend class OutputSoftware; + + protected: + + /* __________________ __________ _______________ */ + /* | |<---.. | | | | */ + /* | channelgroup DSP |<--------| mDSPHead |<------| mDSPWaveTable | */ + /* |__________________|<---.. |__________| |_______________| */ + + DSPI *mDSPHead; + DSPFilter mDSPHeadMemory; +#ifdef PLATFORM_PS3 + char mDSPHeadMemoryPad[16]; +#endif + DSPWaveTable *mDSPWaveTable; + DSPWaveTable mDSPWaveTableMemory; +#ifdef PLATFORM_PS3 + char mDSPWaveTableMemoryPad[16]; +#endif + DSPResampler *mDSPResampler; /* mDSPResampler is connected between mDSPHead and mDSPWaveTable sometimes. */ + DSPI *mDSPLowPass; /* Optional if the user specifies FMOD_INIT_OCCLUSION_LOWPASS. */ + DSPI *mDSPReverb; /* Points to the unit the reverb unit is connected to. */ + DSPCodec *mDSPCodec; + DSPConnectionI *mDSPConnection; + + public: + + ChannelSoftware(); + + FMOD_RESULT init (int index, SystemI *system, Output *output, DSPI *dspmixtarget); + FMOD_RESULT close (); + FMOD_RESULT alloc (); + FMOD_RESULT alloc (DSPI *dsp); + FMOD_RESULT start (); + + FMOD_RESULT stop (); + FMOD_RESULT setVolume (float volume); + FMOD_RESULT setFrequency (float frequency); + FMOD_RESULT setPan (float pan, float fbpan = 1); + FMOD_RESULT setDSPClockDelay (); + FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + FMOD_RESULT setSpeakerLevels (int speaker, float *levels, int numlevels); + FMOD_RESULT setPaused (bool paused); + FMOD_RESULT getPaused (bool *paused); + FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT setLoopPoints (unsigned int loopstart, unsigned int looplength); + FMOD_RESULT setLoopCount (int loopcount); + FMOD_RESULT setMode (FMOD_MODE mode); + FMOD_RESULT setLowPassGain (float gain); + FMOD_RESULT set3DOcclusion (float directOcclusion, float reverbOcclusion); + FMOD_RESULT isPlaying (bool *isplaying, bool includethreadlatency = false); + FMOD_RESULT getSpectrum (float *spectrumarray, int numentries, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + FMOD_RESULT getWaveData (float *wavearray, int numentries, int channeloffset); + FMOD_RESULT getDSPHead (DSPI **dsp); + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT setReverbMix (ReverbI* reverb, float gainlin); + FMOD_RESULT updateDirectMix (float volume); + FMOD_RESULT updateReverbMix (ReverbI* reverb, float volume); + FMOD_RESULT addToReverbs (DSPI *dsp); + FMOD_RESULT moveChannelGroup (ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup, bool forcedspreconnect); + FMOD_RESULT setupDSPCodec (DSPI *dsp); + FMOD_RESULT getDSPCodec (DSPCodec **dsp) { *dsp = mDSPCodec; return FMOD_OK; } + }; +} + +#endif + +#endif diff --git a/src/fmod_channel_stream.cpp b/src/fmod_channel_stream.cpp new file mode 100755 index 0000000..78dfe88 --- /dev/null +++ b/src/fmod_channel_stream.cpp @@ -0,0 +1,1846 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_STREAMING + +#include "fmod_async.h" +#include "fmod_channel_stream.h" +#include "fmod_codeci.h" +#include "fmod_localcriticalsection.h" +#include "fmod_soundi.h" +#include "fmod_sound_stream.h" +#include "fmod_sound_sample.h" +#include "fmod_speakermap.h" +#include "fmod_systemi.h" + + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelStream::ChannelStream() +{ + int count; + + for (count = 0; count < FMOD_CHANNEL_MAXREALSUBCHANNELS; count++) + { + mRealChannel[count] = 0; + } + + mSamplesPlayed = mSamplesWritten = 0; + mNumRealChannels = 1; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setRealChannel(ChannelReal *realchan) +{ + mRealChannel[0] = realchan; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::set2DFreqVolumePanFor3D() +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->set2DFreqVolumePanFor3D(); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::moveChannelGroup(ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup, bool forcedspreconnect) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->moveChannelGroup(oldchannelgroup, newchannelgroup, forcedspreconnect); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::alloc() +{ + FMOD_RESULT result; + Stream *stream = SAFE_CAST(Stream, mSound); + int count; + + mFlags = (CHANNELREAL_FLAG)(mFlags & ~CHANNELREAL_FLAG_STOPPED); + + mSystem = stream->mSystem; + mFinished = false; + mLastPCM = 0; + mDecodeOffset = 0; + stream->mFlags &= ~FMOD_SOUND_FLAG_THREADFINISHED; + + if (stream->mSubSoundParent) + { + Stream *streamParent = SAFE_CAST(Stream, stream->mSubSoundParent); + mPosition = streamParent->mInitialPosition; + streamParent->mFlags &= ~FMOD_SOUND_FLAG_THREADFINISHED; + } + else + { + mPosition = stream->mInitialPosition; + } + + mSamplesPlayed = 0; + mSamplesWritten = 0; + mMinFrequency = mRealChannel[0]->mMinFrequency; + if (mMinFrequency < 100) + { + mMinFrequency = 100; /* Streams can't go backwards. */ + } + mMaxFrequency = mRealChannel[0]->mMaxFrequency; + + for (count = 0; count < mNumRealChannels; count++) + { + Sample *sample = SAFE_CAST(Sample, stream->mSample); + + if (sample) + { + if (!(stream->mMode & FMOD_OPENUSER) && stream->mLength <= sample->mLength && !stream->mSubSoundList && sample->mMode & FMOD_SOFTWARE) + { + stream->mFlags |= FMOD_SOUND_FLAG_FULLYBUFFERED; + stream->mFlags |= FMOD_SOUND_FLAG_FINISHED; /* Nothing more to read. */ + + if (mMode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)) + { + sample->setMode(FMOD_LOOP_NORMAL); + sample->setLoopPoints(stream->mLoopStart, FMOD_TIMEUNIT_PCM, stream->mLoopStart + stream->mLoopLength - 1, FMOD_TIMEUNIT_PCM); + } + else + { + sample->setMode(FMOD_LOOP_OFF); + sample->setLoopPoints(0, FMOD_TIMEUNIT_PCM, sample->mLength - 1, FMOD_TIMEUNIT_PCM); + } + } + else + { + stream->mFlags &= ~FMOD_SOUND_FLAG_FULLYBUFFERED; + + sample->setMode(FMOD_LOOP_NORMAL); + sample->setLoopPoints(0, FMOD_TIMEUNIT_PCM, sample->mLength - 1, FMOD_TIMEUNIT_PCM); + } + + if (sample->mNumSubSamples) + { + sample = (Sample *)sample->mSubSample[count]; + } + + sample->mSubSoundParent = stream; + + mRealChannel[count]->mMode = sample->mMode; + mRealChannel[count]->mLoopStart = sample->mLoopStart; + mRealChannel[count]->mLoopLength = sample->mLoopLength; + mRealChannel[count]->mLength = sample->mLength; + } + mRealChannel[count]->mSound = sample; + mRealChannel[count]->mSubChannelIndex = count; + mRealChannel[count]->mDSP = 0; + mRealChannel[count]->mLoopCount = -1; /* Reset any previous loop counts that might be left over. */ + mRealChannel[count]->mParent = mParent; + + result = mRealChannel[count]->alloc(); + if (result != FMOD_OK) + { + return result; + } + } + + FMOD_OS_CriticalSection_Enter(mSystem->mStreamListCrit); + { + mStreamNode.setData(this); + mStreamNode.addBefore(&mSystem->mStreamListChannelHead); + } + FMOD_OS_CriticalSection_Leave(mSystem->mStreamListCrit); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + CALLED FROM THREAD + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::start() +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->start(); + if (result != FMOD_OK) + { + return result; + } + + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_STOPPED; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED; + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_PLAYING; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::update(int delta) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->update(delta); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + CALLED FROM THREAD + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::updateStream() +{ + FMOD_RESULT result = FMOD_OK; + Sample *sample; + Stream *stream; + unsigned int pcm = 0; + int delta; + LocalCriticalSection crit(mSystem->mStreamRealchanCrit); + + crit.enter(); + { + if (!mSound) /* Left critical section in other thread, now it is null! */ + { + return FMOD_OK; + } + + stream = SAFE_CAST(Stream, mSound); + sample = stream->mSample; + + if (stream->mOpenState != FMOD_OPENSTATE_READY) + { + if (stream->mOpenState == FMOD_OPENSTATE_SETPOSITION) + { + stream->mFlags |= FMOD_SOUND_FLAG_SETPOS_SAFE; + } + return FMOD_ERR_NOTREADY; + } + + stream->mFlags &= ~FMOD_SOUND_FLAG_SETPOS_SAFE; + + if (mFlags & CHANNELREAL_FLAG_STOPPED) + { + return FMOD_OK; + } + + if (mRealChannel[0]) + { + bool isplaying; + + result = mRealChannel[0]->isPlaying(&isplaying); + if (result != FMOD_OK) + { + return result; + } + + if (!isplaying) + { + mFinished = true; + } + } + + if (mFinished) + { + stream->mFlags |= FMOD_SOUND_FLAG_FINISHED; + return FMOD_OK; /* Another thread set it */ + } + + if (mRealChannel[0]) + { + result = mRealChannel[0]->updateStream(); + if (result != FMOD_OK) + { + return result; + } + + result = mRealChannel[0]->getPosition(&pcm, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + } + crit.leave(); + +// FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ChannelStream::updateStream", "mSamplesPlayed %d mSamplesWritten %d pcm %d / %d stream->mBlockSize %d\n", mSamplesPlayed, mSamplesWritten, pcm, stream->mSample->mLength, stream->mBlockSize)); + + while ((mSamplesPlayed > mSamplesWritten && mSamplesPlayed - mSamplesWritten >= (unsigned int)stream->mBlockSize) || + (mSamplesPlayed && mSamplesPlayed < mSamplesWritten && mSamplesWritten - mSamplesPlayed >= (unsigned int)stream->mBlockSize)) // <- unsigned int wraparound case. + { + unsigned int len; + + crit.enter(); + { + if (mFlags & CHANNELREAL_FLAG_STOPPED || stream->mFlags & FMOD_SOUND_FLAG_FULLYBUFFERED) + { + break; + } + + /* + ================================================================================================== + Stream SOUND logic. + Stream CHANNEL related logic is in the next section. + ================================================================================================== + */ + len = stream->mBlockSize; + + if (mDecodeOffset > sample->mLength) + { + len = 0; + } + else if (mDecodeOffset + len > sample->mLength) + { + len = sample->mLength - mDecodeOffset; + } + } + crit.leave(); + + /* + Fill the channel stream buffer. + */ + result = stream->fill(mDecodeOffset, len, 0); + + crit.enter(); + { + /* + Handle fatal errors. + */ + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF && result != FMOD_ERR_FILE_DISKEJECTED) + { + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + if (mRealChannel[count]) + { + mRealChannel[count]->setPaused(true); + } + } + + stream->mOpenState = FMOD_OPENSTATE_ERROR; + if (stream->mAsyncData) + { + stream->mAsyncData->mResult = result; + } + + mFinished = true; + return result; + } + + stream->mFlags |= FMOD_SOUND_FLAG_WANTSTOFLUSH; + + /* + Update fill cursor + */ + mDecodeOffset += len; + if (mDecodeOffset >= sample->mLength) + { + mDecodeOffset -= sample->mLength; + } + mSamplesWritten += len; + } + crit.leave(); + } + + + delta = pcm - mLastPCM; + if (delta < 0) + { + delta += sample->mLoopLength; + } + + if (delta < 0) /* Sanity check - just in case something goes bad. */ + { + delta = 0; + } + + /* + ================================================================================================== + Stream CHANNEL logic. + Update 'channel' position, which is latency adjusted and 'asynchronous' to the 'sound' position. + The 'sound 'position is the fill/read ahead position, and will be ahead of this value. + ================================================================================================== + */ + { + unsigned int endpoint; + unsigned int len = delta; + + if (stream->mLength < mLoopStart + mLoopLength) + { + mLoopLength = stream->mLength - mLoopStart; + } + + if (mMode & FMOD_LOOP_NORMAL && mLoopCount) + { + endpoint = mLoopStart + mLoopLength -1; + } + else + { + endpoint = stream->mLength - 1; + } + + mPosition += len; + + if (mPosition > endpoint) + { + if ((mMode & FMOD_LOOP_NORMAL && mLoopCount) || stream->mLength == (unsigned int)-1) /* 0xFFFFFFFF should mean it is an infinite netstream */ + { + mPosition -= mLoopLength; + + if (mLoopCount > 0) + { + mLoopCount--; + } + } + else if (stream->mFlags & FMOD_SOUND_FLAG_FINISHED) + { + int count; + + mPosition = stream->mLength; + + for (count = 0; count < mNumRealChannels; count++) + { + if (mRealChannel[count]) + { + mRealChannel[count]->setPaused(true); + } + } + + mFinished = true; + } + } + } + + mSamplesPlayed += delta; + mLastPCM = pcm; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + int count; + + result = ChannelReal::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + + result = mSound->setMode(mode); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setMode(mode & ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::stop() +{ + FMOD_RESULT result = FMOD_OK; + FMOD_UINT_NATIVE id; + + mFinished = true; + + FMOD_OS_Thread_GetCurrentID(&id); + + if (!(mMode & FMOD_NONBLOCKING && id == mSystem->mMainThreadID)) + { + if (mSound && mSound->mCodec && mSound->mCodec->mFile) + { + mSound->mCodec->mFile->cancel(); /* Set cancel flag. Don't do this for nonblocking osunds it might cancel when channel is restarted due to async file flip function. */ + } + + FMOD_OS_CriticalSection_Enter(mSystem->mStreamUpdateCrit); /* Protect the whole lot. */ + } + + FMOD_OS_CriticalSection_Enter(mSystem->mStreamRealchanCrit); /* protect the realchan. */ + { + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + if (mRealChannel[count]) + { + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_IN_USE; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_PLAYING; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_PAUSED; + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_STOPPED; + + result = mRealChannel[count]->stop(); + mRealChannel[count]->mSound = 0; + mRealChannel[count]->mDSP = 0; + mRealChannel[count]->mParent = 0; + mRealChannel[count] = 0; + } + } + } + FMOD_OS_CriticalSection_Leave(mSystem->mStreamRealchanCrit); /* protect the realchan. */ + + if (!(mMode & FMOD_NONBLOCKING && id == mSystem->mMainThreadID)) + { + FMOD_OS_CriticalSection_Leave(mSystem->mStreamUpdateCrit); /* Protect the whole lot. */ + } + + + FMOD_OS_CriticalSection_Enter(mSystem->mStreamListCrit); + { + if (mSystem->mStreamListChannelNext == &mStreamNode) + { + mSystem->mStreamListChannelNext = mStreamNode.getNext(); + } + + mStreamNode.removeNode(); + mStreamNode.setData(0); + } + FMOD_OS_CriticalSection_Leave(mSystem->mStreamListCrit); + + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setPaused(bool paused) +{ + FMOD_RESULT result = FMOD_OK; + int count; + LocalCriticalSection crit(mSystem->mStreamRealchanCrit); + + if (mFlags & CHANNELREAL_FLAG_PAUSEDFORSETPOS) + { + return FMOD_OK; + } + + crit.enter(); + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setPaused(paused); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setVolume(float volume) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (mNumRealChannels > 1 && mParent->mLevels && mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->updateSpeakerLevels(volume); + } + } + else if (mNumRealChannels > 1 && mParent->mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setSpeakerMix(mParent->mSpeakerFL, mParent->mSpeakerFR, mParent->mSpeakerC, mParent->mSpeakerLFE, mParent->mSpeakerBL, mParent->mSpeakerBR, mParent->mSpeakerSL, mParent->mSpeakerSR); + } + } + else + { + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setVolume(volume); + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setFrequency(float frequency) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setFrequency(frequency); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setPan(float pan, float fbpan) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + if (mNumRealChannels > 1) + { + if (mNumRealChannels == 2 || (mSound && mSound->mDefaultChannelMask == SPEAKER_ALLSTEREO)) + { + if (!(count & 1)) + { + pan = -1; + } + else + { + pan = 1; + } + } + } + + result = mRealChannel[count]->setPan(pan, fbpan); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setDSPClockDelay() +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setDSPClockDelay(); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setSpeakerLevels(speaker, levels, numlevels); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setPositionEx(unsigned int position, FMOD_TIMEUNIT postype, bool fromasync) +{ + FMOD_RESULT result = FMOD_OK; + Stream *stream; + + if (mFlags & CHANNELREAL_FLAG_STOPPED) + { + return FMOD_ERR_INVALID_HANDLE; + } + + stream = SAFE_CAST(Stream, mSound); + if (!stream) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (stream->mOpenState == FMOD_OPENSTATE_SETPOSITION && !fromasync) + { + return FMOD_ERR_NOTREADY; + } + + /* + This isn't redundant, we need this conversion so we can do an mPosition == newpos check + and mPosition only wants to know about PCM, not bytes or ms. + */ + if (postype == FMOD_TIMEUNIT_MS || postype == FMOD_TIMEUNIT_PCM || postype == FMOD_TIMEUNIT_PCMBYTES) + { + switch (postype) + { + case FMOD_TIMEUNIT_MS: + { + position = (unsigned int)((float)position / 1000.0f * stream->mDefaultFrequency); + postype = FMOD_TIMEUNIT_PCM; + break; + } + case FMOD_TIMEUNIT_PCM: + { + break; + } + case FMOD_TIMEUNIT_PCMBYTES: + { + stream->getSamplesFromBytes(position, &position); + postype = FMOD_TIMEUNIT_PCM; + break; + } + default: + { + break; + } + } + } + +#ifdef FMOD_SUPPORT_SENTENCING + if (postype == FMOD_TIMEUNIT_SENTENCE) + { + stream->mSubSoundIndex = stream->mSubSoundList[position].mIndex; + postype = FMOD_TIMEUNIT_MS; + position = 0; + } +#endif + + if (stream->mFlags & FMOD_SOUND_FLAG_FULLYBUFFERED) + { + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->setPosition(position, FMOD_TIMEUNIT_PCM); + } + + mLastPCM = position; + mDecodeOffset = 0; + mPosition = position; + mSamplesWritten = 0; + mSamplesPlayed = 0; + } + else if (postype != FMOD_TIMEUNIT_PCM || /* Could be mod/s3m/xm/it order/row type of seek */ + stream->mSubSoundIndex != stream->mCodec->mSubSoundIndex || /* Switching subsounds. Most definitely need to seek. */ + (stream->mFlags & FMOD_SOUND_FLAG_WANTSTOFLUSH) || /* The channel stream thread has filled some data and moved the mPosition value. */ + (mFlags & CHANNELREAL_FLAG_PLAYING && !(mParent->mFlags & CHANNELI_FLAG_JUSTWENTVIRTUAL))) /* If it is playing then the position cursor must have moved (even if mPosition hasn't, because we might not have been serviced by the ChannelStream thread yet, so we better do the seek to get the seekcallback and reset the voice and reset the mPosition variable. If it just went virtual, then it must be 0. */ + { + int count; + +#ifdef FMOD_SUPPORT_NONBLOCKSETPOS + if (stream->mMode & FMOD_NONBLOCKING && !fromasync) + { + result = AsyncThread::getAsyncThread(stream); + if (result != FMOD_OK) + { + return result; + } + + /* + Pause the real channel so it doesn't stutter in the stream thread. + */ + mFlags |= CHANNELREAL_FLAG_PAUSEDFORSETPOS; + + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->setPaused(true); + } + stream->mSample->clear(0, stream->mSample->mLength); + + + FMOD_OS_CriticalSection_Enter(stream->mAsyncData->mThread->mCrit); + { + stream->mOpenState = FMOD_OPENSTATE_SETPOSITION; + if (stream->mSubSoundParent) + { + stream->mSubSoundParent->mOpenState = FMOD_OPENSTATE_SETPOSITION; + } + + stream->mAsyncData->mPosition = position; + stream->mAsyncData->mPositionType = postype; + stream->mAsyncData->mNode.setData(stream); + stream->mAsyncData->mNode.addBefore(&stream->mAsyncData->mThread->mHead); + } + FMOD_OS_CriticalSection_Leave(stream->mAsyncData->mThread->mCrit); + + stream->mAsyncData->mThread->mThread.wakeupThread(); + } + else +#endif + { + bool paused = false; + + if (!fromasync) + { + FMOD_OS_CriticalSection_Enter(mSystem->mStreamUpdateCrit); + + result = mRealChannel[0]->getPaused(&paused); + if (result != FMOD_OK) + { + FMOD_OS_CriticalSection_Leave(mSystem->mStreamUpdateCrit); + return result; + } + + /* + Pause the real channel and reset it if it is playing + */ + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->setPaused(true); + } + } + + /* + Call the sound stream to setposition. + */ + result = stream->setPosition(position, postype); + if (result == FMOD_OK) + { + /* + Now reset and fill the pcm buffer. + */ + + FMOD_OS_CriticalSection_Enter(stream->mSystem->mStreamRealchanCrit); + for (count = 0; count < mNumRealChannels; count++) + { + if (mRealChannel[count]) + { + mRealChannel[count]->setPosition(0, FMOD_TIMEUNIT_PCM); + } + } + FMOD_OS_CriticalSection_Leave(stream->mSystem->mStreamRealchanCrit); + + mLastPCM = 0; + mDecodeOffset = 0; + mPosition = position; + mSamplesWritten = 0; + mSamplesPlayed = 0; + + result = stream->flush(); + } + + if (!fromasync) + { + /* + Set the pause state of the channel to what it used to be before + */ + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->setPaused(paused); + } + FMOD_OS_CriticalSection_Leave(mSystem->mStreamUpdateCrit); + } + } + } + else + { + /* + If this is a sentence, and the user started screwing around with getSubSound, make sure it is reset. + */ +#ifdef FMOD_SUPPORT_SENTENCING + if (stream->mSubSoundList && stream->mSubSoundShared && stream->mSubSoundIndex != stream->mSubSoundShared->mSubSoundIndex) + { + stream->mSubSoundShared->updateSubSound(stream->mSubSoundIndex, true); + } +#endif + +#ifdef PLATFORM_PS3 + /* + PS3 only? .. The raw codec and resampler never gets reset, so reset it here. + */ + if (mFlags & CHANNELREAL_FLAG_PAUSED) + { + int count; + /* + Now reset and fill the pcm buffer. + */ + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->setPosition(0, FMOD_TIMEUNIT_PCM); + } + } +#endif + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + Stream *stream; + bool getsubsoundtime = false; + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + stream = SAFE_CAST(Stream, mSound); + if (!stream) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + First check for subsound stuff. + */ + if (postype == FMOD_TIMEUNIT_SENTENCE_MS) + { + postype = FMOD_TIMEUNIT_MS; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCM) + { + postype = FMOD_TIMEUNIT_PCM; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCMBYTES) + { + postype = FMOD_TIMEUNIT_PCMBYTES; + getsubsoundtime = true; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE || postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + getsubsoundtime = true; + } + + if (getsubsoundtime +#ifdef FMOD_SUPPORT_SENTENCING + && !mSound->mSubSoundList +#endif + ) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (postype == FMOD_TIMEUNIT_MS || + postype == FMOD_TIMEUNIT_PCM || + postype == FMOD_TIMEUNIT_PCMBYTES || + postype == FMOD_TIMEUNIT_SENTENCE || + postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + unsigned int pos; + int currentsubsoundid = 0; + int currentsentenceid = 0; + + pos = mPosition; + +#ifdef FMOD_SUPPORT_SENTENCING + if (getsubsoundtime) + { + for (currentsubsoundid = 0; currentsubsoundid < mSound->mSubSoundListNum; currentsubsoundid++) + { + unsigned int lengthpcm = 0; + SoundI *sound = mSound->mSubSound[mSound->mSubSoundList[currentsubsoundid].mIndex]; + + if (sound) + { + if (mSound->mSubSoundShared) + { + FMOD_RESULT result; + FMOD_CODEC_WAVEFORMAT waveformat; + + result = stream->mCodec->mDescription.getwaveformat(stream->mCodec, stream->mSubSoundList[currentsubsoundid].mIndex, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + lengthpcm = waveformat.lengthpcm; + } + else + { + lengthpcm = sound->mLength; + } + } + + if (pos >= lengthpcm) + { + pos -= lengthpcm; + } + else + { + break; + } + + currentsentenceid++; + } + } +#endif + + if (postype == FMOD_TIMEUNIT_SENTENCE) + { + *position = currentsentenceid; + } +#ifdef FMOD_SUPPORT_SENTENCING + else if (postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + *position = mSound->mSubSoundList[currentsentenceid].mIndex; + } +#endif + else if (postype == FMOD_TIMEUNIT_PCM) + { + *position = pos; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(pos, position, mSound->mChannels, mSound->mFormat); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + *position = (unsigned int)((float)pos / mSound->mDefaultFrequency * 1000.0f); + } + } + else + { + return stream->getPosition(position, postype); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setLoopPoints(unsigned int loopstart, unsigned int looplength) +{ + FMOD_RESULT result; + + result = ChannelReal::setLoopPoints(loopstart, looplength); + if (result != FMOD_OK) + { + return FMOD_OK; + } + + result = mSound->setLoopPoints(loopstart, FMOD_TIMEUNIT_PCM, loopstart + looplength - 1, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return FMOD_OK; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setLoopCount(int loopcount) +{ + FMOD_RESULT result; + + result = ChannelReal::setLoopCount(loopcount); + if (result != FMOD_OK) + { + return FMOD_OK; + } + + result = mSound->setLoopCount(loopcount); + if (result != FMOD_OK) + { + return FMOD_OK; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::set3DAttributes() +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->set3DAttributes(); //pos, vel); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setLowPassGain(float gain) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setLowPassGain(gain); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::set3DMinMaxDistance() +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->set3DMinMaxDistance(); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::set3DOcclusion(float directocclusion, float reverbocclusion) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->set3DOcclusion(directocclusion, reverbocclusion); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setReverbProperties(prop); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels && count < 1; count++) + { + result = mRealChannel[count]->getReverbProperties(prop); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::isPlaying(bool *isplaying, bool includethreadlatency) +{ + *isplaying = !mFinished; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + return mRealChannel[0]->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + return mRealChannel[0]->getWaveData(wavearray, numvalues, channeloffset); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getDSPHead(DSPI **dsp) +{ + return mRealChannel[0]->getDSPHead(dsp); +} + + +#ifdef PLATFORM_WII +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setLowPassFilter(int cutoff) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setLowPassFilter(cutoff); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getLowPassFilter(int *cutoff) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->getLowPassFilter(cutoff); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setBiquadFilter(bool active, unsigned short b0, unsigned short b1, unsigned short b2, unsigned short a1, unsigned short a2) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setBiquadFilter(active, b0, b1, b2, a1, a2); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getBiquadFilter(bool *active, unsigned short *b0, unsigned short *b1, unsigned short *b2, unsigned short *a1, unsigned short *a2) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->getBiquadFilter(active, b0, b1, b2, a1, a2); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::setControllerSpeaker (unsigned int controllerspeaker, int subchannel) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + if (subchannel == count || subchannel < 0) + { + result2 = mRealChannel[count]->setControllerSpeaker(controllerspeaker); + if (result == FMOD_OK) + { + result = result2; + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelStream::getControllerSpeaker(unsigned int *controllerspeaker) +{ + *controllerspeaker = mControllerSpeaker; + + return FMOD_OK; +} +#endif /* #ifdef PLATFORM_WII */ +} + +#endif /* #ifdef FMOD_SUPPORT_STREAMING */ + diff --git a/src/fmod_channel_stream.h b/src/fmod_channel_stream.h new file mode 100755 index 0000000..5d01151 --- /dev/null +++ b/src/fmod_channel_stream.h @@ -0,0 +1,81 @@ +#ifndef _FMOD_CHANNEL_STREAM_H +#define _FMOD_CHANNEL_STREAM_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_STREAMING + +#include "fmod_channel_real.h" + +namespace FMOD +{ + class ChannelStream : public ChannelReal, public LinkedListNode + { + public: + + volatile bool mFinished; + unsigned int mLastPCM; + unsigned int mDecodeOffset; + unsigned int mSamplesPlayed; + unsigned int mSamplesWritten; + LinkedListNode mStreamNode; + int mNumRealChannels; + ChannelReal *mRealChannel[FMOD_CHANNEL_MAXREALSUBCHANNELS]; /* Even a real channel may contain a pointer to another real channel! (ie stream channel points to dsound channel) */ + + public: + + ChannelStream(); + + FMOD_RESULT setRealChannel(ChannelReal *realchan); + bool isStream() { return true; } + + FMOD_RESULT set2DFreqVolumePanFor3D(); + FMOD_RESULT moveChannelGroup(ChannelGroupI *oldchannelgroup, ChannelGroupI *newchannelgroup, bool forcedspreconnect); + + FMOD_RESULT alloc (); + FMOD_RESULT start (); + FMOD_RESULT update (int delta); + FMOD_RESULT updateStream (); + FMOD_RESULT setMode (FMOD_MODE mode); + + FMOD_RESULT stop (); + FMOD_RESULT setPaused (bool paused); + FMOD_RESULT setVolume (float volume); + FMOD_RESULT setFrequency (float frequency); + FMOD_RESULT setPan (float pan, float fbpan = 1); + FMOD_RESULT setDSPClockDelay (); + FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + FMOD_RESULT setSpeakerLevels (int speaker, float *levels, int numlevels); + FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype) { return setPositionEx(position, postype); } + FMOD_RESULT setPositionEx (unsigned int position, FMOD_TIMEUNIT postype, bool fromasync = false); + FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT setLoopPoints (unsigned int loopstart, unsigned int looplength); + FMOD_RESULT setLoopCount (int loopcount); + FMOD_RESULT setLowPassGain (float gain); + FMOD_RESULT set3DAttributes (); + FMOD_RESULT set3DMinMaxDistance (); + FMOD_RESULT set3DConeSettings (float iconeangle, float oconeangle, float ovolume); + FMOD_RESULT set3DConeOrientation(FMOD_VECTOR *orientation); + FMOD_RESULT set3DOcclusion (float directocclusion, float reverbocclusion); + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT isPlaying (bool *isplaying, bool includethreadlatency = false); + FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset); + FMOD_RESULT getDSPHead (DSPI **dsp); + +#ifdef PLATFORM_WII + FMOD_RESULT setLowPassFilter (int cutoff); + FMOD_RESULT getLowPassFilter (int *cutoff); + FMOD_RESULT setBiquadFilter (bool active, unsigned short b0, unsigned short b1, unsigned short b2, unsigned short a1, unsigned short a2); + FMOD_RESULT getBiquadFilter (bool *active, unsigned short *b0, unsigned short *b1, unsigned short *b2, unsigned short *a1, unsigned short *a2); + FMOD_RESULT setControllerSpeaker(unsigned int controllerspeaker, int subchannel = -1); + FMOD_RESULT getControllerSpeaker(unsigned int *controllerspeaker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_channelgroup.cpp b/src/fmod_channelgroup.cpp new file mode 100755 index 0000000..53c17a4 --- /dev/null +++ b/src/fmod_channelgroup.cpp @@ -0,0 +1,577 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_channelgroupi.h" +#include "fmod_systemi.h" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT ChannelGroup::release() +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->release(); + } +} + + +FMOD_RESULT ChannelGroup::getSystemObject(System **system) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getSystemObject(system); + } +} + + +FMOD_RESULT ChannelGroup::setVolume(float volume) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->setVolume(volume); + } +} + + +FMOD_RESULT ChannelGroup::getVolume(float *volume) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getVolume(volume); + } +} + + +FMOD_RESULT ChannelGroup::setPitch(float pitch) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->setPitch(pitch); + } +} + + +FMOD_RESULT ChannelGroup::getPitch(float *pitch) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getPitch(pitch); + } +} + + +FMOD_RESULT ChannelGroup::set3DOcclusion(float directocclusion, float reverbocclusion) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->set3DOcclusion(directocclusion, reverbocclusion); + } +} + + +FMOD_RESULT ChannelGroup::get3DOcclusion(float *directocclusion, float *reverbocclusion) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->get3DOcclusion(directocclusion, reverbocclusion); + } +} + + +FMOD_RESULT ChannelGroup::setPaused(bool paused) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->setPaused(paused); + } +} + + +FMOD_RESULT ChannelGroup::getPaused(bool *paused) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getPaused(paused); + } +} + + +FMOD_RESULT ChannelGroup::setMute(bool mute) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->setMute(mute); + } +} + + +FMOD_RESULT ChannelGroup::getMute(bool *mute) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getMute(mute); + } +} + + +FMOD_RESULT ChannelGroup::stop() +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->stop(); + } +} + + +FMOD_RESULT ChannelGroup::overrideVolume(float volume) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->overrideVolume(volume); + } +} + + +FMOD_RESULT ChannelGroup::overrideFrequency(float frequency) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->overrideFrequency(frequency); + } +} + + +FMOD_RESULT ChannelGroup::overridePan(float pan) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->overridePan(pan); + } +} + + +FMOD_RESULT ChannelGroup::overrideReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->overrideReverbProperties(prop); + } +} + + +FMOD_RESULT ChannelGroup::override3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->override3DAttributes(pos, vel); + } +} + + +FMOD_RESULT ChannelGroup::overrideSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->overrideSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); + } +} + + +FMOD_RESULT ChannelGroup::addGroup(ChannelGroup *group) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->addGroup((ChannelGroupI *)group); + } +} + + +FMOD_RESULT ChannelGroup::getNumGroups(int *numgroups) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getNumGroups(numgroups); + } +} + + +FMOD_RESULT ChannelGroup::getGroup(int index, ChannelGroup **group) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getGroup(index, (ChannelGroupI **)group); + } +} + + +FMOD_RESULT ChannelGroup::getParentGroup(ChannelGroup **group) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getParentGroup((ChannelGroupI **)group); + } +} + + +FMOD_RESULT ChannelGroup::getDSPHead(DSP **dsp) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getDSPHead((DSPI **)dsp); + } +} + + +FMOD_RESULT ChannelGroup::addDSP(DSP *dsp, DSPConnection **connection) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->addDSP((DSPI *)dsp, (DSPConnectionI **)connection); + } +} + + +FMOD_RESULT ChannelGroup::getName(char *name, int namelen) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getName(name, namelen); + } +} + + +FMOD_RESULT ChannelGroup::getNumChannels(int *numchannels) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getNumChannels(numchannels); + } +} + + +FMOD_RESULT ChannelGroup::getChannel(int index, Channel **channel) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getChannel(index, (Channel **)channel); + } +} + + +FMOD_RESULT ChannelGroup::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); + } +} + + +FMOD_RESULT ChannelGroup::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getWaveData(wavearray, numvalues, channeloffset); + } +} + + +FMOD_RESULT ChannelGroup::setUserData(void *_userdata) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->setUserData(_userdata); + } +} + + +FMOD_RESULT ChannelGroup::getUserData(void **_userdata) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getUserData(_userdata); + } +} + + +FMOD_RESULT ChannelGroup::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + ChannelGroupI *channelgroupi; + + result = ChannelGroupI::validate(this, &channelgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return channelgroupi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_channelgroupi.cpp b/src/fmod_channelgroupi.cpp new file mode 100755 index 0000000..f622659 --- /dev/null +++ b/src/fmod_channelgroupi.cpp @@ -0,0 +1,1959 @@ +#include "fmod_settings.h" + +#include "fmod_channelgroupi.h" +#include "fmod_systemi.h" +#include "fmod_dsp_fft.h" +#include "fmod_dsp_filter.h" +#include "fmod_outputi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::validate(ChannelGroup *channelgroup, ChannelGroupI **channelgroupi) +{ + FMOD::ChannelGroupI *cg = (FMOD::ChannelGroupI *)channelgroup; + + if (!channelgroup) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!channelgroupi) + { + return FMOD_ERR_INVALID_PARAM; + } + + *channelgroupi = cg; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT ChannelGroupI::release() +{ + if (this == mSystem->mChannelGroup) + { + return FMOD_ERR_INVALID_HANDLE; + } + + return releaseInternal(); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::releaseInternal(bool releasechildren) +{ + if (mGroupHead && releasechildren) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + ChannelGroupI *next = (ChannelGroupI *)currentgroup->getNext(); + + currentgroup->releaseInternal(true); + + currentgroup = next; + } + } + + /* + First move all channels back to the main group + */ + if (mSystem->mChannelGroup && this != mSystem->mChannelGroup) + { + while (mChannelHead.getNext() != &mChannelHead) + { + ChannelI *channel = (ChannelI *)mChannelHead.getNext()->getData(); + + channel->setChannelGroup(mSystem->mChannelGroup); + } + } + + if (mDSPHead && mDSPMixTarget && mDSPMixTarget != mDSPHead) + { + /* + If mDSPMixTarget != mDSPHead then the user added their own DSP unit to this ChannelGroup using addDSP and we had + to create a new mDSPHead on the fly. In this case, we need to free that mDSPHead's memory (mDSPHead->release()) + but not free the mDSPMixTarget's memory (mDSPMixTarget->release(false)) because mDSPMixTarget is pointing to the + original mDSPHead which is a pointer to a DSPFilter object statically declared in the ChannelGroup class. It's like + that to save on memory allocs. + + AJS 07/07/08 + */ + + mDSPMixTarget->release(false); + mDSPMixTarget = 0; + + if (mDSPHead) + { + mDSPHead->release(); + mDSPHead = 0; + } + } + else + { + if (mDSPHead) + { + mDSPHead->release(false); + mDSPHead = 0; + } + + mDSPMixTarget = 0; + } + + if (mName) + { + FMOD_Memory_Free(mName); + } + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + ChannelGroupI *mastergroup; + + mSystem->getMasterChannelGroup(&mastergroup); + + /* + If it is not the master, put any children groups back onto the master. + */ + if (mastergroup && this != mastergroup) + { + while (currentgroup != mGroupHead) + { + ChannelGroupI *next = (ChannelGroupI *)currentgroup->getNext(); + + mastergroup->addGroup(currentgroup); + + currentgroup = next; + } + } + + FMOD_Memory_Free(mGroupHead); + } + + if (this == mSystem->mOutput->mMusicChannelGroup) + { + mSystem->mOutput->mMusicChannelGroup = 0; + } + + removeNode(); + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT ChannelGroupI::getSystemObject(System **system) +{ + if (!system) + { + return FMOD_ERR_INVALID_PARAM; + } + + *system = (System *)mSystem; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setVolume(float volume) +{ + FMOD_RESULT result; + + if (volume < 0) + { + volume = 0; + } + if (volume > 1) + { + volume = 1; + } + + mVolume = volume; + + result = setVolumeInternal(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setVolumeInternal() +{ + LinkedListNode *current; + bool forceupdatepos = false; + float newvolume; + + newvolume = (mParent ? mParent->mRealVolume : 1.0f) * mVolume; + + if (newvolume != mRealVolume) + { + forceupdatepos = true; + } + + mRealVolume = newvolume; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->setVolumeInternal(); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + float v; + + channel->getVolume(&v); + channel->setVolume(v, forceupdatepos); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getVolume(float *volume) +{ + if (!volume) + { + return FMOD_ERR_INVALID_PARAM; + } + + *volume = mVolume; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setPitch(float pitch) +{ + FMOD_RESULT result; + + if (pitch < 0) + { + pitch = 0; + } + + mPitch = pitch; + + result = setPitchInternal(); + if (result != FMOD_OK) + { + return result; + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setPitchInternal() +{ + LinkedListNode *current; + + mRealPitch = (mParent ? mParent->mRealPitch : 1.0f) * mPitch; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->setPitchInternal(); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + float freq; + + channel->getFrequency(&freq); + channel->setFrequency(freq); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getPitch(float *pitch) +{ + if (!pitch) + { + return FMOD_ERR_INVALID_PARAM; + } + + *pitch = mPitch; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::set3DOcclusion(float direct, float reverb) +{ + FMOD_RESULT result; + + direct = FMOD_MAX(0, FMOD_MIN(direct, 1)); + reverb = FMOD_MAX(0, FMOD_MIN(reverb, 1)); + + mDirectOcclusion = direct; + mReverbOcclusion = reverb; + + result = set3DOcclusionInternal(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::set3DOcclusionInternal() +{ + LinkedListNode *current; + + mRealDirectOcclusionVolume = (mParent ? mParent->mRealDirectOcclusionVolume : 1.0f) * (1.0f - mDirectOcclusion); + mRealReverbOcclusionVolume = (mParent ? mParent->mRealReverbOcclusionVolume : 1.0f) * (1.0f - mReverbOcclusion); + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->set3DOcclusionInternal(); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + float direct, reverb; + + /* + Ignore return codes, as we want to do this for all channels + regardless of individual failures + */ + channel->get3DOcclusionInternal(&direct, &reverb); + channel->set3DOcclusionInternal(direct, reverb, false); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::get3DOcclusion(float *directOcclusion, float *reverbOcclusion) +{ + if (directOcclusion) + { + *directOcclusion = mDirectOcclusion; + } + if (reverbOcclusion) + { + *reverbOcclusion = mReverbOcclusion; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setPaused(bool paused, bool setpausedflag) +{ + LinkedListNode *current; + + if (setpausedflag) + { + mPaused = paused; + } + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->setPaused(paused, false); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + bool paused = false; + + // ignore return values here in case we get FMOD_ERR_INVALID_HANDLE + channel->getPaused(&paused); + channel->setPaused(paused); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getPaused(bool *paused) +{ + if (!paused) + { + return FMOD_ERR_INVALID_PARAM; + } + + *paused = mPaused; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setMute(bool mute, bool setmuteflag) +{ + LinkedListNode *current; + + if (setmuteflag) + { + mMute = mute; + } + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->setMute(mute, false); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->setMute(channel->mFlags & CHANNELI_FLAG_MUTED ? true : false); + + current = current->getNext(); + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getMute(bool *mute) +{ + if (!mute) + { + return FMOD_ERR_INVALID_PARAM; + } + + *mute = mMute; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::stop() +{ + LinkedListNode *current; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->stop(); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + LinkedListNode *next = current->getNext(); + + channel->stop(); + + current = next; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::overrideVolume(float volume) +{ + LinkedListNode *current; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->overrideVolume(volume); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->setVolume(volume); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::overrideFrequency(float frequency) +{ + LinkedListNode *current; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->overrideFrequency(frequency); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->setFrequency(frequency); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::overridePan(float pan) +{ + LinkedListNode *current; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->overridePan(pan); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->setPan(pan); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::overrideReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + LinkedListNode *current; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->overrideReverbProperties(prop); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->setReverbProperties(prop); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::override3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) +{ + LinkedListNode *current; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->override3DAttributes(pos, vel); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->set3DAttributes(pos, vel); + + current = current->getNext(); + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::overrideSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + LinkedListNode *current; + + if (mGroupHead) + { + ChannelGroupI *currentgroup = (ChannelGroupI *)(mGroupHead->getNext()); + + while (currentgroup != mGroupHead) + { + currentgroup->overrideSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); + + currentgroup = (ChannelGroupI *)currentgroup->getNext(); + } + } + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + ChannelI *channel = (ChannelI *)current->getData(); + + channel->setSpeakerMix(frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::updateChildMixTarget(DSPI *dsp) +{ + FMOD_RESULT result; + DSPI *olddsp = mDSPMixTarget; + + if (mDSPHead) + { + return FMOD_OK; /* Child groups should already be pointing to this dsp. No need to recurse down */ + } + else + { + mDSPMixTarget = dsp; /* set this mixtarget to be the parent's dsp. */ + } + + if (mGroupHead) + { + for (LinkedListNode *node = mGroupHead->getNext(); node != mGroupHead; node = node->getNext()) + { + ChannelGroupI *c = (ChannelGroupI *)node; + + result = c->updateChildMixTarget(dsp); + if (result != FMOD_OK) + { + return result; + } + } + } + + /* Fix up channels. */ + { + LinkedListNode *current; + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + FMOD::DSPI *dsphead; + // cache next because setChannelGroupInternal modifies the list + LinkedListNode *next = current->getNext(); + ChannelI *channel = (ChannelI *)current->getData(); + + result = channel->getDSPHead(&dsphead); + if (result == FMOD_OK) + { + result = olddsp->disconnectFrom(dsphead); + if (result != FMOD_OK) + { + return result; + } + + result = channel->setChannelGroupInternal(this, true, true); + if (result != FMOD_OK) + { + return result; + } + } + + current = next; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::addGroup(ChannelGroupI *group) +{ + FMOD_RESULT result; + + if (!group) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Disconnect new group from where it came. + */ + group->removeNode(); + + if (group->mDSPHead) + { + group->mDSPHead->disconnectAll(false, true); + } + + if (!mGroupHead) + { + mGroupHead = FMOD_Object_Calloc(ChannelGroupI); + if (!mGroupHead) + { + return FMOD_ERR_MEMORY; + } + } + + /* + Add to the new group. + */ + group->addBefore(mGroupHead); + + if (mDSPMixTarget) + { + if (group->mDSPHead) + { + result = mDSPMixTarget->addInputQueued(group->mDSPHead, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = group->updateChildMixTarget(mDSPMixTarget); + if (result != FMOD_OK) + { + return result; + } + } + } + + group->mParent = this; + + result = group->setPaused(mPaused, false); + if (result != FMOD_OK) + { + return result; + } + result = group->setMute(mMute, false); + if (result != FMOD_OK) + { + return result; + } + result = group->setVolumeInternal(); + if (result != FMOD_OK) + { + return result; + } + result = group->setPitchInternal(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getNumGroups(int *numgroups) +{ + if (!numgroups) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mGroupHead) + { + *numgroups = 0; + } + else + { + *numgroups = mGroupHead->count(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getGroup(int index, ChannelGroupI **group) +{ + ChannelGroupI *current; + int count, numgroups; + + if (!mGroupHead) + { + return FMOD_ERR_INVALID_PARAM; + } + + numgroups = mGroupHead->count(); + + if (index < 0 || index >= numgroups) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!group) + { + return FMOD_ERR_INVALID_PARAM; + } + + current = (ChannelGroupI *)(mGroupHead->getNext()); + for (count = 0; count < numgroups; count++) + { + if (count == index) + { + *group = current; + } + + current = (ChannelGroupI *)(current->getNext()); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getParentGroup(ChannelGroupI **group) +{ + if (!group) + { + return FMOD_ERR_INVALID_PARAM; + } + + *group = (ChannelGroupI *)mParent; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getDSPHead(DSPI **dsp) +{ + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mDSPHead) + { + *dsp = 0; + return FMOD_ERR_DSP_NOTFOUND; + } + + *dsp = mDSPHead; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::addDSP(DSPI *dsp, DSPConnectionI **dspconnection) +{ + FMOD_RESULT result; + + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mDSPHead) + { + return FMOD_ERR_DSP_NOTFOUND; + } + + /* + Allocate a new dsp node and make it the new dsphead. Let the old node now be a seperate mDSPMixTarget with its own memory. + */ + if (mDSPHead == mDSPMixTarget) + { + DSPI *output; + FMOD_DSP_DESCRIPTION description = mDSPHead->mDescription; + + result = mSystem->createDSP(&description, (DSPI **)&mDSPHead); + if (result != FMOD_OK) + { + return result; + } + mDSPHead->setDefaults((float)mSystem->mOutputRate, -1, -1, -1); + mDSPHead->setActive(true); + + /* + Now disconnect from parent channelgroup (or maybe the dsp effect the parent may have added). + */ + result = mDSPMixTarget->getOutput(0, &output); + if (result != FMOD_OK) + { + return result; + } + + result = output->disconnectFrom(mDSPMixTarget); + if (result != FMOD_OK) + { + return result; + } + + result = output->addInput(mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPHead->addInput(mDSPMixTarget); + if (result != FMOD_OK) + { + return result; + } + } + + result = mDSPHead->insertInputBetween(dsp, 0, false, dspconnection); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getName(char *name, int namelen) +{ + if (!name) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (namelen > FMOD_STRING_MAXNAMELEN) + { + namelen = FMOD_STRING_MAXNAMELEN; + } + + if (mName) + { + FMOD_strncpy(name, mName, namelen); + } + else + { + FMOD_strncpy(name, "(null)", namelen); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getNumChannels(int *numchannels) +{ + if (!numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numchannels = mNumChannels; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getChannel(int index, Channel **channel) +{ + int count = 0; + LinkedListNode *current; + + if (!channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + *channel = 0; + + current = mChannelHead.getNext(); + + while (current != &mChannelHead) + { + if (count == index) + { + *channel = (Channel *)((ChannelI *)current->getData())->mHandleCurrent; + return FMOD_OK; + } + + count++; + current = current->getNext(); + } + + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ +#ifdef FMOD_SUPPORT_GETSPECTRUM + FMOD_RESULT result = FMOD_OK; + float *buffer; + unsigned int position, length; + int numchannels, windowsize; + static DSPFFT fft; + DSPFilter *dsphead = (DSPFilter *)mDSPHead; + + if (!dsphead) + { + return FMOD_ERR_DSP_NOTFOUND; + } + + windowsize = numvalues * 2; + + if (windowsize != (1 << 7) && + windowsize != (1 << 8) && + windowsize != (1 << 9) && + windowsize != (1 << 10) && + windowsize != (1 << 11) && + windowsize != (1 << 12) && + windowsize != (1 << 13) && + windowsize != (1 << 14)) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mSystem->getSoftwareFormat(0, 0, &numchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = dsphead->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (windowsize > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + position -= windowsize; + if ((int)position < 0) + { + position += length; + } + + mSystem->mUpdateTimeStamp.stampIn(); + + result = fft.getSpectrum(buffer, position, length, spectrumarray, windowsize, channeloffset, numchannels, windowtype); + + mSystem->mUpdateTimeStamp.stampOut(95); + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif // FMOD_SUPPORT_GETSPECTRUM +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result = FMOD_OK; + float *buffer; + unsigned int position, length; + int numchannels, count; + DSPFilter *dsphead = (DSPFilter *)mDSPHead; + + if (!dsphead) + { + return FMOD_ERR_DSP_NOTFOUND; + } + + result = mSystem->getSoftwareFormat(0, 0, &numchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = dsphead->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (numvalues > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + position -= numvalues; + if ((int)position < 0) + { + position += length; + } + + for (count = 0; count < numvalues; count++) + { + wavearray[count] = buffer[position*numchannels+channeloffset]; + position++; + if (position >= length) + { + position = 0; + } + } + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelGroupI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT ChannelGroupI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + /* + This channelgroup object + */ + tracker->add(false, FMOD_MEMBITS_CHANNELGROUP, sizeof(*this)); + + /* + Channelgroup name, if any + */ + if (mName) + { + tracker->add(false, FMOD_MEMBITS_STRING, FMOD_strlen(mName) + 1); + } + + /* + If there are child ChannelGroups then it will have a ChannelGroupI here just for list linkage + */ + if (mGroupHead) + { + tracker->add(false, FMOD_MEMBITS_CHANNELGROUP, sizeof(ChannelGroupI)); + } + + /* + Head DSP node + */ + if (mDSPHead) + { + tracker->add(false, FMOD_MEMBITS_DSP, sizeof(DSPFilter)); + } + + // if mDSPHead != mDSPMixTarget then any units between them should be counted +//AJS DSPI *mDSPMixTarget; /* By default this just points to mDSPHead unless dsp effects are added. */ + + return FMOD_OK; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#if defined(FMOD_SUPPORT_MEMORYTRACKER) && defined(FMOD_SUPPORT_SOFTWARE) + +FMOD_RESULT ChannelGroupSoftware::getMemoryUsedImpl(MemoryTracker *tracker) +{ + /* + This channelgroup object + */ + tracker->add(false, FMOD_MEMBITS_CHANNELGROUP, sizeof(*this)); + + /* + Channelgroup name, if any + */ + if (mName) + { + tracker->add(false, FMOD_MEMBITS_STRING, FMOD_strlen(mName) + 1); + } + + /* + If there are child ChannelGroups then it will have a ChannelGroupI here just for list linkage + */ + if (mGroupHead) + { + tracker->add(false, FMOD_MEMBITS_CHANNELGROUP, sizeof(ChannelGroupI)); + + for (LinkedListNode *node = mGroupHead->getNext(); node != mGroupHead; node = node->getNext()) + { + ChannelGroupSoftware *c = (ChannelGroupSoftware *)node; + CHECK_RESULT(c->getMemoryUsed(tracker)); + } + } + + /* + This points to mDSPHeadMemory now, which has already been counted + */ +/*AJS + if (mDSPHead) + { + tracker->add(MEMTYPE_DSPUNIT, sizeof(DSPFilter)); + } +AJS*/ + + // if mDSPHead != mDSPMixTarget then any units between them should be counted +//AJS DSPI *mDSPMixTarget; /* By default this just points to mDSPHead unless dsp effects are added. */ + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_channelgroupi.h b/src/fmod_channelgroupi.h new file mode 100755 index 0000000..b359c16 --- /dev/null +++ b/src/fmod_channelgroupi.h @@ -0,0 +1,128 @@ +#ifndef _FMOD_CHANNELGROUPI_H +#define _FMOD_CHANNELGROUPI_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_dsp_filter.h" +#include "fmod_linkedlist.h" +#include "fmod_string.h" +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class ChannelI; + class DSPI; + class DSPConnectionI; + class SystemI; + + class ChannelGroupI : public LinkedListNode /* This linked list node entry is for System::mChannelGroupHead */ + { + DECLARE_MEMORYTRACKER + + public: + + static FMOD_RESULT validate(ChannelGroup *channelgroup, ChannelGroupI **channelgroupi); + + SystemI *mSystem; + void *mUserData; + DSPI *mDSPHead; + DSPI *mDSPMixTarget; /* By default this just points to mDSPHead unless dsp effects are added. */ + ChannelGroupI *mParent; + ChannelGroupI *mGroupHead; + LinkedListNode mChannelHead; + int mNumChannels; + char *mName; + float mVolume, mRealVolume; + float mDirectOcclusion, mReverbOcclusion; + float mRealDirectOcclusionVolume, mRealReverbOcclusionVolume; + float mPitch; + float mRealPitch; + + bool mMute; + bool mPaused; + + FMOD_RESULT updateChildMixTarget(DSPI *dsp); + + public: + + ChannelGroupI() + { + mVolume = mRealVolume = 1; + mPitch = mRealPitch = 1; + mDirectOcclusion = mReverbOcclusion = 0; + mRealDirectOcclusionVolume = mRealReverbOcclusionVolume = 1; + } + + FMOD_RESULT release (); + FMOD_RESULT releaseInternal (bool releasechildren = false); + FMOD_RESULT getSystemObject (System **system); + + // Channelgroup scale values. (changes attributes relative to the channels, doesn't overwrite them) + FMOD_RESULT setVolume (float volume); + FMOD_RESULT setVolumeInternal (); + FMOD_RESULT getVolume (float *volume); + FMOD_RESULT setPitch (float pitch); + FMOD_RESULT setPitchInternal (); + FMOD_RESULT getPitch (float *pitch); + FMOD_RESULT set3DOcclusion (float directocclusion, float reverbocclusion); + FMOD_RESULT set3DOcclusionInternal (); + FMOD_RESULT get3DOcclusion (float *directocclusion, float *reverbocclusion); + FMOD_RESULT setPaused (bool paused, bool setpausedflag = true); + FMOD_RESULT getPaused (bool *paused); + FMOD_RESULT setMute (bool mute, bool setmuteflag = true); + FMOD_RESULT getMute (bool *mute); + + // Channelgroup override values. (recursively overwrites whatever settings the channels had) + FMOD_RESULT stop (); + FMOD_RESULT overrideVolume (float volume); + FMOD_RESULT overrideFrequency (float frequency); + FMOD_RESULT overridePan (float pan); + FMOD_RESULT overrideReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT override3DAttributes (const FMOD_VECTOR *pos, const FMOD_VECTOR *vel); + FMOD_RESULT overrideSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + + // Nested channel groups. + FMOD_RESULT addGroup (ChannelGroupI *group); + FMOD_RESULT getNumGroups (int *numgroups); + FMOD_RESULT getGroup (int index, ChannelGroupI **group); + FMOD_RESULT getParentGroup (ChannelGroupI **group); + + // DSP functionality only for channel groups playing sounds created with FMOD_SOFTWARE. + FMOD_RESULT getDSPHead (DSPI **dsp); + FMOD_RESULT addDSP (DSPI *dsp, DSPConnectionI **connection); + + // Information only functions. + FMOD_RESULT getName (char *name, int namelen); + FMOD_RESULT getNumChannels (int *numchannels); + FMOD_RESULT getChannel (int index, Channel **channel); + FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset); + + // Userdata set/get. + FMOD_RESULT setUserData (void *userdata); + FMOD_RESULT getUserData (void **userdata); + + FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + }; + +#ifdef FMOD_SUPPORT_SOFTWARE + class ChannelGroupSoftware : public ChannelGroupI + { + DECLARE_MEMORYTRACKER + + public: + DSPFilter mDSPHeadMemory; +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + char mAlign[128]; +#endif + }; +#endif + +} + +#endif + + diff --git a/src/fmod_channeli.cpp b/src/fmod_channeli.cpp new file mode 100755 index 0000000..1152871 --- /dev/null +++ b/src/fmod_channeli.cpp @@ -0,0 +1,6756 @@ +#include "fmod_settings.h" + +#include "fmod_3d.h" +#include "fmod_channeli.h" +#include "fmod_channel_real.h" +#include "fmod_codeci.h" +#include "fmod_dspi.h" +#include "fmod_dsp_filter.h" +#include "fmod_output.h" +#include "fmod_output_emulated.h" +#include "fmod_soundi.h" +#include "fmod_sound_sample.h" +#include "fmod_speakermap.h" +#include "fmod_speakerlevels_pool.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + Channel handle are composed like so : + + S = system id = SystemI::mId + I = channel index = index into SystemI::mChannel + C = reference count + + SSSSIIII IIIIIIII CCCCCCCC CCCCCCCC + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_API ChannelI::validate(Channel *channel, ChannelI **channeli) +{ + unsigned int handle = (unsigned int)((FMOD_UINT_NATIVE)channel); + unsigned int sysindex = (handle >> SYSTEMID_SHIFT) & SYSTEMID_MASK; + unsigned int index = (handle >> CHANINDEX_SHIFT) & CHANINDEX_MASK; + unsigned int refcount = (handle >> REFCOUNT_SHIFT) & REFCOUNT_MASK; + ChannelI *tmpchanneli; + SystemI *sys; + FMOD_RESULT result; + + if (!channeli) + { + return FMOD_ERR_INVALID_PARAM; + } + + *channeli = 0; + + result = SystemI::getInstance(sysindex, &sys); + if (result != FMOD_OK) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!sys->mChannel) + { + return FMOD_ERR_UNINITIALIZED; + } + + if ((int)index >= sys->mNumChannels) + { + return FMOD_ERR_INVALID_HANDLE; + } + + tmpchanneli = &sys->mChannel[index]; + + /* + refcount == 0 means it's an absolute channel handle so it's always valid + */ + if (refcount && (tmpchanneli->mHandleCurrent != handle)) + { + /* + If the reference count is greater by two or more (it's incremented on every play and every stop) + then the channel has been reused i.e. stolen + */ + if ((((tmpchanneli->mHandleCurrent >> REFCOUNT_SHIFT) & REFCOUNT_MASK) - refcount) >= 2) + { + return FMOD_ERR_CHANNEL_STOLEN; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + } + + *channeli = tmpchanneli; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::returnToFreeList() +{ + SystemI *systemi; + + systemi = SAFE_CAST(SystemI, mSystem); + if (!systemi) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Just remove it from this list, dont add it anywhere else. + */ + mSortedListNode.removeNode(); + mSoundGroupSortedListNode.removeNode(); + + /* + Remove it from used list, add it to free list. + */ + removeNode(); + addAfter(&systemi->mChannelFreeListHead); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setDefaults() +{ + float realfrequency, realvolume, realpan; + int defaultpriority, channels; + float defaultfrequency; + float defaultvolume; + float defaultpan; + float frequencyvariation; + float volumevariation; + float panvariation; + unsigned int channelmask; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->mSound) + { + SoundI *soundi = SAFE_CAST(SoundI, mRealChannel[0]->mSound); + + defaultpriority = soundi->mDefaultPriority; + defaultfrequency = soundi->mDefaultFrequency; + defaultvolume = soundi->mDefaultVolume; + defaultpan = soundi->mDefaultPan; + frequencyvariation = soundi->mFrequencyVariation; + volumevariation = soundi->mVolumeVariation; + panvariation = soundi->mPanVariation; + channels = soundi->mChannels; + channelmask = soundi->mDefaultChannelMask; + } + else if (mRealChannel[0]->mDSP) + { + DSPI *dspi = SAFE_CAST(DSPI, mRealChannel[0]->mDSP); + + defaultpriority = dspi->mDefaultPriority; + defaultfrequency = dspi->mDefaultFrequency; + defaultvolume = dspi->mDefaultVolume; + defaultpan = dspi->mDefaultPan; + frequencyvariation = 0; + volumevariation = 0; + panvariation = 0; + channels = 0; + channelmask = 0; + } + else + { + return FMOD_ERR_INTERNAL; + } + + /* + Virtual channel stuff + */ + mPriority = defaultpriority; + + /* + Now update the real channel's defaults. + */ + mDirectOcclusion = 0.0f; + mDirectOcclusionTarget = 0.0f; + mUserDirectOcclusion = 0.0f; + mReverbOcclusion = 0.0f; + mReverbOcclusionTarget = 0.0f; + mUserReverbOcclusion = 0.0f; + mDirectOcclusionRateOfChange = 0.0f; + mReverbOcclusionRateOfChange = 0.0f; + + realfrequency = defaultfrequency; + realvolume = defaultvolume; + realpan = defaultpan; + + if (frequencyvariation > 0) + { + float randval = ((float)(FMOD_RAND() % 32768) / 16384.0f) - 1.0f; /* -1.0 to +1.0 */ + realfrequency += frequencyvariation * randval; + } + if (volumevariation > 0) + { + float randval = ((float)(FMOD_RAND() % 32768) / 16384.0f) - 1.0f; /* -1.0 to +1.0 */ + realvolume += volumevariation * randval; + } + if (panvariation > 0) + { + float randval = ((float)(FMOD_RAND() % 32768) / 8192.0f) - 2.0f; /* -2.0 to +2.0 */ + realpan += panvariation * randval; + } + + setFrequency(realfrequency); + setVolume(realvolume); + + if (channelmask & SPEAKER_WAVEFORMAT_MASK) + { + int speaker, incount; + unsigned int maskbit = 1; + float clevels[DSP_MAXLEVELS_IN]; + + FMOD_memset(clevels, 0, DSP_MAXLEVELS_IN * sizeof(float)); + + incount = 0; + for (speaker = 0; speaker < channels; speaker++) + { + if (channelmask & maskbit) + { + clevels[incount] = 1.0f; + + incount++; + } + + maskbit <<= 1; + } + + setSpeakerMix(clevels[0], clevels[1], clevels[2], clevels[3], clevels[4], clevels[5], clevels[6], clevels[7]); + } + else + { + setPan(realpan); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::referenceStamp(bool newstamp) +{ + unsigned int sysindex, index, refcount; + + sysindex = (mHandleCurrent >> SYSTEMID_SHIFT) & SYSTEMID_MASK; + index = (mHandleCurrent >> CHANINDEX_SHIFT) & CHANINDEX_MASK; + refcount = ((newstamp ? mHandleCurrent : mHandleOriginal) >> REFCOUNT_SHIFT) & REFCOUNT_MASK; + + refcount++; + if (refcount > REFCOUNT_MASK) + { + refcount = 1; + } + + mHandleCurrent = (sysindex << SYSTEMID_SHIFT) | ((index << CHANINDEX_SHIFT) & (CHANINDEX_MASK << CHANINDEX_SHIFT)) | (refcount << REFCOUNT_SHIFT); + + if (newstamp) + { + mHandleOriginal = mHandleCurrent; + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + This function takes 3 states. + -1000 = Let the function calculate listpos + 0 to 1025 = Supply a listpos manually + -1 = Supply a code for 'remove me' + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::updatePosition() +{ + unsigned int oldlistposition, oldsglistposition; + float audibility, audibilitysoundgroup; + bool forcevirtual = false; + float realdirecttransmission, realreverbtransmission; + SoundI *sound = mRealChannel[0]->mSound; + + if (mFlags & CHANNELI_FLAG_PLAYINGPAUSED) + { + return FMOD_OK; + } + + if (!mSystem) + { + return FMOD_ERR_INVALID_HANDLE; + } + + getAudibilityInternal(&audibilitysoundgroup, false); + audibility = audibilitysoundgroup * mFadeVolume; + + realdirecttransmission = (1.0f - mDirectOcclusion) * + (1.0f - mUserDirectOcclusion) * + mChannelGroup->mRealDirectOcclusionVolume; + realreverbtransmission = (1.0f - mReverbOcclusion) * + (1.0f - mUserReverbOcclusion) * + mChannelGroup->mRealReverbOcclusionVolume; + + if (audibility <= mSystem->mAdvancedSettings.vol0virtualvol && mSystem->mFlags & FMOD_INIT_VOL0_BECOMES_VIRTUAL) + { + forcevirtual = true; + } + if (realdirecttransmission == 0.0f && realreverbtransmission > 0.0f) /* Even though dry is silent, there might be a bit of wet. */ + { + forcevirtual = false; + } + if (mPriority == 0 && sound && sound->isStream()) + { + forcevirtual = false; + } + + forceVirtual(forcevirtual); + + oldlistposition = mListPosition; + oldsglistposition = mSoundGroupListPosition; + mSoundGroupListPosition = mListPosition = mPriority * (FMOD_MAXAUDIBIILITY + 1); // 0-1000, 1001-2001, 2002-3002, 3003-4003. + mListPosition += (FMOD_MAXAUDIBIILITY - (int)(audibility * FMOD_MAXAUDIBIILITY)); // 0-1000 + mSoundGroupListPosition += (FMOD_MAXAUDIBIILITY - (int)(audibilitysoundgroup * FMOD_MAXAUDIBIILITY)); // 0-1000 + + if (mListPosition != oldlistposition) + { + mSortedListNode.removeNode(); + mSortedListNode.addAt(&mSystem->mChannelSortedListHead, &mSystem->mChannelSortedListHead, mListPosition); + mSortedListNode.setData(this); + } + + if (sound && sound->mSoundGroup && mSoundGroupListPosition != oldsglistposition) + { + mSoundGroupSortedListNode.removeNode(); + mSoundGroupSortedListNode.addAt(&sound->mSoundGroup->mChannelListHead, &sound->mSoundGroup->mChannelListHead, mSoundGroupListPosition); + mSoundGroupSortedListNode.setData(this); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::forceVirtual(bool force) +{ + FMOD_RESULT result; + + if (force) + { + bool isvirtual; + + if (mFlags & CHANNELI_FLAG_FORCEVIRTUAL) + { + return FMOD_OK; + } + + result = isVirtual(&isvirtual); + if (result != FMOD_OK) + { + return result; + } + + mFlags |= CHANNELI_FLAG_FORCEVIRTUAL; + + if (!isvirtual && mRealChannel[0]) + { + FMOD_CHANNEL_INFO realinfo; + ChannelReal *emuchannelreal; + ChannelGroupI *channelgroup; + bool playingpaused = (mFlags & CHANNELI_FLAG_PLAYINGPAUSED) ? true : false; + + result = mSystem->mEmulated->getFreeChannel(mRealChannel[0]->mMode, &emuchannelreal, 1, 1, 0); + if (result != FMOD_OK) + { + return result; + } + + channelgroup = mChannelGroup; + getChannelInfo(&realinfo); + stopEx(CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_DONTFREELEVELS); + + /* + This channel is going from real to emulated, so just give it 1 subchannel and point it to 1 emulated voice + */ + mNumRealChannels = 1; + mRealChannel[0] = emuchannelreal; + + if (realinfo.mSound) + { + result = play(realinfo.mSound->mSubSampleParent, true, false, false); + setChannelGroup(channelgroup); + setChannelInfo(&realinfo); + + mFlags &= ~CHANNELI_FLAG_PLAYINGPAUSED; /* Stop it from re-evaluating position in setpaused(false). */ + setPaused(realinfo.mPaused); + } + else if (realinfo.mDSP) + { + result = play(realinfo.mDSP, true, false, false); + setChannelGroup(channelgroup); + setChannelInfo(&realinfo); + + mFlags &= ~CHANNELI_FLAG_PLAYINGPAUSED; /* Stop it from re-evaluating position in setpaused(false). */ + setPaused(realinfo.mPaused); + } + if (playingpaused) + { + mFlags |= CHANNELI_FLAG_JUSTWENTVIRTUAL; + } + } + } + else + { + if (!(mFlags & CHANNELI_FLAG_FORCEVIRTUAL)) + { + return FMOD_OK; + } + + mFlags &= ~CHANNELI_FLAG_FORCEVIRTUAL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getChannelInfo(FMOD_CHANNEL_INFO *info) +{ + info->mRealChannel = mRealChannel[0]; + info->mLevels = mLevels; + info->mSound = 0; + info->mDSP = 0; + + /* + Other, not stored in ChannelI. + */ + getMode (&info->mMode); + getPosition (&info->mPCM, FMOD_TIMEUNIT_PCM); + getLoopPoints (&info->mLoopStart, FMOD_TIMEUNIT_PCM, &info->mLoopEnd, FMOD_TIMEUNIT_PCM); + getCurrentSound (&info->mSound); + if (!info->mSound) + { + getCurrentDSP(&info->mDSP); + } + getLoopCount (&info->mLoopCount); + getMute (&info->mMute); + getPaused (&info->mPaused); + +#ifdef PLATFORM_WII + getLowPassFilter (&info->mLowPassCutoff); + getBiquadFilter (&info->mBiquadActive, &info->mBiquadB0, &info->mBiquadB1, &info->mBiquadB2, &info->mBiquadA1, &info->mBiquadA2); + getControllerSpeaker(&info->mControllerSpeaker); +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setChannelInfo(FMOD_CHANNEL_INFO *info) +{ + int count; + + setMode(info->mMode); + + /* + 2D + */ + setVolume(mVolume); + setFrequency(mFrequency); + + /* + Set speaker levels around + */ + { + if (mLastPanMode == FMOD_CHANNEL_PANMODE_PAN) + { + setPan(mPan, true); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + setSpeakerMix(mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE, mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR, true); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + if (mLevels && mLevels != info->mLevels) + { + mSystem->mSpeakerLevelsPool.free(mLevels); + } + + mLevels = info->mLevels; + if (mLevels) + { + for (count = 0; count < mSystem->mMaxOutputChannels; count++) + { + setSpeakerLevels((FMOD_SPEAKER)count, &mLevels[count * mSystem->mMaxOutputChannels], mSystem->mMaxInputChannels, true); + } + } + } + } + + /* + 3D + */ + + set3DAttributes(&mPosition3D, &mVelocity3D); + + /* + Other, not stored in ChannelI. + */ + setDelay (FMOD_DELAYTYPE_DSPCLOCK_START, mDSPClockDelay.mHi, mDSPClockDelay.mLo); + setPosition (info->mPCM, FMOD_TIMEUNIT_PCM); + setLoopPoints (info->mLoopStart, FMOD_TIMEUNIT_PCM, info->mLoopEnd, FMOD_TIMEUNIT_PCM); + setLoopCount (info->mLoopCount); + setMute (info->mMute); + + for (count = 0; count < FMOD_REVERB_MAXINSTANCES; count++) + { + FMOD_REVERB_CHANNELPROPERTIES prop; + + FMOD_memset(&prop, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + + prop.Flags |= (FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << count); + + if (getReverbProperties(&prop) == FMOD_OK) + { + setReverbProperties(&prop); + } + } + + +#ifdef PLATFORM_WII + setLowPassFilter (info->mLowPassCutoff); + setBiquadFilter (info->mBiquadActive, info->mBiquadB0, info->mBiquadB1, info->mBiquadB2, info->mBiquadA1, info->mBiquadA2); + setControllerSpeaker(info->mControllerSpeaker); +#endif + + if (mAddDSPHead) + { + FMOD::DSPI *dsphead; + FMOD_RESULT result; + + result = getDSPHead(&dsphead); + if (result == FMOD_OK) + { + dsphead->insertInputBetween(mAddDSPHead, 0, true, 0); + } + } + + if (mCallback) + { + bool isvirtual; + + isVirtual(&isvirtual); + + mCallback((FMOD_CHANNEL *)((FMOD_UINT_NATIVE)mHandleCurrent), FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE, isvirtual ? (void *)1 : 0, 0); + } + + update(0); + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getRealChannel(ChannelReal **realchan, int *subchannels) +{ +#ifdef FMOD_SUPPORT_STREAMING + if (mRealChannel[0]->isStream()) + { + ChannelStream *channelstream = SAFE_CAST(ChannelStream, mRealChannel[0]); + + if (realchan) + { + int count; + + for (count = 0; count < channelstream->mNumRealChannels; count++) + { + realchan[count] = channelstream->mRealChannel[count]; + } + } + + if (subchannels) + { + *subchannels = channelstream->mNumRealChannels; + } + } + else +#endif + { + if (realchan) + { + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + realchan[count] = mRealChannel[count]; + } + } + + if (subchannels) + { + *subchannels = mNumRealChannels; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::calcVolumeAndPitchFor3D(int delta) +{ + float pitch3d = 1.0f, vol3d = 1.0f, conevol = 1.0f; + +#if defined(FMOD_SUPPORT_3DSOUND) && !defined(FMOD_STATICFORPLUGINS) + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + if (!(mRealChannel[count]->mMode & FMOD_2D)) + { + FMOD_RESULT result; + int count2, numlisteners; + float smallestdistance; + + result = mSystem->get3DNumListeners(&numlisteners); + if (result != FMOD_OK) + { + return result; + } + + smallestdistance = 999999999.0f; + + for (count2 = 0; count2 < numlisteners; count2++) + { + FMOD_VECTOR currdiff; + float distance; + Listener *listener; + float dopplerscale, distancescale, rolloffscale; + + result = mSystem->getListenerObject(count2, &listener); + if (result != FMOD_OK) + { + return result; + } + + if (!(mFlags & CHANNELI_FLAG_MOVED) && !listener->mMoved) + { + return FMOD_OK; + } + + result = mSystem->get3DSettings(&dopplerscale, &distancescale, &rolloffscale); + if (result != FMOD_OK) + { + return result; + } + + dopplerscale *= m3DDopplerLevel; + + /* + Distance between emitter and listener this frame + */ + if (mRealChannel[count]->mMode & FMOD_3D_HEADRELATIVE) + { + currdiff = mPosition3D; + } + else + { + FMOD_Vector_Subtract(&mPosition3D, &listener->mPosition, &currdiff); + } + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + currdiff.z = -currdiff.z; + } + + distance = (float)FMOD_Vector_GetLengthFast(&currdiff); + + /* + VOLUME ATTENUATION + */ + if (distance < smallestdistance) + { + mDistance = smallestdistance = distance; + + if (mSystem->mRolloffCallback) + { + vol3d = mSystem->mRolloffCallback((FMOD_CHANNEL *)mRealChannel[count]->mParent->mHandleCurrent, mDistance); + } + else if (mRealChannel[count]->mMode & FMOD_3D_CUSTOMROLLOFF) + { + if (mRolloffPoint && mNumRolloffPoints) + { + if (distance >= mRolloffPoint[mNumRolloffPoints - 1].x) + { + vol3d = mRolloffPoint[mNumRolloffPoints - 1].y; + } + else + { + int count2; + + for (count2 = 1; count2 < mNumRolloffPoints; count2++) + { + if (distance >= mRolloffPoint[count2-1].x && distance < mRolloffPoint[count2].x) + { + float frac = (distance - mRolloffPoint[count2-1].x) / (mRolloffPoint[count2].x - mRolloffPoint[count2-1].x); + + vol3d = ((1.0f - frac) * mRolloffPoint[count2-1].y) + (frac * mRolloffPoint[count2].y); + break; + } + } + } + } + else + { + vol3d = 1.0f; + } + } + else + { + if (distance >= mMaxDistance) + { + distance = mMaxDistance; + } + if (distance < mMinDistance) + { + distance = mMinDistance; + } + + if (mRealChannel[count]->mMode & FMOD_3D_LINEARROLLOFF) + { + float range; + + range = mMaxDistance - mMinDistance; + if (range <= 0) + { + vol3d = 1.0f; + } + else + { + distance = mMaxDistance - distance; + vol3d = distance / range; + } + } + else + { + if ((distance > mMinDistance) && (rolloffscale != 1.0f)) + { + distance -= mMinDistance; + distance *= rolloffscale; + distance += mMinDistance; + } + + /* + Rolloffscale is 0 safe but mindistance may be 0 + */ + + if (distance < .000001f) + { + distance = .000001f; + } + + /* + Get the reciprocal. + */ + vol3d = mMinDistance / distance; + } + } + + if (vol3d < 0.0f) + { + vol3d = 0.0f; + } + if (vol3d > 1.0f) + { + vol3d = 1.0f; + } + + /* + Cone calculation. + */ + if (mConeOutsideAngle < 360.0f || mConeInsideAngle < 360.0f) + { + float angle; + + if (mDistance <= 0) + { + angle = 0; + } + else + { + float scale = 1.0f / mDistance; + FMOD_VECTOR coneorientation = mConeOrientation; + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + coneorientation.z = -coneorientation.z; + } + + /* + Normalize + */ + FMOD_Vector_Scale(&currdiff, scale, &currdiff); + + angle = -FMOD_Vector_DotProduct(&currdiff, &coneorientation); + angle = angle < -1.0f ? -1.0f : angle > 1.0f ? 1.0f : angle; /* clamp just in case */ + angle = FMOD_ACOS(angle) / FMOD_PI * 180.0f; + angle *= 2; // cone angles are from side to side (0 to 360), not from side to axis + } + + if (angle < mConeInsideAngle) + { + conevol = 1.0f; + } + else if (angle < mConeOutsideAngle) + { + float f; + f = (angle - mConeInsideAngle) / (mConeOutsideAngle - mConeInsideAngle); + conevol = (mConeOutsideVolume * f) + (1.0f * (1.0f -f)); + } + else + { + conevol = mConeOutsideVolume; + } + } + } + + + /* + DOPPLER EFFECT + */ + #define SPEED_OF_SOUND 340.0f /* same as A3D */ + + if ((dopplerscale > 0) && (numlisteners == 1)) + { + FMOD_VECTOR lastdiff, lastpos, tmp; + float lastdistance; + // if delta is <= 0, set it to 1 second so it has no effect + // also clamp delta to 1 second maximum + float delta_sec = (delta <= 0 || delta > 1000) ? 1.0f : delta / 1000.0f; + + FMOD_Vector_Scale(&mVelocity3D, delta_sec, &tmp); + FMOD_Vector_Subtract(&mPosition3D, &tmp, &lastpos); + + if (mRealChannel[count]->mMode & FMOD_3D_HEADRELATIVE) + { + lastdiff = lastpos; + } + else + { + FMOD_VECTOR listenerlastpos; + + FMOD_Vector_Scale(&listener->mVelocity, delta_sec, &tmp); + FMOD_Vector_Subtract(&listener->mPosition, &tmp, + &listenerlastpos); + + FMOD_Vector_Subtract(&lastpos, &listenerlastpos, &lastdiff); + } + lastdistance = (float)FMOD_Vector_GetLengthFast(&lastdiff); + + pitch3d = SPEED_OF_SOUND * distancescale; + pitch3d -= ((mDistance - lastdistance) / delta_sec * dopplerscale); + pitch3d /= (SPEED_OF_SOUND * distancescale); + } + + if (pitch3d < .000001f) + { + pitch3d = .000001f; + } + } + } + } +#endif + + mVolume3D = vol3d; + mConeVolume3D = conevol; + mPitch3D = pitch3d; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelI::ChannelI() +{ + init(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelI::ChannelI(int index, SystemI *system) +{ + init(); + + mIndex = index; + mSystem = system; + mHandleCurrent = (mSystem->mIndex << SYSTEMID_SHIFT) | ((mIndex << CHANINDEX_SHIFT) & (CHANINDEX_MASK << CHANINDEX_SHIFT)) | (1 << REFCOUNT_SHIFT); + mHandleOriginal = mHandleCurrent; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::init() +{ + int count; + + mSystem = 0; + mHandleCurrent = 0; + mHandleOriginal = 0; + mCallback = 0; + + for (count = 0; count < FMOD_CHANNEL_MAXREALSUBCHANNELS; count++) + { + mRealChannel[count] = 0; + } + for (count = 0; count < FMOD_CHANNEL_MAXINPUTCHANNELS; count++) + { + mInputMix[count] = 1.0f; + } + + mNumRealChannels = 1; + + mPriority = FMOD_CHANNEL_DEFAULTPRIORITY; + mListPosition = (unsigned int)-1; + mIndex = 0; + + /* + 2D Stuff + */ + mVolume = 1.0f; + mFadeVolume = 1.0f; + mFadeTarget = 1.0f; + mFrequency = DEFAULT_FREQUENCY; + mPan = 0.0f; + mSpeakerFL = 1.0f; + mSpeakerFR = 1.0f; + mSpeakerC = 1.0f; + mSpeakerLFE = 1.0f; + mSpeakerBL = 1.0f; + mSpeakerBR = 1.0f; + mSpeakerSL = 1.0f; + mSpeakerSR = 1.0f; + mLevels = 0; + + /* + 3D Stuff + */ + mVolume3D = 1.0f; + mPitch3D = 1.0f; + mConeVolume3D = 1.0f; + mDirectOcclusion = 0.0f; + mReverbDryVolume = 1.0f; + mPosition3D.x = 0; + mPosition3D.y = 0; + mPosition3D.z = 0; + mVelocity3D.x = 0; + mVelocity3D.y = 0; + mVelocity3D.z = 0; + mMinDistance = 1.0f; + mMaxDistance = 1000000000.0f; + mRolloffPoint = 0; + mNumRolloffPoints = 0; + + mFlags = 0; + + mEndDelay = 0; + mDSPClockDelay.mHi = 0; + mDSPClockDelay.mLo = 0; + + mLowPassGain = 1.0f; + + mChannelGroupNode.initNode(); + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getSystemObject(System **system) +{ + if (!system) + { + return FMOD_ERR_INVALID_PARAM; + } + + *system = (System *)mSystem; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::play(SoundI *sound, bool paused, bool reset, bool startmuted) +{ + FMOD_RESULT result; + SoundI *soundi; + + soundi = SAFE_CAST(SoundI, sound); + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + /* + Allow the channel to allocate any needed resources + */ + result = alloc(sound, reset); + if (result != FMOD_OK) + { + return result; + } + + mFlags |= CHANNELI_FLAG_PLAYINGPAUSED; + + /* + Start the channel (paused) + */ + result = setPaused(true); + if (result != FMOD_OK) + { + return result; + } + + /* + Copy defaults from sound to channel. + */ + if (reset) + { + if (startmuted) + { + mFadeVolume = 0.0f; + mFadeTarget = 0.0f; + } + else + { + mFadeVolume = 1.0f; + mFadeTarget = 1.0f; + } + + result = setDefaults(); + if (result != FMOD_OK) + { + return result; + } + + /* + Now start the voice in hardware (if it isnt paused) + */ + result = setPosition(0, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Now start the voice in hardware (if it isnt paused) + */ + result = start(); + if (result != FMOD_OK) + { + return result; + } + + sound->mNumAudible++; + if (sound->mSoundGroup && mSystem) + { + FMOD_OS_CriticalSection_Enter(mSystem->gSoundListCrit); + { + sound->mSoundGroup->removeNode(); + sound->mSoundGroup->addAfter(&mSystem->mSoundGroupUsedHead); + } + FMOD_OS_CriticalSection_Leave(mSystem->gSoundListCrit); + } + + /* + Set the 3d position of the sound to that of the listener by default. + */ + if (reset) + { + #ifdef FMOD_SUPPORT_3DSOUND + { + FMOD_MODE mode; + + sound->getMode(&mode); + if (mode & FMOD_3D) + { + FMOD_VECTOR vel = { 0, 0, 0 }; + + result = set3DAttributes(&mSystem->mListener[0].mPosition, &vel); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + + /* + If the master channelgroup is muted make sure that this channel starts muted. + */ + if (mSystem && mSystem->mChannelGroup->mMute) + { + result = setMute(mFlags & CHANNELI_FLAG_MUTED ? true : false); + if (result != FMOD_OK) + { + return result; + } + } + } + + if (sound->mSyncPointHead && sound->mNumSyncPoints) + { + mSyncPointCurrent = SAFE_CAST(SyncPoint, sound->mSyncPointHead->getNext()); + mSyncPointLastPos = 0; + } + + /* + unpause the sound if the user requested it + */ + if (!paused) + { + result = setPaused(paused); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::play(DSPI *dsp, bool paused, bool reset, bool startmuted) +{ + FMOD_RESULT result; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + /* + Allow the channel to allocate any needed resources + */ + result = alloc(dsp, reset); + if (result != FMOD_OK) + { + return result; + } + + /* + Start the channel (paused) + */ + result = setPaused(true); + if (result != FMOD_OK) + { + return result; + } + + if (startmuted) + { + mFadeVolume = 0.0f; + mFadeTarget = 0.0f; + } + else + { + mFadeVolume = 1.0f; + mFadeTarget = 1.0f; + } + + /* + Copy defaults from sound to channel, and make sure it is paused to start with. + */ + if (reset) + { + result = setDefaults(); + if (result != FMOD_OK) + { + return result; + } + + /* + Now start the voice in hardware (if it isn't paused) + */ + result = setPosition(0, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + + + /* + Now start the voice in hardware (if it isn't paused) + */ + result = start(); + if (result != FMOD_OK) + { + return result; + } + + /* + Set the 3d position of the sound to that of the listener by default. + */ + if (reset) + { + #ifdef FMOD_SUPPORT_3DSOUND + { + FMOD_MODE mode = 0; + + getMode(&mode); + if (mode & FMOD_3D) + { + FMOD_VECTOR vel = { 0, 0, 0 }; + + result = set3DAttributes(&mSystem->mListener[0].mPosition, &vel); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + } + + /* + unpause the sound if the user requested it + */ + if (!paused) + { + result = setPaused(paused); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::alloc(SoundI *sound, bool reset) +{ + FMOD_RESULT result; + int count; + + if (sound->mNumSubSounds && (!sound->mLength +#ifdef FMOD_SUPPORT_SENTENCING + || !sound->mSubSoundList)) +#else + )) +#endif + { + return FMOD_ERR_SUBSOUNDS; + } + + if (reset) + { + mSyncPointCurrent = 0; + + if (mSystem) + { + mChannelGroup = mSystem->mChannelGroup; + mSpeakerMode = mSystem->mSpeakerMode; + } + + /* + 2D stuff + */ + mFlags &= ~CHANNELI_FLAG_REALMUTE; + mFlags &= ~CHANNELI_FLAG_PAUSED; + mFlags &= ~CHANNELI_FLAG_MUTED; + mFlags &= ~CHANNELI_FLAG_FORCEVIRTUAL; + mFlags &= ~CHANNELI_FLAG_USEDPAUSEDELAY; + + for (count = 0; count < FMOD_CHANNEL_MAXINPUTCHANNELS; count++) + { + mInputMix[count] = 1.0f; + } + + mEndDelay = 0; + mDSPClockEnd.mHi = 0; + mDSPClockEnd.mLo = 0; + mDSPClockPause.mHi = 0; + mDSPClockPause.mLo = 0; + + mLowPassGain = 1.0f; + + if (mSystem) + { + mDSPClockDelay.mHi = mSystem->mDSPClock.mHi; + mDSPClockDelay.mLo = mSystem->mDSPClock.mLo; + } + else + { + mDSPClockDelay.mHi = 0; + mDSPClockDelay.mLo = 0; + } + + /* + 3D stuff + */ + mFlags &= ~CHANNELI_FLAG_MOVED; + mVolume3D = 1.0f; + mConeVolume3D = 1.0f; + mPitch3D = 1.0f; + mDirectOcclusion = 0.0f; + mReverbDryVolume = 1.0f; + mMinDistance = sound->mMinDistance; + mMaxDistance = sound->mMaxDistance; + mDistance = 0; + mConeInsideAngle = sound->mConeInsideAngle; + mConeOutsideAngle = sound->mConeOutsideAngle; + mConeOutsideVolume = sound->mConeOutsideVolume; + mConeOrientation.x = 0.0f; + mConeOrientation.y = 0.0f; + mConeOrientation.z = 1.0f; + mRolloffPoint = sound->mRolloffPoint; + mNumRolloffPoints = sound->mNumRolloffPoints; + m3DPanLevel = 1.0f; + m3DDopplerLevel = 1.0f; + + #ifdef FMOD_SUPPORT_REVERB + if (mSystem) + { + int instance; + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + mSystem->mReverbGlobal.resetChanProperties(instance, mIndex); + } + + #ifdef FMOD_SUPPORT_MULTIREVERB + if (mSystem->mReverb3D.mInstance[0].mDSP) + { + mSystem->mReverb3D.resetChanProperties(0, mIndex); + } + + ReverbI *reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); + while (reverb_c != &(mSystem->mReverb3DHead)) + { + if (reverb_c->mInstance[0].mDSP) + { + reverb_c->resetChanProperties(0, mIndex); + } + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()); + } + #endif + + } + #endif + + } + + for (count = 0; count < mNumRealChannels; count++) + { + if (!mRealChannel[count]) + { + return FMOD_ERR_INVALID_HANDLE; + } + +#ifdef FMOD_SUPPORT_STREAMING + if (sound->isStream()) + { + mRealChannel[count]->mSound = sound; + } + else +#endif + { + Sample *sample = SAFE_CAST(Sample, sound); + + mRealChannel[count]->mSound = mNumRealChannels > 1 ? sample->mSubSample[count] : sound; + } + + mRealChannel[count]->mSubChannelIndex = count; + mRealChannel[count]->mDSP = 0; + mRealChannel[count]->mMode = sound->mMode; + mRealChannel[count]->mLoopStart = sound->mLoopStart; + mRealChannel[count]->mLoopLength = sound->mLoopLength; + mRealChannel[count]->mLoopCount = sound->mLoopCount; + mRealChannel[count]->mLength = sound->mLength; + mRealChannel[count]->mParent = this; + mRealChannel[count]->mSubSoundListCurrent = 0; + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_HASPLAYED; + + result = mRealChannel[count]->alloc(); + if (result != FMOD_OK) + { + return result; + } + + sound->mFlags |= FMOD_SOUND_FLAG_PLAYED; + if (sound->mSubSoundParent) + { + sound->mSubSoundParent->mFlags |= FMOD_SOUND_FLAG_PLAYED; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::alloc(DSPI *dsp, bool reset) +{ + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->mSubChannelIndex = count; + mRealChannel[count]->mSound = 0; + mRealChannel[count]->mDSP = dsp; + mRealChannel[count]->mLoopStart = 0; + mRealChannel[count]->mLoopLength = (unsigned int)-1; + mRealChannel[count]->mLoopCount = -1; + mRealChannel[count]->mMode = FMOD_2D | FMOD_SOFTWARE; + mRealChannel[count]->mParent = this; + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_HASPLAYED; + } + + if (reset) + { + /* + 2D stuff + */ + mFlags &= ~CHANNELI_FLAG_REALMUTE; + mFlags &= ~CHANNELI_FLAG_PAUSED; + mFlags &= ~CHANNELI_FLAG_MUTED; + mFlags &= ~CHANNELI_FLAG_FORCEVIRTUAL; + + for (count = 0; count < FMOD_CHANNEL_MAXINPUTCHANNELS; count++) + { + mInputMix[count] = 1.0f; + } + + mEndDelay = 0; + mDSPClockDelay.mHi = mSystem->mDSPClock.mHi; + mDSPClockDelay.mLo = mSystem->mDSPClock.mLo; + + mLowPassGain = 1.0f; + + /* + 3D stuff + */ + mFlags &= ~CHANNELI_FLAG_MOVED; + mVolume3D = 1.0f; + mConeVolume3D = 1.0f; + mPitch3D = 1.0f; + mDirectOcclusion = 0.0f; + mReverbDryVolume = 1.0f; + mMinDistance = 1.0f; // mMinDistance = dsp->mmMinDistance; + mMaxDistance = 10000.0f; // mMaxDistance = dsp->mMaxDistance; + mDistance = 0; + mConeInsideAngle = 360.0f; // mConeInsideAngle = dsp->mConeInsideAngle; + mConeOutsideAngle = 360.0f; // mConeOutsideAngle = dsp->mConeOutsideAngle; + mConeOutsideVolume = 1.0f; // mConeOutsideVolume = dsp->mConeOutsideVolume; + mConeOrientation.x = 0.0f; + mConeOrientation.y = 0.0f; + mConeOrientation.z = 1.0f; + mRolloffPoint = 0; // mRolloffPoint = dsp->mRolloffPoint; + mNumRolloffPoints = 0; // mNumRolloffPoints = dsp->mNumRolloffPoints; + m3DPanLevel = 1.0f; + m3DDopplerLevel = 1.0f; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result; + + result = mRealChannel[count]->alloc(dsp); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::start() +{ + FMOD_RESULT result; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->start(); + if (result != FMOD_OK) + { + return result; + } + + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_STOPPED; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED; + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_PLAYING; + } + + mFlags &= ~CHANNELI_FLAG_ENDDELAY; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Process sync point callbacks. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Currently it scans from the start of the syncpoint list to the end which is a bit inefficient but right now it works + fairly well because it supports even triggering the syncpoints correctly with sounds playing backwards, and complex loops. + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::updateSyncPoints(bool seeking) +{ + FMOD_RESULT result; + SoundI *sound = mRealChannel[0]->mSound; + + if (sound) + { + if (sound->mSubSampleParent) + { + sound = sound->mSubSampleParent; + } + + if (sound->mSyncPointHead && sound->mNumSyncPoints && !mSyncPointCurrent) + { + mSyncPointCurrent = SAFE_CAST(SyncPoint, sound->mSyncPointHead->getNext()); + } + + if (mSyncPointCurrent) + { + unsigned int position; + + result = getPosition(&position, FMOD_TIMEUNIT_PCM); + if (result == FMOD_OK) + { + if (seeking) + { + if (mFrequency > 0) + { + mSyncPointCurrent = SAFE_CAST(SyncPoint, sound->mSyncPointHead->getNext()); + } + else + { + mSyncPointCurrent = SAFE_CAST(SyncPoint, sound->mSyncPointTail->getPrev()); + } + + while ((mFrequency > 0 && position > mSyncPointCurrent->mOffset) || (mFrequency < 0 && position < mSyncPointCurrent->mOffset)) + { + if (mFrequency > 0) + { + if (mSyncPointCurrent->mOffset >= position) + { + break; + } + + mSyncPointCurrent = SAFE_CAST(SyncPoint, mSyncPointCurrent->getNext()); + if (mSyncPointCurrent == sound->mSyncPointTail) + { + break; + } + } + else + { + if (mSyncPointCurrent->mOffset <= position) + { + break; + } + + mSyncPointCurrent= SAFE_CAST(SyncPoint, mSyncPointCurrent->getPrev()); + if (mSyncPointCurrent == sound->mSyncPointHead) + { + break; + } + } + } + } + else + { + bool loop = false; + + if ((mFrequency > 0 && position < mSyncPointLastPos) || + (mFrequency < 0 && position > mSyncPointLastPos)) /* Loop */ + { + loop = true; + } + + while ((mFrequency > 0 && position > mSyncPointCurrent->mOffset) || (mFrequency < 0 && position < mSyncPointCurrent->mOffset) || loop) + { + if (mFrequency > 0) + { + if (mSyncPointCurrent != sound->mSyncPointTail) + { + bool callcallback = true; + SyncPoint *next = SAFE_CAST(SyncPoint, mSyncPointCurrent->getNext()); + + if (sound->mSubSoundShared && mSyncPointCurrent->mSubSoundIndex != sound->mSubSoundIndex) + { + callcallback = false; + } + + if (callcallback && mCallback) + { + mCallback((FMOD_CHANNEL *)((FMOD_UINT_NATIVE)mHandleCurrent), FMOD_CHANNEL_CALLBACKTYPE_SYNCPOINT, (void *)mSyncPointCurrent->mIndex, 0); + } + + mSyncPointCurrent = next; + } + + if (loop && mSyncPointCurrent == sound->mSyncPointTail) + { + mSyncPointCurrent = SAFE_CAST(SyncPoint, sound->mSyncPointHead->getNext()); + loop = false; + } + } + else + { + if (mSyncPointCurrent != sound->mSyncPointHead) + { + bool callcallback = true; + SyncPoint *prev = SAFE_CAST(SyncPoint, mSyncPointCurrent->getPrev()); + + if (sound->mSubSoundShared && mSyncPointCurrent->mSubSoundIndex != sound->mSubSoundIndex || mSyncPointCurrent == sound->mSyncPointHead) + { + callcallback = false; + } + + if (callcallback && mCallback) + { + mCallback((FMOD_CHANNEL *)((FMOD_UINT_NATIVE)mHandleCurrent), FMOD_CHANNEL_CALLBACKTYPE_SYNCPOINT, (void *)mSyncPointCurrent->mIndex, 0); + } + + mSyncPointCurrent = prev; + } + + if (loop && mSyncPointCurrent == sound->mSyncPointHead) + { + mSyncPointCurrent = SAFE_CAST(SyncPoint, sound->mSyncPointTail->getPrev()); + loop = false; + } + } + } + } + } + + mSyncPointLastPos = position; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::update(int delta, bool updategeometrynow) +{ + FMOD_RESULT result; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mFlags & CHANNELI_FLAG_PLAYINGPAUSED) + { + return FMOD_OK; + } + + /* + Geometry / Polygon based occlusion update. + */ +#ifdef FMOD_SUPPORT_GEOMETRY + + bool playingpaused = (mRealChannel[0] && mRealChannel[0]->mFlags & CHANNELREAL_FLAG_PLAYING && mRealChannel[0]->mFlags & CHANNELREAL_FLAG_PAUSED) || updategeometrynow; + + if (mRealChannel[0] && (mRealChannel[0]->mMode & FMOD_3D) && !(mRealChannel[0]->mMode & FMOD_3D_IGNOREGEOMETRY)) + { + float directOcclusionTargetOriginal = mDirectOcclusionTarget; + float reverbOcclusionTargetOriginal = mReverbOcclusionTarget; + bool occlusionupdated = false; + + if (((mFlags & CHANNELI_FLAG_MOVED) || mSystem->mListener[0].mMoved || mSystem->mGeometryMgr.mMoved || playingpaused) && mSystem->mGeometryList) + { + FMOD_VECTOR channelpos; + + /* Convert head relative position to world space */ + if (mRealChannel[0]->mMode & FMOD_3D_HEADRELATIVE) + { + FMOD_Vector_Add(&mPosition3D, &mSystem->mListener[0].mPosition, &channelpos); + } + else + { + FMOD_Vector_Copy(&mPosition3D, &channelpos); + } + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + if (updategeometrynow) +#endif //FMOD_SUPPORT_GEOMETRY_THREADED + { + /* Process geometry in main thread */ + mSystem->mGeometryMgr.lineTestAll(&mSystem->mListener[0].mPosition, &channelpos, &mDirectOcclusionTarget, &mReverbOcclusionTarget); + occlusionupdated = true; + +#ifdef FMOD_SUPPORT_MULTIREVERB + float gain; + + calculate3DReverbGain(&mSystem->mReverb3D, &channelpos, &gain); + mSystem->mReverb3D.setPresenceGain(0, mIndex, gain); +#endif //FMOD_SUPPORT_MULTIREVERB + } +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + else + { + /* Offload geometry processing to backround thread */ + mSystem->mGeometryMgr.mOcclusionThread.enqueue(mIndex, mHandleCurrent, &channelpos); + } +#endif //FMOD_SUPPORT_GEOMETRY_THREADED + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + Find channel's source contribution to each 3D reverb's input gain + Listener contributions are calculated by SystemI + */ + ReverbI* reverb_c; + for (reverb_c = SAFE_CAST(ReverbI, mSystem->mReverb3DHead.getNext()); reverb_c != &mSystem->mReverb3DHead; reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + if (reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + float gain; + + calculate3DReverbGain(reverb_c, &channelpos, &gain); + + // + // Set channel's presence gain relative to this reverb + // + reverb_c->setPresenceGain(0, mIndex, gain); + } + } +#endif //FMOD_SUPPORT_MULTIREVERB + } + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + if (!updategeometrynow) + { + /* Check if the background thread has updated this channels occlusion values */ + OCCLUSION_TASK *task = mSystem->mGeometryMgr.mOcclusionThread.retrieveOcclusion(mIndex); + if (task && task->currenthandle == mHandleCurrent) + { + mDirectOcclusionTarget = task->directocclusion; + mReverbOcclusionTarget = task->reverbocclusion; + +#ifdef FMOD_SUPPORT_MULTIREVERB + mSystem->mReverb3D.setPresenceGain(0, mIndex, task->reverbgain); +#endif + occlusionupdated = true; + } + } +#endif //FMOD_SUPPORT_GEOMETRY_THREADED + + if (mCallback && occlusionupdated) + { + mCallback((FMOD_CHANNEL *)((FMOD_UINT_NATIVE)mHandleCurrent), FMOD_CHANNEL_CALLBACKTYPE_OCCLUSION, &mDirectOcclusionTarget, &mReverbOcclusionTarget); + } + + if ((mDirectOcclusion != mDirectOcclusionTarget || mReverbOcclusion != mReverbOcclusionTarget) && (delta || playingpaused)) + { + // update occlusion + const float DEFAULT_OCCLUSION_MAX_FADE_TIME = 500; + const float OCCLUSION_DURATION_OF_CHANGE = mSystem->mAdvancedSettings.geometryMaxFadeTime ? mSystem->mAdvancedSettings.geometryMaxFadeTime : DEFAULT_OCCLUSION_MAX_FADE_TIME; + const float MIN_OCCLUSION_RATE_OF_CHANGE = 0.0001f; + + if(occlusionupdated) + { + if(directOcclusionTargetOriginal != mDirectOcclusionTarget) + { + mDirectOcclusionRateOfChange = (mDirectOcclusionTarget - mDirectOcclusion) / OCCLUSION_DURATION_OF_CHANGE; + + if(mDirectOcclusionRateOfChange < 0.0f) + { + mDirectOcclusionRateOfChange = mDirectOcclusionRateOfChange > -MIN_OCCLUSION_RATE_OF_CHANGE ? -MIN_OCCLUSION_RATE_OF_CHANGE : mDirectOcclusionRateOfChange; + } + else + { + mDirectOcclusionRateOfChange = mDirectOcclusionRateOfChange < MIN_OCCLUSION_RATE_OF_CHANGE ? MIN_OCCLUSION_RATE_OF_CHANGE : mDirectOcclusionRateOfChange; + } + } + if(reverbOcclusionTargetOriginal != mReverbOcclusionTarget) + { + mReverbOcclusionRateOfChange = (mReverbOcclusionTarget - mReverbOcclusion) / OCCLUSION_DURATION_OF_CHANGE; + + if(mReverbOcclusionRateOfChange < 0.0f) + { + mReverbOcclusionRateOfChange = mReverbOcclusionRateOfChange > -MIN_OCCLUSION_RATE_OF_CHANGE ? -MIN_OCCLUSION_RATE_OF_CHANGE : mReverbOcclusionRateOfChange; + } + else + { + mReverbOcclusionRateOfChange = mReverbOcclusionRateOfChange < MIN_OCCLUSION_RATE_OF_CHANGE ? MIN_OCCLUSION_RATE_OF_CHANGE : mReverbOcclusionRateOfChange; + } + } + } + + if (playingpaused) + { + mDirectOcclusion = mDirectOcclusionTarget; + } + else if (mDirectOcclusion < mDirectOcclusionTarget) + { + mDirectOcclusion += mDirectOcclusionRateOfChange * (float)delta; + if (mDirectOcclusion > mDirectOcclusionTarget) + { + mDirectOcclusion = mDirectOcclusionTarget; + } + } + else if (mDirectOcclusion > mDirectOcclusionTarget) + { + mDirectOcclusion += mDirectOcclusionRateOfChange * (float)delta; + if (mDirectOcclusion < mDirectOcclusionTarget) + { + mDirectOcclusion = mDirectOcclusionTarget; + } + } + + if (playingpaused) + { + mReverbOcclusion = mReverbOcclusionTarget; + } + if (mReverbOcclusion < mReverbOcclusionTarget) + { + mReverbOcclusion += mReverbOcclusionRateOfChange * (float)delta; + if (mReverbOcclusion > mReverbOcclusionTarget) + { + mReverbOcclusion = mReverbOcclusionTarget; + } + } + else if (mReverbOcclusion > mReverbOcclusionTarget) + { + mReverbOcclusion += mReverbOcclusionRateOfChange * (float)delta; + if (mReverbOcclusion < mReverbOcclusionTarget) + { + mReverbOcclusion = mReverbOcclusionTarget; + } + } + + set3DOcclusionInternal(mDirectOcclusion, mReverbOcclusion, false); + } + } + + #endif /* FMOD_SUPPORT_GEOMETRY */ + + if (mEndDelay && mFlags & CHANNELI_FLAG_ENDDELAY) + { + if (mEndDelay <= (unsigned int)delta) + { + mEndDelay = 0; + } + else + { + mEndDelay -= delta; + } + } + + result = calcVolumeAndPitchFor3D(delta); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->set2DFreqVolumePanFor3D(); + if (result != FMOD_OK) + { + return result; + } + } + + result = updateSyncPoints(false); + if (result != FMOD_OK) + { + return result; + } + + /* + Check if the sync point has stopped the channel? + */ + if (mRealChannel[0] && mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED) + { + return FMOD_OK; + } + + /* + Call realchannel's update function if it needs to. + */ + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->update(delta); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Update sorted list position based on audibility. + */ + if ((mFlags & CHANNELI_FLAG_MOVED) || mSystem->mListener[0].mMoved) + { + result = updatePosition(); + if (result != FMOD_OK) + { + return result; + } + + mFlags &= ~CHANNELI_FLAG_MOVED; + } + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_MULTIREVERB +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::calculate3DReverbGain(ReverbI *reverb, FMOD_VECTOR *channelpos, float *gain) +{ + if (!gain) + { + return FMOD_OK; + } + + // Make an initial gain value based on whether the + // channel is 'inside' the reverb's extent + if (reverb == &(mSystem->mReverb3D)) + { + // + // Main 3D reverb extent is infinite + // + *gain = 1.0f; + } + else + { + // + // Other 3D reverb gain based on min/max distance to reverb + // + reverb->calculateDistanceGain(channelpos, gain, 0); + } + +#ifdef FMOD_SUPPORT_GEOMETRY + /* + Reverb occlusion gain of the channel-reverb path + */ + if (*gain > 0.0f) + { + float direct_o, reverb_o; + FMOD_VECTOR reverbpos; + + reverb->get3DAttributes(&reverbpos, 0, 0); + + mSystem->mGeometryMgr.lineTestAll(channelpos, &reverbpos, &direct_o, &reverb_o); + + *gain *= (1.0f - reverb_o); + } +#endif + + return FMOD_OK; +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::updateStream() +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + return mRealChannel[0]->updateStream(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::stop() +{ + return stopEx(CHANNELI_STOPFLAG_REFSTAMP | + CHANNELI_STOPFLAG_UPDATELIST | + CHANNELI_STOPFLAG_RESETCALLBACKS | + CHANNELI_STOPFLAG_CALLENDCALLBACK | + CHANNELI_STOPFLAG_RESETCHANNELGROUP | + CHANNELI_STOPFLAG_UPDATESYNCPOINTS); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::stopEx(FMOD_CHANNEL_STOPFLAG stopflag) +{ + FMOD_RESULT result; + int count; + ChannelReal *oldrealchannel[FMOD_CHANNEL_MAXREALSUBCHANNELS]; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED) + { + return FMOD_OK; /* Already stopped */ + } + + if (stopflag & CHANNELI_STOPFLAG_PROCESSENDDELAY && mEndDelay) + { + mFlags |= CHANNELI_FLAG_ENDDELAY; + return FMOD_OK; + } + + if (stopflag & CHANNELI_STOPFLAG_UPDATESYNCPOINTS) + { + updateSyncPoints(false); + } + + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_IN_USE; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_PLAYING; + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_PAUSED; + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_STOPPED; + } + + if (stopflag & CHANNELI_STOPFLAG_RESETCHANNELGROUP) + { + result = setChannelGroupInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->stop(); + if (result != FMOD_OK) + { + return result; + } + + if (mRealChannel[count]->mSound) + { + mRealChannel[count]->mSound->mNumAudible--; + if (!mRealChannel[count]->mSound->mNumAudible) + { + mRealChannel[count]->mSound->mLastAudibleDSPClock = mSystem ? mSystem->mDSPClock.mValue : 0; + } + } + } + + if (stopflag & CHANNELI_STOPFLAG_UPDATELIST) + { + mEndDelay = 0; + mDSPClockDelay.mHi = 0; + mDSPClockDelay.mLo = 0; + + result = returnToFreeList(); + if (result != FMOD_OK) + { + return result; + } + mListPosition = (unsigned int)-1; + + mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL; + + mAddDSPHead = 0; + } + + mFlags &= ~CHANNELI_FLAG_USEDINPUTMIX; + mFlags &= ~CHANNELI_FLAG_PLAYINGPAUSED; + + for (count = 0; count < mNumRealChannels; count++) + { + oldrealchannel[count] = mRealChannel[count]; + } + + /* + End of sound callback. Warning! All sorts of things could have been called in this by the user! + */ + if (stopflag & CHANNELI_STOPFLAG_CALLENDCALLBACK && mCallback) + { + mCallback((FMOD_CHANNEL *)((FMOD_UINT_NATIVE)mHandleCurrent), FMOD_CHANNEL_CALLBACKTYPE_END, 0, 0); + } + + /* + Real channel may have been re-used, by playsound. Don't clear out it's attributes if it has. + */ + for (count = 0; count < mNumRealChannels; count++) + { + if (!(mRealChannel[count]->mFlags & CHANNELREAL_FLAG_PLAYING) || oldrealchannel[count] != mRealChannel[count]) + { + bool crit = false; + + if (oldrealchannel[count]->mSound && oldrealchannel[count]->mSound->isStream()) + { + crit = true; + } + +#ifdef FMOD_SUPPORT_STREAMING + if (crit) + { + FMOD_OS_CriticalSection_Enter(mSystem->mStreamRealchanCrit); + } +#endif + + oldrealchannel[count]->mSound = 0; + oldrealchannel[count]->mDSP = 0; + oldrealchannel[count]->mParent = 0; + +#ifdef FMOD_SUPPORT_STREAMING + if (crit) + { + FMOD_OS_CriticalSection_Leave(mSystem->mStreamRealchanCrit); + } +#endif + } + } + + /* + This ChannelI may have been used. It had its listposition set to -1 before, so any playsound in the callback would have set it to something else. + */ + if (mListPosition == (unsigned int)-1 && !(mFlags & CHANNELI_FLAG_PLAYINGPAUSED)) + { + for (count = 0; count < mNumRealChannels; count++) + { + mRealChannel[count] = 0; + } + + if (stopflag & CHANNELI_STOPFLAG_RESETCALLBACKS) + { + mCallback = 0; + mSyncPointCurrent = 0; + } + + if (stopflag & CHANNELI_STOPFLAG_REFSTAMP) + { + result = referenceStamp(); + if (result != FMOD_OK) + { + return result; + } + } + } + + if (mLevels) + { + /* + Don't free the speaker levels if they have been acquired by getChannelInfo, + simply disown them by nulling out the pointer. + */ + if (!(stopflag & CHANNELI_STOPFLAG_DONTFREELEVELS)) + { + mSystem->mSpeakerLevelsPool.free(mLevels); + } + mLevels = NULL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setPaused(bool paused) +{ + FMOD_RESULT result = FMOD_OK; + int count; + ChannelGroupI *current; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (paused) + { + mFlags |= CHANNELI_FLAG_PAUSED; + } + else + { + mFlags &= ~CHANNELI_FLAG_PAUSED; + + if (mFlags & CHANNELI_FLAG_PLAYINGPAUSED) + { + FMOD_MODE mode = 0; + + mFlags &= ~CHANNELI_FLAG_PLAYINGPAUSED; + + updatePosition(); + + getMode(&mode); + if (mode & FMOD_3D) + { + update(0, true); + } + } + } + + /* + Scan up through channelgroup heirarchy. If any parent group is paused, then the channel should be paused. + */ + current = mChannelGroup; + do + { + if (current->mPaused) + { + paused = true; + break; + } + current = current->mParent; + } while (current); + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setPaused(paused); + if (result == FMOD_OK) + { + result = result2; + } + + if (paused) + { + mRealChannel[count]->mFlags |= CHANNELREAL_FLAG_PAUSED; + } + else + { + mRealChannel[count]->mFlags &= ~CHANNELREAL_FLAG_PAUSED; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getPaused(bool *paused) +{ + if (!paused) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *paused = mFlags & CHANNELI_FLAG_PAUSED ? true : false; + + /* + Make sure by probing the software channel that setactive hasn't been used from within the mixer. + */ + if (!*paused && mFlags & CHANNELI_FLAG_USEDPAUSEDELAY) + { + FMOD_RESULT result; + + result = mRealChannel[0]->getPaused(paused); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setVolume(float volume, bool forceupdatepos) +{ + FMOD_RESULT result = FMOD_OK; + int count; + bool updatepos = false; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (volume < 0) + { + volume = 0; + } + if (volume > 1.0f) + { + volume = 1.0f; + } + + if (mVolume != volume) + { + updatepos = true; + } + + mVolume = volume; + + if (mFlags & CHANNELI_FLAG_REALMUTE) + { + volume = 0; + } + + if (mNumRealChannels > 1 && mLevels && mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->updateSpeakerLevels(volume); + } + } + else if (mNumRealChannels > 1 && mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setSpeakerMix(mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE, mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR); + } + } + else + { + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setVolume(volume); + } + } + + if (updatepos || forceupdatepos) + { + result = updatePosition(); + if (result != FMOD_OK) + { + return result; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getVolume(float *volume) +{ + if (!volume) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *volume = mVolume; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setFrequency(float frequency) +{ + FMOD_RESULT result = FMOD_OK; + int count; + float oldfrequency = mFrequency; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (frequency < mRealChannel[0]->mMinFrequency) + { + frequency = mRealChannel[0]->mMinFrequency; + } + if (frequency > mRealChannel[0]->mMaxFrequency) + { + frequency = mRealChannel[0]->mMaxFrequency; + } + + mFrequency = frequency; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setFrequency(mFrequency); + if (result == FMOD_OK) + { + result = result2; + } + } + + + if (mSyncPointCurrent && ((oldfrequency < 0 && mFrequency > 0) || (oldfrequency > 0 && mFrequency < 0))) + { + updateSyncPoints(true); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getFrequency(float *frequency) +{ + if (!frequency) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *frequency = mFrequency; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setPan(float pan, bool calldriver) +{ + FMOD_RESULT result = FMOD_OK; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (pan < -1.0f) + { + pan = -1.0f; + } + if (pan > 1.0f) + { + pan = 1.0f; + } + + mPan = pan; + + mLastPanMode = FMOD_CHANNEL_PANMODE_PAN; + + if (mRealChannel[0]->mMode & FMOD_3D) + { + return FMOD_OK; /* Just return. The 3d system will pick up the actual driver call later. */ + } + else if (calldriver) + { + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + if (mNumRealChannels > 1) + { + if (mNumRealChannels == 2 || (mRealChannel[0]->mSound && mRealChannel[0]->mSound->mDefaultChannelMask == SPEAKER_ALLSTEREO)) + { + if (!(count & 1)) + { + pan = -1; + } + else + { + pan = 1; + } + } + } + + result2 = mRealChannel[count]->setPan(pan); + if (result == FMOD_OK) + { + result = result2; + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getPan(float *pan) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->mMode & FMOD_3D) + { + return FMOD_ERR_NEEDS2D; + } + + if (!pan) + { + return FMOD_ERR_INVALID_PARAM; + } + + *pan = mPan; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setDelay(FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (delaytype == FMOD_DELAYTYPE_END_MS) + { + mEndDelay = delayhi; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_START) + { + mDSPClockDelay.mHi = delayhi; + mDSPClockDelay.mLo = delaylo; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setDSPClockDelay(); + if (result == FMOD_OK) + { + result = result2; + } + } + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_END) + { + mDSPClockEnd.mHi = delayhi; + mDSPClockEnd.mLo = delaylo; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setDSPClockDelay(); + if (result == FMOD_OK) + { + result = result2; + } + } + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_PAUSE) + { + mDSPClockPause.mHi = delayhi; + mDSPClockPause.mLo = delaylo; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setDSPClockDelay(); + if (result == FMOD_OK) + { + result = result2; + } + } + + mFlags |= CHANNELI_FLAG_USEDPAUSEDELAY; + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getDelay(FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (delayhi) + { + if (delaytype == FMOD_DELAYTYPE_END_MS) + { + *delayhi = mEndDelay; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_START) + { + *delayhi = mDSPClockDelay.mHi; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_END) + { + *delayhi = mDSPClockEnd.mHi; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_PAUSE) + { + *delayhi = mDSPClockPause.mHi; + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + } + + if (delaylo) + { + if (delaytype == FMOD_DELAYTYPE_END_MS) + { + *delaylo = 0; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_START) + { + *delaylo = mDSPClockDelay.mLo; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_END) + { + *delaylo = mDSPClockEnd.mLo; + } + else if (delaytype == FMOD_DELAYTYPE_DSPCLOCK_PAUSE) + { + *delaylo = mDSPClockPause.mLo; + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright, bool calldriver) +{ + FMOD_RESULT result = FMOD_OK; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + mSpeakerFL = frontleft < 0 ? 0 : frontleft > 5.0f ? 5.0f : frontleft ; + mSpeakerFR = frontright < 0 ? 0 : frontright > 5.0f ? 5.0f : frontright; + mSpeakerC = center < 0 ? 0 : center > 5.0f ? 5.0f : center ; + mSpeakerLFE = lfe < 0 ? 0 : lfe > 5.0f ? 5.0f : lfe; + mSpeakerBL = backleft < 0 ? 0 : backleft > 5.0f ? 5.0f : backleft ; + mSpeakerBR = backright < 0 ? 0 : backright > 5.0f ? 5.0f : backright ; + mSpeakerSL = sideleft < 0 ? 0 : sideleft > 5.0f ? 5.0f : sideleft ; + mSpeakerSR = sideright < 0 ? 0 : sideright > 5.0f ? 5.0f : sideright ; + + mLastPanMode = FMOD_CHANNEL_PANMODE_SPEAKERMIX; + + if (mRealChannel[0]->mMode & FMOD_3D) + { + return FMOD_OK; /* Just return, the LFE value has been set and the 3d system will pick up the actual driver call later. */ + } + else if (calldriver) + { + int count; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setSpeakerMix(mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE, mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR); + if (result == FMOD_OK) + { + result = result2; + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getSpeakerMix(float *frontleft, float *frontright, float *center, float *lfe, float *backleft, float *backright, float *sideleft, float *sideright) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (frontleft) + { + *frontleft = mSpeakerFL; + } + if (frontright) + { + *frontright = mSpeakerFR; + } + if (center) + { + *center = mSpeakerC; + } + if (lfe) + { + *lfe = mSpeakerLFE; + } + if (backleft) + { + *backleft = mSpeakerBL; + } + if (backright) + { + *backright = mSpeakerBR; + } + if (sideleft) + { + *sideleft = mSpeakerSL; + } + if (sideright) + { + *sideright = mSpeakerSR; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setSpeakerLevels(FMOD_SPEAKER speaker, float *levels, int numlevels, bool calldriver) +{ + FMOD_RESULT result = FMOD_OK; + int count; + float actuallevels[DSP_MAXLEVELS_IN]; + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (numlevels > mSystem->mMaxInputChannels) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + if (mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC) + { + if (speaker < 0 || speaker >= 6) + { + return FMOD_ERR_INVALID_PARAM; + } + } + else + { + if (speaker < 0 || speaker >= mSystem->mMaxOutputChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + } + + switch (mSpeakerMode) + { + case FMOD_SPEAKERMODE_MONO: + { + if (speaker != FMOD_SPEAKER_MONO) + { + return FMOD_ERR_INVALID_SPEAKER; + } + break; + } + case FMOD_SPEAKERMODE_STEREO: + { + if (speaker != FMOD_SPEAKER_FRONT_LEFT && speaker != FMOD_SPEAKER_FRONT_RIGHT) + { + return FMOD_ERR_INVALID_SPEAKER; + } + break; + } + case FMOD_SPEAKERMODE_QUAD: + { + if (speaker != FMOD_SPEAKER_FRONT_LEFT && speaker != FMOD_SPEAKER_FRONT_RIGHT && + speaker != FMOD_SPEAKER_BACK_LEFT && speaker != FMOD_SPEAKER_BACK_RIGHT) + { + return FMOD_ERR_INVALID_SPEAKER; + } + + if (speaker == FMOD_SPEAKER_BACK_LEFT) + { + speaker = (FMOD_SPEAKER)2; + } + if (speaker == FMOD_SPEAKER_BACK_RIGHT) + { + speaker = (FMOD_SPEAKER)3; + } + break; + } + case FMOD_SPEAKERMODE_SURROUND: + { + if (speaker != FMOD_SPEAKER_FRONT_LEFT && speaker != FMOD_SPEAKER_FRONT_RIGHT && + speaker != FMOD_SPEAKER_BACK_LEFT && speaker != FMOD_SPEAKER_BACK_RIGHT && + speaker != FMOD_SPEAKER_FRONT_CENTER) + { + return FMOD_ERR_INVALID_SPEAKER; + } + break; + } + case FMOD_SPEAKERMODE_PROLOGIC: + case FMOD_SPEAKERMODE_5POINT1: + { + if (speaker != FMOD_SPEAKER_FRONT_LEFT && speaker != FMOD_SPEAKER_FRONT_RIGHT && + speaker != FMOD_SPEAKER_BACK_LEFT && speaker != FMOD_SPEAKER_BACK_RIGHT && + speaker != FMOD_SPEAKER_FRONT_CENTER && speaker != FMOD_SPEAKER_LOW_FREQUENCY) + { + return FMOD_ERR_INVALID_SPEAKER; + } + break; + } + case FMOD_SPEAKERMODE_7POINT1: + { + if (speaker > FMOD_SPEAKER_SIDE_RIGHT) + { + return FMOD_ERR_INVALID_SPEAKER; + } + break; + } + default: + { + break; + } + }; + + FMOD_memset(actuallevels, 0, DSP_MAXLEVELS_IN * sizeof(float)); + + if (!mLevels) + { + mSystem->mSpeakerLevelsPool.alloc(&mLevels); + if (!mLevels) + { + return FMOD_ERR_MEMORY; + } + } + + for (count = 0; count < numlevels; count++) + { + float volume = levels[count]; + + if (volume < 0) + { + volume = 0; + } + if (volume > 1.0f) + { + volume = 1.0f; + } + + mLevels[(speaker * mSystem->mMaxInputChannels) + count] = volume; + + actuallevels[count] = volume; + } + + mLastPanMode = FMOD_CHANNEL_PANMODE_SPEAKERLEVELS; + + if (mRealChannel[0]->mMode & FMOD_3D) + { + return FMOD_OK; /* Just return. The 3d system will pick up the actual driver call later. */ + } + else if (calldriver) + { + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setSpeakerLevels(speaker, actuallevels, numlevels); + if (result == FMOD_OK) + { + result = result2; + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getSpeakerLevels(FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + int count; + + if (!levels || !numlevels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (speaker < 0 || speaker >= mSystem->mMaxOutputChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mLevels) + { + for (count = 0; count < numlevels; count++) + { + levels[count] = mLevels[(mSystem->mMaxInputChannels * speaker) + count]; + } + } + else + { + for (count = 0; count < numlevels; count++) + { + levels[count] = 0; + } + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setInputChannelMix(float *levels, int numlevels) +{ + int count, diff; + + if (numlevels > FMOD_CHANNEL_MAXINPUTCHANNELS) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + diff = 0; + for (count = 0; count < numlevels; count++) + { + if (mInputMix[count] != levels[count]) + { + diff++; + } + mInputMix[count] = levels[count]; + } + + if (!diff && (mFlags & CHANNELI_FLAG_USEDINPUTMIX)) + { + return FMOD_OK; + } + + mFlags |= CHANNELI_FLAG_USEDINPUTMIX; + + setVolume(mVolume); + + if (mLastPanMode == FMOD_CHANNEL_PANMODE_PAN) + { + setPan(mPan, true); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + setSpeakerMix(mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE, mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR, true); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + int count; + + if (mLevels) + { + for (count = 0; count < mSystem->mMaxOutputChannels; count++) + { + setSpeakerLevels((FMOD_SPEAKER)count, &mLevels[count * mSystem->mMaxOutputChannels], mSystem->mMaxInputChannels, true); + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getInputChannelMix(float *levels, int numlevels) +{ + int count; + + if (numlevels > FMOD_CHANNEL_MAXINPUTCHANNELS) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0; count < numlevels; count++) + { + levels[count] = mInputMix[count]; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setMute(bool mute) +{ + FMOD_RESULT result = FMOD_OK; + ChannelGroupI *current; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mute) + { + mFlags |= CHANNELI_FLAG_MUTED; + } + else + { + mFlags &= ~CHANNELI_FLAG_MUTED; + } + + /* + Scan up through channelgroup heirarchy. If any parent group is muted, then the channel should be muted. + */ + current = mChannelGroup; + do + { + if (current->mMute) + { + mute = true; + break; + } + current = current->mParent; + } while (current); + + if (mute) + { + int count; + + mFlags |= CHANNELI_FLAG_REALMUTE; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setVolume(0); + if (result == FMOD_OK) + { + result = result2; + } + } + + if (result == FMOD_OK) + { + result = updatePosition(); + } + } + else + { + mFlags &= ~CHANNELI_FLAG_REALMUTE; + + result = setVolume(mVolume, true); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getMute(bool *mute) +{ + if (!mute) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFlags & CHANNELI_FLAG_MUTED) + { + *mute = true; + } + else + { + *mute = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setPriority(int priority) +{ + if (priority < 0 || priority > FMOD_MAXPRIORITIES) + { + return FMOD_ERR_INVALID_PARAM; + } + + mPriority = priority; + + return updatePosition(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getPriority(int *priority) +{ + if (!priority) + { + return FMOD_ERR_INVALID_PARAM; + } + + *priority = mPriority; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setLowPassGain(float gain) +{ + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (gain < 0) + { + gain = 0; + } + if (gain > 1.0f) + { + gain = 1.0f; + } + + mLowPassGain = gain; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result = FMOD_OK; + + result = mRealChannel[count]->setLowPassGain(gain); + if (result == FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getLowPassGain(float *gain) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!gain) + { + return FMOD_ERR_INVALID_PARAM; + } + + *gain = mLowPassGain; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DAttributes(const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (pos) + { + #ifdef FMOD_DEBUG + { + FMOD_RESULT result; + + result = FMOD_CHECKFLOAT(pos->x); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(pos->y); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(pos->z); + if (result != FMOD_OK) + { + return result; + } + } + #endif + + if (mPosition3D.x != pos->x || + mPosition3D.y != pos->y || + mPosition3D.z != pos->z) + { + mFlags |= CHANNELI_FLAG_MOVED; + } + mPosition3D = *pos; + } + + if (vel) + { + #ifdef FMOD_DEBUG + { + FMOD_RESULT result; + + result = FMOD_CHECKFLOAT(vel->x); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(vel->y); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(vel->z); + if (result != FMOD_OK) + { + return result; + } + } + #endif + + if (mVelocity3D.x != vel->x || + mVelocity3D.y != vel->y || + mVelocity3D.z != vel->z) + { + mFlags |= CHANNELI_FLAG_MOVED; + } + mVelocity3D = *vel; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_OK; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->set3DAttributes(); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DAttributes(FMOD_VECTOR *pos, FMOD_VECTOR *vel) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (pos) + { + pos->x = mPosition3D.x; + pos->y = mPosition3D.y; + pos->z = mPosition3D.z; + } + + if (vel) + { + vel->x = mVelocity3D.x; + vel->y = mVelocity3D.y; + vel->z = mVelocity3D.z; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DMinMaxDistance(float mindistance, float maxdistance) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + FMOD_MODE mode = mRealChannel[0]->mMode; + + if (!(mode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (mindistance < 0 || maxdistance < 0 || maxdistance < mindistance) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mMinDistance != mindistance || + mMaxDistance != maxdistance) + { + mMinDistance = mindistance; + mMaxDistance = maxdistance; + + if (mode & (FMOD_3D_LOGROLLOFF | FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback) + { + mFlags |= CHANNELI_FLAG_MOVED; + + result = update(0); + if (result != FMOD_OK) + { + return result; + } + + result = setVolume(mVolume); + if (result != FMOD_OK) + { + return result; + } + } + else + { + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->set3DMinMaxDistance(); + if (result == FMOD_OK) + { + result = result2; + } + } + + mFlags |= CHANNELI_FLAG_MOVED; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DMinMaxDistance(float *mindistance, float *maxdistance) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (mindistance) + { + *mindistance = mMinDistance; + } + + if (maxdistance) + { + *maxdistance = mMaxDistance; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DConeSettings(float insideconeangle, float outsideconeangle, float outsidevolume) +{ + FMOD_RESULT result = FMOD_OK; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (insideconeangle > outsideconeangle) + { + return FMOD_ERR_INVALID_PARAM; + } + if (outsidevolume > 1.0f) + { + outsidevolume = 1.0f; + } + if (outsidevolume < 0.0f) + { + outsidevolume = 0.0f; + } + + mConeInsideAngle = insideconeangle; + mConeOutsideAngle = outsideconeangle; + mConeOutsideVolume = outsidevolume; + + mFlags |= CHANNELI_FLAG_MOVED; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DConeSettings(float *insideconeangle, float *outsideconeangle, float *outsidevolume) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (insideconeangle) + { + *insideconeangle = mConeInsideAngle; + } + + if (outsideconeangle) + { + *outsideconeangle = mConeOutsideAngle; + } + + if (outsidevolume) + { + *outsidevolume = mConeOutsideVolume; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DConeOrientation(FMOD_VECTOR *orientation) +{ + FMOD_RESULT result = FMOD_OK; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (!orientation) + { + return FMOD_ERR_INVALID_PARAM; + } + + mConeOrientation = *orientation; + + mFlags |= CHANNELI_FLAG_MOVED; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DConeOrientation(FMOD_VECTOR *orientation) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (orientation) + { + *orientation = mConeOrientation; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DCustomRolloff(FMOD_VECTOR *points, int numpoints) +{ + if (numpoints < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (points) + { + int count; + + for (count = 1; count < numpoints; count++) + { + if (points[count].x <= points[count-1].x) + { + return FMOD_ERR_INVALID_PARAM; + } + if (points[count].y < 0 || points[count].y > 1) + { + return FMOD_ERR_INVALID_PARAM; + } + } + } + + mRolloffPoint = points; + mNumRolloffPoints = numpoints; + + mFlags |= CHANNELI_FLAG_MOVED; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DCustomRolloff(FMOD_VECTOR **points, int *numpoints) +{ + if (points) + { + *points = mRolloffPoint; + } + + if (numpoints) + { + *numpoints = mNumRolloffPoints; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DOcclusionInternal(float direct, float reverb, bool resettarget) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (direct < 0) + { + direct = 0; + } + if (reverb < 0) + { + reverb = 0; + } + if (direct > 1) + { + direct = 1; + } + if (reverb > 1) + { + reverb = 1; + } + + mDirectOcclusion = direct; + mReverbOcclusion = reverb; + + if (resettarget) + { + mDirectOcclusionTarget = mDirectOcclusion; + mReverbOcclusionTarget = mReverbOcclusion; + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->set3DOcclusion(direct, reverb); + } + + result = updatePosition(); + if (result != FMOD_OK) + { + return result; + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DOcclusion(float direct, float reverb) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (direct < 0) + { + direct = 0; + } + if (reverb < 0) + { + reverb = 0; + } + if (direct > 1) + { + direct = 1; + } + if (reverb > 1) + { + reverb = 1; + } + + mUserDirectOcclusion = direct; + mUserReverbOcclusion = reverb; + + // recalculate real channel occlusion values + CHECK_RESULT(set3DOcclusionInternal(mDirectOcclusion, mReverbOcclusion, false)); + + return FMOD_OK; +} + + +FMOD_RESULT ChannelI::get3DOcclusionInternal(float *directOcclusion, float *reverbOcclusion) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (directOcclusion) + { + *directOcclusion = mDirectOcclusion; + } + if (reverbOcclusion) + { + *reverbOcclusion = mReverbOcclusion; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DOcclusion(float *directOcclusion, float *reverbOcclusion) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (directOcclusion) + { + *directOcclusion = mUserDirectOcclusion; + } + if (reverbOcclusion) + { + *reverbOcclusion = mUserReverbOcclusion; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DSpread(float angle) +{ + FMOD_RESULT result = FMOD_OK; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (angle < 0.0f || angle > 360.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + mSpread = angle; + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DSpread(float *angle) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (!angle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *angle = mSpread; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DPanLevel(float level) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (level < 0.0f || level > 1.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + m3DPanLevel = level; + + if (mRealChannel[0]->mFlags & CHANNELREAL_FLAG_PAUSED && m3DPanLevel < 1.0f) + { + return update(0); /* If there is a 2d component and we are still in play paused mode, update the speaker levels from what was set in the initial play. */ + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DPanLevel(float *level) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (!level) + { + return FMOD_ERR_INVALID_PARAM; + } + + *level = m3DPanLevel; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::set3DDopplerLevel(float level) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (level < 0.0f || level > 5.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + m3DDopplerLevel = level; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::get3DDopplerLevel(float *level) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + return FMOD_ERR_NEEDS3D; + } + + if (!level) + { + return FMOD_ERR_INVALID_PARAM; + } + + *level = m3DDopplerLevel; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setReverbProperties(prop); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->getReverbProperties(prop); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setChannelGroupInternal(ChannelGroupI *channelgroup, bool resetattributes, bool forcedspreconnect) +{ + FMOD_RESULT result = FMOD_OK; + ChannelGroupI *oldchannelgroup = mChannelGroup; + int count; + + if (mChannelGroup) + { + if (mChannelGroupNode.isEmpty()) + { + return FMOD_OK; /* Possibly doesn't belong to a channel group such as mod/s3m/xm/it/midi and shouldnt be returned to the master list. */ + } + + mChannelGroup->mNumChannels--; + mChannelGroupNode.removeNode(); + } + + if (!channelgroup) + { + channelgroup = mSystem->mChannelGroup; + } + + mChannelGroup = channelgroup; + mChannelGroupNode.addAfter(&mChannelGroup->mChannelHead); + mChannelGroupNode.setData(this); + mChannelGroup->mNumChannels++; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + /* + Fix up DSP connection inconsistancies, like volume/freq/pan etc for connections. + */ + if (resetattributes) + { + float levels[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN]; + + if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + for (count = 0; count < mSystem->mMaxOutputChannels; count++) + { + getSpeakerLevels((FMOD_SPEAKER)count, levels[count], mSystem->mMaxInputChannels); + } + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->moveChannelGroup(oldchannelgroup, mChannelGroup, forcedspreconnect); + if (result == FMOD_OK) + { + result = result2; + } + } + + /* + If channelgroup has overriden mute/paused = true, then mute this voice, otherwise leave it at whatever the channel mute setting was set to. + */ + result = setMute(mFlags & CHANNELI_FLAG_MUTED ? true : false); + if (result != FMOD_OK) + { + return result; + } + + result = setPaused(mFlags & CHANNELI_FLAG_PAUSED ? true : false); + if (result != FMOD_OK) + { + return result; + } + + /* + Because the connection has been terminated, with the pan and volume in the old connection, + we have to reset the pan and volume in the new connection. + */ + setVolume(mVolume); + + if (!(mRealChannel[0]->mMode & FMOD_3D)) + { + if (mLastPanMode == FMOD_CHANNEL_PANMODE_PAN) + { + setPan(mPan); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + setSpeakerMix(mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE, mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + for (count = 0; count < mSystem->mMaxOutputChannels; count++) + { + setSpeakerLevels((FMOD_SPEAKER)count, levels[count], mSystem->mMaxInputChannels); + } + } + } + + setFrequency(mFrequency); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setChannelGroup(ChannelGroupI *channelgroup) +{ + return setChannelGroupInternal(channelgroup, true); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getChannelGroup(ChannelGroupI **channelgroup) +{ + if (!channelgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + + *channelgroup = mChannelGroup; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::isPlaying(bool *isplaying) +{ + FMOD_RESULT result; + int count; + + if (!isplaying) + { + return FMOD_ERR_INVALID_PARAM; + } + + *isplaying = false; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED) + { + *isplaying = false; + return FMOD_OK; + } + + for (count = 0; count < mNumRealChannels; count++) + { + bool isplayingtemp; + + result = mRealChannel[count]->isPlaying(&isplayingtemp); + if (result != FMOD_OK) + { + return result; + } + + if (isplayingtemp) /* If any are playing, final isplaying = true. Dont let the last channel dictate if the whole voice is on or not. */ + { + *isplaying = true; + break; + } + } + + if (!*isplaying) + { + if (mEndDelay) + { + mFlags |= CHANNELI_FLAG_ENDDELAY; + *isplaying = true; + return FMOD_OK; + } + } + + + /* + A bit of a helper, isplaying can force the sound to the end of the list if the user happens to call it. + */ + if (!*isplaying) + { + mListPosition = (unsigned int)-1; + + if (mSortedListNode.getData()) /* Might not be part of a list. */ + { + mSortedListNode.removeNode(); + mSortedListNode.addBefore(&mSystem->mChannelSortedListHead); + mSortedListNode.setData(this); + } + if (mSoundGroupSortedListNode.getData()) /* Might not be part of a list. */ + { + SoundI *sound = mRealChannel[0]->mSound; + + if (sound) + { + mSoundGroupSortedListNode.removeNode(); + if( sound->mSoundGroup ) + mSoundGroupSortedListNode.addBefore(&sound->mSoundGroup->mChannelListHead); + mSoundGroupSortedListNode.setData(this); + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::isVirtual(bool *isvirtual) +{ + if (!isvirtual) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + *isvirtual = false; + return FMOD_ERR_INVALID_HANDLE; + } + + return mRealChannel[0]->isVirtual(isvirtual); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getAudibilityInternal(float *audibility, bool usefadevolume) +{ + if (!audibility) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mFlags & CHANNELI_FLAG_MUTED) + { + *audibility = 0; + return FMOD_OK; + } + + if (mRealChannel[0]->mMode & FMOD_3D) + { + if (m3DPanLevel < 1.0f) + { + /* Sound is partially 2D */ + float non3Dportion = 1.0f - m3DPanLevel; + *audibility = mVolume * + (non3Dportion + m3DPanLevel * mVolume3D) * + (non3Dportion + m3DPanLevel * mConeVolume3D) * + (non3Dportion + m3DPanLevel * (1.0f - mDirectOcclusion)) * + (non3Dportion + m3DPanLevel * (1.0f - mUserDirectOcclusion)) * + (usefadevolume ? mFadeVolume : 1.0f) * + (non3Dportion + m3DPanLevel * mChannelGroup->mRealDirectOcclusionVolume) * + mReverbDryVolume * + mChannelGroup->mRealVolume; + } + else + { + *audibility = mVolume * + mVolume3D * + mConeVolume3D * + (1.0f - mDirectOcclusion) * + (1.0f - mUserDirectOcclusion) * + (usefadevolume ? mFadeVolume : 1.0f) * + mChannelGroup->mRealDirectOcclusionVolume * + mReverbDryVolume * + mChannelGroup->mRealVolume; + } + } + else + { + *audibility = mVolume * mChannelGroup->mRealVolume * (usefadevolume ? mFadeVolume : 1.0f); + } + + return FMOD_OK; +} + + +FMOD_RESULT F_API ChannelI::getFinalFrequency(float *frequency) +{ + *frequency = mFrequency * + mChannelGroup->mRealPitch * + ((1.0f - m3DPanLevel) + (mPitch3D * m3DPanLevel)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getAudibility(float *audibility) +{ + return getAudibilityInternal(audibility, true); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getCurrentSound(SoundI **sound) +{ + if (!sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + *sound = 0; + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->mSound) + { + *sound = mRealChannel[0]->mSound->mSubSampleParent; + } + else + { + *sound = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getCurrentDSP(DSPI **dsp) +{ + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + *dsp = 0; + return FMOD_ERR_INVALID_HANDLE; + } + + *dsp = mRealChannel[0]->mDSP; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getSpectrum(float *spectrumarray, int numvalues, int channel, FMOD_DSP_FFT_WINDOW windowtype) +{ +#ifdef FMOD_SUPPORT_GETSPECTRUM + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mNumRealChannels > 1) + { + if (channel >= mNumRealChannels || channel < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + return mRealChannel[channel]->getSpectrum(spectrumarray, numvalues, 0, windowtype); + } + else + { + return mRealChannel[0]->getSpectrum(spectrumarray, numvalues, channel, windowtype); + } +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getWaveData(float *wavearray, int numvalues, int channel) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mNumRealChannels > 1) + { + if (channel >= mNumRealChannels || channel < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + return mRealChannel[channel]->getWaveData(wavearray, numvalues, 0); + } + else + { + return mRealChannel[0]->getWaveData(wavearray, numvalues, channel); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getIndex(int *index) +{ + if (!index) + { + return FMOD_ERR_INVALID_PARAM; + } + + *index = mIndex; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setCallback(FMOD_CHANNEL_CALLBACK callback) +{ + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + mCallback = callback; + +#if 0 + if (type == FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE) + { + FMOD_RESULT result; + bool isvirtual = false; + + result = isVirtual(&isvirtual); + if (result != FMOD_OK) + { + return result; + } + + mCallback((FMOD_CHANNEL *)((FMOD_UINT_NATIVE)mHandleCurrent), type, isvirtual ? 1 : 0, 0); + } +#endif + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->mSound) + { + SoundI *soundi = mRealChannel[0]->mSound->mSubSampleParent; + unsigned int length; + +#ifdef FMOD_SUPPORT_SENTENCING + if (postype == FMOD_TIMEUNIT_SENTENCE_MS || + postype == FMOD_TIMEUNIT_SENTENCE_PCM || + postype == FMOD_TIMEUNIT_SENTENCE_PCMBYTES || + postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + unsigned int sentenceid, subsound; + + if (!soundi->mSubSoundList) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (postype == FMOD_TIMEUNIT_SENTENCE_SUBSOUND) + { + if (position >= (unsigned int)soundi->mSubSoundListNum) + { + return FMOD_ERR_INVALID_POSITION; + } + + sentenceid = position; + postype = FMOD_TIMEUNIT_PCM; + position = 0; + } + else + { + result = getPosition(&sentenceid, FMOD_TIMEUNIT_SENTENCE); + if (result != FMOD_OK) + { + return result; + } + } + + subsound = soundi->mSubSoundList[sentenceid].mIndex; + + if (postype == FMOD_TIMEUNIT_SENTENCE_MS) + { + postype = FMOD_TIMEUNIT_MS; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCM) + { + postype = FMOD_TIMEUNIT_PCM; + } + else if (postype == FMOD_TIMEUNIT_SENTENCE_PCMBYTES) + { + postype = FMOD_TIMEUNIT_PCMBYTES; + } + + + if (soundi->mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + soundi->mCodec->mDescription.getwaveformat(soundi->mCodec, subsound, &waveformat); + + length = waveformat.lengthpcm; + + if (postype == FMOD_TIMEUNIT_MS) + { + position = (unsigned int)((float)position / 1000.0f * waveformat.frequency); + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(position, &position, waveformat.channels, waveformat.format); + } + + postype = FMOD_TIMEUNIT_PCM; + } + else + { + result = soundi->mSubSound[subsound]->getLength(&length, postype); + if (result != FMOD_OK) + { + return result; + } + } + + if (position >= length) + { + return FMOD_ERR_INVALID_POSITION; + } + + { + unsigned int count; + + for (count = 0; count < sentenceid; count++) + { + if (soundi->mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + soundi->mCodec->mDescription.getwaveformat(soundi->mCodec, soundi->mSubSoundList[count].mIndex, &waveformat); + + length = waveformat.lengthpcm; + } + else + { + SoundI *sound = soundi->mSubSound[soundi->mSubSoundList[count].mIndex]; + + sound->getLength(&length, postype); + } + + position += length; + } + } + } + else +#endif + { + result = soundi->getLength(&length, postype); + if (result != FMOD_OK) + { + return result; + } + + if (position >= length) + { + return FMOD_ERR_INVALID_POSITION; + } + } + + /* + If a sentence, recalculate the sentence current value in the setposition. + */ +#ifdef FMOD_SUPPORT_SENTENCING + if (soundi->mSubSound && soundi->mSubSoundList) + { + int count; + unsigned int offset = 0, pos = position; + + if (postype == FMOD_TIMEUNIT_MS) + { + pos = (unsigned int)((float)position / 1000.0f * soundi->mDefaultFrequency); + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + soundi->getSamplesFromBytes(pos, &pos); + } + + + for (count = 0; count < soundi->mSubSoundListNum; count++) + { + SoundI *subsound = soundi->mSubSound[soundi->mSubSoundList[count].mIndex]; + + if (subsound) + { + FMOD_RESULT result; + unsigned int lengthpcm; + + if (soundi->mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + result = soundi->mCodec->mDescription.getwaveformat(soundi->mCodec, soundi->mSubSoundList[count].mIndex, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + lengthpcm = waveformat.lengthpcm; + } + else + { + lengthpcm = subsound->mLength; + } + + if (pos >= offset && pos < offset + lengthpcm) + { + int count2; + + for (count2 = 0; count2 < mNumRealChannels; count2++) + { + mRealChannel[count2]->mSubSoundListCurrent = count; + } + break; + } + + offset += lengthpcm; + } + } + } +#endif + } + + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setPosition(position, postype); + if (result2 != FMOD_OK && result2 != FMOD_ERR_INVALID_POSITION) + { + return result2; + } + } + + result = updateSyncPoints(true); + if (result != FMOD_OK) + { + return result; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + return mRealChannel[0]->getPosition(position, postype); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getDSPHead(DSPI **dsp) +{ + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + return mRealChannel[0]->getDSPHead(dsp); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::addDSP(DSPI *dsp, DSPConnectionI **dspconnection) +{ + FMOD_RESULT result; + DSPI *dsphead; + + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + #ifdef PLATFORM_PS3 + if (mRealChannel[0]->mSound && mRealChannel[0]->mSound->mChannels > 8) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "ChannelI::addDSP", "Cannot addDSP to channel with more than 8 channels. Please use a Channel Group.\n")); + return FMOD_ERR_TOOMANYCHANNELS; + } + #endif + + result = getDSPHead(&dsphead); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->insertInputBetween(dsp, 0, false, dspconnection); + if (result != FMOD_OK) + { + return result; + } + + mAddDSPHead = dsp; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + int count; + FMOD_MODE oldmode; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + oldmode = mRealChannel[0]->mMode; + + for (count = 0; count < mNumRealChannels; count++) + { + result = mRealChannel[count]->setMode(mode); + if (result != FMOD_OK) + { + return result; + } + } + + if (mRealChannel[0]->mMode & FMOD_SOFTWARE) + { + if (!(oldmode & FMOD_2D) && mode & FMOD_2D) /* Reset the pan/speaker level settings. */ + { + unsigned int channelmask = 0; + int channels = 0; + + result = setVolume(mVolume); + if (result != FMOD_OK) + { + return result; + } + + if (mRealChannel[0]->mSound) + { + SoundI *soundi = SAFE_CAST(SoundI, mRealChannel[0]->mSound); + + channelmask = soundi->mDefaultChannelMask; + channels = soundi->mChannels; + } + + if (channelmask & SPEAKER_WAVEFORMAT_MASK) + { + int speaker, incount; + unsigned int maskbit = 1; + + incount = 0; + for (speaker = 0; speaker < mSystem->mMaxOutputChannels; speaker++) + { + if (channelmask & maskbit) + { + float clevels[DSP_MAXLEVELS_IN]; + + FMOD_memset(clevels, 0, DSP_MAXLEVELS_IN * sizeof(float)); + + clevels[incount] = 1.0f; + + setSpeakerLevels((FMOD_SPEAKER)speaker, clevels, channels); + + incount++; + if (incount >= channels) + { + break; + } + } + + maskbit <<= 1; + } + } + else + { + if (mLastPanMode == FMOD_CHANNEL_PANMODE_PAN) + { + setPan(mPan, true); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERMIX) + { + setSpeakerMix(mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE, mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR, true); + } + else if (mLastPanMode == FMOD_CHANNEL_PANMODE_SPEAKERLEVELS) + { + if (mLevels) + { + for (count = 0; count < mSystem->mMaxOutputChannels; count++) + { + setSpeakerLevels((FMOD_SPEAKER)count, &mLevels[count * mSystem->mMaxOutputChannels], mSystem->mMaxInputChannels, true); + } + } + } + } + } + else if (!(oldmode & FMOD_3D) && mode & FMOD_3D) + { + FMOD_VECTOR pos = mPosition3D; + + mPosition3D.x += 1.0f; /* Make it different so that it updates. */ + + result = set3DAttributes(&pos, &mVelocity3D); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + if ((mode & FMOD_3D) && (mode & (FMOD_3D_LOGROLLOFF | FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback)) + { + result = set3DAttributes(&mPosition3D, &mVelocity3D); + if (result != FMOD_OK) + { + return result; + } + + mFlags |= CHANNELI_FLAG_MOVED; + + result = update(0); + if (result != FMOD_OK) + { + return result; + } + + result = setVolume(mVolume); + if (result != FMOD_OK) + { + return result; + } + } + } + + if ((mode & FMOD_3D_IGNOREGEOMETRY) != (oldmode & FMOD_3D_IGNOREGEOMETRY)) + { + if( mode & FMOD_3D_IGNOREGEOMETRY ) + { + set3DOcclusionInternal(mUserDirectOcclusion, mUserReverbOcclusion); + } + else + { + mFlags |= CHANNELI_FLAG_MOVED; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getMode(FMOD_MODE *mode) +{ + if (!mode) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *mode = mRealChannel[0]->mMode; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setLoopCount(int loopcount) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (loopcount < -1) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setLoopCount(loopcount); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getLoopCount(int *loopcount) +{ + if (!loopcount) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *loopcount = mRealChannel[0]->mLoopCount; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setLoopPoints(unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int loopstartpcm = 0, loopendpcm = 0, looplengthpcm = 0; + int count; + SoundI *soundi; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if ((loopstarttype != FMOD_TIMEUNIT_MS && loopstarttype != FMOD_TIMEUNIT_PCM && loopstarttype != FMOD_TIMEUNIT_PCMBYTES) || + (loopendtype != FMOD_TIMEUNIT_MS && loopendtype != FMOD_TIMEUNIT_PCM && loopendtype != FMOD_TIMEUNIT_PCMBYTES)) + { + return FMOD_ERR_FORMAT; + } + + soundi = SAFE_CAST(SoundI, mRealChannel[0]->mSound); + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + soundi = soundi->mSubSampleParent; + + if (loopstarttype == FMOD_TIMEUNIT_PCM) + { + loopstartpcm = loopstart; + } + else if (loopstarttype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(loopstart, &loopstartpcm, soundi->mChannels, soundi->mFormat); + } + else if (loopstarttype == FMOD_TIMEUNIT_MS) + { + loopstartpcm = (unsigned int)((float)loopstart / 1000.0f * soundi->mDefaultFrequency); + } + + if (loopendtype == FMOD_TIMEUNIT_PCM) + { + loopendpcm = loopend; + } + else if (loopendtype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(loopend, &loopendpcm, soundi->mChannels, soundi->mFormat); + } + else if (loopendtype == FMOD_TIMEUNIT_MS) + { + loopendpcm = (unsigned int)((float)loopend / 1000.0f * soundi->mDefaultFrequency); + } + + if (loopstartpcm >= loopendpcm) + { + return FMOD_ERR_INVALID_PARAM; + } + + looplengthpcm = loopendpcm - loopstartpcm + 1; + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setLoopPoints(loopstartpcm, looplengthpcm); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getLoopPoints(unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) +{ + SoundI *soundi; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if ((loopstarttype != FMOD_TIMEUNIT_MS && loopstarttype != FMOD_TIMEUNIT_PCM && loopstarttype != FMOD_TIMEUNIT_PCMBYTES) || + (loopendtype != FMOD_TIMEUNIT_MS && loopendtype != FMOD_TIMEUNIT_PCM && loopendtype != FMOD_TIMEUNIT_PCMBYTES)) + { + return FMOD_ERR_FORMAT; + } + + soundi = SAFE_CAST(SoundI, mRealChannel[0]->mSound); + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + soundi = soundi->mSubSampleParent; + + if (loopstart) + { + if (loopstarttype == FMOD_TIMEUNIT_PCM) + { + *loopstart = mRealChannel[0]->mLoopStart; + } + else if (loopstarttype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(mRealChannel[0]->mLoopStart, loopstart, soundi->mChannels, soundi->mFormat); + } + else if (loopstarttype == FMOD_TIMEUNIT_MS) + { + *loopstart = (unsigned int)((float)mRealChannel[0]->mLoopStart * 1000.0f / soundi->mDefaultFrequency); + } + } + + if (loopend) + { + unsigned int lend = mRealChannel[0]->mLoopStart + mRealChannel[0]->mLoopLength - 1; + + if (loopendtype == FMOD_TIMEUNIT_PCM) + { + *loopend = lend; + } + else if (loopendtype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(lend, loopend, soundi->mChannels, soundi->mFormat); + } + else if (loopendtype == FMOD_TIMEUNIT_MS) + { + *loopend = (unsigned int)((float)lend * 1000.0f / soundi->mDefaultFrequency); + } + } + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + + +#ifdef PLATFORM_WII +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setLowPassFilter(int cutoff) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setLowPassFilter(cutoff); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getLowPassFilter(int *cutoff) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->getLowPassFilter(cutoff); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setBiquadFilter(bool active, unsigned short b0, unsigned short b1, unsigned short b2, unsigned short a1, unsigned short a2) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->setBiquadFilter(active, b0, b1, b2, a1, a2); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getBiquadFilter(bool *active, unsigned short *b0, unsigned short *b1, unsigned short *b2, unsigned short *a1, unsigned short *a2) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->getBiquadFilter(active, b0, b1, b2, a1, a2); + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::setControllerSpeaker(unsigned int controller, int subchannel) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (mRealChannel[0]->isStream()) + { + result = mRealChannel[0]->setControllerSpeaker(controller, subchannel); + } + else + { + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + if (subchannel == count || subchannel < 0) + { + result2 = mRealChannel[count]->setControllerSpeaker(controller, subchannel); + if (result == FMOD_OK) + { + result = result2; + } + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getControllerSpeaker(unsigned int *controller) +{ + FMOD_RESULT result = FMOD_OK; + int count; + + if (!mRealChannel[0]) + { + return FMOD_ERR_INVALID_HANDLE; + } + + for (count = 0; count < mNumRealChannels; count++) + { + FMOD_RESULT result2; + + result2 = mRealChannel[count]->getControllerSpeaker(controller); + + if (result == FMOD_OK) + { + result = result2; + } + } + + return result; +} +#endif + + +/* +[API] +[ + [DESCRIPTION] + Callback for channel events. + + [PARAMETERS] + 'channel' Pointer to a channel handle. + 'type' The type of callback. Refer to FMOD_CHANNEL_CALLBACKTYPE. + 'commanddata1' The first callback type specific data generated by the callback. See remarks for meaning. + 'commanddata2' The second callback type specific data generated by the callback. See remarks for meaning. + + [RETURN_VALUE] + + [REMARKS] + C++ Users. Cast FMOD_CHANNEL * to FMOD::Channel * inside the callback and use as normal.
    +
    + 'commanddata1' and 'commanddata2' meanings.
    + These 2 values are set by the callback depending on what is happening in the callback and the type of callback.
    +
  • FMOD_CHANNEL_CALLBACKTYPE_END
    + commanddata1: Always 0.
    + commanddata2: Always 0.
    +
  • FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE
    + commanddata1: (cast to int) 0 when voice is swapped from emulated to real. 1 when voice is swapped from real to emulated.
    + commanddata2: Always 0.
    +
  • FMOD_CHANNEL_CALLBACKTYPE_SYNCPOINT
    + commanddata1: (cast to int) The index of the sync point. Use Sound::getSyncPointInfo to retrieve the sync point's attributes.
    + commanddata2: Always 0.
    +
  • FMOD_CHANNEL_CALLBACKTYPE_OCCLUSION
    + commanddata1: (cast to float *) pointer to a floating point direct value that can be read (dereferenced) and modified after the geometry engine has calculated it for this channel.
    + commanddata2: (cast to float *) pointer to a floating point reverb value that can be read (dereferenced) and modified after the geometry engine has calculated it for this channel.
    +
    + Note! Currently the user must call System::update for these callbacks to trigger! + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setCallback + FMOD_CHANNEL_CALLBACKTYPE + System::update +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CHANNEL_CALLBACK(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Callback for system wide 3d channel volume calculation which overrides fmod's internal calculation code. + + [PARAMETERS] + 'channel' Pointer to a channel handle. + 'distance' Distance in units (meters by default). + + [RETURN_VALUE] + + [REMARKS] + C++ Users. Cast FMOD_CHANNEL * to FMOD::Channel * inside the callback and use as normal.

    + NOTE: When using the event system, call Channel::getUserData to get the event instance handle of the event that spawned the channel in question. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::set3DRolloffCallback + System::set3DListenerAttributes + System::get3DListenerAttributes + Channel::getUserData +] +*/ +/* +float F_CALLBACK FMOD_3D_ROLLOFFCALLBACK(FMOD_CHANNEL *channel, float distance) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT ChannelI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(*this)); + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_channeli.h b/src/fmod_channeli.h new file mode 100755 index 0000000..3315f6f --- /dev/null +++ b/src/fmod_channeli.h @@ -0,0 +1,322 @@ +#ifndef _FMOD_CHANNELI_H +#define _FMOD_CHANNELI_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_linkedlist.h" +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + + const unsigned int SYSTEMID_BITS = 4; + const unsigned int CHANINDEX_BITS = 12; + const unsigned int REFCOUNT_BITS = 16; + const unsigned int SYSTEMID_SHIFT = REFCOUNT_BITS + CHANINDEX_BITS; + const unsigned int CHANINDEX_SHIFT = REFCOUNT_BITS; + const unsigned int REFCOUNT_SHIFT = 0; + const unsigned int SYSTEMID_MASK = (unsigned int)0xffffffff >> (32 - SYSTEMID_BITS); + const unsigned int CHANINDEX_MASK = (unsigned int)0xffffffff >> (32 - CHANINDEX_BITS); + const unsigned int REFCOUNT_MASK = (unsigned int)0xffffffff >> (32 - REFCOUNT_BITS); + + const unsigned int FMOD_CHANNEL_DEFAULTPRIORITY = 128; + + const int FMOD_CHANNEL_MAXINPUTCHANNELS = 16; + + #if defined(PLATFORM_PS3) || defined(PLATFORM_XENON) || defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + const int FMOD_CHANNEL_MAXREALSUBCHANNELS = 1; /* We know these 2 are definitely software only and the mixer supports 16ch. */ + #else + const int FMOD_CHANNEL_MAXREALSUBCHANNELS = FMOD_CHANNEL_MAXINPUTCHANNELS; + #endif + + typedef enum + { + FMOD_CHANNEL_PANMODE_PAN, + FMOD_CHANNEL_PANMODE_SPEAKERMIX, + FMOD_CHANNEL_PANMODE_SPEAKERLEVELS, + } FMOD_CHANNEL_PANMODE; + + class ChannelGroupI; + class ChannelReal; + class DSPI; + class DSPConnectionI; + class ReverbI; + class SoundI; + class Stream; + class SyncPoint; + class SystemI; + class OcclusionProcessor; + + typedef unsigned int FMOD_CHANNEL_FLAG; + typedef unsigned int FMOD_CHANNEL_STOPFLAG; + + #define CHANNELI_FLAG_PAUSED 0x00000001 + #define CHANNELI_FLAG_MUTED 0x00000002 + #define CHANNELI_FLAG_JUSTWENTVIRTUAL 0x00000004 + #define CHANNELI_FLAG_REALMUTE 0x00000008 + #define CHANNELI_FLAG_MOVED 0x00000010 + #define CHANNELI_FLAG_FORCEVIRTUAL 0x00000040 + #define CHANNELI_FLAG_USEDINPUTMIX 0x00000080 + #define CHANNELI_FLAG_PLAYINGPAUSED 0x00000100 + #define CHANNELI_FLAG_USEDPAUSEDELAY 0x00000200 + #define CHANNELI_FLAG_ENDDELAY 0x00000400 + #define CHANNELI_FLAG_MUSICOWNED 0x00000800 + + #define CHANNELI_STOPFLAG_REFSTAMP 0x00000001 + #define CHANNELI_STOPFLAG_UPDATELIST 0x00000002 + #define CHANNELI_STOPFLAG_RESETCALLBACKS 0x00000004 + #define CHANNELI_STOPFLAG_CALLENDCALLBACK 0x00000008 + #define CHANNELI_STOPFLAG_RESETCHANNELGROUP 0x00000010 + #define CHANNELI_STOPFLAG_PROCESSENDDELAY 0x00000020 + #define CHANNELI_STOPFLAG_UPDATESYNCPOINTS 0x00000040 + #define CHANNELI_STOPFLAG_DONTFREELEVELS 0x00000080 + + typedef struct + { + float *mLevels; + unsigned int mPCM, mLoopStart, mLoopEnd; + ChannelReal *mRealChannel; + SoundI *mSound; + DSPI *mDSP; + int mLoopCount; + bool mMute, mPaused; + unsigned int mDSPClockHi, mDSPClockLo; + FMOD_MODE mMode; + + #ifdef PLATFORM_WII + int mLowPassCutoff; + bool mBiquadActive; + unsigned short mBiquadB0; + unsigned short mBiquadB1; + unsigned short mBiquadB2; + unsigned short mBiquadA1; + unsigned short mBiquadA2; + unsigned int mControllerSpeaker; + #endif + } FMOD_CHANNEL_INFO; + + class ChannelI : public LinkedListNode /* This linked list node entry is for System::mChannelFreeListHead / mChannelUsedListHead */ + { /* 16 bytes */ + DECLARE_MEMORYTRACKER + + friend class Channel; + friend class OcclusionProcessor; + + public: + + SortedLinkedListNode mSortedListNode; /* 12 bytes */ + SortedLinkedListNode mSoundGroupSortedListNode; /* 12 bytes */ + + int mIndex; /* 4 bytes */ + void *mUserData; /* 4 bytes */ + unsigned int mHandleOriginal; /* Handle as it was when this channel was first played */ /* 4 bytes */ + SystemI *mSystem; /* 4 bytes */ + int mNumRealChannels; /* 4 bytes */ + ChannelReal *mRealChannel[FMOD_CHANNEL_MAXREALSUBCHANNELS]; /* 64 bytes <-------- */ + unsigned int mHandleCurrent; /* Handle as it is now */ /* 4 bytes */ + + /* + Misc + */ + FMOD_CHANNEL_FLAG mFlags; /* 4 bytes */ + FMOD_CHANNEL_PANMODE mLastPanMode; /* 4 bytes */ + int mPriority; /* 4 bytes */ + unsigned int mListPosition; /* 4 bytes */ + unsigned int mSoundGroupListPosition; /* 4 bytes */ + SyncPoint *mSyncPointCurrent; /* 4 bytes */ + unsigned int mSyncPointLastPos; /* 4 bytes */ + ChannelGroupI *mChannelGroup; /* 4 bytes */ + LinkedListNode mChannelGroupNode; /* 16 bytes */ + float mFadeVolume; /* 4 bytes */ + float mFadeTarget; /* 4 bytes */ + unsigned int mEndDelay; /* 4 bytes */ + FMOD_UINT64P mDSPClockDelay; /* 8 bytes */ + FMOD_UINT64P mDSPClockEnd; /* 8 bytes */ + FMOD_UINT64P mDSPClockPause; /* 8 bytes */ + float mLowPassGain; /* 4 bytes */ + DSPI *mAddDSPHead; /* 4 bytes */ + FMOD_SPEAKERMODE mSpeakerMode; /* 4 bytes */ + + /* + 2D + */ + float mVolume; /* 4 bytes */ + float mFrequency; /* 4 bytes */ + float mPan; /* 4 bytes */ + float mSpeakerFL, mSpeakerFR, mSpeakerC, mSpeakerLFE; /* 16 bytes */ + float mSpeakerBL, mSpeakerBR, mSpeakerSL, mSpeakerSR; /* 16 bytes */ + float mInputMix[FMOD_CHANNEL_MAXINPUTCHANNELS]; /* 64 bytes */ + float *mLevels; /* 4 bytes */ + + /* + 3D + */ + float mReverbDryVolume; /* 4 bytes */ + float mVolume3D; /* current relative 3d volume. */ /* 4 bytes */ + float mPitch3D; /* current relative 3d frequency. */ /* 4 bytes */ + FMOD_VECTOR mPosition3D; /* 12 bytes */ + FMOD_VECTOR mVelocity3D; /* 12 bytes */ + float mDistance; /* 4 bytes */ + float mMinDistance; /* 4 bytes */ + float mMaxDistance; /* 4 bytes */ + float mConeVolume3D; /* 4 bytes */ + float mConeInsideAngle; /* 4 bytes */ + float mConeOutsideAngle; /* 4 bytes */ + float mConeOutsideVolume; /* 4 bytes */ + FMOD_VECTOR mConeOrientation; /* 12 bytes */ + float mDirectOcclusion; /* 4 bytes */ + float mReverbOcclusion; /* 4 bytes */ + float mDirectOcclusionTarget; /* 4 bytes */ + float mReverbOcclusionTarget; /* 4 bytes */ + float mUserDirectOcclusion; /* 4 bytes */ + float mUserReverbOcclusion; /* 4 bytes */ + float mDirectOcclusionRateOfChange; /* 4 bytes */ + float mReverbOcclusionRateOfChange; /* 4 bytes */ + FMOD_VECTOR *mRolloffPoint; /* 4 bytes */ + int mNumRolloffPoints; /* 4 bytes */ + float mSpread; /* 4 bytes */ + float m3DPanLevel; /* 4 bytes */ + float m3DDopplerLevel; /* 4 bytes */ + + FMOD_CHANNEL_CALLBACK mCallback; /* 4 bytes */ + /* --------- */ + // F_API so EventSound::getEndTime can link to it + static FMOD_RESULT F_API validate(Channel *channel, ChannelI **channeli); /* ??? bytes */ + + FMOD_RESULT returnToFreeList(); + FMOD_RESULT setDefaults(); + FMOD_RESULT referenceStamp(bool newstamp = false); + FMOD_RESULT updatePosition(); + FMOD_RESULT forceVirtual(bool force); + FMOD_RESULT getChannelInfo(FMOD_CHANNEL_INFO *info); + FMOD_RESULT setChannelInfo(FMOD_CHANNEL_INFO *info); + FMOD_RESULT getRealChannel(ChannelReal **realchan, int *subchannels); + FMOD_RESULT calcVolumeAndPitchFor3D(int delta); + FMOD_RESULT setChannelGroupInternal(ChannelGroupI *channelgroup, bool resetattributes, bool forcedspreconnect = false); + FMOD_RESULT set3DOcclusionInternal(float directOcclusion, float reverbOcclusion, bool resettarget=true); + FMOD_RESULT get3DOcclusionInternal(float *directOcclusion, float *reverbOcclusion); + FMOD_RESULT getAudibilityInternal(float *audibility, bool usefadevolume); + // F_API so EventSound::getEndTime can link to it + FMOD_RESULT F_API getFinalFrequency(float *frequency); + + public: + + ChannelI(); + ChannelI(int index, SystemI *system); + FMOD_RESULT init(); + + + FMOD_RESULT getSystemObject (System **system); + + FMOD_RESULT play (SoundI *sound, bool paused, bool reset, bool startmuted); + FMOD_RESULT play (DSPI *dsp, bool paused, bool reset, bool startmuted); + FMOD_RESULT alloc (SoundI *sound, bool reset); + FMOD_RESULT alloc (DSPI *dsp, bool reset); + FMOD_RESULT start (); + FMOD_RESULT updateSyncPoints (bool seeking); + FMOD_RESULT update (int delta, bool updategeometrynow = false); + FMOD_RESULT updateStream (); + + FMOD_RESULT stop (); + FMOD_RESULT stopEx (FMOD_CHANNEL_STOPFLAG stopflag); + FMOD_RESULT setPaused (bool paused); + FMOD_RESULT getPaused (bool *paused); + FMOD_RESULT setVolume (float volume, bool forceupdatepos = false); + FMOD_RESULT getVolume (float *volume); + FMOD_RESULT setFrequency (float frequency); + FMOD_RESULT getFrequency (float *frequency); + FMOD_RESULT setPan (float pan, bool calldriver = true); + FMOD_RESULT getPan (float *pan); + FMOD_RESULT setDelay (FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo); + FMOD_RESULT getDelay (FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo); + FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright, bool calldriver = true); + FMOD_RESULT getSpeakerMix (float *frontleft, float *frontright, float *center, float *lfe, float *backleft, float *backright, float *sideleft, float *sideright); + FMOD_RESULT setSpeakerLevels (FMOD_SPEAKER speaker, float *levels, int numlevels, bool calldriver = true); + FMOD_RESULT getSpeakerLevels (FMOD_SPEAKER speaker, float *levels, int numlevels); + FMOD_RESULT setInputChannelMix (float *levels, int numlevels); + FMOD_RESULT getInputChannelMix (float *levels, int numlevels); + FMOD_RESULT setMute (bool mute); + FMOD_RESULT getMute (bool *mute); + FMOD_RESULT setPriority (int priority); + FMOD_RESULT getPriority (int *priority); + FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT setChannelGroup (ChannelGroupI *channelgroup); + FMOD_RESULT getChannelGroup (ChannelGroupI **channelgroup); + FMOD_RESULT setCallback (FMOD_CHANNEL_CALLBACK callback); + FMOD_RESULT calculate3DReverbGain(ReverbI *reverb, FMOD_VECTOR *channelpos, float *gain); + FMOD_RESULT setLowPassGain (float gain); + FMOD_RESULT getLowPassGain (float *gain); + + // 3D functionality. + FMOD_RESULT set3DAttributes (const FMOD_VECTOR *pos, const FMOD_VECTOR *vel); + FMOD_RESULT get3DAttributes (FMOD_VECTOR *pos, FMOD_VECTOR *vel); + FMOD_RESULT set3DMinMaxDistance (float mindistance, float maxdistance); + FMOD_RESULT get3DMinMaxDistance (float *mindistance, float *maxdistance); + FMOD_RESULT set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume); + FMOD_RESULT get3DConeSettings (float *insideconeangle, float *outsideconeangle, float *outsidevolume); + FMOD_RESULT set3DConeOrientation (FMOD_VECTOR *orientation); + FMOD_RESULT get3DConeOrientation (FMOD_VECTOR *orientation); + FMOD_RESULT set3DCustomRolloff (FMOD_VECTOR *points, int numpoints); + FMOD_RESULT get3DCustomRolloff (FMOD_VECTOR **points, int *numpoints); + FMOD_RESULT set3DOcclusion (float directOcclusion, float reverbOcclusion); + FMOD_RESULT get3DOcclusion (float *directOcclusion, float *reverbOcclusion); + FMOD_RESULT set3DSpread (float angle); + FMOD_RESULT get3DSpread (float *angle); + FMOD_RESULT set3DPanLevel (float level); + FMOD_RESULT get3DPanLevel (float *level); + FMOD_RESULT set3DDopplerLevel (float level); + FMOD_RESULT get3DDopplerLevel (float *level); + + // DSP functionality only for channels playing sounds created with FMOD_SOFTWARE. + FMOD_RESULT getDSPHead (DSPI **dsp); + FMOD_RESULT addDSP (DSPI *dsp, DSPConnectionI **connection); + + // Information only functions. + FMOD_RESULT isPlaying (bool *isplaying); + FMOD_RESULT isVirtual (bool *isvirtual); + FMOD_RESULT getAudibility (float *audibility); + FMOD_RESULT getCurrentSound (SoundI **sound); + FMOD_RESULT getCurrentDSP (DSPI **dsp); + FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset); + FMOD_RESULT getIndex (int *index); + + // Functions also found in Sound class but here they can be set per channel. + FMOD_RESULT setMode (FMOD_MODE mode); + FMOD_RESULT getMode (FMOD_MODE *mode); + FMOD_RESULT setLoopCount (int loopcount); + FMOD_RESULT getLoopCount (int *loopcount); + FMOD_RESULT setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype); + FMOD_RESULT getLoopPoints (unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype); + + // Userdata set/get. + FMOD_RESULT setUserData (void *userdata); + FMOD_RESULT getUserData (void **userdata); + + FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + +#ifdef PLATFORM_WII + // Lowpass set/get, this is for GameCube and Wii only + FMOD_RESULT setLowPassFilter (int cutoff); + FMOD_RESULT getLowPassFilter (int *cutoff); + + // Biquad set/get, this is for Wii only + FMOD_RESULT setBiquadFilter (bool active, unsigned short b0, unsigned short b1, unsigned short b2, unsigned short a1, unsigned short a2); + FMOD_RESULT getBiquadFilter (bool *active, unsigned short *b0, unsigned short *b1, unsigned short *b2, unsigned short *a1, unsigned short *a2); + + // Controller speaker set/get, this is for Wii only + FMOD_RESULT setControllerSpeaker (unsigned int controller, int subchannel = -1); + FMOD_RESULT getControllerSpeaker (unsigned int *controller); +#endif + }; +} + +#endif + + diff --git a/src/fmod_channelpool.cpp b/src/fmod_channelpool.cpp new file mode 100755 index 0000000..b87c943 --- /dev/null +++ b/src/fmod_channelpool.cpp @@ -0,0 +1,364 @@ +#include "fmod_settings.h" + +#include "fmod_channeli.h" +#include "fmod_channel_real.h" +#include "fmod_channelpool.h" +#include "fmod_memory.h" +#include "fmod_soundi.h" +#include "fmod_channel_software.h" + +#include "fmod_systemi.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelPool::ChannelPool() +{ + mChannel = 0; + mNumChannels = 0; + mChannelsUsed = 0; + mSystem = 0; + mOutput = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::init(SystemI *system, Output *output, int numchannels) +{ + if (numchannels < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (numchannels) + { + mChannel = (ChannelReal **)FMOD_Memory_Calloc(numchannels * sizeof(ChannelReal *)); + if (!mChannel) + { + return FMOD_ERR_MEMORY; + } + } + + mNumChannels = numchannels; + mSystem = system; + mOutput = output; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::release() +{ + int count; + + if (mChannel) + { + for (count = 0; count < mNumChannels; count++) + { + if (mChannel[count]) + { + mChannel[count]->close(); + } + } + + FMOD_Memory_Free(mChannel); + } + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::allocateChannel(ChannelReal **realchannel, int index, int numchannels, int *found_out, bool ignorereserved) +{ + int count; + int found = 0; + + if (!realchannel) + { + if (found_out) + { + *found_out = found; + } + return FMOD_ERR_INVALID_PARAM; + } + + if (index == FMOD_CHANNEL_FREE) + { + for (count = 0; count < mNumChannels; count++) + { + FMOD_RESULT result; + bool playing; + + if (mChannel[count]->mFlags & CHANNELREAL_FLAG_ALLOCATED || + mChannel[count]->mFlags & CHANNELREAL_FLAG_IN_USE || + (mChannel[count]->mFlags & CHANNELREAL_FLAG_RESERVED && !ignorereserved)) + { + continue; + } + + result = mChannel[count]->isPlaying(&playing, true); + if (result == FMOD_OK && !playing) + { + mChannel[count]->mFlags |= CHANNELREAL_FLAG_ALLOCATED; + mChannel[count]->mFlags |= CHANNELREAL_FLAG_IN_USE; + mChannel[count]->mFlags &= ~CHANNELREAL_FLAG_STOPPED; + mChannel[count]->mFlags &= ~CHANNELREAL_FLAG_RESERVED; + + realchannel[found] = mChannel[count]; + found++; + if (found == numchannels) + { + if (found_out) + { + *found_out = found; + } + return FMOD_OK; + } + } + } + } + else if (index >= 0 && index < mNumChannels) + { + if (numchannels > 1) + { + return FMOD_ERR_CHANNEL_ALLOC; + } + + mChannel[index]->mFlags |= CHANNELREAL_FLAG_ALLOCATED; + mChannel[index]->mFlags |= CHANNELREAL_FLAG_IN_USE; + mChannel[index]->mFlags &= ~CHANNELREAL_FLAG_STOPPED; + + realchannel[0] = mChannel[index]; + return FMOD_OK; + } + + /* + Undo the allocations that occured before just in case they are not used. + */ + for (count = 0; count < found; count++) + { + if (realchannel[count]) + { + realchannel[count]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED; + realchannel[count]->mFlags &= ~CHANNELREAL_FLAG_IN_USE; + realchannel[count]->mFlags |= CHANNELREAL_FLAG_STOPPED; + } + } + + if (found_out) + { + *found_out = found; + } + + return FMOD_ERR_CHANNEL_ALLOC; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::getNumChannels(int *numchannels) +{ + if (!numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numchannels = mNumChannels; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::getChannelsUsed(int *numchannels) +{ + if (!numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numchannels = mChannelsUsed; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::setChannel(int index, ChannelReal *channel, DSPI *dspmixtarget) +{ + if (!channel || index < 0 || index >= mNumChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + mChannel[index] = channel; + mChannel[index]->mPool = this; + + return mChannel[index]->init(index, mSystem, mOutput, dspmixtarget); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelPool::getChannel(int index, ChannelReal **channel) +{ + if (!channel || index < 0 || index >= mNumChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *channel = mChannel[index]; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT ChannelPool::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(*this)); + + if (mChannel) + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, mNumChannels * sizeof(ChannelReal *)); + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_channelpool.h b/src/fmod_channelpool.h new file mode 100755 index 0000000..bf7a8eb --- /dev/null +++ b/src/fmod_channelpool.h @@ -0,0 +1,49 @@ +#ifndef _FMOD_CHANNELPOOL_H +#define _FMOD_CHANNELPOOL_H + +#include "fmod_settings.h" +#include "fmod.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class ChannelReal; + class DSPI; + class Output; + class Sound; + class SystemI; + + class ChannelPool + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class ChannelReal; + + protected: + + int mNumChannels; + int mChannelsUsed; + SystemI *mSystem; + Output *mOutput; + + public: + + ChannelReal **mChannel; + + ChannelPool(); + + FMOD_RESULT init(SystemI *system, Output *output, int numchannels); + FMOD_RESULT release(); + FMOD_RESULT find(int id, Sound *sound, ChannelReal **channel, int excludeid, bool stopstreamable); + FMOD_RESULT allocateChannel(ChannelReal **realchannel, int index, int numchannels, int *found, bool ignorereserved = false); + FMOD_RESULT getNumChannels(int *numchannels); + FMOD_RESULT getChannelsUsed(int *numchannels); + FMOD_RESULT setChannel(int index, ChannelReal *channel, DSPI *dspmixtarget = 0); + FMOD_RESULT getChannel(int index, ChannelReal **channel); + }; +} + +#endif diff --git a/src/fmod_cmdlog.cpp b/src/fmod_cmdlog.cpp new file mode 100755 index 0000000..70c4502 --- /dev/null +++ b/src/fmod_cmdlog.cpp @@ -0,0 +1,249 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CMDLOG + +#define FMOD_CMDLOG_PLAYBACK + + +#include "fmod_cmdlog.h" +#include "fmod.hpp" +#include "fmod_memory.h" +#include "fmod_time.h" +#include "fmod_systemi.h" +#include "stdio.h" + + +FMOD_RESULT FMOD_CmdLog_Flush(); + +const char *FMOD_CMDLOG_FILENAME = "cmdlog.bin"; +const int FMOD_CMDLOGSIZE = 1024 * 1024; + +static char *gCmdLogBuf = 0; +static char *gCmdLogPtr = 0; +static FILE *readfp = 0; + + +FMOD_RESULT FMOD_CmdLog_Init() +{ +#ifndef FMOD_CMDLOG_PLAYBACK + + gCmdLogBuf = (char *)FMOD_Memory_Calloc(FMOD_CMDLOGSIZE); + if (!gCmdLogBuf) + { + return FMOD_ERR_MEMORY; + } + + FILE *fp = fopen(FMOD_CMDLOG_FILENAME, "wb"); + if (!fp) + { + return FMOD_ERR_FILE_BAD; + } + unsigned int version = FMOD_VERSION; + fwrite(&version, 1, sizeof(version), fp); + fclose(fp); + + gCmdLogPtr = gCmdLogBuf; + +#endif + + return FMOD_OK; +} + + +FMOD_RESULT FMOD_CmdLog_Release() +{ +#ifndef FMOD_CMDLOG_PLAYBACK + + FMOD_RESULT result = FMOD_CmdLog_Flush(); + if (result != FMOD_OK) + { + return result; + } + + if (gCmdLogBuf) + { + FMOD_Memory_Free(gCmdLogBuf); + gCmdLogBuf = 0; + } + +#endif + + return FMOD_OK; +} + + +void FMOD_CmdLog_Push(void *data, int datalen) +{ +#ifndef FMOD_CMDLOG_PLAYBACK + + int i; + char *d; +/*AJS + unsigned int t; + + FMOD_OS_Time_GetMs(&t); + d = (char *)(&t); + for (i=0; i < sizeof(t); i++) + { + *gCmdLogPtr++ = *d++; + + if ((gCmdLogPtr - gCmdLogBuf) >= FMOD_CMDLOGSIZE) + { + FMOD_CmdLog_Flush(); + } + } +AJS*/ + + d = (char *)data; + for (i=0; i < datalen; i++) + { + *gCmdLogPtr++ = *d++; + + if ((gCmdLogPtr - gCmdLogBuf) >= FMOD_CMDLOGSIZE) + { + FMOD_CmdLog_Flush(); + } + } + +#endif +} + + +FMOD_RESULT FMOD_CmdLog_Flush() +{ +#ifndef FMOD_CMDLOG_PLAYBACK + + FILE *fp = fopen(FMOD_CMDLOG_FILENAME, "ab"); + if (!fp) + { + FLOGC((FMOD::LOG_ERROR, __FILE__, __LINE__, "FMOD_CmdLog_Flush", "Error opening %s for writing\n", FMOD_CMDLOG_FILENAME)); + return FMOD_ERR_FILE_BAD; + } + + int b = gCmdLogPtr - gCmdLogBuf; + + if (fwrite(gCmdLogBuf, 1, b, fp) != (unsigned int)b) + { + FLOGC((FMOD::LOG_ERROR, __FILE__, __LINE__, "FMOD_CmdLog_Flush", "Error writing %d bytes to %s\n", b, FMOD_CMDLOG_FILENAME)); + } + + fclose(fp); + + gCmdLogPtr = gCmdLogBuf; + +#endif + + return FMOD_OK; +} + + + +FMOD_RESULT FMOD_CmdLog_Begin() +{ + if (readfp) + { + return FMOD_ERR_INITIALIZED; + } + + readfp = fopen(FMOD_CMDLOG_FILENAME, "rb"); + if (!readfp) + { + return FMOD_ERR_FILE_BAD; + } + + unsigned int version; + if (fread(&version, 1, sizeof(version), readfp) != sizeof(version)) + { + fclose(readfp); + return FMOD_ERR_FILE_BAD; + } + + if (version != FMOD_VERSION) + { + //AJS complain? + } + + return FMOD_OK; +} + + +FMOD_RESULT FMOD_CmdLog_Read(void *buf, int bytes) +{ + if (!readfp) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (buf) + { + int bytesread = fread(buf, 1, bytes, readfp); + if (bytesread != bytes) + { + return FMOD_ERR_FILE_EOF; + } + } + else + { + if (fseek(readfp, bytes, SEEK_CUR)) + { + return FMOD_ERR_FILE_EOF; + } + } + + return FMOD_OK; +} + + +FMOD_RESULT FMOD_CmdLog_Execute() +{ + int cmd; + + FMOD_CmdLog_Read(&cmd, sizeof(int)); + + switch (cmd) + { + case FMOD_CMDLOG_SYSTEM_PLAYSOUND : + { + FMOD::SystemI *_cl_system; + FMOD_CHANNELINDEX _cl_channelid; + FMOD::SoundI *_cl_sound; + bool _cl_paused; + FMOD::Channel **_cl_channel; + + FMOD_CmdLog_Read(&_cl_system, sizeof(FMOD::System *)); + FMOD_CmdLog_Read(&_cl_channelid, sizeof(FMOD_CHANNELINDEX)); + FMOD_CmdLog_Read(&_cl_sound, sizeof(FMOD::SoundI *)); + FMOD_CmdLog_Read(&_cl_paused, sizeof(bool)); + FMOD_CmdLog_Read(0, sizeof(FMOD::Channel **)); + + _cl_system = (FMOD::SystemI *)FMOD::gSystemHead->getNodeByIndex(0); + + //AJS need to resolve sound and system somehow + //AJS they're ptrs not handles - make them handles? just use indices? + + FMOD_RESULT result = _cl_system->playSound(_cl_channelid, _cl_sound, _cl_paused, 0); + + break; + } + + default : + break; + } + + return FMOD_OK; +} + + +FMOD_RESULT FMOD_CmdLog_End() +{ + if (readfp) + { + fclose(readfp); + readfp = 0; + } + + return FMOD_OK; +} + + +#endif diff --git a/src/fmod_cmdlog.h b/src/fmod_cmdlog.h new file mode 100755 index 0000000..fae9add --- /dev/null +++ b/src/fmod_cmdlog.h @@ -0,0 +1,29 @@ +#ifndef _FMOD_CMDLOG_H +#define _FMOD_CMDLOG_H + +#ifdef FMOD_SUPPORT_CMDLOG + + +#ifndef _FMOD_H +#include "fmod.h" +#endif + + +// don't change the order! +enum +{ + FMOD_CMDLOG_SYSTEM_PLAYSOUND = 0, +}; + + +extern FMOD_RESULT FMOD_CmdLog_Init(); +extern FMOD_RESULT FMOD_CmdLog_Release(); +extern void FMOD_CmdLog_Push(void *data, int datalen); + +extern FMOD_RESULT FMOD_CmdLog_Begin(); +extern FMOD_RESULT FMOD_CmdLog_Execute(); +extern FMOD_RESULT FMOD_CmdLog_End(); + +#endif + +#endif diff --git a/src/fmod_codec.cpp b/src/fmod_codec.cpp new file mode 100755 index 0000000..8a26c79 --- /dev/null +++ b/src/fmod_codec.cpp @@ -0,0 +1,865 @@ +#include "fmod_settings.h" + +#include "fmod_codeci.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_soundi.h" +#include "fmod_file_disk.h" +namespace FMOD +{ + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Codec::release() +{ + FMOD_RESULT result; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Codec::release", "\n")); + + if (mDescription.close) + { + mDescription.close(this); + } + + if (mFile) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Codec::release", "Close file (mFile = %p)\n", mFile)); + + mFile->close(); + + FMOD_Memory_Free(mFile); + mFile = 0; + } + + if (mWaveFormatMemory && mType == FMOD_SOUND_TYPE_FSB) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + if (mMetadata) + { + mMetadata->release(); + mMetadata = 0; + } + + result = Plugin::release(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Codec::release", "done\n")); + + return result; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + This is just a helper function for codecs if mPCMBuffer and mPCMBufferLengthBytes have been allocated inside the codec code. + It simply calls the decode in a loop, feeding the output buffer from a smaller pcm decode buffer (mPCMBuffer). + Codecs that want to skip offline buffering and write directly to the output pointer (ie raw 1:1 pcm that just does an fread for example) + should not allocate those variables. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Codec::read(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + bool checkmetadata = false; + unsigned int read = 0; + + if (mPCMBuffer && mPCMBufferLengthBytes) + { + while (sizebytes) + { + unsigned int lenbytes = sizebytes; + unsigned int bytesdecoded = 0; + + if (!mPCMBufferOffsetBytes) + { + result = mDescription.read(this, mPCMBuffer, mPCMBufferLengthBytes, &bytesdecoded); + if (result != FMOD_OK) + { + break; + } + + mPCMBufferFilledBytes = bytesdecoded; + + lenbytes = bytesdecoded; + if (lenbytes > sizebytes) + { + lenbytes = sizebytes; + } + + checkmetadata = true; + } + + if (mPCMBufferOffsetBytes + lenbytes > mPCMBufferFilledBytes) + { + lenbytes = mPCMBufferFilledBytes - mPCMBufferOffsetBytes; + } + + FMOD_memcpy((char *)buffer + read, mPCMBuffer + mPCMBufferOffsetBytes, lenbytes); + + + mPCMBufferOffsetBytes += lenbytes; + if (mPCMBufferOffsetBytes >= mPCMBufferFilledBytes) + { + mPCMBufferOffsetBytes = 0; + } + + if (!lenbytes) + { + lenbytes = sizebytes; + break; + } + + sizebytes -= lenbytes; + read += lenbytes; + } + } + else + { + result = mDescription.read(this, buffer, sizebytes, &read); + if (result == FMOD_OK) + { + checkmetadata = true; + } + } + + #ifndef PLATFORM_PS3_SPU + if (checkmetadata) + { + getMetadataFromFile(); + } + #endif + + if (bytesread) + { + *bytesread = read; + } + + return result; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Codec::getMetadataFromFile() +{ + FMOD_RESULT result = FMOD_OK; + Metadata *filemetadata; + + if (mFile) + { + result = mFile->getMetadata(&filemetadata); + if (result == FMOD_OK) + { + if (!mMetadata) + { + mMetadata = FMOD_Object_Alloc(Metadata); + if (!mMetadata) + { + return FMOD_ERR_MEMORY; + } + } + + result = mMetadata->add(filemetadata); + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Codec::getLength(unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ + FMOD_RESULT result; + + if (lengthtype == FMOD_TIMEUNIT_RAWBYTES) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + result = mDescription.getwaveformat(this, mSubSoundIndex, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + *length = waveformat.lengthbytes; + + return FMOD_OK; + } + + if (!mDescription.getlength) + { + *length = 0; + return FMOD_ERR_UNSUPPORTED; + } + + result = mDescription.getlength(this, length, lengthtype); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Codec::setPosition(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + FMOD_CODEC_WAVEFORMAT waveformat; + + if (numsubsounds && subsound >= numsubsounds) + { + return FMOD_ERR_INVALID_POSITION; + } + + if (!mDescription.setposition) + { + return FMOD_ERR_UNSUPPORTED; + } + + if (subsound < 0) + { + subsound = mSubSoundIndex; + } + + if (!numsubsounds) + { + subsound = 0; + } + + + result = mDescription.getwaveformat(this, subsound, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + /* + If the incoming timeformat is not a codec friendly format, convert it to the + codec's favourite time format. (for convertable formats like ms/pcm/bytes) + */ + if (mDescription.timeunits & FMOD_TIMEUNIT_PCM) + { + if (postype & FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(position, &position, waveformat.channels, waveformat.format); + postype = FMOD_TIMEUNIT_PCM; + } + else if (postype & FMOD_TIMEUNIT_MS) + { + position = (unsigned int)((float)position / 1000.0f * waveformat.frequency); + postype = FMOD_TIMEUNIT_PCM; + } + } + else if (mDescription.timeunits & FMOD_TIMEUNIT_PCMBYTES) + { + if (postype & FMOD_TIMEUNIT_PCM) + { + SoundI::getBytesFromSamples(position, &position, waveformat.channels, waveformat.format); + postype = FMOD_TIMEUNIT_PCMBYTES; + } + else if (postype & FMOD_TIMEUNIT_MS) + { + position = (unsigned int)((float)position / 1000.0f * waveformat.frequency); + SoundI::getBytesFromSamples(position, &position, waveformat.channels, waveformat.format); + postype = FMOD_TIMEUNIT_PCMBYTES; + } + } + else if (mDescription.timeunits & FMOD_TIMEUNIT_MS) + { + if (postype & FMOD_TIMEUNIT_PCM) + { + position = (unsigned int)((float)position / waveformat.frequency * 1000.0f); + postype = FMOD_TIMEUNIT_MS; + } + else if (postype & FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(position, &position, waveformat.channels, waveformat.format); + position = (unsigned int)((float)position / waveformat.frequency * 1000.0f); + postype = FMOD_TIMEUNIT_MS; + } + } + + if (!(postype & mDescription.timeunits)) + { + return FMOD_ERR_FORMAT; + } + + mPCMBufferOffsetBytes = 0; + + result = mDescription.setposition(this, subsound, position, postype); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + mSubSoundIndex = subsound; + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Codec::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + + if (postype == FMOD_TIMEUNIT_RAWBYTES) + { + if (!mFile) + { + *position = 0; + } + + result = mFile->tell(position); + if (result != FMOD_OK) + { + *position = 0; + return result; + } + + *position -= mSrcDataOffset; + } + + if (!mDescription.getposition) + { + return FMOD_ERR_UNSUPPORTED; + } + + if (!(postype & mDescription.timeunits)) + { + return FMOD_ERR_FORMAT; + } + + result = mDescription.getposition(this, position, postype); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] + FMOD_TAGDATATYPE +] +*/ +FMOD_RESULT Codec::metaData(FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, bool unique) +{ + if (!mMetadata) + { + mMetadata = FMOD_Object_Alloc(Metadata); + if (!mMetadata) + { + return FMOD_ERR_MEMORY; + } + } + + return mMetadata->addTag(type, name, data, datalen, datatype, unique); +} + +#endif // !PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#if defined(FMOD_SUPPORT_MEMORYTRACKER) && !defined(PLATFORM_PS3_SPU) + +FMOD_RESULT Codec::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_CODEC, mDescription.mSize); + + if (mFile) + { + CHECK_RESULT(((FMOD::DiskFile *)mFile)->getMemoryUsed(tracker)); + } + + if (mDescription.getmemoryused) + { + CHECK_RESULT(mDescription.getmemoryused(this, tracker)); + } + + return FMOD_OK; +} + +#endif + + +/* +[API] +[ + [DESCRIPTION] + Open callback for the codec for when FMOD tries to open a sound using this codec. + This is the callback the file format check is done in, codec related memory is allocated, and things are generally initialized / set up for the codec. + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'usermode' Mode that the user supplied via System::createSound. This is informational and can be ignored, or used if it has relevance to your codec. + 'userexinfo' Extra info structure that the user supplied via System::createSound. This is informational and can be ignored, or used if it has relevance to your codec. + + [RETURN_VALUE] + + [REMARKS] + The usermode and userexinfo parameters tell the codec what was passed in by the user.
    + Generally these can be ignored, as the file format usually determines the format and frequency of the sound.
    +
    + If you have a flexible format codec (ie you don't mind what output format your codec writes to), you might want to use the parameter that was passed in by the user to specify the output sound format / frequency.
    + For example if you normally create a codec that is always 32bit floating point, the user might supply 16bit integer to save memory, so you could use this information to decode your data to this format instead of the original default format. +
    + Read and seek within the file using the 'fileread' and 'fileseek' members of the FMOD_CODEC codec that is passed in.
    + Note: DO NOT USE YOUR OWN FILESYSTEM.
    + The reasons for this are:
    +

  • The user may have set their own file system via user filesystem callbacks.
    +
  • FMOD allows file reading via disk, memory and TCP/IP. If you use your own file routines you will lose this ability.
    +
    + Important! FMOD will ping all codecs trying to find the right one for the file the user has passed in. Make sure the first line of your codec open is a FAST format check. Ie it reads an identifying string, checks it and returns an error FMOD_ERR_FORMAT if it is not found.
    + There may be a lot of codecs loaded into FMOD, so you don't want yours slowing down the System::createSound call because it is inneficient in determining if it is the right format or not.
    +
    + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSound + FMOD_CREATESOUNDEXINFO + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_OPENCALLBACK(FMOD_CODEC_STATE *codec_state, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Close callback for the codec for when FMOD tries to close a sound using this codec.
    + This is the callback any codec related memory is freed, and things are generally de-initialized / shut down for the codec. + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_CLOSECALLBACK(FMOD_CODEC_STATE *codec_state) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Read callback for the codec for when FMOD tries to read some data from the file to the destination format (format specified in the open callback). + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'buffer' Buffer to read PCM data to. Note that the format of this data is the format described in FMOD_CODEC_WAVEFORMAT. + 'sizebytes' Number of bytes to read + 'bytesread' Number of bytes actually read + + [RETURN_VALUE] + + [REMARKS] + If you cannot read number of bytes requested, simply return FMOD_OK and give bytesread the number of bytes you read. +
    + Read and seek within the file using the 'fileread' and 'fileseek' members of the FMOD_CODEC codec that is passed in.
    + Note: DO NOT USE YOUR OWN FILESYSTEM.
    + The reasons for this are:
    +
  • The user may have set their own file system via user filesystem callbacks.
    +
  • FMOD allows file reading via disk, memory and TCP/IP. If you use your own file routines you will lose this ability.
    +
    + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_READCALLBACK(FMOD_CODEC_STATE *codec_state, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Callback to return the length of the song in whatever format required when Sound::getLength is called. + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'length' Address of a variable that is to receive the length of the sound determined by the format specified in the lengttype parameter. + 'lengthtype' Timeunit type of length to return. This will be one of the timeunits supplied by the codec author in the FMOD_CODEC_DESCRIPTION structure. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_TIMEUNIT + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_GETLENGTHCALLBACK(FMOD_CODEC_STATE *codec_state, unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ +/* +[API] +[ + [DESCRIPTION] + Seek callback for the codec for when FMOD tries to seek within the file with Channel::setPosition. + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'subsound' Subsound within which to seek. + 'position' Position to seek to in the sound based on the timeunit specified in the postype parameter. + 'postype' Timeunit type of the position parameter. This will be one of the timeunits supplied by the codec author in the FMOD_CODEC_DESCRIPTION structure. + + [RETURN_VALUE] + + [REMARKS] + Read and seek within the file using the 'fileread' and 'fileseek' members of the FMOD_CODEC codec that is passed in.
    + Note: DO NOT USE YOUR OWN FILESYSTEM.
    + The reasons for this are:
    +
  • The user may have set their own file system via user filesystem callbacks.
    +
  • FMOD allows file reading via disk, memory and TCP/IP. If you use your own file routines you will lose this ability.
    +
    + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::setPosition + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_SETPOSITIONCALLBACK(FMOD_CODEC_STATE *codec_state, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ +/* +[API] +[ + [DESCRIPTION] + Tell callback for the codec for when FMOD tries to get the current position within the with Channel::getPosition. + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'position' Address of a variable to receive the current position in the codec based on the timeunit specified in the postype parameter. + 'postype' Timeunit type of the position parameter that is requested. This will be one of the timeunits supplied by the codec author in the FMOD_CODEC_DESCRIPTION structure. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Channel::getPosition + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_GETPOSITIONCALLBACK(FMOD_CODEC_STATE *codec_state, unsigned int *position, FMOD_TIMEUNIT postype) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Sound creation callback for the codec when FMOD finishes creating the sound. Ie so the codec can set more parameters for the related created sound, ie loop points/mode or 3D attributes etc. + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'subsound' Subsound index being created. + 'sound' Pointer to the sound being created. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSound + System::createStream + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_SOUNDCREATECALLBACK(FMOD_CODEC_STATE *codec_state, int subsound, FMOD_SOUND *sound) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Callback for sounds that have their + + [PARAMETERS] + 'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'type' Source of tag being updated, ie id3v2 or oggvorbis tag for example. See FMOD_TAGDATATYPE. + 'name' Name of the tag being updated. + 'data' Contents of tag. + 'datalen' Length of the tag data in bytes. + 'datatype' Data type of tag. Binary / string / unicode etc. See FMOD_TAGDATATYPE. + 'unique' If this is true, then the tag (determined by the name) being updated is the only one of its type. If it is false then there are multiple versions of this tag with the same name. + + [RETURN_VALUE] + + [REMARKS] + This callback is usually called from sounds that can udate their metadata / tag info at runtime. Such a sound could be an internet SHOUTcast / Icecast stream for example.
    +
    + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.
    + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_CODEC_STATE + FMOD_CODEC_DESCRIPTION + FMOD_CODEC_OPENCALLBACK + FMOD_CODEC_CLOSECALLBACK + FMOD_CODEC_READCALLBACK + FMOD_CODEC_GETLENGTHCALLBACK + FMOD_CODEC_SETPOSITIONCALLBACK + FMOD_CODEC_GETPOSITIONCALLBACK + FMOD_CODEC_SOUNDCREATECALLBACK + FMOD_TAGDATATYPE +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_CODEC_METADATACALLBACK(FMOD_CODEC_STATE *codec_state, FMOD_TAGTYPE type, char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, int unique) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +} + + diff --git a/src/fmod_codec_aiff.cpp b/src/fmod_codec_aiff.cpp new file mode 100755 index 0000000..14d7c87 --- /dev/null +++ b/src/fmod_codec_aiff.cpp @@ -0,0 +1,815 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_AIFF + +#include "fmod.h" +#include "fmod_codec_aiff.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#include + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX aiffcodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescriptionEx is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecAIFF::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecAIFF::getDescriptionEx() +{ + FMOD_memset(&aiffcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + aiffcodec.name = "FMOD AIFF Codec"; + aiffcodec.version = 0x00010100; + aiffcodec.timeunits = FMOD_TIMEUNIT_PCM; + aiffcodec.open = &CodecAIFF::openCallback; + aiffcodec.close = &CodecAIFF::closeCallback; + aiffcodec.read = &CodecAIFF::readCallback; + aiffcodec.setposition = &CodecAIFF::setPositionCallback; + + aiffcodec.mType = FMOD_SOUND_TYPE_AIFF; + aiffcodec.mSize = sizeof(CodecAIFF); + + return &aiffcodec; +} + + + +#define UnsignedToFloat(u) (((float)((int)(u - 2147483647L - 1))) + 2147483648.0f) + +/**************************************************************** + * Extended precision IEEE floating-point conversion routine. + ****************************************************************/ +float ConvertFromIeeeExtended(unsigned char *bytes) +{ + float f; + int expon; + unsigned int hiMant, loMant; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((unsigned int)(bytes[2] & 0xFF) << 24) + | ((unsigned int)(bytes[3] & 0xFF) << 16) + | ((unsigned int)(bytes[4] & 0xFF) << 8) + | ((unsigned int)(bytes[5] & 0xFF)); + loMant = ((unsigned int)(bytes[6] & 0xFF) << 24) + | ((unsigned int)(bytes[7] & 0xFF) << 16) + | ((unsigned int)(bytes[8] & 0xFF) << 8) + | ((unsigned int)(bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) + { + f = 0; + } + else + { + if (expon == 0x7FFF) + { /* Infinity or NaN */ + f = 0; + } + else + { + expon -= 16383; + f = (float)FMOD_LDEXP(UnsignedToFloat(hiMant), expon-=31); + f += (float)FMOD_LDEXP(UnsignedToFloat(loMant), expon-=32); + } + } + + if (bytes[0] & 0x80) + { + return -f; + } + else + { + return f; + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecAIFF::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int fileoffset, filesize; + int beginloop = -1, endloop = -1; + unsigned int lensamples = 0; + char id[4]; + AIFF_CHUNK chunk; + bool done = false; + int bits = 0; + + mIsAIFC = false; + mLittleEndian = false; + + init(FMOD_SOUND_TYPE_AIFF); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecAIFF::openInternal", "attempting to open as AIFF..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Read header + */ + result = mFile->read(&chunk, 1, sizeof(AIFF_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp((const char *)chunk.id, "FORM", 4)) + { + return FMOD_ERR_FORMAT; + } + + result = mFile->read(&id, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + filesize = chunk.size; +#ifdef PLATFORM_ENDIAN_LITTLE + filesize = FMOD_SWAPENDIAN_DWORD(filesize); +#endif + + if (!FMOD_strncmp(id, "AIFC", 4)) + { + mIsAIFC = true; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecAIFF::openInternal", "This AIFF is an AIF-C variation.\n")); + } + else if (FMOD_strncmp(id, "AIFF", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecAIFF::openInternal", "'FORM' or 'AIFF' ID check failed [%c%c%c%c] : [%c%c%c%c]\n", chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3], id[0], id[1], id[2], id[3])); + return FMOD_ERR_FORMAT; + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + waveformat = mWaveFormatMemory; + + + /* + Get size of file in bytes + */ + result = mFile->getSize(&waveformat[0].lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = (unsigned int)-1; + + fileoffset = 0; + fileoffset += sizeof(AIFF_CHUNK); + fileoffset += 4; + + /* + Decode chunks + */ + do + { + result = mFile->seek(fileoffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&chunk, 1, sizeof(AIFF_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_LITTLE + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecAIFF::openInternal", "chunk : id %c%c%c%c size %d\n", chunk.id[0],chunk.id[1],chunk.id[2],chunk.id[3], chunk.size)); + + /* + COMMON CHUNK + */ + if (!FMOD_strncmp((const char *)chunk.id, "COMM", 4)) + { + AIFF_COMMONCHUNK commonchunk; + AIFC_COMMONCHUNK commonchunkaifc; + + if (mIsAIFC) + { + result = mFile->read(&commonchunkaifc, 1, sizeof(AIFC_COMMONCHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + // If format is "sowt", this means it is little endian + if (!FMOD_strncmp(commonchunkaifc.compressionid, "NONE", 4)) + { + mLittleEndian = false; + } + else if (!FMOD_strncmp(commonchunkaifc.compressionid, "sowt", 4)) + { + mLittleEndian = true; + } + else + { + return FMOD_ERR_FORMAT; + } + } + else + { + result = mFile->read(&commonchunk, 1, sizeof(AIFF_COMMONCHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + } + + #ifdef PLATFORM_ENDIAN_LITTLE + if (mIsAIFC) + { + commonchunkaifc.numChannels = FMOD_SWAPENDIAN_WORD(commonchunkaifc.numChannels); + commonchunkaifc.numSampleFrames = FMOD_SWAPENDIAN_DWORD(commonchunkaifc.numSampleFrames); + commonchunkaifc.sampleSize = FMOD_SWAPENDIAN_WORD(commonchunkaifc.sampleSize); + } + else + { + commonchunk.numChannels = FMOD_SWAPENDIAN_WORD(commonchunk.numChannels); + commonchunk.numSampleFrames = FMOD_SWAPENDIAN_DWORD(commonchunk.numSampleFrames); + commonchunk.sampleSize = FMOD_SWAPENDIAN_WORD(commonchunk.sampleSize); + } + #endif + + if (mIsAIFC) + { + lensamples = commonchunkaifc.numSampleFrames; + + waveformat[0].frequency = (int)ConvertFromIeeeExtended(&commonchunkaifc.eSampleRate[0]); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecAIFF::openInternal", "channels %d samplesize %d\n", commonchunkaifc.numChannels, commonchunkaifc.sampleSize)); + + bits = commonchunkaifc.sampleSize; + result = SoundI::getFormatFromBits(bits, &waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + waveformat[0].channels = commonchunkaifc.numChannels; + } + else + { + lensamples = commonchunk.numSampleFrames; + + waveformat[0].frequency = (int)ConvertFromIeeeExtended(&commonchunk.eSampleRate[0]); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecAIFF::openInternal", "channels %d samplesize %d\n", commonchunk.numChannels, commonchunk.sampleSize)); + + bits = commonchunk.sampleSize; + result = SoundI::getFormatFromBits(bits, &waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + waveformat[0].channels = commonchunk.numChannels; + } + } + + /* + SOUND DATA CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "SSND", 4)) + { + AIFF_SOUNDDATACHUNK sounddatachunk; + + result = mFile->read(&sounddatachunk, 1, sizeof(AIFF_SOUNDDATACHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (mSrcDataOffset == (unsigned int)-1) + { + waveformat[0].lengthbytes = chunk.size - sizeof(AIFF_SOUNDDATACHUNK); + result = mFile->tell(&mSrcDataOffset); + if (result != FMOD_OK) + { + return result; + } + } + + if (!(mFile->mFlags & FMOD_FILE_SEEKABLE)) + { + done = true; + } + } + + /* + SOUND DATA CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "INST", 4)) + { + AIFF_INSTRUMENTCHUNK instchunk; + + result = mFile->read(&instchunk, 1, sizeof(AIFF_INSTRUMENTCHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + /* want sustainLoop */ + #ifdef PLATFORM_ENDIAN_LITTLE + instchunk.sustainLoop.beginLoop = FMOD_SWAPENDIAN_WORD(instchunk.sustainLoop.beginLoop); + instchunk.sustainLoop.endLoop = FMOD_SWAPENDIAN_WORD(instchunk.sustainLoop.endLoop); + #endif + + beginloop = instchunk.sustainLoop.beginLoop; + endloop = instchunk.sustainLoop.endLoop; + + if (!beginloop) + { + beginloop++; + endloop++; + } + } + + /* + SOUND DATA CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "MARK", 4)) + { +#if 0 + AIFF_MARKER markerchunk; + unsigned short nummarkers; + int count; + + result = mFile->read(&nummarkers, 1, 2, 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_LITTLE + nummarkers = FMOD_SWAPENDIAN_WORD(nummarkers); + #endif + + if (nummarkers && points) + { + FMOD_SyncPoints_Init(points); + } + + for (count=0; count < nummarkers; count++) + { + char name[FMOD_STRING_MAXNAMELEN]; + + if (FMOD_File_Read(&markerchunk, 1, sizeof(AIFF_MARKER), fp) != sizeof(AIFF_MARKER)) + { + FMOD_ErrorNo = FMOD_ERR_FILE_BAD; + return FALSE; + } + + #ifdef PLATFORM_ENDIAN_LITTLE + markerchunk.position = FMOD_SWAPENDIAN_DWORD(markerchunk.position); + #endif + + FMOD_memset(name, 0, FMOD_STRING_MAXNAMELEN); + if (FMOD_File_Read(name, 1, markerchunk.markerlength + 1, fp) != markerchunk.markerlength + 1) + { + FMOD_ErrorNo = FMOD_ERR_FILE_BAD; + return FALSE; + } + + if (markerchunk.position >= lensamples) + { + markerchunk.position = lensamples - 1; + } + + if (looppoints) + { + if (beginloop == count + 1) + { + looppoints[0] = markerchunk.position; + } + else if (endloop == count + 1) + { + looppoints[1] = markerchunk.position; + } + } + + if (points) + { + FMOD_SyncPoint_Add(points, markerchunk.position, name); + } + } +#endif + } + + fileoffset += sizeof(AIFF_CHUNK); + fileoffset += chunk.size; + + if (chunk.size & 1) + { + fileoffset++; + } + + if (chunk.size < 0) + { + break; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecAIFF::openInternal", "offset = %d / %d\n", fileoffset, filesize)); + + } while (fileoffset < filesize && fileoffset > 0 && !done); + + /* + Didnt find the data chunk! + */ + if (mSrcDataOffset == (unsigned int)-1) + { + mSrcDataOffset = 0; + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecAIFF::openInternal", "couldn't find a data chunk\n")); + + return FMOD_ERR_FILE_BAD; + } + + result = SoundI::getSamplesFromBytes(waveformat[0].lengthbytes, &waveformat[0].lengthpcm, waveformat[0].channels, waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + + waveformat[0].blockalign = waveformat[0].channels * bits / 8; + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecAIFF::closeInternal() +{ + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + waveformat = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecAIFF::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result; + + // Bring the number of bytes to a multiple of 3 bytes + if(waveformat[0].format == FMOD_SOUND_FORMAT_PCM24) + { + if(sizebytes >= 3) + { + sizebytes = (sizebytes / 3) * 3; + } + } + + result = mFile->read(buffer, 1, sizebytes, bytesread); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + if (waveformat[0].format == FMOD_SOUND_FORMAT_PCM16) + { + unsigned int count; + signed short *wptr = (signed short *)buffer; + + #ifdef PLATFORM_ENDIAN_LITTLE + + if (!mLittleEndian) + { + count = *bytesread >> 1; + count >>= 2; + while (count) + { + wptr[0] = FMOD_SWAPENDIAN_WORD(wptr[0]); + wptr[1] = FMOD_SWAPENDIAN_WORD(wptr[1]); + wptr[2] = FMOD_SWAPENDIAN_WORD(wptr[2]); + wptr[3] = FMOD_SWAPENDIAN_WORD(wptr[3]); + wptr+=4; + count--; + } + + count = *bytesread >> 1; + count &= 3; + while (count) + { + wptr[0] = FMOD_SWAPENDIAN_WORD(wptr[0]); + wptr++; + count--; + } + } + + #else + + if (mLittleEndian) + { + count = *bytesread >> 1; + count >>= 2; + while (count) + { + wptr[0] = FMOD_SWAPENDIAN_WORD(wptr[0]); + wptr[1] = FMOD_SWAPENDIAN_WORD(wptr[1]); + wptr[2] = FMOD_SWAPENDIAN_WORD(wptr[2]); + wptr[3] = FMOD_SWAPENDIAN_WORD(wptr[3]); + wptr+=4; + count--; + } + + count = *bytesread >> 1; + count &= 3; + while (count) + { + wptr[0] = FMOD_SWAPENDIAN_WORD(wptr[0]); + wptr++; + count--; + } + } + + #endif + } + else if (waveformat[0].format == FMOD_SOUND_FORMAT_PCM24) + { + unsigned int count; + unsigned char tmp0, tmp1, tmp2, tmp3; + FMOD_INT24 *wptr = (FMOD_INT24 *)buffer; + + count = *bytesread / 3; + count >>= 2; + while (count) + { + tmp0 = wptr[0].val[0]; + tmp1 = wptr[1].val[0]; + tmp2 = wptr[2].val[0]; + tmp3 = wptr[3].val[0]; + + wptr[0].val[0] = wptr[0].val[2]; + wptr[1].val[0] = wptr[1].val[2]; + wptr[2].val[0] = wptr[2].val[2]; + wptr[3].val[0] = wptr[3].val[2]; + + wptr[0].val[2] = tmp0; + wptr[1].val[2] = tmp1; + wptr[2].val[2] = tmp2; + wptr[3].val[2] = tmp3; + + wptr+=4; + count--; + } + + count = *bytesread / 3; + count &= 3; + while (count) + { + tmp0 = wptr[0].val[0]; + wptr[0].val[0] = wptr[0].val[2]; + wptr[0].val[2] = tmp0; + wptr++; + count--; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecAIFF::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + int bits; + unsigned int bytes; + + result = SoundI::getBitsFromFormat(waveformat[0].format, &bits); + if (result != FMOD_OK) + { + return result; + } + + SoundI::getBytesFromSamples(position, &bytes, waveformat[0].channels, waveformat[0].format); + + result = mFile->seek(mSrcDataOffset + bytes, SEEK_SET); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecAIFF::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecAIFF *aiff = (CodecAIFF *)codec; + + return aiff->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecAIFF::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecAIFF *aiff = (CodecAIFF *)codec; + + return aiff->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecAIFF::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecAIFF *aiff = (CodecAIFF *)codec; + + return aiff->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecAIFF::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecAIFF *aiff = (CodecAIFF *)codec; + + return aiff->setPositionInternal(subsound, position, postype); +} + +} + +#endif + + diff --git a/src/fmod_codec_aiff.h b/src/fmod_codec_aiff.h new file mode 100755 index 0000000..2b089ff --- /dev/null +++ b/src/fmod_codec_aiff.h @@ -0,0 +1,111 @@ +#ifndef _FMOD_CODEC_AIFF_H +#define _FMOD_CODEC_AIFF_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_AIFF + +#include "fmod_codeci.h" +#include "fmod_types.h" + +namespace FMOD +{ + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + typedef struct + { + signed char id[4] FMOD_PACKED_INTERNAL; + int size FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFF_CHUNK; + + typedef struct + { + short numChannels FMOD_PACKED_INTERNAL; + unsigned int numSampleFrames FMOD_PACKED_INTERNAL; + short sampleSize FMOD_PACKED_INTERNAL; + unsigned char eSampleRate[10] FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFF_COMMONCHUNK; + + typedef struct + { + short numChannels FMOD_PACKED_INTERNAL; + unsigned int numSampleFrames FMOD_PACKED_INTERNAL; + short sampleSize FMOD_PACKED_INTERNAL; + unsigned char eSampleRate[10] FMOD_PACKED_INTERNAL; + char compressionid[4]; FMOD_PACKED_INTERNAL; + unsigned char compressionName[256]; FMOD_PACKED_INTERNAL; + unsigned char compressionNameLength; FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFC_COMMONCHUNK; + + typedef struct + { + unsigned int offset FMOD_PACKED_INTERNAL; + unsigned int blockSize FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFF_SOUNDDATACHUNK; + + typedef struct + { + short id FMOD_PACKED_INTERNAL; + unsigned int position FMOD_PACKED_INTERNAL; + unsigned char markerlength FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFF_MARKER; + + typedef struct + { + short playMode FMOD_PACKED_INTERNAL; + short beginLoop FMOD_PACKED_INTERNAL; + short endLoop FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFF_LOOP; + + typedef struct + { + char baseNote FMOD_PACKED_INTERNAL; + char detune FMOD_PACKED_INTERNAL; + char lowNote FMOD_PACKED_INTERNAL; + char highNote FMOD_PACKED_INTERNAL; + char lowVelocity FMOD_PACKED_INTERNAL; + char highVelocity FMOD_PACKED_INTERNAL; + short gain FMOD_PACKED_INTERNAL; + AIFF_LOOP sustainLoop FMOD_PACKED_INTERNAL; + AIFF_LOOP releaseLoop FMOD_PACKED_INTERNAL; + } FMOD_PACKED AIFF_INSTRUMENTCHUNK; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + + class CodecAIFF : public Codec + { + private: + + bool mIsAIFC; + bool mLittleEndian; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_AIFF */ + +#endif + + diff --git a/src/fmod_codec_celt.cpp b/src/fmod_codec_celt.cpp new file mode 100755 index 0000000..d5d65fe --- /dev/null +++ b/src/fmod_codec_celt.cpp @@ -0,0 +1,758 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CELT + +#include "fmod.h" +#include "fmod_codec_celt.h" +#include "fmod_soundi.h" + +extern "C" { + +void * FMOD_CELT_Calloc(int size) +{ + return FMOD_Memory_Calloc(size); +} + +} + + + + +namespace FMOD +{ + +CELTMode *CodecCELT::mCELTModeMono = 0; +CELTMode *CodecCELT::mCELTModeStereo = 0; +int CodecCELT::mCELTModeMonoRef = 0; +int CodecCELT::mCELTModeStereoRef = 0; + +FMOD_CODEC_DESCRIPTION_EX celtcodec; + +#if defined(PLUGIN_EXPORTS) && !defined(PLUGIN_FSB) + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecCELT::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecCELT::getDescriptionEx() +{ + FMOD_memset(&celtcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + celtcodec.name = "FMOD CELT Codec"; + celtcodec.version = 0x00010100; + celtcodec.timeunits = FMOD_TIMEUNIT_PCM; + celtcodec.open = &CodecCELT::openCallback; + celtcodec.close = &CodecCELT::closeCallback; + celtcodec.read = &CodecCELT::readCallback; + celtcodec.setposition = &CodecCELT::setPositionCallback; + celtcodec.init = &CodecCELT::initCallback; + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + celtcodec.getmemoryused = &CodecCELT::getMemoryUsedCallback; +#endif + + celtcodec.mSize = sizeof(CodecCELT); + + return &celtcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::createCELTModeMono() +{ + int error; + + if (!mCELTModeMono) + { + mCELTModeMono = celt_mode_create(FMOD_CELT_RATE, 1, FMOD_CELT_FRAMESIZESAMPLES, &error); + if (error != CELT_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecCELT::createCELTModeMono", "Error (%d) creating CELT mode: %d\n", error, 1)); + return FMOD_ERR_INTERNAL; + } + } + + mCELTModeMonoRef++; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::createCELTModeStereo() +{ + int error; + + if (!mCELTModeStereo) + { + mCELTModeStereo = celt_mode_create(FMOD_CELT_RATE, 2, FMOD_CELT_FRAMESIZESAMPLES, &error); + if (error != CELT_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecCELT::createCELTModeStereo", "Error creating CELT mode: %d\n", error)); + return FMOD_ERR_INTERNAL; + } + } + + mCELTModeStereoRef++; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +int CodecCELT::getBitstreamVersion() +{ + int bitstreamversion = 0; + + if (mCELTModeMono) + { + celt_mode_info(mCELTModeMono, CELT_GET_BITSTREAM_VERSION, (celt_int32_t *)&bitstreamversion); + } + else if (mCELTModeStereo) + { + celt_mode_info(mCELTModeStereo, CELT_GET_BITSTREAM_VERSION, (celt_int32_t *)&bitstreamversion); + } + + return bitstreamversion; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::CELTinit(int numstreams) +{ + mCELTDecoderBuffer = (char *)FMOD_Memory_Calloc(numstreams * FMOD_CELT_DECODERBUFFERSIZE); + if (!mCELTDecoderBuffer) + { + return FMOD_ERR_MEMORY; + } + + for (int i = 0; i < numstreams; i++) + { + mCELTDecoder[i] = fmod_celt_decoder_create_only(mCELTDecoderBuffer + i * FMOD_CELT_DECODERBUFFERSIZE); + if (!mCELTDecoder[i]) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecCELT::CELTinit", "celt_decoder_create failed\n")); + return FMOD_ERR_INTERNAL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + /* + CodecCELT for fsb only + */ + + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::closeInternal() +{ + mCELTModeMonoRef--; + if (mCELTModeMonoRef == 0) + { + celt_mode_destroy(mCELTModeMono); + } + + mCELTModeStereoRef--; + if (mCELTModeStereoRef == 0) + { + celt_mode_destroy(mCELTModeStereo); + } + + if (mCELTDecoderBuffer) + { + FMOD_Memory_Free(mCELTDecoderBuffer); + mCELTDecoderBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::decodeHeader(void *buffer, unsigned int *framesize) +{ + FMOD_CELT_FRAMEHEADER *frameheader = (FMOD_CELT_FRAMEHEADER *)buffer; + + #ifdef PLATFORM_ENDIAN_BIG + frameheader->magic = FMOD_SWAPENDIAN_DWORD(frameheader->magic); + frameheader->framesize = FMOD_SWAPENDIAN_DWORD(frameheader->framesize); + #endif + + if (frameheader->magic != FMOD_CELT_MAGIC) + { + return FMOD_ERR_FORMAT; + } + + if (framesize) + { + *framesize = frameheader->framesize; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result; + int error = 0; + unsigned char readbuffer[1024]; + unsigned int framesize; + int channel = 0; + + *bytesread = 0; + + /* + Multichannel interleaved. + */ + do + { + unsigned int bytesreadinternal = 0; + + /* + Get next valid header + */ + do + { + result = mFile->read(readbuffer, sizeof(FMOD_CELT_FRAMEHEADER), 1); + if (result != FMOD_OK) + { + return result; + } + + /* + Decode header + */ + result = decodeHeader(readbuffer, &framesize); + if (result != FMOD_OK) + { + mFile->seek(-3, SEEK_CUR); /* increment a byte at a time! */ + } + } + while (result != FMOD_OK); + + /* + Read in a frame and decode it + */ + result = mFile->read(readbuffer, framesize, 1); + if (result != FMOD_OK) + { + return result; + } + + if (waveformat[0].channels > 2) + { + short decodebuffer[FMOD_CELT_FRAMESIZESAMPLES * 2]; + + error = celt_decode(mCELTDecoder[channel], readbuffer, framesize, (celt_int16_t *)decodebuffer); + if (error != CELT_OK) + { + return FMOD_ERR_INTERNAL; + } + + /* + Interleave into pcm decode buffer + */ + short *outbuffer = (short *)buffer + channel * 2; + + for (int i = 0; i < FMOD_CELT_FRAMESIZESAMPLES * 2; i+= 2) + { + outbuffer[0] = decodebuffer[i + 0]; + outbuffer[1] = decodebuffer[i + 1]; + + outbuffer += waveformat[0].channels; + } + + SoundI::getBytesFromSamples(FMOD_CELT_FRAMESIZESAMPLES, &bytesreadinternal, 2, FMOD_SOUND_FORMAT_PCM16); + } + else + { + error = celt_decode(mCELTDecoder[channel], readbuffer, framesize, (celt_int16_t *)buffer); + if (error != CELT_OK) + { + return FMOD_ERR_INTERNAL; + } + + SoundI::getBytesFromSamples(FMOD_CELT_FRAMESIZESAMPLES, &bytesreadinternal, waveformat[0].channels, FMOD_SOUND_FORMAT_PCM16); + } + + *bytesread += bytesreadinternal; + + channel++; + } + while (channel < waveformat[0].channels / 2); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + unsigned int frame, pcmbytes, bytespersample; + unsigned int excessbytes = 0; + unsigned int raw = 0; + + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecCELT::setPositionInternal", "%d\n", position)); + + /* + set the CELT mode + */ + if (waveformat[0].channels == 1) + { + /* + Mono + */ + fmod_celt_decoder_setmode(mCELTDecoder[0], mCELTModeMono); + } + else + { + /* + Stereo or Multichannel. (Interleaved stereo pairs) + */ + for (int i = 0; i < waveformat[0].channels / 2; i++) + { + fmod_celt_decoder_setmode(mCELTDecoder[i], mCELTModeStereo); + } + } + + bytespersample = sizeof(signed short) * waveformat[0].channels; + pcmbytes = position * bytespersample; + frame = pcmbytes / mPCMFrameLengthBytes; + + if (pcmbytes) + { + unsigned int framerewind = 1; + + excessbytes = pcmbytes - (frame * mPCMFrameLengthBytes); + + if (frame < framerewind) + { + framerewind = frame; + } + + frame -= framerewind; + excessbytes += (mPCMFrameLengthBytes * framerewind); + } + else + { + excessbytes = 0; + position = 0; + } + + if (position > (excessbytes / bytespersample)) + { + int numframes = (int)((waveformat[0].lengthpcm * bytespersample) / mPCMFrameLengthBytes); + int compressedframesize = waveformat[0].lengthbytes / numframes; + raw = frame * compressedframesize; + } + else + { + raw = 0; + } + + raw += mSrcDataOffset; + + if (raw > (unsigned int)mSrcDataOffset + (unsigned int)waveformat[0].lengthbytes) + { + raw = mSrcDataOffset; + } + + result = mFile->seek(raw, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + if (!(mFlags & FMOD_CODEC_FROMFSB)) + { + mFlags |= FMOD_CODEC_SEEKING; + } + + while (excessbytes) + { + char buff[2048]; + unsigned int read = 0, toread = 2048; + + if (toread > excessbytes) + { + toread = excessbytes; + } + + result = Codec::read(buff, toread, &read); + if (result != FMOD_OK) + { + break; + } + + /* + During the first read after the seek, decode sometimes fails because the decoder + hasn't built up state yet, just push on, decoded frames are dropped anyway. + */ + if (read == 0) + { + read = toread; + } + + if (excessbytes >= read) + { + excessbytes -= read; + } + else + { + excessbytes = 0; + } + } + + mFlags &= ~FMOD_CODEC_SEEKING; + + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCELT::getMemoryUsedImpl(MemoryTracker *tracker) +{ + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCELT::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecCELT *celt = (CodecCELT *)codec; + + return celt->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCELT::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecCELT *celt = (CodecCELT *)codec; + + return celt->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCELT::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecCELT *celt = (CodecCELT *)codec; + + return celt->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCELT::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecCELT *celt = (CodecCELT *)codec; + + return celt->setPositionInternal(subsound, position, postype); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCELT::initCallback(FMOD_CODEC_STATE *codec, int numstreams) +{ + CodecCELT *celt = (CodecCELT *)codec; + + return celt->CELTinit(numstreams); +} + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCELT::getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker) +{ + CodecCELT *celt = (CodecCELT *)codec; + + return celt->getMemoryUsed(tracker); +} +#endif +} + +#endif // FMOD_SUPPORT_CELT diff --git a/src/fmod_codec_celt.h b/src/fmod_codec_celt.h new file mode 100755 index 0000000..b8dd733 --- /dev/null +++ b/src/fmod_codec_celt.h @@ -0,0 +1,119 @@ +#ifndef _FMOD_CODEC_CELT_H +#define _FMOD_CODEC_CELT_H + +/* + Changes made to CELT code: + + - inline changed to FMOD_INLINE + + - add fmod_celt_decoder_create_only() function + - add fmod_celt_decoder_setmode() function + +*/ + + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CELT + +#include "fmod_codeci.h" +#include "../lib/libcelt/celt.h" +#include "../lib/libcelt/modes.h" +#include "../lib/libcelt/rate.h" /* For MAX_PULSES */ +#include "../lib/libcelt/mdct.h" /* For mdct_lookup */ +#include "../lib/libcelt/kiss_fftr.h" /* For kiss_fftr_cfg */ + +#define FMOD_CELT_MAGIC 0xf30dc317 +#define FMOD_CELT_FRAMESIZESAMPLES 512 +#define FMOD_CELT_MAXFRAMESIZEBYTES 1024 +#define FMOD_CELT_RATE 44100 + +/* + Make sure to double check these values on each CELT update +*/ +#define FMOD_CELT_MAX_EBANDS 24 +#define FMOD_CELT_MAX_PBANDS 8 +#define FMOD_CELT_MAX_ALLOCVECTORS 12 +#define FMOD_CELT_MAX_OVERLAP 128 + +/* + CELTDecoder memory usage: + + FMOD: MemPool::alloc : 76 bytes (01A7FD40) (alloc 387) - 32 bit + or + FMOD: MemPool::alloc : 136 bytes (01A7FD40) (alloc 387) - 64 bit + + FMOD: MemPool::alloc : 5120 bytes (0214FA38) (alloc 388) + FMOD: MemPool::alloc : 192 bytes (02150E88) (alloc 389) + FMOD: MemPool::alloc : 8 bytes (0121FFB8) (alloc 386) + + = 5456 bytes, round up to 5520 bytes +*/ +#define FMOD_CELT_DECODERBUFFERSIZE 5520 + +namespace FMOD +{ + typedef struct + { + unsigned int magic; + unsigned int framesize; + + } FMOD_CELT_FRAMEHEADER; + + + class CodecCELT : public Codec + { + friend class CodecFSB; + friend class ChannelSoftware; + + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + + //char mCELTDecoderBuffer[5408]; /* For multichannel, will need multiples of this */ + + char *mCELTDecoderBuffer; + CELTDecoder *mCELTDecoder[8]; /* Need on for each stereo stream in a multichannel sound */ + + static CELTMode *mCELTModeMono; + static int mCELTModeMonoRef; + static CELTMode *mCELTModeStereo; + static int mCELTModeStereoRef; + + FMOD_CODEC_WAVEFORMAT mWaveFormat; + + FMOD_RESULT openInternal (FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal (); + FMOD_RESULT readInternal (void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal (int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + FMOD_RESULT decodeHeader (void *buffer, unsigned int *framesize); + + public: + + unsigned int mPCMFrameLengthBytes; + + FMOD_RESULT CELTinit (int numstreams); + + static FMOD_RESULT createCELTModeMono(); + static FMOD_RESULT createCELTModeStereo(); + static int getBitstreamVersion(); + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int size, unsigned int *read); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + #ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker); + #endif + + static FMOD_RESULT F_CALLBACK initCallback(FMOD_CODEC_STATE *codec, int numstreams); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif // FMOD_SUPPORT_CELT +#endif // _FMOD_CODEC_CELT_H diff --git a/src/fmod_codec_dls.cpp b/src/fmod_codec_dls.cpp new file mode 100755 index 0000000..e3dda72 --- /dev/null +++ b/src/fmod_codec_dls.cpp @@ -0,0 +1,971 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DLS + +#include "fmod.h" +#include "fmod_codec_dls.h" +#include "fmod_codec_wav.h" +#include "fmod_soundi.h" + +#include + +namespace FMOD +{ + +FMOD_CODEC_DESCRIPTION_EX dlscodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandatory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecDLS::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecDLS::getDescriptionEx() +{ + FMOD_memset(&dlscodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + dlscodec.name = "FMOD DLS Codec"; + dlscodec.version = 0x00010100; + dlscodec.timeunits = FMOD_TIMEUNIT_PCM; + dlscodec.open = &CodecDLS::openCallback; + dlscodec.close = &CodecDLS::closeCallback; + dlscodec.read = &CodecDLS::readCallback; + dlscodec.setposition = &CodecDLS::setPositionCallback; + + dlscodec.mType = FMOD_SOUND_TYPE_DLS; + dlscodec.mSize = sizeof(CodecDLS); + + return &dlscodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecDLS::parseChunk(char *parentchunk, unsigned int chunksize) +{ + unsigned int offset, fileoffset; + FMOD_RESULT result; + bool done = false; + + result = mFile->tell(&fileoffset); + if (result != FMOD_OK) + { + return result; + } + + offset = 4; + fileoffset -= sizeof(WAVE_CHUNK); + + /* + Decode chunks + */ + do + { + WAVE_CHUNK chunk; + + result = mFile->seek(fileoffset + sizeof(WAVE_CHUNK), SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecDLS::parseRIFF","chunk : id %c%c%c%c size %d\n", chunk.id[0],chunk.id[1],chunk.id[2],chunk.id[3], chunk.size)); + + /* + CHUNKS WE DONT CARE ABOUT + */ + if (!FMOD_strncmp((const char *)chunk.id, "vers", 4) || + !FMOD_strncmp((const char *)chunk.id, "msyn", 4) || + !FMOD_strncmp((const char *)chunk.id, "dlid", 4)) + { + } + + /* + NUMBER OF INSTRUMENTS CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "colh", 4)) + { + result = mFile->read(&mNumInstruments, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + + mInstrument = (CodecDLSInstrument *)FMOD_Memory_Calloc(mNumInstruments * sizeof(CodecDLSInstrument)); + if (!mInstrument) + { + return FMOD_ERR_MEMORY; + } + } + /* + POOL TABLE CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "ptbl", 4)) + { + unsigned int size; + + result = mFile->read(&size, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mNumSamples, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + waveformat = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT) * mNumSamples); + if (!waveformat) + { + return FMOD_ERR_MEMORY; + } + + mSample = (CodecDLSSample *)FMOD_Memory_Calloc(mNumSamples *sizeof(CodecDLSSample)); + if (!mSample) + { + return FMOD_ERR_MEMORY; + } + } + /* + LIST CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "LIST", 4)) + { + char listid[4]; + + result = mFile->read(listid, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + result = parseChunk(listid, chunk.size); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp((const char *)listid, "wave", 4)) + { + mSampleID++; + } + else if (!FMOD_strncmp((const char *)listid, "ins ", 4)) + { + mInstrumentID++; + if (mInstrumentID >= mNumInstruments) + { + mInstrumentID = mInstrumentID; + } + } + else if (!FMOD_strncmp((const char *)listid, "rgn ", 4)) + { + mRegionID++; + } + } + else if (!FMOD_strncmp((const char *)chunk.id, "dlid", 4)) + { + FMOD_GUID dlsid; + + result = mFile->read(&dlsid, 1, sizeof(FMOD_GUID), 0); + if (result != FMOD_OK) + { + return result; + } + } + else if (!FMOD_strncmp((const char *)chunk.id, "insh", 4)) + { + result = mFile->read(&mInstrument[mInstrumentID].mHeader, 1, sizeof(DLS_INSTRUMENTHEADER), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + mInstrument[mInstrumentID].mHeader.cRegions = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mHeader.cRegions); + mInstrument[mInstrumentID].mHeader.Locale.ulBank = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mHeader.Locale.ulBank); + mInstrument[mInstrumentID].mHeader.Locale.ulInstrument = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mHeader.Locale.ulInstrument); + #endif + + mInstrument[mInstrumentID].mRegion = (CodecDLSRegion *)FMOD_Memory_Calloc(sizeof(CodecDLSRegion) * mInstrument[mInstrumentID].mHeader.cRegions); + if (!mInstrument[mInstrumentID].mRegion) + { + return FMOD_ERR_MEMORY; + } + mRegionID = 0; + } + else if (!FMOD_strncmp((const char *)chunk.id, "rgnh", 4)) + { + result = mFile->read(&mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader, 1, sizeof(DLS_REGIONHEADER), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeKey.usLow = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeKey.usLow); + mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeKey.usHigh = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeKey.usHigh); + mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeVelocity.usLow = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeVelocity.usLow); + mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeVelocity.usHigh = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.RangeVelocity.usHigh); + mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.fusOptions = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.fusOptions); + mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.usKeyGroup = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mRegionHeader.usKeyGroup); + #endif + } + else if (!FMOD_strncmp((const char *)chunk.id, "wsmp", 4)) + { + unsigned int size = sizeof(DLS_WAVESAMPLE); + + if (chunk.size < size) + { + size = chunk.size; + } + + if (!FMOD_strncmp(parentchunk, "wave", 4)) + { + result = mFile->read(&mSample[mSampleID].mWaveSample, 1, size, 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + mSample[mSampleID].mWaveSample.cbSize = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.cbSize); + mSample[mSampleID].mWaveSample.usUnityNote = FMOD_SWAPENDIAN_WORD(mSample[mSampleID].mWaveSample.usUnityNote); + mSample[mSampleID].mWaveSample.sFineTune = FMOD_SWAPENDIAN_WORD(mSample[mSampleID].mWaveSample.sFineTune); + mSample[mSampleID].mWaveSample.lAttenuation = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.lAttenuation); + mSample[mSampleID].mWaveSample.fulOptions = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.fulOptions); + #endif + + if (mSample[mSampleID].mWaveSample.cSampleLoops) + { + #ifdef PLATFORM_ENDIAN_BIG + mSample[mSampleID].mWaveSample.loop[0].cbSize = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.loop[0].cbSize); + mSample[mSampleID].mWaveSample.loop[0].ulLoopType = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.loop[0].ulLoopType); + mSample[mSampleID].mWaveSample.loop[0].ulLoopStart = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.loop[0].ulLoopStart); + mSample[mSampleID].mWaveSample.loop[0].ulLoopLength = FMOD_SWAPENDIAN_DWORD(mSample[mSampleID].mWaveSample.loop[0].ulLoopLength); + #endif + + waveformat[mSampleID].mode = FMOD_LOOP_NORMAL; + waveformat[mSampleID].loopstart = mSample[mSampleID].mWaveSample.loop[0].ulLoopStart; + waveformat[mSampleID].loopend = mSample[mSampleID].mWaveSample.loop[0].ulLoopStart + + mSample[mSampleID].mWaveSample.loop[0].ulLoopLength - 1; + } + } + else if (!FMOD_strncmp(parentchunk, "rgn ", 4)) + { + result = mFile->read(&mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample, 1, size, 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.cbSize = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.cbSize); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.usUnityNote = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.usUnityNote); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.sFineTune = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.sFineTune); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.lAttenuation = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.lAttenuation); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.fulOptions = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.fulOptions); + + if (mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.cSampleLoops) + { + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].cbSize = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].cbSize); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].ulLoopType = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].ulLoopType); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].ulLoopStart = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].ulLoopStart); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].ulLoopLength = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveSample.loop[0].ulLoopLength); + } + #endif + } + } + else if (!FMOD_strncmp((const char *)chunk.id, "wlnk", 4)) + { + result = mFile->read(&mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink, 1, sizeof(DLS_WAVELINK), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.fusOptions = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.fusOptions); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.usPhaseGroup = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.usPhaseGroup); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.ulChannel = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.ulChannel); + mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.ulTableIndex = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mWaveLink.ulTableIndex); + #endif + } + else if (!FMOD_strncmp((const char *)chunk.id, "art1", 4)) + { + struct + { + unsigned int cbSize; + unsigned int cConnectionBlocks; + } articulator; + + result = mFile->read(&articulator, 1, sizeof(articulator), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + articulator.cbSize = FMOD_SWAPENDIAN_DWORD(articulator.cbSize); + articulator.cConnectionBlocks = FMOD_SWAPENDIAN_DWORD(articulator.cConnectionBlocks); + #endif + + if (articulator.cbSize > sizeof(articulator)) + { + mFile->seek(articulator.cbSize - sizeof(articulator), SEEK_CUR); + } + + if (mRegionID < mInstrument[mInstrumentID].mHeader.cRegions) + { + mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock = (DLS_CONNECTIONBLOCK *)FMOD_Memory_Calloc(sizeof(DLS_CONNECTIONBLOCK) * articulator.cConnectionBlocks); + if (!mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock) + { + return FMOD_ERR_MEMORY; + } + + mInstrument[mInstrumentID].mRegion[mRegionID].mNumConnectionBlocks = articulator.cConnectionBlocks; + + result = mFile->read(mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock, 1, sizeof(DLS_CONNECTIONBLOCK) * articulator.cConnectionBlocks, 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + { + unsigned int count; + + for (count = 0; count < articulator.cConnectionBlocks; count++) + { + mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usSource = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usSource); + mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usControl = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usControl); + mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usDestination = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usDestination); + mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usTransform = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].usTransform); + mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].lScale = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mRegion[mRegionID].mConnectionBlock[count].lScale); + } + } + #endif + } + else + { + mInstrument[mInstrumentID].mConnectionBlock = (DLS_CONNECTIONBLOCK *)FMOD_Memory_Calloc(sizeof(DLS_CONNECTIONBLOCK) * articulator.cConnectionBlocks); + if (!mInstrument[mInstrumentID].mConnectionBlock) + { + return FMOD_ERR_MEMORY; + } + + mInstrument[mInstrumentID].mNumConnectionBlocks = articulator.cConnectionBlocks; + + result = mFile->read(mInstrument[mInstrumentID].mConnectionBlock, 1, sizeof(DLS_CONNECTIONBLOCK) * articulator.cConnectionBlocks, 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + unsigned int count; + + for (count = 0; count < articulator.cConnectionBlocks; count++) + { + mInstrument[mInstrumentID].mConnectionBlock[count].usSource = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mConnectionBlock[count].usSource); + mInstrument[mInstrumentID].mConnectionBlock[count].usControl = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mConnectionBlock[count].usControl); + mInstrument[mInstrumentID].mConnectionBlock[count].usDestination = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mConnectionBlock[count].usDestination); + mInstrument[mInstrumentID].mConnectionBlock[count].usTransform = FMOD_SWAPENDIAN_WORD(mInstrument[mInstrumentID].mConnectionBlock[count].usTransform); + mInstrument[mInstrumentID].mConnectionBlock[count].lScale = FMOD_SWAPENDIAN_DWORD(mInstrument[mInstrumentID].mConnectionBlock[count].lScale); + } + #endif + } + } + /* + FORMAT CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "fmt ", 4)) + { + WAVE_FORMATEXTENSIBLE srcformat; + unsigned int size = chunk.size; + + /* Reduced allocs here and used a local instance of waveformatextensible instead */ + if (size > sizeof(WAVE_FORMATEXTENSIBLE)) + { + size = sizeof(WAVE_FORMATEXTENSIBLE); + } + + FMOD_memset(&srcformat, 0, sizeof(WAVE_FORMATEXTENSIBLE)); + result = mFile->read(&srcformat, 1, size, 0); + if (result != FMOD_OK) + { + return result; + } + + if (chunk.size > sizeof(WAVE_FORMATEXTENSIBLE)) + { + result = mFile->seek(chunk.size - sizeof(WAVE_FORMATEXTENSIBLE), SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + + #ifdef PLATFORM_ENDIAN_BIG + srcformat.Format.wFormatTag = FMOD_SWAPENDIAN_WORD (srcformat.Format.wFormatTag); + srcformat.Format.nChannels = FMOD_SWAPENDIAN_WORD (srcformat.Format.nChannels); + srcformat.Format.nSamplesPerSec = FMOD_SWAPENDIAN_DWORD(srcformat.Format.nSamplesPerSec); + srcformat.Format.nAvgBytesPerSec = FMOD_SWAPENDIAN_DWORD(srcformat.Format.nAvgBytesPerSec); + srcformat.Format.nBlockAlign = FMOD_SWAPENDIAN_WORD (srcformat.Format.nBlockAlign); + srcformat.Format.wBitsPerSample = FMOD_SWAPENDIAN_WORD (srcformat.Format.wBitsPerSample); + srcformat.Format.cbSize = FMOD_SWAPENDIAN_WORD (srcformat.Format.cbSize); + srcformat.Samples.wValidBitsPerSample = FMOD_SWAPENDIAN_WORD (srcformat.Samples.wValidBitsPerSample); + srcformat.dwChannelMask = FMOD_SWAPENDIAN_DWORD(srcformat.dwChannelMask); + #endif + + switch (srcformat.Format.wBitsPerSample) + { + case 4: + { + if (srcformat.Format.wFormatTag == 0x6666) + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_VAG; + } + else if (srcformat.Format.wFormatTag == 0x7777) + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_GCADPCM; + } + break; + } + case 8: + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_PCM8; + break; + } + case 16: + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_PCM16; + break; + } + case 24: + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_PCM24; + break; + } + case 32: + { + if (srcformat.Format.wFormatTag == WAVE_FORMAT_PCM) + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_PCM32; + } + else if (srcformat.Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) + { + waveformat[mSampleID].format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + break; + } + } + waveformat[mSampleID].channels = srcformat.Format.nChannels; + waveformat[mSampleID].frequency = srcformat.Format.nSamplesPerSec; + waveformat[mSampleID].blockalign = srcformat.Format.nBlockAlign; + } + /* + DATA CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "data", 4)) + { + SoundI::getSamplesFromBytes(chunk.size, &waveformat[mSampleID].lengthpcm, waveformat[mSampleID].channels, waveformat[mSampleID].format); + + result = mFile->tell(&mSample[mSampleID].mDataOffset); + if (result != FMOD_OK) + { + return result; + } + } + else if (!FMOD_strncmp((const char *)chunk.id, "INAM", 4)) + { + if (mInstrumentID < mNumInstruments) + { + FMOD_memset(mInstrument[mInstrumentID].mName, 0, FMOD_STRING_MAXNAMELEN); + + result = mFile->read(mInstrument[mInstrumentID].mName, 1, chunk.size, 0); + if (result != FMOD_OK) + { + return result; + } + } + else if (mSampleID < mNumSamples) + { + FMOD_memset(mSample[mSampleID].mName, 0, FMOD_STRING_MAXNAMELEN); + + result = mFile->read(mSample[mSampleID].mName, 1, chunk.size, 0); + if (result != FMOD_OK) + { + return result; + } + + FMOD_strncpy(waveformat[mSampleID].name, mSample[mSampleID].mName, FMOD_STRING_MAXNAMELEN); + } + } + else if (!FMOD_strncmp((const char *)chunk.id, "IARL", 4) || + !FMOD_strncmp((const char *)chunk.id, "IART", 4) || + !FMOD_strncmp((const char *)chunk.id, "ICMS", 4) || + !FMOD_strncmp((const char *)chunk.id, "ICMT", 4) || + !FMOD_strncmp((const char *)chunk.id, "ICOP", 4) || + !FMOD_strncmp((const char *)chunk.id, "ICRD", 4) || + !FMOD_strncmp((const char *)chunk.id, "IENG", 4) || + !FMOD_strncmp((const char *)chunk.id, "IGNR", 4) || + !FMOD_strncmp((const char *)chunk.id, "IKEY", 4) || + !FMOD_strncmp((const char *)chunk.id, "IMED", 4) || + !FMOD_strncmp((const char *)chunk.id, "IPRD", 4) || + !FMOD_strncmp((const char *)chunk.id, "ISBJ", 4) || + !FMOD_strncmp((const char *)chunk.id, "ISFT", 4) || + !FMOD_strncmp((const char *)chunk.id, "ISRC", 4) || + !FMOD_strncmp((const char *)chunk.id, "ISRF", 4) || + !FMOD_strncmp((const char *)chunk.id, "ITCH", 4)) + { + result = result; + } + else + { + mFile->seek(chunk.size, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + + offset += (chunk.size+sizeof(WAVE_CHUNK)); + fileoffset += (chunk.size+sizeof(WAVE_CHUNK)); + + if (chunk.size & 1) + { + offset++; + fileoffset++; + } + + if (chunk.size < 0) + { + break; + } + + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecDLS::parseRIFF", "offset = %d / %d\n", offset, chunksize)); + + } while (offset < chunksize && offset > 0 && !done); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecDLS::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + WAVE_CHUNK chunk; + char dls[4]; + + init(FMOD_SOUND_TYPE_DLS); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecDLS::openInternal", "attempting to open as DLS..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + mNumInstruments = 0; + mNumSamples = 0; + + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp((const char *)chunk.id, "RIFF", 4)) + { + return FMOD_ERR_FORMAT; + } + + result = mFile->read(dls, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp(dls, "DLS ", 4)) + { + return FMOD_ERR_FORMAT; + } + + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + mSrcDataOffset = 0; + mSampleID = 0; + + result = parseChunk(dls, chunk.size); + if (result != FMOD_OK) + { + return result; + } + + if (mNumInstruments <= 0) + { + return FMOD_ERR_FORMAT; + } + + numsubsounds = mNumSamples; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecDLS::closeInternal() +{ + if (waveformat) + { + FMOD_Memory_Free(waveformat); + waveformat = 0; + } + + if (mInstrument) + { + int count; + + for (count = 0; count < mNumInstruments; count++) + { + if (mInstrument[count].mRegion) + { + unsigned int region; + + /* + Look for special drum articulators. + */ + for (region = 0; region < mInstrument[count].mHeader.cRegions; region++) + { + if (mInstrument[count].mRegion[region].mConnectionBlock) + { + FMOD_Memory_Free(mInstrument[count].mRegion[region].mConnectionBlock); + } + } + + FMOD_Memory_Free(mInstrument[count].mRegion); + } + if (mInstrument[count].mConnectionBlock) + { + FMOD_Memory_Free(mInstrument[count].mConnectionBlock); + } + } + + FMOD_Memory_Free(mInstrument); + mInstrument = 0; + } + + if (mSample) + { + FMOD_Memory_Free(mSample); + mSample = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecDLS::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + + result = mFile->read(buffer, 1, sizebytes, bytesread); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + /* + PCM8 is stored in unsigned 8-bit, so convert it to signed 8-bit for FMOD + */ + if (waveformat[mCurrentIndex].format == FMOD_SOUND_FORMAT_PCM8) + { + unsigned char *wptr = (unsigned char *)buffer; + + for (unsigned int count = 0; count < *bytesread; count++) + { + *wptr++ ^= 128; + } + } + else /* Must be PCM16 */ + { + #ifdef PLATFORM_ENDIAN_BIG + { + signed short *wptr = (signed short *)buffer; + + for (unsigned int count = 0; count < *bytesread >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + + } + #endif + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecDLS::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + + if (subsound < 0 || (numsubsounds && subsound >= numsubsounds)) + { + return FMOD_ERR_INVALID_POSITION; + } + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + unsigned int posbytes; + + if (subsound != mCurrentIndex) + { + mCurrentIndex = subsound; + } + + result = SoundI::getBytesFromSamples(position, &posbytes, waveformat[mCurrentIndex].channels, waveformat[mCurrentIndex].format); + if (result != FMOD_OK) + { + return result; + } + + posbytes += mSample[mCurrentIndex].mDataOffset; + + result = mFile->seek(posbytes, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecDLS::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecDLS *dls = (CodecDLS *)codec; + + return dls->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecDLS::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecDLS *dls = (CodecDLS *)codec; + + return dls->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecDLS::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecDLS *dls = (CodecDLS *)codec; + + return dls->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecDLS::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecDLS *dls = (CodecDLS *)codec; + + return dls->setPositionInternal(subsound, position, postype); +} + +} + +#endif + diff --git a/src/fmod_codec_dls.h b/src/fmod_codec_dls.h new file mode 100755 index 0000000..cc81a28 --- /dev/null +++ b/src/fmod_codec_dls.h @@ -0,0 +1,252 @@ +#ifndef _FMOD_CODEC_DLS_H +#define _FMOD_CODEC_DLS_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DLS + +#include "fmod_codeci.h" + +namespace FMOD +{ + /////////////////////////////////////////////////////////////////////////// + // Articulation connection graph definitions + /////////////////////////////////////////////////////////////////////////// + // Generic Sources + #define CONN_SRC_NONE 0x0000 + #define CONN_SRC_LFO 0x0001 + #define CONN_SRC_KEYONVELOCITY 0x0002 + #define CONN_SRC_KEYNUMBER 0x0003 + #define CONN_SRC_EG1 0x0004 + #define CONN_SRC_EG2 0x0005 + #define CONN_SRC_PITCHWHEEL 0x0006 + + // Midi Controllers 0-127 + #define CONN_SRC_CC1 0x0081 // mod wheel + #define CONN_SRC_CC7 0x0087 // Channel volume + #define CONN_SRC_CC10 0x008a // Pan + #define CONN_SRC_CC11 0x008b // Expression. + + // Generic Destinations + #define CONN_DST_NONE 0x0000 + #define CONN_DST_ATTENUATION 0x0001 + #define CONN_DST_RESERVED 0x0002 + #define CONN_DST_PITCH 0x0003 + #define CONN_DST_PAN 0x0004 + + // LFO Destinations + #define CONN_DST_LFO_FREQUENCY 0x0104 + #define CONN_DST_LFO_STARTDELAY 0x0105 + + // EG1 Destinations + #define CONN_DST_EG1_ATTACKTIME 0x0206 + #define CONN_DST_EG1_DECAYTIME 0x0207 + #define CONN_DST_EG1_RESERVED 0x0208 + #define CONN_DST_EG1_RELEASETIME 0x0209 + #define CONN_DST_EG1_SUSTAINLEVEL 0x020a + + // EG2 Destinations + #define CONN_DST_EG2_ATTACKTIME 0x030a + #define CONN_DST_EG2_DECAYTIME 0x030b + #define CONN_DST_EG2_RESERVED 0x030c + #define CONN_DST_EG2_RELEASETIME 0x030d + #define CONN_DST_EG2_SUSTAINLEVEL 0x030e + + // Envelope transform type + #define CONN_TRN_NONE 0x0000 + #define CONN_TRN_CONCAVE 0x0001 + + #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 + + typedef enum + { + CONN_SRC_FLAG_NONE = 0x0001, + CONN_SRC_FLAG_LFO = 0x0002, + CONN_SRC_FLAG_KEYONVELOCITY = 0x0004, + CONN_SRC_FLAG_KEYNUMBER = 0x0008, + CONN_SRC_FLAG_EG1 = 0x0010, + CONN_SRC_FLAG_EG2 = 0x0020, + CONN_SRC_FLAG_PITCHWHEEL = 0x0040, + CONN_SRC_FLAG_MODWHEEL = 0x0080, + CONN_SRC_FLAG_CHANNELVOL = 0x0100, + CONN_SRC_FLAG_PAN = 0x0200, + CONN_SRC_FLAG_EXPRESSION = 0x0400 + } CONN_SRC_FLAGS; + + #define CONN_SRC_FLAG_COMMON (CONN_SRC_FLAGS)(CONN_SRC_FLAG_NONE | \ + CONN_SRC_FLAG_KEYONVELOCITY | \ + CONN_SRC_FLAG_KEYNUMBER | \ + CONN_SRC_FLAG_PITCHWHEEL | \ + CONN_SRC_FLAG_MODWHEEL | \ + CONN_SRC_FLAG_CHANNELVOL | \ + CONN_SRC_FLAG_PAN | \ + CONN_SRC_FLAG_EXPRESSION) + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + typedef struct + { + unsigned int ulBank FMOD_PACKED_INTERNAL; + unsigned int ulInstrument FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_MIDILOCALE; + + typedef struct + { + unsigned int cRegions FMOD_PACKED_INTERNAL; + DLS_MIDILOCALE Locale FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_INSTRUMENTHEADER; + + + typedef struct _RGNRANGE + { + unsigned short usLow FMOD_PACKED_INTERNAL; /* Low Value of Range */ + unsigned short usHigh FMOD_PACKED_INTERNAL; /* High Value of Range*/ + } FMOD_PACKED DLS_RGNRANGE; + + typedef struct + { + DLS_RGNRANGE RangeKey FMOD_PACKED_INTERNAL; + DLS_RGNRANGE RangeVelocity FMOD_PACKED_INTERNAL; + unsigned short fusOptions FMOD_PACKED_INTERNAL; + unsigned short usKeyGroup FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_REGIONHEADER; + + typedef struct + { + unsigned short fusOptions FMOD_PACKED_INTERNAL; + unsigned short usPhaseGroup FMOD_PACKED_INTERNAL; + unsigned int ulChannel FMOD_PACKED_INTERNAL; + unsigned int ulTableIndex FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_WAVELINK; + + typedef struct + { + unsigned int cbSize FMOD_PACKED_INTERNAL; + unsigned int ulLoopType FMOD_PACKED_INTERNAL; + unsigned int ulLoopStart FMOD_PACKED_INTERNAL; + unsigned int ulLoopLength FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_WAVESAMPLELOOP; + + typedef struct + { + unsigned int cbSize FMOD_PACKED_INTERNAL; + unsigned short usUnityNote FMOD_PACKED_INTERNAL; + signed short sFineTune FMOD_PACKED_INTERNAL; + signed int lAttenuation FMOD_PACKED_INTERNAL; + unsigned int fulOptions FMOD_PACKED_INTERNAL; + unsigned int cSampleLoops FMOD_PACKED_INTERNAL; + DLS_WAVESAMPLELOOP loop[1] FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_WAVESAMPLE; + + typedef struct + { + unsigned short usSource FMOD_PACKED_INTERNAL; + unsigned short usControl FMOD_PACKED_INTERNAL; + unsigned short usDestination FMOD_PACKED_INTERNAL; + unsigned short usTransform FMOD_PACKED_INTERNAL; + int lScale FMOD_PACKED_INTERNAL; + } FMOD_PACKED DLS_CONNECTIONBLOCK; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + + typedef struct + { + float mTime; + float mSrcValue, mDestValue; + } CodecDLSEnvelopePoint; + + typedef enum + { + CODEC_DLS_ENVPOINT_ATTACK, + CODEC_DLS_ENVPOINT_DECAY, + CODEC_DLS_ENVPOINT_RELEASE, + CODEC_DLS_ENVPOINT_MAX + } CODEC_DLS_ENVPOINT; + + class CodecDLSEnvelope + { + public: + + CodecDLSEnvelopePoint mPoint[CODEC_DLS_ENVPOINT_MAX]; /* Attack, decay, release. Sustain is the point decay ends. */ + + CODEC_DLS_ENVPOINT mPosition; + float mTime; + float mSustain; + float mRange; + bool mActive; + }; + + + class CodecDLSRegion + { + public: + DLS_REGIONHEADER mRegionHeader; + DLS_WAVESAMPLE mWaveSample; + DLS_WAVELINK mWaveLink; + int mNumConnectionBlocks; + DLS_CONNECTIONBLOCK *mConnectionBlock; + }; + + class CodecDLSInstrument + { + public: + char mName[FMOD_STRING_MAXNAMELEN]; + DLS_INSTRUMENTHEADER mHeader; + CodecDLSRegion *mRegion; + int mNumConnectionBlocks; + DLS_CONNECTIONBLOCK *mConnectionBlock; + }; + + class CodecDLSSample + { + public: + char mName[FMOD_STRING_MAXNAMELEN]; + unsigned int mDataOffset; + DLS_WAVESAMPLE mWaveSample; + }; + + class CodecDLS : public Codec + { + public: + + int mNumInstruments; + int mInstrumentID; + CodecDLSInstrument *mInstrument; + + int mNumSamples; + int mSampleID; + CodecDLSSample *mSample; + + int mCurrentIndex; /* current DLS index */ + unsigned int mRegionID; /* This should always be 0-15 with melodic instruments. */ + + FMOD_RESULT parseChunk(char *parentchunk, unsigned int chunksize); + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_DLS */ + +#endif + diff --git a/src/fmod_codec_flac.cpp b/src/fmod_codec_flac.cpp new file mode 100755 index 0000000..794d3fa --- /dev/null +++ b/src/fmod_codec_flac.cpp @@ -0,0 +1,787 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FLAC + +#include "fmod.h" +#include "fmod_codec_flac.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#include +#include + + +#define FMOD_FLAC_MAX_BLOCKSIZE 8192 // Theoretically, this should be 65536 but that's way too much memory and + // hopefully we won't get blocks that big in real life + + +#ifdef PLUGIN_EXPORTS +extern "C" +{ +void * FMOD_FLAC_Malloc(void *context, int size) +{ + void *mem = FMOD_Memory_Alloc(size); + + if (mem) + { + FMOD::CodecFLAC *flac = (FMOD::CodecFLAC *)context; + + flac->mMemUsed += size; + } + + return mem; +} +void * FMOD_FLAC_Calloc(void *context, int count, int size) +{ + void *mem = FMOD_Memory_Calloc(count * size); + + if (mem) + { + FMOD::CodecFLAC *flac = (FMOD::CodecFLAC *)context; + flac->mMemUsed += (count * size); + } + + return mem; +} +void * FMOD_FLAC_ReAlloc(void *context, void *ptr, int size) +{ + void *mem; + FMOD::CodecFLAC *flac = (FMOD::CodecFLAC *)context; + + if (ptr) + { + FMOD::MemBlockHeader *block = (FMOD::MemBlockHeader *)ptr; + + block--; + + flac->mMemUsed -= block->mSize; + } + + mem = FMOD_Memory_ReAlloc(ptr, size); + if (mem) + { + flac->mMemUsed += size; + } + + return mem; +} +void FMOD_FLAC_Free(void *context, void *ptr) +{ + if (ptr) + { + FMOD::MemBlockHeader *block = (FMOD::MemBlockHeader *)ptr; + FMOD::CodecFLAC *flac = (FMOD::CodecFLAC *)context; + + block--; + + flac->mMemUsed -= block->mSize; + } + + FMOD_Memory_Free(ptr); +} +} +#endif + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX flaccodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandatory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecFLAC::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +static FLAC__StreamDecoderReadStatus FMOD_FLAC_ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FMOD_RESULT result; + unsigned int rd; + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + result = codec->mFile->read(buffer, 1, (unsigned int)*bytes, &rd); + *bytes = rd; + + if (!*bytes) + { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + + +static FLAC__StreamDecoderWriteStatus FMOD_FLAC_WriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + CodecFLAC *codec; + int i, j; + + codec = (CodecFLAC *)client_data; + + if (codec->mPCMBuffer) + { + int blocksize = (frame->header.blocksize > FMOD_FLAC_MAX_BLOCKSIZE) ? FMOD_FLAC_MAX_BLOCKSIZE : frame->header.blocksize; + + if (frame->header.bits_per_sample == 8) + { + signed char *dst = (signed char *)codec->mPCMBuffer; + + for (i = 0; i < blocksize; i++) + { + for (j=0; j < (int)frame->header.channels; j++) + { + *dst++ = ((signed char)buffer[j][i]); + } + } + + codec->mPCMBufferFilledBytes = blocksize * frame->header.channels; + } + else if (frame->header.bits_per_sample == 16) + { + signed short *dst = (signed short *)codec->mPCMBuffer; + + for (i=0; i < blocksize; i++) + { + for (j=0; j < (int)frame->header.channels; j++) + { + *dst++ = (signed short)buffer[j][i]; + } + } + + codec->mPCMBufferFilledBytes = blocksize * frame->header.channels * 2; + } + else if (frame->header.bits_per_sample == 24) + { + signed char *dst = (signed char *)codec->mPCMBuffer; + + for (i = 0; i < blocksize; i++) + { + for (j = 0; j < (int)frame->header.channels; j++) + { + FMOD_memcpy(dst, &buffer[j][i], 3); + dst += 3; + } + } + + codec->mPCMBufferFilledBytes = blocksize * frame->header.channels * 3; + } + else + { + //printf("Unhandled bitdepth %d\n", frame->header.bits_per_sample); + } + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + + +static FLAC__StreamDecoderSeekStatus FMOD_FLAC_SeekCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + if (codec->mFile->seek((int)absolute_byte_offset, SEEK_SET) == FMOD_OK) + { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } + else + { + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } +} + + +static FLAC__StreamDecoderTellStatus FMOD_FLAC_TellCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + unsigned int pos; + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + if (codec->mFile->tell(&pos) == FMOD_OK) + { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } + else + { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } +} + + +static FLAC__StreamDecoderLengthStatus FMOD_FLAC_LengthCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + unsigned int len; + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + if (codec->mFile->getSize(&len) == FMOD_OK) + { + *stream_length = (FLAC__uint64)len; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } + else + { + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } +} + + +static FLAC__bool FMOD_FLAC_EofCallback(const FLAC__StreamDecoder *decoder, void *client_data) +{ + unsigned int pos, len; + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + codec->mFile->tell(&pos); + codec->mFile->getSize(&len); + + return (pos >= len); +} + + +static void FMOD_FLAC_ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + switch (status) + { + /**< An error in the stream caused the decoder to lose synchronization. */ + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC : + break; + + /**< The decoder encountered a corrupted frame header. */ + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER : + break; + + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH : + break; + } +} + + +static void FMOD_FLAC_MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + CodecFLAC *codec; + + codec = (CodecFLAC *)client_data; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) + { + FMOD_RESULT result; + + result = SoundI::getFormatFromBits(metadata->data.stream_info.bits_per_sample, &codec->waveformat[0].format); + if (result != FMOD_OK) + { + return; + } + codec->waveformat[0].channels = metadata->data.stream_info.channels; + codec->waveformat[0].frequency = metadata->data.stream_info.sample_rate; + codec->waveformat[0].lengthpcm = (unsigned int)metadata->data.stream_info.total_samples; + } + else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) + { + int i; + char *name, *value, buf[4096]; + + for (i=0;i < (int)metadata->data.vorbis_comment.num_comments;i++) + { + if (metadata->data.vorbis_comment.comments[i].length > 4095) + { + continue; + } + + FMOD_memcpy(buf, metadata->data.vorbis_comment.comments[i].entry, metadata->data.vorbis_comment.comments[i].length); + buf[metadata->data.vorbis_comment.comments[i].length] = 0; + + name = buf; + value = name; + for (;value && (*value != '=');value++) ; + *value++ = 0; + + codec->metadata(codec, FMOD_TAGTYPE_VORBISCOMMENT, name, value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, false); + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecFLAC::getDescriptionEx() +{ + FMOD_memset(&flaccodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + flaccodec.name = "FMOD FLAC Codec"; + flaccodec.version = 0x00010100; + flaccodec.timeunits = FMOD_TIMEUNIT_PCM; + flaccodec.open = &CodecFLAC::openCallback; + flaccodec.close = &CodecFLAC::closeCallback; + flaccodec.read = &CodecFLAC::readCallback; + flaccodec.setposition = &CodecFLAC::setPositionCallback; + + flaccodec.mType = FMOD_SOUND_TYPE_FLAC; + flaccodec.mSize = sizeof(CodecFLAC); + + return &flaccodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFLAC::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + unsigned int rd; + char header[4]; + FMOD_RESULT result = FMOD_OK; + int bits; + + init(FMOD_SOUND_TYPE_FLAC); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFLAC::openInternal", "attempting to open as FLAC..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Quick format check + */ + result = mFile->read(header, 1, 4, &rd); + if (result != FMOD_OK) + { + return result; + } + + if (rd != 4) + { + return FMOD_ERR_FILE_BAD; + } + + if ((header[0] == 'f') && + (header[1] == 'L') && + (header[2] == 'a') && + (header[3] == 'C')) + { + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + else + { + return FMOD_ERR_FORMAT; + } + + mDecoder = FLAC__stream_decoder_new(); + if (!mDecoder) + { + return FMOD_ERR_FILE_BAD; + } + + if (!FLAC__stream_decoder_set_md5_checking(mDecoder, false)) + { + return FMOD_ERR_FILE_BAD; + } + + if (!FLAC__stream_decoder_set_metadata_respond(mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT)) + { + return FMOD_ERR_FILE_BAD; + } + + if (FLAC__stream_decoder_init_stream(this, + mDecoder, + FMOD_FLAC_ReadCallback, + FMOD_FLAC_SeekCallback, + FMOD_FLAC_TellCallback, + FMOD_FLAC_LengthCallback, + FMOD_FLAC_EofCallback, + FMOD_FLAC_WriteCallback, + FMOD_FLAC_MetadataCallback, + FMOD_FLAC_ErrorCallback, + this) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + { + return FMOD_ERR_FILE_BAD; + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + waveformat = mWaveFormatMemory; + + /* + Get the first block which is guaranteed to be a STREAMINFO metadata block + */ + FLAC__stream_decoder_process_until_end_of_metadata(this, mDecoder); + + /* + Get size of file in bytes + */ + result = mFile->getSize(&waveformat[0].lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + result = SoundI::getBitsFromFormat(waveformat[0].format, &bits); + if (result != FMOD_OK) + { + return result; + } + + result = SoundI::getBytesFromSamples(FMOD_FLAC_MAX_BLOCKSIZE, &mPCMBufferLengthBytes, waveformat[0].channels, waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + + if (mPCMBufferLengthBytes) + { + #ifdef PLATFORM_PS3 + + mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes + 16); + if (!mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mPCMBufferMemory, 16); + + #else + + mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes); + if (!mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mPCMBuffer = mPCMBufferMemory; + + #endif + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFLAC::closeInternal() +{ + if (mDecoder) + { + FLAC__stream_decoder_finish(this, mDecoder); + FLAC__stream_decoder_delete(this, mDecoder); + mDecoder = 0; + } + + if (mPCMBufferMemory) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFLAC::release", "Free PCM Buffer\n")); + + FMOD_Memory_Free(mPCMBufferMemory); + mPCMBuffer = mPCMBufferMemory = 0; + } + mPCMBufferLengthBytes = 0; + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + waveformat = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFLAC::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + FLAC__StreamDecoderState state; + + if (!mDecoder) + { + return FMOD_ERR_INVALID_PARAM; + } + + // Only get a new frame if there isn't one ready and waiting + if (!mFrameReady) + { + FLAC__stream_decoder_process_single(this, mDecoder); + } + + // These variables were already set directly via the FLAC Write Callback, but pass them + // back here for consistency + buffer = mPCMBuffer; + *bytesread = mPCMBufferFilledBytes; + mFrameReady = false; + + state = FLAC__stream_decoder_get_state(mDecoder); + if (state == FLAC__STREAM_DECODER_END_OF_STREAM) + { + *bytesread = 0; + return FMOD_ERR_FILE_EOF; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFLAC::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + if (!mDecoder) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + /* + NOTE! I think there is a stack corruption in this function. + previously there was an 'FMOD_RESULT result' variable that was stored in a register (esi) + which was then pushed onto the stack before calling this function. + When the stack was popped back into esi, the number was corrupted. + */ + if (!FLAC__stream_decoder_seek_absolute(this, mDecoder, position)) + { + return FMOD_ERR_INTERNAL; + } + + // FLAC decoder seek will cause the sample at the given position to be written via the FLAC + // Write Callback, so flag that data is ready so ReadInternal() will not overwrite the data + mFrameReady = true; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFLAC::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecFLAC *flac = (CodecFLAC *)codec; + + return flac->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFLAC::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecFLAC *flac = (CodecFLAC *)codec; + + return flac->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFLAC::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecFLAC *flac = (CodecFLAC *)codec; + + return flac->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFLAC::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecFLAC *flac = (CodecFLAC *)codec; + + return flac->setPositionInternal(subsound, position, postype); +} + +} + +#endif + + diff --git a/src/fmod_codec_flac.h b/src/fmod_codec_flac.h new file mode 100755 index 0000000..5de7535 --- /dev/null +++ b/src/fmod_codec_flac.h @@ -0,0 +1,48 @@ +#ifndef _FMOD_CODEC_FLAC_H +#define _FMOD_CODEC_FLAC_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FLAC + +#include "fmod_codeci.h" + +#include "../lib/flac-1.2.1/include/FLAC/all.h" + +namespace FMOD +{ + class CodecFLAC : public Codec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + FLAC__StreamDecoder *mDecoder; + bool mFrameReady; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + unsigned int mMemUsed; + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker); +#endif + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_FLAC */ + +#endif + + diff --git a/src/fmod_codec_fsb.cpp b/src/fmod_codec_fsb.cpp new file mode 100755 index 0000000..9cbf8fb --- /dev/null +++ b/src/fmod_codec_fsb.cpp @@ -0,0 +1,3764 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FSB + +#include "fmod.h" +#include "fmod_async.h" +#include "fmod_codec_fsb.h" +#include "fmod_debug.h" +#include "fmod_dsp_codec.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_outputi.h" +#include "fmod_soundi.h" +#include "fmod_speakermap.h" +#include "fmod_string.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_common_spu.h" + #include "fmod_spu_printf.h" + #include "fmod_systemi_spu.h" + #include +#else + #include "fmod_systemi.h" +#endif + +#ifdef FMOD_SUPPORT_IMAADPCM + #include "fmod_codec_wav_imaadpcm.h" +#endif + +#ifdef FMOD_SUPPORT_GCADPCM + #include "fmod_codec_gcadpcm.h" +#endif + +#ifdef FMOD_SUPPORT_XMA + #include "fmod_codec_xma.h" +#endif + +#ifdef FMOD_SUPPORT_MPEG + #include "fmod_codec_mpeg.h" +#endif + +#ifdef FMOD_SUPPORT_VAG + #include "fmod_codec_swvag.h" +#endif + +#ifdef FMOD_SUPPORT_CELT + #include "fmod_codec_celt.h" +#endif + +namespace FMOD +{ + +#ifndef PLATFORM_PS3_SPU + +FMOD_CODEC_DESCRIPTION_EX fsbcodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandatory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecFSB::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +#ifdef FMOD_FSB_USEHEADERCACHE +CodecFSBCache CodecFSB::gCacheHead; +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecFSB::getDescriptionEx() +{ + FMOD_memset(&fsbcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + fsbcodec.name = "FMOD FSB Codec"; + fsbcodec.version = 0x00010100; + fsbcodec.timeunits = FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_RAWBYTES; + fsbcodec.open = &CodecFSB::openCallback; + fsbcodec.close = &CodecFSB::closeCallback; + fsbcodec.read = &CodecFSB::readCallback; + fsbcodec.setposition = &CodecFSB::setPositionCallback; + fsbcodec.getposition = &CodecFSB::getPositionCallback; + fsbcodec.soundcreate = &CodecFSB::soundcreateCallback; + fsbcodec.getwaveformat = &CodecFSB::getWaveFormatCallback; + fsbcodec.reset = &CodecFSB::resetCallback; + fsbcodec.canpoint = &CodecFSB::canPointCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + fsbcodec.getmemoryused = &CodecFSB::getMemoryUsedCallback; +#endif + + fsbcodec.mType = FMOD_SOUND_TYPE_FSB; + fsbcodec.mSize = sizeof(CodecFSB); + + return &fsbcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + unsigned int rd; + int count, numsamples, subsoundindex; + char *shdrblock = 0; + FMOD_RESULT result = FMOD_OK; + unsigned int offset, soffset; + bool mixedchannels = false; + + init(FMOD_SOUND_TYPE_FSB); + +#ifdef FMOD_FSB_USEHEADERCACHE + mCacheEntry = 0; +#endif + + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "attempting to open as FSB..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&mHeader, 1, sizeof(FMOD_FSB_HEADER), &rd); + if (result != FMOD_OK) + { + return result; + } + + if (rd != sizeof(FMOD_FSB_HEADER)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "rd != sizeof(FMOD_FSB_HEADER)\n")); + return FMOD_ERR_FILE_BAD; + } + + /* + If it failed, use old method of decryption to check if it is an older FSB. + */ + if (userexinfo && userexinfo->encryptionkey && (FMOD_strncmp(mHeader.id, "FSB3", 4) && FMOD_strncmp(mHeader.id, "FSB4", 4))) + { + int offset = 0, keylen = FMOD_strlen(userexinfo->encryptionkey); + unsigned char *ptr = (unsigned char *)&mHeader; + + for (count = 0; count < (int)sizeof(FMOD_FSB_HEADER); count++) + { + FMOD_ENCRYPT(ptr[count], userexinfo->encryptionkey[offset]); /* put it back the way it was. */ + FMOD_DECRYPT_OLD(ptr[count], userexinfo->encryptionkey[offset]); /* Now redecrypt using the old method. */ + offset++; + if (offset >= keylen) + { + offset = 0; + } + } + + mFile->mFlags |= FMOD_FILE_USEOLDDECRYPT; + } + + if (!FMOD_strncmp(mHeader.id, "FSB4", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "found FSB4\n")); + } + else if (!FMOD_strncmp(mHeader.id, "FSB2", 4) || !FMOD_strncmp(mHeader.id, "FSB3", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "found %s\n", mHeader.id)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "Sorry %s not supported any more. Please recompile using a newer FSBankEx\n", mHeader.id)); + return FMOD_ERR_FORMAT; + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "Header check failed. Not an FSB\n")); + + return FMOD_ERR_FORMAT; + } + +#ifdef PLATFORM_ENDIAN_BIG + mHeader.numsamples = FMOD_SWAPENDIAN_DWORD(mHeader.numsamples); + mHeader.datasize = FMOD_SWAPENDIAN_DWORD(mHeader.datasize); + mHeader.shdrsize = FMOD_SWAPENDIAN_DWORD(mHeader.shdrsize); + mHeader.version = FMOD_SWAPENDIAN_DWORD(mHeader.version); + mHeader.mode = FMOD_SWAPENDIAN_DWORD(mHeader.mode); +#endif + +#ifndef FMOD_FSB_FORCE_3_0 + if (mHeader.version < FMOD_FSB_VERSION_3_1) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error - FSB 3.0 not supported any more, mustb e 3.1\n")); + return FMOD_ERR_VERSION; + } +#endif + + mSrcDataOffset = 0; + + mFlags |= FMOD_CODEC_FROMFSB; + if (mHeader.mode & FMOD_FSB_SOURCE_MPEG_PADDED) + { + mFlags |= FMOD_CODEC_PADDED; + } + else if (mHeader.mode & FMOD_FSB_SOURCE_MPEG_PADDED4) + { + mFlags |= FMOD_CODEC_PADDED4; + } + + if (mHeader.numsamples < 1) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error - mHeader.numsamples < 1\n")); + return FMOD_ERR_FILE_BAD; + } + + /* + If there is an inclusion list and the number of subsounds has been set by the user, go into 'sorted mode'. + */ + numsamples = mHeader.numsamples; + if (userexinfo && userexinfo->inclusionlist && userexinfo->inclusionlistnum && userexinfo->numsubsounds == userexinfo->inclusionlistnum) + { + numsamples = userexinfo->inclusionlistnum; + } + + if (mHeader.mode & FMOD_FSB_SOURCE_FORMAT) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error - FMOD Ex does no longer support 'use source format' FSB files.\n")); + return FMOD_ERR_FORMAT; + } + + mNonInterleaved = (mHeader.mode & FMOD_FSB_SOURCE_NOTINTERLEAVED) ? true : false; + if (mNonInterleaved) + { + if (usermode & FMOD_CREATESTREAM) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error - Cannot open a non interleaved FSB as a stream.\n")); + return FMOD_ERR_FORMAT; + } + + if (usermode & FMOD_SOFTWARE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error - Cannot open a non interleaved FSB with FMOD_SOFTWARE mode.\n")); + return FMOD_ERR_FORMAT; + } + + if (mSystem->mOutput->mDescription.getsamplemaxchannels) + { + if (mSystem->mOutput->mDescription.getsamplemaxchannels(mSystem->mOutput, usermode, FMOD_SOUND_FORMAT_NONE) > 1) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error - Cannot open a non interleaved FSB with current output mode.\n")); + return FMOD_ERR_FORMAT; + } + } + } + +#ifdef FMOD_FSB_USEHEADERCACHE + bool found = false; + CodecFSBCache *current = 0; + + if (mHeader.version >= FMOD_FSB_VERSION_4_0) + { + FMOD_FSB_HEADER header; + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + + memcpy(&header, &mHeader, sizeof(FMOD_FSB_HEADER)); + header.numsamples = numsamples; + + for (current = (CodecFSBCache *)gCacheHead.getNext(); current != &gCacheHead; current = (CodecFSBCache *)current->getNext()) + { + if (!memcmp(&header, ¤t->mHeader, sizeof(FMOD_FSB_HEADER))) + { + found = true; + break; + } + } + } + + if (found) + { + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + + /* + Busy loop here until other thread has finished loading cache data, we don't want to use the crit because + we are sharing gAsyncCrit which would mean we would block multiple non-blocking loads from main thread. + */ + while (current->mStillLoading) + { + FMOD_OS_Time_Sleep(5); + } + + result = mFile->seek(mHeader.shdrsize, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + shdrblock = current->mShdrData; + mShdr = current->mShdr; + mShdrb = current->mShdrb; + mDataOffset = current->mDataOffset; + + current->mShareCount++; + + mCacheEntry = current; + } + else + { + shdrblock = 0; + + if (mHeader.version >= FMOD_FSB_VERSION_4_0) + { + mCacheEntry = FMOD_Object_Calloc(CodecFSBCache); + if (!mCacheEntry) + { + return FMOD_ERR_MEMORY; + } + + mCacheEntry->mShdr = 0; + mCacheEntry->mShdrb = 0; + mCacheEntry->mShdrData = 0; + mCacheEntry->mDataOffset = 0; + + mCacheEntry->addBefore(&gCacheHead); + mCacheEntry->mShareCount = 0; + FMOD_memcpy(&mCacheEntry->mHeader, &mHeader, sizeof(FMOD_FSB_HEADER)); + + if (numsamples != mHeader.numsamples) + { + mCacheEntry->mHeader.numsamples = numsamples; /* Dont cache this stuff. */ + mCacheEntry->mShdr = 0; + mCacheEntry->mShdrb = 0; + mCacheEntry->mDataOffset = 0; + } + + mCacheEntry->mStillLoading = true; + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + } + } +#endif + + if (!mShdrb && !mShdr) + { + /* + Allocate and read FSB sample header pointers + */ + if (mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS) + { + mShdrb = (FMOD_FSB_SAMPLE_HEADER_BASIC **)FMOD_Memory_Calloc(sizeof(void *) * numsamples); + if (!mShdrb) + { + return FMOD_ERR_MEMORY; + } + } + else + { + mShdr = (FMOD_FSB_SAMPLE_HEADER **)FMOD_Memory_Calloc(sizeof(void *) * numsamples); + if (!mShdr) + { + return FMOD_ERR_MEMORY; + } + } + } + + if (!shdrblock) + { + /* + Allocate and read FSB sample header data + */ + shdrblock = (char *)FMOD_Memory_Calloc(mHeader.shdrsize); + if (!shdrblock) + { + return FMOD_ERR_MEMORY; + } + + result = mFile->read(shdrblock, 1, mHeader.shdrsize, &rd); + if (result != FMOD_OK) + { + FMOD_Memory_Free(shdrblock); + return result; + } + if (rd != mHeader.shdrsize) + { + FMOD_Memory_Free(shdrblock); + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "rd != mHeader.shdrsize\n")); + return FMOD_ERR_FILE_BAD; + } + } + + if (!mDataOffset) + { + /* + Calculate file offsets for each sound, and if little endian, do some byte swapping. + */ + mDataOffset = (unsigned int *)FMOD_Memory_Calloc(sizeof(unsigned int) * numsamples); + if (!mDataOffset) + { + FMOD_Memory_Free(shdrblock); + return FMOD_ERR_MEMORY; + } + } + + #ifdef FMOD_FSB_USEHEADERCACHE + if (!found && mCacheEntry) + { + mCacheEntry->mShdrData = shdrblock; /* pointer to sample header block */ + + if (numsamples == mHeader.numsamples) + { + mCacheEntry->mShdr = mShdr; /* array of sample header pointers */ + mCacheEntry->mShdrb = mShdrb; /* array of sample header pointers */ + mCacheEntry->mDataOffset = mDataOffset; /* array of file sample data offsets. */ + } + } + #endif + + mSrcDataOffset = (mHeader.version >= FMOD_FSB_VERSION_4_0 ? sizeof(FMOD_FSB_HEADER) : sizeof(FMOD_FSB3_HEADER)) + mHeader.shdrsize; + + waveformat = 0; + offset = mSrcDataOffset; + mFirstSample = 0; + + if ((usermode & (FMOD_SOFTWARE | FMOD_HARDWARE)) && !(mOriginalMode & (FMOD_SOFTWARE | FMOD_HARDWARE))) + { + usermode &= ~(FMOD_SOFTWARE | FMOD_HARDWARE); + } + if ((mMode & (FMOD_SOFTWARE | FMOD_HARDWARE)) && !(mOriginalMode & (FMOD_SOFTWARE | FMOD_HARDWARE))) + { + mMode &= ~(FMOD_SOFTWARE | FMOD_HARDWARE); + } + + mUserMode = usermode; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "FSB contains %d sounds\n", mHeader.numsamples)); + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + for (soffset = 0, subsoundindex = 0, count = 0; count < mHeader.numsamples; count++) + { + bool found = true; + + if (userexinfo && userexinfo->inclusionlist && userexinfo->inclusionlistnum && userexinfo->numsubsounds == userexinfo->inclusionlistnum) + { + int count2; + + for (count2 = 0; count2 < userexinfo->inclusionlistnum; count2++) + { + if (userexinfo->inclusionlist[count2] >= mHeader.numsamples) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (userexinfo->inclusionlist[count2] == count) + { + break; + } + } + if (count2 == userexinfo->inclusionlistnum) + { + found = false; + } + } + + if ((mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS) && count) /* exclude first sample */ + { + FMOD_FSB_SAMPLE_HEADER_BASIC *s = (FMOD_FSB_SAMPLE_HEADER_BASIC *)((char *)shdrblock + soffset); + + if (found) + { + mShdrb[subsoundindex] = s; + mDataOffset[subsoundindex] = offset; + } + + #ifdef PLATFORM_ENDIAN_BIG + #ifdef FMOD_FSB_USEHEADERCACHE + if (!current || !current->mShareCount) /* Don't swap more than once! */ + #endif + { + s->lengthsamples = FMOD_SWAPENDIAN_DWORD(s->lengthsamples); + s->lengthcompressedbytes = FMOD_SWAPENDIAN_DWORD(s->lengthcompressedbytes); + } + #endif + + offset += (s->lengthcompressedbytes + FSB_SAMPLE_DATA_ALIGN - 1) & ~(FSB_SAMPLE_DATA_ALIGN - 1); + soffset += sizeof(FMOD_FSB_SAMPLE_HEADER_BASIC); + + mChannels = mFirstSample->numchannels; + if (mMode & FMOD_SOFTWARE && mChannels > mSystem->mMaxInputChannels) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + /* + Add offset for ADPCM coefficients - assume they're the same size as the ones with firstsample + i.e. all GCADPCM samples in an FSB must have the same number of channels + */ + if (mFirstSample->mode & FSOUND_GCADPCM) + { + #ifdef FMOD_SUPPORT_GCADPCM + int sizeofheader = sizeof(FMOD_FSB_SAMPLE_HEADER); + soffset += (mFirstSample->size - sizeofheader); + + /* + Make sure GCADPCM samples are aligned + */ + s->lengthsamples /= 14; + s->lengthsamples *= 14; + #else + return FMOD_ERR_FORMAT; + #endif + } + else if (mFirstSample->mode & FSOUND_XMA) + { + #ifdef FMOD_SUPPORT_XMA + if (*((int *)((char *)mFirstSample + sizeof(FMOD_FSB_SAMPLE_HEADER) + sizeof(FMOD_XMA_LOOPINFO)))) + { + if (mFirstSample->size_32bits) /* We didnt flag the block size change, so this is a dodgy way to work out if the FSB is the old FSB format. */ + { + soffset += ((((s->lengthcompressedbytes+32767) / 32768 * sizeof(unsigned int)) + 12) + 16); /* +8 = numentries and something else, +16 = loopinfo */ + } + else + { + soffset += ((((s->lengthcompressedbytes+2047) / 2048 * sizeof(unsigned int)) + 8) + 16); /* +8 = numentries and something else, +16 = loopinfo */ + } + } + else + { + soffset += 16; /* +16 = loopinfo */ + } + #else + return FMOD_ERR_FORMAT; + #endif + } + else if (mFirstSample->mode & FSOUND_IMAADPCM) + { + #ifdef FMOD_SUPPORT_IMAADPCM + #ifdef PLATFORM_XBOX + if (usermode & FMOD_HARDWARE) + { + } + else + #endif + { + mReadBufferLength = 36 * (mFirstSample->numchannels < 2 ? 2 : mFirstSample->numchannels); + mPCMBufferLength = 64; + mPCMBufferLengthBytes = mPCMBufferLength * sizeof(signed short) * (mFirstSample->numchannels < 2 ? 2 : mFirstSample->numchannels); + } + #else + return FMOD_ERR_FORMAT; + #endif + } + else if (mFirstSample->mode & FSOUND_MPEG) + { + #ifdef FMOD_SUPPORT_MPEG_SONYDECODER + if (mFirstSample->mode & FSOUND_MPEG_LAYER2) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Only MP3 format is supported when using the Sony MP3 decoder.\n")); + return FMOD_ERR_FORMAT; + } + #endif + + if (mFirstSample->numchannels > 2 && !(usermode & FMOD_CREATESTREAM)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to load %d channel MP2/MP3. Static codecs can only support stereo maximum. Multichannel MP2/MP3 can only be streamed.\n", mFirstSample->numchannels)); + return FMOD_ERR_TOOMANYCHANNELS; + } + } + else if (mFirstSample->mode & FSOUND_CELT) + { + if (mFirstSample->numchannels > 2 && !(usermode & FMOD_CREATESTREAM)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to load %d channel CELT. Static codecs can only support stereo maximum. Multichannel CELT can only be streamed.\n", mFirstSample->numchannels)); + return FMOD_ERR_TOOMANYCHANNELS; + } + } + + if (userexinfo && userexinfo->speakermap) + { + if (userexinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + mFirstSample->mode |= FSOUND_CHANNELMODE_ALLMONO; + } + else if (userexinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO) + { + mFirstSample->mode |= FSOUND_CHANNELMODE_ALLSTEREO; + } + else if (userexinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + mFirstSample->mode |= FSOUND_CHANNELMODE_PROTOOLS; + } + } + } + else + { + FMOD_FSB_SAMPLE_HEADER *s = (FMOD_FSB_SAMPLE_HEADER *)((char *)shdrblock + soffset); + FMOD_FSB_SAMPLE_HEADER tmpsample; + + if (!count) + { + mFirstSample = s; + } + + if (found) + { + if (!(mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS)) + { + mShdr[subsoundindex] = s; + } + + if (s->name[0] != 0) + { + mDataOffset[subsoundindex] = offset; + } + else + { + if (!count) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "!count\n")); + return FMOD_ERR_FILE_BAD; + } + mDataOffset[subsoundindex] = mDataOffset[subsoundindex - 1]; + } + } + + FMOD_memcpy(&tmpsample, s, sizeof(FMOD_FSB_SAMPLE_HEADER)); /* FMOD_memcpy'd instead of pointed to for alignment reasons */ + + #ifdef PLATFORM_ENDIAN_BIG + #ifdef FMOD_FSB_USEHEADERCACHE + if (!current || !current->mShareCount) /* Don't swap more than once! */ + #endif + { + tmpsample.size = FMOD_SWAPENDIAN_WORD(tmpsample.size); + + tmpsample.lengthsamples = FMOD_SWAPENDIAN_DWORD(tmpsample.lengthsamples); + tmpsample.lengthcompressedbytes = FMOD_SWAPENDIAN_DWORD(tmpsample.lengthcompressedbytes); + tmpsample.loopstart = FMOD_SWAPENDIAN_DWORD(tmpsample.loopstart); + tmpsample.loopend = FMOD_SWAPENDIAN_DWORD(tmpsample.loopend); + tmpsample.mode = FMOD_SWAPENDIAN_DWORD(tmpsample.mode); + tmpsample.deffreq = FMOD_SWAPENDIAN_DWORD(tmpsample.deffreq); + + tmpsample.defvol = FMOD_SWAPENDIAN_WORD(tmpsample.defvol); + tmpsample.defpan = FMOD_SWAPENDIAN_WORD(tmpsample.defpan); + tmpsample.defpri = FMOD_SWAPENDIAN_WORD(tmpsample.defpri); + tmpsample.numchannels = FMOD_SWAPENDIAN_WORD(tmpsample.numchannels); + +#ifndef FMOD_FSB_FORCE_3_0 + tmpsample.size_32bits = FMOD_SWAPENDIAN_DWORD(tmpsample.size_32bits); + tmpsample.varvol = FMOD_SWAPENDIAN_WORD(tmpsample.varvol); + tmpsample.varpan = FMOD_SWAPENDIAN_WORD(tmpsample.varpan); + + FMOD_SWAPENDIAN_FLOAT(tmpsample.mindistance); + FMOD_SWAPENDIAN_FLOAT(tmpsample.maxdistance); +#endif + } + #endif + + offset += (tmpsample.name[0] ? (tmpsample.lengthcompressedbytes + FSB_SAMPLE_DATA_ALIGN - 1) & ~(FSB_SAMPLE_DATA_ALIGN - 1) : 0); + + if (tmpsample.size == 65535) /* Stupid backwards compatibility thing. 0xffff signifies we are to re-use a new 32bit var. */ + { + if ((int)tmpsample.size_32bits < tmpsample.size) + { + return FMOD_ERR_UNSUPPORTED; /* This will barf on an old FSB that just happens to have size = 64k, because the memory used to be 'varfreq' and it is always going to be lower than 64k */ + } + + soffset += tmpsample.size_32bits; + } + else + { + soffset += tmpsample.size; + } + +#ifndef FMOD_FSB_FORCE_3_0 + tmpsample.varvol <<= 2; + tmpsample.varpan <<= 2; + if (tmpsample.mindistance == tmpsample.maxdistance) + { + tmpsample.mindistance = 1.0f; + tmpsample.maxdistance = 10000.0f; + } +#endif + + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + if (s->mode & FSOUND_MPEG) + { + tmpsample.loopstart /= 576; + tmpsample.loopstart *= 576; + } + } + + FMOD_memcpy(s, &tmpsample, sizeof(FMOD_FSB_SAMPLE_HEADER)); /* FMOD_memcpy'd back */ + + if (!s->numchannels) + { + return FMOD_ERR_FILE_BAD; + } + + if (found) + { + if (s->numchannels != mFirstSample->numchannels) + { + mixedchannels = true; + } + + if (s->numchannels > mChannels) + { + mChannels = s->numchannels; + } + } + + if (mMode & FMOD_SOFTWARE && mChannels > mSystem->mMaxInputChannels) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + /* + Make sure GCADPCM samples are aligned + */ + if (s->mode & FSOUND_GCADPCM) + { + #ifdef FMOD_SUPPORT_GCADPCM + s->lengthsamples /= 14; + s->lengthsamples *= 14; + #else + return FMOD_ERR_FORMAT; + #endif + } + + /* + If any of the subsounds has software mode and the + FSB is XMA format, we need to set mDecodeXMA + */ + if (s->mode & FSOUND_XMA && !(usermode & FMOD_HARDWARE)) + { + #ifdef FMOD_SUPPORT_XMA + mDecodeXMA = true; + #else + return FMOD_ERR_FORMAT; + #endif + } + + if (s->mode & FSOUND_IMAADPCM) + { + #ifdef FMOD_SUPPORT_IMAADPCM + #ifdef PLATFORM_XBOX + if (usermode & FMOD_HARDWARE) + { + } + else + #endif + { + mReadBufferLength = 36 * (mChannels < 2 ? 2 : mChannels); + mPCMBufferLength = 64; + mPCMBufferLengthBytes = mPCMBufferLength * sizeof(signed short) * (mChannels < 2 ? 2 : mChannels); + } + #else + return FMOD_ERR_FORMAT; + #endif + } + else if (s->mode & FSOUND_MPEG) + { + #ifdef FMOD_SUPPORT_MPEG_SONYDECODER + if (s->mode & FSOUND_MPEG_LAYER2) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Only MP3 format is supported when using the Sony MP3 decoder.\n")); + return FMOD_ERR_FORMAT; + } + #endif + + if (s->numchannels > 2 && !(usermode & FMOD_CREATESTREAM)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to load %d channel MP2/MP3. Static codecs can only support stereo maximum. Multichannel MP2/MP3 can only be streamed.\n", mFirstSample->numchannels)); + return FMOD_ERR_TOOMANYCHANNELS; + } + } + else if (s->mode & FSOUND_CELT) + { + if (s->numchannels > 2 && !(usermode & FMOD_CREATESTREAM)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to load %d channel CELT. Static codecs can only support stereo maximum. Multichannel CELT can only be streamed.\n", mFirstSample->numchannels)); + return FMOD_ERR_TOOMANYCHANNELS; + } + } + + if (usermode & FMOD_2D) + { + if (s->mode & FSOUND_HW3D) + { + s->mode &= ~FSOUND_HW3D; + s->mode |= FSOUND_HW2D; + } + else if (s->mode & FSOUND_3D) + { + s->mode &= ~FSOUND_3D; + s->mode |= FSOUND_2D; + } + } + + if (userexinfo && userexinfo->speakermap) + { + if (userexinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + s->mode |= FSOUND_CHANNELMODE_ALLMONO; + } + else if (userexinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO) + { + s->mode |= FSOUND_CHANNELMODE_ALLSTEREO; + } + else if (userexinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + s->mode |= FSOUND_CHANNELMODE_PROTOOLS; + } + } + } + + if (found) + { + subsoundindex++; + } + } + + + /* + ====================================================== + + SET UP CODEC SPECIFIC STUFF + + ====================================================== + */ + + + // ======================================================================================================== + // GC ADPCM + // ======================================================================================================== + if (mFirstSample->mode & FSOUND_GCADPCM) + { +#ifdef FMOD_SUPPORT_GCADPCM + FMOD_GCADPCMINFO **info; + + info = (FMOD_GCADPCMINFO **)FMOD_Memory_Calloc(sizeof(void *) * numsamples); + if (!info) + { + return FMOD_ERR_MEMORY; + } + + for (soffset = 0, subsoundindex = 0, count = 0; count < mHeader.numsamples; count++) + { + bool found = true; + + if (userexinfo && userexinfo->inclusionlist && userexinfo->inclusionlistnum && userexinfo->numsubsounds == userexinfo->inclusionlistnum) + { + int count2; + + for (count2 = 0; count2 < userexinfo->inclusionlistnum; count2++) + { + if (userexinfo->inclusionlist[count2] == count) + { + break; + } + } + if (count2 == userexinfo->inclusionlistnum) + { + found = false; + } + } + + if ((mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS) && count) /* exclude first sample */ + { + FMOD_FSB_SAMPLE_HEADER_BASIC *s = (FMOD_FSB_SAMPLE_HEADER_BASIC *)((char *)shdrblock + soffset); + + if (found) + { + info[subsoundindex] = (FMOD_GCADPCMINFO *)FMOD_Memory_Calloc(sizeof(FMOD_GCADPCMINFO) * mFirstSample->numchannels); + if (!info[subsoundindex]) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memcpy(info[subsoundindex], (char *)s + sizeof(FMOD_FSB_SAMPLE_HEADER_BASIC), sizeof(FMOD_GCADPCMINFO) * mFirstSample->numchannels); + } + + soffset += sizeof(FMOD_FSB_SAMPLE_HEADER_BASIC); + + /* + Add offset for ADPCM coefficients - assume they're the same size as the ones with firstsample + i.e. all GCADPCM samples in an FSB must have the same number of channels + */ + soffset += (mFirstSample->size - sizeof(FMOD_FSB_SAMPLE_HEADER)); + } + else + { + FMOD_FSB_SAMPLE_HEADER *s = (FMOD_FSB_SAMPLE_HEADER *)((char *)shdrblock + soffset); + + if (found) + { + info[subsoundindex] = (FMOD_GCADPCMINFO *)FMOD_Memory_Calloc(sizeof(FMOD_GCADPCMINFO) * s->numchannels); + if (!info[subsoundindex]) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memcpy(info[subsoundindex], ((char *)s) + sizeof(FMOD_FSB_SAMPLE_HEADER), sizeof(FMOD_GCADPCMINFO) * s->numchannels); + } + + if (s->size == 65535) /* Stupid backwards compatibility thing. 0xffff signifies we are to re-use a new 32bit var. */ + { + if ((int)s->size_32bits < s->size) + { + return FMOD_ERR_UNSUPPORTED; /* This will barf on an old FSB that just happens to have size = 64k, because the memory used to be 'varfreq' and it is always going to be lower than 64k */ + } + + soffset += s->size_32bits; + } + else + { + soffset += s->size; + } + } + + if (found) + { + subsoundindex++; + } + } + + plugindata = info; +#else + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to open an GCADPCM bank on a platform that doesn't support it!\n")); + return FMOD_ERR_FORMAT; +#endif + } + + // ======================================================================================================== + // XMA + // ======================================================================================================== + else if (mFirstSample->mode & FSOUND_XMA) + { +#ifdef FMOD_SUPPORT_XMA + int **seektable; + int headersize; + int dataoffset = 0; + int seektablesize; + + if (usermode & FMOD_CREATESTREAM || usermode & FMOD_SOFTWARE || usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + mDecodeXMA = true; + } + + if (mDecodeXMA) + { + mXMA = FMOD_Object_Calloc(CodecXMA); + if (!mXMA) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memcpy(&mXMA->mDescription, CodecXMA::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + mXMA->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + mXMA->mFile = mFile; + mXMA->mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mXMA->mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + mXMA->waveformat = mXMA->mWaveFormatMemory; + mXMA->waveformat->frequency = mFirstSample->deffreq; +#ifdef FMOD_SUPPORT_XMA_NEWHAL + if (mFirstSample->size_32bits || mChannels > 2) + { + mXMA->mBlockSize = 32*1024; + } + else + { + mXMA->mBlockSize = 2*1024; + } +#endif + if (mixedchannels) + { + mXMA->mFlags |= FMOD_CODEC_FSBXMAMIXEDCHANNELS; + } + + if (!(usermode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + result = mXMA->XMAInit((mChannels + 1) / 2); /* stream for every stereo pair. (and if odd, 1 for last mono channel as well) */ + if (result != FMOD_OK) + { + return result; + } + } + } + + /* + Check if there is a seektable. + */ + headersize = sizeof(FMOD_FSB_SAMPLE_HEADER); + + FMOD_memcpy(&seektablesize, (char *)mFirstSample + headersize + sizeof(FMOD_XMA_LOOPINFO), 4); + + #ifdef PLATFORM_ENDIAN_BIG + seektablesize = FMOD_SWAPENDIAN_DWORD(seektablesize); + #endif + + if (seektablesize) + { + mXMASeekable = mXMA->mXMASeekable = true; + + seektable = (int **)FMOD_Memory_Calloc(sizeof(int *) * numsamples); + if (!seektable) + { + return FMOD_ERR_MEMORY; + } + + for (soffset = 0, subsoundindex = 0, count = 0; count < mHeader.numsamples; count++) + { + if ((mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS) && count) /* exclude first sample */ + { + FMOD_FSB_SAMPLE_HEADER_BASIC *s = (FMOD_FSB_SAMPLE_HEADER_BASIC *)((char *)shdrblock + soffset); + int *stab; + + /* + Now get seek table length + */ + FMOD_memcpy(&seektablesize, (char *)s + sizeof(FMOD_FSB_SAMPLE_HEADER_BASIC) + sizeof(FMOD_XMA_LOOPINFO), 4); + + #ifdef PLATFORM_ENDIAN_BIG + seektablesize = FMOD_SWAPENDIAN_DWORD(seektablesize); + #endif + + /* + Copy in the seektable + */ + stab = (int *)((char *)s + sizeof(FMOD_FSB_SAMPLE_HEADER_BASIC) + sizeof(FMOD_XMA_LOOPINFO) + 4); + #ifdef PLATFORM_ENDIAN_BIG + #ifdef FMOD_FSB_USEHEADERCACHE + if (!current || (current && !current->mShareCount)) + #endif + { + /* + Flip the endian of the seektable + */ + for (int i = 0; i < seektablesize / 4; i++) + { + stab[i] = FMOD_SWAPENDIAN_DWORD(stab[i]); + } + } + #endif + + if (subsoundindex < numsamples) + { + seektable[subsoundindex] = stab; + } + + soffset += sizeof(FMOD_FSB_SAMPLE_HEADER_BASIC); + + if (*((int *)((char *)mFirstSample + sizeof(FMOD_FSB_SAMPLE_HEADER) + sizeof(FMOD_XMA_LOOPINFO)))) + { + if (mFirstSample->size_32bits) /* We didnt flag the block size change, so this is a dodgy way to work out if the FSB is the old FSB format. */ + { + soffset += ((((s->lengthcompressedbytes+32767) / 32768 * sizeof(unsigned int)) + 12) + 16); /* +8 = numentries and something else, +16 = loopinfo */ + } + else + { + soffset += ((((s->lengthcompressedbytes+2047) / 2048 * sizeof(unsigned int)) + 8) + 16); /* +8 = numentries and something else, +16 = loopinfo */ + } + } + else + { + soffset += 16; /* +16 = loopinfo */ + } + } + else + { + FMOD_FSB_SAMPLE_HEADER *s = (FMOD_FSB_SAMPLE_HEADER *)((char *)shdrblock + soffset); + int *stab; + + headersize = sizeof(FMOD_FSB_SAMPLE_HEADER); + + /* + Now get seek table length + */ + FMOD_memcpy(&seektablesize, (char *)s + headersize + sizeof(FMOD_XMA_LOOPINFO), 4); + + #ifdef PLATFORM_ENDIAN_BIG + seektablesize = FMOD_SWAPENDIAN_DWORD(seektablesize); + #endif + + /* + Copy in the seektable + */ + stab = (int *)((char *)s + headersize + sizeof(FMOD_XMA_LOOPINFO) + 4); + + #ifdef PLATFORM_ENDIAN_BIG + #ifdef FMOD_FSB_USEHEADERCACHE + if (!current || (current && !current->mShareCount)) + #endif + { + /* + Flip the endian of the seektable + */ + for (int i = 0; i < seektablesize / 4; i++) + { + stab[i] = FMOD_SWAPENDIAN_DWORD(stab[i]); + } + } + #endif + + if (subsoundindex < numsamples) + { + seektable[subsoundindex] = stab; + } + + if (s->size == 65535) /* Stupid backwards compatibility thing. 0xffff signifies we are to re-use a new 32bit var. */ + { + if ((int)s->size_32bits < s->size) + { + return FMOD_ERR_UNSUPPORTED; /* This will barf on an old FSB that just happens to have size = 64k, because the memory used to be 'varfreq' and it is always going to be lower than 64k */ + } + + soffset += s->size_32bits; + } + else + { + soffset += s->size; + } + } + + if (userexinfo && userexinfo->inclusionlist && userexinfo->inclusionlistnum && userexinfo->numsubsounds == userexinfo->inclusionlistnum) + { + int count2; + + for (count2 = 0; count2 < userexinfo->inclusionlistnum; count2++) + { + if (userexinfo->inclusionlist[count2] == count) + { + subsoundindex++; + break; + } + } + } + else + { + subsoundindex++; + } + } + + plugindata = seektable; + } + else + { + mXMASeekable = mXMA->mXMASeekable = false; + + FLOG((FMOD_DEBUG_LEVEL_WARNING, __FILE__, __LINE__, "CodecFSB::openInternal", "FSB was built *without* XMA seek table. Seeking will not be allowed.\n")); + + plugindata = 0; + } +#else + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to open an XMA bank on a platform that doesn't support it!\n")); + return FMOD_ERR_FORMAT; +#endif + } + + // ======================================================================================================== + // IMA ADPCM + // ======================================================================================================== + else if (mFirstSample->mode & FSOUND_IMAADPCM) + { +#ifdef FMOD_SUPPORT_IMAADPCM + + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE + #ifdef PLATFORM_XBOX + && (usermode & FMOD_SOFTWARE) /* Has to be software for xbox as well, because hardware can decode this stuff itself. All other platforms decompress regardless. */ + #endif + ) + { + mDecodeADPCM = true; + } + + if (mDecodeADPCM) + { + mADPCM = FMOD_Object_Calloc(CodecWav); + if (!mADPCM) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memcpy(&mADPCM->mDescription, CodecWav::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + mADPCM->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + mADPCM->mFile = mFile; + mADPCM->mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mADPCM->mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + mADPCM->waveformat = mADPCM->mWaveFormatMemory; + mADPCM->mPCMBufferLengthBytes = mPCMBufferLengthBytes; + } + +#else + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to open an ADPCM bank on a platform that doesn't support it!\n")); + return FMOD_ERR_FORMAT; +#endif + } + + + // ======================================================================================================== + // MPEG + // ======================================================================================================== + else if (mFirstSample->mode & FSOUND_MPEG) + { +#ifdef FMOD_SUPPORT_MPEG + + mMPEG = FMOD_Object_Calloc(CodecMPEG); + if (!mMPEG) + { + return FMOD_ERR_MEMORY; + } + + if (!CodecMPEG::gInitialized) + { + CodecMPEG::initAll(); + CodecMPEG::gInitialized = true; + } + + FMOD_memcpy(&mMPEG->mDescription, CodecMPEG::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + mMPEG->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + mMPEG->mFile = mFile; + mMPEG->mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mMPEG->mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + mMPEG->waveformat = mMPEG->mWaveFormatMemory; + mMPEG->mSystem = mSystem; + + mPCMBufferLength = 1152; + mMPEG->mPCMBufferLength = 1152; + mMPEG->mPCMBufferLengthBytes = 1152 * sizeof(signed short) * (mChannels < 2 ? 2 : mChannels); + mMPEG->mPCMFrameLengthBytes = 1152 * sizeof(signed short) * (mChannels < 2 ? 2 : mChannels); + mMPEG->mChannels = 0; + mMPEG->mFlags |= FMOD_CODEC_FROMFSB; + if (mHeader.mode & FMOD_FSB_SOURCE_MPEG_PADDED) + { + mMPEG->mFlags |= FMOD_CODEC_PADDED; + } + else if (mHeader.mode & FMOD_FSB_SOURCE_MPEG_PADDED4) + { + mMPEG->mFlags |= FMOD_CODEC_PADDED4; + } + + if (!(usermode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + mMPEG->mMemoryBlockMemory = (CodecMPEG_MemoryBlock *)FMOD_Memory_Calloc(sizeof(CodecMPEG_MemoryBlock) * (mChannels > 2 ? mChannels : 1) + 16); + if (!mMPEG->mMemoryBlockMemory) + { + return FMOD_ERR_MEMORY; + } + mMPEG->mMemoryBlock = (CodecMPEG_MemoryBlock *)FMOD_ALIGNPOINTER(mMPEG->mMemoryBlockMemory, 16); + + mMPEG->mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mMPEG->mPCMBufferLengthBytes + 16); + if (!mMPEG->mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mMPEG->mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mMPEG->mPCMBufferMemory, 16); + + if (mChannels > 2) + { + mMPEG->mChannels = mChannels; + } + + mMPEG->resetFrame(); + + for (count = 0; count < (mChannels > 2 ? mChannels : 1); count++) + { + mMPEG->mMemoryBlock[count].mHasXingNumFrames = false; + mMPEG->mMemoryBlock[count].mHasXingToc = false; + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + + mMPEG->mMemoryBlock[count].mFrameSizeOld = -1; + mMPEG->mMemoryBlock[count].mSynthBo = 1; + mMPEG->mMemoryBlock[count].mLayer = 0; + mMPEG->mMemoryBlock[count].mSynthBuffs = (float *)FMOD_ALIGNPOINTER(mMPEG->mMemoryBlock[count].mSynthBuffsMem, 16); + + #endif + } + } + +#else + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to open an MPEG bank on a platform that doesn't support it!\n")); + + return FMOD_ERR_FORMAT; +#endif + } + + + // ======================================================================================================== + // VAG + // ======================================================================================================== + else if (mFirstSample->mode & FSOUND_VAG) + { +#ifdef FMOD_SUPPORT_VAG + bool decodevag = true; + + #if defined(PLATFORM_PS2) || defined (PLATFORM_PSP) + if (!(usermode & FMOD_SOFTWARE)) + { + decodevag = false; + } + #endif + + if (decodevag) + { + mVAG = FMOD_Object_Calloc(CodecVAG); + if (!mVAG) + { + return FMOD_ERR_MEMORY; + } + + mPCMBufferLength = 28 * mChannels; + mPCMBufferLengthBytes = 56 * mChannels; + + FMOD_memcpy(&mVAG->mDescription, CodecVAG::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + mVAG->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + mVAG->mFile = mFile; + mVAG->mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mVAG->mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + mVAG->waveformat = mVAG->mWaveFormatMemory; + mVAG->mPCMBufferLengthBytes = mPCMBufferLengthBytes; + } +#else + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to open an VAG bank on a platform that doesn't support it!\n")); + + return FMOD_ERR_FORMAT; +#endif + } + + // ======================================================================================================== + // CELT + // ======================================================================================================== + else if(mFirstSample->mode & FSOUND_CELT) + { +#ifdef FMOD_SUPPORT_CELT + mCELT = FMOD_Object_Calloc(CodecCELT); + if (!mCELT) + { + return FMOD_ERR_MEMORY; + } + + /* + Generate CELT mode(s) + + 1 for mono, and one for stereo. + */ + if (mixedchannels) + { + /* + Allocate both stereo and mono modes + */ + result = CodecCELT::createCELTModeMono(); + if (result != FMOD_OK) + { + return result; + } + result = CodecCELT::createCELTModeStereo(); + if (result != FMOD_OK) + { + return result; + } + } + else + { + if (mChannels >= 2) + { + /* + Stereo or multichannel + */ + result = CodecCELT::createCELTModeStereo(); + if (result != FMOD_OK) + { + return result; + } + } + else + { + /* + Mono only + */ + result = CodecCELT::createCELTModeMono(); + if (result != FMOD_OK) + { + return result; + } + } + } + + /* + Check bitstream version. + */ + int bitstreamversion; + + FMOD_memcpy(&bitstreamversion, (char *)mFirstSample + sizeof(FMOD_FSB_SAMPLE_HEADER), 4); + + #ifdef PLATFORM_ENDIAN_BIG + bitstreamversion = FMOD_SWAPENDIAN_DWORD(bitstreamversion); + #endif + + if (bitstreamversion != CodecCELT::getBitstreamVersion()) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Bitstream version of FSB %08x doesn't match %08x.", bitstreamversion, CodecCELT::getBitstreamVersion())); + return FMOD_ERR_FORMAT; + } + + FMOD_memcpy(&mCELT->mDescription, CodecCELT::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + mCELT->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + mCELT->mFile = mFile; + mCELT->mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mCELT->mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + mCELT->waveformat = mCELT->mWaveFormatMemory; + + mPCMBufferLength = FMOD_CELT_FRAMESIZESAMPLES; + mCELT->mPCMBufferLength = FMOD_CELT_FRAMESIZESAMPLES; + mCELT->mPCMBufferLengthBytes = FMOD_CELT_FRAMESIZESAMPLES * sizeof(signed short) * (mChannels < 2 ? 2 : mChannels); + mCELT->mPCMFrameLengthBytes = FMOD_CELT_FRAMESIZESAMPLES * sizeof(signed short) * mChannels; + + if (!(usermode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + mCELT->mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mCELT->mPCMBufferLengthBytes + 16); + if (!mCELT->mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mCELT->mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mCELT->mPCMBufferMemory, 16); + + result = mCELT->CELTinit((mChannels + 1) / 2); /* stream for every stereo pair. */ + if (result != FMOD_OK) + { + return result; + } + } +#else + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::openInternal", "Error! Tried to open a CELT bank on a platform that doesn't support it!.\n")); + return FMOD_ERR_FORMAT; +#endif + } + +#ifdef FMOD_FSB_USEHEADERCACHE + if (mCacheEntry) + { + mCacheEntry->mStillLoading = false; + } +#endif + + /* + ====================================================== + + Load syncpoints / seektables. + + ====================================================== + */ + if (!(mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS)) + { + int fsbheadersize = sizeof(FMOD_FSB_SAMPLE_HEADER); + int headersize; + + headersize = fsbheadersize; + + for (soffset = 0, subsoundindex = 0, count = 0; count < mHeader.numsamples && subsoundindex < numsamples; count++) + { + FMOD_FSB_SAMPLE_HEADER *s; + bool found = true; + + if (userexinfo && userexinfo->inclusionlist && userexinfo->inclusionlistnum && userexinfo->numsubsounds == userexinfo->inclusionlistnum) + { + int count2; + + for (count2 = 0; count2 < userexinfo->inclusionlistnum; count2++) + { + if (userexinfo->inclusionlist[count2] == count) + { + break; + } + } + if (count2 == userexinfo->inclusionlistnum) + { + found = false; + } + } + + s = (FMOD_FSB_SAMPLE_HEADER *)((char *)shdrblock + soffset); + + #ifdef FMOD_SUPPORT_XMA + if (mFirstSample->mode & FSOUND_XMA) + { + int seektablelength = 0; + + headersize = fsbheadersize; + + /* + Get seek table length + */ + FMOD_memcpy(&seektablelength, (char *)s + headersize + sizeof(FMOD_XMA_LOOPINFO), 4); + #ifdef PLATFORM_ENDIAN_BIG + seektablelength = FMOD_SWAPENDIAN_DWORD(seektablelength); + #endif + + headersize += sizeof(FMOD_XMA_LOOPINFO); + headersize += sizeof(int); + headersize += seektablelength; + } + #endif + +#ifdef FMOD_SUPPORT_GCADPCM + if (mFirstSample->mode & FSOUND_GCADPCM) + { + headersize = fsbheadersize + (sizeof(FMOD_GCADPCMINFO) * s->numchannels); + } +#endif + + if (s->mode & FSOUND_SYNCPOINTS && found) + { + char *syncpointdata = (char *)s + headersize; + + if (!FMOD_strncmp(syncpointdata, "SYNC", 4)) + { + if (!mSyncPointData) + { + mSyncPointData = (char **)FMOD_Memory_Calloc(numsamples * sizeof(char *)); + if (!mSyncPointData) + { + return FMOD_ERR_MEMORY; + } + } + + mSyncPointData[subsoundindex] = syncpointdata; + } + } + + if (s->size == 65535) /* Stupid backwards compatibility thing. 0xffff signifies we are to re-use a new 32bit var. */ + { + if ((int)s->size_32bits < s->size) + { + return FMOD_ERR_UNSUPPORTED; /* This will barf on an old FSB that just happens to have size = 64k, because the memory used to be 'varfreq' and it is always going to be lower than 64k */ + } + + soffset += s->size_32bits; + } + else + { + soffset += s->size; + } + + if (found) + { + subsoundindex++; + } + } + } + + + /* + ====================================================== + + ALLOCATE BUFFERS + + ====================================================== + */ + if (!(usermode & FMOD_CREATECOMPRESSEDSAMPLE)) /* There's going to be a global readbuff and unique pcm buffs for every dspcodec, dont alloc them here. */ + { + if (mReadBufferLength) + { + mReadBuffer = (unsigned char *)FMOD_Memory_Calloc(mReadBufferLength); + if (!mReadBuffer) + { + return FMOD_ERR_MEMORY; + } + } + + if (mPCMBufferLengthBytes) + { + mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes + 16); + if (!mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mPCMBufferMemory, 16); + } + } + + numsubsounds = numsamples; + + if (userexinfo && userexinfo->inclusionlist && userexinfo->inclusionlistnum && userexinfo->numsubsounds == userexinfo->inclusionlistnum) + { + mHeader.numsamples = numsamples; + userexinfo->inclusionlist = 0; + userexinfo->inclusionlistnum = 0; /* Get rid of the inclusionlist. */ + } + + /* + ====================================================== + + SET UP CODEC POOLS + + ====================================================== + */ + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + if (0) + { + } + #ifdef FMOD_SUPPORT_XMA + /* + Create a pool of XMA codecs if using realtime software mixed xma playback. + */ + else if (mXMA) + { + int count; + + if (mChannels > 2) + { + return FMOD_ERR_TOOMANYCHANNELS; /* Sorry we're only allocating memory for a pool of maximum stereo voices. */ + } + + getWaveFormatInternal(0, &mXMA->waveformat[0]); + + #ifdef FMOD_SUPPORT_DSPCODEC + if (!mSystem->mDSPCodecPool_XMA.mNumDSPCodecs) + { + result = mSystem->mDSPCodecPool_XMA.init(FMOD_DSP_CATEGORY_DSPCODECXMA, 512, mSystem->mAdvancedSettings.maxXMAcodecs ? mSystem->mAdvancedSettings.maxXMAcodecs : FMOD_ADVANCEDSETTINGS_MAXXMACODECS); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_XMA.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_XMA.mPool[count]); + CodecXMA *xma = (CodecXMA *)dspcodec->mCodec; + + xma->mSrcDataOffset = 0; /* Raw data will start at 0. */ + xma->mWaveFormatMemory = 0; /* This will be set up upon play. */ + + result = xma->XMAInit(1); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + } + #endif + + #ifdef FMOD_SUPPORT_IMAADPCM + /* + Create a pool of ADPCM codecs if using realtime software mixed adpcm playback. + */ + else if (mADPCM) + { + if (mChannels > 2) + { + return FMOD_ERR_TOOMANYCHANNELS; /* Sorry we're only allocating memory for a pool of maximum stereo voices. */ + } + + mADPCM->mReadBufferLength = mReadBufferLength; + mADPCM->mReadBuffer = 0; + mADPCM->mSamplesPerADPCMBlock = 64; + + getWaveFormatInternal(0, &mADPCM->waveformat[0]); + + #ifdef FMOD_SUPPORT_DSPCODEC + if (!mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs) + { + int count; + + result = mSystem->mDSPCodecPool_ADPCM.init(FMOD_DSP_CATEGORY_DSPCODECADPCM, 64, mSystem->mAdvancedSettings.maxADPCMcodecs ? mSystem->mAdvancedSettings.maxADPCMcodecs : FMOD_ADVANCEDSETTINGS_MAXADPCMCODECS); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_ADPCM.mPool[count]); + CodecWav *wav = (CodecWav *)dspcodec->mCodec; + + wav->mSrcDataOffset = 0; /* Raw data will start at 0. */ + wav->mSrcFormat = &wav->mSrcFormatMemory; + wav->mSrcFormat->Format.wFormatTag = WAVE_FORMAT_IMA_ADPCM; + } + } + #endif + } + #endif + #ifdef FMOD_SUPPORT_MPEG + /* + Create a pool of MPEG codecs if using realtime software mixed MPEG playback. + */ + else if (mMPEG) + { + if (mChannels > 2) + { + return FMOD_ERR_TOOMANYCHANNELS; /* Sorry we're only allocating memory for a pool of maximum stereo voices. */ + } + + mMPEG->mReadBufferLength = mReadBufferLength; + mMPEG->mReadBuffer = mReadBuffer; + + getWaveFormatInternal(0, &mMPEG->waveformat[0]); + + #ifdef FMOD_SUPPORT_DSPCODEC + if (!mSystem->mDSPCodecPool_MPEG.mNumDSPCodecs) + { + int count; + + result = mSystem->mDSPCodecPool_MPEG.init(FMOD_DSP_CATEGORY_DSPCODECMPEG, 1152, mSystem->mAdvancedSettings.maxMPEGcodecs ? mSystem->mAdvancedSettings.maxMPEGcodecs : FMOD_ADVANCEDSETTINGS_MAXMPEGCODECS ); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_MPEG.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_MPEG.mPool[count]); + CodecMPEG *mpeg = (CodecMPEG *)dspcodec->mCodec; + + mpeg->mSrcDataOffset = 0; /* Raw data will start at 0. */ + mpeg->mWaveFormatMemory = 0; /* This will be set up upon play. */ + mpeg->resetFrame(); + } + } + #endif + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (mCELT) + { + if (!mSystem->mDSPCodecPool_CELT.mNumDSPCodecs) + { + int count; + + result = mSystem->mDSPCodecPool_CELT.init(FMOD_DSP_CATEGORY_DSPCODECCELT, FMOD_CELT_FRAMESIZESAMPLES, mSystem->mAdvancedSettings.maxCELTcodecs ? mSystem->mAdvancedSettings.maxCELTcodecs : FMOD_ADVANCEDSETTINGS_MAXCELTCODECS); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_CELT.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_CELT.mPool[count]); + CodecCELT *celt = (CodecCELT *)dspcodec->mCodec; + + celt->mSrcDataOffset = 0; /* Raw data will start at 0. */ + celt->mWaveFormatMemory = 0; /* This will be set up upon play. */ + + result = celt->CELTinit(1); + if (result != FMOD_OK) + { + return result; + } + } + } + } + #endif + } + + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::openInternal", "done.\n")); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::closeInternal() +{ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::closeInternal", "\n")); + +#ifdef FMOD_FSB_USEHEADERCACHE + if (mCacheEntry) + { + if (mCacheEntry->mShdrData) + { + mFirstSample = 0; + } + if (mCacheEntry->mShdr) + { + mShdr = 0; + } + if (mCacheEntry->mShdrb) + { + mShdrb = 0; + } + if (mCacheEntry->mDataOffset) + { + mDataOffset = 0; + } + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + + if (!mCacheEntry->mShareCount) + { + if (mCacheEntry->mShdrData) + { + FMOD_Memory_Free(mCacheEntry->mShdrData); + } + if (mCacheEntry->mShdr) + { + FMOD_Memory_Free(mCacheEntry->mShdr); + } + if (mCacheEntry->mShdrb) + { + FMOD_Memory_Free(mCacheEntry->mShdrb); + } + if (mCacheEntry->mDataOffset) + { + FMOD_Memory_Free(mCacheEntry->mDataOffset); + } + mCacheEntry->removeNode(); + + FMOD_Memory_Free(mCacheEntry); + mCacheEntry = 0; + } + else + { + mCacheEntry->mShareCount--; + } + + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + } +#endif + + if (mFirstSample) /* The start of the sample header block. */ + { + FMOD_Memory_Free(mFirstSample); + mFirstSample = 0; + } + if (mShdr) + { + FMOD_Memory_Free(mShdr); + mShdr = 0; + } + if (mShdrb) + { + FMOD_Memory_Free(mShdrb); + mShdrb = 0; + } + if (mDataOffset) + { + FMOD_Memory_Free(mDataOffset); + mDataOffset = 0; + } + if (mSyncPointData) + { + FMOD_Memory_Free(mSyncPointData); + mSyncPointData = 0; + } + if (mPCMBufferMemory) + { + FMOD_Memory_Free(mPCMBufferMemory); + mPCMBuffer = mPCMBufferMemory = 0; + } + mPCMBufferLengthBytes = 0; + + if (mReadBuffer) + { + FMOD_Memory_Free(mReadBuffer); + mReadBuffer = 0; + } + mReadBufferLength = 0; + +#ifdef FMOD_SUPPORT_GCADPCM + { + FMOD_GCADPCMINFO **info = (FMOD_GCADPCMINFO **)plugindata; + + if (info) + { + for (int count = 0; count < mHeader.numsamples; count++) + { + if (info[count]) + { + FMOD_Memory_Free(info[count]); + info[count] = 0; + } + } + } + } + + if (plugindata) + { + FMOD_Memory_Free(plugindata); + plugindata = 0; + } +#endif + +#ifdef FMOD_SUPPORT_XMA + if (plugindata) + { + FMOD_Memory_Free(plugindata); + plugindata = 0; + } + + if (mXMA) + { + mXMA->mReadBuffer = 0; + mXMA->mFile = 0; + mXMA->mSeekTable = 0; /* This is part of FSB memory, not the XMA codec. */ + mXMA->release(); + } +#endif + +#ifdef FMOD_SUPPORT_IMAADPCM + if (mADPCM) + { + mADPCM->mReadBuffer = 0; + mADPCM->mFile = 0; + mADPCM->release(); + } +#endif + +#ifdef FMOD_SUPPORT_MPEG + if (mMPEG) + { + mMPEG->mReadBuffer = 0; + mMPEG->mFile = 0; + mMPEG->release(); + } +#endif + +#ifdef FMOD_SUPPORT_VAG + if (mVAG) + { + mVAG->mReadBuffer = 0; + mVAG->mFile = 0; + mVAG->release(); + } +#endif + +#ifdef FMOD_SUPPORT_CELT + if (mCELT) + { + mCELT->mReadBuffer = 0; + mCELT->mFile = 0; + mCELT->release(); + } +#endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::closeInternal", "done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int bytesreadinternal; + unsigned int srcmode = mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS ? mFirstSample->mode : mShdr[mCurrentIndex]->mode; + FMOD_CODEC_WAVEFORMAT waveformat; + + getWaveFormatInternal(mCurrentIndex, &waveformat); + + if (0) + { + } +#ifdef FMOD_SUPPORT_XMA + else if (mXMA) /* This means it is a codec wanting to be decompressed to PCM */ + { + result = mXMA->read(buffer, sizebytes * waveformat.channels / mChannels, &bytesreadinternal); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + } +#endif +#ifdef FMOD_SUPPORT_MPEG + else if (mMPEG) /* This means it is a codec wanting to be decompressed to PCM */ + { + result = mMPEG->read(buffer, sizebytes * waveformat.channels / mChannels, &bytesreadinternal); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + } +#endif +#ifdef FMOD_SUPPORT_IMAADPCM + else if (srcmode & FSOUND_IMAADPCM && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + unsigned char readbuff[36 * 16]; + int readbufflength = 36 * waveformat.channels; + int blockalign = 36 * waveformat.channels; + + result = mFile->read(readbuff, 1, blockalign, 0); + if (result != FMOD_OK) + { + return result; + } + + if (waveformat.channels == 1) + { + result = IMAAdpcm_DecodeM16(readbuff, (signed short *)mPCMBuffer, 1, blockalign, 64, 1); + } + else if (srcmode & FSOUND_IMAADPCMSTEREO && waveformat.channels == 2) + { + result = IMAAdpcm_DecodeS16(readbuff, (signed short *)mPCMBuffer, 1, blockalign, 64); + } + else + { + int count = 0; + + blockalign /= waveformat.channels; + + for (count = 0; count < waveformat.channels; count++) + { + signed short tempin[4096]; + int count2; + + for (count2 = 0; count2 < (int)readbufflength / waveformat.channels; count2++) + { + tempin[count2] = ((signed short *)readbuff)[(count2 * waveformat.channels) + count]; + } + + result = IMAAdpcm_DecodeM16((unsigned char *)tempin, (signed short *)mPCMBuffer + count, 1, blockalign, 64, waveformat.channels); + } + } + + bytesreadinternal = 64 * sizeof(signed short) * waveformat.channels; + } +#endif +#ifdef FMOD_SUPPORT_VAG + else if (mVAG) /* This means it is a codec wanting to be decompressed to PCM */ + { + result = mVAG->read(buffer, sizebytes * waveformat.channels / mChannels, &bytesreadinternal); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + } +#endif +#ifdef FMOD_SUPPORT_CELT + else if (mCELT) + { + result = mCELT->read(buffer, sizebytes * waveformat.channels / mChannels, &bytesreadinternal); + } +#endif + else + { + result = mFile->read(buffer, 1, sizebytes / mChannels * waveformat.channels, &bytesreadinternal); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + /* + Convert data from unsigned to signed that fmod requires. + */ + if (waveformat.format == FMOD_SOUND_FORMAT_PCM8) + { + unsigned int len; + unsigned char *src = (unsigned char *)buffer; + + len = bytesreadinternal >> 3; + while (len) + { + src[0] ^= 128; + src[1] ^= 128; + src[2] ^= 128; + src[3] ^= 128; + src[4] ^= 128; + src[5] ^= 128; + src[6] ^= 128; + src[7] ^= 128; + src+=8; + len--; + } + len = bytesreadinternal & 7; + while (len) + { + src[0] ^= 128; + src++; + len--; + } + } + + if (!(mHeader.mode & FMOD_FSB_SOURCE_BIGENDIANPCM)) + { + #ifdef PLATFORM_ENDIAN_BIG + if (waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + unsigned int count; + signed short *wptr = (signed short *)buffer; + + for (count=0; count < bytesreadinternal >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + + } + else if (waveformat.format == FMOD_SOUND_FORMAT_PCMFLOAT) + { + unsigned int count; + unsigned int *wptr = (unsigned int *)buffer; + + for (count=0; count < bytesreadinternal >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_DWORD(wptr[count]); + } + } + #endif + } + else + { + #ifdef PLATFORM_ENDIAN_LITTLE + if (waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + unsigned int count; + signed short *wptr = (signed short *)buffer; + + for (count=0; count < bytesreadinternal >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + + } + else if (waveformat.format == FMOD_SOUND_FORMAT_PCMFLOAT) + { + unsigned int count; + unsigned int *wptr = (unsigned int *)buffer; + + for (count=0; count < bytesreadinternal >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_DWORD(wptr[count]); + } + } + #endif + } + } + + /* + Upscale to the stream channel format if the incoming data is lower. (just duplicate channel data) + */ + if (waveformat.channels < mChannels) + { + int count, block; + int blocksize, numblocks; + char *srcptr, *destptr; + + /* + Stereo XADPCM is a special case where it is interleaved every 4 bytes. + */ + if (srcmode & FSOUND_IMAADPCM && waveformat.format == FMOD_SOUND_FORMAT_IMAADPCM && mChannels == 2) + { + blocksize = 4; + } + /* + GCADPCM is interleaved like PCM16 + */ + else if (waveformat.format == FMOD_SOUND_FORMAT_GCADPCM) + { + blocksize = 2; + } + /* + VAG is being decoded to software, so we want to deinterleave it from a block of 28 16bit words into the pcm16 dest. + */ + else if (srcmode & FSOUND_VAG && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + blocksize = 2; + } + else + { + blocksize = waveformat.blockalign / waveformat.channels; + } + + srcptr = (char *)buffer + (bytesreadinternal ) - (blocksize * waveformat.channels); + destptr = (char *)buffer + (bytesreadinternal / waveformat.channels * mChannels) - (blocksize * mChannels); + numblocks = bytesreadinternal / (blocksize * waveformat.channels); + + /* + 8bit data + */ + if (blocksize == 1) + { + if (waveformat.channels == 1) + { + for (block = 0; block < numblocks; block++) + { + for (count = mChannels - 1; count >= 0; count--) + { + destptr[count] = srcptr[0]; + } + + srcptr --; + destptr -= mChannels; + } + } + else + { + for (block = 0; block < numblocks; block++) + { + int srcchannel = waveformat.channels - 1; + + for (count = mChannels - 1; count >= 0; count--) + { + if (count > srcchannel) + { + destptr[count] = 0; + } + else + { + destptr[count] = srcptr[srcchannel]; + srcchannel--; + } + } + + srcptr -= waveformat.channels; + destptr -= mChannels; + } + } + } + /* + 16bit data + */ + else if (blocksize == 2) + { + signed short *srcptrw = (signed short *)srcptr; + signed short *destptrw = (signed short *)destptr; + + if (waveformat.channels == 1) + { + for (block = 0; block < numblocks; block++) + { + for (count = mChannels - 1; count >= 0; count--) + { + destptrw[count] = srcptrw[0]; + } + + srcptrw --; + destptrw -= mChannels; + } + } + else + { + for (block = 0; block < numblocks; block++) + { + int srcchannel = waveformat.channels - 1; + + for (count = mChannels - 1; count >= 0; count--) + { + if (count > srcchannel) + { + destptrw[count] = 0; + } + else + { + destptrw[count] = srcptrw[srcchannel]; + srcchannel--; + } + } + + srcptrw -= waveformat.channels; + destptrw -= mChannels; + } + } + } + /* + 32bit data + */ + else if (blocksize == 4) + { + int *srcptrd = (int *)srcptr; + int *destptrd = (int *)destptr; + + if (waveformat.channels == 1) + { + for (block = 0; block < numblocks; block++) + { + for (count = mChannels - 1; count >= 0; count--) + { + destptrd[count] = srcptrd[0]; + } + + srcptrd --; + destptrd -= mChannels; + } + } + else + { + for (block = 0; block < numblocks; block++) + { + int srcchannel = waveformat.channels - 1; + + for (count = mChannels - 1; count >= 0; count--) + { + if (count > srcchannel) + { + destptrd[count] = 0; + } + else + { + destptrd[count] = srcptrd[srcchannel]; + srcchannel--; + } + } + + srcptrd -= waveformat.channels; + destptrd -= mChannels; + } + } + } + /* + Other types of sample data (native compressed data?) + */ + else + { + if (waveformat.channels == 1) + { + for (block = 0; block < numblocks; block++) + { + for (count = mChannels - 1; count >= 0; count--) + { + FMOD_memcpy(destptr + (count * blocksize), srcptr, blocksize); + } + + srcptr -= blocksize; + destptr -= (blocksize * mChannels); + } + } + else + { + for (block = 0; block < numblocks; block++) + { + int srcchannel = waveformat.channels - 1; + + for (count = mChannels - 1; count >= 0; count--) + { + if (count > srcchannel) + { + FMOD_memset(destptr + (count * blocksize), 0, blocksize); + } + else + { + FMOD_memcpy(destptr + (count * blocksize), srcptr + (srcchannel * blocksize), blocksize); + srcchannel--; + } + } + + srcptr -= (blocksize * waveformat.channels); + destptr -= (blocksize * mChannels); + } + } + } + + *bytesread = bytesreadinternal * mChannels / waveformat.channels; + } + else + { + *bytesread = bytesreadinternal; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + + if (subsound < 0 || (numsubsounds && subsound >= numsubsounds)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecFSB::setPositionInternal", "ERROR - Invalid subsound. subsound %d position %d postype %d\n", subsound, position, postype)); + return FMOD_ERR_INVALID_POSITION; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::setPositionInternal", "subsound %d position %d postype %d\n", subsound, position, postype)); + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + unsigned int pos = 0, srcmode; + FMOD_CODEC_WAVEFORMAT waveformat; + + if (subsound != mCurrentIndex) + { + mCurrentIndex = subsound; + } + + getWaveFormatInternal(mCurrentIndex, &waveformat); + + srcmode = mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS ? mFirstSample->mode : mShdr[mCurrentIndex]->mode; + + if (postype == FMOD_TIMEUNIT_RAWBYTES) + { + pos = mDataOffset[mCurrentIndex] + position; + + result = mFile->seek(pos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + else +#ifdef FMOD_SUPPORT_XMA + if (srcmode & FSOUND_XMA && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + int **seektable = (int **)plugindata; + + mXMA->mSrcDataOffset = mDataOffset[mCurrentIndex]; + if (mXMA->mXMASeekable) + { + mXMA->mSeekTable = seektable[mCurrentIndex]; + } + mXMA->mFile = mFile; + mXMA->mNumStreams = (waveformat.channels + 1) / 2; + + FMOD_memcpy(mXMA->mWaveFormatMemory, &waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + + result = mXMA->setPositionInternal(subsound, position, postype); + if (result != FMOD_OK) + { + return result; + } + } + else +#endif +#ifdef FMOD_SUPPORT_MPEG + if (srcmode & FSOUND_MPEG && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + mMPEG->mSrcDataOffset = mDataOffset[mCurrentIndex]; + mMPEG->mFile = mFile; + mMPEG->mPCMFrameLengthBytes = 1152 * sizeof(signed short) * waveformat.channels; + + FMOD_memcpy(mMPEG->mWaveFormatMemory, &waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + + result = mMPEG->setPositionInternal(subsound, position, postype); + if (result != FMOD_OK) + { + return result; + } + } + else +#endif +#ifdef FMOD_SUPPORT_IMAADPCM + if (srcmode & FSOUND_IMAADPCM && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + unsigned int pcm = position; + unsigned int pcmaligned = pcm; + unsigned int excessbytes = 0; + + pcmaligned /= 64; + pcmaligned *= 64; + + SoundI::getBytesFromSamples(pcmaligned, &pos, waveformat.channels, FMOD_SOUND_FORMAT_IMAADPCM); + + pos += mDataOffset[mCurrentIndex]; + + result = mFile->seek(pos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + SoundI::getBytesFromSamples(pcm - pcmaligned, &excessbytes, waveformat.channels, waveformat.format); + + while (excessbytes) + { + static char buff[1000]; + unsigned int read = 0, toread = 1000; + + if (toread > excessbytes) + { + toread = excessbytes; + } + + result = Codec::read(buff, toread, &read); + if (result != FMOD_OK) + { + break; + } + + excessbytes -= read; + } + } + else +#endif +#ifdef FMOD_SUPPORT_VAG + if (srcmode & FSOUND_VAG && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + mVAG->mSrcDataOffset = mDataOffset[mCurrentIndex]; + mVAG->mFile = mFile; + + FMOD_memcpy(mVAG->mWaveFormatMemory, &waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + + result = mVAG->setPositionInternal(subsound, position, postype); + if (result != FMOD_OK) + { + return result; + } + } + else +#endif +#ifdef FMOD_SUPPORT_CELT + if (srcmode & FSOUND_CELT && waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + mCELT->mSrcDataOffset = mDataOffset[mCurrentIndex]; + mCELT->mFile = mFile; + + FMOD_memcpy(mCELT->mWaveFormatMemory, &waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + + result = mCELT->setPositionInternal(subsound, position, postype); + if (result != FMOD_OK) + { + return result; + } + } + else +#endif + { + result = SoundI::getBytesFromSamples(position, &pos, waveformat.channels, waveformat.format); + if (result != FMOD_OK) + { + return result; + } + + pos += mDataOffset[mCurrentIndex]; + + result = mFile->seek(pos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecFSB::setPositionInternal", "done\n")); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::getPositionInternal(unsigned int *position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + + unsigned int pos; + FMOD_CODEC_WAVEFORMAT waveformat; + + getWaveFormatInternal(mCurrentIndex, &waveformat); + + result = mFile->tell(&pos); + if (result != FMOD_OK) + { + return result; + } + + pos -= mDataOffset[mCurrentIndex]; + + if (postype == FMOD_TIMEUNIT_PCM && waveformat.format == FMOD_SOUND_FORMAT_GCADPCM) + { + SoundI::getSamplesFromBytes(pos, position, waveformat.channels, waveformat.format); + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::getNumSyncPoints(int subsound, int *numsyncpoints) +{ + char *syncpointdata = mSyncPointData[subsound]; + + if (!syncpointdata) + { + *numsyncpoints = 0; + return FMOD_OK; + } + + syncpointdata += 4; /* Skip 'SYNC' label. */ + + *numsyncpoints = *((int *)syncpointdata); + + #ifdef PLATFORM_ENDIAN_BIG + *numsyncpoints = FMOD_SWAPENDIAN_DWORD(*numsyncpoints); + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::getSyncPointData(int subsound, int index, char **name, int *offset) +{ + char *syncpointdata = mSyncPointData[subsound]; + + syncpointdata += 8; /* Skip 'SYNC' label, Skip 'numsyncpoints' dword. */ + + if (mShdr[subsound]->mode & FSOUND_SYNCPOINTS_NONAMES) + { + SYNCDATA_NONAME *syncdata = (SYNCDATA_NONAME *)syncpointdata; + + *name = 0; + *offset = syncdata[index].offset; + + #ifdef PLATFORM_ENDIAN_BIG + *offset = FMOD_SWAPENDIAN_DWORD(*offset); + #endif + } + else + { + SYNCDATA *syncdata = (SYNCDATA *)syncpointdata; + + *name = syncdata[index].name; + *offset = syncdata[index].offset; + + #ifdef PLATFORM_ENDIAN_BIG + *offset = FMOD_SWAPENDIAN_DWORD(*offset); + #endif + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::soundcreateInternal(int subsound, FMOD_SOUND *sound) +{ + FMOD_RESULT result; + SoundI *s = (SoundI *)sound; + FMOD_CODEC_WAVEFORMAT waveformat; + + getWaveFormatInternal(subsound, &waveformat); + + if (!(mMode & FMOD_CREATESTREAM)) + { + mChannels = waveformat.channels; + } + + { + FMOD_FSB_SAMPLE_HEADER *shdr = (FMOD_FSB_SAMPLE_HEADER *)(mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS ? mFirstSample : mShdr[subsound]); + FMOD_FSB_SAMPLE_HEADER tmp; + + FMOD_memcpy(&tmp, shdr, sizeof(FMOD_FSB_SAMPLE_HEADER)); /* FMOD_memcpy'd instead of pointed to for alignment reasons */ + + result = s->setDefaults((float)tmp.deffreq, (float)tmp.defvol / 255.0f, tmp.defpan == 0 ? -1.0f : tmp.defpan == 255 ? 1.0f : tmp.defpan == 128 ? 0.0f : ((float)tmp.defpan / 255.0f * 2.0f) - 1.0f, tmp.defpri); + if (result != FMOD_OK) + { + return result; + } +#ifndef FMOD_FSB_FORCE_3_0 + result = s->set3DMinMaxDistance(tmp.mindistance, tmp.maxdistance); + if ((result != FMOD_OK) && (result != FMOD_ERR_NEEDS3D)) + { + return result; + } +#endif + } + + if (mSyncPointData) + { + int count, numsyncpoints; + + if (getNumSyncPoints(subsound, &numsyncpoints) == FMOD_OK) + { + char *name; + int offset; + + for (count = 0; count < numsyncpoints; count++) + { + getSyncPointData(subsound, count, &name, &offset); + + s->addSyncPointInternal(offset, FMOD_TIMEUNIT_PCM, name, 0, subsound, false); + } + + s->syncPointFixIndicies(); + } + } + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::getWaveFormatInternal(int index, FMOD_CODEC_WAVEFORMAT *waveformat_out) +{ + FMOD_memset(waveformat_out, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + + if (mHeader.mode & FMOD_FSB_SOURCE_BASICHEADERS) + { + unsigned int mode = 0; + + #ifdef PLATFORM_PS3_SPU + + char firstsamplemem[sizeof(FMOD_FSB_SAMPLE_HEADER) + 32]; // 80 + 32 bytes + char *firstsample = (char *)FMOD_ALIGNPOINTER(firstsamplemem, 16); + + FMOD_PS3_SPU_AlignedDMA((void **)&firstsample, (unsigned int)mFirstSample, sizeof(FMOD_FSB_SAMPLE_HEADER)); + + mFirstSample = (FMOD_FSB_SAMPLE_HEADER *)firstsample; + + #endif + + #ifndef PLATFORM_PS3_SPU + if (mFirstSample->mode & FSOUND_HW3D || mFirstSample->mode & FSOUND_HW2D) + { + mode |= FMOD_HARDWARE; + if (mFirstSample->mode & FSOUND_HW2D) + { + mode |= FMOD_2D; + } + if (mFirstSample->mode & FSOUND_HW3D) + { + mode |= FMOD_3D; + } + } + else + #endif + { + mode |= FMOD_SOFTWARE; + + if (mFirstSample->mode & FSOUND_2D) + { + mode |= FMOD_2D; + } + if (mFirstSample->mode & FSOUND_3D) + { + mode |= FMOD_3D; + } + } + + waveformat_out->mode = (FMOD_MODE)mode; + #ifndef PLATFORM_PS3_SPU + FMOD_strncpy(waveformat_out->name, mFirstSample->name, FMOD_STRING_MAXNAMELEN); + #endif + waveformat_out->channels = mFirstSample->numchannels; + waveformat_out->frequency = mFirstSample->deffreq; + + /* + Set FMOD Ex format based on fmod3 flags. + */ + if (mFirstSample->mode & FSOUND_8BITS) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM8; + } + else if (mFirstSample->mode & FSOUND_16BITS) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + else if (mFirstSample->mode & FSOUND_32BITS) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + else if (mFirstSample->mode & FSOUND_IMAADPCM) + { + #ifdef FMOD_SUPPORT_IMAADPCM + #ifdef PLATFORM_XBOX + if (mUserMode & FMOD_HARDWARE) + { + waveformat_out->format = FMOD_SOUND_FORMAT_IMAADPCM; + } + else + #endif + { + if (mADPCM) + { + waveformat_out->format = FMOD_SOUND_FORMAT_IMAADPCM; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + } + #else + return FMOD_ERR_FORMAT; + #endif + } + else if (mFirstSample->mode & FSOUND_VAG) + { + #ifdef FMOD_SUPPORT_VAG + if (mVAG) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + waveformat_out->blockalign = 28 * sizeof(short) * waveformat_out->channels; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_VAG; + } + #else + return FMOD_ERR_FORMAT; + #endif + } + #ifdef FMOD_SUPPORT_GCADPCM + else if (mFirstSample->mode & FSOUND_GCADPCM) + { + waveformat_out->format = FMOD_SOUND_FORMAT_GCADPCM; + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (mFirstSample->mode & FSOUND_XMA) + { + if ((waveformat_out->mode & FMOD_SOFTWARE || mUserMode & FMOD_SOFTWARE || mUserMode & FMOD_CREATESTREAM) && !(mUserMode & (FMOD_CREATECOMPRESSEDSAMPLE | FMOD_HARDWARE))) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_XMA; + waveformat_out->mode |= FMOD_CREATECOMPRESSEDSAMPLE; + } + } + #endif + #ifdef FMOD_SUPPORT_MPEG + else if (mFirstSample->mode & FSOUND_MPEG) + { + #ifdef FMOD_SUPPORT_STREAMING + if (mUserMode & FMOD_CREATECOMPRESSEDSAMPLE && (waveformat_out->mode & FMOD_SOFTWARE || mUserMode & FMOD_SOFTWARE) && !(mUserMode & FMOD_CREATESTREAM)) + #else + if (mUserMode & FMOD_CREATECOMPRESSEDSAMPLE && (waveformat_out->mode & FMOD_SOFTWARE || mUserMode & FMOD_SOFTWARE)) + #endif + { + waveformat_out->format = FMOD_SOUND_FORMAT_MPEG; + waveformat_out->mode |= FMOD_CREATECOMPRESSEDSAMPLE; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + } + #endif + + /* + Block size override based on machine specific block sizes. + */ + if (!waveformat_out->blockalign) + { + if (0) {} +#ifdef FMOD_SUPPORT_VAG + else if (waveformat_out->format == FMOD_SOUND_FORMAT_VAG) + { + waveformat_out->blockalign = 0; + while (waveformat_out->blockalign <= SOUND_READCHUNKSIZE) + { + waveformat_out->blockalign += 16 * waveformat_out->channels; /* Make the block size bigger to do bigger dma chunks. */ + } + waveformat_out->blockalign -= 16 * waveformat_out->channels; /* Rewind one to get it under. */ + } +#endif +#ifdef FMOD_SUPPORT_GCADPCM + else if (waveformat_out->format == FMOD_SOUND_FORMAT_GCADPCM) + { + waveformat_out->blockalign = 32 * waveformat_out->channels; + } +#endif +#ifdef FMOD_SUPPORT_XMA + else if (waveformat_out->format == FMOD_SOUND_FORMAT_XMA) + { + waveformat_out->blockalign = 2048; + } +#endif +#ifdef FMOD_SUPPORT_MPEG + else if (waveformat_out->format == FMOD_SOUND_FORMAT_MPEG) + { + waveformat_out->blockalign = 1152 * sizeof(signed short) * waveformat_out->channels; + } +#endif + else + { + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat_out->blockalign, waveformat_out->channels, waveformat_out->format); + } + } + + if (index) + { + waveformat_out->lengthbytes = mShdrb[index]->lengthcompressedbytes; + waveformat_out->lengthpcm = mShdrb[index]->lengthsamples; + } + else + { + if (mShdrb[index]) /* This being non 0 is a special case where inclusionlist + numsubsounds is set in exinfo. */ + { + waveformat_out->lengthbytes = mShdrb[index]->lengthcompressedbytes; + waveformat_out->lengthpcm = mShdrb[index]->lengthsamples; + } + else + { + waveformat_out->lengthpcm = mFirstSample->lengthsamples; + waveformat_out->lengthbytes = mFirstSample->lengthcompressedbytes; + } + } + + mode = mFirstSample->mode & FSOUND_CHANNELMODE_MASK; + if (mode == FSOUND_CHANNELMODE_ALLMONO) + { + waveformat_out->channelmask = SPEAKER_ALLMONO; + } + else if (mode == FSOUND_CHANNELMODE_ALLSTEREO) + { + waveformat_out->channelmask = SPEAKER_ALLSTEREO; + } + else if (mode == FSOUND_CHANNELMODE_PROTOOLS) + { + waveformat_out->channelmask = SPEAKER_PROTOOLS; + } + } + else + { + unsigned int mode = 0; + + #ifdef PLATFORM_PS3_SPU + + char shdrmem[sizeof(FMOD_FSB_SAMPLE_HEADER) + 32]; // 80 + 32 bytes + char *shdr = (char *)FMOD_ALIGNPOINTER(shdrmem, 16); + + unsigned int shdrmram = cellDmaGetUint32((uint64_t)&mShdr[index], TAG1, TID, RID); + + FMOD_PS3_SPU_AlignedDMA((void **)&shdr, shdrmram, sizeof(FMOD_FSB_SAMPLE_HEADER)); + + mShdr[0] = (FMOD_FSB_SAMPLE_HEADER *)shdr; + + index = 0; + + #endif + + #ifndef PLATFORM_PS3_SPU + if (mShdr[index]->mode & FSOUND_HW3D || mShdr[index]->mode & FSOUND_HW2D) + { + mode |= FMOD_HARDWARE; + if (mShdr[index]->mode & FSOUND_HW2D) + { + mode |= FMOD_2D; + } + if (mShdr[index]->mode & FSOUND_HW3D) + { + mode |= FMOD_3D; + } + } + else + #endif + { + mode |= FMOD_SOFTWARE; + + if (mShdr[index]->mode & FSOUND_2D) + { + mode |= FMOD_2D; + } + if (mShdr[index]->mode & FSOUND_3D) + { + mode |= FMOD_3D; + } + } + + if (mShdr[index]->mode & FSOUND_LOOP_NORMAL) + { + mode |= FMOD_LOOP_NORMAL; + } + else if (mShdr[index]->mode & FSOUND_LOOP_BIDI) + { + mode |= FMOD_LOOP_BIDI; + } + + waveformat_out->mode = (FMOD_MODE)mode; + #ifndef PLATFORM_PS3_SPU + int namelength = FMOD_MIN( sizeof(FMOD_CODEC_WAVEFORMAT().name), FMOD_STRING_MAXNAMELEN ); //hack - codec name length is hardcoded + FMOD_strncpy(waveformat_out->name, mShdr[index]->name, namelength); + FMOD_memset(waveformat_out->name + FMOD_FSB_NAMELEN, 0, namelength - FMOD_FSB_NAMELEN); + #endif + waveformat_out->channels = mShdr[index]->numchannels; + waveformat_out->frequency = mShdr[index]->deffreq; + waveformat_out->lengthpcm = mShdr[index]->lengthsamples; + waveformat_out->loopstart = mShdr[index]->loopstart; + waveformat_out->loopend = mShdr[index]->loopend; + waveformat_out->lengthbytes = mShdr[index]->lengthcompressedbytes; + + if (mShdr[index]->mode & FSOUND_8BITS) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM8; + } + else if (mShdr[index]->mode & FSOUND_16BITS) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + else if (mShdr[index]->mode & FSOUND_32BITS) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + else if (mShdr[index]->mode & FSOUND_IMAADPCM) + { + #ifdef FMOD_SUPPORT_IMAADPCM + #ifdef PLATFORM_XBOX + if (mUserMode & FMOD_HARDWARE) + { + waveformat_out->format = FMOD_SOUND_FORMAT_IMAADPCM; + } + else + #endif + { + if (mDecodeADPCM) + { + waveformat_out->format = FMOD_SOUND_FORMAT_IMAADPCM; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + } + #else + return FMOD_ERR_FORMAT; + #endif + } + else if (mShdr[index]->mode & FSOUND_VAG) + { + #ifdef FMOD_SUPPORT_VAG + if (mVAG) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + waveformat_out->blockalign = 28 * sizeof(short) * waveformat_out->channels; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_VAG; + } + #else + return FMOD_ERR_FORMAT; + #endif + } +#ifdef FMOD_SUPPORT_GCADPCM + else if (mShdr[index]->mode & FSOUND_GCADPCM) + { + waveformat_out->format = FMOD_SOUND_FORMAT_GCADPCM; + } +#endif +#ifdef FMOD_SUPPORT_XMA + else if (mShdr[index]->mode & FSOUND_XMA) + { + if (mUserMode & FMOD_CREATESTREAM || ((waveformat_out->mode & FMOD_SOFTWARE || mUserMode & FMOD_SOFTWARE) && !(mUserMode & FMOD_CREATECOMPRESSEDSAMPLE))) + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_XMA; + waveformat_out->mode |= FMOD_CREATECOMPRESSEDSAMPLE; + } + } +#endif +#ifdef FMOD_SUPPORT_MPEG + else if (mShdr[index]->mode & FSOUND_MPEG) + { + #ifdef FMOD_SUPPORT_STREAMING + if (mUserMode & FMOD_CREATECOMPRESSEDSAMPLE && !(mUserMode & FMOD_CREATESTREAM)) + #else + if (mUserMode & FMOD_CREATECOMPRESSEDSAMPLE) + #endif + { + waveformat_out->format = FMOD_SOUND_FORMAT_MPEG; + waveformat_out->mode |= FMOD_CREATECOMPRESSEDSAMPLE; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + } +#endif +#ifdef FMOD_SUPPORT_CELT + else if (mShdr[index]->mode & FSOUND_CELT) + { + #ifdef FMOD_SUPPORT_STREAMING + if (mUserMode & FMOD_CREATECOMPRESSEDSAMPLE && !(mUserMode & FMOD_CREATESTREAM)) + #else + if (mUserMode & FMOD_CREATECOMPRESSEDSAMPLE) + #endif + { + waveformat_out->format = FMOD_SOUND_FORMAT_CELT; + waveformat_out->mode |= FMOD_CREATECOMPRESSEDSAMPLE; + } + else + { + waveformat_out->format = FMOD_SOUND_FORMAT_PCM16; + } + } +#endif + + /* + Block size override based on machine specific block sizes. + */ + if (!waveformat_out->blockalign) + { + if (0) {} +#ifdef FMOD_SUPPORT_VAG + else if (waveformat_out->format == FMOD_SOUND_FORMAT_VAG) + { + waveformat_out->blockalign = 0; + while (waveformat_out->blockalign <= SOUND_READCHUNKSIZE) + { + waveformat_out->blockalign += 16 * waveformat_out->channels; /* Make the block size bigger to do bigger dma chunks. */ + } + waveformat_out->blockalign -= 16 * waveformat_out->channels; /* Rewind one to get it under. */ + } +#endif +#ifdef FMOD_SUPPORT_GCADPCM + else if (waveformat_out->format == FMOD_SOUND_FORMAT_GCADPCM) + { + waveformat_out->blockalign = 32 * waveformat_out->channels; + } +#endif +#ifdef FMOD_SUPPORT_XMA + else if (waveformat_out->format == FMOD_SOUND_FORMAT_XMA) + { + waveformat_out->blockalign = 2048; + } +#endif +#ifdef FMOD_SUPPORT_MPEG + else if (waveformat_out->format == FMOD_SOUND_FORMAT_MPEG) + { + waveformat_out->blockalign = 1152 * sizeof(signed short) * waveformat_out->channels; + } +#endif + else + { + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat_out->blockalign, waveformat_out->channels, waveformat_out->format); + } + } + + mode = mShdr[index]->mode & FSOUND_CHANNELMODE_MASK; + if (mode == FSOUND_CHANNELMODE_ALLMONO) + { + waveformat_out->channelmask = SPEAKER_ALLMONO; + } + else if (mode == FSOUND_CHANNELMODE_ALLSTEREO) + { + waveformat_out->channelmask = SPEAKER_ALLSTEREO; + } + else if (mode == FSOUND_CHANNELMODE_PROTOOLS) + { + waveformat_out->channelmask = SPEAKER_PROTOOLS; + } + } + + if (!waveformat_out->channelmask) + { + if (waveformat_out->channels == 4) + { + waveformat_out->channelmask = SPEAKER_QUAD; + } + else if (waveformat_out->channels == 6) + { + waveformat_out->channelmask = SPEAKER_5POINT1; + } + } + + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mShdrb) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(void *) * mHeader.numsamples); + } + else if (mShdr) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(void *) * mHeader.numsamples); + } + + tracker->add(false, FMOD_MEMBITS_CODEC, mHeader.shdrsize); + + if (mDataOffset) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(unsigned int) * mHeader.numsamples); + } + + #ifdef FMOD_SUPPORT_IMAADPCM + if (mADPCM) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(CodecWav)); + + if (mADPCM->mWaveFormatMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(FMOD_CODEC_WAVEFORMAT)); + } + + if (mADPCM->mPCMBufferMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, mADPCM->mPCMBufferLengthBytes + 16); + } + } + #endif + #ifdef FMOD_SUPPORT_MPEG + if (mMPEG) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(CodecMPEG)); + + if (mMPEG->mWaveFormatMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(FMOD_CODEC_WAVEFORMAT)); + } + + if (mMPEG->mMemoryBlockMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(CodecMPEG_MemoryBlock) * (mChannels > 2 ? mChannels : 1) + 16); + } + + if (mMPEG->mPCMBufferMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, mMPEG->mPCMBufferLengthBytes + 16); + } + } + #endif + #ifdef FMOD_SUPPORT_VAG + if (mVAG) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(CodecVAG)); + + if (mVAG->mWaveFormatMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(FMOD_CODEC_WAVEFORMAT)); + } + } + #endif + + if (mReadBuffer) + { + tracker->add(false, FMOD_MEMBITS_CODEC, mReadBufferLength); + } + + if (mPCMBufferMemory) + { + tracker->add(false, FMOD_MEMBITS_CODEC, mPCMBufferLengthBytes + 16); + } + + if (mSyncPointData) + { + tracker->add(false, FMOD_MEMBITS_CODEC, mHeader.numsamples * sizeof(char *)); + } + + +#ifdef FMOD_FSB_USEHEADERCACHE + /* + NEED TO WORK OUT HOW TO MAKE THIS ONLY HAPPEN ONCE. WE CANT PUT IT IN SYSTEMI::GETMEMORYUSED - THEN THE PLUGIN VERSION COMPLAINS. + */ + { + CodecFSBCache *current = 0; + + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gAsyncCrit); + + for (current = (CodecFSBCache *)CodecFSB::gCacheHead.getNext(); current != &CodecFSB::gCacheHead; current = (CodecFSBCache *)current->getNext()) + { + tracker->add(false, FMOD_MEMBITS_CODEC, sizeof(CodecFSBCache)); + } + + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gAsyncCrit); + } +#endif + return FMOD_OK; +} + +#endif + + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::resetInternal() +{ + #ifdef FMOD_SUPPORT_XMA + if (mXMA) + { + mXMA->reset(); + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + if (mADPCM) + { + mADPCM->reset(); + } + #endif + #ifdef FMOD_SUPPORT_MPEG + if (mMPEG) + { + mMPEG->reset(); + } + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecFSB::canPointInternal() +{ + #ifdef FMOD_SUPPORT_XMA + if (mXMA) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + if (mADPCM) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + #endif + #ifdef FMOD_SUPPORT_MPEG + if (mMPEG) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->setPositionInternal(subsound, position, postype); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::getPositionCallback(FMOD_CODEC_STATE *codec, unsigned int *position, FMOD_TIMEUNIT postype) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->getPositionInternal(position, postype); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->soundcreateInternal(subsound, sound); +} + +#endif //!PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::getWaveFormatCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_CODEC_WAVEFORMAT *waveformat) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->getWaveFormatInternal(subsound, waveformat); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::resetCallback(FMOD_CODEC_STATE *codec) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::canPointCallback(FMOD_CODEC_STATE *codec) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->canPointInternal(); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecFSB::getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker) +{ + CodecFSB *fsb = (CodecFSB *)codec; + + return fsb->getMemoryUsed(tracker); +} +#endif + + +#endif //!PLATFORM_PS3_SPU + +} + +#endif + diff --git a/src/fmod_codec_fsb.h b/src/fmod_codec_fsb.h new file mode 100755 index 0000000..30641a8 --- /dev/null +++ b/src/fmod_codec_fsb.h @@ -0,0 +1,243 @@ +#ifndef _FMOD_CODEC_FSB_H +#define _FMOD_CODEC_FSB_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FSB + +#include "fmod_codeci.h" +#include "fmod_syncpoint.h" + +#define FMOD_FSB_NAMELEN 30 + +#define FMOD_FSB_SOURCE_FORMAT 0x00000001 /* all samples stored in their original compressed format */ +#define FMOD_FSB_SOURCE_BASICHEADERS 0x00000002 /* samples should use the basic header structure */ +#define FMOD_FSB_SOURCE_ENCRYPTED 0x00000004 /* all sample data is encrypted */ +#define FMOD_FSB_SOURCE_BIGENDIANPCM 0x00000008 /* pcm samples have been written out in big-endian format */ +#define FMOD_FSB_SOURCE_NOTINTERLEAVED 0x00000010 /* Sample data is not interleaved. */ +#define FMOD_FSB_SOURCE_MPEG_PADDED 0x00000020 /* Mpeg frames are now rounded up to the nearest 2 bytes for normal sounds, or 16 bytes for multichannel. */ +#define FMOD_FSB_SOURCE_MPEG_PADDED4 0x00000040 /* Mpeg frames are now rounded up to the nearest 4 bytes for normal sounds, or 16 bytes for multichannel. */ + +//#define FMOD_FSB_FORCE_3_0 + +#define FMOD_FSB_VERSION_3_0 0x00030000 /* FSB version 3.0 */ +#define FMOD_FSB_VERSION_3_1 0x00030001 /* FSB version 3.1 */ +#define FMOD_FSB_VERSION_4_0 0x00040000 /* FSB version 4.0 */ + +/* + FMOD 3 defines. +*/ +#define FSOUND_LOOP_OFF 0x00000001 /* For non looping samples. */ +#define FSOUND_LOOP_NORMAL 0x00000002 /* For forward looping samples. */ +#define FSOUND_LOOP_BIDI 0x00000004 /* For bidirectional looping samples. (no effect if in hardware). */ +#define FSOUND_8BITS 0x00000008 /* For 8 bit samples. */ +#define FSOUND_16BITS 0x00000010 /* For 16 bit samples. */ +#define FSOUND_MONO 0x00000020 /* For mono samples. */ +#define FSOUND_STEREO 0x00000040 /* For stereo samples. */ +#define FSOUND_UNSIGNED 0x00000080 /* For user created source data containing unsigned samples. */ +#define FSOUND_SIGNED 0x00000100 /* For user created source data containing signed data. */ +#define FSOUND_MPEG 0x00000200 /* For MPEG layer 2/3 data. */ +#define FSOUND_CHANNELMODE_ALLMONO 0x00000400 /* Sample is a collection of mono channels. */ +#define FSOUND_CHANNELMODE_ALLSTEREO 0x00000800 /* Sample is a collection of stereo channel pairs */ +#define FSOUND_HW3D 0x00001000 /* Attempts to make samples use 3d hardware acceleration. (if the card supports it) */ +#define FSOUND_2D 0x00002000 /* Tells software (not hardware) based sample not to be included in 3d processing. */ +#define FSOUND_SYNCPOINTS_NONAMES 0x00004000 /* Specifies that syncpoints are present with no names */ +#define FSOUND_DUPLICATE 0x00008000 /* This subsound is a duplicate of the previous one i.e. it uses the same sample data but w/different mode bits */ +#define FSOUND_CHANNELMODE_PROTOOLS 0x00010000 /* Sample is 6ch and uses L C R LS RS LFE standard. */ +#define FSOUND_MPEGACCURATE 0x00020000 /* For FSOUND_Stream_Open - for accurate FSOUND_Stream_GetLengthMs/FSOUND_Stream_SetTime. WARNING, see FSOUND_Stream_Open for inital opening time performance issues. */ +#define FSOUND_HW2D 0x00080000 /* 2D hardware sounds. allows hardware specific effects */ +#define FSOUND_3D 0x00100000 /* 3D software sounds */ +#define FSOUND_32BITS 0x00200000 /* For 32 bit (float) samples. */ +#define FSOUND_IMAADPCM 0x00400000 /* Contents are stored compressed as IMA ADPCM */ +#define FSOUND_VAG 0x00800000 /* For PS2 only - Contents are compressed as Sony VAG format */ +#define FSOUND_XMA 0x01000000 /* For Xbox360 only - Contents are compressed as XMA format */ +#define FSOUND_GCADPCM 0x02000000 /* For Gamecube only - Contents are compressed as Gamecube DSP-ADPCM format */ +#define FSOUND_MULTICHANNEL 0x04000000 /* For PS2 and Gamecube only - Contents are interleaved into a multi-channel (more than stereo) format */ +#define FSOUND_OGG 0x08000000 /* For vorbis encoded ogg data */ +#define FSOUND_CELT 0x08000000 /* For vorbis encoded ogg data */ +#define FSOUND_MPEG_LAYER3 0x10000000 /* Data is in MP3 format. */ +#define FSOUND_MPEG_LAYER2 0x00040000 /* Data is in MP2 format. */ +#define FSOUND_LOADMEMORYIOP 0x20000000 /* For PS2 only - "name" will be interpreted as a pointer to data for streaming and samples. The address provided will be an IOP address */ +#define FSOUND_IMAADPCMSTEREO 0x20000000 /* Signify IMA ADPCM is actually stereo not two interleaved mono */ +#define FSOUND_IGNORETAGS 0x40000000 /* Skips id3v2 etc tag checks when opening a stream, to reduce seek/read overhead when opening files (helps with CD performance) */ +#define FSOUND_SYNCPOINTS 0x80000000 /* Specifies that syncpoints are present */ + +#define FSOUND_CHANNELMODE_MASK (FSOUND_CHANNELMODE_ALLMONO | FSOUND_CHANNELMODE_ALLSTEREO | FSOUND_CHANNELMODE_PROTOOLS) +#define FSOUND_CHANNELMODE_DEFAULT 0x00000000 /* Determine channel assignment automatically from channel count. */ +#define FSOUND_CHANNELMODE_RESERVED 0x00000C00 + +#define FSOUND_NORMAL (FSOUND_16BITS | FSOUND_SIGNED | FSOUND_MONO) + +#define FSB_SAMPLE_DATA_ALIGN 32 + +namespace FMOD +{ + typedef struct + { + char id[4]; /* 'FSB3' */ + int numsamples; /* number of samples in the file */ + unsigned int shdrsize; /* size in bytes of all of the sample headers including extended information */ + unsigned int datasize; /* size in bytes of compressed sample data */ + + unsigned int version; /* extended fsb version */ + unsigned int mode; /* flags that apply to all samples in the fsb */ + } FMOD_FSB3_HEADER; /* 24 bytes */ + + typedef struct + { + char id[4]; /* 'FSB4' */ + int numsamples; /* number of samples in the file */ + unsigned int shdrsize; /* size in bytes of all of the sample headers including extended information */ + unsigned int datasize; /* size in bytes of compressed sample data */ + + unsigned int version; /* extended fsb version */ + unsigned int mode; /* flags that apply to all samples in the fsb */ + FMOD_UINT64 hash; /* trunacted MD5 hash generated using only information which would break FEV/FSB combatibility */ + FMOD_GUID guid; /* Unique identifier. */ + + } FMOD_FSB_HEADER; /* 48 bytes */ + + typedef struct + { + unsigned short size; + char name[FMOD_FSB_NAMELEN]; + + unsigned int lengthsamples; + unsigned int lengthcompressedbytes; + unsigned int loopstart; + unsigned int loopend; + + unsigned int mode; + int deffreq; + unsigned short defvol; + short defpan; + unsigned short defpri; + unsigned short numchannels; + #ifndef FMOD_FSB_FORCE_3_0 + float mindistance; + float maxdistance; + unsigned int size_32bits; + unsigned short varvol; + short varpan; + #endif + } FMOD_FSB_SAMPLE_HEADER; /* 80 bytes */ + + typedef struct + { + unsigned int lengthsamples; + unsigned int lengthcompressedbytes; + + } FMOD_FSB_SAMPLE_HEADER_BASIC; /* 8 bytes */ + + + class CodecXMA; + class CodecWav; + class CodecMPEG; + class CodecVAG; + class CodecCELT; + class ChannelSoftware; + class ChannelOpenAL; + class CodecFSB; + + #define FMOD_FSB_USEHEADERCACHE + +#ifdef FMOD_FSB_USEHEADERCACHE + class CodecFSBCache : public LinkedListNode + { + public: + FMOD_FSB_HEADER mHeader; /* These should be a GUID that is stored with the FSB. */ + FMOD_FSB_SAMPLE_HEADER **mShdr; /* array of sample header pointers */ + FMOD_FSB_SAMPLE_HEADER_BASIC **mShdrb; /* array of sample header pointers */ + char *mShdrData; /* Pointer to data block */ + unsigned int *mDataOffset; /* array of offsets to raw sample data */ + int mShareCount; + bool mStillLoading; + }; +#endif + + class CodecFSB : public Codec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class ChannelSoftware; + friend class ChannelOpenAL; + friend class DSPCodec; + + private: + + FMOD_PPCALIGN16(FMOD_FSB_HEADER mHeader); + FMOD_PPCALIGN16(FMOD_FSB_SAMPLE_HEADER **mShdr); /* array of sample header pointers */ + FMOD_PPCALIGN16(FMOD_FSB_SAMPLE_HEADER_BASIC **mShdrb); /* array of sample header pointers */ + FMOD_PPCALIGN16(FMOD_FSB_SAMPLE_HEADER *mFirstSample); /* first sample header */ + + unsigned int *mDataOffset; /* array of offsets to raw sample data */ + int mCurrentIndex; /* current FSB index */ +#ifdef FMOD_FSB_USEHEADERCACHE + CodecFSBCache *mCacheEntry; +#endif + + char **mSyncPointData; + + #ifdef FMOD_SUPPORT_XMA + CodecXMA *mXMA; + bool mDecodeXMA; + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + CodecWav *mADPCM; + bool mDecodeADPCM; + #endif + #ifdef FMOD_SUPPORT_MPEG + CodecMPEG *mMPEG; + #endif + #ifdef FMOD_SUPPORT_VAG + CodecVAG *mVAG; + #endif + #ifdef FMOD_SUPPORT_CELT + CodecCELT *mCELT; + #endif + + int mChannels; + FMOD_MODE mUserMode; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getPositionInternal(unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT soundcreateInternal(int subsound, FMOD_SOUND *sound); + FMOD_RESULT getWaveFormatInternal(int index, FMOD_CODEC_WAVEFORMAT *waveformat); + FMOD_RESULT resetInternal(); + FMOD_RESULT canPointInternal(); + FMOD_RESULT getNumSyncPoints(int subsound, int *numsyncpoints); + FMOD_RESULT getSyncPointData(int subsound, int index, char **name, int *offset); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK getPositionCallback(FMOD_CODEC_STATE *codec, unsigned int *position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + static FMOD_RESULT F_CALLBACK getWaveFormatCallback(FMOD_CODEC_STATE *codec_state, int index, FMOD_CODEC_WAVEFORMAT *waveformat); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_CODEC_STATE *codec_state); + static FMOD_RESULT F_CALLBACK canPointCallback(FMOD_CODEC_STATE *codec_state); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker); +#endif + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + +#ifdef FMOD_FSB_USEHEADERCACHE + static CodecFSBCache gCacheHead; +#endif + + FMOD_FSB_SAMPLE_HEADER **getShdr() { return mShdr; } + FMOD_UINT64 getHash() { return mHeader.hash; } + }; +} + +#endif /* FMOD_SUPPORT_FSB */ + +#endif + diff --git a/src/fmod_codec_it.cpp b/src/fmod_codec_it.cpp new file mode 100755 index 0000000..e545bbf --- /dev/null +++ b/src/fmod_codec_it.cpp @@ -0,0 +1,6614 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_IT + +#include "fmod.h" + +#include "fmod_channel_software.h" +#include "fmod_codec_it.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_dsp_itecho.h" +#include "fmod_dsp_lowpass2.h" +#include "fmod_file.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_string.h" + +#include +#include +#include + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX itcodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecIT::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecIT::getDescriptionEx() +{ + FMOD_memset(&itcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + itcodec.name = "FMOD IT Codec"; + itcodec.version = 0x00010100; + itcodec.timeunits = (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_MODORDER | FMOD_TIMEUNIT_MODROW | FMOD_TIMEUNIT_MODPATTERN); + itcodec.defaultasstream = 1; + itcodec.open = &CodecIT::openCallback; + itcodec.close = &CodecIT::closeCallback; + itcodec.read = &CodecIT::readCallback; + itcodec.getlength = &MusicSong::getLengthCallback; + itcodec.setposition = &CodecIT::setPositionCallback; + itcodec.getposition = &MusicSong::getPositionCallback; + + itcodec.getmusicnumchannels = &MusicSong::getMusicNumChannelsCallback; + itcodec.setmusicchannelvolume = &MusicSong::setMusicChannelVolumeCallback; + itcodec.getmusicchannelvolume = &MusicSong::getMusicChannelVolumeCallback; + + itcodec.mType = FMOD_SOUND_TYPE_IT; + itcodec.mSize = sizeof(CodecIT); + + return &itcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::calculateLength() +{ + waveformat[0].lengthpcm = 0; + + play(); + + while (!mFinished) + { + update(false); + + waveformat[0].lengthpcm += mMixerSamplesPerTick; + } + + stop(); + + return FMOD_OK; +} + + +#define FMUSIC_ITLINEARPERIOD2HZ(_per) ( (int)(8363.0f*FMOD_POW(2.0f, (float)((5.0f*12.0f*64.0f - (float)_per) / (12.0*64.0)) )) ) + + +#define FMUSIC_PLAYPACKED + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::readBits(unsigned char b, unsigned int *result) /* reads b bits from the stream */ +{ + unsigned int value,value2; + + if (b <= mSrcRemBits) + { + value2 = value = *mSrcPos; + + #ifdef PLATFORM_ENDIAN_BIG + value2 = value = FMOD_SWAPENDIAN_DWORD(value); + #endif + + value &= ((1<>= b; + + #ifdef PLATFORM_ENDIAN_BIG + value2 = FMOD_SWAPENDIAN_DWORD(value2); /* put it BACK into little endia format for memory storage */ + #endif + + *mSrcPos = value2; + mSrcRemBits -= b; + } + else + { + unsigned int nbits = b-mSrcRemBits; + + value = *mSrcPos++; + + #ifdef PLATFORM_ENDIAN_BIG + value = FMOD_SWAPENDIAN_DWORD(value); + #endif + + value2 = *mSrcPos; + + #ifdef PLATFORM_ENDIAN_BIG + value2 = FMOD_SWAPENDIAN_DWORD(value2); + #endif + + value |= ((value2 & ((1<>= nbits; + + #ifdef PLATFORM_ENDIAN_BIG + value2 = FMOD_SWAPENDIAN_DWORD(value2); /* put it BACK into little endian format */ + #endif + + *mSrcPos = value2; + + mSrcRemBits= (unsigned char)(32-nbits); + } + + if (result) + { + *result = value; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::readBlock(signed char **buff) /* gets block of compressed data from file */ +{ + unsigned short size; + + /* + Read 1 byte at a time to save misaligned reads. + */ + size = ((*buff)[1] << 8); + size |= ((*buff)[0] & 0xFF); + (*buff)+=2; + + mSrcBuffer = (unsigned int *)FMOD_Memory_Alloc(size * 2); + if (!mSrcBuffer) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memcpy(mSrcBuffer, *buff, size); + *buff+= size; + + mSrcPos = mSrcBuffer; + mSrcRemBits = 32; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::freeBlock() /* frees that block again */ +{ + if (mSrcBuffer) + { + FMOD_Memory_Free(mSrcBuffer); + mSrcBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + decompresses 8-bit it214 sample + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::decompress8(void **src, void *dst, int len, bool it215, int channels) +{ + signed char *destbuf; /* the destination buffer which will be returned */ + unsigned short blklen; /* length of compressed data block in samples */ + unsigned short blkpos; /* position in block */ + unsigned char width; /* actual "bit width" */ + unsigned int value; /* value read from file to be processed */ + signed char d1, d2; /* integrator buffers (d2 for it2.15) */ + signed char *destpos; + + destbuf = (signed char *)dst; + if (!destbuf) + { + return FMOD_ERR_INVALID_PARAM; + } + if (!src || !*src) + { + return FMOD_ERR_INVALID_PARAM; + } + + destpos=destbuf; /* position in output buffer */ + + /* + Now unpack data till the dest buffer is full + */ + while (len) + { + FMOD_RESULT result; + + /* + Read a new block of compressed data and reset variables + */ + result = readBlock((signed char **)src); + if (result != FMOD_OK) + { + return result; + } + + blklen=(len<0x8000)?len:0x8000; + blkpos=0; + + width=9; /* start with width of 9 bits */ + d1=d2=0; /* reset integrator buffers */ + + /* + Now uncompress the data block + */ + while (blkpos read new width; */ + width = (value>(9-width)) - 4; /* lower border for width chg */ + + if (value > border && value <= (border + 8U)) + { + value-=border; /* convert width to 1-8 */ + width = (value>=shift; + } + else + v = (signed char)value; + + /* integrate upon the sample values */ + d1+=v; + d2+=d1; + + /* ... and store it into the buffer */ + *(destpos+=channels)=it215?d2:d1; + blkpos++; + } + + /* + Now subtract block length from total length and go on + */ + freeBlock(); + len-=blklen; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + decompresses 8-bit it214 sample + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::decompress16(void **src, void *dst, int len, bool it215, int channels) +{ + short *destbuf; /* the destination buffer which will be returned */ + unsigned short blklen; /* length of compressed data block in samples */ + unsigned short blkpos; /* position in block */ + unsigned char width; /* actual "bit width" */ + unsigned int value; /* value read from file to be processed */ + short d1, d2; /* integrator buffers (d2 for it2.15) */ + short *destpos; + + destbuf = (short *)dst; + if (!destbuf) + { + return FMOD_ERR_INVALID_PARAM; + } + if (!src || !*src) + { + return FMOD_ERR_INVALID_PARAM; + } + + destpos=destbuf; /* position in output buffer */ + + /* + Now unpack data till the dest buffer is full + */ + while (len) + { + FMOD_RESULT result; + + /* + Read a new block of compressed data and reset variables + */ + result = readBlock((signed char **)src); + if (result != FMOD_OK) + { + return result; + } + blklen=(len<0x4000)?len:0x4000; /* 0x4000 samples => 0x8000 bytes again */ + blkpos=0; + + width=17; /* start with width of 17 bits */ + d1=d2=0; /* reset integrator buffers */ + + /* + Now uncompress the data block + */ + while (blkpos read new width; */ + width = (unsigned char)((value>(17-width)) - 8; /* lower border for width chg */ + + if (value > border && value <= (unsigned short)(border+16)) + { + value-=border; /* convert width to 1-8 */ + width = (unsigned char)((value>=shift; + } + else + { + v = (short)value; + } + + /* + Integrate upon the sample values + */ + d1+=v; + d2+=d1; + + /* + ... and store it into the buffer + */ + *(destpos+=channels)=it215?d2:d1; + blkpos++; + } + + /* + Now subtract block lenght from total length and go on + */ + freeBlock(); + len-=blklen; + + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::volumeSlide() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + if (!(mVolumeSlide & 0xF)) + { + mVolume += (mVolumeSlide >> 4); + } + if (!(mVolumeSlide >> 4)) + { + mVolume -= (mVolumeSlide & 0xF); + } + + if (mVolume > 64) + { + mVolume = 64; + } + if (mVolume < 0) + { + mVolume = 0; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::panSlide() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + if (!(mPanSlide & 0xF)) + { + mPan -= (mPanSlide >> 4); + } + if (!(mPanSlide >> 4)) + { + mPan += (mPanSlide & 0xF); + } + + if (mPan > 64) + { + mPan = 64; + } + if (mPan < 0) + { + mPan = 0; + } + + vcptr->mNoteControl |= FMUSIC_PAN; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] + +put Gx volume column on a row and Gxx on a row TOGETHER +You'll notice the actual speed it uses +is Gxx + 1 +instead of Gxx + Gx + + +Gx isn't just a simple equation to find the speed +you have to use a little algorithm +here is what it looks like + + +void CALLING mpTonePortaCmdITStyle ( usigned char arg ) +{ + if ( mpTicks != 0 ) +} +} + mpTonePortamento ( 1 << 6 + ( 0x20 * ( arg - 6 ) ) ); + mpTonePortamento ( 1 << arg ); + else if ( arg < 7 ) mpTonePortamento ( arg ); + else if ( arg < 2 ) + +Thats for the volume column +( ( chan->info != 0 ) && ( arg != 0 ) ) ) arg++; + if ( ( ( chan->cmd == mpcTonePortamento ) || ( chan->cmd == mpcToneVol ) ) && + +Oh IT doesn't like to tone portamento twice so you don't want to call 2 TonePortamento routines +like even if there is a Gx on the volume column and one on the effect. You only call it once +You just add to the speed +1 if there are 2. There is a flag you have to set like slidAlready = 0; + + + +*/ +FMOD_RESULT MusicChannelIT::portamento() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + MusicSong *mod = mModule; + + if (mPortaReached) + { + return FMOD_OK; + } + + /* + Slide pitch down if it needs to. + */ + if (vcptr->mFrequency < mPortaTarget) + { + if (mod->mMusicFlags & FMUSIC_ITFLAGS_EFFECT_G) + { + vcptr->mFrequency += (int)mPortaSpeed << 2; + } + else + { + vcptr->mFrequency += (int)mPortaUpDown << 2; + } + + if (vcptr->mFrequency >= mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + mPortaReached = true; + } + } + + /* + Slide pitch up if it needs too. + */ + else if (vcptr->mFrequency > mPortaTarget) + { + if (mod->mMusicFlags & FMUSIC_ITFLAGS_EFFECT_G) + { + vcptr->mFrequency -= (int)mPortaSpeed << 2; + } + else + { + vcptr->mFrequency -= (int)mPortaUpDown << 2; + } + + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + mPortaReached = true; + } + } + + /* + if (glissando[track]) + { + } + */ + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + to carry out a vibrato at a certain depth and speed + + [PARAMETERS] + track - the track number to do the vibrato too + + [RETURN_VALUE] + + [REMARKS] + AND'ing temp with 31 removes the sign bit giving the abs value + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::vibrato() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + MusicSong *mod = mModule; + + temp = (unsigned char)(mVibPos & 31); + + switch (mWaveControlVibrato) + { + case 0: + { + delta = gSineTable[temp]; /* sine */ + break; + } + case 1: + { + temp <<= 3; /* ramp down */ + if (mVibPos < 0) + { + temp=(unsigned char)(255-temp); + } + delta=temp; + break; + } + case 2: + { + delta = 255; /* square */ + break; + } + case 3: + { + delta = FMOD_RAND()&255; /* random */ + break; + } + default: + { + delta = 0; + } + }; + + delta *= mVibDepth; + delta >>=7; + delta <<=1; /* we use 4*periods so make vibrato 4 times bigger */ + + if (mod->mMusicFlags & FMUSIC_ITFLAGS_OLD_IT_EFFECTS) + { + delta <<= 1; + } + + mVibPos += mVibSpeed; + if (mVibPos > 31) + { + mVibPos -= 64; + } + + if (mVibPos >= 0) + { + vcptr->mFrequencyDelta += -delta; + } + else + { + vcptr->mFrequencyDelta += delta; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::fineVibrato() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + MusicSong *mod = mModule; + + temp = (unsigned char)(mVibPos & 31); + + switch (mWaveControlVibrato) + { + case 0: + { + delta = gSineTable[temp]; /* sine */ + break; + } + case 1: + { + temp <<= 3; /* ramp down */ + if (mVibPos < 0) + { + temp=255-temp; + } + delta=temp; + break; + } + case 2: + { + delta = 255; /* square */ + break; + } + case 3: + { + delta = FMOD_RAND()&255; /* random */ + break; + } + default: + { + delta = 0; + } + }; + + delta *= mVibDepth; + delta >>=7; + + if (mod->mMusicFlags & FMUSIC_ITFLAGS_OLD_IT_EFFECTS) + { + delta <<= 1; + } + + if (mVibPos >= 0) + { + vcptr->mFrequencyDelta += delta; + } + else + { + vcptr->mFrequencyDelta += -delta; + } + + mVibPos += mVibSpeed; + if (mVibPos > 31) + { + mVibPos -= 64; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + To carry out a tremolo at a certain depth and speed + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::tremolo() +{ + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (unsigned char)(mTremoloPosition & 31); + + switch (mWaveControlTremolo) + { + case 0: + { + mVolumeDelta = gSineTable[temp]; /* sine */ + break; + } + case 1: + { + temp <<= 3; /* ramp down */ + if (mTremoloPosition < 0) + { + temp=255-temp; + } + mVolumeDelta=temp; + break; + } + case 2: + { + mVolumeDelta = 255; /* square */ + break; + } + case 3: + { + mVolumeDelta = gSineTable[temp]; /* random (just use sine for now) */ + break; + } + }; + + mVolumeDelta *= mTremoloDepth; + mVolumeDelta >>= 6; + + if (mTremoloPosition >= 0) + { + if (mVolume + mVolumeDelta > 64) + { + mVolumeDelta = 64 - mVolume; + } + } + else + { + if ((short)(mVolume-mVolumeDelta) < 0) + { + mVolumeDelta = mVolume; + } + + mVolumeDelta = -mVolumeDelta; /* flip it */ + } + + mTremoloPosition += mTremoloSpeed; + if (mTremoloPosition > 31) + { + mTremoloPosition -=64; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + To carry out a panbrello at a certain depth and speed + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::panbrello() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + switch (mWaveControlPan) + { + case 0: + { + vcptr->mPanDelta = (int)gFineSineTable[mPanbrelloPos]; /* sine */ + break; + } + case 1: + { + vcptr->mPanDelta = (128-mPanbrelloPos)>>1; /* ramp left */ + break; + } + case 2: + { + if (mPanbrelloPos < 128) + { + vcptr->mPanDelta = 64; /* square */ + } + else + { + vcptr->mPanDelta = -64; + } + break; + } + case 3: + { + vcptr->mPanDelta = (int)gFineSineTable[mPanbrelloPos]; /* sine */ + break; + } + }; + vcptr->mPanDelta *= mPanbrelloDepth; + vcptr->mPanDelta >>= 5; + + if (mPanbrelloPos >= 0) + { + if (vcptr->mPan + vcptr->mPanDelta > 64) + { + vcptr->mPanDelta = 64 - vcptr->mPan; + } + } + else + { + if ((short)(vcptr->mPan-vcptr->mPanDelta) < 0) + { + vcptr->mPanDelta = vcptr->mPan; + } + vcptr->mPanDelta = -vcptr->mPanDelta; + } + + mPanbrelloPos += mPanbrelloSpeed; + if (mPanbrelloPos > 255) + { + mPanbrelloPos -= 256; + } + + vcptr->mNoteControl |= FMUSIC_PAN; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Inumpoints = iptr->mVolumeNumPoints + v = &iptr->mVolumePoints + type = iptr->mVolumeType + mLoopStart = iptr->mVolumeSustainLoopStart + loopend = iptr->mVolumeSustainLoopEnd + control = FMUSIC_VOLUME + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::processEnvelope(MusicEnvelopeState *env, MusicVirtualChannel *vcptr, int Inumpoints, MusicEnvelopeNode *v, int type, int mLoopStart, int loopend, int susmLoopStart, int susloopend, unsigned char control) +{ + if (env->mPosition < Inumpoints) + { + if (env->mTick != v[env->mPosition].mTick) + { + env->mFraction += env->mDelta; /* interpolate */ + + if ((int)env->mFraction < 0 && type == FMUSIC_VOLUME) + { + env->mFraction = 0; + } + } + else + { + while (env->mTick == v[env->mPosition].mTick && env->mPosition < Inumpoints) /* if we are at the correct tick for the position */ + { + int currpos, nextpos; + int currtick, nexttick; + int currval, nextval, tickdiff; + + mRestartenv: + + currpos = env->mPosition; + nextpos = env->mPosition + 1; + + currtick = v[currpos].mTick; /* get tick at this point */ + nexttick = v[nextpos].mTick; /* get tick at next point */ + + currval = v[currpos].mValue << 16; /* get val at this point << 16 */ + nextval = v[nextpos].mValue << 16; /* get val at next point << 16 */ + + /* + Handle sustain loop + */ + if ((type & FMUSIC_ENVELOPE_SUSTAIN) && currpos >= susloopend && !vcptr->mKeyOff) + { + if (susloopend == susmLoopStart) + { + env->mValue = v[currpos].mValue; + return FMOD_OK; + } + env->mPosition = susmLoopStart; + env->mTick = v[env->mPosition].mTick - 1; + + goto mRestartenv; + } + + /* + Handle loop + */ + if ((type & FMUSIC_ENVELOPE_LOOP) && env->mPosition >= loopend) + { + if (loopend <= mLoopStart) + { + env->mValue = v[mLoopStart].mValue; + return FMOD_OK; + } + + env->mPosition = mLoopStart; + env->mTick = v[env->mPosition].mTick - 1; + goto mRestartenv; + } + + /* + If it is at the last position, abort the envelope and continue last val + */ + if (env->mPosition == Inumpoints - 1) + { + env->mValue = v[currpos].mValue; + env->mStopped = true; + return FMOD_OK; + } + + /* + Interpolate 2 points to find delta step + */ + tickdiff = nexttick - currtick; + if (tickdiff) + { + env->mDelta = (nextval-currval) / tickdiff; + } + else + { + env->mDelta = 0; + } + + env->mFraction = currval; + env->mPosition++; + } + } + } + + env->mValue = env->mFraction >> 16; + env->mTick++; + + vcptr->mNoteControl |= control; + + return FMOD_OK; +} + +#define FMUSIC_IT_GETPERIOD(_note) ((int)gITLogPeriodTable[_note] * 8363L / vcptr->mSample->mMiddleC) + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::processPitchEnvelope(MusicVirtualChannel *vcptr, MusicInstrument *iptr, int note) +{ + /* + Pan Envelope + */ + if (vcptr->mEnvPitchPos < iptr->mPitchNumpoints) + { + MusicEnvelopeNode *v = (MusicEnvelopeNode *)&iptr->mPitchPoints; + + if (vcptr->mEnvPitchTick != v[vcptr->mEnvPitchPos].mTick) + { + vcptr->mEnvPitchFrac += vcptr->mEnvPitchDelta; /* interpolate */ + } + else + { + while (vcptr->mEnvPitchTick == v[vcptr->mEnvPitchPos].mTick) /* if we are at the correct tick for the position */ + { + int currpos, nextpos; + int currtick, nexttick; + int currpitch, nextpitch, tickdiff; + + mRestartpitchenv: + + currpos = vcptr->mEnvPitchPos; + nextpos = vcptr->mEnvPitchPos + 1; + + currtick = v[currpos].mTick; /* get tick at this point */ + nexttick = v[nextpos].mTick; /* get tick at next point */ + + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY || iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) + { + currpitch = v[currpos].mValue * 32; + nextpitch = v[nextpos].mValue * 32; + } + else + { + if (v[currpos].mValue & 1) + { + int a,b,c; + a = FMUSIC_IT_GETPERIOD(note); + b = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1)); + c = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1) + 1); + currpitch = a - ((b+c) / 2); + } + else + { + currpitch = FMUSIC_IT_GETPERIOD(note) - FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1)); + } + + if (v[nextpos].mValue & 1) + { + int a,b,c; + a = FMUSIC_IT_GETPERIOD(note); + b = FMUSIC_IT_GETPERIOD(note + (v[nextpos].mValue >> 1)); + c = FMUSIC_IT_GETPERIOD(note + (v[nextpos].mValue >> 1) + 1); + nextpitch = a - ((b+c) / 2); + } + else + { + nextpitch = FMUSIC_IT_GETPERIOD(note) - FMUSIC_IT_GETPERIOD(note + (v[nextpos].mValue >> 1)); + } + } + + currpitch <<= 16; + nextpitch <<= 16; + + /* + Handle sustain loop + */ + if ((iptr->mPitchType & FMUSIC_ENVELOPE_SUSTAIN) && currpos >= iptr->mPitchSustainLoopEnd && !vcptr->mKeyOff) + { + if (iptr->mPitchSustainLoopEnd == iptr->mPitchSustainLoopStart) + { + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY || iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) + { + vcptr->mEnvPitch = v[currpos].mValue * 32; + } + else + { + if (v[currpos].mValue & 1) + { + int a,b,c; + a = FMUSIC_IT_GETPERIOD(note); + b = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1)); + c = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1) + 1); + vcptr->mEnvPitch = a - ((b+c) / 2); + } + else + { + vcptr->mEnvPitch = (FMUSIC_IT_GETPERIOD(note) - FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1))); + } + } + return FMOD_OK; + } + vcptr->mEnvPitchPos = iptr->mPitchSustainLoopStart; + vcptr->mEnvPitchTick = v[vcptr->mEnvPitchPos].mTick-1; + goto mRestartpitchenv; + } + + /* + Handle loop + */ + if ((iptr->mPitchType & FMUSIC_ENVELOPE_LOOP) && vcptr->mEnvPitchPos >= iptr->mPitchLoopEnd) + { + if (iptr->mPitchLoopEnd <= iptr->mPitchLoopStart) + { + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY || iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) + { + vcptr->mEnvPitch = v[iptr->mPitchLoopStart].mValue * 32; + } + else + { + if (v[currpos].mValue & 1) + { + int a,b,c; + a = FMUSIC_IT_GETPERIOD(note); + b = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1)); + c = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1) + 1); + vcptr->mEnvPitch = a - ((b+c) / 2); + } + else + { + vcptr->mEnvPitch = (FMUSIC_IT_GETPERIOD(note) - FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1))); + } + } + return FMOD_OK; + } + vcptr->mEnvPitchPos = iptr->mPitchLoopStart; + vcptr->mEnvPitchTick = v[vcptr->mEnvPitchPos].mTick-1; + goto mRestartpitchenv; + } + + /* + If it is at the last position, abort the envelope and continue last pitch + */ + if (vcptr->mEnvPitchPos == iptr->mPitchNumpoints - 1) + { + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY || iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) + { + vcptr->mEnvPitch = v[currpos].mValue * 32; + } + else + { + if (v[currpos].mValue & 1) + { + int a,b,c; + a = FMUSIC_IT_GETPERIOD(note); + b = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1)); + c = FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1) + 1); + vcptr->mEnvPitch = a - ((b+c) / 2); + } + else + { + vcptr->mEnvPitch = FMUSIC_IT_GETPERIOD(note) - FMUSIC_IT_GETPERIOD(note + (v[currpos].mValue >> 1)); + } + } + vcptr->mEnvPitchStopped = true; + return FMOD_OK; + } + + /* + Interpolate 2 points to find delta step + */ + tickdiff = nexttick - currtick; + if (tickdiff) + { + vcptr->mEnvPitchDelta = (nextpitch-currpitch) / tickdiff; + } + else + { + vcptr->mEnvPitchDelta = 0; + } + + vcptr->mEnvPitchFrac = currpitch; + + vcptr->mEnvPitchPos++; + } + } + } + + if (!(iptr->mPitchType & FMUSIC_ENVELOPE_FILTER)) + { + vcptr->mNoteControl |= FMUSIC_FREQ; + } + + vcptr->mEnvPitch = vcptr->mEnvPitchFrac >> 16; + vcptr->mEnvPitchTick++; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + to carry out an instrument vibrato at a certain depth and speed + + [PARAMETERS] + track - the track number to do the vibrato too + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::sampleVibrato(MusicVirtualChannel *vcptr) +{ + int delta = 0; + MusicSample *sptr = vcptr->mSample; + + switch (sptr->mVibType) + { + case 0: delta = (int)gFineSineTable[vcptr->mIVibPos]; /* sine */ + break; + case 1: delta = (128-((vcptr->mIVibPos+128)%256))>>1; + break; + case 2: if (vcptr->mIVibPos < 128) + delta=64; /* square */ + else + delta = -64; + break; + case 3: delta = (int)gFineSineTable[vcptr->mIVibPos]; /* sine */ + break; + }; + + delta *= sptr->mVibDepth; + delta = delta * vcptr->mIVibSweepPos >> 16; + delta >>= 7; + + vcptr->mFrequencyDelta -= delta; + + vcptr->mIVibSweepPos += sptr->mVibRate << 1; + if (vcptr->mIVibSweepPos > 256L << 8) + { + vcptr->mIVibSweepPos = 256L << 8; + } + + vcptr->mIVibPos += sptr->mVibSpeed; + if (vcptr->mIVibPos > 255) + { + vcptr->mIVibPos -= 256; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelIT::processVolumeByte(MusicNote *current, bool newrow) +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + MusicSong *mod = mModule; + unsigned char val, volume = current->mVolume - 1; + + if (newrow) + { + /* + Set volume + */ + if (volume <= 64) + { + mVolume = volume; + } + /* + Fine volume slide up + */ + if (volume >= 65 && volume <= 74) + { + val = volume - 65; + if (val) + { + mVolumeColumnVolumeSlide = val; + } + + mVolume += mVolumeColumnVolumeSlide; + if (mVolume > 0x40) + { + mVolume = 0x40; + } + } + /* + Fine volume slide down + */ + if (volume >= 75 && volume <= 84) + { + val = volume - 75; + if (val) + { + mVolumeColumnVolumeSlide = val; + } + + mVolume -= mVolumeColumnVolumeSlide; + if (mVolume < 0) + { + mVolume = 0; + } + } + /* + Pan set + */ + if (volume >= 128 && volume <= 192) + { + mPan = (volume - 128); + vcptr->mPan = mPan; + vcptr->mNoteControl |= FMUSIC_PAN; + } + } + + /* + Volume slide up + */ + if (volume >= 85 && volume <= 94) + { + val = volume - 85; + if (val) + { + mVolumeColumnVolumeSlide = val; + } + + /* + On other ticks beside 0 + */ + if (!newrow) + { + mVolume += mVolumeColumnVolumeSlide; + if (mVolume > 0x40) + { + mVolume = 0x40; + } + } + } + /* + Volume slide down + */ + if (volume >= 95 && volume <= 104) + { + val = volume - 95; + if (val) + { + mVolumeColumnVolumeSlide = val; + } + + /* + On other ticks beside 0 + */ + if (!newrow) + { + mVolume -= mVolumeColumnVolumeSlide; + if (mVolume < 0) + { + mVolume = 0; + } + } + } + + /* + Pitch slide up + */ + if (volume >= 105 && volume <= 114) + { + val = volume - 105; + if (val) + { + mPortaUpDown = val; + } + + vcptr->mFrequency += mPortaUpDown << 4; + } + + /* + Pitch slide down + */ + if (volume >= 115 && volume <= 124) + { + val = volume - 115; + if (val) + { + mPortaUpDown = val; + } + + vcptr->mFrequency -= mPortaUpDown << 4; + if (vcptr->mFrequency < 1) + { + vcptr->mNoteControl |= FMUSIC_STOP; + } + else + { + vcptr->mNoteControl |= FMUSIC_FREQ; + } + } + + + /* + Portamento + */ + if (volume >= 193 && volume <= 202) + { + val = volume - 193; + + if (!mod->mTick) + { + if (val) + { + if (mod->mMusicFlags & FMUSIC_ITFLAGS_EFFECT_G) + { + mPortaSpeed = val << 4; + } + else + { + mPortaUpDown = val << 4; + } + } + mPortaTarget = mPeriod; + + if (current->mNote) + { + mPortaReached = false; + } + } + else + { + portamento(); + } + } + + /* + Vibrato + */ + if (volume >= 203 && volume <= 212) + { + val = volume - 203; + + if (!mod->mTick) + { + if (val) + { + mVibDepth = val; + } + + if (val) + { + mVibType = FMUSIC_IT_VIBRATO; + } + + if (vcptr->mBackground) + { + return FMOD_OK; + } + + if (!(mod->mMusicFlags & FMUSIC_ITFLAGS_OLD_IT_EFFECTS)) + { + if (mVibType == FMUSIC_IT_FINEVIBRATO) + { + fineVibrato(); + } + else + { + vibrato(); + } + } + } + else + { + if (vcptr->mBackground) + { + return FMOD_OK; + } + + if (mVibType == FMUSIC_IT_FINEVIBRATO) + { + fineVibrato(); + } + else + { + vibrato(); + } + } + } + + return FMOD_OK; +} + + + +#ifdef FMUSIC_PLAYPACKED +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::unpackRow() +{ + unsigned char channelvariable; + MusicNote *nptr; + + if (!mPatternPtr) + { + return FMOD_ERR_INTERNAL; + } + + FMOD_memset(mNote, 0, sizeof(MusicNote) * mNumChannels); + + do + { + channelvariable = *mPatternPtr++; + + if (channelvariable) + { + unsigned char channel, maskvariable; + + channel = (channelvariable-1) & 63; + + nptr = &mNote[channel]; + + if (channelvariable & 128) + { + maskvariable = *mPatternPtr++; + mPreviousMaskVariable[channel] = maskvariable; + } + else + maskvariable = mPreviousMaskVariable[channel]; + + if (maskvariable & 1) + { + unsigned char note = *mPatternPtr++; + + if (note >= 254) + { + nptr->mNote = note; + } + else + { + nptr->mNote = note + 1; + } + mLastNote[channel] = nptr->mNote; + } + if (maskvariable & 2) + { + nptr->mNumber = *mPatternPtr++; + mLastNumber[channel] = nptr->mNumber; + } + if (maskvariable & 4) + { + nptr->mVolume = (*mPatternPtr++) + 1; + mLastVolume[channel] = nptr->mVolume; + } + if (maskvariable & 8) + { + nptr->mEffect = *mPatternPtr++; + nptr->mEffectParam = *mPatternPtr++; + mLastEffect[channel] = nptr->mEffect; + mLastEffectParam[channel] = nptr->mEffectParam; + } + if (maskvariable & 16) nptr->mNote = mLastNote[channel]; + if (maskvariable & 32) nptr->mNumber = mLastNumber[channel]; + if (maskvariable & 64) nptr->mVolume = mLastVolume[channel]; + if (maskvariable & 128) + { + nptr->mEffect = mLastEffect[channel]; + nptr->mEffectParam = mLastEffectParam[channel]; + } + } + } while (channelvariable); + + return FMOD_OK; +} +#endif + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::updateRow(bool audible) +{ + MusicNote *current; + bool breakflag = false; + bool jumpflag = false; + int count; + MusicPattern *pptr; + + /* + Point our note pointer to the correct pattern buffer, and to the + correct offset in this buffer indicated by row and number of channels + */ + pptr = &mPattern[mOrderList[mOrder]]; + if (!pptr) + { + return FMOD_OK; + } + +#ifndef FMUSIC_PLAYPACKED + FMOD_memcpy(mNote, pptr->mData + (mRow * mNumChannels), sizeof(MusicNote) * mNumChannels); +#endif + + if (!mTick && mVisited) + { + if (mVisited[(mOrder * FMUSIC_MAXROWS) + mRow]) + { + mFinished = true; + return FMOD_OK; + } + mVisited[(mOrder * FMUSIC_MAXROWS) + mRow] = true; + } + + + /* + Loop through each channel in the row until we have finished + */ + for (count = 0; count < mNumChannels; count++) + { + MusicChannelIT *cptr = 0; + MusicVirtualChannel *vcptr = 0; + bool porta = false; + bool trig = false; + bool newrow = false; + unsigned char paramx, paramy; + + current = &mNote[count]; + paramx = current->mEffectParam >> 4; /* get effect param x */ + paramy = current->mEffectParam & 0xF; /* get effect param y */ + + /* + Dont do shit if it is a delay note (maybe wrong .. may need to update vibratos etc) + */ + if (current->mEffect == FMUSIC_IT_SPECIAL && paramx == FMUSIC_IT_NOTEDELAY && (mTick < paramy || (!mTick && !paramy)) ) + { + continue; + } + + /* + Just entered after a delay note + */ + if (mTick == 0) + { + newrow = true; + } + else if (current->mEffect == FMUSIC_IT_SPECIAL && paramx == FMUSIC_IT_NOTEDELAY && (mTick == paramy || (mTick == 1 && !paramy))) + { + newrow = true; + } + + cptr = (MusicChannelIT *)mMusicChannel[count]; + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + if (newrow) + { + /* + If there is no more tremolo, set volume to volume + last tremolo delta + */ + if (cptr->mRecentEffect == FMUSIC_IT_TREMOLO && current->mEffect != FMUSIC_IT_TREMOLO) + { + cptr->mVolume += cptr->mVolumeDelta; + } + + cptr->mRecentEffect = current->mEffect; + + cptr->mVolumeDelta = 0; + vcptr->mPanDelta = 0; + vcptr->mNoteControl = 0; + + /* + RETRIEVE NEW INSTRUMENT NUMBER AND / OR NOTE + */ + if (current->mNote && current->mNote != FMUSIC_KEYOFF && current->mNote != FMUSIC_KEYCUT) + { + trig = true; + cptr->mNote = current->mNote-1; /* remember the note */ + } + + if (current->mNumber) + { + cptr->mInstrument = current->mNumber-1; /* remember the Instrument # */ + + /* + another crazy bugfix (2.09) + */ + if (vcptr == &gDummyVirtualChannel) + { + trig = true; + } + +// if (InstCallback[current->mNumber] && InstCallback[current->mNumber]->callback) +// { +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_INSTRUMENT, current->mNumber); +// } + } + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + if (cptr->mInstrument >= mNumInstruments) + { + trig = false; + } + } + + /* + RESET STUFF + */ + cptr->mRecentEffect = current->mEffect; + vcptr->mNoteControl = 0; + + if (mDefaultPan[count] & 128) + { + trig = false; + } + + porta = (current->mEffect == FMUSIC_IT_PORTATO || current->mEffect == FMUSIC_IT_PORTATOVOLSLIDE || ((current->mVolume-1) >= 193 && (current->mVolume-1) <= 202) ); + + /* + bugfix (fmod 2.09) + */ + if (porta && vcptr == &gDummyVirtualChannel) + { + porta = false; + } + + /* + PROCESS NOTE + */ + if (trig) + { + int samp; + MusicInstrument *iptr = &mInstrument[cptr->mInstrument]; + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + samp = iptr->mNoteTable[(cptr->mNote<<1)+1]; + } + else + { + samp = cptr->mInstrument+1; + } + + if (samp) + { + MusicVirtualChannel *childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + + + cptr->mSample = samp-1; + + /* + DTC / DCA's + */ + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + if (iptr->mDupCheckType) + { + /* + Search through child channels for any duplicate actions, if so set cut,fade or off + */ + for (childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); childvcptr != &cptr->mVirtualChannelHead; childvcptr = (MusicVirtualChannel *)childvcptr->getNext()) + { + switch (iptr->mDupCheckType) + { + case 1 : /* NOTE */ + { + if (cptr->mNote == childvcptr->mLastNote) + { + switch (iptr->mDupCheckAction) + { + case 0: + { + childvcptr->mNoteControl |= FMUSIC_STOP; + break; + } + case 1: + { + MusicInstrument *iptr = &mInstrument[childvcptr->mLastInstrument]; + childvcptr->mKeyOff = true; + if (!(vcptr->mVolType & FMUSIC_ENVELOPE_ON)) + { + childvcptr->mFade = true; + } + else if (iptr->mVolumeType & FMUSIC_ENVELOPE_LOOP) + { + childvcptr->mFade = true; /* undocumented */ + } + break; + } + case 2: + { + childvcptr->mFade = true; + break; + } + } + } + break; + } + case 2 : /* SAMPLE */ + { + if (cptr->mSample == childvcptr->mLastSample) + { + switch (iptr->mDupCheckAction) + { + case 0: + { + childvcptr->mNoteControl |= FMUSIC_STOP; + break; + } + case 1: + { + MusicInstrument *iptr = &mInstrument[childvcptr->mLastInstrument]; + childvcptr->mKeyOff = true; + if (!(vcptr->mVolType & FMUSIC_ENVELOPE_ON)) + { + childvcptr->mFade = true; + } + else if (iptr->mVolumeType & FMUSIC_ENVELOPE_LOOP) + { + childvcptr->mFade = true; /* undocumented */ + } + break; + } + case 2: + { + childvcptr->mFade = true; + break; + } + } + } + break; + } + case 3 : /* INSTRUMENT */ + { + if (cptr->mInstrument == childvcptr->mLastInstrument) + { + switch (iptr->mDupCheckAction) + { + case 0: + { + childvcptr->mNoteControl |= FMUSIC_STOP; + break; + } + case 1: + { + MusicInstrument *iptr = &mInstrument[childvcptr->mLastInstrument]; + childvcptr->mKeyOff = true; + if (!(vcptr->mVolType & FMUSIC_ENVELOPE_ON)) + { + childvcptr->mFade = true; + } + else if (iptr->mVolumeType & FMUSIC_ENVELOPE_LOOP) + { + childvcptr->mFade = true; /* undocumented */ + } + break; + } + case 2: + { + childvcptr->mFade = true; + break; + } + } + } + break; + } + } + } + } + } + + /* + CHANNEL ALLOCATION, NNA'S + */ + if (!porta) /* dont spawn any new channels if it is a portamento */ + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + + /* + If the channel has not got any children, then just spawn a new one + */ + if (vcptr == &cptr->mVirtualChannelHead) + { + FMOD_RESULT result; + + result = spawnNewVirtualChannel(cptr, 0, &vcptr); + if (result != FMOD_OK) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + } + else if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + MusicVirtualChannel *newvcptr; + MusicInstrument *iptr = &mInstrument[vcptr->mLastInstrument]; + + switch (vcptr->mNNA) + { + case FMUSIC_CUT : + { + /* + nothing special here.. just use this channel and exit + */ + break; + } + case FMUSIC_OFF : + { + FMOD_RESULT result; + + vcptr->mBackground = true; + vcptr->mKeyOff = true; + if (!(vcptr->mVolType & FMUSIC_ENVELOPE_ON)) + { + vcptr->mFade = true; + } + else if (iptr->mVolumeType & FMUSIC_ENVELOPE_LOOP) + { + vcptr->mFade = true; /* undocumented */ + } + + if (!cptr->mVolume) + { + vcptr->mNoteControl |= FMUSIC_STOP; /* remove if it is silent */ + } + + result = spawnNewVirtualChannel(cptr, 0, &newvcptr); + if (!newvcptr) + { + result = result; + } + + if (result == FMOD_OK) + { + vcptr = newvcptr; + } + else + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + vcptr->mVolume = cptr->mVolume; + + break; + } + case FMUSIC_CONTINUE : + { + FMOD_RESULT result; + + vcptr->mBackground = true; + if (!cptr->mVolume) + { + vcptr->mNoteControl |= FMUSIC_STOP; /* remove if it is silent */ + } + + result = spawnNewVirtualChannel(cptr, 0, &newvcptr); + if (!newvcptr) + { + result = result; + } + if (result == FMOD_OK) + { + vcptr = newvcptr; + } + else + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + vcptr->mVolume = cptr->mVolume; + + break; + } + case FMUSIC_FADEOUT : + { + FMOD_RESULT result; + + vcptr->mBackground = true; + vcptr->mFade = true; + if (!cptr->mVolume) + { + vcptr->mNoteControl |= FMUSIC_STOP; /* remove if it is silent */ + } + + result = spawnNewVirtualChannel(cptr, 0, &newvcptr); + if (!newvcptr) + { + result = result; + } + if (result == FMOD_OK) + { + vcptr = newvcptr; + } + else + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + vcptr->mVolume = cptr->mVolume; + + break; + } + }; + } + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mBackground = false; + vcptr->mNNA = mInstrument[cptr->mInstrument].mNNA; + vcptr->mVolType = mInstrument[cptr->mInstrument].mVolumeType; + } + + vcptr->mLastSample = cptr->mSample; + vcptr->mLastInstrument = cptr->mInstrument; + vcptr->mLastNote = cptr->mNote; + if (cptr->mSample >= mNumSamples || !mSample[cptr->mSample]) + { + vcptr->mSample = &gDummySample; + } + else + { + vcptr->mSample = mSample[cptr->mSample]; + } + } + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + cptr->mRealNote = iptr->mNoteTable[(cptr->mNote<<1)]; /* get note according to relative note */ + } + else + { + cptr->mRealNote = cptr->mNote; + } + + /* + Get period according to mRealNote and finetune (linear freqs are just 6bit fixed point) + */ + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY) + { + float scalar, logs; + + /* + 10 octaves, 12 notes per octave, 64 linear steps per note + */ + cptr->mPeriod = (10*12*64) - (cptr->mRealNote*64); + + /* + Fine tune - as mMiddleC hz is logarithmic and our periods are linear.. special conversion is needed + */ + scalar = (float)vcptr->mSample->mMiddleC / 8363.0f; + logs = (float)FMOD_LOG(scalar); + logs /= 0.693147f; /*log(2.0f); */ + + logs *= 768.0f; + + cptr->mPeriod -= (int)logs; + } + else + { + cptr->mPeriod = FMUSIC_IT_GETPERIOD(cptr->mRealNote); + } + + /* + frequency only changes if there are no portamento effects + */ + if (!porta) + { + + /* + retrigger tremolo / vibrato panbrello waveforms + */ + cptr->mVibPos = 0; + cptr->mTremoloPosition = 0; + cptr->mPanbrelloPos = 0; + cptr->mTremorPosition = 0; /* retrigger tremor count */ + cptr->mRetrigCount = 0; + + vcptr->mFrequency = cptr->mPeriod; + vcptr->mPan = cptr->mPan; + vcptr->mDirection = 1; + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + if (!(iptr->mDefaultPan & 128)) + { + vcptr->mPan = iptr->mDefaultPan * 64 / 63; + } + } + + if (vcptr->mSample->mDefaultPan & 128) + { + vcptr->mPan = (vcptr->mSample->mDefaultPan & 127); + } + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mPan = vcptr->mPan + ((cptr->mNote - iptr->mPitchPanCenter) * iptr->mPitchPanSep / 8); + vcptr->mSampGlobalVol = vcptr->mSample->mGlobalVolume * iptr->mGlobalVolume / 128; + } + else + { + vcptr->mSampGlobalVol = vcptr->mSample->mGlobalVolume; + } + + vcptr->mNoteControl = FMUSIC_TRIGGER | FMUSIC_PAN; + } + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS && ((porta && (mMusicFlags & FMUSIC_ITFLAGS_EFFECT_G)) || !porta)) + { + vcptr->mKeyOff = false; + + if (!(mInstrument[cptr->mInstrument].mVolumeType & FMUSIC_ENVELOPE_CARRY) || vcptr->mNNA == FMUSIC_CUT) + { + vcptr->mEnvVolume.mValue = 64; + vcptr->mEnvVolume.mPosition = 0; + vcptr->mEnvVolume.mTick = 0; + vcptr->mEnvVolume.mDelta = 0; + } + + if (!(mInstrument[cptr->mInstrument].mPanType & FMUSIC_ENVELOPE_CARRY) || vcptr->mNNA == FMUSIC_CUT) + { + vcptr->mEnvPan.mValue = 0; + vcptr->mEnvPan.mPosition = 0; + vcptr->mEnvPan.mTick = 0; + vcptr->mEnvPan.mDelta = 0; + } + + if (!(mInstrument[cptr->mInstrument].mPitchType & FMUSIC_ENVELOPE_CARRY) || vcptr->mNNA == FMUSIC_CUT) + { + vcptr->mEnvPitch = 0; + vcptr->mEnvPitchPos = 0; + vcptr->mEnvPitchTick = 0; + vcptr->mEnvPitchDelta = 0; + } + + vcptr->mFade = false; + vcptr->mFadeOutVolume = 1024; + vcptr->mEnvVolume.mStopped = false; + vcptr->mEnvPan.mStopped = false; + vcptr->mIVibSweepPos = 0; + vcptr->mIVibPos = 0; + } + } + } + + /* + PROCESS SAMPLE / INSTRUMENT NUMBER + */ + if (vcptr->mSample && current->mNumber) + { + cptr->mVolume = vcptr->mSample->mDefaultVolume; + } + } + + +/* vcptr->mFrequencyDelta = 0; +*/ + /* + bugfix - 3.10 - child channels still had their freqdelta so some child channels were out of tune etc. + */ + { + MusicVirtualChannel *childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + for (childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); childvcptr != &cptr->mVirtualChannelHead; childvcptr = (MusicVirtualChannel *)childvcptr->getNext()) + { + childvcptr->mFrequencyDelta = 0; + } + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mNoteControl |= FMUSIC_VOLUME; + + /* + PROCESS VOLUME BYTE + */ + if (current->mVolume) + { + cptr->processVolumeByte(current, newrow); + } + + /* + PROCESS KEY OFF + */ + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + MusicInstrument *iptr = &mInstrument[cptr->mInstrument]; + + if (current->mNote == FMUSIC_KEYOFF) + { + vcptr->mKeyOff = true; + if (!(vcptr->mVolType & FMUSIC_ENVELOPE_ON)) + { + vcptr->mFade = true; + } + else if (iptr->mVolumeType & FMUSIC_ENVELOPE_LOOP) + { + vcptr->mFade = true; /* undocumented */ + } + } + } + + if (current->mNote == FMUSIC_KEYCUT) + { + vcptr->mNoteControl |= FMUSIC_STOP; + } + + /* + PROCESS EFFECTS + */ + switch (current->mEffect) + { + case FMUSIC_IT_SETSPEED : + { + if (!mTick && current->mEffectParam) /* 3.41 BUGFIX - speed 0 was being parsed */ + { + mSpeed = current->mEffectParam; + } + break; + } + case FMUSIC_IT_PATTERNJUMP : /* --- 00 B00 : --- 00 D63 , should put us at ord=0, row=63 */ + { + if (!mTick) + { + mNextOrder = current->mEffectParam; + mNextRow = 0; + if (mNextOrder >= mNumOrders) + { + mNextOrder=0; + } + jumpflag = 1; + } + break; + } + case FMUSIC_IT_PATTERNBREAK : + { + if (!mTick) + { + mNextRow = current->mEffectParam; + if (mNextRow > pptr->mRows) + { + mNextRow = 0; + } + if (!breakflag && !jumpflag) + { + mNextOrder = mOrder+1; + } + if (mNextOrder >= mNumOrders) + { + mNextOrder=0; + } + } + break; + } + case FMUSIC_IT_VOLUMESLIDE : + { + if (!mTick) + { + /* Dxy - Volume slide, fine vol DFx = slide down, DxF = slide up */ + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + /* DFF is classed as a slide up so it gets priority */ + if ((cptr->mVolumeSlide &0xF) == 0xF) + { + cptr->mVolume += (cptr->mVolumeSlide >> 4); + } + else if ((cptr->mVolumeSlide >>4 ) == 0xF) + { + cptr->mVolume -= (cptr->mVolumeSlide & 0xF); + } + if (cptr->mVolume > 64) + { + cptr->mVolume = 64; + } + if (cptr->mVolume < 0) + { + cptr->mVolume = 0; + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + cptr->volumeSlide(); + } + break; + } + case FMUSIC_IT_PORTADOWN : + { + if (!mTick) + { + if (current->mEffectParam) + { + cptr->mPortaUpDown = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + if ((cptr->mPortaUpDown >> 4)==0xF) + { + vcptr->mFrequency += ((cptr->mPortaUpDown & 0xF) << 2); + } + if ((cptr->mPortaUpDown >> 4)==0xE) + { + vcptr->mFrequency += (cptr->mPortaUpDown & 0xF); + } + } + else if (cptr->mPortaUpDown <0xE0) + { + if (vcptr->mBackground) + { + break; + } + + vcptr->mFrequency += (cptr->mPortaUpDown << 2); + } + + break; + } + + case FMUSIC_IT_PORTAUP : + { + if (!mTick) + { + if (current->mEffectParam) + { + cptr->mPortaUpDown = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + if ((cptr->mPortaUpDown >>4)==0xF) + { + vcptr->mFrequency -= ((cptr->mPortaUpDown & 0xF) << 2); + } + if ((cptr->mPortaUpDown >>4)==0xE) + { + vcptr->mFrequency -= (cptr->mPortaUpDown & 0xF); + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + if (cptr->mPortaUpDown <0xE0) + { + vcptr->mFrequency -= cptr->mPortaUpDown << 2; + if (vcptr->mFrequency < 1) + { + vcptr->mNoteControl |= FMUSIC_STOP; + } + else + { + vcptr->mNoteControl |= FMUSIC_FREQ; + } + } + } + break; + } + case FMUSIC_IT_PORTATO : + { + if (!mTick) + { + if (current->mEffectParam) + { + if (mMusicFlags & FMUSIC_ITFLAGS_EFFECT_G) + { + cptr->mPortaSpeed = current->mEffectParam; + } + else + { + cptr->mPortaUpDown = current->mEffectParam; + } + } + cptr->mPortaTarget = cptr->mPeriod; + + if (current->mNote) + { + cptr->mPortaReached = false; + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + cptr->portamento(); + } + break; + } + + case FMUSIC_IT_VIBRATO : + { + if (!mTick) + { + if (paramx) + cptr->mVibSpeed = paramx; + if (paramy) + cptr->mVibDepth = paramy; + + if (paramy) + cptr->mVibType = FMUSIC_IT_VIBRATO; + + if (vcptr->mBackground) + { + break; + } + + if (!(mMusicFlags & FMUSIC_ITFLAGS_OLD_IT_EFFECTS)) + { + if (cptr->mVibType == FMUSIC_IT_FINEVIBRATO) + { + cptr->fineVibrato(); + } + else + { + cptr->vibrato(); + } + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + if (cptr->mVibType == FMUSIC_IT_FINEVIBRATO) + { + cptr->fineVibrato(); + } + else + { + cptr->vibrato(); + } + } + + break; + } + case FMUSIC_IT_TREMOR : + { + if (!mTick && current->mEffectParam) + { + cptr->mTremorOn = (paramx+1); + cptr->mTremorOff = (paramy+1); + } + + if (vcptr->mBackground) + { + break; + } + + if (cptr->mTremorPosition >= cptr->mTremorOn) + { + cptr->mVolumeDelta = -cptr->mVolume; + } + cptr->mTremorPosition++; + if (cptr->mTremorPosition >= (cptr->mTremorOn + cptr->mTremorOff)) + { + cptr->mTremorPosition = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_IT_ARPEGGIO : + { + if (!mTick && current->mEffectParam) + { + cptr->mArpeggio = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + if (cptr->mArpeggio > 0) + { + paramx = cptr->mArpeggio >> 4; + paramy = cptr->mArpeggio & 0xF; + + switch (mTick % 3) + { + case 1: + { + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY) + { + vcptr->mFrequencyDelta -= (int)(paramx) << 6; + } + else + { + vcptr->mFrequencyDelta += (FMUSIC_IT_GETPERIOD(cptr->mNote+paramx) -FMUSIC_IT_GETPERIOD(cptr->mNote)); + } + break; + } + case 2: + { + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY) + { + vcptr->mFrequencyDelta -= (int)(paramy) << 6; + } + else + { + vcptr->mFrequencyDelta += (FMUSIC_IT_GETPERIOD(cptr->mNote+paramy) - FMUSIC_IT_GETPERIOD(cptr->mNote)); + } + break; + } + }; + vcptr->mNoteControl |= FMUSIC_FREQ; + } + break; + } + case FMUSIC_IT_VIBRATOVOLSLIDE : + { + if (!mTick) + { + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + /* + DFF is classed as a slide up so it gets priority + */ + if ((cptr->mVolumeSlide &0xF) == 0xF) + { + cptr->mVolume += (cptr->mVolumeSlide >> 4); + } + else if ((cptr->mVolumeSlide >>4 ) == 0xF) + { + cptr->mVolume -= (cptr->mVolumeSlide & 0xF); + } + + if (cptr->mVolume > 64) + { + cptr->mVolume = 64; + } + if (cptr->mVolume < 0) + { + cptr->mVolume = 0; + } + + if (!(mMusicFlags & FMUSIC_ITFLAGS_OLD_IT_EFFECTS)) + { + if (cptr->mVibType == FMUSIC_IT_FINEVIBRATO) + { + cptr->fineVibrato(); + } + else + { + cptr->vibrato(); + } + } + + } + else + { + if (vcptr->mBackground) + { + break; + } + + if (cptr->mVibType == FMUSIC_IT_FINEVIBRATO) + { + cptr->fineVibrato(); + } + else + { + cptr->vibrato(); + } + + cptr->volumeSlide(); + } + break; + } + case FMUSIC_IT_PORTATOVOLSLIDE : + { + if (!mTick) + { + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + cptr->mPortaTarget = cptr->mPeriod; + if (current->mNote) + { + cptr->mPortaReached = false; + } + + if (vcptr->mBackground) + { + break; + } + + /* + DFF is classed as a slide up so it gets priority + */ + if ((cptr->mVolumeSlide &0xF) == 0xF) + { + cptr->mVolume += (cptr->mVolumeSlide >> 4); + } + else if ((cptr->mVolumeSlide >>4 ) == 0xF) + { + cptr->mVolume -= (cptr->mVolumeSlide & 0xF); + } + + if (cptr->mVolume > 64) + { + cptr->mVolume = 64; + } + if (cptr->mVolume < 0) + { + cptr->mVolume = 0; + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + cptr->portamento(); + cptr->volumeSlide(); + } + break; + } + case FMUSIC_IT_PANSLIDE : + { + if (!mTick) + { + /* Dxy - Volume slide, fine vol DFx = slide down, DxF = slide up */ + if (current->mEffectParam) + { + cptr->mPanSlide = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + /* DFF is classed as a slide up so it gets priority */ + if ((cptr->mPanSlide &0xF) == 0xF) + { + cptr->mPan += (cptr->mPanSlide >> 4); + } + else if ((cptr->mPanSlide >>4 ) == 0xF) + { + cptr->mPan -= (cptr->mPanSlide & 0xF); + } + if (cptr->mPan > 64) + { + cptr->mPan = 64; + } + if (cptr->mPan < 0) + { + cptr->mPan = 0; + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + cptr->panSlide(); + } + + break; + } + case FMUSIC_IT_SETCHANNELVOLUME : + { + if (!mTick) + { + cptr->mGlobalVolume = current->mEffectParam; + if (cptr->mGlobalVolume > 0x40) + { + cptr->mGlobalVolume = 0x40; + } + } + break; + } + case FMUSIC_IT_CHANNELVOLSLIDE: + { + if (!mTick) + { + /* + Nxy - Volume slide, fine vol NFx = slide down, NxF = slide up + */ + if (current->mEffectParam) + { + cptr->mChannelVolumeSlide = current->mEffectParam; + } + + if (vcptr->mBackground) + { + break; + } + + /* + NFF is classed as a slide up so it gets priority + */ + if ((cptr->mChannelVolumeSlide &0xF) == 0xF) + { + cptr->mGlobalVolume += (cptr->mChannelVolumeSlide >> 4); + } + else if ((cptr->mChannelVolumeSlide >>4 ) == 0xF) + { + cptr->mGlobalVolume -= (cptr->mChannelVolumeSlide & 0xF); + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + if (!(cptr->mChannelVolumeSlide & 0xF)) + { + cptr->mGlobalVolume += (cptr->mChannelVolumeSlide >> 4); + } + if (!(cptr->mChannelVolumeSlide >> 4)) + { + cptr->mGlobalVolume -= (cptr->mChannelVolumeSlide & 0xF); + } + } + + if (cptr->mGlobalVolume > 64) + { + cptr->mGlobalVolume = 64; + } + if (cptr->mGlobalVolume < 0) + { + cptr->mGlobalVolume = 0; + } + break; + } + case FMUSIC_IT_SETSAMPLEOFFSET : + { + unsigned int offset; + + if (!mTick) + { + if (!vcptr->mSample) + { + break; + } + + if (current->mEffectParam) + { + cptr->mSampleOffset = current->mEffectParam; + } + + offset = cptr->mHighOffset; + offset <<= 16; + offset += (int)(cptr->mSampleOffset) << 8; + + if (offset >= vcptr->mSample->mLoopStart + vcptr->mSample->mLoopLength) + { + offset = vcptr->mSample->mLoopStart + vcptr->mSample->mLoopLength - 1; + } + + vcptr->mSampleOffset = offset; + } + break; + } + case FMUSIC_IT_RETRIGVOLSLIDE : + { + if (!mTick && current->mEffectParam) + { + cptr->mRetrigX = paramx; + cptr->mRetrigY = paramy; + } + + if (cptr->mRetrigCount) + { + if (vcptr->mBackground) + { + break; + } + + if (!cptr->mRetrigY) + { + break; /* divide by 0 bugfix */ + } + + if (!(cptr->mRetrigCount % cptr->mRetrigY)) + { + if (cptr->mRetrigX) + { + switch (cptr->mRetrigX) + { + case 1: cptr->mVolume--; + break; + case 2: cptr->mVolume -= 2; + break; + case 3: cptr->mVolume -= 4; + break; + case 4: cptr->mVolume -= 8; + break; + case 5: cptr->mVolume -= 16; + break; + case 6: cptr->mVolume *= 2/3; + break; + case 7: cptr->mVolume >>= 1; + break; + case 8: /* ? */ + break; + case 9: cptr->mVolume++; + break; + case 0xA: cptr->mVolume += 2; + break; + case 0xB: cptr->mVolume += 4; + break; + case 0xC: cptr->mVolume += 8; + break; + case 0xD: cptr->mVolume += 16; + break; + case 0xE: cptr->mVolume *= 3/2; + break; + case 0xF: cptr->mVolume <<= 1; + break; + }; + if (cptr->mVolume > 64) + { + cptr->mVolume = 64; + } + if (cptr->mVolume < 0) + { + cptr->mVolume = 0; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + vcptr->mNoteControl |= FMUSIC_PAN; + vcptr->mNoteControl |= FMUSIC_TRIGGER; + } + } + cptr->mRetrigCount ++; + break; + } + case FMUSIC_IT_TREMOLO : + { + if (!mTick) + { + if (paramx) + { + cptr->mTremoloSpeed = paramx; + } + if (paramy) + { + cptr->mTremoloDepth = paramy; + } + } + + if (vcptr->mBackground) + { + break; + } + + cptr->tremolo(); + + break; + } + + case FMUSIC_IT_SPECIAL : + { + if (current->mEffectParam) + { + cptr->mSpecialParam = current->mEffectParam; + } + + paramx = cptr->mSpecialParam >> 4; /* get effect param x */ + paramy = cptr->mSpecialParam & 0xF; /* get effect param y */ + + switch (paramx) + { + case FMUSIC_IT_NOTECUT: + { + if (!mTick) + { + break; + } + + if (mTick==paramy || (mTick == 1 && !paramy)) + { + cptr->mVolume = 0; + vcptr->mNoteControl |= FMUSIC_STOP; + } + break; + } + case FMUSIC_IT_SETVIBRATOWAVE : + { + if (!mTick) + { + cptr->mWaveControlVibrato = paramy & 3; + } + break; + } + case FMUSIC_IT_SETTREMOLOWAVE : + { + if (!mTick) + { + cptr->mWaveControlTremolo = paramy & 3; + } + break; + } + case FMUSIC_IT_SETPANBRELLOWAVE : + { + if (!mTick) + { + cptr->mWaveControlPan = paramy & 3; + } + break; + } + case FMUSIC_IT_PATTERNDELAYTICKS : + { + if (!mTick) + { + mPatternDelayTicks = paramy; + } + break; + } + case FMUSIC_IT_S7SPECIAL : + { + switch (paramy) + { + case FMUSIC_IT_PASTNOTECUT : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + MusicVirtualChannel *childvcptr; + + for (childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); childvcptr != &cptr->mVirtualChannelHead; childvcptr = (MusicVirtualChannel *)childvcptr->getNext()) + { + if (childvcptr->mBackground) + { + childvcptr->mNoteControl |= FMUSIC_STOP; + } + } + } + break; + } + case FMUSIC_IT_PASTNOTEOFF : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + MusicVirtualChannel *childvcptr; + + for (childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); childvcptr != &cptr->mVirtualChannelHead; childvcptr = (MusicVirtualChannel *)childvcptr->getNext()) + { + MusicInstrument *ciptr; + + if (childvcptr->mBackground) + { + ciptr = &mInstrument[childvcptr->mLastInstrument]; + childvcptr->mKeyOff = true; + if (!(childvcptr->mVolType & FMUSIC_ENVELOPE_ON)) + { + childvcptr->mFade = true; + } + else if (ciptr->mVolumeType & FMUSIC_ENVELOPE_LOOP) + { + childvcptr->mFade = true; /* undocumented */ + } + } + } + } + break; + } + case FMUSIC_IT_PASTNOTEFADE : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + MusicVirtualChannel *childvcptr; + + for (childvcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); childvcptr != &cptr->mVirtualChannelHead; childvcptr = (MusicVirtualChannel *)childvcptr->getNext()) + { + if (childvcptr->mBackground) + { + childvcptr->mFade = true; + } + } + } + break; + } + + case FMUSIC_IT_SETNNACUT : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mNNA = FMUSIC_CUT; + } + break; + } + case FMUSIC_IT_SETNNACONTINUE : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mNNA = FMUSIC_CONTINUE; + } + break; + } + case FMUSIC_IT_SETNNANOTEOFF : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mNNA = FMUSIC_OFF; + } + break; + } + case FMUSIC_IT_SETNNANOTEFADE : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mNNA = FMUSIC_FADEOUT; + } + break; + } + + case FMUSIC_IT_VOLENVELOPEOFF : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mVolType &= ~FMUSIC_ENVELOPE_ON; + } + break; + } + case FMUSIC_IT_VOLENVELOPEON : + { + if (!mTick && mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + vcptr->mVolType |= FMUSIC_ENVELOPE_ON; + } + break; + } + } + break; + } + case FMUSIC_IT_SETPANPOSITION16: + { + if (!mTick) + { + cptr->mPan = paramy<<2; + + if (vcptr->mBackground) + { + break; + } + + vcptr->mPan = cptr->mPan; + vcptr->mNoteControl |= FMUSIC_PAN; + vcptr->mNoteControl |= FMUSIC_SURROUNDOFF; + } + break; + } + case FMUSIC_IT_S9SPECIAL: + { + if (!mTick) + { + if (vcptr->mBackground) + { + break; + } + + switch (paramy) + { + case FMUSIC_IT_DISABLESURROUND: + { + vcptr->mNoteControl |= FMUSIC_SURROUNDOFF; + break; + } + case FMUSIC_IT_ENABLESURROUND: + { + vcptr->mNoteControl |= FMUSIC_SURROUND; + break; + } + case FMUSIC_IT_PLAYFORWARD: + { + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mDirection = 1; + break; + } + case FMUSIC_IT_PLAYBACKWARD: + { + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mDirection = -1; + break; + } + }; + } + break; + } + case FMUSIC_IT_SETHIGHOFFSET : + { + if (!mTick) + { + cptr->mHighOffset = paramy; + } + break; + } + case FMUSIC_IT_PATTERNLOOP : + { + if (!mTick) + { + if (paramy == 0) + { + cptr->mPatternLoopRow = mRow; + } + else + { + if (!cptr->mPatternLoopNumber) + { + cptr->mPatternLoopNumber = paramy; + } + else + { + cptr->mPatternLoopNumber--; + } + + if (cptr->mPatternLoopNumber) + { + int count2; + + mNextRow = cptr->mPatternLoopRow; + + if (mVisited) + { + for (count2 = cptr->mPatternLoopRow; count2 <= mRow; count2++) + { + mVisited[(mOrder * FMUSIC_MAXROWS) + count2] = false; + } + } + } + } + } + break; + } + case FMUSIC_IT_PATTERNDELAY : + { + if (!mTick) + { + mPatternDelay = paramy; + mPatternDelay *= mSpeed; + } + break; + } + }; + break; + } + case FMUSIC_IT_SETTEMPO : + { + if (!mTick && current->mEffectParam > 0x1f) + { + setBPM(current->mEffectParam); + } + else if (current->mEffectParam <= 0x1f) + { + if (paramx == 0) + { + setBPM(mBPM - paramy); + } + else if (paramy == 1) + { + setBPM(mBPM + paramy); + } + } + break; + } + case FMUSIC_IT_FINEVIBRATO : + { + if (!mTick) + { + if (paramx) + { + cptr->mVibSpeed = paramx; + } + if (paramy) + { + cptr->mVibDepth = paramy; + } + + if (paramy) + { + cptr->mVibType = FMUSIC_IT_FINEVIBRATO; + } + + if (vcptr->mBackground) + { + break; + } + + if (!(mMusicFlags & FMUSIC_ITFLAGS_OLD_IT_EFFECTS)) + { + if (cptr->mVibType == FMUSIC_IT_FINEVIBRATO) + { + cptr->fineVibrato(); + } + else + { + cptr->vibrato(); + } + } + } + else + { + if (vcptr->mBackground) + { + break; + } + + if (cptr->mVibType == FMUSIC_IT_FINEVIBRATO) + { + cptr->fineVibrato(); + } + else + { + cptr->vibrato(); + } + } + + break; + } + case FMUSIC_IT_SETGLOBALVOLUME : + { + if (!mTick) + { + mGlobalVolume = current->mEffectParam; + if (mGlobalVolume > 128) + { + mGlobalVolume = 128; + } + } + break; + } + case FMUSIC_IT_GLOBALVOLUMESLIDE : + { + if (!mTick) + { + /* + Wxy - Volume slide, fine vol WFx = slide down, WxF = slide up + */ + if (current->mEffectParam) + { + mGlobalVolumeSlide = current->mEffectParam; + } + + /* + WFF is classed as a slide up so it gets priority + */ + if ((mGlobalVolumeSlide &0xF) == 0xF) + { + mGlobalVolume += (mGlobalVolumeSlide >> 4); + } + else if ((mGlobalVolumeSlide >>4 ) == 0xF) + { + mGlobalVolume -= (mGlobalVolumeSlide & 0xF); + } + } + else + { + if (!(mGlobalVolumeSlide & 0xF)) + { + mGlobalVolume += (mGlobalVolumeSlide >> 4); + } + if (!(mGlobalVolumeSlide >> 4)) + { + mGlobalVolume -= (mGlobalVolumeSlide & 0xF); + } + } + + if (mGlobalVolume > 128) + { + mGlobalVolume = 128; + } + if (mGlobalVolume < 0) + { + mGlobalVolume = 0; + } + + break; + } + case FMUSIC_IT_SETPAN : + { + if (!mTick) + { + cptr->mPan = current->mEffectParam >> 2; + + if (vcptr->mBackground) + { + break; + } + + vcptr->mPan = cptr->mPan; + vcptr->mNoteControl |= FMUSIC_PAN; + vcptr->mNoteControl |= FMUSIC_SURROUNDOFF; + } + break; + } + case FMUSIC_IT_PANBRELLO : + { + if (!mTick) + { + if (paramx) + { + cptr->mPanbrelloSpeed = paramx; + } + if (paramy) + { + cptr->mPanbrelloDepth = paramy; + } + } + + if (vcptr->mBackground) + { + break; + } + + cptr->panbrello(); + + break; + } + case FMUSIC_IT_MIDIMACROS : + { +// if (!mTick) +// { +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ZXX, current->mEffectParam); +// } + break; + } + }; + } + + if (!audible) + { + return FMOD_OK; + } + + for (count=0; count < mNumChannels; count++) + { + bool filter; + MusicChannel *cptr = 0; + MusicVirtualChannel *vcptr = 0; + + if (mDefaultPan[count] & 128) + { + continue; + } + + /* + GET MUSIC CURRENT CHANNEL POINTER + */ + cptr = (MusicChannelIT *)mMusicChannel[count]; + + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + while (vcptr != &cptr->mVirtualChannelHead) + { + int volumedelta; + MusicInstrument *iptr = 0; + MusicVirtualChannel *next = (MusicVirtualChannel *)vcptr->getNext(); + + if (!vcptr->mSample) + { + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + } + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + iptr = &mInstrument[vcptr->mLastInstrument]; + + /* + PROCESS ENVELOPES + */ + if (vcptr->mVolType & FMUSIC_ENVELOPE_ON) + { + if (!vcptr->mEnvVolume.mStopped) + { + processEnvelope(&vcptr->mEnvVolume, vcptr, iptr->mVolumeNumPoints, (MusicEnvelopeNode *)iptr->mVolumePoints, iptr->mVolumeType, iptr->mVolumeLoopStart, iptr->mVolumeLoopEnd, iptr->mVolumeSustainLoopStart, iptr->mVolumeSustainLoopEnd, FMUSIC_VOLUME); + } + else + { + vcptr->mFade = true; + if (vcptr->mEnvVolume.mStopped && !vcptr->mEnvVolume.mValue) + { + vcptr->mNoteControl |= FMUSIC_STOP; + } + } + } + + if (iptr->mPanType & FMUSIC_ENVELOPE_ON) + { + if (!vcptr->mEnvPan.mStopped) + { + processEnvelope(&vcptr->mEnvPan, vcptr, iptr->mPanNumPoints, (MusicEnvelopeNode *)iptr->mPanPoints, iptr->mPanType, iptr->mPanLoopStart, iptr->mPanLoopEnd, iptr->mPanSustainLoopStart, iptr->mPanSustainLoopEnd, FMUSIC_PAN); + } + } + + if (iptr->mPitchType & FMUSIC_ENVELOPE_ON) + { + if (!vcptr->mEnvPitchStopped) + { + processPitchEnvelope(vcptr, iptr, cptr->mNote); + } + } + + /* + PROCESS VOLUME FADEOUT + */ + if (vcptr->mFade) + { + vcptr->mFadeOutVolume -= iptr->mVolumeFade; + if (vcptr->mFadeOutVolume < 0) + { + vcptr->mFadeOutVolume = 0; + } + + if (vcptr->mFadeOutVolume) + { + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + else + { + vcptr->mNoteControl |= FMUSIC_STOP; + } + } + } + + /* + SAMPLE VIBRATO + */ + sampleVibrato(vcptr); /* this gets added to previous freqdeltas */ + + + if (!(vcptr->mFrequency + vcptr->mFrequencyDelta)) + { + vcptr->mNoteControl &= ~FMUSIC_FREQ; /* divide by 0 check */ + } + + /* + set up volume values + */ + if (vcptr->mBackground) + { + volumedelta = 0; + } + else + { + volumedelta = cptr->mVolumeDelta; + vcptr->mVolume = cptr->mVolume; + } + + if (vcptr->mNoteControl & FMUSIC_TRIGGER) + { + FMOD_RESULT result; + bool filter = false; + SNDMIXPLUGIN *plugin = 0; + + if (iptr) + { + if (iptr->mFilterCutOff & 128) + { + filter = true; + } + + if (!(iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) && iptr->mFilterCutOff == 0xFF) + { + filter = false; + } + + if (iptr->mMIDIOutput & 128) + { + plugin = mMixPlugin[(iptr->mMIDIOutput & 127) - 1]; + } + } + + if (mChannelPlugin[count]) + { + plugin = mMixPlugin[mChannelPlugin[count] - 1]; + } + + if (plugin && plugin->Info.dwInputRouting & 0x1) + { + plugin = 0; /* It is already in as the final mix, don't add it again here */ + } + + result = playSound(vcptr->mSample, vcptr, filter, plugin); + + /* + If a negative frequency is selected, according to MPT it should start from the end. + */ + if (vcptr->mDirection < 0) + { + vcptr->mChannel.setPosition(vcptr->mSample->mSound->mLength - 1, FMOD_TIMEUNIT_PCM); + } + } + + if (vcptr->mNoteControl & FMUSIC_VOLUME) + { + float finalvol; + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) /* bugfix (fmod 2.21) */ + { + finalvol = (float)vcptr->mEnvVolume.mValue; /* 6 bits ( 64) */ + finalvol *= (vcptr->mVolume+volumedelta); /* 6 bits ( 64) */ + finalvol *= vcptr->mFadeOutVolume; /* 10 bits (1024) */ + finalvol *= vcptr->mSampGlobalVol; /* 6 bits ( 64) */ + finalvol *= cptr->mGlobalVolume; /* 6 bits ( 64) */ + finalvol *= mGlobalVolume; /* 7 bits ( 128) */ + finalvol *= mMasterVolume; /* 7 bits ( 128) */ + /* ============== */ + /* 48 bits */ + /* + Any half arsed compiler will convert this into 1 constant at compile time. + */ + finalvol *= (1.0f / (64.0f * 64.0f * 1024.0f * 64.0f * 64.0f * 128.0f * 128.0f) * 0.5f); + } + else + { + finalvol = (float)(vcptr->mVolume+volumedelta); /* 6 bits ( 64) */ + finalvol *= vcptr->mSampGlobalVol; /* 6 bits ( 64) */ + finalvol *= cptr->mGlobalVolume; /* 6 bits ( 64) */ + finalvol *= mGlobalVolume; /* 7 bits ( 128) */ + finalvol *= mMasterVolume; /* 7 bits ( 128) */ + /* ============== */ + /* 32 bits */ + /* + Any half arsed compiler will convert this into 1 constant at compile time. + */ + finalvol *= (1.0f / (64.0f * 64.0f * 64.0f * 128.0f * 128.0f) * 0.5f); + } + + vcptr->mChannel.setVolume(finalvol * cptr->mMasterVolume); + } + if (vcptr->mNoteControl & FMUSIC_PAN) + { + int finalpan; + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + finalpan = (vcptr->mPan + vcptr->mPanDelta + vcptr->mEnvPan.mValue); + } + else + { + finalpan = (vcptr->mPan + vcptr->mPanDelta); + } + + vcptr->mChannel.setPan(((float)finalpan / 31.5f) - 1.0f); + } + if (vcptr->mNoteControl & FMUSIC_FREQ) + { + int finalfreq; + + if (mMusicFlags & FMUSIC_ITFLAGS_INSTRUMENTS) + { + if (iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) + { + finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + } + else + { + finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta - vcptr->mEnvPitch; + } + } + else + { + finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + } + + if (mMusicFlags & FMUSIC_ITFLAGS_LINEARFREQUENCY) + { + finalfreq = FMUSIC_ITLINEARPERIOD2HZ(finalfreq); + } + else + { + finalfreq = period2HZ(finalfreq); + } + + if (vcptr->mDirection < 0) + { + finalfreq = -finalfreq; + } + + vcptr->mChannel.setFrequency((float)finalfreq); + } + + filter = false; + + if (iptr) + { + if (iptr->mFilterCutOff & 128) + { + filter = true; + } + if (!(iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) && iptr->mFilterCutOff == 0xFF) + { + filter = false; + } + } + + if (filter) + { + float Fc; + float cutoffhz, resonance; + int modifier = 256; + + if (iptr->mPitchType & FMUSIC_ENVELOPE_FILTER) + { + modifier = vcptr->mEnvPitch / 4; + } + + if (mMusicFlags & FMUSIC_ITFLAGS_EXTENDFILTERRANGE) + { + Fc = 110.0f * FMOD_POW(2.0f, 0.25f + ((float)((iptr->mFilterCutOff & 127) * (modifier+256)))/(20.0f*512.0f)); + } + else + { + Fc = 110.0f * FMOD_POW(2.0f, 0.25f + ((float)((iptr->mFilterCutOff & 127) *(modifier+256)))/(24.0f*512.0f)); + } + + cutoffhz = Fc; + if (cutoffhz < 120) + { + cutoffhz = 120; + } + if (cutoffhz > 20000) + { + cutoffhz = 20000; + } + if (cutoffhz * 2 > waveformat[0].frequency) + { + cutoffhz = (float)waveformat[0].frequency / 2.0f; + } + + resonance = (float)(iptr->mFilterResonance & 127); + + mLowPass[vcptr->mChannel.mIndex]->setParameter(FMOD_DSP_ITLOWPASS_CUTOFF, cutoffhz); + mLowPass[vcptr->mChannel.mIndex]->setParameter(FMOD_DSP_ITLOWPASS_RESONANCE, resonance); + } + + if (vcptr->mNoteControl & FMUSIC_SURROUND) + { +// FMOD_SetSurround(channel, true); + } + if (vcptr->mNoteControl & FMUSIC_SURROUNDOFF) + { +// FMOD_SetSurround(channel, false); + } + + if (vcptr->mNoteControl & FMUSIC_STOP) + { + if (mLowPass) + { + mLowPass[vcptr->mChannel.mIndex]->remove(); + } + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */ + } + + vcptr->mNoteControl = 0; + vcptr->cleanUp(); + + vcptr = next; + } + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::update(bool audible) +{ + if (mTick == 0) /* new note */ + { +// if (mFinished && !mLooping) +// { +// stop(); +// } +// else + { + /* + Process any rows commands to set the next order/row + */ + if (mNextOrder >= 0) + { + mOrder = mNextOrder; + +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ORDER, (unsigned char)mOrder); + if (mNextOrder >= 0) + { + mOrder = mNextOrder; + } + + /* + Skip any 'skip to next order' patterns + */ + while (mOrderList[mOrder] == 254) + { + mOrder++; + + if (mOrder >= mNumOrders) + { + if (!mLooping) + { + stop(); + } + mOrder = mRestart; + } + } + if (mOrderList[mOrder] == 255) + { + mOrder = mRestart; + #ifdef FMUSIC_PLAYPACKED + mPatternPtr = (signed char *)mPattern[mOrderList[mOrder]].mData; + #endif + } + } + + #ifdef FMUSIC_PLAYPACKED + if ((mNextRow >=0 && mNextRow != mRow + 1) || mNextOrder >= 0) + { + int count; + + /* + Reset pointer to start of pattern data + */ + mPatternPtr = (signed char *)mPattern[mOrderList[mOrder]].mData; + + for (count=0; count < mNextRow; count++) + { + unpackRow(); + } + } + #endif + + + if (mNextRow >= 0) + { + mRow = mNextRow; + +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ROW, (unsigned char)mRow); + if (mNextRow >= 0) + { + mRow = mNextRow; + } + + #ifdef FMUSIC_PLAYPACKED + unpackRow(); + #endif + } + + mNextOrder = -1; + mNextRow = -1; + updateRow(audible); /* Update and play the note */ + + /* + If there were no row commands + */ + if (mNextRow == -1) + { + mNextRow = mRow+1; + if (mNextRow >= mPattern[mOrderList[mOrder]].mRows) /* if end of pattern */ + { + mNextOrder = mOrder+1; /* so increment the order */ + if (mNextOrder >= mNumOrders) + { + mNextOrder = mRestart; + } + mNextRow = 0; /* start at top of pattn */ + } + } + +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ROW, (unsigned char)mRow); + } + } + else + { + updateRow(audible); /* Else update the inbetween row effects */ + } + + mTick++; + if (mTick >= mSpeed + mPatternDelay + mPatternDelayTicks) + { + mPatternDelay = 0; + mPatternDelayTicks = 0; + mTick = 0; + } + + mPCMOffset += mMixerSamplesPerTick; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::play(bool fromopen) +{ + MusicSong::play(fromopen); + +#ifdef FMUSIC_PLAYPACKED + int pattern; + + do + { + pattern = mOrderList[mOrder]; + if (pattern >= mNumPatternsMem) + { + mOrder++; + } + } + while (pattern >= mNumPatternsMem && mOrder < mNumOrders && mOrder < 255); + + if (pattern < mNumPatternsMem) + { + mPatternPtr = (signed char *)mPattern[pattern].mData; + unpackRow(); + } + else + { + mFinished = true; + mPlaying = false; + return FMOD_ERR_FORMAT; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + int count, highestorder; + char str[256]; + unsigned char sampleformat[256]; + unsigned char *buff = 0; + unsigned int largestsample = 0; + unsigned short filenumpatterns=0; + unsigned int filesize, filepos, lengthbytes; + int totalpatternsize = 0; + int version; + static int ioffset[256], poffset[256], soffset[256], sdataoffset[256]; + bool lowpassused = false; + FMOD_RESULT result = FMOD_OK; + int messageoffset; + int messagelength; + + if (!(mFile->mFlags & FMOD_FILE_SEEKABLE)) + { + return FMOD_ERR_FORMAT; + } + + init(FMOD_SOUND_TYPE_IT); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "attempting to open as IT..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get size of file in bytes + */ + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + /* + Verify Format + */ + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(str, 1, 4); + if (result != FMOD_OK) + { + return result; + } + if (FMOD_strncmp(str, "IMPM", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecIT::openInternal", "'IMPM' etc ID check failed [%c%c%c%c]\n", str[0], str[1], str[2], str[3])); + return FMOD_ERR_FORMAT; + } + + result = mFile->getSize(&filesize); + if (result != FMOD_OK) + { + return result; + } + + /* + Set a few default values for this format + */ + for (count = 0; count < MUSIC_MAXCHANNELS; count++) + { + mMusicChannel[count] = 0; + } + mPattern = 0; + mMasterSpeed = 1.0f; + mDefaultSpeed = 6; + mDefaultBPM = 125; + mDefaultGlobalVolume = 64; + mNumPatterns = 0; + mRestart = 0; + mNumSamples = 0; + mNumChannels = 64; + mMasterVolume = 128; + mPatternPtr = 0; + + FMOD_memset(mLastNote, 0 ,64); + FMOD_memset(mLastNumber, 0 ,64); + FMOD_memset(mLastVolume, 0 ,64); + FMOD_memset(mLastEffect, 0 ,64); + FMOD_memset(mLastEffectParam, 0 ,64); + FMOD_memset(mPreviousMaskVariable, 0 ,64); + FMOD_memset(sdataoffset, -1, 256); + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + + waveformat = mWaveFormatMemory; + waveformat[0].lengthbytes = lengthbytes; + + mSystem->getSoftwareFormat(&waveformat[0].frequency, 0, 0, 0, 0, 0); + + if (userexinfo && userexinfo->defaultfrequency) + { + waveformat[0].frequency = userexinfo->defaultfrequency; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "read song information..\n")); + + /* + Start loading data from the file. + */ + result = mFile->read(mSongName, 1, 26); /* read in module name. */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(2, SEEK_CUR); /* xx */ + if (result != FMOD_OK) + { + return result; + } + + result = mFile->getWord(&mNumOrders); /* num/orders */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mNumInstruments); /* num/instruments */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mNumSamples); /* num/samples */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&filenumpatterns); /* num/patterns */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(2, SEEK_CUR); /* cwt */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&version); /* cmwt */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mMusicFlags); /* Flags */ + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(2, SEEK_CUR); /* Special */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&mDefaultGlobalVolume /* Global volume */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&mMasterVolume); /* Master/mixing volume */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&mDefaultSpeed); /* Default speed */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte((unsigned int *)&mDefaultBPM); /* Default bpm */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* panning seperation (1 byte) */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* 0 (1 byte) */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&messagelength); /* message length (2 bytes) */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getDword(&messageoffset); /* message offset (4 bytes) */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getDword(); /* reserved (4 bytes) */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(mDefaultPan, 1, 64); /* Default pan */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mDefaultVolume, 1, 64); /* Default volume */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mOrderList, 1, mNumOrders); /* Orderlist */ + if (result != FMOD_OK) + { + return result; + } + + highestorder = 0; + for (count=0; count highestorder) + { + highestorder = count; + } + } + mNumOrders = highestorder+1; + + result = mFile->read(&ioffset, 4, mNumInstruments); /* Instrument data offset */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&soffset, 4, mNumSamples); /* Sample data offset */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&poffset, 4, filenumpatterns); /* Pattern data offset */ + if (result != FMOD_OK) + { + return result; + } + + /* + IT EXTRA INFO + */ + { + int numfilters; + + result = mFile->getWord(&numfilters); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(numfilters * 8, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + + /* + SKIP MIDI CONFIG + */ + if ((!messageoffset || filepos < (unsigned int)messageoffset) && + (!ioffset[0] || filepos < (unsigned int)ioffset[0]) && + filepos < (unsigned int)soffset[0] && + filepos < (unsigned int)poffset[0]) + { + if (mMusicFlags & FMUSIC_ITFLAGS_EMBEDMIDICFG) + { + result = mFile->seek(sizeof(MODMIDICFG), SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + + /* + PATTERN NAMES + */ + FMOD_memset(str, 0, 4); + + if ((!messageoffset || filepos < (unsigned int)messageoffset) && + (!ioffset[0] || filepos < (unsigned int)ioffset[0]) && + filepos < (unsigned int)soffset[0] && + filepos < (unsigned int)poffset[0]) + { + result = mFile->read(str, 1, 4); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp(str, "PNAM", 4)) + { + unsigned int length; + + result = mFile->getDword(&length); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(length, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mFile->seek(filepos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + + /* + CHANNEL NAMES + */ + if ((!messageoffset || filepos < (unsigned int)messageoffset) && + (!ioffset[0] || filepos < (unsigned int)ioffset[0]) && + filepos < (unsigned int)soffset[0] && + filepos < (unsigned int)poffset[0]) + { + result = mFile->read(str, 1, 4); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp(str, "CNAM", 4)) + { + unsigned int length; + + result = mFile->getDword(&length); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(length, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(str, 1, 4); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mFile->seek(filepos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + + /* + RUDIMENTARY CROSS PLATFORM MPT PLUGIN SUPPORT. + CURRENTLY ONLY ECHO IS SUPPORTED, BUT HEY! ITS CROSS PLATFORM AND DOESN'T RELY ON DIRECTX! + */ + while ((!messageoffset || filepos < (unsigned int)messageoffset) && + (!ioffset[0] || filepos < (unsigned int)ioffset[0]) && + filepos < (unsigned int)soffset[0] && + filepos < (unsigned int)poffset[0]) + { + char id[4]; + unsigned int pluginsize; + int pluginid; + + result = mFile->read(id, 1, 4); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getDword(&pluginsize); + if (result != FMOD_OK) + { + return result; + } + + if (id[0] == 'C' && id[1] == 'H' && id[2] == 'F' && id[3] == 'X') + { + for (count = 0; count < 64; count++) + { + if (count * 4 < (int)pluginsize) + { + result = mFile->getDword(&mChannelPlugin[count]); + if (result != FMOD_OK) + { + return result; + } + } + } + } + else if (id[0] == 'E' && id[1] == 'Q' && id[2] == 'F' && id[3] == 'X') + { + result = mFile->seek(pluginsize, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + else + { + if (id[0] != 'F' || id[1] != 'X' || id[2] < '0' || id[3] < '0') + { + break; + } + + pluginid = (id[2]-'0') * 10 + (id[3]-'0'); + + /* + If we have a valid plugin, attempt to load its data. + */ + if ((pluginid < MAX_MIXPLUGINS) && (pluginsize >= sizeof(SNDMIXPLUGININFO)+4)) + { + char *plugindata; + unsigned int dwExtra; + SNDMIXPLUGININFO *plugininfo; + + plugindata = (char *)FMOD_Memory_Calloc(pluginsize); + if (!plugindata) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mFile->read(plugindata, 1, pluginsize); + if (result != FMOD_OK) + { + return result; + } + + plugininfo = (SNDMIXPLUGININFO *)plugindata; + + dwExtra = *(unsigned int *)(plugindata+sizeof(SNDMIXPLUGININFO)); + + #ifdef PLATFORM_ENDIAN_BIG + dwExtra = FMOD_SWAPENDIAN_DWORD(dwExtra); + #endif + + if (!FMOD_strncmp(plugininfo->szLibraryName, "Echo", 4) && dwExtra == sizeof(SNDMIXPLUGINDATA_ECHO)) + { + if (!mMixPlugin[pluginid]) + { + mMixPlugin[pluginid] = (SNDMIXPLUGIN *)FMOD_Memory_Calloc(sizeof(SNDMIXPLUGIN)); + if (!mMixPlugin[pluginid]) + { + return FMOD_ERR_MEMORY; + } + } + + new (&mMixPlugin[pluginid]->mChannelGroup) ChannelGroupI(); + + mMixPlugin[pluginid]->Info = *plugininfo; + + #ifdef PLATFORM_ENDIAN_BIG + mMixPlugin[pluginid]->Info.dwInputRouting = FMOD_SWAPENDIAN_DWORD(mMixPlugin[pluginid]->Info.dwInputRouting); + mMixPlugin[pluginid]->Info.dwOutputRouting = FMOD_SWAPENDIAN_DWORD(mMixPlugin[pluginid]->Info.dwOutputRouting); + mMixPlugin[pluginid]->Info.dwPluginId1 = FMOD_SWAPENDIAN_DWORD(mMixPlugin[pluginid]->Info.dwPluginId1); + mMixPlugin[pluginid]->Info.dwPluginId2 = FMOD_SWAPENDIAN_DWORD(mMixPlugin[pluginid]->Info.dwPluginId2); + #endif + + if (dwExtra && (dwExtra <= pluginsize - sizeof(SNDMIXPLUGININFO)-4)) + { + DSPI *dsp; + SNDMIXPLUGINDATA_ECHO *echo = 0; + + echo = (SNDMIXPLUGINDATA_ECHO *)FMOD_Memory_Calloc(dwExtra); + if (!echo) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memcpy(echo, plugindata+sizeof(SNDMIXPLUGININFO)+4, dwExtra); + + #ifdef PLATFORM_ENDIAN_BIG + FMOD_SWAPENDIAN_FLOAT(echo->mFeedback); + FMOD_SWAPENDIAN_FLOAT(echo->mLeftDelay); + FMOD_SWAPENDIAN_FLOAT(echo->mPanDelay); + FMOD_SWAPENDIAN_FLOAT(echo->mRightDelay); + FMOD_SWAPENDIAN_FLOAT(echo->mWetDryMix); + FMOD_SWAPENDIAN_FLOAT(echo->unknown); + #endif + + mMixPlugin[pluginid]->mChannelGroup.mVolume = 1.0f; + + result = mSystem->createDSPByType(FMOD_DSP_TYPE_ITECHO, &mMixPlugin[pluginid]->mChannelGroup.mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + dsp = mMixPlugin[pluginid]->mChannelGroup.mDSPMixTarget = mMixPlugin[pluginid]->mChannelGroup.mDSPHead; + dsp->mDefaultFrequency = (float)waveformat[0].frequency; + + #ifdef PLATFORM_PS3 + /* + This itecho is happening on the PPU, point it back to PPU address. + */ + dsp->mDescription.read = DSPITEcho::readCallback; + #endif + + result = dsp->setParameter(FMOD_DSP_ITECHO_WETDRYMIX, echo->mWetDryMix * 100.0f); + if (result != FMOD_OK) + { + return result; + } + result = dsp->setParameter(FMOD_DSP_ITECHO_FEEDBACK, echo->mFeedback * 100.0f); + if (result != FMOD_OK) + { + return result; + } + result = dsp->setParameter(FMOD_DSP_ITECHO_LEFTDELAY, echo->mLeftDelay * 2000.0f); + if (result != FMOD_OK) + { + return result; + } + result = dsp->setParameter(FMOD_DSP_ITECHO_RIGHTDELAY, echo->mRightDelay * 2000.0f); + if (result != FMOD_OK) + { + return result; + } + result = dsp->setParameter(FMOD_DSP_ITECHO_PANDELAY, echo->mPanDelay); + if (result != FMOD_OK) + { + return result; + } + + if (echo) + { + FMOD_Memory_Free(echo); + } + } + } + + FMOD_Memory_Free(plugindata); + } + else + { + result = mFile->seek(pluginsize, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + } + } + + /* + READ MESSAGE AS TAG + */ + if (messagelength) + { + char *message; + + result = mFile->seek(messageoffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + message = (char *)FMOD_Memory_Calloc(messagelength + 1); + if (!message) + { + return FMOD_ERR_MEMORY; + } + + result = mFile->read(message, messagelength, 1); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < messagelength; count++) + { + if (message[count] == 0xd) + { + message[count] = '\n'; + } + } + + result = metaData(FMOD_TAGTYPE_FMOD, "Song message", message, messagelength, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + + FMOD_Memory_Free(message); + } + + /* + ALLOCATE MUSIC CHANNELS + */ + for (count = 0; count < mNumChannels; count++) + { + MusicChannelIT *cptr; + + mMusicChannel[count] = FMOD_Object_Calloc(MusicChannelIT); + if (!mMusicChannel[count]) + { + return FMOD_ERR_MEMORY; + } + cptr = (MusicChannelIT *)mMusicChannel[count]; + cptr->mModule = this; + } + + result = metaData(FMOD_TAGTYPE_FMOD, "Number of channels", &mNumChannels, sizeof(mNumChannels), FMOD_TAGDATATYPE_INT, false); + if (result != FMOD_OK) + { + return result; + } + + /* + LOAD INSTRUMENTS + */ + + /* + Alloc instrument array + */ + mInstrument = (MusicInstrument *)FMOD_Memory_Calloc((mNumInstruments) * sizeof(MusicInstrument)); + if (!mInstrument) + { + return FMOD_ERR_MEMORY; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "read instrument information..\n")); + + /* + Load instrument information + */ + for (count = 0; count < mNumInstruments; count++) + { + MusicInstrument *iptr; + unsigned char dat; + int count2; + + /* + Point a pointer to that particular instrument + */ + iptr = &mInstrument[count]; + + result = mFile->seek(ioffset[count], SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(str, 1, 4); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp(str, "IMPI", 4)) + { + return FMOD_ERR_FORMAT; + } + + result = mFile->seek(12, SEEK_CUR); /* skip dos filename */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* 0 */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mNNA /* NNA */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mDupCheckType /* duplicate check type */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mDupCheckAction /* duplicate check action */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&iptr->mVolumeFade); /* fade out value */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPitchPanSep ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPitchPanCenter ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mGlobalVolume ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mDefaultPan); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeVariation ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanVariation ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(2, SEEK_CUR); /* instrument version */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mNumSamples); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* x */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(iptr->mName, 1, 26); /* instrument name */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mFilterCutOff); + if (result != FMOD_OK) + { + return result; + } + + if (iptr->mFilterCutOff & 128) + { + lowpassused = true; + } + + result = mFile->getByte(&iptr->mFilterResonance); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mMIDIOutput); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* midi program (instrument)*/ + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(2, SEEK_CUR); /* xx */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(iptr->mNoteTable, 1, 240); /* keymap layout */ + if (result != FMOD_OK) + { + return result; + } + + /* + Volume envelope + */ + iptr->mVolumeType = 0; + result = mFile->getByte(&dat); + if (result != FMOD_OK) + { + return result; + } + if (dat & 1) iptr->mVolumeType |= FMUSIC_ENVELOPE_ON; + if (dat & 2) iptr->mVolumeType |= FMUSIC_ENVELOPE_LOOP; + if (dat & 4) iptr->mVolumeType |= FMUSIC_ENVELOPE_SUSTAIN; + if (dat & 8) iptr->mVolumeType |= FMUSIC_ENVELOPE_CARRY; + result = mFile->getByte(&iptr->mVolumeNumPoints ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeLoopStart ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeLoopEnd ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeSustainLoopStart ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeSustainLoopEnd ); + if (result != FMOD_OK) + { + return result; + } + + for (count2=0; count2 < 25; count2++) + { + unsigned short val; + MusicEnvelopeNode *v = (MusicEnvelopeNode *)&iptr->mVolumePoints; + + result = mFile->getByte(&v[count2].mValue); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&val); + if (result != FMOD_OK) + { + return result; + } + v[count2].mTick = val; + } + + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + if (iptr->mVolumeNumPoints < 2) + { + iptr->mVolumeType = FMUSIC_ENVELOPE_OFF; + } + + /* + Pan envelope + */ + iptr->mPanType = 0; + result = mFile->getByte(&dat); + if (result != FMOD_OK) + { + return result; + } + if (dat & 1) iptr->mPanType |= FMUSIC_ENVELOPE_ON; + if (dat & 2) iptr->mPanType |= FMUSIC_ENVELOPE_LOOP; + if (dat & 4) iptr->mPanType |= FMUSIC_ENVELOPE_SUSTAIN; + if (dat & 8) iptr->mPanType |= FMUSIC_ENVELOPE_CARRY; + result = mFile->getByte(&iptr->mPanNumPoints); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanLoopStart); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanLoopEnd); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanSustainLoopStart ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanSustainLoopEnd); + if (result != FMOD_OK) + { + return result; + } + for (count2=0; count2 < 25; count2++) + { + unsigned short val; + MusicEnvelopeNode *v = (MusicEnvelopeNode *)&iptr->mPanPoints; + + result = mFile->getByte(&v[count2].mValue); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&val); + if (result != FMOD_OK) + { + return result; + } + v[count2].mTick = val; + } + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + if (iptr->mPanNumPoints < 2) + { + iptr->mPanType = FMUSIC_ENVELOPE_OFF; + } + + /* + Pitch envelope + */ + iptr->mPitchType = 0; + result = mFile->getByte(&dat); + if (result != FMOD_OK) + { + return result; + } + if (dat & 1) iptr->mPitchType |= FMUSIC_ENVELOPE_ON; + if (dat & 2) iptr->mPitchType |= FMUSIC_ENVELOPE_LOOP; + if (dat & 4) iptr->mPitchType |= FMUSIC_ENVELOPE_SUSTAIN; + if (dat & 8) iptr->mPitchType |= FMUSIC_ENVELOPE_CARRY; + if (dat & 0x80) iptr->mPitchType |= FMUSIC_ENVELOPE_FILTER; + + result = mFile->getByte(&iptr->mPitchNumpoints ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPitchLoopStart ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPitchLoopEnd ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPitchSustainLoopStart); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPitchSustainLoopEnd ); + if (result != FMOD_OK) + { + return result; + } + for (count2=0; count2 < 25; count2++) + { + unsigned short val; + MusicEnvelopeNode *v = (MusicEnvelopeNode *)&iptr->mPitchPoints; + + result = mFile->getByte(&v[count2].mValue); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&val); + if (result != FMOD_OK) + { + return result; + } + v[count2].mTick = val; + } + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + if (iptr->mPitchNumpoints < 2) + { + iptr->mPitchType = FMUSIC_ENVELOPE_OFF; + } + } + + + /* + LOAD SAMPLES + */ + + /* + Alloc sample array + */ + mSample = 0; + if (mNumSamples) + { + mSample = (MusicSample **)FMOD_Memory_Calloc(mNumSamples * sizeof(MusicSample *)); + if (!mSample) + { + return FMOD_ERR_MEMORY; + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "read sample information..\n")); + + /* + Load sample information + */ + for (count=0; count < mNumSamples; count++) + { + MusicSample *sptr; + char sample_name[26]; + FMOD_MODE sample_mode; + unsigned int sample_length; + FMOD_SOUND_FORMAT sample_format; + int sample_channels; + + mSample[count] = &mSampleMem[count]; + sptr = mSample[count]; + + result = mFile->seek(soffset[count], SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(str, 1, 4); + if (result != FMOD_OK) + { + return result; + } + if (FMOD_strncmp(str, "IMPS", 4)) + { + return FMOD_ERR_FORMAT; + } + result = mFile->seek(12, SEEK_CUR); /* skip dos filename */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* 0 */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mGlobalVolume /* global volume (0-64) */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mFlags); /* flags */ + if (result != FMOD_OK) + { + return result; + } + + sample_mode = FMOD_SOFTWARE | FMOD_2D; + sample_channels = 1; + + if (sptr->mFlags & 2) + { + sample_format = FMOD_SOUND_FORMAT_PCM16; + } + else + { + sample_format = FMOD_SOUND_FORMAT_PCM8; + } + + if (sptr->mFlags & 4) + { + sample_channels = 2; + } + + if (sptr->mFlags & 16) + { + if (sptr->mFlags & 64) + { + sample_mode |= FMOD_LOOP_BIDI; + } + else + { + sample_mode |= FMOD_LOOP_NORMAL; + } + } + else + { + sample_mode |= FMOD_LOOP_OFF; + } + + /* if (sptr->mFlags & 128) sptr-> */ + result = mFile->getByte(&sptr->mDefaultVolume /* default volume (0-64) */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(sample_name, 1, 26); /* sample name */ + if (result != FMOD_OK) + { + return result; + } + + { + char s[256]; + + sprintf(s, "Sample name %d", count); + + result = metaData(FMOD_TAGTYPE_FMOD, s, sample_name, 28, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + } + + result = mFile->getByte(&sampleformat[count] /* cvt */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mDefaultPan /* default pan (0-64) */); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sample_length, 4, 1); /* length in samples */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mLoopStart, 4, 1); /* loop start */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mLoopLength, 4, 1); /* loop end */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mMiddleC, 4, 1); /* C5spd */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mSusLoopBegin, 4, 1);/* sustain loop begin */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mSusLoopEnd, 4, 1); /* sustain loop end */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sdataoffset[count], 4, 1); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->getByte(&sptr->mVibSpeed); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mVibDepth); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mVibRate ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mVibType ); + if (result != FMOD_OK) + { + return result; + } + + sptr->mLoopLength-=sptr->mLoopStart; + + if (!sptr->mMiddleC) + { + sptr->mMiddleC = 8363; + } + + if (!sptr->mLoopLength || sample_mode & FMOD_LOOP_OFF) + { + sptr->mLoopStart = 0; + sptr->mLoopLength = sample_length; + sample_mode &= ~FMOD_LOOP_NORMAL; + sample_mode &= ~FMOD_LOOP_BIDI; + sample_mode |= FMOD_LOOP_OFF; + } + + /* + ALLOCATE MEMORY FOR THE SAMPLE BUFFER + */ + if (sample_length) + { + FMOD_CREATESOUNDEXINFO exinfo; + unsigned int lenbytes; + + SoundI::getBytesFromSamples(sample_length, &lenbytes, sample_channels, sample_format); + + FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.length = lenbytes; + exinfo.numchannels = sample_channels; + exinfo.defaultfrequency = sptr->mMiddleC; + exinfo.format = sample_format; + + result = mSystem->createSound(0, sample_mode | FMOD_OPENUSER, &exinfo, &sptr->mSound); + if (result != FMOD_OK) + { + return result; + } + + if (sample_mode & FMOD_LOOP_NORMAL || sample_mode & FMOD_LOOP_BIDI) + { + result = sptr->mSound->setLoopPoints(sptr->mLoopStart, FMOD_TIMEUNIT_PCM, sptr->mLoopStart + sptr->mLoopLength - 1, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { +// return result; + } + } + } + } + sdataoffset[count] = filesize; + + + /* + LOAD PATTERNS + */ + + /* + Get the true number of patterns + */ + mNumPatterns = 0; + for (count=0; count= mNumPatterns) + { + mNumPatterns = mOrderList[count]+1; + } + } + + /* + Alloc pattern array + */ + mNumPatternsMem = (mNumPatterns > filenumpatterns ? mNumPatterns : filenumpatterns); + mPattern = (MusicPattern *)FMOD_Memory_Calloc(mNumPatternsMem * sizeof(MusicPattern)); + if (!mPattern) + { + return FMOD_ERR_MEMORY; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "read pattern information..\n")); + + /* + Unpack and read patterns + */ + for (count=0; count < filenumpatterns; count++) + { + MusicPattern *pptr; + unsigned short rows; +#ifndef FMUSIC_PLAYPACKED + unsigned short row; + unsigned char channelvariable; +#endif + + pptr = &mPattern[count]; + + if (!poffset[count]) + { + /* + Allocate memory for pattern buffer + */ + pptr->mRows = 64; + +#ifdef FMUSIC_PLAYPACKED + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(pptr->mRows); +#else + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); +#endif + if (!pptr->mData) + { + return FMOD_ERR_MEMORY; + } + } + else + { + unsigned short packedpatlen; + + result = mFile->seek(poffset[count], SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&packedpatlen); /* length of packed pattern */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&rows); /* length of pattern */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(4, SEEK_CUR); /* xxxx */ + if (result != FMOD_OK) + { + return result; + } + pptr->mRows = rows; + +#ifdef FMUSIC_PLAYPACKED + + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(packedpatlen); + result = mFile->read(pptr->mData, 1, packedpatlen); + if (result != FMOD_OK) + { + return result; + } + totalpatternsize += packedpatlen; +#else + /* + this method uses 20k per pattern.. ow.. (255 patterns = 5.1mb!) + allocate memory for pattern buffer + */ + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); + if (!pptr->mData) + { + FMOD_ErrorNo = FMOD_ERR_MEMORY; + return false; + } + totalpatternsize += mNumChannels * pptr->mRows * sizeof(MusicNote); + + for (row=0; row < pptr->mRows; ) + { + result = mFile->getByte(&channelvariable); + if (result != FMOD_OK) + { + return result; + } + + if (channelvariable) + { + unsigned char channel, maskvariable; + MusicNote *nptr; + + channel = (channelvariable-1) & 63; + + nptr = pptr->mData+(row*mNumChannels)+channel; + + if (channelvariable & 128) + { + result = mFile->getByte(&maskvariable); + if (result != FMOD_OK) + { + return result; + } + mPreviousMaskVariable[channel] = maskvariable; + } + else maskvariable = mPreviousMaskVariable[channel]; + + if (maskvariable & 1) + { + result = mFile->getByte(&unsigned char note); + if (result != FMOD_OK) + { + return result; + } + + if (note >= 254) + { + nptr->mNote = note; + } + else + { + nptr->mNote = note + 1; + } + + mLastNote[channel] = nptr->mNote; + } + if (maskvariable & 2) + { + result = mFile->getByte(&nptr->mNumber); + if (result != FMOD_OK) + { + return result; + } + mLastNumber[channel] = nptr->mNumber; + } + if (maskvariable & 4) + { + nptr->mVolume = FMOD_File_GetChar(fp) + 1; + mLastVolume[channel] = nptr->mVolume; + } + if (maskvariable & 8) + { + result = mFile->getByte(&nptr->mEffect); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&nptr->mEffectParam ); + if (result != FMOD_OK) + { + return result; + } + mLastEffect[channel] = nptr->mEffect; + mLastEffectParam[channel] = nptr->mEffectParam; + } + if (maskvariable & 16) nptr->mNote = mLastNote[channel]; + if (maskvariable & 32) nptr->mNumber = mLastNumber[channel]; + if (maskvariable & 64) nptr->mVolume = mLastVolume[channel]; + if (maskvariable & 128) + { + nptr->mEffect = mLastEffect[channel]; + nptr->mEffectParam = mLastEffectParam[channel]; + } + + } + else + { + row++; + } + } +#endif + } + } + + /* + Allocate and clean out any extra patterns + */ + if (mNumPatterns > filenumpatterns) + { + for (count=filenumpatterns; count < mNumPatterns; count++) + { + MusicPattern *pptr; + + pptr = &mPattern[count]; + pptr->mRows = 64; + + /* + Allocate memory for pattern buffer + */ +#ifdef FMUSIC_PLAYPACKED + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(pptr->mRows); +#else + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); +#endif + if (!pptr->mData) + { + return FMOD_ERR_MEMORY; + } + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "load sample data..\n")); + + /* + LOAD SAMPLES + */ + + for (count = 0; count < mNumSamples; count++) + { + MusicSample *sptr = mSample[count]; + unsigned int srclenbytes = 0; + + if (count == 8) + { + count = count; + } + + if (sdataoffset[count+1] > sdataoffset[count]) + { + srclenbytes = sdataoffset[count+1] - sdataoffset[count]; + } + else + { + if (sptr->mSound) + { + mSample[count]->mSound->getLength(&srclenbytes, FMOD_TIMEUNIT_PCMBYTES); + } + } + + if (sptr->mSound) + { + void *ptr1, *ptr2; + unsigned int len1, len2; + bool signeddata = false; + unsigned int lenbytes; + int compression = 0; + FMOD_SOUND_FORMAT format; + + mSample[count]->mSound->getLength(&lenbytes, FMOD_TIMEUNIT_PCMBYTES); + + sptr->mSound->getFormat(0, &format, 0, 0); + + if (sampleformat[count] & 1) + { + signeddata = true; + } + + #if 1 + if (sptr->mFlags & 8) + { + compression = ((version >= 0x215) && (sampleformat[count] & 4)) ? 215 : 214; + } + #else + if (version >= 0x0214) + { + if (version == 0x0215) + { + if (sampleformat[count] & 4) + { + compression = 215; + } + else + { + compression = 214; + } + } + else + { + compression = 214; + } + } + + if (srclenbytes == lenbytes) + { + compression = 0; + } + #endif + + result = mFile->seek(sdataoffset[count], SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = sptr->mSound->lock(0, lenbytes, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + if (ptr1 && len1) + { + if (!compression && signeddata) + { + if (format == FMOD_SOUND_FORMAT_PCM16) + { + if (sptr->mSound->mChannels == 1) + { + result = mFile->read(ptr1, 2, len1 >> 1); + } + else + { + unsigned int count2; + + /* + IT stereo samples are not interleaved! + */ + unsigned int off, len; + + /* LEFT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed short tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 2, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed short *)ptr1)[(off + count2) * 2] = tmp[count2]; + } + + len -= r; + off += r; + } + + /* RIGHT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed short tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 2, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed short *)ptr1)[((off + count2) * 2) + 1] = tmp[count2]; + } + + len -= r; + off += r; + } + } + + if (result != FMOD_OK) + { + return result; + } + } + else + { + if (sptr->mSound->mChannels == 1) + { + result = mFile->read(ptr1, 1, len1); + if (result != FMOD_OK) + { + return result; + } + } + else + { + unsigned int count2; + + /* + IT stereo samples are not interleaved! + */ + unsigned int off, len; + + /* LEFT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed char tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 1, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed char *)ptr1)[(off + count2) * 2] = tmp[count2]; + } + + len -= r; + off += r; + } + + /* RIGHT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed char tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 1, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed char *)ptr1)[((off + count2) * 2) + 1] = tmp[count2]; + } + + len -= r; + off += r; + } + } + } + } + else + { + if (srclenbytes > largestsample) + { + if (buff) + { + FMOD_Memory_Free(buff); + } + + buff = (unsigned char *)FMOD_Memory_Alloc(srclenbytes); + if (!buff) + { + return FMOD_ERR_MEMORY; + } + largestsample = srclenbytes; + } + + FMOD_memset(buff, 0, largestsample); + result = mFile->read(buff, 1, srclenbytes); /* if from file, load and then upload */ + if (result != FMOD_OK) + { + return result; + } + + if (format == FMOD_SOUND_FORMAT_PCM8) + { + int count2; + void *src = buff; + for (count2 = 0; count2 < sptr->mSound->mChannels; count2++) + { + decompress8(&src, (char *)ptr1 + count2, lenbytes / sptr->mSound->mChannels, compression == 215, sptr->mSound->mChannels); + } + } + else + { + int count2; + void *src = buff; + for (count2 = 0; count2 < sptr->mSound->mChannels; count2++) + { + decompress16(&src, (short *)ptr1 + count2, lenbytes / 2 / sptr->mSound->mChannels, compression == 215, sptr->mSound->mChannels); + } + } + } + } + + result = sptr->mSound->unlock(ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + return result; + } + } + } + + if (buff) + { + FMOD_Memory_Free(buff); + } + + /* + Set up general codec parameters. + */ + if (userexinfo && userexinfo->format != FMOD_SOUND_FORMAT_NONE) + { + waveformat[0].format = userexinfo->format; + } + #ifndef PLATFORM_PS3 + else if (usermode & FMOD_SOFTWARE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + #endif + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + waveformat[0].channels = 2; + FMOD_strncpy(waveformat[0].name, mSongName, FMOD_STRING_MAXNAMELEN); + mSrcDataOffset = 0; + + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, waveformat[0].channels, waveformat[0].format); + + /* + Create final target unit. + */ + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + descriptionex.version = 0x00010100; + descriptionex.channels = waveformat[0].channels; + descriptionex.mFormat = waveformat[0].format; + descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + + FMOD_strcpy(descriptionex.name, "FMOD IT final mixdown unit"); + result = mSystem->createDSP(&descriptionex, &mDSPFinalHead); + if (result != FMOD_OK) + { + return result; + } + mDSPFinalHead->mDefaultFrequency = (float)waveformat[0].frequency; + mDSPFinalHead->setActive(true); + } + + /* + Create mixdown unit that other effect units can connect to along side the mDSPHead. + */ + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + descriptionex.version = 0x00010100; + descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER; + FMOD_strcpy(descriptionex.name, "FMOD IT global effect head unit"); + result = mSystem->createDSP(&descriptionex, &mDSPEffectHead); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPFinalHead->addInput(mDSPEffectHead); + if (result != FMOD_OK) + { + return result; + } + + mDSPEffectHead->mDefaultFrequency = (float)waveformat[0].frequency; + mDSPEffectHead->setActive(true); + } + + /* + Create head units that software channels connect to. + */ + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + descriptionex.version = 0x00010100; + descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER; + FMOD_strcpy(descriptionex.name, "FMOD IT channel head unit"); + result = mSystem->createDSP(&descriptionex, &mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPEffectHead->addInput(mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + mDSPHead->mDefaultFrequency = (float)waveformat[0].frequency; + mDSPHead->setActive(true); + } + + + /* + ADD DSP EFFECTS IF THEY EXIST. + */ + + for (count = 0; count < MAX_MIXPLUGINS; count++) + { + if (mMixPlugin[count] && mMixPlugin[count]->mChannelGroup.mDSPHead) + { + DSPI *effect; + + effect = mMixPlugin[count]->mChannelGroup.mDSPHead; + + /* + Final mix or not. + */ + if (mMixPlugin[count]->Info.dwInputRouting & 0x1) + { + DSPI *nexteffect; + + result = mDSPFinalHead->getInput(0, &nexteffect); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPFinalHead->disconnectFrom(nexteffect); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPFinalHead->addInput(effect); + if (result != FMOD_OK) + { + return result; + } + + result = effect->addInput(nexteffect); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mDSPEffectHead->addInput(effect); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Bypass flag. + */ + if (mMixPlugin[count]->Info.dwInputRouting & 0x2) + { + result = effect->setBypass(true); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Mix in the dry as well. + This could be done by creating another dummy unit which just passes the dry signal into the mix along side the echo. + Its pretty annoying, but i don't think many musicians are really using it. + */ + if (mMixPlugin[count]->Info.dwInputRouting & 0x4) + { + // ???? + } + + result = effect->setActive(true); + if (result != FMOD_OK) + { + return result; + } + } + } + + + /* + Create a pool of virtual channels. + */ + { + if (userexinfo && userexinfo->maxpolyphony) + { + mNumVirtualChannels = userexinfo->maxpolyphony; + } + else + { + mNumVirtualChannels = mNumChannels; + } + + mVirtualChannel = (MusicVirtualChannel *)FMOD_Memory_Calloc(sizeof(MusicVirtualChannel) * mNumVirtualChannels); + if (!mVirtualChannel) + { + return FMOD_ERR_MEMORY; + } + for (count = 0; count < mNumVirtualChannels; count++) + { + new (&mVirtualChannel[count]) MusicVirtualChannel; + } + } + + + /* + Create a pool of real channels. 2x the number of virtual channels for double buffer channel usage. + */ + mNumRealChannels = mNumVirtualChannels * 2; + + mChannelPool = FMOD_Object_Calloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, 0, mNumRealChannels); + if (result != FMOD_OK) + { + return result; + } + + mChannelSoftware = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * mNumRealChannels); + if (!mChannelSoftware) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < mNumRealChannels; count++) + { + new (&mChannelSoftware[count]) ChannelSoftware; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannelSoftware[count], mDSPHead)); + CHECK_RESULT(mChannelSoftware[count].allowReverb(false)); + } + + if (lowpassused) + { + mLowPass = (DSPI **)FMOD_Memory_Calloc(sizeof(DSPI *) * mNumRealChannels); + if (!mLowPass) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < mNumRealChannels; count++) + { + result = mSystem->createDSPByType(FMOD_DSP_TYPE_ITLOWPASS, &mLowPass[count]); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_PS3 + /* + This lowpass is happening on the PPU, point it back to PPU address. + */ + mLowPass[count]->mDescription.read = DSPLowPass2::readCallback; + #endif + } + } + + if ((usermode & FMOD_ACCURATETIME) || (usermode & FMOD_CREATESAMPLE)) + { + /* + Calculate the length of the song by quickly playing through it. + */ + mVisited = (bool *)FMOD_Memory_Calloc(sizeof(bool) * mNumOrders * FMUSIC_MAXROWS); + if (!mVisited) + { + return FMOD_ERR_MEMORY; + } + + calculateLength(); + } + else + { + mVisited = 0; + waveformat[0].lengthpcm = (unsigned int)-1; /* Thanks to those stupid comment markers! */ + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + play(true); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecIT::openInternal", "done.\n")); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::closeInternal() +{ + int count; + + stop(); + + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + + if (mDSPFinalHead) + { + mDSPFinalHead->release(); + mDSPFinalHead = 0; + } + + if (mDSPEffectHead) + { + mDSPEffectHead->release(); + mDSPEffectHead = 0; + } + + if (mDSPHead) + { + mDSPHead->release(); + mDSPHead = 0; + } + + if (mLowPass) + { + for (count = 0; count < mNumRealChannels; count++) + { + if (mLowPass[count]) + { + mLowPass[count]->release(); + } + } + + FMOD_Memory_Free(mLowPass); + mLowPass = 0; + } + + if (mSample) + { + for (count = 0; count < mNumSamples; count++) + { + if (mSample[count] && mSample[count]->mSound) + { + mSample[count]->mSound->release(); + mSample[count]->mSound = 0; + mSample[count] = 0; + } + } + + FMOD_Memory_Free(mSample); + mSample = 0; + } + + if (mInstrument) + { + FMOD_Memory_Free(mInstrument); + mInstrument = 0; + } + + if (mVirtualChannel) + { + FMOD_Memory_Free(mVirtualChannel); + mVirtualChannel = 0; + } + + if (mChannelSoftware) + { + FMOD_Memory_Free(mChannelSoftware); + mChannelSoftware = 0; + } + + if (mPattern) + { + for (count = 0; count < mNumPatternsMem; count++) + { + if (mPattern[count].mData) + { + FMOD_Memory_Free(mPattern[count].mData); + mPattern[count].mData = 0; + } + } + + FMOD_Memory_Free(mPattern); + mPattern = 0; + } + + for (count = 0; count < MAX_MIXPLUGINS; count++) + { + if (mMixPlugin[count]) + { + mMixPlugin[count]->mChannelGroup.mDSPHead->release(); + + FMOD_Memory_Free(mMixPlugin[count]); + } + } + + for (count = 0; count < mNumChannels; count++) + { + if (mMusicChannel[count]) + { + FMOD_Memory_Free(mMusicChannel[count]); + mMusicChannel[count] = 0; + } + } + + if (mVisited) + { + FMOD_Memory_Free(mVisited); + mVisited = 0; + } + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int numsamples; + int numchannels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + numchannels = waveformat[0].channels; + + SoundI::getSamplesFromBytes(sizebytes, &numsamples, numchannels, waveformat[0].format); + + if (mPlaying && mMasterSpeed) + { + unsigned int mixedsofar = 0; + unsigned int mixedleft = mMixerSamplesLeft; + unsigned int samplestomix; + char *destptr = (char *)buffer; /* Keep resetting the mix pointer to the beginning of this portion of the ring buffer */ + + while (mixedsofar < numsamples) + { + unsigned int read = 0, bytes; + + if (!mixedleft) + { + result = update(true); + if (result != FMOD_OK) + { + return result; + } + + samplestomix = mMixerSamplesPerTick; + mixedleft = samplestomix; + } + else + { + samplestomix = mixedleft; + } + + if (mixedsofar + samplestomix > numsamples) + { + samplestomix = numsamples - mixedsofar; + } + + read = samplestomix; + + criticalsection.enter(); + if (buffer) + { + result = mDSPFinalHead->read(destptr, &read, FMOD_SPEAKERMODE_STEREO_LINEAR, 2, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + + mDSPTick++; + } + + SoundI::getBytesFromSamples(read, &bytes, numchannels, waveformat[0].format); + + /* + Buffer returned from the DSP head execute may not be the one we sent in (it may be + one of the global buffers). Don't leave mDSPCrit until after we have copied data out + */ + criticalsection.leave(); + + mixedsofar += read; + destptr += bytes; + mixedleft -= read; + } + + mMixerSamplesLeft = mixedleft; + } + + if (bytesread) + { + *bytesread = sizebytes; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecIT::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + if (postype == FMOD_TIMEUNIT_MODORDER) + { + play(); + mNextOrder = mOrder = position; + + return FMOD_OK; + } + else if (postype == FMOD_TIMEUNIT_PCM) + { + bool mRestarted = false; + + if (position == mPCMOffset) + { + return FMOD_OK; + } + + /* + Want to seek backwards, start from the start. + */ + if (position < mPCMOffset) + { + play(); + mRestarted = true; + } + + while (mPCMOffset < position) + { + update(true); + } + + if (mRestarted) + { + bool oldplaying = mPlaying; + bool oldfinished = mFinished; + + stop(); + + mPlaying = oldplaying; + mFinished = oldfinished; + } + + return FMOD_OK; + } + + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecIT::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecIT *cit = (CodecIT *)codec; + + return cit->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecIT::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecIT *cit = (CodecIT *)codec; + + return cit->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecIT::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecIT *cit = (CodecIT *)codec; + + return cit->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecIT::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecIT *cit = (CodecIT *)codec; + + return cit->setPositionInternal(subsound, position, postype); +} + +} + +#endif + + diff --git a/src/fmod_codec_it.h b/src/fmod_codec_it.h new file mode 100755 index 0000000..7792973 --- /dev/null +++ b/src/fmod_codec_it.h @@ -0,0 +1,200 @@ +#ifndef _FMOD_CODEC_IT_H +#define _FMOD_CODEC_IT_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_IT + +#include "fmod_music.h" + +namespace FMOD +{ + class DSPI; + class ChannelPool; + + enum FMUSIC_ITCOMMANDS + { + FMUSIC_IT_SETSPEED = 1, // always + FMUSIC_IT_PATTERNJUMP, // always + FMUSIC_IT_PATTERNBREAK, // always + FMUSIC_IT_VOLUMESLIDE, // always set, foreground channel only execute + FMUSIC_IT_PORTADOWN, // always set, foreground channel only execute + FMUSIC_IT_PORTAUP, // always set, foreground channel only execute + FMUSIC_IT_PORTATO, // always set, foreground channel only execute + FMUSIC_IT_VIBRATO, // always set, foreground channel only execute + FMUSIC_IT_TREMOR, // always set, foreground channel only execute + FMUSIC_IT_ARPEGGIO, // always set, foreground channel only execute + FMUSIC_IT_VIBRATOVOLSLIDE, // always set, foreground channel only execute + FMUSIC_IT_PORTATOVOLSLIDE, // always set, foreground channel only execute + FMUSIC_IT_SETCHANNELVOLUME, // always + FMUSIC_IT_CHANNELVOLSLIDE, // always + FMUSIC_IT_SETSAMPLEOFFSET, // always set, foreground channel only execute + FMUSIC_IT_PANSLIDE, // always set, foreground channel only execute + FMUSIC_IT_RETRIGVOLSLIDE, // always set, foreground channel only execute + FMUSIC_IT_TREMOLO, // always set, foreground channel only execute + FMUSIC_IT_SPECIAL, // + FMUSIC_IT_SETTEMPO, // always + FMUSIC_IT_FINEVIBRATO, // always set, foreground channel only execute + FMUSIC_IT_SETGLOBALVOLUME, // always + FMUSIC_IT_GLOBALVOLUMESLIDE, // always + FMUSIC_IT_SETPAN, // foreground channel only + FMUSIC_IT_PANBRELLO, // foreground channel only + FMUSIC_IT_MIDIMACROS // + }; + + enum FMUSIC_ITCOMMANDS_SPECIAL + { + FMUSIC_IT_S0, // + FMUSIC_IT_S1, // + FMUSIC_IT_S2, // + FMUSIC_IT_SETVIBRATOWAVE, // + FMUSIC_IT_SETTREMOLOWAVE, // + FMUSIC_IT_SETPANBRELLOWAVE, // + FMUSIC_IT_PATTERNDELAYTICKS, // + FMUSIC_IT_S7SPECIAL, // + FMUSIC_IT_SETPANPOSITION16, // + FMUSIC_IT_S9SPECIAL, // + FMUSIC_IT_SETHIGHOFFSET, // + FMUSIC_IT_PATTERNLOOP, // + FMUSIC_IT_NOTECUT, // + FMUSIC_IT_NOTEDELAY, // + FMUSIC_IT_PATTERNDELAY, // + FMUSIC_IT_SF + }; + + enum FMUSIC_ITCOMMANDS_S7SPECIAL + { + FMUSIC_IT_PASTNOTECUT, // + FMUSIC_IT_PASTNOTEOFF, // + FMUSIC_IT_PASTNOTEFADE, // + FMUSIC_IT_SETNNACUT, // + FMUSIC_IT_SETNNACONTINUE, // + FMUSIC_IT_SETNNANOTEOFF, // + FMUSIC_IT_SETNNANOTEFADE, // + FMUSIC_IT_VOLENVELOPEOFF, // + FMUSIC_IT_VOLENVELOPEON, // + FMUSIC_IT_S79, + FMUSIC_IT_S7A, + FMUSIC_IT_S7B, + FMUSIC_IT_S7C, + FMUSIC_IT_S7D, + FMUSIC_IT_S7E, + FMUSIC_IT_S7F + }; + + enum FMUSIC_ITCOMMANDS_S9SPECIAL + { + FMUSIC_IT_DISABLESURROUND, + FMUSIC_IT_ENABLESURROUND, + FMUSIC_IT_S92, + FMUSIC_IT_S93, + FMUSIC_IT_S94, + FMUSIC_IT_S95, + FMUSIC_IT_S96, + FMUSIC_IT_S97, + FMUSIC_IT_DISABLEREVERB, + FMUSIC_IT_FORCEREVERB, + FMUSIC_IT_MONOSURROUND, + FMUSIC_IT_QUADSURROUND, + FMUSIC_IT_GLOBALFILTER, + FMUSIC_IT_LOCALFILTER, + FMUSIC_IT_PLAYFORWARD, + FMUSIC_IT_PLAYBACKWARD + }; + + #define FMUSIC_ITFLAGS_NONE 0x0 + #define FMUSIC_ITFLAGS_MONOSTEREO 0x1 + #define FMUSIC_ITFLAGS_VOL0OPTIMIZATIONS 0x2 + #define FMUSIC_ITFLAGS_INSTRUMENTS 0x4 + #define FMUSIC_ITFLAGS_LINEARFREQUENCY 0x8 + #define FMUSIC_ITFLAGS_OLD_IT_EFFECTS 0x10 + #define FMUSIC_ITFLAGS_EFFECT_G 0x20 + #define FMUSIC_ITFLAGS_EMBEDMIDICFG 0x80 + #define FMUSIC_ITFLAGS_EXTENDFILTERRANGE 0x1000 + + #define MAX_MIXPLUGINS 50 + + typedef struct MODMIDICFG + { + char szMidiGlb[9*32]; + char szMidiSFXExt[16*32]; + char szMidiZXXExt[128*32]; + } MODMIDICFG, *LPMODMIDICFG; + + const int IT_MAXROWS = 256; + const int IT_MAXSAMPLES = 256; + + class CodecIT : public MusicSong + { + private: + + MusicSample **mSample; + MusicSample mSampleMem[IT_MAXSAMPLES]; + signed char *mPatternPtr; + unsigned int *mSrcBuffer; /* source buffer */ + unsigned int *mSrcPos; /* actual reading position */ + unsigned char mSrcRemBits; /* bits remaining in actual read dword */ + int mNumRealChannels; + SNDMIXPLUGIN *mMixPlugin[MAX_MIXPLUGINS]; + unsigned int mChannelPlugin[64]; + DSPI *mDSPFinalHead; /* Extra unit that comes before mDSPUnit for submixing purposes. */ + DSPI *mDSPEffectHead; + + FMOD_RESULT calculateLength(); + + FMOD_RESULT readBits(unsigned char b, unsigned int *result); /* reads b bits from the stream */ + FMOD_RESULT readBlock(signed char **buff); /* gets block of compressed data from file */ + FMOD_RESULT freeBlock(); /* frees that block again */ + FMOD_RESULT decompress8(void **src, void *dst, int len, bool it215, int channels); + FMOD_RESULT decompress16(void **src, void *dst, int len, bool it215, int channels); + FMOD_RESULT cutOffToFrequency(unsigned int nCutOff, int flt_modifier, float *freq); + FMOD_RESULT setupChannelFilter(MusicVirtualChannel *vcptr, bool bReset, int flt_modifier); + + FMOD_RESULT updateRow(bool audible); + FMOD_RESULT updateEffects(); + FMOD_RESULT update(bool audible); + + FMOD_RESULT unpackRow(); + FMOD_RESULT processEnvelope(MusicEnvelopeState *env, MusicVirtualChannel *vcptr, int Inumpoints, MusicEnvelopeNode *v, int type, int loopstart, int loopend, int susloopstart, int susloopend, unsigned char control); + FMOD_RESULT processPitchEnvelope(MusicVirtualChannel *vcptr, MusicInstrument *iptr, int note); + FMOD_RESULT sampleVibrato(MusicVirtualChannel *vcptr); + FMOD_RESULT play(bool fromopen = false); + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + + }; + + class MusicChannelIT : public MusicChannel + { + public: + CodecIT *mModule; + + FMOD_RESULT volumeSlide(); + FMOD_RESULT panSlide(); + FMOD_RESULT portamento(); + FMOD_RESULT vibrato(); + FMOD_RESULT tremolo(); + FMOD_RESULT fineVibrato(); + FMOD_RESULT panbrello(); + FMOD_RESULT processVolumeByte(MusicNote *current, bool newrow); + }; + +} + +#endif /* FMOD_SUPPORT_IT */ + +#endif + + diff --git a/src/fmod_codec_midi.cpp b/src/fmod_codec_midi.cpp new file mode 100755 index 0000000..284d03d --- /dev/null +++ b/src/fmod_codec_midi.cpp @@ -0,0 +1,3908 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MIDI + +#include "fmod.h" + +#include "fmod_channel_software.h" +#include "fmod_codec_dls.h" +#include "fmod_codec_midi.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_file.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_string.h" + +#include +#include +#include + +#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_MAC) + #include +#endif + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX midicodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecMIDI::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +CodecMIDIDLSCache CodecMIDI::gDLSCacheHead; + +const char *note[128] = +{ + "C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0", + "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1", + "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", + "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", + "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", + "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", + "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6", + "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7", + "C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8", + "C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9", "G#9", "A9", "A#9", "B9" + "C10", "C#10", "D10", "D#10", "E10", "F10", "F#10", "G10", "G#10" +}; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecMIDI::getDescriptionEx() +{ + FMOD_memset(&midicodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + midicodec.name = "FMOD MIDI Codec"; + midicodec.version = 0x00010100; + midicodec.timeunits = FMOD_TIMEUNIT_PCM; + midicodec.defaultasstream = 1; + midicodec.open = &CodecMIDI::openCallback; + midicodec.close = &CodecMIDI::closeCallback; + midicodec.read = &CodecMIDI::readCallback; + midicodec.setposition = &CodecMIDI::setPositionCallback; + + midicodec.getmusicnumchannels = &CodecMIDI::getMusicNumChannelsCallback; + midicodec.setmusicchannelvolume = &CodecMIDI::setMusicChannelVolumeCallback; + midicodec.getmusicchannelvolume = &CodecMIDI::getMusicChannelVolumeCallback; + + midicodec.mType = FMOD_SOUND_TYPE_MIDI; + midicodec.mSize = sizeof(CodecMIDI); + + return &midicodec; +} + + +/* + ========================================================================================== + + CODEC MIDI SUBCHANNEL CLASS + + ========================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::displayArticulators() +{ + int blockcount; + + for (blockcount = 0; blockcount < mNumArticulators; blockcount++) + { + const char *src = "?", *control = "?", *dest = "?", *trans = "?"; + DLS_CONNECTIONBLOCK *block = &mArticulator[blockcount]; + + switch (block->usSource) + { + case CONN_SRC_NONE: + { + src = "SRC_NONE"; + break; + } + case CONN_SRC_LFO: + { + src = "SRC_LFO"; + break; + } + case CONN_SRC_KEYONVELOCITY: + { + src = "SRC_KEYONVELOCITY"; + break; + } + case CONN_SRC_KEYNUMBER: + { + src = "SRC_KEYNUMBER"; + break; + } + case CONN_SRC_EG1: + { + src = "SRC_EG1"; + break; + } + case CONN_SRC_EG2: + { + src = "SRC_EG2"; + break; + } + case CONN_SRC_PITCHWHEEL: + { + src = "SRC_PITCHWHEEL"; + break; + } + case CONN_SRC_CC1: + { + src = "SRC_CC1"; + break; + } + case CONN_SRC_CC7: + { + src = "SRC_CC7"; + break; + } + case CONN_SRC_CC10: + { + src = "SRC_CC10"; + break; + } + case CONN_SRC_CC11: + { + src = "SRC_CC11"; + break; + } + } + + + switch (block->usControl) + { + case CONN_SRC_NONE: + { + control = "SRC_NONE"; + break; + } + case CONN_SRC_LFO: + { + control = "SRC_LFO"; + break; + } + case CONN_SRC_KEYONVELOCITY: + { + control = "SRC_KEYONVELOCITY"; + break; + } + case CONN_SRC_KEYNUMBER: + { + control = "SRC_KEYNUMBER"; + break; + } + case CONN_SRC_EG1: + { + control = "SRC_EG1"; + break; + } + case CONN_SRC_EG2: + { + control = "SRC_EG2"; + break; + } + case CONN_SRC_PITCHWHEEL: + { + control = "SRC_PITCHWHEEL"; + break; + } + case CONN_SRC_CC1: + { + control = "SRC_CC1"; + break; + } + case CONN_SRC_CC7: + { + control = "SRC_CC7"; + break; + } + case CONN_SRC_CC10: + { + control = "SRC_CC10"; + break; + } + case CONN_SRC_CC11: + { + control = "SRC_CC11"; + break; + } + } + + switch (block->usDestination) + { + case CONN_DST_NONE: + { + dest = "DST_NONE"; + break; + } + case CONN_DST_ATTENUATION: + { + dest = "DST_ATTENUATION"; + break; + } + case CONN_DST_RESERVED: + { + dest = "DST_RESERVED"; + break; + } + case CONN_DST_PITCH: + { + dest = "DST_PITCH"; + break; + } + case CONN_DST_PAN: + { + dest = "DST_PAN"; + break; + } + case CONN_DST_LFO_FREQUENCY: + { + dest = "DST_LFO_FREQUENCY"; + break; + } + case CONN_DST_LFO_STARTDELAY: + { + dest = "DST_LFO_STARTDELAY"; + break; + } + case CONN_DST_EG1_ATTACKTIME: + { + dest = "DST_EG1_ATTACKTIME"; + break; + } + case CONN_DST_EG1_DECAYTIME: + { + dest = "DST_EG1_DECAYTIME"; + break; + } + case CONN_DST_EG1_RESERVED: + { + dest = "DST_EG1_RESERVED"; + break; + } + case CONN_DST_EG1_RELEASETIME: + { + dest = "DST_EG1_RELEASETIME"; + break; + } + case CONN_DST_EG1_SUSTAINLEVEL: + { + dest = "DST_EG1_SUSTAINLEVEL"; + break; + } + case CONN_DST_EG2_ATTACKTIME: + { + dest = "DST_EG2_ATTACKTIME"; + break; + } + case CONN_DST_EG2_DECAYTIME: + { + dest = "DST_EG2_DECAYTIME"; + break; + } + case CONN_DST_EG2_RESERVED: + { + dest = "DST_EG2_RESERVED"; + break; + } + case CONN_DST_EG2_RELEASETIME: + { + dest = "DST_EG2_RELEASETIME"; + break; + } + case CONN_DST_EG2_SUSTAINLEVEL: + { + dest = "DST_EG2_SUSTAINLEVEL"; + break; + } + }; + + if (block->usTransform == CONN_TRN_CONCAVE) + { + trans = "TRN_CONCAVE"; + } + else + { + trans = "TRN_NONE"; + } + + printf("%2d | %-13s | %-8s | %-20s | %08x | %-10s\n", blockcount, src, control, dest, block->lScale, trans); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::findArticulator(int src, int dest) +{ + int blockcount; + + for (blockcount = 0; blockcount < mNumArticulators; blockcount++) + { + DLS_CONNECTIONBLOCK *block = &mArticulator[blockcount]; + + if (block->usSource == src && block->usDestination == dest) + { + return FMOD_OK; + } + } + + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::articulateDest(CONN_SRC_FLAGS srcflags, int dest, int *usDestination) +{ + int blockcount; + bool found = false; + + *usDestination = 0; + + for (blockcount = 0; blockcount < mNumArticulators; blockcount++) + { + DLS_CONNECTIONBLOCK *block = &mArticulator[blockcount]; + float usSource = 1.0f; + float usControl = 1.0f; + + if (block->usDestination == dest) + { + switch (block->usSource) + { + case CONN_SRC_NONE: + { + if (!(srcflags & CONN_SRC_FLAG_NONE)) + { + continue; + } + usSource = 1.0f; + break; + } + case CONN_SRC_LFO: + { + if (!(srcflags & CONN_SRC_FLAG_LFO)) + { + continue; + } + break; + } + case CONN_SRC_KEYONVELOCITY: + { + usSource = (float)mKeyOnVelocity / 128.0f; + if (!(srcflags & CONN_SRC_FLAG_KEYONVELOCITY)) + { + continue; + } + break; + } + case CONN_SRC_KEYNUMBER: + { + usSource = (float)mKeyOnKey / 128.0f; + if (!(srcflags & CONN_SRC_FLAG_KEYNUMBER)) + { + continue; + } + break; + } + case CONN_SRC_EG1: + { + if (!(srcflags & CONN_SRC_FLAG_EG1)) + { + continue; + } + break; + } + case CONN_SRC_EG2: + { + if (!(srcflags & CONN_SRC_FLAG_EG2)) + { + continue; + } + usSource = 1.0f; + break; + } + case CONN_SRC_PITCHWHEEL: + { + if (!(srcflags & CONN_SRC_FLAG_PITCHWHEEL)) + { + continue; + } + break; + } + case CONN_SRC_CC1: + { + if (!(srcflags & CONN_SRC_FLAG_MODWHEEL)) + { + continue; + } + break; + } + case CONN_SRC_CC7: + { + if (!(srcflags & CONN_SRC_FLAG_CHANNELVOL)) + { + continue; + } + break; + } + case CONN_SRC_CC10: + { + if (!(srcflags & CONN_SRC_FLAG_PAN)) + { + continue; + } + break; + } + case CONN_SRC_CC11: + { + if (!(srcflags & CONN_SRC_FLAG_EXPRESSION)) + { + continue; + } + break; + } + }; + + + switch (block->usControl) + { + case CONN_SRC_NONE: + { + usControl = 1.0f; + break; + } + case CONN_SRC_LFO: + { + usControl = 1.0f; + break; + } + case CONN_SRC_KEYONVELOCITY: + { + usControl = (float)mKeyOnVelocity / 128.0f; + break; + } + case CONN_SRC_KEYNUMBER: + { + usControl = (float)mKeyOnKey / 128.0f; + break; + } + case CONN_SRC_EG1: + { + break; + } + case CONN_SRC_EG2: + { + break; + } + case CONN_SRC_PITCHWHEEL: + { + break; + } + case CONN_SRC_CC1: + { + usControl = (float)mParent->mModWheel / 128.0f; + break; + } + case CONN_SRC_CC7: + { + usControl = (float)mParent->mVolume / 128.0f; + break; + } + case CONN_SRC_CC10: + { + usControl = (float)mParent->mPan / 128.0f; + break; + } + case CONN_SRC_CC11: + { + usControl = (float)mParent->mExpression / 128.0f; + break; + } + } + + if (block->usTransform == CONN_TRN_CONCAVE) + { + // *usDestination = *usDestination + (int)usTransform(usSource * (usControl * (float)block->lScale)) + } + else + { + *usDestination = *usDestination + (int)(usSource * (usControl * (float)block->lScale)); + } + + found = true; + } + } + + if (found) + { + return FMOD_OK; + } + + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#define USETAB + +#ifdef USETAB + +/* + int count = 0; + for (val = -10.0f; val < 5.5f; val += ((5.5f - -10.0f) / 128.0f)) + { + printf("%9.03ff, ", FMOD_POW(2.0f, val) * 1000.0f); + count++; + if (count && !(count % 4)) + { + printf("\n"); + } + } +*/ +#define TIMECENTSTABLENUM 128 +static float gTimeCentsTable[TIMECENTSTABLENUM] = +{ + 0.977f, 1.062f, 1.155f, 1.256f, 1.366f, 1.486f, 1.616f, 1.757f, + 1.911f, 2.079f, 2.261f, 2.459f, 2.674f, 2.908f, 3.163f, 3.439f, + 3.741f, 4.068f, 4.424f, 4.812f, 5.233f, 5.691f, 6.190f, 6.732f, + 7.321f, 7.962f, 8.659f, 9.417f, 10.242f, 11.139f, 12.114f, 13.175f, + 14.328f, 15.583f, 16.947f, 18.431f, 20.045f, 21.800f, 23.709f, 25.785f, + 28.042f, 30.498f, 33.168f, 36.072f, 39.231f, 42.666f, 46.401f, 50.464f, + 54.883f, 59.688f, 64.915f, 70.598f, 76.780f, 83.503f, 90.814f, 98.766f, + 107.414f, 116.819f, 127.047f, 138.171f, 150.270f, 163.427f, 177.737f, 193.299f, + 210.224f, 228.631f, 248.650f, 270.421f, 294.099f, 319.850f, 347.856f, 378.314f, + 411.439f, 447.464f, 486.644f, 529.254f, 575.595f, 625.993f, 680.805f, 740.415f, + 805.245f, 875.752f, 952.432f, 1035.826f, 1126.522f, 1225.159f, 1332.433f, 1449.099f, + 1575.981f, 1713.972f, 1864.046f, 2027.260f, 2204.765f, 2397.812f, 2607.763f, 2836.096f, + 3084.422f, 3354.491f, 3648.207f, 3967.640f, 4315.043f, 4692.864f, 5103.767f, 5550.648f, + 6036.658f, 6565.222f, 7140.066f, 7765.244f, 8445.161f, 9184.612f, 9988.808f, 10863.418f, +11814.609f, 12849.085f, 13974.139f, 15197.702f,16528.398f, 17975.609f, 19549.537f, 21261.276f, +23122.893f, 25147.512f, 27349.404f, 29744.092f,32348.457f, 35180.858f, 38261.261f, 41611.381f +}; + +#endif + +float CodecMIDISubChannel::getTimeCentsFromlScale(int lScale) +{ + if ((unsigned int)lScale == 0x80000000) + { + return 0; + } + +#ifdef USETAB + { + float val; + int index; + + val = (float)lScale / (1200.0f*65536.0f); + val += 10.0f; + val *= (1.0f / ((5.0f - -10.0f) / 128.0f)); + + if (val < 0) + { + val = 0; + } + if (val >= TIMECENTSTABLENUM) + { + val = TIMECENTSTABLENUM - 1; + } + + index = (int)val; + + return gTimeCentsTable[index]; + } +#else + return FMOD_POW(2.0f, lScale / (1200.0f*65536.0f) ) * 1000.0f; +#endif +} + + +FMOD_RESULT CodecMIDISubChannel::setUpArticulators() +{ + mKeyOff = false; + mMiddleC = 12800; /* */ + mLFOStartDelay = 0; + mLFOTime = 0; + mLFOFrequency = 0; + + /* + Reset volume envelope + */ + mVolumeEnvelope.mPosition = CODEC_DLS_ENVPOINT_ATTACK; + mVolumeEnvelope.mTime = 0; + mVolumeEnvelope.mSustain = 0.0f; + mVolumeEnvelope.mActive = true; + mVolumeEnvelope.mRange = 0; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mTime = 0.0f; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mSrcValue = -96.0f; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mDestValue = 0.0f; + + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mTime = 0.0f; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mSrcValue = 0.0f; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mDestValue = -96.0f; + + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mTime = 0.0f; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mSrcValue = 0.0f; + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mDestValue = -96.0f; + + /* + Reset pitch envelope + */ + mPitchEnvelope.mPosition = CODEC_DLS_ENVPOINT_ATTACK; + mPitchEnvelope.mTime = 0; + mPitchEnvelope.mSustain = 1.0f; + mPitchEnvelope.mActive = false; + mPitchEnvelope.mRange = 0; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mTime = 0.0f; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mSrcValue = 0.0f; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mDestValue = 1.0f; + + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mTime = 0.0f; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mSrcValue = 1.0f; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mDestValue = 0.0f; + + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mTime = 0.0f; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mSrcValue = 0.0f; + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mDestValue = 0.0f; + +#if 0 + displayArticulators(); +#endif + + /* + Run through articulators. + */ + { + int lScale; + + /* + LFO + */ + if (articulateDest(CONN_SRC_FLAG_LFO, CONN_DST_ATTENUATION, &lScale) == FMOD_OK) + { + mTremoloScale = -(lScale / (65536.0f * 10.0f)); + mTremoloScale = 1.0f - FMOD_POW(10.0f, mTremoloScale / 20.0f); + } + if (articulateDest(CONN_SRC_FLAG_LFO, CONN_DST_PITCH, &lScale) == FMOD_OK) + { + mVibratoScale = (float)lScale / 65536.0f; + } + if (articulateDest(CONN_SRC_FLAG_LFO, CONN_DST_PAN, &lScale) == FMOD_OK) + { + mPanbrelloScale = lScale == 0x8000000 ? -96.0f : (1.0f - (lScale / (1000.0f * 65536.0f))) * -96.0f; + } + if (articulateDest(CONN_SRC_FLAG_COMMON, CONN_DST_LFO_FREQUENCY, &lScale) == FMOD_OK) + { + mLFOFrequency = FMOD_POW(2.0f, (((float)lScale / 65536.0f) - 6900.0f) / 1200.0f) * 440.0f; + } + if (articulateDest(CONN_SRC_FLAG_COMMON, CONN_DST_LFO_STARTDELAY, &lScale) == FMOD_OK) + { + mLFOStartDelay = getTimeCentsFromlScale(lScale); + } + + /* + Volume envelope + */ + if (findArticulator(CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME) == FMOD_OK) + { + if (articulateDest(CONN_SRC_FLAG_COMMON, CONN_DST_EG1_ATTACKTIME, &lScale) == FMOD_OK) + { + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mTime = getTimeCentsFromlScale(lScale); + } + } + if (findArticulator(CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME) == FMOD_OK) + { + if (articulateDest(CONN_SRC_FLAG_COMMON, CONN_DST_EG1_DECAYTIME, &lScale) == FMOD_OK) + { + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mTime = getTimeCentsFromlScale(lScale); + } + } + if (findArticulator(CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME) == FMOD_OK) + { + if (articulateDest(CONN_SRC_FLAG_COMMON, CONN_DST_EG1_RELEASETIME, &lScale) == FMOD_OK) + { + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mTime = getTimeCentsFromlScale(lScale); + } + } + if (articulateDest(CONN_SRC_FLAG_COMMON, CONN_DST_EG1_SUSTAINLEVEL, &lScale) == FMOD_OK) + { + mVolumeEnvelope.mSustain = lScale == 0x8000000 ? -96.0f : (1.0f - (lScale / (1000.0f * 65536.0f))) * -96.0f; + } + + /* + Pitch envelope + */ + if (articulateDest(CONN_SRC_FLAG_EG2, CONN_DST_PITCH, &lScale) == FMOD_OK) + { + mPitchEnvelope.mActive = true; + mPitchEnvelope.mRange = (float)lScale / 65536.0f; + + if (findArticulator(CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME) == FMOD_OK) + { + if (articulateDest(CONN_SRC_FLAG_NONE, CONN_DST_EG2_ATTACKTIME, &lScale) == FMOD_OK) + { + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_ATTACK].mTime = getTimeCentsFromlScale(lScale); + } + } + if (findArticulator(CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME) == FMOD_OK) + { + if (articulateDest(CONN_SRC_FLAG_NONE, CONN_DST_EG2_DECAYTIME, &lScale) == FMOD_OK) + { + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_DECAY].mTime = getTimeCentsFromlScale(lScale); + } + } + if (findArticulator(CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME) == FMOD_OK) + { + if (articulateDest(CONN_SRC_FLAG_NONE, CONN_DST_EG2_RELEASETIME, &lScale) == FMOD_OK) + { + mPitchEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mTime = getTimeCentsFromlScale(lScale); + } + } + if (articulateDest(CONN_SRC_FLAG_NONE, CONN_DST_EG2_SUSTAINLEVEL, &lScale) == FMOD_OK) + { + mPitchEnvelope.mSustain = lScale == 0x8000000 ? 0 : lScale / (1000.0f * 65536.0f); + } + } + + + /* + Pan envelope? + */ + if (articulateDest(CONN_SRC_FLAG_EG2, CONN_DST_PAN, &lScale) == FMOD_OK) + { + lScale = lScale; + } + } + + /* + This helps remove clicks, but might not be a good idea. + */ + if (mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mTime < 50.0f) + { + mVolumeEnvelope.mPoint[CODEC_DLS_ENVPOINT_RELEASE].mTime = 50.0f; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::stop() +{ + mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + mInstrument = 0; + mKeyOff = false; + mCurrentNote = -1; + removeNode(); + addAfter(&mMIDI->mChannelFreeListHead); + + return FMOD_OK; +} + +#if 1 +/* +float val; +int count = 0; + +for (val = -96.0f; val <= 0; val += 0.5f) +{ + printf("%.04f ", FMOD_POW(10.0f, val/20.0f)); + if (count && !(count % 8)) + { + printf("\n"); + } + count++; +} +eg1=eg1; +*/ + +static float gDBToLinearTable[(96*2) + 1] = +{ + 0.0000f, // -96db + 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, + 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, + 0.0000f, 0.0000f, 0.0000f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, + 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, + 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0001f, 0.0002f, + 0.0002f, 0.0002f, 0.0002f, 0.0002f, 0.0002f, 0.0002f, 0.0002f, 0.0003f, + 0.0003f, 0.0003f, 0.0003f, 0.0003f, 0.0003f, 0.0004f, 0.0004f, 0.0004f, + 0.0004f, 0.0004f, 0.0005f, 0.0005f, 0.0005f, 0.0006f, 0.0006f, 0.0006f, + 0.0007f, 0.0007f, 0.0007f, 0.0008f, 0.0008f, 0.0009f, 0.0009f, 0.0010f, + 0.0011f, 0.0011f, 0.0012f, 0.0013f, 0.0013f, 0.0014f, 0.0015f, 0.0016f, + 0.0017f, 0.0018f, 0.0019f, 0.0020f, 0.0021f, 0.0022f, 0.0024f, 0.0025f, + 0.0027f, 0.0028f, 0.0030f, 0.0032f, 0.0033f, 0.0035f, 0.0038f, 0.0040f, + 0.0042f, 0.0045f, 0.0047f, 0.0050f, 0.0053f, 0.0056f, 0.0060f, 0.0063f, + 0.0067f, 0.0071f, 0.0075f, 0.0079f, 0.0084f, 0.0089f, 0.0094f, 0.0100f, + 0.0106f, 0.0112f, 0.0119f, 0.0126f, 0.0133f, 0.0141f, 0.0150f, 0.0158f, + 0.0168f, 0.0178f, 0.0188f, 0.0200f, 0.0211f, 0.0224f, 0.0237f, 0.0251f, + 0.0266f, 0.0282f, 0.0299f, 0.0316f, 0.0335f, 0.0355f, 0.0376f, 0.0398f, + 0.0422f, 0.0447f, 0.0473f, 0.0501f, 0.0531f, 0.0562f, 0.0596f, 0.0631f, + 0.0668f, 0.0708f, 0.0750f, 0.0794f, 0.0841f, 0.0891f, 0.0944f, 0.1000f, + 0.1059f, 0.1122f, 0.1189f, 0.1259f, 0.1334f, 0.1413f, 0.1496f, 0.1585f, + 0.1679f, 0.1778f, 0.1884f, 0.1995f, 0.2113f, 0.2239f, 0.2371f, 0.2512f, + 0.2661f, 0.2818f, 0.2985f, 0.3162f, 0.3350f, 0.3548f, 0.3758f, 0.3981f, + 0.4217f, 0.4467f, 0.4732f, 0.5012f, 0.5309f, 0.5623f, 0.5957f, 0.6310f, + 0.6683f, 0.7079f, 0.7499f, 0.7943f, 0.8414f, 0.8913f, 0.9441f, 1.0000f // 0db +}; + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::updateVolume() +{ + float eg1 = 1.0f, velocity, channelvol, expression, attenuation, tremolo = 1.0f; + + if (mVolumeEnvelope.mActive) + { + /* + Handle a pre-mature keyoff. + */ + if ((mKeyOff && !mParent->mDamperPedal) && mVolumeEnvelope.mPosition != CODEC_DLS_ENVPOINT_RELEASE) + { + CodecDLSEnvelopePoint *point; + float src; + + point = &mVolumeEnvelope.mPoint[mVolumeEnvelope.mPosition]; + + if (point->mTime > 0 && mVolumeEnvelope.mTime <= point->mTime) + { + src = point->mSrcValue + (((point->mDestValue - point->mSrcValue) / point->mTime) * mVolumeEnvelope.mTime); + } + else + { + src = point->mSrcValue; + } + + if (mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_ATTACK) + { + /* + Turn the linear value into dB so it scales properly with the release segment which is logarithmic. + */ + if (src > -96.0f) + { + src = 1.0f - (src / -96.0f); + src = log10f(src) * 20.0f; + } + } + if (mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_DECAY && src < mVolumeEnvelope.mSustain) + { + src = mVolumeEnvelope.mSustain; + } + + mVolumeEnvelope.mPosition = CODEC_DLS_ENVPOINT_RELEASE; + point = &mVolumeEnvelope.mPoint[mVolumeEnvelope.mPosition]; + + if (point->mDestValue - point->mSrcValue != 0 && point->mTime != 0) + { + // Determine the time in release that gives the same volume as the original segment (avoids a click) + mVolumeEnvelope.mTime = (src - point->mSrcValue) / ((point->mDestValue - point->mSrcValue) / point->mTime); + } + else + { + // The release segment doesn't have a ramp, jump to initial volume for release segment + mVolumeEnvelope.mTime = 0; + } + } + + /* + Increment through A/D/S/R stages if nescessary. + */ + while (mVolumeEnvelope.mTime >= mVolumeEnvelope.mPoint[mVolumeEnvelope.mPosition].mTime && mVolumeEnvelope.mPosition < CODEC_DLS_ENVPOINT_MAX) + { + if (mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_DECAY && !(mKeyOff && !mParent->mDamperPedal)) + { + mVolumeEnvelope.mTime = mVolumeEnvelope.mPoint[mVolumeEnvelope.mPosition].mTime; + break; + } + else + { + mVolumeEnvelope.mTime -= mVolumeEnvelope.mPoint[mVolumeEnvelope.mPosition].mTime; + mVolumeEnvelope.mPosition = (CODEC_DLS_ENVPOINT)(mVolumeEnvelope.mPosition + 1); + } + } + + /* + If the release is completed, stop the sound. + */ + if (mVolumeEnvelope.mPosition >= CODEC_DLS_ENVPOINT_MAX) + { + return stop(); + } + /* + Otherwise calculate the nescessary volume. + */ + else + { + CodecDLSEnvelopePoint *point = &mVolumeEnvelope.mPoint[mVolumeEnvelope.mPosition]; + + if (point->mTime > 0) + { + eg1 = point->mSrcValue + (((point->mDestValue - point->mSrcValue) / point->mTime) * mVolumeEnvelope.mTime); + } + else + { + eg1 = point->mSrcValue; + } + + if (mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_DECAY && eg1 < mVolumeEnvelope.mSustain) + { + eg1 = mVolumeEnvelope.mSustain; + } + + /* + Attack segment is linear. + */ + if (mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_ATTACK) + { + eg1 = 1.0f - (eg1 / -96.0f); + } + else + { + #ifdef USETAB + + eg1 = gDBToLinearTable[192 - (int)(eg1 * -2.0f)]; + + #else + + eg1 = FMOD_POW(10.0f, eg1/20.0f); + + #endif + } + } + } + + velocity = (float)(mKeyOnVelocity * mKeyOnVelocity ) / (127.0f * 127.0f); + channelvol = (float)(mParent->mVolume * mParent->mVolume ) / (127.0f * 127.0f); + expression = (float)(mParent->mExpression * mParent->mExpression ) / (127.0f * 127.0f); + + if (mLFOTime >= mLFOStartDelay) + { + tremolo = 1.0f + (FMOD_SIN((mLFOTime - mLFOStartDelay) / 1000.0f * FMOD_PI2 * mLFOFrequency) * mTremoloScale); + if (tremolo < 0.0f) + { + tremolo = 0.0f; + } + if (tremolo > 1.0f) + { + tremolo = 1.0f; + } + } + + attenuation = eg1 * velocity * channelvol * expression * mSampleAttenuation * tremolo; + + if (mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_RELEASE && attenuation < (1.0f / 1024.0f)) + { + return stop(); + } + + mChannel.setVolume(attenuation * mParent->mMasterVolume); + + /* + Some sounds are one-shot (namely drums), the mode is set as non-looping by + the DLS so ignore the envelope and stop the sub channel. By convention the + envelope will be attack = 0, decay = max, release = max for one-shots. + */ + { + bool isPlaying = true; + + mChannel.isPlaying(&isPlaying); + if (isPlaying == false) + { + return stop(); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::updatePitch() +{ + float eg2 = 0; + + if (mPitchEnvelope.mActive) + { + /* + Handle a pre-mature keyoff. + */ + if ((mKeyOff && !mParent->mDamperPedal) && mPitchEnvelope.mPosition != CODEC_DLS_ENVPOINT_RELEASE) + { + CodecDLSEnvelopePoint *point; + float src; + + point = &mPitchEnvelope.mPoint[mPitchEnvelope.mPosition]; + + if (point->mTime > 0 && mPitchEnvelope.mTime <= point->mTime) + { + src = point->mSrcValue + (((point->mDestValue - point->mSrcValue) / point->mTime) * mPitchEnvelope.mTime); + } + else + { + src = point->mSrcValue; + } + + if (mPitchEnvelope.mPosition == CODEC_DLS_ENVPOINT_DECAY && src < mPitchEnvelope.mSustain) + { + src = mPitchEnvelope.mSustain; + } + + mPitchEnvelope.mPosition = CODEC_DLS_ENVPOINT_RELEASE; + point = &mPitchEnvelope.mPoint[mPitchEnvelope.mPosition]; + + if (point->mDestValue - point->mSrcValue != 0 && point->mTime != 0) + { + // Determine the time in release that gives the same volume as the original segment (avoids a click) + mPitchEnvelope.mTime = (src - point->mSrcValue) / ((point->mDestValue - point->mSrcValue) / point->mTime); + } + else + { + // The release segment doesn't have a ramp, jump to initial volume for release segment + mPitchEnvelope.mTime = 0; + } + } + + /* + Increment through A/D/S/R stages if nescessary. + */ + while (mPitchEnvelope.mTime >= mPitchEnvelope.mPoint[mPitchEnvelope.mPosition].mTime && mPitchEnvelope.mPosition < CODEC_DLS_ENVPOINT_MAX) + { + if (mPitchEnvelope.mPosition == CODEC_DLS_ENVPOINT_DECAY && mPitchEnvelope.mSustain > 0 && !(mKeyOff && !mParent->mDamperPedal)) + { + mPitchEnvelope.mTime = mPitchEnvelope.mPoint[mPitchEnvelope.mPosition].mTime; + break; + } + else + { + mPitchEnvelope.mTime -= mPitchEnvelope.mPoint[mPitchEnvelope.mPosition].mTime; + mPitchEnvelope.mPosition = (CODEC_DLS_ENVPOINT)(mPitchEnvelope.mPosition + 1); + } + } + + if (mPitchEnvelope.mPosition >= CODEC_DLS_ENVPOINT_MAX) + { + eg2 = 0; + mPitchEnvelope.mActive = false; + } + else + { + CodecDLSEnvelopePoint *point = &mPitchEnvelope.mPoint[mPitchEnvelope.mPosition]; + + if (point->mTime > 0) + { + eg2 = point->mSrcValue + (((point->mDestValue - point->mSrcValue) / point->mTime) * mPitchEnvelope.mTime); + } + else + { + eg2 = point->mSrcValue; + } + + if (mPitchEnvelope.mPosition == CODEC_DLS_ENVPOINT_DECAY && eg2 < mPitchEnvelope.mSustain) + { + eg2 = mPitchEnvelope.mSustain; + } + + eg2 *= mPitchEnvelope.mRange; + } + } + + /* + Calculate frequency based on envelope, pitch wheel, key number, fine tune and unity. + */ + { + float pw, kn, ft, unity, freq, pitch, vibrato, deffreq; + float sensitivity = mParent->mPitchBendSensitivity / 256.0f * 100.0f; + + pw = sensitivity * ((float)(mParent->mPitchBend) / 8192.0f); /* Pitch wheel */ + kn = (float)mMiddleC * (float)mKeyOnKey / 128.0f; /* Key number */ + ft = (float)mFineTune; /* Fine tune */ + unity = (float)mUnityNote * 100.0f; /* Unity */ + + if (mLFOTime >= mLFOStartDelay) + { + vibrato = FMOD_SIN((mLFOTime - mLFOStartDelay) / 1000.0f * FMOD_PI2 * mLFOFrequency) * mVibratoScale; + } + else + { + vibrato = 0; + } + + pitch = eg2 + pw + kn + ft - unity + vibrato; + + freq = FMOD_POW(2.0f, (pitch / 1200)); + + mSound->getDefaults(&deffreq, 0, 0, 0); + + freq *= deffreq; + + //printf("freq %s : %s = %d\n", mInstrument->mName, note[mKeyOnKey], (int)freq); + + mChannel.setFrequency(freq); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDISubChannel::updatePan() +{ + mChannel.setPan(((float)mParent->mPan / 64.0f) - 1.0f); + + return FMOD_OK; +} + +/* + ========================================================================================== + + CODEC MIDI CHANNEL CLASS + + ========================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDIChannel::getSound(int key, SoundI **sound, CodecDLSInstrument **instrument, int *unitynote, int *finetune, int *sampleattenuation, bool *duplicateallowed, int *keygroup, int *numarticulators, DLS_CONNECTIONBLOCK **articulators) +{ + CodecDLS *dls = mTrack->mMIDI->mDLS; + + for (int count = 0; count < dls->mNumInstruments; count++) + { + CodecDLSInstrument *inst = &dls->mInstrument[count]; + + if (inst->mHeader.Locale.ulBank == mBank && inst->mHeader.Locale.ulInstrument == mProgram) + { + FMOD_RESULT result = FMOD_OK; + unsigned int region = 0; + int sampleid = -1; + + *instrument = inst; + +/*MGB FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDIChannel::getSound", "Key = %s\n", note[key])); + for (region = 0; region < inst->mHeader.cRegions; region++) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDIChannel::getSound", "%s (%d): Region %d: low %s to high %s\n", inst->mName, mIndex, region, note[inst->mRegion[region].mRegionHeader.RangeKey.usLow], note[inst->mRegion[region].mRegionHeader.RangeKey.usHigh])); + }*/ + + for (region = 0; region < inst->mHeader.cRegions; region++) + { + if (key >= inst->mRegion[region].mRegionHeader.RangeKey.usLow && key <= inst->mRegion[region].mRegionHeader.RangeKey.usHigh) + { + sampleid = inst->mRegion[region].mWaveLink.ulTableIndex; + + // If present, use the wave sample in the region + if (inst->mRegion[region].mWaveSample.cbSize != 0) + { + *unitynote = inst->mRegion[region].mWaveSample.usUnityNote; + *finetune = inst->mRegion[region].mWaveSample.sFineTune; + *sampleattenuation = inst->mRegion[region].mWaveSample.lAttenuation; + } + // Otherwise, if present, use the wave sample referenced + else if (sampleid >= 0 && sampleid < dls->mNumSamples) + { + *unitynote = dls->mSample[sampleid].mWaveSample.usUnityNote; + *finetune = dls->mSample[sampleid].mWaveSample.sFineTune; + *sampleattenuation = dls->mSample[sampleid].mWaveSample.lAttenuation; + } + // Otherwise, use the default + else + { + *unitynote = 60; // Middle C + *finetune = 0; + *sampleattenuation = 0; + } + + *keygroup = inst->mRegion[region].mRegionHeader.usKeyGroup; + *numarticulators = inst->mRegion[region].mNumConnectionBlocks; + *articulators = inst->mRegion[region].mConnectionBlock; + + if (inst->mRegion[region].mRegionHeader.fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE) + { + // *duplicateallowed = true; /* What the hell? Dont support this? The only song that does use it i've seen sounds wrong when trying to allow each duplicate to play. */ + } + + break; + } + } + + if (sampleid == -1) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDIChannel::getSound", "Cannot find sample for instrument %s (%d): Key %s (%d)\n", inst->mName, mIndex, note[key], key)); + return FMOD_ERR_FILE_BAD; + } + + result = mTrack->mMIDI->mDLSSound->getSubSound(sampleid, sound); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDIChannel::getSound", "Cannot find sample (%d) for instrument %s (%d): Key %s (%d)\n", sampleid, inst->mName, mIndex, note[key], key)); + return result; + } + + if (mTrack->mMIDI->mSampleInclusionList && !*sound) + { + mTrack->mMIDI->mSampleInclusionList[sampleid] = true; /* This is for the loading stage when it scans the midi to see what samples should be actually loaded from disk. */ + } + + if (inst->mNumConnectionBlocks && inst->mConnectionBlock) + { + *numarticulators = inst->mNumConnectionBlocks; + *articulators = inst->mConnectionBlock; + } + +//MGB FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDIChannel::getSound", "%s (%d): Using region %d, unity %s, fine tune %d, sample '%s'\n\n", inst->mName, mIndex, region, note[*unitynote], *finetune, dls->mSample[sampleid].mName)); + + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDIChannel::process(unsigned char event, bool reuse, unsigned char runningdata, bool calledfromopen) +{ + FMOD_RESULT result; + CodecMIDISubChannel *current; + int bankchange = false; + + if (mIndex == 10 && mBank != MIDI_F_INSTRUMENT_DRUMS) + { + mBank = MIDI_F_INSTRUMENT_DRUMS; + } + + switch (event & 0xF0) + { + case MIDI_VOICE_NOTEOFF: + { + unsigned char keyoffkey; + + if (reuse) + { + keyoffkey = runningdata; + } + else + { + result = mTrack->readByte(&keyoffkey); + if (result != FMOD_OK) + { + break; + } + } + result = mTrack->readByte(&mKeyOffVelocity); + if (result != FMOD_OK) + { + break; + } + + if (calledfromopen) + { + break; + } + + current = (CodecMIDISubChannel *)mChannelHead.getNext(); + while (current != &mChannelHead) + { + if (keyoffkey == current->mCurrentNote) + { + current->mKeyOff = true; + current->mCurrentNote = -1; + break; + } + current = (CodecMIDISubChannel *)current->getNext(); + } + +// printf("NOTE OFF: Track %d, KEY %s: velocity %d\n", mIndex, keyoffkey < 128 ? note[keyoffkey]: "???", mKeyOffVelocity); + break; + } + case MIDI_VOICE_NOTEON: + { + unsigned char keyonkey; + int unitynote = 0, finetune = 0, sampleattenuation = 0, keygroup = 0; + bool noteoff = false, duplicateallowed = false; + SoundI *sound = 0; + CodecDLSInstrument *instrument = 0; + int numarticulators = 0; + DLS_CONNECTIONBLOCK *articulators = 0; + + if (reuse) + { + keyonkey = runningdata; + } + else + { + result = mTrack->readByte(&keyonkey); + if (result != FMOD_OK) + { + break; + } + } + + result = mTrack->readByte(&mKeyOnVelocity); + if (result != FMOD_OK) + { + break; + } + + result = getSound(keyonkey, &sound, &instrument, &unitynote, &finetune, &sampleattenuation, &duplicateallowed, &keygroup, &numarticulators, &articulators); + + if (calledfromopen) + { + break; + } + + if (result == FMOD_OK && sound) + { + /* + NoteOff is often implemented by doing a NoteOn with velocity 0, but NoteOff is also implied if + the same note is played again with a valid velocity. + */ + if (!duplicateallowed) + { + /* + If a duplicate note occurs then keyoff the old note. + */ + current = (CodecMIDISubChannel *)mChannelHead.getNext(); + while (current != &mChannelHead) + { + if (keyonkey == current->mCurrentNote) + { +// printf("keyoff duplicate %s : %s : subchan %d\n", instrument->mName, note[keyonkey], count); + current->mKeyOff = true; + current->mCurrentNote = -1; + noteoff = true; + break; + } + + current = (CodecMIDISubChannel *)current->getNext(); + } + + /* + Only break if this note is implmenting a NoteOff through a duplicate NoteOn with velocity 0 + */ + if (noteoff && mKeyOnVelocity == 0) + { + break; + } + } + + if (mKeyOnVelocity) + { + FMOD_RESULT result; + ChannelReal *realchannel; + + /* + If a playing note is in the same keygroup as the requested sound, kill it. + */ + current = (CodecMIDISubChannel *)mChannelHead.getNext(); + while (current != &mChannelHead) + { + CodecMIDISubChannel *next = (CodecMIDISubChannel *)current->getNext(); + + if (keygroup && keygroup == current->mKeyGroup) + { + current->stop(); + } + + current = next; + } + + if (mTrack->mMIDI->mChannelFreeListHead.isEmpty()) + { + float lowestvol = 9999.0f, lowestinreleasevol = 9999.0f; + int count; + CodecMIDISubChannel *lowestinrelease = 0; + CodecMIDISubChannel *lowest = 0; + + /* + Steal an existing channel. + */ + for (count = 0; count < 16; count++) + { + /* + Find a channel in release phase below volume of 10 + */ + current = (CodecMIDISubChannel *)mTrack->mMIDI->mMIDIChannel[count].mChannelHead.getNext(); + while (current != &mTrack->mMIDI->mMIDIChannel[count].mChannelHead) + { + float vol; + + current->mChannel.getVolume(&vol); + if (current->mVolumeEnvelope.mPosition == CODEC_DLS_ENVPOINT_RELEASE && vol < lowestinreleasevol) + { + lowestinreleasevol = vol; + lowestinrelease = current; + } + if (vol < lowestvol) + { + lowestvol = vol; + lowest = current; + } + + current = (CodecMIDISubChannel *)current->getNext();; + } + } + + if (lowestinrelease) + { + lowestinrelease->stop(); + } + else + { + lowest->stop(); + } + } + + current = (CodecMIDISubChannel *)mTrack->mMIDI->mChannelFreeListHead.getNext(); + current->removeNode(); + current->addAfter(&mChannelHead); + + current->mCurrentNote = keyonkey; + current->mParent = this; + current->mSound = sound; + current->mKeyOnKey = keyonkey; + current->mUnityNote = unitynote; + current->mFineTune = finetune; + current->mKeyOnVelocity = mKeyOnVelocity; + + current->mInstrument = instrument; + current->mKeyGroup = keygroup; + current->mSampleAttenuation = FMOD_POW(10.0f, (float)sampleattenuation / (200.0f * 65536.0f) ); + current->mNumArticulators = numarticulators; + current->mArticulator = articulators; + + result = current->setUpArticulators(); + if (result != FMOD_OK) + { + return result; + } + + result = mTrack->mMIDI->mChannelPool->allocateChannel(&realchannel, FMOD_CHANNEL_FREE, 1, 0); + if (result == FMOD_OK) + { +//MGB FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDIChannel::process", "play %s : %s : Velocity = %d\n", instrument->mName, note[keyonkey], mKeyOnVelocity)); + + current->mChannel.mRealChannel[0] = realchannel; + + result = current->mChannel.play(sound, true, true, false); + if (result != FMOD_OK) + { + return result; + } + + result = current->updatePitch(); + if (result != FMOD_OK) + { + return result; + } + + result = current->updateVolume(); + if (result != FMOD_OK) + { + return result; + } + + result = current->updatePan(); + if (result != FMOD_OK) + { + return result; + } + + current->mChannel.setPaused(false); + } + else + { + printf("ran out of FMOD channels?????\n"); + } + } + } + +// printf("NOTE ON: Track %d (%d), delta %6d, KEY %s: velocity %d\n", mIndex, channel, delta, keyonkey < 128 ? note[keyonkey]: "???", mKeyOnVelocity); + break; + } + case MIDI_VOICE_AFTERTOUCH: + { + if (reuse) + { + mAfterTouchKey = runningdata; + } + else + { + result = mTrack->readByte(&mAfterTouchKey); + if (result != FMOD_OK) + { + break; + } + } + result = mTrack->readByte(&mAfterTouchPressure); + if (result != FMOD_OK) + { + break; + } +// printf("AFTERTCH: Track %d (%d), delta %6d, KEY %s: velocity %d\n", mIndex, channel, delta, mAfterTouchKey < 128 ? note[mAfterTouchKey]: "???", mAfterTouchPressure); + break; + } + case MIDI_VOICE_CONTROLLERCHANGE: + { + unsigned char number, value; + + if (reuse) + { + number = runningdata; + } + else + { + result = mTrack->readByte(&number); + if (result != FMOD_OK) + { + break; + } + } + + result = mTrack->readByte(&value); + if (result != FMOD_OK) + { + break; + } + + switch (number) + { + case MIDI_CONTROLLERCHANGE_BANKSELECT_MSB: + { + if (mIndex != 10) /* Drum track. Not allowed to do this. (Program change yes, but bank select. No. It should always be MIDI_F_INSTRUMENT_DRUMS */ + { + mBank = (unsigned int)value << 8; + } + bankchange = true; + break; + } + case MIDI_CONTROLLERCHANGE_MODULATIONWHEEL_MSB: + { + mModWheel |= value; + break; + } + case MIDI_CONTROLLERCHANGE_BREATHCONTROL_MSB: + { + break; + } + case MIDI_CONTROLLERCHANGE_FOOTCONTROLLER_MSB: + { + break; + } + case MIDI_CONTROLLERCHANGE_PORTAMENTOTIME_MSB: + { + break; + } + case MIDI_CONTROLLERCHANGE_DATAENTRY_MSB: + { + switch (mRPN) + { + case MIDI_CONTROLLERCHANGE_RPN_PITCHBENDSENSITIVITY: + { + mPitchBendSensitivity = (value << 8); + break; + } + } + break; + } + case MIDI_CONTROLLERCHANGE_CHANNELVOLUME_MSB: + { + mVolume = value; + break; + } + case MIDI_CONTROLLERCHANGE_BALANCE_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_PAN_MSB: + { + mPan = value; + break; + } + case MIDI_CONTROLLERCHANGE_EXPRESSIONCONTROLLER_MSB: + { + mExpression = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTCONTROL1_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTCONTROL2_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER1_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER2_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER3_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER4_MSB: + { + value = value; + break; + } + + case MIDI_CONTROLLERCHANGE_BANKSELECT_LSB: + { + mBank |= (unsigned int)value; + bankchange = true; + break; + } + case MIDI_CONTROLLERCHANGE_MODULATIONWHEEL_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_BREATHCONTROL_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_FOOTCONTROLLER_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_PORTAMENTOTIME_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_DATAENTRY_LSB: + { + switch (mRPN) + { + case MIDI_CONTROLLERCHANGE_RPN_PITCHBENDSENSITIVITY: + { + mPitchBendSensitivity |= value; + break; + } + } + break; + } + case MIDI_CONTROLLERCHANGE_CHANNELVOLUME_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_BALANCE_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_PAN_LSB: + { + value = value; /* implement */ + break; + } + case MIDI_CONTROLLERCHANGE_EXPRESSIONCONTROLLER_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTCONTROL1_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTCONTROL2_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER1_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER2_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER3_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER4_LSB: + { + value = value; + break; + } + + case MIDI_CONTROLLERCHANGE_DAMPERPEDALONOFF: + { + if (value == 0) + { + mDamperPedal = false; + } + else + { + mDamperPedal = true; + } + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_PORTAMENTOONOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SUSTENUTOONOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOFTPEDALONOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_LEGATOFOOTSWITCH: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_HOLD2: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER1: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER2: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER3: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER4: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER5: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER6: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER7: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER8: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER9: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER10: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER5: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER6: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER7: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER8: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_PORTAMENTOCONTROL: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTS1DEPTH: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTS2DEPTH: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTS3DEPTH: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTS4DEPTH: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_EFFECTS5DEPTH: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_DATAENTRYINC: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_DATAENTRYDEC: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_NONREGISTEREDPARAMNUMBER_LSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_NONREGISTEREDPARAMNUMBER_MSB: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_REGISTEREDPARAMETERNUMBER_LSB: + { + mRPN |= value; + break; + } + case MIDI_CONTROLLERCHANGE_REGISTEREDPARAMETERNUMBER_MSB: + { + mRPN = value << 8; + break; + } + case MIDI_CONTROLLERCHANGE_ALLSOUNDOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_RESETALLCONTROLLERS: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_LOCALCONTROLONOFF: + { + value = value; /* implement */ + break; + } + case MIDI_CONTROLLERCHANGE_ALLNOTESOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_OMNIMODEOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_OMNIMODEON: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_POLYMODEONOFF: + { + value = value; + break; + } + case MIDI_CONTROLLERCHANGE_POLYMODEON: + { + value = value; + break; + } + } + + break; + } + case MIDI_VOICE_PROGRAMCHANGE: + { + if (reuse) + { + mProgram = runningdata; + } + else + { + result = mTrack->readByte(&mProgram); + if (result != FMOD_OK) + { + break; + } + } + + /* + Check if the drum set actually exists in the DLS set. If it doesn't, reset to the default set. + */ + if (mIndex == 10) + { + CodecDLS *dls = mTrack->mMIDI->mDLS; + int count; + bool found = false; + + for (count = 0; count < dls->mNumInstruments; count++) + { + CodecDLSInstrument *inst = &dls->mInstrument[count]; + + if (inst->mHeader.Locale.ulBank == mBank && inst->mHeader.Locale.ulInstrument == mProgram) + { + found = true; + } + } + + if (!found) + { + mProgram = 0; + } + } + + break; + } + case MIDI_VOICE_CHANNELKEYPRESSURE: + { + if (reuse) + { + mChannelPressure = runningdata; + } + else + { + result = mTrack->readByte(&mChannelPressure); + if (result != FMOD_OK) + { + break; + } + } + break; + } + case MIDI_VOICE_PITCHBEND: + { + unsigned char msb, lsb; + + if (reuse) + { + lsb = runningdata; + } + else + { + result = mTrack->readByte(&lsb); + if (result != FMOD_OK) + { + break; + } + } + result = mTrack->readByte(&msb); + if (result != FMOD_OK) + { + break; + } + + mPitchBend = (((int)msb - 0x40) << 7) | (int)lsb; + break; + } + default: + { +// mOffset += length; + event = event; + break; + } + }; + + /* + Check if the new program + bank combo exists. + */ + if (bankchange) + { + if (mBank) + { + CodecDLS *dls = mTrack->mMIDI->mDLS; + int count; + bool found = false; + + for (count = 0; count < dls->mNumInstruments; count++) + { + CodecDLSInstrument *inst = &dls->mInstrument[count]; + + if (inst->mHeader.Locale.ulBank == mBank && inst->mHeader.Locale.ulInstrument == mProgram) + { + found = true; + } + } + + if (!found) + { + mBank = 0; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDIChannel::update() +{ + CodecMIDISubChannel *current; + + current = (CodecMIDISubChannel *)mChannelHead.getNext(); + while (current != &mChannelHead) + { + CodecMIDISubChannel *next = (CodecMIDISubChannel *)current->getNext(); + + if (current->mInstrument) + { + current->updateVolume(); + current->mVolumeEnvelope.mTime += mTrack->mMIDI->mMillisecondsPerTick; + + current->updatePitch(); + current->mPitchEnvelope.mTime += mTrack->mMIDI->mMillisecondsPerTick; + + current->updatePan(); +// current->mPanEnvelope.mTime += mTrack->mMIDI->mMillisecondsPerTick; + + current->mLFOTime += mTrack->mMIDI->mMillisecondsPerTick; + } + + current = next; + } + + return FMOD_OK; +} + + +/* + ========================================================================================== + + CODEC MIDI TRACK CLASS + + ========================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDITrack::readVarLen(unsigned int *result) +{ + register unsigned int value = 0; + register unsigned char ch = 0; + register unsigned char byteCount = 0; + + do + { + if (mOffset >= mLength || byteCount == 4) + { + mFinished = true; + return FMOD_ERR_FILE_EOF; + } + + value = (value << 7) + ((ch = mData[mOffset++]) & 0x7F); + byteCount++; + + } while (ch & 0x80); + + *result = value; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDITrack::readByte(unsigned char *result) +{ + if (mOffset >= mLength) + { + mFinished = true; + return FMOD_ERR_FILE_EOF; + } + + *result = mData[mOffset++]; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDITrack::read(void *buff, int length) +{ + if (mOffset >= mLength) + { + mFinished = true; + return FMOD_ERR_FILE_EOF; + } + + if (mOffset + length > mLength) + { + length = mLength - mOffset; + } + + if (buff) + { + FMOD_memcpy(buff, mData + mOffset, length); + } + + mOffset += length; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDITrack::addTag(const char *name, int length, bool calledfromopen) +{ + FMOD_RESULT result; + void *data; + + if (!calledfromopen) + { + return read(0, length); + } + + data = FMOD_Memory_Calloc(length); + if (!data) + { + return FMOD_ERR_MEMORY; + } + + result = read(data, length); + if (result != FMOD_OK) + { + return result; + } + + result = mMIDI->metaData(FMOD_TAGTYPE_MIDI, name, data, length, FMOD_TAGDATATYPE_STRING, false); + + FMOD_Memory_Free(data); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDITrack::process(bool calledfromopen) +{ + unsigned char event; + + if (!mData) + { + return FMOD_OK; + } + + if (!mFinished) + { + FMOD_RESULT result; + + while (mTick <= mMIDI->mTick) + { + if (!mReadDelta) + { + unsigned int delta; + + result = readVarLen(&delta); + if (result != FMOD_OK) + { + return result; + } + mTick += (float)delta; + mReadDelta = false; + } + + if (mTick > mMIDI->mTick) + { + mReadDelta = true; + break; + } + + mReadDelta = false; + + result = readByte(&event); + if (result != FMOD_OK) + { + return result; + } + + if (event >= 0xF0) + { + switch (event) + { + case MIDI_SYSEXESCAPE: + { + unsigned int length; + + result = readVarLen(&length); + if (result != FMOD_OK) + { + break; + } + + break; + } + case MIDI_SYSEXEVENT: + { + unsigned int length; + + result = readVarLen(&length); + if (result != FMOD_OK) + { + break; + } + + mOffset += length; + + break; + } + case MIDI_METAEVENT: + { + unsigned char type; + unsigned int length; + + result = readByte(&type); + if (result != FMOD_OK) + { + break; + } + + result = readVarLen(&length); + if (result != FMOD_OK) + { + break; + } + + switch (type) + { + case MIDI_METAEVENT_SEQNUMBER: + { + if (!length) + { + mMIDI->mSequenceNumber = mIndex; + } + else + { + if (length > 4) /* Eh? I dont know how but 1 midi did this */ + { + result = read(0, length); + if (result != FMOD_OK) + { + break; + } + } + else + { + result = read(&mMIDI->mSequenceNumber, length); + if (result != FMOD_OK) + { + break; + } + } + } + break; + } + case MIDI_METAEVENT_TEXT: + { + int timeOffsetMs = (int)mMIDI->mMillisecondsPlayed; + + result = addTag("Text", length, calledfromopen); + if (calledfromopen) + { + result = mMIDI->metaData(FMOD_TAGTYPE_MIDI, "Text (Time Ms)", &timeOffsetMs, sizeof(int), FMOD_TAGDATATYPE_INT, false); + } + break; + } + case MIDI_METAEVENT_COPYRIGHT: + { + result = addTag("Copyright", length, calledfromopen); + break; + } + case MIDI_METAEVENT_NAME: + { + char s[256]; + + sprintf(s, "Track %d Name", mIndex); + + result = addTag(s, length, calledfromopen); + break; + } + case MIDI_METAEVENT_INSTRUMENT: + { + result = addTag("Instrument", length, calledfromopen); + break; + } + case MIDI_METAEVENT_LYRIC: + { + result = addTag("Lyric", length, calledfromopen); + break; + } + case MIDI_METAEVENT_MARKER: + { + result = addTag("Marker", length, calledfromopen); + break; + } + case MIDI_METAEVENT_CUEPOINT: + { + result = addTag("Cue Point", length, calledfromopen); + break; + } + case MIDI_METAEVENT_PATCHNAME: + { + result = addTag("Patch Name", length, calledfromopen); + break; + } + case MIDI_METAEVENT_PORTNAME: + { + result = addTag("Port Name", length, calledfromopen); + break; + } + case MIDI_METAEVENT_CHANNEL: + { + unsigned char dummy; + result = readByte(&dummy); + break; + } + case MIDI_METAEVENT_PORT: + { + if (length) + { + result = readByte(&mPort); + } + break; + } + case MIDI_METAEVENT_ENDOFTRACK: + { + mFinished = true; + break; + } + case MIDI_METAEVENT_TEMPO: + { + unsigned char ttt[3]; + + result = read(&ttt[0], length); + + mMIDI->mTempo = ttt[0] << 16; + mMIDI->mTempo |= ttt[1] << 8; + mMIDI->mTempo |= ttt[2] << 0; + + // Tempo is microseconds per MIDI quarter-note + mMIDI->mMillisecondsPerTick = ((float)mMIDI->mTempo / (float)mMIDI->mDivision) / 1000.0f; + mMIDI->mMixerSamplesPerTick = (int)(mMIDI->mMillisecondsPerTick / 1000.0f * mMIDI->waveformat[0].frequency); + mMIDI->mTimingScale = 1.0f; + + if (mMIDI->mMixerSamplesPerTick < (unsigned int)MIDI_MINIMUMSAMPLEGRANULARITY) + { + mMIDI->mTimingScale = MIDI_MINIMUMSAMPLEGRANULARITY / (mMIDI->mMillisecondsPerTick / 1000.0f * mMIDI->waveformat[0].frequency); + mMIDI->mMixerSamplesPerTick = MIDI_MINIMUMSAMPLEGRANULARITY; + mMIDI->mMillisecondsPerTick = MIDI_MINIMUMSAMPLEGRANULARITY * 1000.0f / mMIDI->waveformat[0].frequency; + } + + break; + } + case MIDI_METAEVENT_SMTPOFFSET: + { + result = readByte(&mMIDI->mSMTPOffsetHours); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mSMTPOffsetMinutes); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mSMTPOffsetSeconds); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mSMTPOffsetFrames); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mSMTPOffsetSubFrames); + if (result != FMOD_OK) + { + break; + } + break; + } + case MIDI_METAEVENT_TIMESIGNATURE: + { + result = readByte(&mMIDI->mTimeSignatureNumerator); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mTimeSignatureDenominator); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mTimeSignatureMetronome); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mTimeSignatureNotated32nds); + if (result != FMOD_OK) + { + break; + } + break; + } + case MIDI_METAEVENT_KEYSIGNATURE: + { + result = readByte(&mMIDI->mKeySignatureSF); + if (result != FMOD_OK) + { + break; + } + result = readByte(&mMIDI->mKeySignatureMI); + if (result != FMOD_OK) + { + break; + } + break; + } + case MIDI_METAEVENT_PROPRIETARY: + { + result = addTag("Proprietory Data", length, calledfromopen); + break; + } + default: + { + mOffset += length; + break; + } + }; + break; + } + default: + { + // mOffset += length; + event = event; + break; + } + }; + } + else + { + int midichannel; + unsigned char runningdata = 0; + bool reuse = false; + + if (event < 0x80) + { + runningdata = event; + event = mEvent; + reuse = true; + } + + midichannel = (event & 0x0F); + + mMIDI->mMIDIChannel[midichannel].mTrack = this; + mMIDI->mMIDIChannel[midichannel].process(event, reuse, runningdata, calledfromopen); + } + + mEvent = event; + + if (mFinished) + { + break; + } + } + } + + return FMOD_OK; +} + + +/* + ========================================================================================== + + CODEC MIDI CLASS + + ========================================================================================== +*/ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::update(bool audible) +{ + int count; + + for (count = 0; count < mNumTracks; count++) + { + mTrack[count].process(false); + } + + for (count = 0; count < 16; count++) + { + mMIDIChannel[count].update(); +//MGB FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::update", "Chan: %d -- Voices: %d\n", count, mMIDIChannel[count].mChannelHead.count())); + } +//MGB FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::update", "\n")); + + mTick += mTimingScale; + + mPCMOffset += mMixerSamplesPerTick; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::play(bool fromopen) +{ + int count; + + for (count = 0; count < mNumTracks; count++) + { + mTrack[count].mOffset = 0; + mTrack[count].mTick = 0; + mTrack[count].mFinished = false; + mTrack[count].mEvent = 0; + mTrack[count].mReadDelta = 0; + } + + mChannelFreeListHead.initNode(); + + for (count = 0; count < mNumSubChannels; count++) + { + mMIDISubChannel[count].initNode(); + mMIDISubChannel[count].stop(); + } + + for (count = 0; count < 16; count++) + { + mMIDIChannel[count].mChannelHead.initNode(); + mMIDIChannel[count].mIndex = count + 1; + mMIDIChannel[count].mPan = 64; + mMIDIChannel[count].mVolume = 100; + mMIDIChannel[count].mExpression = 127; + mMIDIChannel[count].mBank = 0; + mMIDIChannel[count].mProgram = 0; + mMIDIChannel[count].mPitchBendSensitivity = 0x200; + mMIDIChannel[count].mRPN = 0xFFFFFFFF; + + if (fromopen) + { + mMIDIChannel[count].mTrack = 0; + mMIDIChannel[count].mMasterVolume = 1.0f; + } + } + + mTick = 0; + mPCMOffset = 0; + mMixerSamplesLeft = 0; + mMillisecondsPlayed = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + int count; + unsigned int offset, lengthbytes; + MTHD_CHUNK midiheaderchunk; + int trackindex; + + init(FMOD_SOUND_TYPE_MIDI); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::openInternal", "attempting to open as MIDI..\n")); + + /* + Need to explicitly construct the ChannelGroupI here because CodecMIDI doesn't have a constructor + */ + new (&mChannelGroup) ChannelGroupI(); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get size of file in bytes + */ + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + offset = 0; + + result = mFile->read(&midiheaderchunk, 1, sizeof(MTHD_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + +#ifdef PLATFORM_ENDIAN_LITTLE + midiheaderchunk.mChunk.mSize = FMOD_SWAPENDIAN_DWORD(midiheaderchunk.mChunk.mSize); + midiheaderchunk.mFormat = FMOD_SWAPENDIAN_WORD(midiheaderchunk.mFormat); + midiheaderchunk.mDivision = FMOD_SWAPENDIAN_WORD(midiheaderchunk.mDivision); + midiheaderchunk.mNumTracks = FMOD_SWAPENDIAN_WORD(midiheaderchunk.mNumTracks); +#endif + + if (FMOD_strncmp((const char *)midiheaderchunk.mChunk.mID, "MThd", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::openInternal", "'HThd' ID check failed [%c%c%c%c]\n", midiheaderchunk.mChunk.mID[0], midiheaderchunk.mChunk.mID[1], midiheaderchunk.mChunk.mID[2], midiheaderchunk.mChunk.mID[3])); + return FMOD_ERR_FORMAT; + } + + mNumTracks = midiheaderchunk.mNumTracks + 1; /* Some midi files written out wrongly! */ + mMIDIFormat = (MIDI_FORMAT)midiheaderchunk.mFormat; + mDivision = midiheaderchunk.mDivision; + mTempo = 60000 / 120 * 1000; + mMillisecondsPerTick = ((float)mTempo / (float)mDivision) / 1000.0f; + mTimingScale = 1.0f; + + if (userexinfo && userexinfo->maxpolyphony) + { + mNumSubChannels = userexinfo->maxpolyphony; + } + else + { + mNumSubChannels = MIDI_DEFAULTSUBCHANNELS; + } + + /* + Load the DLS definitions and audio data so we can actually hear something. + */ + { + FMOD_CREATESOUNDEXINFO exinfo; + char dlsname[FMOD_STRING_MAXPATHLEN]; + int list[1] = { -1 }; + + FMOD_memset(dlsname, 0, FMOD_STRING_MAXPATHLEN); + + if (userexinfo && userexinfo->dlsname) + { + strncpy(dlsname, userexinfo->dlsname, FMOD_STRING_MAXPATHLEN); + } + #if defined(PLATFORM_WINDOWS) + else + { + char *sysroot; + + sysroot = getenv("windir"); + if (sysroot) + { + struct _stat statbuf; + + strncpy(dlsname, sysroot, FMOD_STRING_MAXPATHLEN); + strcat(dlsname, "/system32/drivers/gm.dls"); + + if (_stat(dlsname, &statbuf)) + { + strncpy(dlsname, sysroot, FMOD_STRING_MAXPATHLEN); + strcat(dlsname, "/system32/drivers/etc/gm.dls"); + + if (_stat(dlsname, &statbuf)) + { + return FMOD_ERR_PLUGIN_RESOURCE; + } + } + } + } + #endif + #if defined(PLATFORM_MAC) + else + { + struct stat statbuf; + + strcat(dlsname, "/System/Library/Components/CoreAudio.component/Contents/Resources/gs_instruments.dls"); + + if (stat(dlsname, &statbuf)) + { + return FMOD_ERR_PLUGIN_RESOURCE; + } + } + #endif + + FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.inclusionlist = list; + exinfo.inclusionlistnum = 1; + + /* + Search the cache for the required DLS file, create if it doesn't exist + */ + mDLSCache = NULL; + for (CodecMIDIDLSCache *current = (CodecMIDIDLSCache *)gDLSCacheHead.getNext(); current != &gDLSCacheHead; current = (CodecMIDIDLSCache *)current->getNext()) + { + if (!FMOD_strncmp(current->mFilePath, dlsname, FMOD_STRING_MAXPATHLEN)) + { + mDLSCache = current; + mDLSCache->mRefCount++; + break; + } + } + + if (mDLSCache == NULL) + { + SoundI *dlsFile = NULL; + + result = mSystem->createSound(dlsname, FMOD_2D, &exinfo, &dlsFile); + if (result == FMOD_ERR_FILE_NOTFOUND) + { + return FMOD_ERR_PLUGIN_RESOURCE; + } + else if (result != FMOD_OK) + { + return result; + } + + mDLSCache = FMOD_Object_Alloc(CodecMIDIDLSCache); + if (mDLSCache == NULL) + { + return FMOD_ERR_MEMORY; + } + + FMOD_strncpy(mDLSCache->mFilePath, dlsname, FMOD_STRING_MAXPATHLEN); + mDLSCache->mRefCount = 1; + mDLSCache->mDLSFile = dlsFile; + mDLSCache->addBefore(&gDLSCacheHead); + } + + mDLSSound = mDLSCache->mDLSFile; + mDLS = (CodecDLS *)mDLSSound->mCodec; + } + + /* + Create and parse the MIDI Tracks. + */ + mTrack = (CodecMIDITrack *)FMOD_Memory_Calloc(sizeof(CodecMIDITrack) * mNumTracks); + if (!mTrack) + { + return FMOD_ERR_MEMORY; + } + + mMIDISubChannel = (CodecMIDISubChannel *)FMOD_Memory_Calloc(sizeof(CodecMIDISubChannel) * mNumSubChannels); + if (!mMIDISubChannel) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < mNumSubChannels; count++) + { + mMIDISubChannel[count].initNode(); + mMIDISubChannel[count].mChannel.init(); + mMIDISubChannel[count].mChannel.mFlags |= CHANNELI_FLAG_MUSICOWNED; + mMIDISubChannel[count].mMIDI = this; + mMIDISubChannel[count].mChannel.mChannelGroup = &mChannelGroup; + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + + waveformat = mWaveFormatMemory; + waveformat[0].lengthbytes = lengthbytes; + + trackindex = 0; + + offset += sizeof(MTHD_CHUNK); + offset -= sizeof(MIDI_CHUNK); + + /* + Decode chunks + */ + do + { + MIDI_CHUNK chunk; + + result = mFile->seek(offset + sizeof(MIDI_CHUNK), SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&chunk, 1, sizeof(MIDI_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_LITTLE + chunk.mSize = FMOD_SWAPENDIAN_DWORD(chunk.mSize); + #endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::openInternal","chunk: id %c%c%c%c size %d\n", chunk.mID[0],chunk.mID[1],chunk.mID[2],chunk.mID[3], chunk.mSize)); + + /* + DATA CHUNK + */ + if (!FMOD_strncmp((const char *)chunk.mID, "MTrk", 4)) + { + mTrack[trackindex].mData = (unsigned char *)FMOD_Memory_Calloc(chunk.mSize); + if (!mTrack[trackindex].mData) + { + return FMOD_ERR_MEMORY; + } + + result = mFile->read(mTrack[trackindex].mData, 1, chunk.mSize, 0); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) /* allow truncated files :S */ + { + return result; + } + + mTrack[trackindex].mIndex = trackindex; + mTrack[trackindex].mLength = chunk.mSize; + mTrack[trackindex].mOffset = 0; + mTrack[trackindex].mMIDI = this; + trackindex++; + } + else + { + mFile->seek(chunk.mSize, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + + offset += (chunk.mSize+sizeof(MIDI_CHUNK)); + + if (chunk.mSize < 0) + { + break; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::openInternal", "offset = %d / %d\n", offset, waveformat[0].lengthbytes - sizeof(MIDI_CHUNK))); + + } while (offset < (waveformat[0].lengthbytes - sizeof(MIDI_CHUNK) - sizeof(MIDI_CHUNK)) && offset > 0); + + /* + Set up general codec parameters. + */ + if (userexinfo && userexinfo->format != FMOD_SOUND_FORMAT_NONE) + { + waveformat[0].format = userexinfo->format; + } + #ifndef PLATFORM_PS3 + else if (usermode & FMOD_SOFTWARE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + #endif + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + waveformat[0].channels = 2; + result = mSystem->getSoftwareFormat(&waveformat[0].frequency, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, waveformat[0].channels, waveformat[0].format); + + /* + Create a default set of values for mixersamples per tick and ms per tick. + */ + mMixerSamplesPerTick = (int)(mMillisecondsPerTick / 1000.0f * waveformat[0].frequency); + if (mMixerSamplesPerTick < (unsigned int)MIDI_MINIMUMSAMPLEGRANULARITY) + { + mTimingScale = MIDI_MINIMUMSAMPLEGRANULARITY / (mMillisecondsPerTick / 1000.0f * waveformat[0].frequency); + mMixerSamplesPerTick = MIDI_MINIMUMSAMPLEGRANULARITY; + mMillisecondsPerTick = MIDI_MINIMUMSAMPLEGRANULARITY * 1000.0f / waveformat[0].frequency; + } + + /* + Scan the midi to get the length and also to find what instruments are used. + */ + play(true); + + mSampleInclusionList = (bool *)FMOD_Memory_Calloc(sizeof(bool) * mDLSSound->mNumSubSounds); + if (!mSampleInclusionList) + { + return FMOD_ERR_MEMORY; + } + + { + const float ticksPerStep = 10.0f; + int finishedCount = 0; + float msPerTick = 0; + + waveformat[0].lengthpcm = 0; + + // Quickly process the file untill all tracks report finished to determine the length + while (finishedCount != mNumTracks) + { + finishedCount = 0; + + for (count = 0; count < mNumTracks; count++) + { + // Process each track and record finished (or invalid data) tracks + mTrack[count].process(true); + if (mTrack[count].mFinished || !mTrack[count].mData) + { + finishedCount++; + } + } + + // Each slice process updates tempo, so calculate the time this slice would play for + msPerTick = ((float)mTempo / (float)mDivision) / 1000.0f; + mMillisecondsPlayed += msPerTick * ticksPerStep; + mTick += ticksPerStep; + } + + // Convert the time played into playback samples (time adjusted) + waveformat[0].lengthpcm = (int)((mMillisecondsPlayed / 1000.0f) * waveformat[0].frequency); + } + + // Determine the maximum number of channels played in the MIDI file + { + int totalNumChannels = 0; + + for (int channelIndex = 0; channelIndex < 16; channelIndex++) + { + // If the channel has been assigned a track, then this channel has played at least once + if (mMIDIChannel[channelIndex].mTrack) + { + totalNumChannels++; + } + } + + result = metaData(FMOD_TAGTYPE_FMOD, "Number of channels", &totalNumChannels, sizeof(totalNumChannels), FMOD_TAGDATATYPE_INT, false); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Now that the midi data has been scanned load the appropriate sounds. + */ + for (count=0; count < mDLSSound->mNumSubSounds; count++) + { + if (mSampleInclusionList[count]) + { + if (mDLSSound->mSubSound[count] == NULL) + { + result = mDLSSound->loadSubSound(count, FMOD_SOFTWARE | FMOD_2D); + if (result != FMOD_OK) + { + return result; + } + } + } + } + + FMOD_Memory_Free(mSampleInclusionList); + mSampleInclusionList = 0; + + for (count = 0; count < mDLS->mNumInstruments; count++) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMIDI::openInternal", "%3d: %-20s: Bank %08x Program %4d\n", count, mDLS->mInstrument[count].mName, mDLS->mInstrument[count].mHeader.Locale.ulBank, mDLS->mInstrument[count].mHeader.Locale.ulInstrument)); + } + + /* + Create a head unit that software channels connect to. + */ + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD MIDI Target Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = waveformat[0].channels; + descriptionex.mFormat = waveformat[0].format; + descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + + result = mSystem->createDSP(&descriptionex, &mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + mDSPHead->mDefaultFrequency = (float)waveformat[0].frequency; + } + + mChannelGroup.mDSPHead = mDSPHead; + mChannelGroup.mDSPMixTarget = mDSPHead; + mChannelGroup.mVolume = 1.0f; + + /* + Create a pool of real channels. + */ + { + mChannelPool = FMOD_Object_Calloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, 0, mNumSubChannels); + if (result != FMOD_OK) + { + return result; + } + + mChannelSoftware = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * mNumSubChannels); + if (!mChannelSoftware) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < mNumSubChannels; count++) + { + new (&mChannelSoftware[count]) ChannelSoftware; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannelSoftware[count], mDSPHead)); + CHECK_RESULT(mChannelSoftware[count].allowReverb(false)); + } + } + + play(false); + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + mDSPTick = 1; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::closeInternal() +{ + int count; + + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + if (mDSPHead) + { + mDSPHead->release(); + mDSPHead = 0; + } + if (mDLSCache) + { + if (--mDLSCache->mRefCount == 0) + { + mDLSCache->mDLSFile->release(); + mDLSCache->removeNode(); + FMOD_Memory_Free(mDLSCache); + } + mDLSCache = 0; + mDLSSound = 0; + mDLS = 0; + } + + if (mTrack) + { + for (count = 0; count < mNumTracks; count++) + { + if (mTrack[count].mData) + { + FMOD_Memory_Free(mTrack[count].mData); + } + } + FMOD_Memory_Free(mTrack); + } + + if (mMIDISubChannel) + { + FMOD_Memory_Free(mMIDISubChannel); + mMIDISubChannel = 0; + } + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mMIDISubChannel = 0; + } + + if (mChannelSoftware) + { + FMOD_Memory_Free(mChannelSoftware); + mChannelSoftware = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int numsamples; + int numchannels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + numchannels = waveformat[0].channels; + + // If no notes have played, this buffer is returned from dspHeadExecute unchanged, so clear it for silence + FMOD_memset(buffer, 0, sizebytes); + + SoundI::getSamplesFromBytes(sizebytes, &numsamples, numchannels, waveformat[0].format); + + { + unsigned int mixedsofar = 0; + unsigned int mixedleft = mMixerSamplesLeft; + unsigned int samplestomix; + char *destptr; + + /* + Keep resetting the mix pointer to the beginning of this portion of the ring buffer + */ + destptr = (char *)buffer; + + while (mixedsofar < numsamples) + { + unsigned int read, bytes; + + if (!mixedleft) + { + result = update(true); + if (result != FMOD_OK) + { + return result; + } + + samplestomix = mMixerSamplesPerTick; + mixedleft = samplestomix; + } + else + { + samplestomix = mixedleft; + } + + if (mixedsofar + samplestomix > numsamples) + { + samplestomix = numsamples - mixedsofar; + } + + read = samplestomix; + + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + + criticalsection.enter(); + if (buffer) + { + result = mDSPHead->read(destptr, &read, FMOD_SPEAKERMODE_STEREO, 2, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + mDSPTick++; + } + + SoundI::getBytesFromSamples(read, &bytes, numchannels, waveformat[0].format); + + /* + Buffer returned from the DSP head execute may not be the one we sent in (it may be + one of the global buffers). Don't leave mDSPCrit until after we have copied data out + */ + criticalsection.leave(); + + mixedsofar += read; + destptr += bytes; + mixedleft -= read; + } + + mMixerSamplesLeft = mixedleft; + } + + if (bytesread) + { + *bytesread = sizebytes; + } + +// printf("chans %d\n", mNumSubChannels - mChannelFreeListHead.count()); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + + if (position == mPCMOffset) + { + return FMOD_OK; + } + + /* + Want to seek backwards, start from the start. + */ + if (position < mPCMOffset) + { + play(false); + } + + while (mPCMOffset < position) + { + update(true); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::getMusicNumChannelsInternal(int *numchannels) +{ + int totalNumChannels = 0; + + if (!numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (int channelIndex = 0; channelIndex < 16; channelIndex++) + { + if (mMIDIChannel[channelIndex].mTrack) + { + totalNumChannels++; + } + } + + *numchannels = totalNumChannels; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::setMusicChannelVolumeInternal(int channel, float volume) +{ + int totalNumChannels = 0; + + if (channel < 0 || channel >= 16 || volume < 0.0f || volume > 1.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (int channelIndex = 0; channelIndex < 16; channelIndex++) + { + if (mMIDIChannel[channelIndex].mTrack) + { + if (channel == totalNumChannels) + { + mMIDIChannel[channelIndex].mMasterVolume = volume; + break; + } + totalNumChannels++; + } + } + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMIDI::getMusicChannelVolumeInternal(int channel, float *volume) +{ + int totalNumChannels = 0; + + if (channel < 0 || channel >= 16 || !volume) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (int channelIndex = 0; channelIndex < 16; channelIndex++) + { + // If the channel has been assigned a track, then this channel has played at least once + if (mMIDIChannel[channelIndex].mTrack) + { + if (channel == totalNumChannels) + { + *volume = mMIDIChannel[channelIndex].mMasterVolume; + break; + } + totalNumChannels++; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecMIDI *cmidi = (CodecMIDI *)codec; + + return cmidi->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecMIDI *cmidi = (CodecMIDI *)codec; + + return cmidi->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecMIDI *cmidi = (CodecMIDI *)codec; + + return cmidi->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecMIDI *cmidi = (CodecMIDI *)codec; + + return cmidi->setPositionInternal(subsound, position, postype); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::getMusicNumChannelsCallback(FMOD_CODEC_STATE *codec, int *numchannels) +{ + CodecMIDI *cmusic = (CodecMIDI *)codec; + + return cmusic->getMusicNumChannelsInternal(numchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::setMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float volume) +{ + CodecMIDI *cmusic = (CodecMIDI *)codec; + + return cmusic->setMusicChannelVolumeInternal(channel, volume); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMIDI::getMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float *volume) +{ + CodecMIDI *cmusic = (CodecMIDI *)codec; + + return cmusic->getMusicChannelVolumeInternal(channel, volume); +} + + +} + +#endif + diff --git a/src/fmod_codec_midi.h b/src/fmod_codec_midi.h new file mode 100755 index 0000000..1da12d3 --- /dev/null +++ b/src/fmod_codec_midi.h @@ -0,0 +1,358 @@ +#ifndef _FMOD_CODEC_MIDI_H +#define _FMOD_CODEC_MIDI_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MIDI + +#include "fmod_music.h" + +namespace FMOD +{ + class DSPI; + class ChannelPool; + class CodecMIDI; + class CodecMIDITrack; + class CodecMIDIChannel; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + typedef struct + { + char mID[4] FMOD_PACKED_INTERNAL; + unsigned int mSize FMOD_PACKED_INTERNAL; + } FMOD_PACKED MIDI_CHUNK; + + typedef struct + { + MIDI_CHUNK mChunk FMOD_PACKED_INTERNAL; + unsigned short mFormat FMOD_PACKED_INTERNAL; + unsigned short mNumTracks FMOD_PACKED_INTERNAL; + unsigned short mDivision FMOD_PACKED_INTERNAL; + } FMOD_PACKED MTHD_CHUNK; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + + typedef enum + { + MIDI_FORMAT_SINGLETRACK, + MIDI_FORMAT_MULTITRACK, + MIDI_FORMAT_PATTERN, + } MIDI_FORMAT; + + static const unsigned char MIDI_VOICE_NOTEOFF = 0x80; + static const unsigned char MIDI_VOICE_NOTEON = 0x90; + static const unsigned char MIDI_VOICE_AFTERTOUCH = 0xA0; + static const unsigned char MIDI_VOICE_CONTROLLERCHANGE = 0xB0; + static const unsigned char MIDI_VOICE_PROGRAMCHANGE = 0xC0; + static const unsigned char MIDI_VOICE_CHANNELKEYPRESSURE= 0xD0; + static const unsigned char MIDI_VOICE_PITCHBEND = 0xE0; + static const unsigned char MIDI_SYSEXESCAPE = 0xF7; + static const unsigned char MIDI_SYSEXEVENT = 0xF0; + static const unsigned char MIDI_METAEVENT = 0xFF; + + static const unsigned char MIDI_METAEVENT_SEQNUMBER = 0x00; + static const unsigned char MIDI_METAEVENT_TEXT = 0x01; + static const unsigned char MIDI_METAEVENT_COPYRIGHT = 0x02; + static const unsigned char MIDI_METAEVENT_NAME = 0x03; + static const unsigned char MIDI_METAEVENT_INSTRUMENT = 0x04; + static const unsigned char MIDI_METAEVENT_LYRIC = 0x05; + static const unsigned char MIDI_METAEVENT_MARKER = 0x06; + static const unsigned char MIDI_METAEVENT_CUEPOINT = 0x07; + static const unsigned char MIDI_METAEVENT_PATCHNAME = 0x08; + static const unsigned char MIDI_METAEVENT_PORTNAME = 0x09; + static const unsigned char MIDI_METAEVENT_CHANNEL = 0x20; + static const unsigned char MIDI_METAEVENT_PORT = 0x21; + static const unsigned char MIDI_METAEVENT_ENDOFTRACK = 0x2F; + static const unsigned char MIDI_METAEVENT_TEMPO = 0x51; + static const unsigned char MIDI_METAEVENT_SMTPOFFSET = 0x54; + static const unsigned char MIDI_METAEVENT_TIMESIGNATURE = 0x58; + static const unsigned char MIDI_METAEVENT_KEYSIGNATURE = 0x59; + static const unsigned char MIDI_METAEVENT_PROPRIETARY = 0x7F; + + static const unsigned char MIDI_CONTROLLERCHANGE_BANKSELECT_MSB = 0; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_MODULATIONWHEEL_MSB = 1; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_BREATHCONTROL_MSB = 2; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_FOOTCONTROLLER_MSB = 4; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_PORTAMENTOTIME_MSB = 5; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_DATAENTRY_MSB = 6; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_CHANNELVOLUME_MSB = 7; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_BALANCE_MSB = 8; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_PAN_MSB = 10; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_EXPRESSIONCONTROLLER_MSB = 11; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTCONTROL1_MSB = 12; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTCONTROL2_MSB = 13; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER1_MSB = 16; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER2_MSB = 17; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER3_MSB = 18; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER4_MSB = 19; // 0-127 MSB + + static const unsigned char MIDI_CONTROLLERCHANGE_BANKSELECT_LSB = 32; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_MODULATIONWHEEL_LSB = 33; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_BREATHCONTROL_LSB = 34; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_FOOTCONTROLLER_LSB = 36; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_PORTAMENTOTIME_LSB = 37; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_DATAENTRY_LSB = 38; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_CHANNELVOLUME_LSB = 39; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_BALANCE_LSB = 40; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_PAN_LSB = 42; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EXPRESSIONCONTROLLER_LSB = 43; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTCONTROL1_LSB = 44; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTCONTROL2_LSB = 45; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER1_LSB = 48; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER2_LSB = 49; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER3_LSB = 50; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER4_LSB = 51; // 0-127 LSB + + static const unsigned char MIDI_CONTROLLERCHANGE_DAMPERPEDALONOFF = 64; // >64=ON + static const unsigned char MIDI_CONTROLLERCHANGE_PORTAMENTOONOFF = 65; // >64=ON + static const unsigned char MIDI_CONTROLLERCHANGE_SUSTENUTOONOFF = 66; // >64=ON + static const unsigned char MIDI_CONTROLLERCHANGE_SOFTPEDALONOFF = 67; // >64=ON + static const unsigned char MIDI_CONTROLLERCHANGE_LEGATOFOOTSWITCH = 68; // >64=ON + static const unsigned char MIDI_CONTROLLERCHANGE_HOLD2 = 69; // >64=ON + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER1 = 70; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER2 = 71; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER3 = 72; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER4 = 73; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER5 = 74; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER6 = 75; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER7 = 76; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER8 = 77; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER9 = 78; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_SOUNDCONTROLLER10 = 79; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER5 = 80; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER6 = 81; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER7 = 82; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_GENERALPURPOSECONTROLLER8 = 83; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_PORTAMENTOCONTROL = 84; // -127 SOURCE NOTE + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTS1DEPTH = 91; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTS2DEPTH = 92; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTS3DEPTH = 93; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTS4DEPTH = 94; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_EFFECTS5DEPTH = 95; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_DATAENTRYINC = 96; // N/A + static const unsigned char MIDI_CONTROLLERCHANGE_DATAENTRYDEC = 97; // N/A + static const unsigned char MIDI_CONTROLLERCHANGE_NONREGISTEREDPARAMNUMBER_LSB = 98; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_NONREGISTEREDPARAMNUMBER_MSB = 99; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_REGISTEREDPARAMETERNUMBER_LSB = 100; // 0-127 LSB + static const unsigned char MIDI_CONTROLLERCHANGE_REGISTEREDPARAMETERNUMBER_MSB = 101; // 0-127 MSB + static const unsigned char MIDI_CONTROLLERCHANGE_ALLSOUNDOFF = 120; + static const unsigned char MIDI_CONTROLLERCHANGE_RESETALLCONTROLLERS = 121; + static const unsigned char MIDI_CONTROLLERCHANGE_LOCALCONTROLONOFF = 122; + static const unsigned char MIDI_CONTROLLERCHANGE_ALLNOTESOFF = 123; + static const unsigned char MIDI_CONTROLLERCHANGE_OMNIMODEOFF = 124; + static const unsigned char MIDI_CONTROLLERCHANGE_OMNIMODEON = 125; + static const unsigned char MIDI_CONTROLLERCHANGE_POLYMODEONOFF = 126; + static const unsigned char MIDI_CONTROLLERCHANGE_POLYMODEON = 127; + + static const unsigned char MIDI_CONTROLLERCHANGE_RPN_PITCHBENDSENSITIVITY = 0; + + static const unsigned int MIDI_F_INSTRUMENT_DRUMS = 0x80000000; + + static const int MIDI_DEFAULTSUBCHANNELS = 32; + static const int MIDI_MINIMUMSAMPLEGRANULARITY = 512; // 10.66ms @ 48khz :S + + class CodecMIDIDLSCache : public LinkedListNode + { + public: + + char mFilePath[FMOD_STRING_MAXPATHLEN]; + SoundI *mDLSFile; + int mRefCount; + }; + + class CodecMIDISubChannel : public LinkedListNode + { + public: + + CodecMIDI *mMIDI; + ChannelI mChannel; + CodecDLSInstrument *mInstrument; + + float mLFOStartDelay; + float mLFOTime; + float mLFOFrequency; + CodecDLSEnvelope mVolumeEnvelope; + CodecDLSEnvelope mPitchEnvelope; + + int mCurrentNote; + SoundI *mSound; + unsigned char mKeyOnKey; + unsigned char mUnityNote; + int mFineTune; + unsigned char mKeyOnVelocity; + int mNumArticulators; + DLS_CONNECTIONBLOCK *mArticulator; + + float mTremoloScale; + float mVibratoScale; + float mPanbrelloScale; + + bool mKeyOff; + int mMiddleC; + int mKeyGroup; + float mSampleAttenuation; + CodecMIDIChannel *mParent; + + FMOD_RESULT displayArticulators(); + FMOD_RESULT findArticulator(int src, int dest); + FMOD_RESULT articulateDest(CONN_SRC_FLAGS srcflags, int dest, int *usDestination); + FMOD_RESULT setUpArticulators(); + FMOD_RESULT stop(); + FMOD_RESULT updateVolume(); + FMOD_RESULT updatePitch(); + FMOD_RESULT updatePan(); + + float getTimeCentsFromlScale(int lScale); + }; + + class CodecMIDIChannel : public LinkedListNode + { + public: + CodecMIDITrack *mTrack; + CodecMIDISubChannel mChannelHead; + unsigned char mIndex; + unsigned char mKeyOffVelocity; + unsigned char mKeyOnVelocity; + unsigned char mAfterTouchKey; + unsigned char mAfterTouchPressure; + unsigned int mBank; + unsigned char mProgram; + unsigned int mRPN; + unsigned char mChannelPressure; + float mMasterVolume; + int mPitchBend; + int mPitchBendSensitivity; + bool mDamperPedal; + + int mModWheel; // cc1 + int mVolume; // cc7 + int mPan; // cc10 + int mExpression; // cc11 + + FMOD_RESULT getSound(int key, SoundI **sound, CodecDLSInstrument **instrument, int *unitynote, int *finetune, int *attenuation, bool *duplicateallowed, int *keygroup, int *numarticulators, DLS_CONNECTIONBLOCK **articulators); + FMOD_RESULT process(unsigned char event, bool reuse, unsigned char runningdata, bool calledfromopen); + FMOD_RESULT update(); + }; + + class CodecMIDITrack + { + public: + + CodecMIDI *mMIDI; + + unsigned char *mData; + unsigned int mOffset; + unsigned int mLength; + int mIndex; + bool mReadDelta; + float mTick; + bool mFinished; + unsigned char mPort; + unsigned char mEvent; + + FMOD_RESULT readVarLen(unsigned int *result); + FMOD_RESULT readByte(unsigned char *result); + FMOD_RESULT readWord(unsigned short *result); + FMOD_RESULT readDWord(unsigned int *result); + FMOD_RESULT read(void *buff, int length); + FMOD_RESULT addTag(const char *name, int length, bool calledfromopen); + FMOD_RESULT process(bool calledfromopen); + }; + + + class CodecMIDI : public Codec + { + friend class CodecMIDIChannel; + friend class CodecMIDITrack; + + private: + + DSPI *mDSPHead; + ChannelPool *mChannelPool; + ChannelSoftware *mChannelSoftware; /* Array of FMOD Ex low level real channels. Each one of these lives in a ChannelI channel. */ + int mNumSubChannels; + ChannelGroupI mChannelGroup; + CodecMIDISubChannel *mMIDISubChannel; + CodecMIDIChannel mMIDIChannel[16]; + + bool mFinished; + + int mSequenceNumber; + unsigned int mMixerSamplesLeft; + unsigned int mMixerSamplesPerTick; + float mTimingScale; + unsigned int mPCMOffset; + unsigned int mDSPTick; + + int mNumTracks; + MIDI_FORMAT mMIDIFormat; + int mDivision; + unsigned char mSMTPOffsetHours; + unsigned char mSMTPOffsetMinutes; + unsigned char mSMTPOffsetSeconds; + unsigned char mSMTPOffsetFrames; + unsigned char mSMTPOffsetSubFrames; + + unsigned char mTimeSignatureNumerator; + unsigned char mTimeSignatureDenominator; + unsigned char mTimeSignatureMetronome; + unsigned char mTimeSignatureNotated32nds; + + unsigned char mKeySignatureSF; + unsigned char mKeySignatureMI; + + CodecMIDITrack *mTrack; + unsigned int mTempo; + float mTick; + float mMillisecondsPerTick; + float mMillisecondsPlayed; /* Only valid while loading */ + + CodecMIDIDLSCache *mDLSCache; + SoundI *mDLSSound; + CodecDLS *mDLS; + bool *mSampleInclusionList; + + FMOD_RESULT play(bool fromopen); + FMOD_RESULT readVarLen(unsigned int *length); + FMOD_RESULT update(bool audible); + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getMusicNumChannelsInternal(int *numchannels); + FMOD_RESULT setMusicChannelVolumeInternal(int channel, float volume); + FMOD_RESULT getMusicChannelVolumeInternal(int channel, float *volume); + + static CodecMIDIDLSCache gDLSCacheHead; + + public: + + CodecMIDISubChannel mChannelFreeListHead; + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK getMusicNumChannelsCallback(FMOD_CODEC_STATE *codec, int *numchannels); + static FMOD_RESULT F_CALLBACK setMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float volume); + static FMOD_RESULT F_CALLBACK getMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float *volume); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + + }; +} + +#endif /* FMOD_SUPPORT_MIDI */ + +#endif + diff --git a/src/fmod_codec_mod.cpp b/src/fmod_codec_mod.cpp new file mode 100755 index 0000000..79cc8ed --- /dev/null +++ b/src/fmod_codec_mod.cpp @@ -0,0 +1,2297 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MOD + +#include "fmod.h" + +#include "fmod_channel_software.h" +#include "fmod_codec_mod.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_file.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_string.h" + +#include +#include + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX modcodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecMOD::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecMOD::getDescriptionEx() +{ + FMOD_memset(&modcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + modcodec.name = "FMOD MOD Codec"; + modcodec.version = 0x00010100; + modcodec.timeunits = (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_MODORDER | FMOD_TIMEUNIT_MODROW | FMOD_TIMEUNIT_MODPATTERN); + modcodec.defaultasstream = 1; + modcodec.open = &CodecMOD::openCallback; + modcodec.close = &CodecMOD::closeCallback; + modcodec.read = &CodecMOD::readCallback; + modcodec.getlength = &MusicSong::getLengthCallback; + modcodec.setposition = &CodecMOD::setPositionCallback; + modcodec.getposition = &MusicSong::getPositionCallback; + + modcodec.getmusicnumchannels = &MusicSong::getMusicNumChannelsCallback; + modcodec.setmusicchannelvolume = &MusicSong::setMusicChannelVolumeCallback; + modcodec.getmusicchannelvolume = &MusicSong::getMusicChannelVolumeCallback; + + modcodec.mType = FMOD_SOUND_TYPE_MOD; + modcodec.mSize = sizeof(CodecMOD); + + return &modcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::calculateLength() +{ + waveformat[0].lengthpcm = 0; + + play(); + + while (!(mFinished)) + { + update(false); + + waveformat[0].lengthpcm += mMixerSamplesPerTick; + } + + stop(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +int CodecMOD::getAmigaPeriod(int note, int middlec) +{ + return (8363L * gPeriodTable[note-1] / middlec); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelMOD::portamento() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + /* + Slide pitch down if it needs to. + */ + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency += (mPortaSpeed << 2); + if (vcptr->mFrequency > mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + } + } + + /* + Slide pitch up if it needs to. + */ + if (vcptr->mFrequency > mPortaTarget) + { + vcptr->mFrequency -= (mPortaSpeed << 2); + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency=mPortaTarget; + } + } + + /* + if (glissando[track]) + { + } + */ + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + to carry out a vibrato at a certain depth and speed + + [PARAMETERS] + track - the track number to do the vibrato to + + [RETURN_VALUE] + + [REMARKS] + AND'ing temp with 31 removes the sign bit giving the abs value + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelMOD::vibrato() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (mVibPos & 31); + + switch (mWaveControl & 3) + { + case 0: delta = gSineTable[temp]; /* sine */ + break; + case 1: temp <<= 3; /* ramp down */ + if (mVibPos < 0) + { + temp=255-temp; + } + delta=temp; + break; + case 2: delta = 255; /* square */ + break; + case 3: delta = FMOD_RAND()&255; /* random */ + break; + default:delta = 0; + }; + + delta *= mVibDepth; + delta >>=7; + delta <<=2; /* we use 4*periods so make vibrato 4 times bigger */ + + if (mVibPos >= 0) + { + vcptr->mFrequencyDelta = delta; + } + else + { + vcptr->mFrequencyDelta = -delta; + } + + mVibPos += mVibSpeed; + if (mVibPos > 31) + { + mVibPos -= 64; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + To carry out a tremolo at a certain depth and speed + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelMOD::tremolo() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (mTremoloPosition & 31); + + switch((mWaveControl >> 4) & 3) + { + case 0: delta = gSineTable[temp]; /* sine */ + break; + case 1: temp <<= 3; /* ramp down */ + if (mTremoloPosition < 0) + { + temp=255-temp; + } + delta=temp; + break; + case 2: delta = 255; /* square */ + break; + case 3: delta = gSineTable[temp]; /* random (just use sine for now) */ + break; + default:delta = 0; + }; + + delta *= mTremoloDepth; + delta >>= 6; + + if (mTremoloPosition >= 0) + { + if (vcptr->mVolume + delta > 64) + { + delta = 64 - vcptr->mVolume; + } + vcptr->mVolumeDelta = delta; + } + else + { + if ((short)(vcptr->mVolume - delta) < 0) + { + delta = vcptr->mVolume; + } + vcptr->mVolumeDelta = delta; + } + + mTremoloPosition += mTremoloSpeed; + if (mTremoloPosition > 31) + { + mTremoloPosition -=64; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::updateNote(bool audible) +{ + MusicNote *current; + bool breakflag = false; + bool jumpflag = false; + int count; + + /* + Point our note pointer to the correct pattern buffer, and to the + correct offset in this buffer indicated by row and number of channels + */ + current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels); + if (!current) + { + return FMOD_OK; + } + + if (mVisited) + { + if (mVisited[(mOrder * FMUSIC_MAXROWS) + mRow]) + { + mFinished = true; + return FMOD_OK; + } + mVisited[(mOrder * FMUSIC_MAXROWS) + mRow] = true; + } + + /* + Loop through each channel in the row until we have finished + */ + for (count=0; count < mNumChannels; count++,current++) + { + MusicChannel *cptr = 0; + MusicVirtualChannel *vcptr = 0; + MusicSample *sptr = 0; + unsigned char paramx, paramy; + int oldvolume, oldfreq; + + paramx = current->mEffectParam >> 4; /* get effect param x */ + paramy = current->mEffectParam & 0xF; /* get effect param y */ + + cptr = mMusicChannel[count]; + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + /* + Store any instrument number + */ + if (current->mNumber) + { + cptr->mInstrument = current->mNumber - 1; /* remember the Instrument # */ + +// if (mInstCallback[current->mNumber] && mInstCallback[current->mNumber]->callback) +// { +// checkCallback(FMUSIC_CALLBACK_INSTRUMENT, current->mNumber); +// } + } + + /* + Set up sample pointer + */ + if (cptr->mInstrument < mNumSamples) + { + sptr = &mSample[cptr->mInstrument]; + } + else + { + sptr = &gDummySample; + } + + oldvolume = vcptr->mVolume; + oldfreq = vcptr->mFrequency; + + /* + If there is no more tremolo, set volume to volume + last tremolo delta + */ + if (cptr->mRecentEffect == FMUSIC_MOD_TREMOLO && current->mEffect != FMUSIC_MOD_TREMOLO) + { + vcptr->mVolume += vcptr->mVolumeDelta; + } + cptr->mRecentEffect = current->mEffect; + + vcptr->mVolumeDelta = 0; + vcptr->mNoteControl = 0; + + /* + PROCESS NOTE + */ + if (current->mNote) + { + vcptr->mNoteControl |= FMUSIC_STOP; + + if (vcptr == &gDummyVirtualChannel) + { + FMOD_RESULT result; + + result = spawnNewVirtualChannel(cptr, sptr, &vcptr); + if (result != FMOD_OK) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + } + + /* + Get period according to relative note, c2spd and finetune + */ + cptr->mNote = current->mNote; /* now remember the note */ + cptr->mPeriod = getAmigaPeriod(cptr->mNote, sptr->mMiddleC); + vcptr->mPan = mDefaultPan[count]; + + /* + Retrigger tremolo and vibrato waveforms + */ + if ((cptr->mWaveControl & 0xF) < 4) + { + cptr->mVibPos = 0; + } + if ((cptr->mWaveControl >> 4) < 4) + { + cptr->mTremoloPosition = 0; + } + + /* + Frequency only changes if there are no portamento effects + */ + if (current->mEffect != FMUSIC_MOD_PORTATO && current->mEffect != FMUSIC_MOD_PORTATOVOLSLIDE) + { + vcptr->mFrequency = cptr->mPeriod; + } + + vcptr->mNoteControl = FMUSIC_TRIGGER; + } + + /* + PROCESS INSTRUMENT NUMBER + */ + if (current->mNumber) + { + vcptr->mVolume = sptr->mDefaultVolume; + } + + vcptr->mFrequencyDelta = 0; + vcptr->mNoteControl |= FMUSIC_FREQ | FMUSIC_VOLUME | FMUSIC_PAN; + + /* + TICK 0 EFFECTS + */ + switch (current->mEffect) + { + /* + Not processed on tick 0 + */ + case FMUSIC_MOD_ARPEGGIO : + case FMUSIC_MOD_PORTAUP : + case FMUSIC_MOD_PORTADOWN : + case FMUSIC_MOD_VOLUMESLIDE : + { + break; + } + + case FMUSIC_MOD_SETVOLUME : + { + vcptr->mVolume = current->mEffectParam; + break; + } + case FMUSIC_MOD_PORTATO : + { + if (current->mEffectParam) + { + cptr->mPortaSpeed = current->mEffectParam; + } + cptr->mPortaTarget = cptr->mPeriod; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + break; + } + case FMUSIC_MOD_PORTATOVOLSLIDE : + { + cptr->mPortaTarget = cptr->mPeriod; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + break; + } + case FMUSIC_MOD_VIBRATO : + { + if (paramx) + { + cptr->mVibSpeed = paramx; + } + if (paramy) + { + cptr->mVibDepth = paramy; + } + } + case FMUSIC_MOD_VIBRATOVOLSLIDE : + { + break; /* not processed on tick 0 */ + } + case FMUSIC_MOD_TREMOLO : + { + if (paramx) + { + cptr->mTremoloSpeed = paramx; + } + if (paramy) + { + cptr->mTremoloDepth = paramy; + } + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + break; + } + case FMUSIC_MOD_SETPANPOSITION : + { + vcptr->mPan = current->mEffectParam << 1; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_MOD_SETSAMPLEOFFSET : + { + unsigned int offset; + + if (current->mEffectParam) + { + cptr->mSampleOffset = current->mEffectParam; + } + + offset = (int)(cptr->mSampleOffset) << 8; + + if (offset >= sptr->mLoopStart + sptr->mLoopLength) + { + vcptr->mSampleOffset = sptr->mLoopStart + sptr->mLoopLength - 1; + } + else + { + vcptr->mSampleOffset = offset; + } + break; + } + case FMUSIC_MOD_PATTERNJUMP : /* --- 00 B00 : --- 00 D63 , should put us at ord=0, row=63 */ + { + mNextOrder = current->mEffectParam; + mNextRow = 0; + + if (mNextOrder >= mNumOrders) + { + mNextOrder = 0; + } + + jumpflag = true; + break; + } + case FMUSIC_MOD_PATTERNBREAK : + { + mNextRow = (paramx*10) + paramy; + + if (mNextRow > 63) + { + mNextRow = 0; + } + if (!breakflag && !jumpflag) + { + mNextOrder = mOrder+1; + } + if (mNextOrder >= mNumOrders) + { + mNextOrder=0; + } + break; + } + case FMUSIC_MOD_SETSPEED : + { + if (current->mEffectParam < 0x20) + { + if (current->mEffectParam) + { + mSpeed = current->mEffectParam; + } + } + else + { + setBPM(current->mEffectParam); + } + break; + } + case FMUSIC_MOD_SPECIAL : + { + switch (paramx) + { + /* + Not processed on tick 0 / unsupported + */ + case FMUSIC_MOD_RETRIG : + case FMUSIC_MOD_NOTECUT : + case FMUSIC_MOD_SETFILTER : + case FMUSIC_MOD_FUNKREPEAT : + case FMUSIC_MOD_SETGLISSANDO : + { + break; + } + case FMUSIC_MOD_FINEPORTAUP : + { + vcptr->mFrequency -= (paramy << 2); + break; + } + case FMUSIC_MOD_FINEPORTADOWN : + { + vcptr->mFrequency += (paramy << 2); + break; + } + case FMUSIC_MOD_SETVIBRATOWAV : + { + cptr->mWaveControl &= 0xF0; + cptr->mWaveControl |= paramy; + break; + } + case FMUSIC_MOD_SETFINETUNE : + { + fineTune2Hz(paramy, &sptr->mMiddleC); + break; + } + case FMUSIC_MOD_PATTERNLOOP : + { + if (paramy == 0) + { + cptr->mPatternLoopRow = mRow; + } + else + { + if (!cptr->mPatternLoopNumber) + { + cptr->mPatternLoopNumber = paramy; + } + else + { + cptr->mPatternLoopNumber--; + } + if (cptr->mPatternLoopNumber) + { + int count2; + + mNextRow = cptr->mPatternLoopRow; + + if (mVisited) + { + for (count2 = cptr->mPatternLoopRow; count2 <= mRow; count2++) + { + mVisited[(mOrder * FMUSIC_MAXROWS) + count2] = false; + } + } + } + } + break; + } + case FMUSIC_MOD_SETTREMOLOWAV : + { + cptr->mWaveControl &= 0xF; + cptr->mWaveControl |= (paramy << 4); + break; + } + case FMUSIC_MOD_SETPANPOSITION16 : + { + vcptr->mPan = paramy << 4; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_MOD_FINEVOLUMESLIDEUP : + { + vcptr->mVolume += paramy; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + break; + } + case FMUSIC_MOD_FINEVOLUMESLIDEDOWN : + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + break; + } + case FMUSIC_MOD_NOTEDELAY : + { + vcptr->mVolume = oldvolume; + vcptr->mFrequency = oldfreq; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + vcptr->mNoteControl &= !FMUSIC_PAN; + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + break; + } + case FMUSIC_MOD_PATTERNDELAY : + { + mPatternDelay = paramy; + mPatternDelay *= mSpeed; + break; + } + }; + break; + } + }; + + if (audible) + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + + if (!(vcptr->mFrequency + vcptr->mFrequencyDelta)) + { + vcptr->mNoteControl &= ~FMUSIC_FREQ; /* divide by 0 check */ + } + + if (vcptr->mNoteControl & FMUSIC_TRIGGER) + { + playSound(sptr, vcptr, false); + } + + if (vcptr->mNoteControl & FMUSIC_VOLUME) + { + vcptr->mChannel.setVolume(((float)(vcptr->mVolume + vcptr->mVolumeDelta) / 64.0f) * 0.5f * cptr->mMasterVolume); + } + if (vcptr->mNoteControl & FMUSIC_PAN) + { + float finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation; + + vcptr->mChannel.setPan(finalpan / 128.0f); + } + if (vcptr->mNoteControl & FMUSIC_FREQ) + { + int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + + if (finalfreq < 1) + { + finalfreq = 1; + } + + vcptr->mChannel.setFrequency((float)period2HZ(finalfreq)); + } + if (vcptr->mNoteControl & FMUSIC_STOP) + { + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */ + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::updateEffects() +{ + MusicNote *current; + int count; + + current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels); + + if (!current) + { + return FMOD_OK; + } + + for (count=0; count < mNumChannels; count++,current++) + { + MusicChannelMOD *cptr = 0; + MusicVirtualChannel *vcptr = 0; + MusicSample *sptr = 0; + unsigned char effect, paramx, paramy; + + cptr = (MusicChannelMOD *)mMusicChannel[count]; + + if (cptr->mInstrument < mNumSamples) + { + sptr = &mSample[cptr->mInstrument]; + } + else + { + sptr = &gDummySample; + } + + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + effect = current->mEffect; /* grab the effect number */ + paramx = current->mEffectParam >> 4; /* grab the effect parameter x */ + paramy = current->mEffectParam & 0xF; /* grab the effect parameter y */ + + vcptr->mVolumeDelta = 0; /* this is for tremolo etc */ + vcptr->mFrequencyDelta = 0; /* this is for vibrato / arpeggio etc */ + vcptr->mNoteControl = 0; + + switch(effect) + { + case FMUSIC_MOD_ARPEGGIO : + { + if (current->mEffectParam > 0) + { + switch (mTick % 3) + { + case 1: + /* + BUGFIX : FMOD 3.3 arpeggio was wrong + */ + vcptr->mFrequencyDelta = getAmigaPeriod(cptr->mNote + paramx, sptr->mMiddleC) - getAmigaPeriod(cptr->mNote, sptr->mMiddleC); + break; + case 2: + /* + BUGFIX : FMOD 3.3 arpeggio was wrong + */ + vcptr->mFrequencyDelta = getAmigaPeriod(cptr->mNote + paramy, sptr->mMiddleC) - getAmigaPeriod(cptr->mNote, sptr->mMiddleC); + break; + }; + vcptr->mNoteControl |= FMUSIC_FREQ; + } + break; + } + case FMUSIC_MOD_PORTAUP : + { + vcptr->mFrequency -= (current->mEffectParam << 2); /* subtract freq */ + if (vcptr->mFrequency < 56) + { + vcptr->mFrequency=56; /* stop at B#8 */ + } + vcptr->mNoteControl |= FMUSIC_FREQ; + break; + } + case FMUSIC_MOD_PORTADOWN : + { + vcptr->mFrequency += (current->mEffectParam << 2); + vcptr->mNoteControl |= FMUSIC_FREQ; + break; + } + case FMUSIC_MOD_PORTATO : + { + cptr->portamento(); + break; + } + case FMUSIC_MOD_VIBRATO : + { + cptr->vibrato(); + break; + } + case FMUSIC_MOD_PORTATOVOLSLIDE : + { + cptr->portamento(); + + /* + Slide up takes precedence over down + */ + if (paramx) + { + vcptr->mVolume += paramx; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + } + else if (paramy) + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_MOD_VIBRATOVOLSLIDE : + { + cptr->vibrato(); + + /* + Slide up takes precedence over down + */ + if (paramx) + { + vcptr->mVolume += paramx; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + } + else if (paramy) + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_MOD_TREMOLO : + { + cptr->tremolo(); + break; + } + case FMUSIC_MOD_VOLUMESLIDE : + { + /* + Slide up takes precedence over down + */ + if (paramx) + { + vcptr->mVolume += paramx; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + } + else if (paramy) + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + + /* + Extended PT effects + */ + case FMUSIC_MOD_SPECIAL: + { + switch (paramx) + { + case FMUSIC_MOD_NOTECUT: + { + if (mTick==paramy) + { + vcptr->mVolume = 0; + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + break; + } + case FMUSIC_MOD_RETRIG : + { + if (!paramy) + { + break; /* divide by 0 bugfix */ + } + + if (!(mTick % paramy)) + { + vcptr->mNoteControl |= FMUSIC_TRIGGER; + vcptr->mNoteControl |= FMUSIC_VOLUME; + vcptr->mNoteControl |= FMUSIC_PAN; + } + + break; + } + case FMUSIC_MOD_NOTEDELAY : + { + if (mTick == paramy) + { + if (vcptr == &gDummyVirtualChannel) + { + FMOD_RESULT result; + result = spawnNewVirtualChannel(cptr, sptr, &vcptr); + if (result != FMOD_OK) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + } + + if (current->mNumber) + { + vcptr->mVolume = sptr->mDefaultVolume; + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + vcptr->mPan = mDefaultPan[count]; + vcptr->mFrequency = cptr->mPeriod; + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mNoteControl |= FMUSIC_PAN; + vcptr->mNoteControl |= FMUSIC_TRIGGER; + } + else + { + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + } + break; + } + }; + break; + } + }; + + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + + if (!(vcptr->mFrequency + vcptr->mFrequencyDelta)) + { + vcptr->mNoteControl &= ~FMUSIC_FREQ; /* divide by 0 check */ + } + + if (vcptr->mNoteControl & FMUSIC_TRIGGER) + { + playSound(sptr, vcptr, false); + } + + if (vcptr->mNoteControl & FMUSIC_VOLUME) + { + vcptr->mChannel.setVolume(((float)(vcptr->mVolume + vcptr->mVolumeDelta) / 64.0f) * 0.5f * cptr->mMasterVolume); + } + if (vcptr->mNoteControl & FMUSIC_PAN) + { + float finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation; + + vcptr->mChannel.setPan(finalpan / 128.0f); + } + if (vcptr->mNoteControl & FMUSIC_FREQ) + { + int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + + if (finalfreq < 1) + { + finalfreq = 1; + } + + vcptr->mChannel.setFrequency((float)period2HZ(finalfreq)); + } + if (vcptr->mNoteControl & FMUSIC_STOP) + { + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */ + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::update(bool audible) +{ + if (mTick == 0) /* new note */ + { + if (mFinished && !mLooping) + { + stop(); + } + else + { + /* + Process any commands to set the next order/row from previous row + */ + if (mNextOrder >= 0) + { + mOrder = mNextOrder; +// checkCallback(FMUSIC_CALLBACK_ORDER, (unsigned char)mOrder); + if (mNextOrder >= 0) + { + mOrder = mNextOrder; + } + mNextOrder = -1; + } + if (mNextRow >= 0) + { + mRow = mNextRow; +// checkCallback(FMUSIC_CALLBACK_ROW, (unsigned char)mRow); + if (mNextRow >= 0) + { + mRow = mNextRow; + } + mNextRow = -1; + } + + updateNote(audible); /* Update and play the note */ + + /* + If there were no row commands + */ + if (mNextRow == -1) + { + mNextRow = mRow+1; + if (mNextRow >= 64) /* if end of pattern */ + { + mNextOrder = mOrder+1; /* so increment the order */ + + if (mNextOrder >= mNumOrders) + { + mNextOrder = mRestart; + } + + mNextRow = 0; /* start at top of pattern */ + } + } + } + } + else if (audible) + { + updateEffects(); /* Else update the inbetween row effects */ + } + + if (mSpeed) + { + mTick++; + if (mTick >= mSpeed + mPatternDelay) + { + mPatternDelay = 0; + mTick = 0; + } + } + else + { + mFinished = true; + mTick = -1; + } + + mPCMOffset += mMixerSamplesPerTick; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + int count; + char temp[4]; + unsigned int lengthbytes; + + if (!(mFile->mFlags & FMOD_FILE_SEEKABLE)) + { + return FMOD_ERR_FORMAT; + } + + init(FMOD_SOUND_TYPE_MOD); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMOD::openInternal", "attempting to open as MOD..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get size of file in bytes + */ + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + mFile->setBigEndian(true); + + /* + Verify Format + */ + result = mFile->seek(1080, SEEK_SET); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + MOD signature (M.K. etc),... + */ + result = mFile->read(temp, 1, 4, 0); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + Protracker mods && ft2 multichannel .mod's + */ + if (FMOD_strncmp((char *)temp, "M.K.", 4) && + FMOD_strncmp((char *)temp, "M!K!", 4) && + FMOD_strncmp((char *)temp, "6CHN", 4) && + FMOD_strncmp((char *)temp, "8CHN", 4) && + FMOD_strncmp((char *)temp+2, "CH", 2) && + FMOD_strncmp((char *)temp+1, "CHN", 3)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMOD::openInternal", "'M.K.' etc ID check failed [%c%c%c%c]\n", temp[0], temp[1], temp[2], temp[3])); + + mFile->setBigEndian(false); + return FMOD_ERR_FORMAT; + } + + /* + Protracker mods + */ + if (!FMOD_strncmp(temp, "M.K.", 4)) + { + mNumChannels = 4; + } + else if (!FMOD_strncmp(temp, "M!K!", 4)) + { + mNumChannels = 4; + } + else if (!FMOD_strncmp(temp, "FLT4", 4)) + { + mNumChannels = 4; + } + + /* + FT1 multichannel mods + */ + else if (!FMOD_strncmp(temp, "6CHN", 4)) + { + mNumChannels = 6; + } + else if (!FMOD_strncmp(temp, "8CHN", 4)) + { + mNumChannels = 8; + } + + /* + FT2 multichannel .mod's + */ + else if (!FMOD_strncmp(temp+2, "CH", 2)) + { + temp[3] = 0; /* null terminate this string */ + mNumChannels = atoi(temp); + } + else if (!FMOD_strncmp(temp+1, "CHN", 3)) + { + mNumChannels = (temp[0]-'0'); + } + else + { + mNumChannels = 0; + } + + if (mNumChannels < 1 || mNumChannels > 32) + { + mFile->setBigEndian(false); + return FMOD_ERR_FORMAT; + } + + result = metaData(FMOD_TAGTYPE_FMOD, "Number of channels", &mNumChannels, sizeof(mNumChannels), FMOD_TAGDATATYPE_INT, false); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + + /* + Set a few default values for this format + */ + for (count = 0; count < MUSIC_MAXCHANNELS; count++) + { + mMusicChannel[count] = 0; + } + + mPattern = 0; + mPanSeparation = 0.8f; + mMasterSpeed = 1.0f; + mLooping = true; + + mNumSamples = MOD_MAXSAMPLES; + mDefaultSpeed = 6; + mDefaultBPM = 125; + mNumPatterns = 0; /* currently 0, to be set later. */ + mRestart = 0; + + /* + Start at the beginning of the file and read in module name. + */ + result = mFile->read(mSongName, 1, 20, 0); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + Load instrument headers + */ + for (count = 0; count < mNumSamples; count++) + { + unsigned int count2; + char sample_name[22]; + FMOD_MODE sample_mode; + unsigned int sample_length; + unsigned int sample_loopstart; + unsigned int sample_looplength; + unsigned char sample_volume; + + FMOD_memset(&mSample[count], 0, sizeof(MusicSample)); + + /* + Read sample name, and fix up bad signed characters by making them spaces + */ + result = mFile->read(sample_name, 1, 22, 0); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + for (count2 = 0; count2 < 22; count2++) + { + if (sample_name[count2] < 32) + { + sample_name[count2] = 0; + } + } + + { + char s[256]; + + sprintf(s, "Sample name %d", count); + + result = metaData(FMOD_TAGTYPE_FMOD, s, sample_name, 22, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + } + + sample_mode = FMOD_SOFTWARE | FMOD_2D; + + /* + Read rest of information. + */ + result = mFile->getWord(&sample_length); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + sample_length *= 2; + + result = mFile->getByte(&mSample[count].mMiddleC); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + fineTune2Hz(mSample[count].mMiddleC, &mSample[count].mMiddleC); + + result = mFile->getByte(&sample_volume); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + mSample[count].mDefaultVolume = sample_volume; + + result = mFile->getWord(&sample_loopstart); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + if (sample_loopstart * 2 < sample_length) + { + sample_loopstart *= 2; + } + + result = mFile->getWord(&sample_looplength); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + sample_looplength *= 2; + + if (sample_loopstart + sample_looplength > sample_length) + { + sample_looplength = sample_length - sample_loopstart; + } + + if (sample_looplength > 2) + { + sample_mode |= FMOD_LOOP_NORMAL; + } + else + { + sample_mode |= FMOD_LOOP_OFF; + sample_loopstart = 0; + sample_looplength = sample_length; + } + + /* + ALLOCATE MEMORY FOR THE SAMPLE BUFFER + */ + if (sample_length) + { + FMOD_CREATESOUNDEXINFO exinfo; + + FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.length = sample_length; + exinfo.numchannels = 1; + exinfo.defaultfrequency = mSample[count].mMiddleC; + exinfo.format = FMOD_SOUND_FORMAT_PCM8; + + result = mSystem->createSound(0, sample_mode | FMOD_OPENUSER, &exinfo, &mSample[count].mSound); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + if (sample_mode & FMOD_LOOP_NORMAL) + { + result = mSample[count].mSound->setLoopPoints(sample_loopstart, FMOD_TIMEUNIT_PCM, sample_loopstart + sample_looplength - 1, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + } + mSample[count].mLoopStart = sample_loopstart; + mSample[count].mLoopLength = sample_looplength; + } + } + + /* + Load order data + */ + + result = mFile->getByte(&mNumOrders); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + Restart pos. Usually 127 so it isnt reliable + */ + result = mFile->getByte(); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + Read in 128 orders worth of pattern numbers. + */ + FMOD_memset(mOrderList, 0, MUSIC_MAXORDERS); + result = mFile->read(mOrderList, 1, 128, 0); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + for (count = 0; count < 128; count++) + { + if (mOrderList[count] > mNumPatterns) + { + mNumPatterns = mOrderList[count]; + } + } + mNumPatterns++; + + /* + MOD signature (M.K. etc) + */ + result = mFile->getDword(); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + ALLOCATE MUSIC CHANNELS + */ + for (count = 0; count < mNumChannels; count++) + { + mMusicChannel[count] = FMOD_Object_Calloc(MusicChannelMOD); + if (!mMusicChannel[count]) + { + mFile->setBigEndian(false); + return FMOD_ERR_MEMORY; + } + } + + /* + Set pan values for the mod format + */ + for (count = 0; count < mNumChannels; count++) + { + mDefaultPan[count] = (((count + 1) & 2) == 0) ? 0 : 255; + } + + /* + Alloc pattern array + */ + mNumPatternsMem = mNumPatterns; + mPattern = (MusicPattern *)FMOD_Memory_Calloc(mNumPatternsMem * sizeof(MusicPattern)); + if (!mPattern) + { + mFile->setBigEndian(false); + return FMOD_ERR_MEMORY; + } + + /* + Load pattern data + */ + for (count = 0; count < mNumPatterns; count++) + { + MusicPattern *pptr; + MusicNote *nptr; + int count2; + unsigned short period; + + pptr = &mPattern[count]; + pptr->mRows = 64; + + /* + Allocate memory for pattern buffer + */ + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); + if (!pptr->mData) + { + mFile->setBigEndian(false); + return FMOD_ERR_MEMORY; + } + + nptr = pptr->mData; + + /* + Read the pattern data in + */ + for (count2 = 0; count2 < (mNumChannels * pptr->mRows); count2++) + { + unsigned int count3; + + /* + Load up 4 bytes of note information from file + */ + result = mFile->read(temp, 1, 4, 0); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + /* + Store sample number + */ + nptr->mNumber = (((unsigned char)temp[0] & 0xF0) + ((unsigned char)temp[2] >> 4)); + + /* + Store note + */ + period = (((unsigned char)temp[0] & 0xF) << 8) + (unsigned char)temp[1]; + + /* + Convert the amiga period to a note number + */ + nptr->mNote = 0; + + for (count3=0; count3 < 108; count3++) + { + if (period >= gPeriodTable[count3+24]) + { + nptr->mNote = (unsigned char)count3+1; + break; + } + } + + /* + Store volume byte (mods dont have one so NOVOL = empty) + */ + nptr->mVolume = 0; + + /* + Store effects and arguments + */ + nptr->mEffect = (unsigned char)temp[2] & 0xF; /* Effect number */ + nptr->mEffectParam = (unsigned char)temp[3]; /* parameter */ + + nptr++; + } + } + + +#if 1 + /* + BUGFIX : FMOD 3.3 + Seek to the end of the file minus the sum of all the samples. + This was done because some mods specify more patterns than they actually contain!!!! + */ + { + unsigned int totallen = 0, curroffset, filelen; + + result = mFile->tell(&curroffset); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + result = mFile->getSize(&filelen); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + for (count=0; count < mNumSamples; count++) + { + MusicSample *sptr; + + sptr = &mSample[count]; + + if (sptr->mSound) + { + totallen += sptr->mSound->mLength; + } + } + + /* + Some sort of safety check i guess + */ + if (filelen - totallen > 1080 && (filelen < (curroffset + totallen))) /* BUFIX - FMOD 3.70. Only do it if the filelen is smaller than the expected size */ + { + result = mFile->seek(filelen - totallen, SEEK_SET); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + } + } +#endif + + /* + Load sample data + */ + for (count = 0; count < mNumSamples; count++) + { + MusicSample *sptr; + + sptr = &mSample[count]; + + if (sptr->mSound) + { + void *ptr1, *ptr2; + unsigned int len1, len2, lenbytes; + + sptr->mSound->getBytesFromSamples(sptr->mSound->mLength, &lenbytes); + + result = sptr->mSound->lock(0, lenbytes, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + + if (ptr1 && len1) + { + result = mFile->read(ptr1, 1, len1, 0); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + mFile->setBigEndian(false); + return result; + } + } + + result = sptr->mSound->unlock(ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + mFile->setBigEndian(false); + return result; + } + } + } + + mFile->setBigEndian(false); + + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + + waveformat = mWaveFormatMemory; + waveformat[0].lengthbytes = lengthbytes; + + /* + Set up general codec parameters. + */ + if (userexinfo && userexinfo->format != FMOD_SOUND_FORMAT_NONE) + { + waveformat[0].format = userexinfo->format; + } + #ifndef PLATFORM_PS3 + else if (usermode & FMOD_SOFTWARE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + #endif + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + waveformat[0].channels = 2; + FMOD_strncpy(waveformat[0].name, mSongName, FMOD_STRING_MAXNAMELEN); + + result = mSystem->getSoftwareFormat(&waveformat[0].frequency, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, waveformat[0].channels, waveformat[0].format); + + /* + Create a head unit that software channels connect to. + */ + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD MOD Target Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = waveformat[0].channels; + descriptionex.mFormat = waveformat[0].format; + descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + + result = mSystem->createDSP(&descriptionex, &mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + mDSPHead->mDefaultFrequency = (float)waveformat[0].frequency; + } + + /* + Create a pool of virtual channels. + */ + { + mNumVirtualChannels = mNumChannels; + mVirtualChannel = (MusicVirtualChannel *)FMOD_Memory_Calloc(sizeof(MusicVirtualChannel) * mNumVirtualChannels); + if (!mVirtualChannel) + { + return FMOD_ERR_MEMORY; + } + for (count = 0; count < mNumVirtualChannels; count++) + { + new (&mVirtualChannel[count]) MusicVirtualChannel; + } + } + + /* + Create a pool of real channels. 2x the number of virtual channels for double buffer channel usage. + */ + { + int numrealchannels = mNumVirtualChannels * 2; + + mChannelPool = FMOD_Object_Calloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, 0, numrealchannels); + if (result != FMOD_OK) + { + return result; + } + + mChannelSoftware = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * numrealchannels); + if (!mChannelSoftware) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < numrealchannels; count++) + { + new (&mChannelSoftware[count]) ChannelSoftware; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannelSoftware[count], mDSPHead)); + CHECK_RESULT(mChannelSoftware[count].allowReverb(false)); + } + } + + if ((usermode & FMOD_ACCURATETIME) || (usermode & FMOD_CREATESAMPLE)) + { + mVisited = (bool *)FMOD_Memory_Calloc(sizeof(bool) * mNumOrders * FMUSIC_MAXROWS); + if (!mVisited) + { + return FMOD_ERR_MEMORY; + } + + calculateLength(); + } + else + { + mVisited = 0; + waveformat[0].lengthpcm = (unsigned int)-1; /* Thanks to those stupid comment markers! */ + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + play(true); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::closeInternal() +{ + int count; + + stop(); + + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + + if (mDSPHead) + { + mDSPHead->release(); + mDSPHead = 0; + } + + for (count = 0; count < mNumSamples; count++) + { + if (mSample[count].mSound) + { + mSample[count].mSound->release(); + mSample[count].mSound = 0; + } + } + + if (mVirtualChannel) + { + FMOD_Memory_Free(mVirtualChannel); + mVirtualChannel = 0; + } + + if (mChannelSoftware) + { + FMOD_Memory_Free(mChannelSoftware); + mChannelSoftware = 0; + } + + if (mPattern) + { + for (count = 0; count < mNumPatternsMem; count++) + { + if (mPattern[count].mData) + { + FMOD_Memory_Free(mPattern[count].mData); + mPattern[count].mData = 0; + } + } + + FMOD_Memory_Free(mPattern); + mPattern = 0; + } + + for (count = 0; count < mNumChannels; count++) + { + if (mMusicChannel[count]) + { + FMOD_Memory_Free(mMusicChannel[count]); + mMusicChannel[count] = 0; + } + } + + if (mVisited) + { + FMOD_Memory_Free(mVisited); + mVisited = 0; + } + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int numsamples; + int numchannels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + numchannels = waveformat[0].channels; + + SoundI::getSamplesFromBytes(sizebytes, &numsamples, numchannels, waveformat[0].format); + + if (mPlaying && mMasterSpeed) + { + unsigned int mixedsofar = 0; + unsigned int mixedleft = mMixerSamplesLeft; + unsigned int samplestomix; + char *destptr; + + /* + Keep resetting the mix pointer to the beginning of this portion of the ring buffer + */ + destptr = (char *)buffer; + + while (mixedsofar < numsamples) + { + unsigned int read, bytes; + + if (!mixedleft) + { + result = update(true); + if (result != FMOD_OK) + { + return result; + } + + samplestomix = mMixerSamplesPerTick; + mixedleft = samplestomix; + } + else + { + samplestomix = mixedleft; + } + + if (mixedsofar + samplestomix > numsamples) + { + samplestomix = numsamples - mixedsofar; + } + + read = samplestomix; + + criticalsection.enter(); + if (buffer) + { + result = mDSPHead->read(destptr, &read, FMOD_SPEAKERMODE_STEREO_LINEAR, 2, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + mDSPTick++; + } + + SoundI::getBytesFromSamples(read, &bytes, numchannels, waveformat[0].format); + + /* + Buffer returned from the DSP head execute may not be the one we sent in (it may be + one of the global buffers). Don't leave mDSPCrit until after we have copied data out + */ + criticalsection.leave(); + + mixedsofar += read; + destptr += bytes; + mixedleft -= read; + } + + mMixerSamplesLeft = mixedleft; + } + + if (bytesread) + { + *bytesread = sizebytes; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMOD::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + if (postype == FMOD_TIMEUNIT_MODORDER) + { + play(); + mNextOrder = mOrder = position; + + return FMOD_OK; + } + else if (postype == FMOD_TIMEUNIT_PCM) + { + bool restarted = false; + + if (position == mPCMOffset) + { + return FMOD_OK; + } + + /* + Want to seek backwards, start from the start. + */ + if (position < mPCMOffset) + { + play(); + restarted = true; + } + + while (mPCMOffset < position) + { + update(true); + } + + if (restarted) + { + bool oldplaying = mPlaying; + bool oldfinished = mFinished; + + stop(); + + mPlaying = oldplaying; + mFinished = oldfinished; + } + + return FMOD_OK; + } + + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMOD::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecMOD *cmod = (CodecMOD *)codec; + + return cmod->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMOD::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecMOD *cmod = (CodecMOD *)codec; + + return cmod->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMOD::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecMOD *cmod = (CodecMOD *)codec; + + return cmod->readInternal(buffer, sizebytes, bytesread); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMOD::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype ) +{ + CodecMOD *cmod = (CodecMOD *)codec; + + return cmod->setPositionInternal(subsound, position, postype); +} +} + +#endif + diff --git a/src/fmod_codec_mod.h b/src/fmod_codec_mod.h new file mode 100755 index 0000000..f43f4b7 --- /dev/null +++ b/src/fmod_codec_mod.h @@ -0,0 +1,99 @@ +#ifndef _FMOD_CODEC_MOD_H +#define _FMOD_CODEC_MOD_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MOD + +#include "fmod_music.h" + +namespace FMOD +{ + class DSPI; + class ChannelPool; + + typedef enum + { + FMUSIC_MOD_ARPEGGIO, + FMUSIC_MOD_PORTAUP, + FMUSIC_MOD_PORTADOWN, + FMUSIC_MOD_PORTATO, + FMUSIC_MOD_VIBRATO, + FMUSIC_MOD_PORTATOVOLSLIDE, + FMUSIC_MOD_VIBRATOVOLSLIDE, + FMUSIC_MOD_TREMOLO, + FMUSIC_MOD_SETPANPOSITION, + FMUSIC_MOD_SETSAMPLEOFFSET, + FMUSIC_MOD_VOLUMESLIDE, + FMUSIC_MOD_PATTERNJUMP, + FMUSIC_MOD_SETVOLUME, + FMUSIC_MOD_PATTERNBREAK, + FMUSIC_MOD_SPECIAL, + FMUSIC_MOD_SETSPEED + } MODCOMMANDS; + + typedef enum + { + FMUSIC_MOD_SETFILTER, + FMUSIC_MOD_FINEPORTAUP, + FMUSIC_MOD_FINEPORTADOWN, + FMUSIC_MOD_SETGLISSANDO, + FMUSIC_MOD_SETVIBRATOWAV, + FMUSIC_MOD_SETFINETUNE, + FMUSIC_MOD_PATTERNLOOP, + FMUSIC_MOD_SETTREMOLOWAV, + FMUSIC_MOD_SETPANPOSITION16, + FMUSIC_MOD_RETRIG, + FMUSIC_MOD_FINEVOLUMESLIDEUP, + FMUSIC_MOD_FINEVOLUMESLIDEDOWN, + FMUSIC_MOD_NOTECUT, + FMUSIC_MOD_NOTEDELAY, + FMUSIC_MOD_PATTERNDELAY, + FMUSIC_MOD_FUNKREPEAT + } MODCOMMANDSSPECIAL; + + const int MOD_MAXROWS = 64; + const int MOD_MAXSAMPLES = 31; + + class MusicChannelMOD : public MusicChannel + { + public: + FMOD_RESULT portamento(); + FMOD_RESULT vibrato(); + FMOD_RESULT tremolo(); + }; + + class CodecMOD : public MusicSong + { + private: + + MusicSample mSample[MOD_MAXSAMPLES]; + + FMOD_RESULT calculateLength(); + inline int getAmigaPeriod(int note, int middlec); + + FMOD_RESULT updateNote(bool audible); + FMOD_RESULT updateEffects(); + FMOD_RESULT update(bool audible); + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + + }; +} + +#endif /* FMOD_SUPPORT_MOD */ + +#endif + diff --git a/src/fmod_codec_mpeg.cpp b/src/fmod_codec_mpeg.cpp new file mode 100755 index 0000000..ba6cd2e --- /dev/null +++ b/src/fmod_codec_mpeg.cpp @@ -0,0 +1,1683 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MPEG + +#include "fmod.h" +#include "fmod_codec_mpeg.h" +#include "fmod_codec_wav.h" +#include "fmod_debug.h" +#include "fmod_dsp_codec.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_soundi.h" +#include "fmod_string.h" +#include "fmod_types.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_common_spu.h" + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" + #include + #include + #include "../lib/sony/spu/Mp3DecSpuLib.h" +#else + #include "fmod_systemi.h" +#endif + +#include +#include + +#ifdef FMOD_SUPPORT_MPEG_SPU + #include + #include "fmod_output_ps3.h" + #include "fmod_common_spu.h" +#endif + +namespace FMOD +{ + +FMOD_CODEC_DESCRIPTION_EX mpegcodec; + +#if defined(PLUGIN_EXPORTS) && !defined(PLUGIN_FSB) + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecMPEG::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifdef __cplusplus +extern "C" { +#endif + float *FMOD_Mpeg_DecWin = 0; +#ifdef __cplusplus +} +#endif + +/* + Globals +*/ + +#ifdef FMOD_SUPPORT_MPEG_SPU +FMOD_OS_CRITICALSECTION *gDecodeCrit = 0; +#endif + +bool CodecMPEG::gInitialized = false; + +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +float CodecMPEG::gDecWinMem[512+32+16]; /* +16 for alignment */ +float CodecMPEG::gCos64[16]; +float CodecMPEG::gCos32[8]; +float CodecMPEG::gCos16[4]; +float CodecMPEG::gCos8[2]; +float CodecMPEG::gCos4[1]; +float *CodecMPEG::gPnts[5] = { gCos64,gCos32,gCos16,gCos8,gCos4 }; + +int CodecMPEG::gIntWinBase[] = +{ + 0, -1, -1, -1, -1, -1, -1, -2, -2, -2, + -2, -3, -3, -4, -4, -5, -5, -6, -7, -7, + -8, -9, -10, -11, -13, -14, -16, -17, -19, -21, + -24, -26, -29, -31, -35, -38, -41, -45, -49, -53, + -58, -63, -68, -73, -79, -85, -91, -97, -104, -111, + -117, -125, -132, -139, -147, -154, -161, -169, -176, -183, + -190, -196, -202, -208, -213, -218, -222, -225, -227, -228, + -228, -227, -224, -221, -215, -208, -200, -189, -177, -163, + -146, -127, -106, -83, -57, -29, 2, 36, 72, 111, + 153, 197, 244, 294, 347, 401, 459, 519, 581, 645, + 711, 779, 848, 919, 991, 1064, 1137, 1210, 1283, 1356, + 1428, 1498, 1567, 1634, 1698, 1759, 1817, 1870, 1919, 1962, + 2001, 2032, 2057, 2075, 2085, 2087, 2080, 2063, 2037, 2000, + 1952, 1893, 1822, 1739, 1644, 1535, 1414, 1280, 1131, 970, + 794, 605, 402, 185, -45, -288, -545, -814, -1095, -1388, + -1692, -2006, -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788, + -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, -7910, -8209, + -8491, -8755, -8998, -9219, -9416, -9585, -9727, -9838, -9916, -9959, + -9966, -9935, -9863, -9750, -9592, -9389, -9139, -8840, -8492, -8092, + -7640, -7134, -6574, -5959, -5288, -4561, -3776, -2935, -2037, -1082, + -70, 998, 2122, 3300, 4533, 5818, 7154, 8540, 9975, 11455, + 12980, 14548, 16155, 17799, 19478, 21189, 22929, 24694, 26482, 28289, + 30112, 31947, 33791, 35640, 37489, 39336, 41176, 43006, 44821, 46617, + 48390, 50137, 51853, 53534, 55178, 56778, 58333, 59838, 61289, 62684, + 64019, 65290, 66494, 67629, 68692, 69679, 70590, 71420, 72169, 72835, + 73415, 73908, 74313, 74630, 74856, 74992, 75038 }; + +#endif + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecMPEG::getDescriptionEx() +{ + FMOD_memset(&mpegcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + mpegcodec.name = "FMOD MPEG Codec"; + mpegcodec.version = 0x00010100; + mpegcodec.timeunits = FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_RAWBYTES; + mpegcodec.open = &CodecMPEG::openCallback; + mpegcodec.close = &CodecMPEG::closeCallback; + mpegcodec.read = &CodecMPEG::readCallback; + mpegcodec.setposition = &CodecMPEG::setPositionCallback; + mpegcodec.soundcreate = &CodecMPEG::soundCreateCallback; + mpegcodec.reset = &CodecMPEG::resetCallback; + + mpegcodec.mType = FMOD_SOUND_TYPE_MPEG; + mpegcodec.mSize = sizeof(CodecMPEG); + + return &mpegcodec; +} + +#endif // !PLATFORM_PS3_SPU + +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::makeTables(int scaleval) +{ + int i,j,k,kr,divv; + float *table,*costab; + + for(i=0;i<5;i++) + { + kr=0x10>>i; divv=0x40>>i; + costab = gPnts[i]; + + for(k=0;ktell(&oldpos); + if (result != FMOD_OK) + { + return result; + } + + mMemoryBlock->mNumFrames = 0; + waveformat[0].lengthpcm = 0; + + do + { + unsigned int header; + int framesize; + + if (byteoffset >= waveformat[0].lengthbytes) + { + break; + } + + result = mFile->read(&header, 1, 4, 0); + if (result != FMOD_OK) + { + break; + } + + result = decodeHeader(&header, 0, 0, &framesize); + if (result == FMOD_OK && byteoffset + framesize < waveformat[0].lengthbytes) + { + if (frames >= mMemoryBlock->mNumFrames) + { + mMemoryBlock->mNumFrames += 1000; + mMemoryBlock->mFrameOffset = (unsigned int *)FMOD_Memory_ReAlloc(mMemoryBlock->mFrameOffset, mMemoryBlock->mNumFrames * sizeof(unsigned int)); + if (!mMemoryBlock->mFrameOffset) + { + return FMOD_ERR_MEMORY; + } + } + mMemoryBlock->mFrameOffset[frames] = byteoffset; + + waveformat[0].lengthpcm += mPCMFrameLengthBytes; + byteoffset += (framesize + 4); + frames++; + + result = mFile->seek(framesize, SEEK_CUR); + if (result != FMOD_OK) + { + break; + } + } + else + { + mFile->seek(-3, SEEK_CUR); /* increment a byte at a time! */ + } + } while (1); + + result = mFile->seek(oldpos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + mMemoryBlock->mNumFrames = frames; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + char header[4]; + unsigned int count; + int framesize, channels, frequency; + bool validheader; + bool manualsizecalc = false; + unsigned int lengthbytes = 0; + + init(FMOD_SOUND_TYPE_MPEG); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMPEG::openInternal", "attempting to open as MPEG..\n")); + + mSrcDataOffset = 0; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Support RIFF wrapped MP3? + */ + { + CodecWav tempwav; + WAVE_CHUNK chunk; + FMOD_CODEC_WAVEFORMAT tempwaveformat; + + FMOD_memset(&tempwav, 0, sizeof(CodecWav)); + FMOD_memset(&tempwaveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + + tempwav.mFile = mFile; + tempwav.mSrcDataOffset = (unsigned int)-1; + tempwav.waveformat = &tempwaveformat; + + /* + Read header + */ + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp((const char *)chunk.id, "RIFF", 4)) + { + char wave[4]; + + result = mFile->read(wave, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp(wave, "WAVE", 4)) + { + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + result = tempwav.parseChunk(chunk.size); + if (result == FMOD_OK && tempwav.mSrcFormat && tempwav.mSrcDataOffset != (unsigned int)-1) + { + int format = tempwav.mSrcFormat->Format.wFormatTag; + + if (format == WAVE_FORMAT_MPEG || format == WAVE_FORMAT_MPEGLAYER3) + { + mSrcDataOffset = tempwav.mSrcDataOffset; + lengthbytes = tempwav.waveformat[0].lengthbytes; + mLoopPoints[0] = tempwav.mLoopPoints[0]; + mLoopPoints[1] = tempwav.mLoopPoints[1]; + mSyncPoint = tempwav.mSyncPoint; + mNumSyncPoints = tempwav.mNumSyncPoints; + } + else + { + result = FMOD_ERR_FORMAT; + } + + if (tempwav.mSrcFormat) + { + FMOD_Memory_Free(tempwav.mSrcFormat); + tempwav.mSrcFormat = 0; + } + + if (result != FMOD_OK) + { + return result; + } + } + else + { + if (tempwav.mSrcFormat) + { + FMOD_Memory_Free(tempwav.mSrcFormat); + tempwav.mSrcFormat = 0; + } + } + } + } + } + + /* + If there wasnt a riff size chunk + */ + if (!lengthbytes) + { + /* + Get size of stream in bytes + */ + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + manualsizecalc = true; + } + + mMemoryBlockMemory = (CodecMPEG_MemoryBlock *)FMOD_Memory_Calloc(sizeof(CodecMPEG_MemoryBlock) + 32); + if (!mMemoryBlockMemory) + { + return FMOD_ERR_MEMORY; + } + mMemoryBlock = (CodecMPEG_MemoryBlock *)FMOD_ALIGNPOINTER(mMemoryBlockMemory, 16); + + mMemoryBlock->mHasXingNumFrames = false; + mMemoryBlock->mHasXingToc = false; + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + mMemoryBlock->mFrameSizeOld = -1; + mMemoryBlock->mSynthBo = 1; + mMemoryBlock->mSynthBuffs = (float *)FMOD_ALIGNPOINTER(mMemoryBlock->mSynthBuffsMem, 16); + #endif + + /* + Scan the next 4k or more for a valid header..(arent i nice) + */ + do + { + unsigned int searchlen; + + validheader = false; + + searchlen = lengthbytes; + + if (searchlen > 4096 && !(usermode & FMOD_MPEGSEARCH)) + { + searchlen = 4096; + } + + for (count = 0; count < searchlen; count++) + { + result = mFile->seek(mSrcDataOffset, SEEK_SET); + if (result != FMOD_OK && result != FMOD_ERR_FILE_COULDNOTSEEK) + { + return result; + } + + if (result != FMOD_ERR_FILE_COULDNOTSEEK) + { + result = mFile->read(header, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + mMemoryBlock->mLayer = 0; + #endif + + result = decodeHeader(header, &frequency, &channels, &framesize); + if (result == FMOD_OK) + { + validheader = true; + break; + } + } + + mSrcDataOffset++; + } + + if (!validheader) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecMPEG::openInternal", "failed to open as mpeg\n")); + + return FMOD_ERR_FORMAT; + } + + /* + Just in case we FLUKED a wav with an mpeg type header in it .. try again on the next frame. + */ + result = mFile->seek(framesize, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(header, 1, 4, 0); + + if (result != FMOD_ERR_FILE_EOF) + { + if (result != FMOD_OK) + { + return result; + } + + result = decodeHeader(header, &frequency, &channels, 0); + if (result != FMOD_OK) + { + mSrcDataOffset++; + validheader = false; + } + } + + } while (!validheader); + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + mMemoryBlock->mFrameSizeOld = -1; + mMemoryBlock->mSynthBo = 1; + mMemoryBlock->mLayer = 0; + #endif + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + waveformat = mWaveFormatMemory; + + waveformat[0].frequency = frequency; + waveformat[0].lengthbytes = lengthbytes; + waveformat[0].channels = channels; + + framesize += 4; /* It was size-4 previously */ + + if (!gInitialized) + { + initAll(); + gInitialized = true; + } + + result = mFile->seek(mSrcDataOffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_MPEG; + + if (!(usermode & (FMOD_SOFTWARE | FMOD_HARDWARE))) + { + if (mSystem->mOutputType == FMOD_OUTPUTTYPE_OPENAL) + { + waveformat[0].mode |= FMOD_HARDWARE; + } + else + { + waveformat[0].mode |= FMOD_SOFTWARE; + } + } + } + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + + /* + Check for XING header and decode 1 frame to determine uncompressed frame size. + */ + { + static unsigned char in[MAXFRAMESIZE]; + static unsigned char out[4608]; + FMOD_SOUND_FORMAT fmt; + + result = mFile->read(in, 1, framesize, 0); + if (result != FMOD_OK) + { + return result; + } + + /* + Check if it has a Xing header + */ + result = decodeXingHeader(in, mMemoryBlock->mXingToc, &mMemoryBlock->mNumFrames); + if (result == FMOD_OK) + { + mSrcDataOffset += framesize; + + result = mFile->read(in, 1, framesize, 0); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Unpack 1 frame to get a rough compression ratio + */ + fmt = waveformat[0].format; + + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + + decodeFrame(in, (short *)out, &mPCMFrameLengthBytes); + + waveformat[0].format = fmt; + } + + result = mFile->seek(mSrcDataOffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + If for some reason the frame decode didnt go as planned + */ + if (!mPCMFrameLengthBytes) + { + mPCMFrameLengthBytes = 1152 * sizeof(signed short) * waveformat[0].channels; + } + + framesize += 1; + framesize &= ~1; + + if (usermode & FMOD_ACCURATETIME && mFile->mFlags & FMOD_FILE_SEEKABLE) + { + result = mFile->seek(mSrcDataOffset, SEEK_SET); + + result = getPCMLength(); + if (result != FMOD_OK) + { + return result; + } + } + else + { + if (waveformat[0].lengthbytes == (unsigned int)-1) + { + waveformat[0].lengthpcm = waveformat[0].lengthbytes; + } + else + { + if (mMemoryBlock->mHasXingNumFrames) + { + waveformat[0].lengthpcm = 1152 * mMemoryBlock->mNumFrames; + } + else + { + /* + CBR calculation + */ + int numframes = ((waveformat[0].lengthbytes + (framesize - 1)) / framesize) + 1; /* round up, but not sure why i have to add 1 */ + waveformat[0].lengthpcm = mPCMFrameLengthBytes * numframes; + + mFlags &= ~FMOD_CODEC_ACCURATELENGTH; + } + } + } + + if (manualsizecalc && waveformat[0].lengthbytes != (unsigned int)-1) + { + waveformat[0].lengthbytes -= mSrcDataOffset; + } + + if (waveformat[0].lengthpcm != (unsigned int)-1) + { + if (!mMemoryBlock->mHasXingNumFrames || (usermode & FMOD_ACCURATETIME)) + { + waveformat[0].lengthpcm = waveformat[0].lengthpcm / sizeof(signed short) / waveformat[0].channels; + } + } + + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + /* + Get rid of the mem block for this sound, we don't need it. + */ + if (mMemoryBlockMemory) + { + if (mMemoryBlock->mFrameOffset) + { + FMOD_Memory_Free(mMemoryBlock->mFrameOffset); + mMemoryBlock->mFrameOffset = 0; + } + + FMOD_Memory_Free(mMemoryBlockMemory); + mMemoryBlockMemory = mMemoryBlock = 0; + } + } + else + { + /* + NOT FMOD_CREATECOMPRESSEDSAMPLE + */ + mPCMBufferLength = 1152; + mPCMBufferLengthBytes = 1152 * sizeof(signed short) * 2; + + mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes + 16); + if (!mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mPCMBufferMemory, 16); + } + + waveformat[0].blockalign = 1152 * sizeof(signed short) * waveformat[0].channels; + mPCMBufferLengthBytes = mPCMBufferLength * sizeof(signed short) * waveformat[0].channels; + + #ifdef FMOD_SUPPORT_DSPCODEC + if (waveformat[0].format == FMOD_SOUND_FORMAT_MPEG && !mSystem->mDSPCodecPool_MPEG.mNumDSPCodecs) + { + int count; + + result = mSystem->mDSPCodecPool_MPEG.init(FMOD_DSP_CATEGORY_DSPCODECMPEG, 1152, mSystem->mAdvancedSettings.maxMPEGcodecs ? mSystem->mAdvancedSettings.maxMPEGcodecs : FMOD_ADVANCEDSETTINGS_MAXMPEGCODECS); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_MPEG.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_MPEG.mPool[count]); + CodecMPEG *mpeg = (CodecMPEG *)dspcodec->mCodec; + + mpeg->mSrcDataOffset = 0; /* Raw data will start at 0. */ + mpeg->mWaveFormatMemory = 0; /* This will be set up upon play. */ + mpeg->resetFrame(); + + } + } + #endif + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + resetFrame(); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::closeInternal() +{ + if (mPCMBufferMemory) + { + FMOD_Memory_Free(mPCMBufferMemory); + mPCMBufferMemory = mPCMBuffer = 0; + } + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + if (mMemoryBlockMemory) + { + if (mMemoryBlock->mFrameOffset) + { + FMOD_Memory_Free(mMemoryBlock->mFrameOffset); + mMemoryBlock->mFrameOffset = 0; + } + + FMOD_Memory_Free(mMemoryBlockMemory); + mMemoryBlockMemory = mMemoryBlock = 0; + } + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + int channel; + FMOD_RESULT result = FMOD_OK; + unsigned char *decodebuffer = mPCMBuffer ? mPCMBuffer : (unsigned char *)buffer; + unsigned int bytesreadinternal = 0; + + #if defined(PLATFORM_PS3_SPU) && defined(FMOD_SUPPORT_MPEG_SONYDECODER) + FMOD_PS3_SPU_TempStoreMixBuffers(true); + #endif + + *bytesread = 0; + + channel = 0; + do + { + int mpegchannels = 0; + #ifndef PLATFORM_PS3_SPU_STREAMDECODE + int retries = 0, framesize = 0; + + #ifdef FMOD_SUPPORT_MPEG_SPU + unsigned char *readbuffer = (unsigned char *)gMpegReadBuffer; + unsigned char readbuffertemp[MAXFRAMESIZE]; + #else + unsigned char readbuffer[MAXFRAMESIZE]; + FMOD_memset(readbuffer, 0, MAXFRAMESIZE); + #endif + + if (waveformat->channels > 2) + { + mMemoryBlock = (CodecMPEG_MemoryBlock *)FMOD_ALIGNPOINTER(mMemoryBlockMemory, 16); + mMemoryBlock = &mMemoryBlock[channel]; + } + + do + { + #ifdef FMOD_SUPPORT_MPEG_SPU + result = mFile->read(readbuffertemp, 1, 4, 0); + #else + result = mFile->read(readbuffer, 1, 4, 0); + #endif + if (result != FMOD_OK) + { + break; + } + + if (retries) + { + mMemoryBlock->mLayer = 0; + } + + #ifdef FMOD_SUPPORT_MPEG_SPU + /* + On the PPU for stream decoder + */ + result = decodeHeader(readbuffertemp, 0, &mpegchannels, &framesize); + #else + /* + On the SPU + */ + result = decodeHeader(readbuffer, 0, &mpegchannels, &framesize); + #endif + + /* + If we had a bad header followed by a good header, be suspicious of it. + Skip forward 1 frame to check for sure, and decode that header. + Its nasty but it should only happen for bad data, and misaligned frame seeks. + */ + if ((result == FMOD_OK) && (retries +#ifdef FMOD_SUPPORT_MPEG_LAYER2 + || mFlags & FMOD_CODEC_SEEKING /* Make double sure so it doesn't try and decode an mp2 frame by accident thanks to random bytes) */ +#endif + )) + { + unsigned int oldpos; + unsigned int header; + + result = mFile->tell(&oldpos); + if (result != FMOD_OK) + { + break; + } + + mFile->seek(framesize, SEEK_CUR); + if (result != FMOD_OK) + { + break; + } + + mFile->read(&header, 1, 4, 0); + if (result != FMOD_OK) + { + break; + } + + #ifndef PLATFORM_ENDIAN_BIG + header = FMOD_SWAPENDIAN_DWORD(header); + #endif + + int nextlay = 4-((header>>17)&3); + if (((header & 0xFFE00000) != 0xFFE00000) + || + #ifdef FMOD_SUPPORT_MPEG_SONYDECODER + nextlay != mMemoryBlock->mLayer + #else + nextlay != mMemoryBlock->mFrame.lay + #endif + ) + { + result = FMOD_ERR_FILE_BAD; + } + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + mFile->seek(oldpos, SEEK_SET); + } + } + + if (result != FMOD_OK) + { + #ifdef FMOD_SUPPORT_MPEG_SPU + if (!FMOD_strncmp((char *)readbuffertemp, "TAG", 3)) + #else + if (!FMOD_strncmp((char *)readbuffer, "TAG", 3)) + #endif + { + mFile->seek(128-4, SEEK_CUR); /* skip past an id3 tag */ + } + else + { + mFile->seek(-3, SEEK_CUR); /* increment a byte at a time! */ + } + } + + retries++; + + } while (result != FMOD_OK); + + if (result == FMOD_OK) + { + #ifdef FMOD_SUPPORT_MPEG_SPU + result = mFile->read((char *)readbuffertemp+4, 1, framesize, 0); + + FMOD_OS_CriticalSection_Enter(gDecodeCrit); + + FMOD_memcpy(readbuffer, readbuffertemp, framesize + 4); + #else + result = mFile->read((char *)readbuffer+4, 1, framesize, 0); + #endif + + if (result != FMOD_OK) + { + #ifdef FMOD_SUPPORT_MPEG_SPU + FMOD_OS_CriticalSection_Leave(gDecodeCrit); + #endif + return result; + } + + #ifdef FMOD_SUPPORT_MPEG_SPU + /* + Signal SPU to do decodeFrame + */ + sys_event_t event; + OutputPS3 *output; + + output = (OutputPS3 *)mSystem->mOutput; + + output->mMpegAddresses[0] = (unsigned int)mMemoryBlock; + output->mMpegAddresses[1] = (unsigned int)decodebuffer; + output->mMpegAddresses[2] = (unsigned int)&output->mMpegBytesRead; + output->mMpegAddresses[3] = (unsigned int)mChannels; + output->mMpegAddresses[4] = (unsigned int)channel; + output->mMpegAddresses[5] = (unsigned int)waveformat->channels; + output->mMpegAddresses[6] = bytesreadinternal; + + #ifdef FMOD_SPURS + { + uint16_t waitmask = 0xffff; + + /* + SPURS TASK + */ + + if (output->mSPURSMode == FMOD_PS3_SPURSMODE_TWOTASKS) + { + /* + Wake up SPURS MPEG decode Task + */ + cellSpursEventFlagSet(&output->mMpegEventFlagSPU, 0x01); + } + else + { + /* + Atomic increment to tell SPU to decode + */ + cellAtomicIncr32((unsigned int *)&output->mStreamAtomic); + + if (output->mSPURSMode == FMOD_PS3_SPURSMODE_CREATECONTEXT) + { + /* + Wake up SPURS Task + */ + cellSpursEventFlagSet(&output->mMixEventFlagSPU, 0x01); + } + } + + /* + Wait for message back from SPU to say decode is done. + */ + cellSpursEventFlagWait(&output->mMpegEventFlag, &waitmask, CELL_SPURS_EVENT_FLAG_OR); + + if (waitmask == 0xffff) + { + waitmask = 0; + } + + bytesreadinternal = waitmask; + } + #else + { + /* + SPU THREAD + */ + /* + Atomic increment to tell SPU to decode + */ + cellAtomicIncr32((unsigned int *)&output->mStreamAtomic); + + /* + Wake up SPU Thread + */ + if (output->mSPURSMode == FMOD_PS3_SPURSMODE_TWOTASKS) + { + sys_event_port_send(output->mMpegEventPortSPU, 0, 0, 0); + } + else + { + sys_event_port_send(output->mEventPortMixSPU, 0, 0, 0); + } + + /* + Wait for message back from SPU to say decode is done, this can return the bytes read + */ + sys_event_queue_receive(output->mMpegEventQueue, &event, SYS_NO_TIMEOUT); + + bytesreadinternal = event.data3; + } + #endif + + FMOD_OS_CriticalSection_Leave(gDecodeCrit); + + #else /* FMOD_SUPPORT_MPEG_SPU */ + + result = decodeFrame(readbuffer, decodebuffer, &bytesreadinternal); + if (result != FMOD_OK) + { + /* + Corrupted frame, ignore it. + */ + result = FMOD_OK; + } + + #endif /* FMOD_SUPPORT_MPEG_SPU */ + + *bytesread += bytesreadinternal; + } + #endif /* PLATFORM_PS3_SPU_STREAMDECODE */ + + /* + If there is an error, set mpegchannels, mpegchannels being 0 may cause + an infinite loop. + */ + if (!mpegchannels) + { + mpegchannels = 1; + } + + channel += mpegchannels; + + #if !defined(PLATFORM_PS3) || defined(PLATFORM_PS3_SPU_STREAMDECODE) + decodebuffer += (sizeof(short) * mpegchannels); + #endif + } + while (channel < waveformat->channels); + + if (waveformat->channels > 2) + { + mMemoryBlock = (CodecMPEG_MemoryBlock *)FMOD_ALIGNPOINTER(mMemoryBlockMemory, 16); + } + + #if defined(PLATFORM_PS3_SPU) && defined(FMOD_SUPPORT_MPEG_SONYDECODER) + FMOD_PS3_SPU_TempStoreMixBuffers(false); + #endif + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ +#ifndef PLATFORM_PS3_SPU_STREAMDECODE + FMOD_RESULT result = FMOD_OK; + unsigned int raw = 0; + unsigned int frame, pcmbytes, bytespersample; + unsigned int excessbytes = 0; + int reset = 0; + + if (postype == FMOD_TIMEUNIT_RAWBYTES) + { + result = mFile->seek(mSrcDataOffset + position, SEEK_SET); + return result; + } + + bytespersample = sizeof(signed short) * waveformat[0].channels; + pcmbytes = position * bytespersample; + frame = pcmbytes / mPCMFrameLengthBytes; + + /* + Calculate how far we have gone past a frame boundary, and determine how many samples are needed and which to discard inside the mpeg block. + Also, rewind a whole frame (but discard it) so the mpeg block gets a 'previous frame decode' to eliminate chirping! + */ + if (pcmbytes) + { + unsigned int framerewind = mFlags & FMOD_CODEC_FROMFSB ? 3 : 9; /* FSB doesn't use bit reservoir. */ + + excessbytes = pcmbytes - (frame * mPCMFrameLengthBytes); + + if (frame < framerewind) + { + framerewind = frame; + } + + frame -= framerewind; + excessbytes += (mPCMFrameLengthBytes * framerewind); + } + else + { + excessbytes = 0; + position = 0; +#if 0 + int numframes = waveformat[0].lengthpcm / 1152; + unsigned int framerewind = mFlags & FMOD_CODEC_FROMFSB ? 3 : 9; /* FSB doesn't use bit reservoir. */ + frame = numframes - framerewind; + position = waveformat[0].lengthpcm; + excessbytes += (1152 * bytespersample * framerewind); + reset = true; +#endif + } + + if (mMode & FMOD_ACCURATETIME && mMemoryBlock->mFrameOffset) + { + if (frame > mMemoryBlock->mNumFrames) + { + frame = mMemoryBlock->mNumFrames - 1; + } + raw = mMemoryBlock->mFrameOffset[frame]; + } + else if (mMemoryBlock->mHasXingToc) + { + float percent; + int index; + float fa, fb, fx; + + /* + Calculate percentage into song to seek to + */ + if (position > (excessbytes / bytespersample)) + { + percent = ((float)(position - (excessbytes / bytespersample)) / (float)waveformat[0].lengthpcm) * 100.0f; + } + else + { + percent = 0; + } + + /* + Interpolate in TOC to get file seek point + */ + if (percent < 0.0f) + { + percent = 0.0f; + } + if (percent > 100.0f) + { + percent = 100.0f; + } + + index = (int)percent; + + if (index > 99) + { + index = 99; + } + fa = mMemoryBlock->mXingToc[index]; + + if (index < 99) + { + fb = mMemoryBlock->mXingToc[index+1]; + } + else + { + fb = 256.0f; + } + + fx = fa + (fb-fa) * (percent - index); + + raw = (unsigned int)((1.0f/256.0f) * fx * waveformat[0].lengthbytes); + + if (index > 0) + { + /* + First frame just had xing info so seek forward one frame. + */ + raw += mPCMFrameLengthBytes; + } + } + else + { + if (position > (excessbytes / bytespersample)) + { +// int numframes = (int)(((waveformat[0].lengthpcm + 2304) * bytespersample) / mPCMFrameLengthBytes); /* Old fsbank added 2 frames of pcm more than compressedbytes did. */ + int numframes = (int)((waveformat[0].lengthpcm * bytespersample) / mPCMFrameLengthBytes); + int compressedframesize = waveformat[0].lengthbytes / numframes; + raw = frame * compressedframesize; + } + else + { + raw = 0; + } + } + + raw += mSrcDataOffset; + + if (raw > (unsigned int)mSrcDataOffset + (unsigned int)waveformat[0].lengthbytes) + { + raw = mSrcDataOffset; + } + + result = mFile->seek(raw, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + if (!(mFlags & FMOD_CODEC_FROMFSB)) + { + mFlags |= FMOD_CODEC_SEEKING; + } + + while (excessbytes) + { + char buff[4608]; + unsigned int read = 0, toread = 4608; + + if (toread > excessbytes) + { + toread = excessbytes; + } + + result = Codec::read(buff, toread, &read); + if (result != FMOD_OK) + { + break; + } + + /* + During the first read after the seek, decode sometimes fails because the decoder + hasn't built up state yet, just push on, decoded frames are dropped anyway. + */ + if (read == 0) + { + read = toread; + } + + if (excessbytes >= read) + { + excessbytes -= read; + } + else + { + excessbytes = 0; + } + + /* + If unbuffered, and if the amount left is smaller than a normal mpeg frame size dont keep reading, especially if it means going past the EOF. + */ + if ((!mPCMBuffer || !mPCMBufferLengthBytes) && excessbytes < read) + { + excessbytes = 0; + } + } + + + if (reset) + { + result = mFile->seek(mSrcDataOffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + + mFlags &= ~FMOD_CODEC_SEEKING; + + return result; +#else + return FMOD_OK; +#endif +} + + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::soundCreateInternal(int subsound, FMOD_SOUND *sound) +{ +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result = FMOD_OK; + SoundI *s = (SoundI *)sound; + + if (mNumSyncPoints && mSyncPoint) + { + int count; + + for (count = 0; count < mNumSyncPoints; count++) + { + s->addSyncPointInternal(mSyncPoint[count].mOffset, FMOD_TIMEUNIT_PCM, mSyncPoint[count].mName, 0, 0, false); + } + + s->syncPointFixIndicies(); + + FMOD_Memory_Free(mSyncPoint); + mSyncPoint = 0; + } + + return result; +#else + return FMOD_OK; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMPEG::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecMPEG *mpeg = (CodecMPEG *)codec; + + return mpeg->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMPEG::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecMPEG *mpeg = (CodecMPEG *)codec; + + return mpeg->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMPEG::soundCreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + CodecMPEG *mpeg = (CodecMPEG *)codec; + + return mpeg->soundCreateInternal(subsound, sound); +} + + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMPEG::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecMPEG *mpeg = (CodecMPEG *)codec; + + return mpeg->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMPEG::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecMPEG *mpeg = (CodecMPEG *)codec; + + return mpeg->setPositionInternal(subsound, position, postype); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecMPEG::resetCallback(FMOD_CODEC_STATE *codec) +{ + CodecMPEG *mpeg = (CodecMPEG *)codec; + + return mpeg->resetInternal(); +} + + +} + +#endif + + diff --git a/src/fmod_codec_mpeg.h b/src/fmod_codec_mpeg.h new file mode 100755 index 0000000..705de0c --- /dev/null +++ b/src/fmod_codec_mpeg.h @@ -0,0 +1,353 @@ +#ifndef _FMOD_CODEC_MPEG_H +#define _FMOD_CODEC_MPEG_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MPEG + +#include "fmod_codeci.h" + +#ifdef FMOD_SUPPORT_MPEG_SONYDECODER + #include "../ps3/lib/sony/mp3declib.h" +#endif + +#ifndef PLATFORM_PS3 + #define USE_ISPOW_TABLE +#endif + +namespace FMOD +{ + /* + DEFINITIONS + */ + + #define SBLIMIT 32 + #define SSLIMIT 18 + #define SCALE_BLOCK 12 + + #define MPG_MD_STEREO 0 + #define MPG_MD_JOINT_STEREO 1 + #define MPG_MD_DUAL_CHANNEL 2 + #define MPG_MD_MONO 3 + + #define MAXFRAMESIZE 1792 + + /* + STRUCTURE DEFINITIONS + */ + + struct al_table + { + short bits; + short d; + }; + + typedef struct + { + struct al_table *alloc; + int stereo; + int jsbound; + int II_sblimit; + int lsf; + int mpeg25; + int header_change; + int lay; + int error_protection; + int bitrate_index; + int sampling_frequency; + int padding; + int extension; + int mode; + int mode_ext; + int copyright; + int original; + int emphasis; + int framesize; /* computed framesize */ + } MPEG_FRAME; + + struct gr_info_s + { + int scfsi; + unsigned part2_3_length; + unsigned big_values; + unsigned scalefac_compress; + unsigned block_type; + unsigned mixed_block_flag; + unsigned table_select[3]; + unsigned subblock_gain[3]; + unsigned maxband[3]; + unsigned maxbandl; + unsigned maxb; + unsigned region1start; + unsigned region2start; + unsigned preflag; + unsigned scalefac_scale; + unsigned count1table_select; + float *full_gain[3]; + float *pow2gain; + }; + + struct III_sideinfo + { + unsigned main_data_begin; + unsigned private_bits; + + struct + { + struct gr_info_s gr[2]; + } ch[2]; + }; + + struct bandInfoStruct + { + int longIdx[23]; + int longDiff[22]; + int shortIdx[14]; + int shortDiff[13]; + }; + + class SyncPoint; + + struct bitstream_info + { + int mBitIndex; + unsigned char *mWordPointer; + }; + + struct CodecMPEG_MemoryBlock + { + #ifdef FMOD_SUPPORT_MPEG_SONYDECODER + + CellMP3Context mContext1; + CellMP3Context mContext2; + int mCount; + int mStereo; + char mPad[4]; + + #else + + unsigned char mBSSpace[2][MAXFRAMESIZE+512]; /* 4608 */ + float mSynthBuffsMem[(2 * 576) + 16]; /* 4608 */ + float *mSynthBuffs; + float mBlock[2][2][SBLIMIT * SSLIMIT]; /* 9216 */ + + int mFrameSize; + int mFrameSizeOld; + MPEG_FRAME mFrame; + unsigned int mFrameHeader; + + int mPcmPoint; + int mBSNum; + int mSynthBo; + bitstream_info mBSI; + int mBlc[2]; + + #ifdef PLATFORM_PS3 + char mPad[4]; + #endif + + #endif + + int mLayer; + unsigned int mNumFrames; + unsigned int *mFrameOffset; + + unsigned char mXingToc[100]; + bool mHasXingNumFrames; + bool mHasXingToc; + }; + + class CodecMPEG : public Codec + { + public: + CodecMPEG_MemoryBlock *mMemoryBlock; + CodecMPEG_MemoryBlock *mMemoryBlockMemory; + + SyncPoint *mSyncPoint; + int mNumSyncPoints; + unsigned int mPCMFrameLengthBytes; + unsigned char *mPCMBufferMemory; + int mChannels; + + static int gTabSel123[2][3][16]; + static int gFreqs[9]; + + static bool gInitialized; + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + /* + Static, global members used by all codecs + */ + static float gDecWinMem[512+32+16]; /* +16 for alignment */ + static FMOD_ALIGN16(float gCos64[16]); + static FMOD_ALIGN16(float gCos32[8]); + static FMOD_ALIGN16(float gCos16[4]); + static FMOD_ALIGN16(float gCos8[2]); + static FMOD_ALIGN16(float gCos4[1]); + static float *gPnts[5]; + static int gIntWinBase[]; + + + /* + Layer 2 globals + */ + static unsigned char gGrp3Tab[32 * 3]; + static unsigned char gGrp5Tab[128 * 3]; + static unsigned char gGrp9Tab[1024 * 3]; + static float gMulsTab[27][64]; + static struct al_table gAlloc0[]; + static struct al_table gAlloc1[]; + static struct al_table gAlloc2[]; + static struct al_table gAlloc3[]; + static struct al_table gAlloc4[]; + + /* + Layer 3 globals + */ + static float gIsPowTable[8207]; + static float gAaCa[8]; + static float gAaCs[8]; + static float gWin[4][36]; + static float gWin1[4][36]; + static float gGainPow2[256 + 118 + 4]; + static float gCos6_1; + static float gCos6_2; + static float gTfCos36[9]; + static float gTfCos12[3]; + static float gCos9[3]; + static float gCos18[3]; + static int gLongLimit[9][23]; + static int gShortLimit[9][14]; + static struct bandInfoStruct gBandInfo[9]; + static int gMapBuf0[9][152]; + static int gMapBuf1[9][156]; + static int gMapBuf2[9][44]; + static int *gMap[9][3]; + static int *gMapEnd[9][3]; + static unsigned int gN_SLen2[512]; /* MPEG 2.0 slen for 'normal' mode */ + static unsigned int gI_SLen2[256]; /* MPEG 2.0 slen for intensity stereo */ + static float gTan1_1[16]; + static float gTan2_1[16]; + static float gTan1_2[16]; + static float gTan2_2[16]; + static float gPow1_1[2][16]; + static float gPow2_1[2][16]; + static float gPow1_2[2][16]; + static float gPow2_2[2][16]; + + static FMOD_RESULT makeTables(int scaleval); + static FMOD_RESULT initLayer2(); + static FMOD_RESULT initLayer3(int down_sample_sblimit); + #endif + + static FMOD_RESULT initAll(); + + #ifdef FMOD_SUPPORT_MPEG_SPU + static FMOD_RESULT closeAll(); + #endif + + /* + Per mpeg member functions + */ + FMOD_RESULT decodeHeader(void *in, int *samplerate, int *channels, int *framesize); + FMOD_RESULT decodeXingHeader(unsigned char *in, unsigned char *toc, unsigned int *numframes); + FMOD_RESULT decodeFrame(unsigned char *in, void *out, unsigned int *outlen); + FMOD_RESULT decodeLayer2(void *out, unsigned int *outlen); + FMOD_RESULT decodeLayer3(void *out, unsigned int *outlen); + FMOD_RESULT decodeLayer3(unsigned char *in, void *out, unsigned int *outlen); /* Using Sony Decoder for PS3 */ + FMOD_RESULT getPCMLength(); + FMOD_RESULT resetFrame(); + FMOD_RESULT getIIStuff(); + FMOD_RESULT II_step_one(unsigned int *bit_alloc,int *scale); + FMOD_RESULT II_step_two(unsigned int *bit_alloc,float fraction[2][4][SBLIMIT],int *scale,int x1); + FMOD_RESULT III_get_side_info_1(struct III_sideinfo *si, int stereo, int ms_stereo, int sfreq); + FMOD_RESULT III_get_side_info_2(struct III_sideinfo *si, int stereo, int ms_stereo, int sfreq); + FMOD_RESULT III_get_scale_factors_1(int *scf,struct gr_info_s *gr_info, int *numbits); + FMOD_RESULT III_get_scale_factors_2(int *scf,struct gr_info_s *gr_info,int i_stereo, int *numbits); + FMOD_RESULT III_dequantize_sample(float xr[SBLIMIT][SSLIMIT],int *scf, struct gr_info_s *gr_info,int sfreq,int part2bits); + FMOD_RESULT III_dequantize_sample_ms(float xr[2][SBLIMIT][SSLIMIT], int *scf, struct gr_info_s *gr_info, int sfreq, int part2bits); + FMOD_RESULT III_i_stereo(float xr_buf[2][SBLIMIT][SSLIMIT], int *scalefac, struct gr_info_s *gr_info, int sfreq, int ms_stereo, int lsf); + FMOD_RESULT III_antialias(float xr[SBLIMIT][SSLIMIT], struct gr_info_s *gr_info); + FMOD_RESULT III_hybrid(float fsIn[SBLIMIT][SSLIMIT], float tsOut[SSLIMIT][SBLIMIT], int ch, struct gr_info_s *gr_info); + + FMOD_RESULT synth(void *samples, float *bandPtr, int channels, int channelskip); + FMOD_RESULT synthC(float *b0, int bo1, int channels, signed short *samples); + + unsigned int getBits(int number_of_bits); + unsigned int getBitsFast(int number_of_bits); + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + FMOD_INLINE unsigned int get1Bit() + { + unsigned char rval; + + rval = *(mMemoryBlock->mBSI.mWordPointer) << mMemoryBlock->mBSI.mBitIndex; + + mMemoryBlock->mBSI.mBitIndex++; + mMemoryBlock->mBSI.mWordPointer += (mMemoryBlock->mBSI.mBitIndex>>3); + mMemoryBlock->mBSI.mBitIndex &= 7; + + return (unsigned int)(rval>>7); + } + FMOD_INLINE int getByte() + { + return *mMemoryBlock->mBSI.mWordPointer++; + } + FMOD_INLINE void backbits(int number_of_bits) + { + mMemoryBlock->mBSI.mBitIndex -= number_of_bits; + mMemoryBlock->mBSI.mWordPointer += (mMemoryBlock->mBSI.mBitIndex>>3); + mMemoryBlock->mBSI.mBitIndex &= 0x7; + } + #endif + + static void dct64(float *out0,float *out1,float *samples); + static void dct36(float * inbuf, float * o1, float * o2, float * wintab, float * tsbuf); + static void dct12(float * in, float * rawout1, float * rawout2, register float * wi, register float * ts); + + /* + Codec functions + */ +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT soundCreateInternal(int subsound, FMOD_SOUND *sound); +#endif + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT resetInternal() { return resetFrame(); } + + public: + +#ifndef PLATFORM_PS3_SPU + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK soundCreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); +#endif + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_CODEC_STATE *codec); + }; +} + +#ifdef __cplusplus +extern "C" { +#endif + +extern float *FMOD_Mpeg_DecWin; + +void FMOD_Mpeg_Synth_FPU(float *b0, int bo1, int numchannels, short *samps); +void FMOD_Mpeg_DCT64(float *out0,float *out1,float *samples); + +#ifdef __cplusplus +} +#endif + + +#endif /* FMOD_SUPPORT_MPEG */ + +#endif + + diff --git a/src/fmod_codec_mpeg_decode.cpp b/src/fmod_codec_mpeg_decode.cpp new file mode 100755 index 0000000..98460f6 --- /dev/null +++ b/src/fmod_codec_mpeg_decode.cpp @@ -0,0 +1,1241 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MPEG + +#include "fmod_codec_mpeg.h" + +#include +#include + +#ifdef PLATFORM_PS3_SPU +#include "fmod_common_spu.h" +#include "fmod_spu_printf.h" +#endif + +namespace FMOD +{ + +int CodecMPEG::gTabSel123[2][3][16] = +{ + { + {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, + {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, + {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} + }, + { + {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} + } +}; + +int CodecMPEG::gFreqs[9] = +{ + 44100, + 48000, + 32000, + 22050, + 24000, + 16000, + 11025, + 12000, + 8000 +}; + +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +unsigned int CodecMPEG::getBits(int number_of_bits) +{ + unsigned int rval; + + if(!number_of_bits) + return 0; + + rval = mMemoryBlock->mBSI.mWordPointer[0]; + rval <<= 8; + rval |= mMemoryBlock->mBSI.mWordPointer[1]; + rval <<= 8; + rval |= mMemoryBlock->mBSI.mWordPointer[2]; + rval <<= mMemoryBlock->mBSI.mBitIndex; + rval &= 0xffffff; + + mMemoryBlock->mBSI.mBitIndex += number_of_bits; + + rval >>= (24-number_of_bits); + + mMemoryBlock->mBSI.mWordPointer += (mMemoryBlock->mBSI.mBitIndex>>3); + mMemoryBlock->mBSI.mBitIndex &= 7; + + return rval; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +unsigned int CodecMPEG::getBitsFast(int number_of_bits) +{ + unsigned int rval; + + rval = mMemoryBlock->mBSI.mWordPointer[0]; + rval <<= 8; + rval |= mMemoryBlock->mBSI.mWordPointer[1]; + rval <<= mMemoryBlock->mBSI.mBitIndex; + rval &= 0xffff; + + mMemoryBlock->mBSI.mBitIndex += number_of_bits; + + rval >>= (16-number_of_bits); + + mMemoryBlock->mBSI.mWordPointer += (mMemoryBlock->mBSI.mBitIndex>>3); + mMemoryBlock->mBSI.mBitIndex &= 7; + + return rval; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::decodeHeader(void *in, int *samplerate, int *channels, int *framesize) +{ +#ifdef FMOD_SUPPORT_MPEG_SONYDECODER + + unsigned int head = 0; + + head = (unsigned char)*((unsigned char*)in + 0); + head <<= 8; + head |= (unsigned char)*((unsigned char*)in + 1); + head <<= 8; + head |= (unsigned char)*((unsigned char*)in + 2); + head <<= 8; + head |= (unsigned char)*((unsigned char*)in + 3); + + if ((head & 0xFFE00000) != 0xFFE00000) + { + return FMOD_ERR_FORMAT; + } + + int lsf; + int mpeg25; + int lay; + int sampling_frequency; + int bitrate_index; + int mode; + int mode_ext; + int padding; + int jsbound; + int _framesize; + int II_sblimit; + + if( head & (1<<20) ) + { + lsf = (head & (1<<19)) ? 0x0 : 0x1; + mpeg25 = 0; + } + else + { + if (head & (1<<19)) + { + return FMOD_ERR_FORMAT; + } + + lsf = 1; + mpeg25 = 1; + } + + lay = 4-((head>>17)&3); + + if (lay != 3) + { + /* + Sony MP3 decoder only supports layer 3 + */ + if ((mFlags & FMOD_CODEC_FROMFSB) && lay == 2) + { + return FMOD_ERR_FORMAT; + } + } + + //if (!mMemoryBlock->mLayer) + { + mMemoryBlock->mLayer = lay; + } + + /* + if (lay != mMemoryBlock->mLayer) + { + return FMOD_ERR_FORMAT; + } + */ + + if( ((head>>10)&0x3) == 0x3) + { + return FMOD_ERR_FORMAT; + } + + if (mpeg25) + { + sampling_frequency = 6 + ((head>>10)&0x3); + } + else + { + sampling_frequency = ((head>>10)&0x3) + (lsf*3); + } + + if (samplerate) + { + *samplerate = gFreqs[sampling_frequency]; + } +#if 0 + /* This is a sort of corruption check but it can cause problems if the fsb has its frequency set to something other than the encoded freq. */ + +// else if (gFreqs[mMemoryBlock->mFrame.sampling_frequency] != waveformat[0].frequency) +// { +// return FMOD_ERR_FORMAT; +// } +#endif + + bitrate_index = ((head>>12)&0xf); + padding = ((head>>9)&0x1); + mode = ((head>>6)&0x3); + mode_ext = ((head>>4)&0x3); + mMemoryBlock->mStereo = (mode == MPG_MD_MONO) ? 1 : 2; + + if (mode_ext == 1 || mode_ext == 3) + { + /* + Sony decoder doesn't support Intensity Stereo + */ + return FMOD_ERR_FORMAT; + } + + if (!bitrate_index || bitrate_index == 0xF) + { + return FMOD_ERR_FORMAT; + } + + if (channels) + { + *channels = mMemoryBlock->mStereo; + } + else if (mMemoryBlock->mStereo != waveformat[0].channels && !mChannels) + { + return FMOD_ERR_FORMAT; + } + + _framesize = (int)gTabSel123[lsf][2][bitrate_index] * 144000; + _framesize /= gFreqs[sampling_frequency]<<(lsf); + _framesize = _framesize + padding - 4; + + if (_framesize < 16) + { + return FMOD_ERR_FORMAT; + } + +// mMemoryBlock->mFrameSize = mMemoryBlock->mFrame.framesize; + + if (framesize) + { + *framesize = _framesize; + + if (mFlags & FMOD_CODEC_FROMFSB) + { + if (waveformat && waveformat->channels > 2) + { + *framesize += 4; /* Temporarily add in header */ + *framesize += 15; + *framesize &= ~15; + *framesize -= 4; /* Remove header size */ + } + else if (lay == 3 && mFlags & FMOD_CODEC_PADDED) + { + *framesize += 4; /* Temporarily add in header */ + *framesize += 1; + *framesize &= ~1; + *framesize -= 4; /* Remove header size */ + } + else if (lay == 3 && mFlags & FMOD_CODEC_PADDED4) + { + *framesize += 4; /* Temporarily add in header */ + *framesize += 3; + *framesize &= ~3; + *framesize -= 4; /* Remove header size */ + } + } + } + +#else + + unsigned int head = 0; + + head = (unsigned char)*((unsigned char*)in + 0); + head <<= 8; + head |= (unsigned char)*((unsigned char*)in + 1); + head <<= 8; + head |= (unsigned char)*((unsigned char*)in + 2); + head <<= 8; + head |= (unsigned char)*((unsigned char*)in + 3); + + mMemoryBlock->mFrameHeader = head; + + if ((head & 0xFFE00000) != 0xFFE00000) + { + return FMOD_ERR_FORMAT; + } + + if( head & (1<<20) ) + { + mMemoryBlock->mFrame.lsf = (head & (1<<19)) ? 0x0 : 0x1; + mMemoryBlock->mFrame.mpeg25 = 0; + } + else + { + if (head & (1<<19)) + { + return FMOD_ERR_FORMAT; + } + + mMemoryBlock->mFrame.lsf = 1; + mMemoryBlock->mFrame.mpeg25 = 1; + } + + mMemoryBlock->mFrame.lay = 4-((head>>17)&3); + + if (mMemoryBlock->mFrame.lay != 3 && mMemoryBlock->mFrame.lay != 2) + { + return FMOD_ERR_FORMAT; + } + + if (!mMemoryBlock->mLayer) + { + mMemoryBlock->mLayer = mMemoryBlock->mFrame.lay; + } + + if (mMemoryBlock->mFrame.lay != mMemoryBlock->mLayer) + { + return FMOD_ERR_FORMAT; + } + + if( ((head>>10)&0x3) == 0x3) + { + return FMOD_ERR_FORMAT; + } + + if (mMemoryBlock->mFrame.mpeg25) + { + mMemoryBlock->mFrame.sampling_frequency = 6 + ((head>>10)&0x3); + } + else + { + mMemoryBlock->mFrame.sampling_frequency = ((head>>10)&0x3) + (mMemoryBlock->mFrame.lsf*3); + } + + if (samplerate) + { + *samplerate = gFreqs[mMemoryBlock->mFrame.sampling_frequency]; + } +#if 0 + /* This is a sort of corruption check but it can cause problems if the fsb has its frequency set to something other than the encoded freq. */ + +// else if (gFreqs[mMemoryBlock->mFrame.sampling_frequency] != waveformat[0].frequency) +// { +// return FMOD_ERR_FORMAT; +// } +#endif + + mMemoryBlock->mFrame.error_protection = ((head>>16)&0x1)^0x1; + mMemoryBlock->mFrame.bitrate_index = ((head>>12)&0xf); + mMemoryBlock->mFrame.padding = ((head>>9)&0x1); + mMemoryBlock->mFrame.extension = ((head>>8)&0x1); + mMemoryBlock->mFrame.mode = ((head>>6)&0x3); + mMemoryBlock->mFrame.mode_ext = ((head>>4)&0x3); + mMemoryBlock->mFrame.copyright = ((head>>3)&0x1); + mMemoryBlock->mFrame.original = ((head>>2)&0x1); + mMemoryBlock->mFrame.emphasis = head & 0x3; + mMemoryBlock->mFrame.stereo = (mMemoryBlock->mFrame.mode == MPG_MD_MONO) ? 1 : 2; + + if (!mMemoryBlock->mFrame.bitrate_index || mMemoryBlock->mFrame.bitrate_index == 0xF) + { + return FMOD_ERR_FORMAT; + } + + /* + Disallow certain combinations of channel/bitrate. + */ + if (mMemoryBlock->mFrame.lay == 2) + { + if (!(mFlags & FMOD_CODEC_FROMFSB)) + { + int bitrate = gTabSel123[mMemoryBlock->mFrame.lsf][1][mMemoryBlock->mFrame.bitrate_index]; + + if (mMemoryBlock->mFrame.mode == MPG_MD_MONO) + { + if (bitrate >= 224) + { + return FMOD_ERR_FORMAT; + } + } + else if (bitrate == 32 || bitrate == 48 || bitrate == 56 || bitrate == 80) + { + return FMOD_ERR_FORMAT; + } + } + + if (mMemoryBlock->mFrame.mode != MPG_MD_JOINT_STEREO && mMemoryBlock->mFrame.mode_ext) + { + return FMOD_ERR_FORMAT; + } + } + + + if (channels) + { + *channels = mMemoryBlock->mFrame.stereo; + } + else if (mMemoryBlock->mFrame.stereo != waveformat[0].channels && !mChannels) + { + return FMOD_ERR_FORMAT; + } + + + +/* printf("2.5? %d , bitrate index = %d\n", mMemoryBlock->mFrame.mpeg25, mMemoryBlock->mFrame.bitrate_index); */ + + switch (mMemoryBlock->mFrame.lay) + { + #ifdef FMOD_SUPPORT_MPEG_LAYER2 + case 2: + { + getIIStuff(); + mMemoryBlock->mFrame.jsbound = (mMemoryBlock->mFrame.mode == MPG_MD_JOINT_STEREO) ? (mMemoryBlock->mFrame.mode_ext<<2)+4 : mMemoryBlock->mFrame.II_sblimit; + mMemoryBlock->mFrame.framesize = (int) gTabSel123[mMemoryBlock->mFrame.lsf][1][mMemoryBlock->mFrame.bitrate_index] * 144000; + mMemoryBlock->mFrame.framesize /= gFreqs[mMemoryBlock->mFrame.sampling_frequency]; + mMemoryBlock->mFrame.framesize += mMemoryBlock->mFrame.padding - 4; + break; + } + #endif + #ifdef FMOD_SUPPORT_MPEG_LAYER3 + case 3: + { + mMemoryBlock->mFrame.framesize = (int)gTabSel123[mMemoryBlock->mFrame.lsf][2][mMemoryBlock->mFrame.bitrate_index] * 144000; + mMemoryBlock->mFrame.framesize /= gFreqs[mMemoryBlock->mFrame.sampling_frequency]<<(mMemoryBlock->mFrame.lsf); + mMemoryBlock->mFrame.framesize = mMemoryBlock->mFrame.framesize + mMemoryBlock->mFrame.padding - 4; + break; + } + #endif + default: + { + return FMOD_ERR_UNSUPPORTED; + } + }; + + if (mMemoryBlock->mFrame.framesize < 16) + { + return FMOD_ERR_FORMAT; + } + + mMemoryBlock->mFrameSize = mMemoryBlock->mFrame.framesize; + + if (framesize) + { + *framesize = mMemoryBlock->mFrame.framesize; + + if (mFlags & FMOD_CODEC_FROMFSB) + { + if (waveformat && waveformat->channels > 2) + { + *framesize += 4; /* Temporarily add in header */ + *framesize += 15; + *framesize &= ~15; + *framesize -= 4; /* Remove header size */ + } + else if (mMemoryBlock->mFrame.lay == 3 && mFlags & FMOD_CODEC_PADDED) + { + *framesize += 4; /* Temporarily add in header */ + *framesize += 1; + *framesize &= ~1; + *framesize -= 4; /* Remove header size */ + } + else if (mMemoryBlock->mFrame.lay == 3 && mFlags & FMOD_CODEC_PADDED4) + { + *framesize += 4; /* Temporarily add in header */ + *framesize += 3; + *framesize &= ~3; + *framesize -= 4; /* Remove header size */ + } + } + } + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::decodeXingHeader(unsigned char *in, unsigned char *toc, unsigned int *numframes) +{ + int i, flags = 0; + int id, mode; + + id = (in[1] >> 3) & 1; + mode = (in[3] >> 6) & 3; + + /* + Get offset of Xing header + */ + if (id) // mpeg1 + { + if (mode != 3) + { + in+=(32+4); + } + else + { + in+=(17+4); + } + } + else // mpeg2 + { + if (mode != 3) + { + in+=(17+4); + } + else + { + in+=(9+4); + } + } + + /* + Check if this is a Xing header + */ + if (FMOD_strncmp((const char *)in, "Xing", 4)) + { + return FMOD_ERR_FORMAT; + } + in+=4; + + flags = *in++; + flags <<= 8; + flags |= *in++; + flags <<= 8; + flags |= *in++; + flags <<= 8; + flags |= *in++; + + if (flags & 0x0001 /*FRAMES_FLAG*/) + { + if (numframes) + { + *numframes = *in++; + *numframes <<= 8; + *numframes |= *in++; + *numframes <<= 8; + *numframes |= *in++; + *numframes <<= 8; + *numframes |= *in++; + } + + mMemoryBlock->mHasXingNumFrames = true; + } + if (flags & 0x0004 /*TOC_FLAG*/) + { + if (toc) + { + for (i=0; i<100; i++) + { + toc[i] = *in++; + } + } + + mMemoryBlock->mHasXingToc = true; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + This just unpacks one frame + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::decodeFrame(unsigned char *in, void *out, unsigned int *outlen) +{ + FMOD_RESULT result = FMOD_OK; + +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER + /* + First decode frame header + */ + if (mMemoryBlock->mFrameSize == 0) + { + result = decodeHeader(in, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + } + + mMemoryBlock->mBSI.mWordPointer = mMemoryBlock->mBSSpace[mMemoryBlock->mBSNum] + 512; + mMemoryBlock->mBSNum = (mMemoryBlock->mBSNum + 1) & 0x1; + mMemoryBlock->mBSI.mBitIndex = 0; + + FMOD_memcpy(mMemoryBlock->mBSI.mWordPointer, in+4, mMemoryBlock->mFrameSize); + + if(mMemoryBlock->mFrame.error_protection) + { + getBits(16); + } + + switch (mMemoryBlock->mFrame.lay) + { + #ifdef FMOD_SUPPORT_MPEG_LAYER2 + case 2: + { + #ifdef PLATFORM_PS3_SPU + /* + Because this is called on the PPU in decodeHeader, + we need to call it on the SPU to set up some pointers. + */ + getIIStuff(); + #endif + + result = decodeLayer2(out, outlen); + break; + } + #endif + #ifdef FMOD_SUPPORT_MPEG_LAYER3 + case 3: + { + result = decodeLayer3(out, outlen); + break; + } + #endif + }; + + mMemoryBlock->mFrameSizeOld = mMemoryBlock->mFrameSize; + mMemoryBlock->mFrameSize = 0; + +#else + result = decodeLayer3(in, out, outlen); +#endif + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::resetFrame() +{ + if (mMemoryBlock) + { + int count; + + for (count = 0; count < (mChannels ? mChannels : 1); count++) + { + unsigned int *oldframeoffset = mMemoryBlock[count].mFrameOffset; + unsigned int oldnumframes = mMemoryBlock[count].mNumFrames; + + FMOD_memset(&mMemoryBlock[count], 0, sizeof(CodecMPEG_MemoryBlock)); + + mMemoryBlock[count].mNumFrames = oldnumframes; + mMemoryBlock[count].mFrameOffset = oldframeoffset; + + #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + + mMemoryBlock[count].mSynthBo = 1; + mMemoryBlock[count].mFrameSizeOld = -1; + mMemoryBlock[count].mSynthBuffs = (float *)FMOD_ALIGNPOINTER(mMemoryBlock[count].mSynthBuffsMem, 16); + + #endif + } + } + + return FMOD_OK; +} + +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER +/* +[ + [DESCRIPTION] + DCT Function + + [PARAMETERS] + 'out0' + 'out1' + 'b1' + 'b2' + 'samples' Pointer to 16bit pcm output data + + [RETURN_VALUE] + void + + [REMARKS] + + [SEE_ALSO] +] +*/ +void CodecMPEG::dct64(float *out0,float *out1,float *samples) +{ + float b1[32], b2[32]; + { + register float *costab = gPnts[0]; + + b1[0x00] = samples[0x00] + samples[0x1F]; + b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0]; + + b1[0x01] = samples[0x01] + samples[0x1E]; + b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1]; + + b1[0x02] = samples[0x02] + samples[0x1D]; + b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2]; + + b1[0x03] = samples[0x03] + samples[0x1C]; + b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3]; + + b1[0x04] = samples[0x04] + samples[0x1B]; + b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4]; + + b1[0x05] = samples[0x05] + samples[0x1A]; + b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5]; + + b1[0x06] = samples[0x06] + samples[0x19]; + b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6]; + + b1[0x07] = samples[0x07] + samples[0x18]; + b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7]; + + b1[0x08] = samples[0x08] + samples[0x17]; + b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8]; + + b1[0x09] = samples[0x09] + samples[0x16]; + b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9]; + + b1[0x0A] = samples[0x0A] + samples[0x15]; + b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA]; + + b1[0x0B] = samples[0x0B] + samples[0x14]; + b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB]; + + b1[0x0C] = samples[0x0C] + samples[0x13]; + b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC]; + + b1[0x0D] = samples[0x0D] + samples[0x12]; + b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD]; + + b1[0x0E] = samples[0x0E] + samples[0x11]; + b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE]; + + b1[0x0F] = samples[0x0F] + samples[0x10]; + b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF]; + } + { + register float *costab = gPnts[1]; + + b2[0x00] = b1[0x00] + b1[0x0F]; + b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0]; + b2[0x01] = b1[0x01] + b1[0x0E]; + b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1]; + b2[0x02] = b1[0x02] + b1[0x0D]; + b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2]; + b2[0x03] = b1[0x03] + b1[0x0C]; + b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3]; + b2[0x04] = b1[0x04] + b1[0x0B]; + b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4]; + b2[0x05] = b1[0x05] + b1[0x0A]; + b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5]; + b2[0x06] = b1[0x06] + b1[0x09]; + b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6]; + b2[0x07] = b1[0x07] + b1[0x08]; + b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7]; + + b2[0x10] = b1[0x10] + b1[0x1F]; + b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0]; + b2[0x11] = b1[0x11] + b1[0x1E]; + b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1]; + b2[0x12] = b1[0x12] + b1[0x1D]; + b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2]; + b2[0x13] = b1[0x13] + b1[0x1C]; + b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3]; + b2[0x14] = b1[0x14] + b1[0x1B]; + b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4]; + b2[0x15] = b1[0x15] + b1[0x1A]; + b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5]; + b2[0x16] = b1[0x16] + b1[0x19]; + b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6]; + b2[0x17] = b1[0x17] + b1[0x18]; + b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7]; + } + + { + register float *costab = gPnts[2]; + + b1[0x00] = b2[0x00] + b2[0x07]; + b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0]; + b1[0x01] = b2[0x01] + b2[0x06]; + b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1]; + b1[0x02] = b2[0x02] + b2[0x05]; + b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2]; + b1[0x03] = b2[0x03] + b2[0x04]; + b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3]; + + b1[0x08] = b2[0x08] + b2[0x0F]; + b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0]; + b1[0x09] = b2[0x09] + b2[0x0E]; + b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1]; + b1[0x0A] = b2[0x0A] + b2[0x0D]; + b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2]; + b1[0x0B] = b2[0x0B] + b2[0x0C]; + b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3]; + + b1[0x10] = b2[0x10] + b2[0x17]; + b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0]; + b1[0x11] = b2[0x11] + b2[0x16]; + b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1]; + b1[0x12] = b2[0x12] + b2[0x15]; + b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2]; + b1[0x13] = b2[0x13] + b2[0x14]; + b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3]; + + b1[0x18] = b2[0x18] + b2[0x1F]; + b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0]; + b1[0x19] = b2[0x19] + b2[0x1E]; + b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1]; + b1[0x1A] = b2[0x1A] + b2[0x1D]; + b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2]; + b1[0x1B] = b2[0x1B] + b2[0x1C]; + b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3]; + } + + { + register float const cos0 = gPnts[3][0]; + register float const cos1 = gPnts[3][1]; + + b2[0x00] = b1[0x00] + b1[0x03]; + b2[0x03] = (b1[0x00] - b1[0x03]) * cos0; + b2[0x01] = b1[0x01] + b1[0x02]; + b2[0x02] = (b1[0x01] - b1[0x02]) * cos1; + + b2[0x04] = b1[0x04] + b1[0x07]; + b2[0x07] = (b1[0x07] - b1[0x04]) * cos0; + b2[0x05] = b1[0x05] + b1[0x06]; + b2[0x06] = (b1[0x06] - b1[0x05]) * cos1; + + b2[0x08] = b1[0x08] + b1[0x0B]; + b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0; + b2[0x09] = b1[0x09] + b1[0x0A]; + b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1; + + b2[0x0C] = b1[0x0C] + b1[0x0F]; + b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0; + b2[0x0D] = b1[0x0D] + b1[0x0E]; + b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1; + + b2[0x10] = b1[0x10] + b1[0x13]; + b2[0x13] = (b1[0x10] - b1[0x13]) * cos0; + b2[0x11] = b1[0x11] + b1[0x12]; + b2[0x12] = (b1[0x11] - b1[0x12]) * cos1; + + b2[0x14] = b1[0x14] + b1[0x17]; + b2[0x17] = (b1[0x17] - b1[0x14]) * cos0; + b2[0x15] = b1[0x15] + b1[0x16]; + b2[0x16] = (b1[0x16] - b1[0x15]) * cos1; + + b2[0x18] = b1[0x18] + b1[0x1B]; + b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0; + b2[0x19] = b1[0x19] + b1[0x1A]; + b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1; + + b2[0x1C] = b1[0x1C] + b1[0x1F]; + b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0; + b2[0x1D] = b1[0x1D] + b1[0x1E]; + b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1; + } + + { + register float const cos0 = gPnts[4][0]; + + b1[0x00] = b2[0x00] + b2[0x01]; + b1[0x01] = (b2[0x00] - b2[0x01]) * cos0; + b1[0x02] = b2[0x02] + b2[0x03]; + b1[0x03] = (b2[0x03] - b2[0x02]) * cos0; + b1[0x02] += b1[0x03]; + + b1[0x04] = b2[0x04] + b2[0x05]; + b1[0x05] = (b2[0x04] - b2[0x05]) * cos0; + b1[0x06] = b2[0x06] + b2[0x07]; + b1[0x07] = (b2[0x07] - b2[0x06]) * cos0; + b1[0x06] += b1[0x07]; + b1[0x04] += b1[0x06]; + b1[0x06] += b1[0x05]; + b1[0x05] += b1[0x07]; + + b1[0x08] = b2[0x08] + b2[0x09]; + b1[0x09] = (b2[0x08] - b2[0x09]) * cos0; + b1[0x0A] = b2[0x0A] + b2[0x0B]; + b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0; + b1[0x0A] += b1[0x0B]; + + b1[0x0C] = b2[0x0C] + b2[0x0D]; + b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0; + b1[0x0E] = b2[0x0E] + b2[0x0F]; + b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0; + b1[0x0E] += b1[0x0F]; + b1[0x0C] += b1[0x0E]; + b1[0x0E] += b1[0x0D]; + b1[0x0D] += b1[0x0F]; + + b1[0x10] = b2[0x10] + b2[0x11]; + b1[0x11] = (b2[0x10] - b2[0x11]) * cos0; + b1[0x12] = b2[0x12] + b2[0x13]; + b1[0x13] = (b2[0x13] - b2[0x12]) * cos0; + b1[0x12] += b1[0x13]; + + b1[0x14] = b2[0x14] + b2[0x15]; + b1[0x15] = (b2[0x14] - b2[0x15]) * cos0; + b1[0x16] = b2[0x16] + b2[0x17]; + b1[0x17] = (b2[0x17] - b2[0x16]) * cos0; + b1[0x16] += b1[0x17]; + b1[0x14] += b1[0x16]; + b1[0x16] += b1[0x15]; + b1[0x15] += b1[0x17]; + + b1[0x18] = b2[0x18] + b2[0x19]; + b1[0x19] = (b2[0x18] - b2[0x19]) * cos0; + b1[0x1A] = b2[0x1A] + b2[0x1B]; + b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0; + b1[0x1A] += b1[0x1B]; + + b1[0x1C] = b2[0x1C] + b2[0x1D]; + b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0; + b1[0x1E] = b2[0x1E] + b2[0x1F]; + b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0; + b1[0x1E] += b1[0x1F]; + b1[0x1C] += b1[0x1E]; + b1[0x1E] += b1[0x1D]; + b1[0x1D] += b1[0x1F]; + } + + out0[0x10*16] = b1[0x00]; + out0[0x10*12] = b1[0x04]; + out0[0x10* 8] = b1[0x02]; + out0[0x10* 4] = b1[0x06]; + out0[0x10* 0] = b1[0x01]; + out1[0x10* 0] = b1[0x01]; + out1[0x10* 4] = b1[0x05]; + out1[0x10* 8] = b1[0x03]; + out1[0x10*12] = b1[0x07]; + + b1[0x08] += b1[0x0C]; + out0[0x10*14] = b1[0x08]; + b1[0x0C] += b1[0x0a]; + out0[0x10*10] = b1[0x0C]; + b1[0x0A] += b1[0x0E]; + out0[0x10* 6] = b1[0x0A]; + b1[0x0E] += b1[0x09]; + out0[0x10* 2] = b1[0x0E]; + b1[0x09] += b1[0x0D]; + out1[0x10* 2] = b1[0x09]; + b1[0x0D] += b1[0x0B]; + out1[0x10* 6] = b1[0x0D]; + b1[0x0B] += b1[0x0F]; + out1[0x10*10] = b1[0x0B]; + out1[0x10*14] = b1[0x0F]; + + b1[0x18] += b1[0x1C]; + out0[0x10*15] = b1[0x10] + b1[0x18]; + out0[0x10*13] = b1[0x18] + b1[0x14]; + b1[0x1C] += b1[0x1a]; + out0[0x10*11] = b1[0x14] + b1[0x1C]; + out0[0x10* 9] = b1[0x1C] + b1[0x12]; + b1[0x1A] += b1[0x1E]; + out0[0x10* 7] = b1[0x12] + b1[0x1A]; + out0[0x10* 5] = b1[0x1A] + b1[0x16]; + b1[0x1E] += b1[0x19]; + out0[0x10* 3] = b1[0x16] + b1[0x1E]; + out0[0x10* 1] = b1[0x1E] + b1[0x11]; + b1[0x19] += b1[0x1D]; + out1[0x10* 1] = b1[0x11] + b1[0x19]; + out1[0x10* 3] = b1[0x19] + b1[0x15]; + b1[0x1D] += b1[0x1B]; + out1[0x10* 5] = b1[0x15] + b1[0x1D]; + out1[0x10* 7] = b1[0x1D] + b1[0x13]; + b1[0x1B] += b1[0x1F]; + out1[0x10* 9] = b1[0x13] + b1[0x1B]; + out1[0x10*11] = b1[0x1B] + b1[0x17]; + out1[0x10*13] = b1[0x17] + b1[0x1F]; + out1[0x10*15] = b1[0x1F]; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifndef FMOD_SUPPORT_MPEG_SIMD /* SIMD based decoding */ + + + /* old WRITE_SAMPLE */ +#define WRITE_SAMPLE(samples,sum) \ + if( (sum) > 32767.0f) { *(samples) = 0x7fff; } \ + else if( (sum) < -32768.0f) { *(samples) = -0x8000; } \ + else { *(samples) = (short)sum; } + +FMOD_RESULT CodecMPEG::synthC(float *b0, int bo1, int channelskip, signed short *samples) +{ + register int j; + float *window = FMOD_Mpeg_DecWin + 16 - bo1; + + for (j=16;j;j--,b0+=0x10,window+=0x20,samples+=channelskip) + { + float sum; + sum = window[0x0] * b0[0x0]; + sum -= window[0x1] * b0[0x1]; + sum += window[0x2] * b0[0x2]; + sum -= window[0x3] * b0[0x3]; + sum += window[0x4] * b0[0x4]; + sum -= window[0x5] * b0[0x5]; + sum += window[0x6] * b0[0x6]; + sum -= window[0x7] * b0[0x7]; + sum += window[0x8] * b0[0x8]; + sum -= window[0x9] * b0[0x9]; + sum += window[0xA] * b0[0xA]; + sum -= window[0xB] * b0[0xB]; + sum += window[0xC] * b0[0xC]; + sum -= window[0xD] * b0[0xD]; + sum += window[0xE] * b0[0xE]; + sum -= window[0xF] * b0[0xF]; + sum *= 32767.0f; + + #if (defined(PLATFORM_WINDOWS) || defined(PLATFORM_XBOX)) && defined(PLATFORM_32BIT) + { + signed int val; + __asm fld sum + __asm fistp val + if (val > 32767) val = 32767; + if (val < -32768) val = 32768; + *samples = (signed short)val; + } + #else + WRITE_SAMPLE(samples,sum); + #endif + } + + { + float sum; + sum = window[0x0] * b0[0x0]; + sum += window[0x2] * b0[0x2]; + sum += window[0x4] * b0[0x4]; + sum += window[0x6] * b0[0x6]; + sum += window[0x8] * b0[0x8]; + sum += window[0xA] * b0[0xA]; + sum += window[0xC] * b0[0xC]; + sum += window[0xE] * b0[0xE]; + sum *= 32767.0f; + + #if (defined(PLATFORM_WINDOWS) || defined(PLATFORM_XBOX)) && defined(PLATFORM_32BIT) + { + signed int val; + __asm fld sum + __asm fistp val + if (val > 32767) val = 32767; + if (val < -32768) val = 32768; + *samples = (signed short)val; + } + #else + WRITE_SAMPLE(samples,sum); + #endif + + b0-=0x10,window-=0x20,samples+=channelskip; + } + window += bo1<<1; + + for (j=15;j;j--,b0-=0x10,window-=0x20,samples+=channelskip) + { + float sum; + sum = -window[-0x1] * b0[0x0]; + sum -= window[-0x2] * b0[0x1]; + sum -= window[-0x3] * b0[0x2]; + sum -= window[-0x4] * b0[0x3]; + sum -= window[-0x5] * b0[0x4]; + sum -= window[-0x6] * b0[0x5]; + sum -= window[-0x7] * b0[0x6]; + sum -= window[-0x8] * b0[0x7]; + sum -= window[-0x9] * b0[0x8]; + sum -= window[-0xA] * b0[0x9]; + sum -= window[-0xB] * b0[0xA]; + sum -= window[-0xC] * b0[0xB]; + sum -= window[-0xD] * b0[0xC]; + sum -= window[-0xE] * b0[0xD]; + sum -= window[-0xF] * b0[0xE]; + sum -= window[-0x10] * b0[0xF]; + sum *= 32767.0f; + + #if (defined(PLATFORM_WINDOWS) || defined(PLATFORM_XBOX)) && defined(PLATFORM_32BIT) + { + signed int val; + __asm fld sum + __asm fistp val + if (val > 32767) val = 32767; + if (val < -32768) val = 32768; + *samples = (signed short)val; + } + #else + WRITE_SAMPLE(samples,sum); + #endif + } + + return FMOD_OK; +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::synth(void *samples, float *bandPtr, int channels, int channelskip) +{ + int count; + float *b0,*buf; + int bo1,bob1,bob2; + + mMemoryBlock->mSynthBo--; + mMemoryBlock->mSynthBo &= 0xf; + + bob1 = mMemoryBlock->mSynthBo & 0x1; + bob2 = bob1 ^ 1; + bo1 = mMemoryBlock->mSynthBo+bob2; + + if (!samples) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0 ; count < channels; count++) + { + buf = &mMemoryBlock->mSynthBuffs[count * 576]; + b0 = &buf[bob2 * 288]; + + if (mMemoryBlock->mFrame.lay == 2) + { + #ifdef FMOD_SUPPORT_MPEG_SIMD + FMOD_Mpeg_DCT64(&buf[bob1 * 288]+((mMemoryBlock->mSynthBo+bob1)&0xf),b0+bo1, bandPtr+(count*4*SBLIMIT)); + #else + dct64(&buf[bob1 * 288]+((mMemoryBlock->mSynthBo+bob1)&0xf),b0+bo1, bandPtr+(count*4*SBLIMIT)); + #endif + } + if (mMemoryBlock->mFrame.lay == 3) + { + #ifdef FMOD_SUPPORT_MPEG_SIMD + FMOD_Mpeg_DCT64(&buf[bob1 * 288]+((mMemoryBlock->mSynthBo+bob1)&0xf),b0+bo1, bandPtr+(count*SSLIMIT*SBLIMIT)); + #else + dct64(&buf[bob1 * 288]+((mMemoryBlock->mSynthBo+bob1)&0xf),b0+bo1, bandPtr+(count*SSLIMIT*SBLIMIT)); + #endif + } + + #ifdef FMOD_SUPPORT_MPEG_SIMD + FMOD_Mpeg_Synth_FPU(b0, bo1, channelskip, (short *)samples + count); + #else + synthC(b0, bo1, channelskip, (short *)samples + count); + #endif + } + + return FMOD_OK; +} + +#endif // #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +} + +#endif /* defined(FMOD_SUPPORT_MPEG) */ + + diff --git a/src/fmod_codec_mpeg_layer2.cpp b/src/fmod_codec_mpeg_layer2.cpp new file mode 100755 index 0000000..de06808 --- /dev/null +++ b/src/fmod_codec_mpeg_layer2.cpp @@ -0,0 +1,615 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MPEG + +#ifdef FMOD_SUPPORT_MPEG_LAYER2 + +#include "fmod_codec_mpeg.h" +#include "fmod_types.h" + +#include + +#ifndef PLATFORM_PS3_SPU + #define USE_MULS_TABLE +#endif + +namespace FMOD +{ + +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +unsigned char CodecMPEG::gGrp3Tab[32 * 3] = { 0, }; // used: 27 +unsigned char CodecMPEG::gGrp5Tab[128 * 3] = { 0, }; // used: 125 +unsigned char CodecMPEG::gGrp9Tab[1024 * 3] = { 0, }; // used: 729 + + +#ifdef USE_MULS_TABLE + float CodecMPEG::gMulsTab[27][64]; // also used by layer 1 + + #define gMuls(_x, _y) gMulsTab[_x][_y] +#else + + float gMulMul[27] = + { + 0.0f , -2.0f/3.0f , 2.0f/3.0f , + 2.0f/7.0f , 2.0f/15.0f , 2.0f/31.0f, 2.0f/63.0f , 2.0f/127.0f , 2.0f/255.0f , + 2.0f/511.0f , 2.0f/1023.0f , 2.0f/2047.0f , 2.0f/4095.0f , 2.0f/8191.0f , + 2.0f/16383.0f , 2.0f/32767.0f , 2.0f/65535.0f , + -4.0f/5.0f , -2.0f/5.0f , 2.0f/5.0f, 4.0f/5.0f , + -8.0f/9.0f , -4.0f/9.0f , -2.0f/9.0f , 2.0f/9.0f , 4.0f/9.0f , 8.0f/9.0f + }; + + #ifdef PLATFORM_PS3_SPU + + float gMuls(int _x, int _y) { return ((_y) == 63) ? 0.0f : (float)(gMulMul[(_x)] * FMOD_POW(2.0f, (3 - (int)(_y)) / 3.0f)); } /* Function call to make code smaller */ + + #else + + #define gMuls(_x, _y) ((_y) == 63) ? 0.0f : (float)(gMulMul[(_x)] * FMOD_POW(2.0f, (3 - (int)(_y)) / 3.0f)) + + #endif + +#endif + +/* + * Layer 2 Alloc tables .. + * most other tables are calculated on program start (which is (of course) + * not ISO-conform) .. + * Layer-3 huffman table is in huffman.h + */ + +struct al_table CodecMPEG::gAlloc0[] = +{ + {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, + {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, + {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, + {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, + {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, + {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767} +}; + +struct al_table CodecMPEG::gAlloc1[] = +{ + {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, + {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, + {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, + {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, + {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, + {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767}, + {2,0},{5,3},{7,5},{16,-32767} }; + +struct al_table CodecMPEG::gAlloc2[] = +{ + {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, + {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, + {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, + {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63} +}; + +struct al_table CodecMPEG::gAlloc3[] = +{ + {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, + {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, + {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, + {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63} +}; + +struct al_table CodecMPEG::gAlloc4[] = +{ + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, + {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, + {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9}, + {2,0},{5,3},{7,5},{10,9} +}; + + +#if defined(PLATFORM_PS3_SPU_STREAMDECODE) || !defined(PLATFORM_PS3_SPU) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::initLayer2() +{ + float mulmul[27] = + { + 0.0f , -2.0f/3.0f , 2.0f/3.0f , + 2.0f/7.0f , 2.0f/15.0f , 2.0f/31.0f, 2.0f/63.0f , 2.0f/127.0f , 2.0f/255.0f , + 2.0f/511.0f , 2.0f/1023.0f , 2.0f/2047.0f , 2.0f/4095.0f , 2.0f/8191.0f , + 2.0f/16383.0f , 2.0f/32767.0f , 2.0f/65535.0f , + -4.0f/5.0f , -2.0f/5.0f , 2.0f/5.0f, 4.0f/5.0f , + -8.0f/9.0f , -4.0f/9.0f , -2.0f/9.0f , 2.0f/9.0f , 4.0f/9.0f , 8.0f/9.0f + }; + char base[3][9] = + { + { 1 , 0, 2 , } , + { 17, 18, 0 , 19, 20 , } , + { 21, 1, 22, 23, 0, 24, 25, 2, 26 } + }; + int i,j,k,l,len; + int tablen[3] = { 3 , 5 , 9 }; + unsigned char *itable,*tables[3] = { gGrp3Tab , gGrp5Tab , gGrp9Tab }; + + for(i=0;i<3;i++) + { + itable = tables[i]; + len = tablen[i]; + for(j=0;jmFrame.stereo-1; + int sblimit = mMemoryBlock->mFrame.II_sblimit; + int jsbound = mMemoryBlock->mFrame.jsbound; + int sblimit2 = mMemoryBlock->mFrame.II_sblimit<mFrame.alloc; + int i; + unsigned int scfsi_buf[64]; + unsigned int *scfsi,*bita; + int sc,step; + + bita = bit_alloc; + + if(stereo) + { + for (i=jsbound;i;i--,alloc1+=(1<bits); + *bita++ = (char)getBits(step); + } + for (i=sblimit-jsbound;i;i--,alloc1+=(1<bits); + bita[1] = bita[0]; + bita+=2; + } + + bita = bit_alloc; + scfsi=scfsi_buf; + + for (i=sblimit2;i;i--) + if (*bita++) + *scfsi++ = (char) getBitsFast(2); + } + else // mono + { + for (i=sblimit;i;i--,alloc1+=(1<bits); + } + + bita = bit_alloc; + scfsi=scfsi_buf; + + for (i=sblimit;i;i--) + { + if (*bita++) + { + *scfsi++ = (char) getBitsFast(2); + } + } + } + + bita = bit_alloc; + scfsi=scfsi_buf; + for (i=sblimit2;i;i--) + { + if (*bita++) + { + switch (*scfsi++) + { + case 0: + *scale++ = getBitsFast(6); + *scale++ = getBitsFast(6); + *scale++ = getBitsFast(6); + break; + case 1 : + *scale++ = sc = getBitsFast(6); + *scale++ = sc; + *scale++ = getBitsFast(6); + break; + case 2: + *scale++ = sc = getBitsFast(6); + *scale++ = sc; + *scale++ = sc; + break; + default: // case 3 + *scale++ = getBitsFast(6); + *scale++ = sc = getBitsFast(6); + *scale++ = sc; + break; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::II_step_two(unsigned int *bit_alloc,float fraction[2][4][SBLIMIT],int *scale,int x1) +{ + int i,j,k,ba; + int stereo = mMemoryBlock->mFrame.stereo; + int sblimit = mMemoryBlock->mFrame.II_sblimit; + int jsbound = mMemoryBlock->mFrame.jsbound; + struct al_table *alloc2,*alloc1 = mMemoryBlock->mFrame.alloc; + unsigned int * bita=bit_alloc; + int d1,step; + + for (i=0;ibits; + for (j=0;jbits; + if( (d1=alloc2->d) < 0) + { + float cm = gMuls(k, scale[x1]); + + fraction[j][0][i] = ((float) ((int)getBits(k) + d1)) * cm; + fraction[j][1][i] = ((float) ((int)getBits(k) + d1)) * cm; + fraction[j][2][i] = ((float) ((int)getBits(k) + d1)) * cm; + } + else + { + unsigned char *table[] = { 0,0,0, gGrp3Tab, 0, gGrp5Tab, 0, 0, 0, gGrp9Tab }; + unsigned int idx,m=scale[x1]; + unsigned char *tab; + + idx = (unsigned int) getBits(k); + tab = table[d1] + idx + idx + idx; + + fraction[j][0][i] = gMuls(*tab++, m); + fraction[j][1][i] = gMuls(*tab++, m); + fraction[j][2][i] = gMuls(*tab, m); + } + scale+=3; + } + else + { + fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0f; + } + } + } + + + for (i=jsbound;ibits; + bita++; // channel 1 and channel 2 bitalloc are the same + + ba=*bita++; + if (ba) + { + k=(alloc2 = alloc1+ba)->bits; + if( (d1=alloc2->d) < 0) + { + float cm; + cm = gMuls(k, scale[x1+3]); + fraction[1][0][i] = (fraction[0][0][i] = (float) ((int)getBits(k) + d1) ) * cm; + fraction[1][1][i] = (fraction[0][1][i] = (float) ((int)getBits(k) + d1) ) * cm; + fraction[1][2][i] = (fraction[0][2][i] = (float) ((int)getBits(k) + d1) ) * cm; + cm = gMuls(k, scale[x1]); + fraction[0][0][i] *= cm; fraction[0][1][i] *= cm; fraction[0][2][i] *= cm; + } + else + { + unsigned char *table[] = { 0,0,0, gGrp3Tab, 0, gGrp5Tab, 0, 0, 0, gGrp9Tab }; + unsigned int idx,m1,m2; + unsigned char *tab; + + m1 = scale[x1]; m2 = scale[x1+3]; + idx = (unsigned int) getBits(k); + tab = table[d1] + idx + idx + idx; + fraction[0][0][i] = gMuls(*tab, m1); fraction[1][0][i] = gMuls(*tab++, m2); + fraction[0][1][i] = gMuls(*tab, m1); fraction[1][1][i] = gMuls(*tab++, m2); + fraction[0][2][i] = gMuls(*tab, m1); fraction[1][2][i] = gMuls(*tab, m2); + } + scale+=6; + } + else + { + fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] = + fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0.0f; + } + } + + for(i=sblimit;imFrame.lsf) + { + table = 4; + } + else + { + table = translate[mMemoryBlock->mFrame.sampling_frequency][2-mMemoryBlock->mFrame.stereo][mMemoryBlock->mFrame.bitrate_index]; + } + + sblim = sblims[table]; + + mMemoryBlock->mFrame.alloc = tables[table]; + mMemoryBlock->mFrame.II_sblimit = sblim; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::decodeLayer2(void *pcm_sample, unsigned int *outlen) +{ + int i,j = 0; + int channels = mMemoryBlock->mFrame.stereo; + float fraction[2][4][SBLIMIT]; // pick_table clears unused subbands + unsigned int bit_alloc[64]; + int scale[192]; + int inc = SBLIMIT * sizeof(signed short); + + II_step_one(bit_alloc, scale); + + *outlen = 0; + + for (i=0;i>2); + + for (j=0;j<3;j++) + { + synth(pcm_sample, fraction[0][j], channels, waveformat->channels > 2 ? waveformat->channels : channels); + + pcm_sample = (char *)pcm_sample + inc * waveformat->channels; + *outlen += inc * channels; + } + } + + return FMOD_OK; +} + + +#endif //#ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +} + +#endif // FMOD_SUPPORT_MPEG_LAYER2 + +#endif // FMOD_SUPPORT_MPEG + diff --git a/src/fmod_codec_mpeg_layer3.cpp b/src/fmod_codec_mpeg_layer3.cpp new file mode 100755 index 0000000..661d11f --- /dev/null +++ b/src/fmod_codec_mpeg_layer3.cpp @@ -0,0 +1,3126 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MPEG + +#ifdef FMOD_SUPPORT_MPEG_LAYER3 + +#include "fmod_codec_mpeg.h" +#include "fmod_types.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_common_spu.h" + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" + #include + #include "../lib/sony/spu/Mp3DecSpuLib.h" +#endif + +#include + +namespace FMOD +{ +#ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +#ifdef USE_ISPOW_TABLE + + float CodecMPEG::gIsPowTable[8207]; + + #define gIsPow(_x) gIsPowTable[_x] + +#else + + #define FOUROVERTHREE 1.33333333333333333333f + + #define gIsPow(_i) (float)FMOD_POW((float) _i, FOUROVERTHREE) + +#endif + +float CodecMPEG::gAaCa[8]; +float CodecMPEG::gAaCs[8]; +float CodecMPEG::gWin[4][36]; +float CodecMPEG::gWin1[4][36]; +float CodecMPEG::gGainPow2[256 + 118 + 4]; +float CodecMPEG::gCos6_1; +float CodecMPEG::gCos6_2; +float CodecMPEG::gTfCos36[9]; +float CodecMPEG::gTfCos12[3]; +float CodecMPEG::gCos9[3]; +float CodecMPEG::gCos18[3]; +int CodecMPEG::gLongLimit[9][23]; +int CodecMPEG::gShortLimit[9][14]; + +struct bandInfoStruct CodecMPEG::gBandInfo[9] = +{ + +/* MPEG 1.0 */ + { + {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576}, + {4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158}, + {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 40 * 3, 52 * 3, 66 * 3, 84 * 3, 106 * 3, 136 * 3, 192 * 3}, + {4, 4, 4, 4, 6, 8, 10, 12, 14, 18, 22, 30, 56}}, + + { + {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576}, + {4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192}, + {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 28 * 3, 38 * 3, 50 * 3, 64 * 3, 80 * 3, 100 * 3, 126 * 3, 192 * 3}, + {4, 4, 4, 4, 6, 6, 10, 12, 14, 16, 20, 26, 66}}, + + { + {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576}, + {4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26}, + {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 42 * 3, 58 * 3, 78 * 3, 104 * 3, 138 * 3, 180 * 3, 192 * 3}, + {4, 4, 4, 4, 6, 8, 12, 16, 20, 26, 34, 42, 12}}, + +/* MPEG 2.0 */ + { + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 24 * 3, 32 * 3, 42 * 3, 56 * 3, 74 * 3, 100 * 3, 132 * 3, 174 * 3, 192 * 3}, + {4, 4, 4, 6, 6, 8, 10, 14, 18, 26, 32, 42, 18}}, + + { + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 330, 394, 464, 540, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 18, 22, 26, 32, 38, 46, 52, 64, 70, 76, 36}, + {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3, 104 * 3, 136 * 3, 180 * 3, 192 * 3}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 32, 44, 12}}, + + { + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3, 104 * 3, 134 * 3, 174 * 3, 192 * 3}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}}, +/* MPEG 2.5 */ + { + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}}, + { + {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576}, + {6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54}, + {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576}, + {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}}, + { + {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576}, + {12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 76, 90, 2, 2, 2, 2, 2}, + {0, 24, 48, 72, 108, 156, 216, 288, 372, 480, 486, 492, 498, 576}, + {8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26}}, +}; + + +/* + * probably we could save a few bytes of memory, because the + * smaller tables are often the part of a bigger table + */ + +struct newhuff +{ + unsigned int linbits; + short *table; +}; + +static short tab0[] = +{ + 0 +}; + +static short tab1[] = +{ + -5, -3, -1, 17, 1, 16, 0 +}; + +static short tab2[] = +{ + -15, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 17, -1, 1, + 16, 0 +}; + +static short tab3[] = +{ + -13, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 16, 17, -1, + 1, 0 +}; + +static short tab5[] = +{ + -29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, 49, -3, -1, 19, + 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16, + 0 +}; + +static short tab6[] = +{ + -25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, 50, 48, -1, 19, + 49, -3, -1, 34, 2, 18, -3, -1, 33, 32, 1, -1, 17, -1, 16, + 0 +}; + +static short tab7[] = +{ + -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83, + -1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1, + 80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7, + -3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18, + -5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0 +}; + +static short tab8[] = +{ + -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83, + -3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52, + 67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4, + 64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1, + 2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0 +}; + +static short tab9[] = +{ + -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1, + 84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67, + -1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5, + -3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2, + 18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0 +}; + +static short tab10[] = +{ +-125,-121,-111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118, + 87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3, + -1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1, + 100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23, + -17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81, + -1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7, + -3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1, + 50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1, + 2, 32, 17, -1, 1, 16, 0 +}; + +static short tab11[] = +{ +-121,-113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117, + -3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55, + -1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114, + -1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96, + -1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38, + 6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1, + 36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50, + -1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2, + 32, 17, -3, -1, 1, 16, 0 +}; + +static short tab12[] = +{ +-115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87, + 117, -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115, + 85, 39, -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7, + 112, -1, 54, 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5, + -1, 38, 98, -5, -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37, + 82, -17, -7, -3, -1, 21, 81, -1, 52, 67, -5, -3, -1, 80, 4, + 36, -1, 66, 20, -3, -1, 51, 65, -1, 35, 50, -11, -7, -5, -3, + -1, 64, 3, 48, 19, -1, 49, 34, -1, 18, 33, -7, -5, -3, -1, + 2, 32, 0, 17, -1, 1, 16 +}; + +static short tab13[] = +{ +-509,-503,-475,-405,-333,-265,-205,-153,-115, -83, -53, -35, -21, -13, -9, + -7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, 223, -3, -1, 238, + 207, -1, 222, 191, -9, -3, -1, 251, 206, -1, 220, -1, 175, 233, -1, + 236, 221, -9, -5, -3, -1, 250, 205, 190, -1, 235, 159, -3, -1, 249, + 234, -1, 189, 219, -17, -9, -3, -1, 143, 248, -1, 204, -1, 174, 158, + -5, -1, 142, -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1, + 203, 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, 245, + 231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, 230, 243, -1, + 63, -1, 141, 216, -21, -9, -3, -1, 47, 242, -3, -1, 110, 156, 15, + -5, -3, -1, 201, 94, 171, -3, -1, 125, 215, 78, -11, -5, -3, -1, + 200, 214, 62, -1, 185, -1, 155, 170, -1, 31, 241, -23, -13, -5, -1, + 240, -1, 186, 229, -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1, + 46, 14, -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3, + -1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, 108, -1, + 198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, -1, 210, 29, -5, + -1, 183, -1, 92, 197, -3, -1, 153, 122, 195, -7, -5, -3, -1, 167, + 151, 75, 209, -3, -1, 13, 208, -1, 138, 168, -11, -7, -3, -1, 76, + 196, -1, 107, 182, -1, 60, 44, -3, -1, 194, 91, -3, -1, 181, 137, + 28, -43, -23, -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106, + -5, -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, 43, + -1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, 119, 118, 178, + -11, -3, -1, 27, 177, -3, -1, 11, 176, -1, 150, 74, -7, -3, -1, + 58, 163, -1, 89, 149, -1, 42, 162, -47, -23, -9, -3, -1, 26, 161, + -3, -1, 10, 104, 160, -5, -3, -1, 134, 73, 147, -3, -1, 57, 88, + -1, 133, 103, -9, -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1, + 131, -1, 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25, + 145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, 70, 100, + 40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, 39, 23, -1, 113, + -1, 85, 7, -7, -3, -1, 112, 54, -1, 99, 69, -3, -1, 84, 38, + -1, 98, 53, -5, -1, 129, -1, 8, 128, -3, -1, 22, 97, -1, 6, + 96, -13, -9, -5, -3, -1, 83, 68, 37, -1, 82, 5, -1, 21, 81, + -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11, + -5, -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, 3, + -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16, + 0 +}; + +static short tab15[] = +{ +-495,-445,-355,-263,-183,-115, -77, -43, -27, -13, -7, -3, -1, 255, 239, + -1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, -1, 237, + 191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, 250, 190, -3, + -1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, 234, -1, 189, 219, + -3, -1, 143, 248, -1, 204, 158, -7, -3, -1, 233, 127, -1, 247, 173, + -3, -1, 218, 188, -1, 111, -1, 174, 15, -19, -11, -3, -1, 203, 246, + -3, -1, 142, 232, -1, 95, 157, -3, -1, 245, 126, -1, 231, 172, -9, + -3, -1, 202, 187, -3, -1, 217, 141, 79, -3, -1, 244, 63, -1, 243, + 216, -33, -17, -9, -3, -1, 230, 47, -1, 242, -1, 110, 240, -3, -1, + 31, 241, -1, 156, 201, -7, -3, -1, 94, 171, -1, 186, 229, -3, -1, + 125, 215, -1, 78, 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3, + -1, 214, 227, -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5, + -1, 225, -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124, + 199, -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1, + 198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, 183, + -5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, 168, 76, + -1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, -9, -3, -1, + 122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, 91, -55, -29, -15, + -7, -3, -1, 181, 28, -1, 137, 152, -3, -1, 193, 75, -1, 180, 106, + -5, -3, -1, 59, 121, 179, -3, -1, 151, 136, -1, 43, 90, -11, -5, + -1, 178, -1, 165, 27, -1, 177, -1, 176, 105, -7, -3, -1, 150, 74, + -1, 164, 120, -3, -1, 135, 58, 163, -17, -7, -3, -1, 89, 149, -1, + 42, 162, -3, -1, 26, 161, -3, -1, 10, 160, 104, -7, -3, -1, 134, + 73, -1, 148, 57, -5, -1, 147, -1, 119, 9, -1, 88, 133, -53, -29, + -13, -7, -3, -1, 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7, + -3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7, + -3, -1, 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86, + -3, -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100, + 23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, 69, + -1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, -19, -9, + -5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, 81, -3, -1, + 5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, -1, 65, -1, 20, + 4, -9, -3, -1, 35, 50, -3, -1, 64, 3, 19, -3, -1, 49, 48, + 34, -9, -7, -3, -1, 18, 33, -1, 2, 32, 17, -3, -1, 1, 16, + 0 +}; + +static short tab16[] = +{ +-509,-503,-461,-323,-103, -37, -27, -15, -7, -3, -1, 239, 254, -1, 223, + 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, 159, -3, + -1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, 255, -9, -5, + -3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, -1, 63, -29, -19, + -13, -7, -5, -1, 206, -1, 236, 221, 222, -1, 233, -1, 234, 217, -1, + 238, -1, 237, 235, -3, -1, 190, 205, -3, -1, 220, 219, 174, -11, -5, + -1, 204, -1, 173, 218, -3, -1, 126, 172, 202, -5, -3, -1, 201, 125, + 94, 189, 242, -93, -5, -3, -1, 47, 15, 31, -1, 241, -49, -25, -13, + -5, -1, 158, -1, 188, 203, -3, -1, 142, 232, -1, 157, 231, -7, -3, + -1, 187, 141, -1, 216, 110, -1, 230, 156, -13, -7, -3, -1, 171, 186, + -1, 229, 215, -1, 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1, + 214, 155, -19, -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169, + -5, -1, 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213, + -3, -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154, + 108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, -1, + 153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, 28, -1, + 192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, -1, 211, 45, + -1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, -3, -1, 196, 107, + -1, 195, 167, -1, 44, -1, 194, 181, -23, -13, -7, -3, -1, 193, 12, + -1, 75, 180, -3, -1, 106, 166, 179, -5, -3, -1, 90, 165, 43, -1, + 178, 27, -13, -5, -1, 177, -1, 11, 176, -3, -1, 105, 150, -1, 74, + 164, -5, -3, -1, 120, 135, 163, -3, -1, 58, 89, 42, -97, -57, -33, + -19, -11, -5, -3, -1, 149, 104, 161, -3, -1, 134, 119, 148, -5, -3, + -1, 73, 87, 103, 162, -5, -1, 26, -1, 10, 160, -3, -1, 57, 147, + -1, 88, 133, -9, -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1, + 145, -1, 144, 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3, + -1, 102, 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1, + 8, 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3, + -1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, -1, + 99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, 96, -9, + -5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, 21, 5, -33, + -23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, + -5, -1, 65, -1, 4, 64, -1, 35, 50, -3, -1, 19, 49, -3, -1, + 3, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16, + 0 +}; + +static short tab24[] = +{ +-451,-117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1, + 207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, 248, -9, + -5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, 245, -1, 79, + 244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, -1, 241, -1, 31, + 240, -25, -9, -1, 15, -3, -1, 238, 222, -1, 237, 206, -7, -3, -1, + 236, 221, -1, 190, 235, -3, -1, 205, 220, -1, 174, 234, -15, -7, -3, + -1, 189, 219, -1, 204, 158, -3, -1, 233, 173, -1, 218, 188, -7, -3, + -1, 203, 142, -1, 232, 157, -3, -1, 217, 126, -1, 231, 172, 255,-235, +-143, -77, -45, -25, -15, -7, -3, -1, 202, 187, -1, 141, 216, -5, -3, + -1, 14, 224, 13, 230, -5, -3, -1, 110, 156, 201, -1, 94, 186, -9, + -5, -1, 229, -1, 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1, + 78, 46, 62, -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185, + 170, -1, 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199, + 77, -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3, + -1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, -3, + -1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, 76, 196, + -3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, 195, 122, -1, + 167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, -19, -7, -3, -1, + 137, 152, -1, 193, 75, -5, -3, -1, 192, 11, 59, -3, -1, 176, 10, + 26, -5, -1, 180, -1, 106, 166, -3, -1, 121, 151, -3, -1, 160, 9, + 144, -9, -3, -1, 179, 136, -3, -1, 43, 90, 178, -7, -3, -1, 165, + 27, -1, 177, 105, -1, 150, 164, -17, -9, -5, -3, -1, 74, 120, 135, + -1, 58, 163, -3, -1, 89, 149, -1, 42, 162, -7, -3, -1, 161, 104, + -1, 134, 119, -3, -1, 73, 148, -1, 57, 147, -63, -31, -15, -7, -3, + -1, 88, 133, -1, 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3, + -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7, + -3, -1, 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86, + 101, -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15, + -7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, 84, + -7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, 53, -1, + 83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, 21, -1, 5, + 80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, 51, 20, -9, -5, + -1, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -7, -5, -3, -1, + 3, 48, 34, 18, -1, 33, -1, 2, 32, -3, -1, 17, 1, -1, 16, + 0 +}; + +static short tab_c0[] = +{ + -29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, -3, -1, 7, 5, + 9, -3, -1, 6, 3, -1, 10, 12, -3, -1, 2, 1, -1, 4, 8, + 0 +}; + +static short tab_c1[] = +{ + -15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, 11, 10, -1, 9, + 8, -7, -3, -1, 7, 6, -1, 5, 4, -3, -1, 3, 2, -1, 1, + 0 +}; + + + +static struct newhuff ht[] = +{ + { /* 0 */ 0 , tab0 } , + { /* 2 */ 0 , tab1 } , + { /* 3 */ 0 , tab2 } , + { /* 3 */ 0 , tab3 } , + { /* 0 */ 0 , tab0 } , + { /* 4 */ 0 , tab5 } , + { /* 4 */ 0 , tab6 } , + { /* 6 */ 0 , tab7 } , + { /* 6 */ 0 , tab8 } , + { /* 6 */ 0 , tab9 } , + { /* 8 */ 0 , tab10 } , + { /* 8 */ 0 , tab11 } , + { /* 8 */ 0 , tab12 } , + { /* 16 */ 0 , tab13 } , + { /* 0 */ 0 , tab0 } , + { /* 16 */ 0 , tab15 } , + + { /* 16 */ 1 , tab16 } , + { /* 16 */ 2 , tab16 } , + { /* 16 */ 3 , tab16 } , + { /* 16 */ 4 , tab16 } , + { /* 16 */ 6 , tab16 } , + { /* 16 */ 8 , tab16 } , + { /* 16 */ 10, tab16 } , + { /* 16 */ 13, tab16 } , + { /* 16 */ 4 , tab24 } , + { /* 16 */ 5 , tab24 } , + { /* 16 */ 6 , tab24 } , + { /* 16 */ 7 , tab24 } , + { /* 16 */ 8 , tab24 } , + { /* 16 */ 9 , tab24 } , + { /* 16 */ 11, tab24 } , + { /* 16 */ 13, tab24 } +}; + +static struct newhuff htc[] = +{ + { /* 1 , 1 , */ 0 , tab_c0 } , + { /* 1 , 1 , */ 0 , tab_c1 } +}; + + +int CodecMPEG::gMapBuf0[9][152]; +int CodecMPEG::gMapBuf1[9][156]; +int CodecMPEG::gMapBuf2[9][44]; +int *CodecMPEG::gMap[9][3]; +int *CodecMPEG::gMapEnd[9][3]; +unsigned int CodecMPEG::gN_SLen2[512]; /* MPEG 2.0 slen for 'normal' mode */ +unsigned int CodecMPEG::gI_SLen2[256]; /* MPEG 2.0 slen for intensity stereo */ +float CodecMPEG::gTan1_1[16]; +float CodecMPEG::gTan2_1[16]; +float CodecMPEG::gTan1_2[16]; +float CodecMPEG::gTan2_2[16]; +float CodecMPEG::gPow1_1[2][16]; +float CodecMPEG::gPow2_1[2][16]; +float CodecMPEG::gPow1_2[2][16]; +float CodecMPEG::gPow2_2[2][16]; + + +#if defined(PLATFORM_PS3_SPU_STREAMDECODE) || !defined(PLATFORM_PS3_SPU) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::initLayer3(int down_sample_sblimit) +{ + int i, j, k, l; + + for (i = -256; i < 118 + 4; i++) + { + gGainPow2[i + 256] = (float)FMOD_POW((float) 2.0f, -0.25f * (float) (i + 210)); + } + +#ifdef USE_ISPOW_TABLE + for (i = 0; i < 8207; i++) + { + float x = 4.0f / 3.0f; + gIsPowTable[i] = (float)FMOD_POW((float) i, x); + } +#endif + + for (i = 0; i < 8; i++) + { + static float Ci[8] = + { + -0.6f, -0.535f, -0.33f, -0.185f, -0.095f, -0.041f, -0.0142f, -0.0037f + }; + float sq = FMOD_SQRT(1.0f + Ci[i] * Ci[i]); + + gAaCs[i] = 1.0f / (float)sq; + gAaCa[i] = (float)Ci[i] / (float)sq; + } + + for (i = 0; i < 18; i++) + { + gWin[0][i] = gWin[1][i] = 0.5f * (float)FMOD_SIN(FMOD_PI / 72.0f * (float) (2 * (i + 0) + 1)) / (float)FMOD_COS(FMOD_PI * (float) (2 * (i + 0) + 19) / 72.0f); + gWin[0][i + 18] = gWin[3][i + 18] = 0.5f * (float)FMOD_SIN(FMOD_PI / 72.0f * (float) (2 * (i + 18) + 1)) / (float)FMOD_COS(FMOD_PI * (float) (2 * (i + 18) + 19) / 72.0f); + } + for (i = 0; i < 6; i++) + { + gWin[1][i + 18] = 0.5f / (float)FMOD_COS(FMOD_PI * (float) (2 * (i + 18) + 19) / 72.0f); + gWin[3][i + 12] = 0.5f / (float)FMOD_COS(FMOD_PI * (float) (2 * (i + 12) + 19) / 72.0f); + gWin[1][i + 24] = 0.5f * (float)FMOD_SIN(FMOD_PI / 24.0f * (float) (2 * i + 13)) / (float)FMOD_COS(FMOD_PI * (float) (2 * (i + 24) + 19) / 72.0f); + gWin[1][i + 30] = gWin[3][i] = 0.0f; + gWin[3][i + 6] = 0.5f * (float)FMOD_SIN(FMOD_PI / 24.0f * (float) (2 * i + 1)) / (float)FMOD_COS(FMOD_PI * (float) (2 * (i + 6) + 19) / 72.0f); + } + + for (i = 0; i < 9; i++) + gTfCos36[i] = 0.5f / (float)FMOD_COS(FMOD_PI * (float) (i * 2 + 1) / 36.0f); + for (i = 0; i < 3; i++) + gTfCos12[i] = 0.5f / (float)FMOD_COS(FMOD_PI * (float) (i * 2 + 1) / 12.0f); + + gCos6_1 = (float)FMOD_COS(FMOD_PI / 6.0f * (float) 1); + gCos6_2 = (float)FMOD_COS(FMOD_PI / 6.0f * (float) 2); + + gCos9[0] = (float)FMOD_COS(1.0f * FMOD_PI / 9.0f); + gCos9[1] = (float)FMOD_COS(5.0f * FMOD_PI / 9.0f); + gCos9[2] = (float)FMOD_COS(7.0f * FMOD_PI / 9.0f); + gCos18[0] = (float)FMOD_COS(1.0f * FMOD_PI / 18.0f); + gCos18[1] = (float)FMOD_COS(11.0f * FMOD_PI / 18.0f); + gCos18[2] = (float)FMOD_COS(13.0f * FMOD_PI / 18.0f); + + for (i = 0; i < 12; i++) + { + gWin[2][i] = 0.5f * (float)FMOD_SIN(FMOD_PI / 24.0f * (float) (2 * i + 1)) / (float)FMOD_COS(FMOD_PI * (float) (2 * i + 7) / 24.0f); + } + + for (j = 0; j < 4; j++) + { + static int len[4] = + { + 36, 36, 12, 36 + }; + + for (i = 0; i < len[j]; i += 2) + { + gWin1[j][i] = +gWin[j][i]; + } + for (i = 1; i < len[j]; i += 2) + { + gWin1[j][i] = -gWin[j][i]; + } + } + + for (i = 0; i < 16; i++) + { + float t = FMOD_TAN((float) i * FMOD_PI / 12.0f); + + if ((float)(1.0f + t) == 0) + { + float one_plus_t = 1.19209e-007f; + + gTan1_1[i] = (float)t / one_plus_t; + gTan2_1[i] = 1.0f / one_plus_t; + gTan1_2[i] = (float)FMOD_SQRT2 * (float)t / one_plus_t; + gTan2_2[i] = (float)FMOD_SQRT2 / one_plus_t; + } + else + { + gTan1_1[i] = (float)t / (float)(1.0f + t); + gTan2_1[i] = 1.0f / (float)(1.0f + t); + gTan1_2[i] = (float)FMOD_SQRT2 * (float)t / (float)(1.0f + t); + gTan2_2[i] = (float)FMOD_SQRT2 / (float)(1.0f + t); + } + + for (j = 0; j < 2; j++) + { + float base = (float)FMOD_POW(2.0f, -0.25f * (j + 1.0f)); + float p1 = 1.0f, p2 = 1.0f; + + if (i > 0) + { + if (i & 1) + p1 = (float)FMOD_POW(base, (i + 1.0f) * 0.5f); + else + p2 = (float)FMOD_POW(base, i * 0.5f); + } + gPow1_1[j][i] = (float)p1; + gPow2_1[j][i] = (float)p2; + gPow1_2[j][i] = (float)FMOD_SQRT2 * (float)p1; + gPow2_2[j][i] = (float)FMOD_SQRT2 * (float)p2; + } + } + + for (j = 0; j < 9; j++) + { + struct bandInfoStruct *bi = &gBandInfo[j]; + int *mp; + int cb, lwin; + int *bdf; + + mp = gMap[j][0] = gMapBuf0[j]; + bdf = bi->longDiff; + for (i = 0, cb = 0; cb < 8; cb++, i += *bdf++) + { + *mp++ = (*bdf) >> 1; + *mp++ = i; + *mp++ = 3; + *mp++ = cb; + } + bdf = bi->shortDiff + 3; + for (cb = 3; cb < 13; cb++) + { + int l = (*bdf++) >> 1; + + for (lwin = 0; lwin < 3; lwin++) + { + *mp++ = l; + *mp++ = i + lwin; + *mp++ = lwin; + *mp++ = cb; + } + i += 6 * l; + } + gMapEnd[j][0] = mp; + + mp = gMap[j][1] = gMapBuf1[j]; + bdf = bi->shortDiff + 0; + for (i = 0, cb = 0; cb < 13; cb++) + { + int l = (*bdf++) >> 1; + + for (lwin = 0; lwin < 3; lwin++) + { + *mp++ = l; + *mp++ = i + lwin; + *mp++ = lwin; + *mp++ = cb; + } + i += 6 * l; + } + gMapEnd[j][1] = mp; + + mp = gMap[j][2] = gMapBuf2[j]; + bdf = bi->longDiff; + for (cb = 0; cb < 22; cb++) + { + *mp++ = (*bdf++) >> 1; + *mp++ = cb; + } + gMapEnd[j][2] = mp; + + } + + for (j = 0; j < 9; j++) + { + for (i = 0; i < 23; i++) + { + gLongLimit[j][i] = (gBandInfo[j].longIdx[i] - 1 + 8) / 18 + 1; + if (gLongLimit[j][i] > (down_sample_sblimit)) + gLongLimit[j][i] = down_sample_sblimit; + } + for (i = 0; i < 14; i++) + { + gShortLimit[j][i] = (gBandInfo[j].shortIdx[i] - 1) / 18 + 1; + if (gShortLimit[j][i] > (down_sample_sblimit)) + gShortLimit[j][i] = down_sample_sblimit; + } + } + + for (i = 0; i < 5; i++) + { + for (j = 0; j < 6; j++) + { + for (k = 0; k < 6; k++) + { + int n = k + j * 6 + i * 36; + + gI_SLen2[n] = i | (j << 3) | (k << 6) | (3 << 12); + } + } + } + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + for (k = 0; k < 4; k++) + { + int n = k + j * 4 + i * 16; + + gI_SLen2[n + 180] = i | (j << 3) | (k << 6) | (4 << 12); + } + } + } + for (i = 0; i < 4; i++) + { + for (j = 0; j < 3; j++) + { + int n = j + i * 3; + + gI_SLen2[n + 244] = i | (j << 3) | (5 << 12); + gN_SLen2[n + 500] = i | (j << 3) | (2 << 12) | (1 << 15); + } + } + + for (i = 0; i < 5; i++) + { + for (j = 0; j < 5; j++) + { + for (k = 0; k < 4; k++) + { + for (l = 0; l < 4; l++) + { + int n = l + k * 4 + j * 16 + i * 80; + + gN_SLen2[n] = i | (j << 3) | (k << 6) | (l << 9) | (0 << 12); + } + } + } + } + for (i = 0; i < 5; i++) + { + for (j = 0; j < 5; j++) + { + for (k = 0; k < 4; k++) + { + int n = k + j * 4 + i * 20; + + gN_SLen2[n + 400] = i | (j << 3) | (k << 6) | (1 << 12); + } + } + } + + return FMOD_OK; +} +#endif // PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + read additional side information + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_get_side_info_1(struct III_sideinfo *si, int stereo, int ms_stereo, int sfreq) +{ + int ch, gr; + int powdiff = 0; //(single == 3) ? 4 : 0; + + si->main_data_begin = getBits(9); + + if (stereo == 1) + si->private_bits = getBitsFast(5); + else + si->private_bits = getBitsFast(3); + + for (ch = 0; ch < stereo; ch++) + { + si->ch[ch].gr[0].scfsi = -1; + si->ch[ch].gr[1].scfsi = getBitsFast(4); + } + + for (gr = 0; gr < 2; gr++) + { + for (ch = 0; ch < stereo; ch++) + { + register struct gr_info_s *gr_info = &(si->ch[ch].gr[gr]); + + gr_info->part2_3_length = getBits(12); + gr_info->big_values = getBitsFast(9); + + if (gr_info->big_values > 288) + { + return FMOD_ERR_FILE_BAD; + } + if (gr_info->part2_3_length > 1L << 12) + { + return FMOD_ERR_FILE_BAD; + } + + gr_info->pow2gain = gGainPow2 + 256 - getBitsFast(8) + powdiff; + + if (ms_stereo) + { + gr_info->pow2gain += 2; + } + + gr_info->scalefac_compress = getBitsFast(4); + + // window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> gWin-sw-flag = 0 + + if (get1Bit()) + { + gr_info->block_type = getBitsFast(2); + gr_info->mixed_block_flag = get1Bit(); + gr_info->table_select[0] = getBitsFast(5); + gr_info->table_select[1] = getBitsFast(5); + /* + * table_select[2] not needed, because there is no region2, + * but to satisfy some verifications tools we set it either. + */ + gr_info->table_select[2] = 0; + + gr_info->full_gain[0] = gr_info->pow2gain + (getBitsFast(3) << 3); + gr_info->full_gain[1] = gr_info->pow2gain + (getBitsFast(3) << 3); + gr_info->full_gain[2] = gr_info->pow2gain + (getBitsFast(3) << 3); + + if (gr_info->block_type == 0) + { + return FMOD_ERR_FILE_BAD; + } + + /* + region_count/start parameters are implicit in this case. + */ + gr_info->region1start = 36 >> 1; + gr_info->region2start = 576 >> 1; + } + else + { + int r0c, r1c; + + gr_info->table_select[0] = getBitsFast(5); + gr_info->table_select[1] = getBitsFast(5); + gr_info->table_select[2] = getBitsFast(5); + + r0c = getBitsFast(4); + r1c = getBitsFast(3); + + gr_info->region1start = gBandInfo[sfreq].longIdx[r0c + 1] >> 1; + gr_info->region2start = gBandInfo[sfreq].longIdx[r0c + 1 + r1c + 1] >> 1; + gr_info->block_type = 0; + gr_info->mixed_block_flag = 0; + } + gr_info->preflag = get1Bit(); + gr_info->scalefac_scale = get1Bit(); + gr_info->count1table_select = get1Bit(); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Side Info for MPEG 2.0 / LSF + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_get_side_info_2(struct III_sideinfo *si, int stereo, int ms_stereo, int sfreq) +{ + int ch; + int powdiff = 0; //(single == 3) ? 4 : 0; + + si->main_data_begin = getBits(8); + + if (stereo == 1) + si->private_bits = get1Bit(); + else + si->private_bits = getBitsFast(2); + + for (ch = 0; ch < stereo; ch++) + { + register struct gr_info_s *gr_info = &(si->ch[ch].gr[0]); + + gr_info->part2_3_length = getBits(12); + gr_info->big_values = getBitsFast(9); + + if (gr_info->big_values > 288) + { + return FMOD_ERR_FILE_BAD; + } + + gr_info->pow2gain = gGainPow2 + 256 - getBitsFast(8) + powdiff; + + if (ms_stereo) + { + gr_info->pow2gain += 2; + } + + gr_info->scalefac_compress = getBits(9); + + /* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> gWin-sw-flag = 0 */ + if (get1Bit()) + { + int i; + + gr_info->block_type = getBitsFast(2); + gr_info->mixed_block_flag = get1Bit(); + gr_info->table_select[0] = getBitsFast(5); + gr_info->table_select[1] = getBitsFast(5); + /* + * table_select[2] not needed, because there is no region2, + * but to satisfy some verifications tools we set it either. + */ + gr_info->table_select[2] = 0; + for (i = 0; i < 3; i++) + gr_info->full_gain[i] = gr_info->pow2gain + (getBitsFast(3) << 3); + + if (gr_info->block_type == 0) + { + return FMOD_ERR_FILE_BAD; + } + + /* region_count/start parameters are implicit in this case. */ + /* check this again! */ + if (gr_info->block_type == 2) + gr_info->region1start = 36 >> 1; + + /* check this for 2.5 and sfreq=8 */ + else if (sfreq == 8) + gr_info->region1start = 108 >> 1; + else + gr_info->region1start = 54 >> 1; + + gr_info->region2start = 576 >> 1; + } + else + { + int r0c, r1c; + + gr_info->table_select[0] = getBitsFast(5); + gr_info->table_select[1] = getBitsFast(5); + gr_info->table_select[2] = getBitsFast(5); + + r0c = getBitsFast(4); + r1c = getBitsFast(3); + + gr_info->region1start = gBandInfo[sfreq].longIdx[r0c + 1] >> 1; + gr_info->region2start = gBandInfo[sfreq].longIdx[r0c + 1 + r1c + 1] >> 1; + gr_info->block_type = 0; + gr_info->mixed_block_flag = 0; + } + gr_info->scalefac_scale = get1Bit(); + gr_info->count1table_select = get1Bit(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + read scalefactors + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_get_scale_factors_1(int *scf,struct gr_info_s *gr_info, int *numbits) +{ + static unsigned char slen[2][16] = + { + {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4}, + {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3} + }; + int num0 = slen[0][gr_info->scalefac_compress]; + int num1 = slen[1][gr_info->scalefac_compress]; + + *numbits = 0; + + if (gr_info->block_type == 2) + { + int i = 18; + + *numbits = (num0 + num1) * 18; + + if (gr_info->mixed_block_flag) + { + for (i = 8; i; i--) + *scf++ = getBitsFast(num0); + i = 9; + *numbits -= num0; /* num0 * 17 + num1 * 18 */ + } + + for (; i; i--) + *scf++ = getBitsFast(num0); + for (i = 18; i; i--) + *scf++ = getBitsFast(num1); + + *scf++ = 0; + *scf++ = 0; + *scf++ = 0; /* short[13][0..2] = 0 */ + } + else + { + int i; + int scfsi = gr_info->scfsi; + + if (scfsi < 0) + { /* scfsi < 0 => granule == 0 */ + for (i = 11; i; i--) + *scf++ = getBitsFast(num0); + for (i = 10; i; i--) + *scf++ = getBitsFast(num1); + *numbits = (num0 + num1) * 10 + num0; + } + else + { + *numbits = 0; + if (!(scfsi & 0x8)) + { + for (i = 6; i; i--) + *scf++ = getBitsFast(num0); + *numbits += num0 * 6; + } + else + { + scf += 6; + } + + if (!(scfsi & 0x4)) + { + for (i = 5; i; i--) + *scf++ = getBitsFast(num0); + *numbits += num0 * 5; + } + else + { + scf += 5; + } + + if (!(scfsi & 0x2)) + { + for (i = 5; i; i--) + *scf++ = getBitsFast(num1); + *numbits += num1 * 5; + } + else + { + scf += 5; + } + + if (!(scfsi & 0x1)) + { + for (i = 5; i; i--) + *scf++ = getBitsFast(num1); + *numbits += num1 * 5; + } + else + { + scf += 5; + } + } + + *scf++ = 0; /* no l[21] in original sources */ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_get_scale_factors_2(int *scf,struct gr_info_s *gr_info,int i_stereo, int *numbits) +{ + unsigned char *pnt; + int i, j; + unsigned int slen; + int n = 0; + + *numbits = 0; + + static unsigned char stab[3][6][4] = + { + { + {6, 5, 5, 5}, + {6, 5, 7, 3}, + {11, 10, 0, 0}, + {7, 7, 7, 0}, + {6, 6, 6, 3}, + {8, 8, 5, 0}}, + { + {9, 9, 9, 9}, + {9, 9, 12, 6}, + {18, 18, 0, 0}, + {12, 12, 12, 0}, + {12, 9, 9, 6}, + {15, 12, 9, 0}}, + { + {6, 9, 9, 9}, + {6, 9, 12, 6}, + {15, 18, 0, 0}, + {6, 15, 12, 0}, + {6, 12, 9, 6}, + {6, 18, 9, 0} + } + }; + + if (i_stereo) /* i_stereo AND second channel -> do_layer3() checks this */ + { + slen = gI_SLen2[gr_info->scalefac_compress >> 1]; + } + else + { + slen = gN_SLen2[gr_info->scalefac_compress]; + } + + gr_info->preflag = (slen >> 15) & 0x1; + + n = 0; + if (gr_info->block_type == 2) + { + n++; + if (gr_info->mixed_block_flag) + n++; + } + + pnt = stab[n][(slen >> 12) & 0x7]; + + for (i = 0; i < 4; i++) + { + int num = slen & 0x7; + + slen >>= 3; + if (num) + { + for (j = 0; j < (int) (pnt[i]); j++) + *scf++ = getBitsFast(num); + *numbits += pnt[i] * num; + } + else + { + for (j = 0; j < (int) (pnt[i]); j++) + *scf++ = 0; + } + } + + n = (n << 1) + 1; + for (i = 0; i < n; i++) + { + *scf++ = 0; + } + + return FMOD_OK; +} + +static int pretab1[22] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0 +}; +static int pretab2[22] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#define getbitoffset() ((-mMemoryBlock->mBSI.mBitIndex)&0x7) +#define BITSHIFT ((int)((sizeof(int)-1)*8)) +#define REFRESH_MASK \ + while (num < BITSHIFT) { \ + mask |= ((unsigned int)getByte())<<(BITSHIFT-num); \ + num += 8; \ + part2remain -= 8; } + + +#if defined(PLATFORM_PS3_SPU_STREAMDECODE) || !defined(PLATFORM_PS3_SPU) +/* +[ + [DESCRIPTION] + read scalefactors + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_dequantize_sample(float xr[SBLIMIT][SSLIMIT],int *scf, struct gr_info_s *gr_info,int sfreq,int part2bits) +{ + int shift = 1 + gr_info->scalefac_scale; + float *xrpnt = (float *) xr; + int l[3],l3; + int part2remain = gr_info->part2_3_length - part2bits; + int *me; + + /* mhipp tree has this split up a bit */ + int num=getbitoffset(); + int mask = (int) getBits(num)<<(BITSHIFT+8-num); + part2remain -= num; + + { + int bv = gr_info->big_values; + int region1 = gr_info->region1start; + int region2 = gr_info->region2start; + + if (region1 > region2) + { + /* That's not optimal: it fixes a segfault with fuzzed data, but also apparently triggers where it shouldn't, see bug 1641196. + The benefit of not crashing / having this security risk is bigger than these few frames of a lame-3.70 file that aren't audible anyway + But still, I want to know if indeed this check or the old lame is at fault. */ + return FMOD_ERR_FILE_BAD; + } + + l3 = ((576>>1)-bv)>>1; + + /* + * we may lose the 'odd' bit here !! + * check this later again + */ + if(bv <= region1) + { + l[0] = bv; + l[1] = 0; + l[2] = 0; + } + else + { + l[0] = region1; + if(bv <= region2) + { + l[1] = bv - l[0]; + l[2] = 0; + } + else + { + l[1] = region2 - l[0]; + l[2] = bv - region2; + } + } + + /* BUGFIX - 3.62 - handle corrupted data */ + if (l[0] < 0) + { + l[0] = 0; + } + if (l[1] < 0) + { + l[1] = 0; + } + if (l[2] < 0) + { + l[2] = 0; + } + + } + + if(gr_info->block_type == 2) { + /* + * decoding with short or mixed mode BandIndex table + */ + int i,max[4]; + int step=0,lwin=3,cb=0; + register float v = 0.0f; + register int *m,mc; + + if(gr_info->mixed_block_flag) { + max[3] = -1; + max[0] = max[1] = max[2] = 2; + m = gMap[sfreq][0]; + me = gMapEnd[sfreq][0]; + } + else { + max[0] = max[1] = max[2] = max[3] = -1; + /* max[3] not really needed in this case */ + m = gMap[sfreq][1]; + me = gMapEnd[sfreq][1]; + } + + mc = 0; + for(i=0;i<2;i++) { + int lp = l[i]; + struct newhuff *h = ht+gr_info->table_select[i]; + for(;lp;lp--,mc--) { + register int x,y; + if( (!mc) ) { + mc = *m++; + xrpnt = ((float *) xr) + (*m++); + lwin = *m++; + cb = *m++; + if(lwin == 3) { + v = gr_info->pow2gain[(*scf++) << shift]; + step = 1; + } + else { + v = gr_info->full_gain[lwin][(*scf++) << shift]; + step = 3; + } + } + { + register short *val = h->table; + REFRESH_MASK; + while((y=*val++)<0) { + if (mask < 0) + val -= y; + num--; + mask <<= 1; + } + x = y >> 4; + y &= 0xf; + } + if(x == 15 && h->linbits) { + max[lwin] = cb; + REFRESH_MASK; + x += ((unsigned int) mask) >> (BITSHIFT+8-h->linbits); + num -= h->linbits+1; + mask <<= h->linbits; + if(mask < 0) + *xrpnt = -gIsPow(x) * v; + else + *xrpnt = gIsPow(x) * v; + mask <<= 1; + } + else if(x) { + max[lwin] = cb; + if(mask < 0) + *xrpnt = -gIsPow(x) * v; + else + *xrpnt = gIsPow(x) * v; + num--; + mask <<= 1; + } + else + *xrpnt = 0; + xrpnt += step; + if(y == 15 && h->linbits) { + max[lwin] = cb; + REFRESH_MASK; + y += ((unsigned int) mask) >> (BITSHIFT+8-h->linbits); + num -= h->linbits+1; + mask <<= h->linbits; + if(mask < 0) + *xrpnt = -gIsPow(y) * v; + else + *xrpnt = gIsPow(y) * v; + mask <<= 1; + } + else if(y) { + max[lwin] = cb; + if(mask < 0) + *xrpnt = -gIsPow(y) * v; + else + *xrpnt = gIsPow(y) * v; + num--; + mask <<= 1; + } + else + *xrpnt = 0; + xrpnt += step; + } + } + + for(;l3 && (part2remain+num > 0);l3--) + { + /* not mixing code and declarations to keep C89 happy */ + struct newhuff* h; + register short* val; + register short a; + /* This is only a humble hack to prevent a special segfault. */ + /* More insight into the real workings is still needed. */ + /* especially why there are (valid?) files that make xrpnt exceed the array with 4 bytes without segfaulting, more seems to be really bad, though. */ + if(!(xrpnt < &xr[SBLIMIT][0]+5)) + { + return FMOD_ERR_FILE_BAD; + } + h = htc+gr_info->count1table_select; + val = h->table; + + REFRESH_MASK; + while((a=*val++)<0) { + if (mask < 0) + val -= a; + num--; + mask <<= 1; + } + if(part2remain+num <= 0) { + num -= part2remain+num; + break; + } + + for(i=0;i<4;i++) { + if(!(i & 1)) { + if(!mc) { + mc = *m++; + xrpnt = ((float *) xr) + (*m++); + lwin = *m++; + cb = *m++; + if(lwin == 3) { + v = gr_info->pow2gain[(*scf++) << shift]; + step = 1; + } + else { + v = gr_info->full_gain[lwin][(*scf++) << shift]; + step = 3; + } + } + mc--; + } + if( (a & (0x8>>i)) ) { + max[lwin] = cb; + if(part2remain+num <= 0) { + break; + } + if(mask < 0) + *xrpnt = -v; + else + *xrpnt = v; + num--; + mask <<= 1; + } + else + *xrpnt = 0; + xrpnt += step; + } + } + + if(lwin < 3) { /* short band? */ + while(1) { + for(;mc > 0;mc--) { + *xrpnt = 0; xrpnt += 3; /* short band -> step=3 */ + *xrpnt = 0; xrpnt += 3; + } + if(m >= me) + break; + mc = *m++; + xrpnt = ((float *) xr) + *m++; + if(*m++ == 0) + break; /* optimize: field will be set to zero at the end of the function */ + m++; /* cb */ + } + } + + gr_info->maxband[0] = max[0]+1; + gr_info->maxband[1] = max[1]+1; + gr_info->maxband[2] = max[2]+1; + gr_info->maxbandl = max[3]+1; + + { + int rmax = max[0] > max[1] ? max[0] : max[1]; + rmax = (rmax > max[2] ? rmax : max[2]) + 1; + gr_info->maxb = rmax ? gShortLimit[sfreq][rmax] : gLongLimit[sfreq][max[3]+1]; + } + + } + else + { + /* + * decoding with 'long' BandIndex table (block_type != 2) + */ + int *pretab = gr_info->preflag ? pretab1 : pretab2; + int i,max = -1; + int cb = 0; + int *m = gMap[sfreq][2]; + register float v = 0.0f; + int mc = 0; + + /* + * long hash table values + */ + for(i=0;i<3;i++) + { + int lp = l[i]; + struct newhuff *h = ht+gr_info->table_select[i]; + + for(;lp;lp--,mc--) + { + int x,y; + if(!mc) + { + mc = *m++; + cb = *m++; + if(cb == 21) + v = 0.0f; + else + v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift]; + } + { + register short *val = h->table; + REFRESH_MASK; + while((y=*val++)<0) { + if (mask < 0) + val -= y; + num--; + mask <<= 1; + } + x = y >> 4; + y &= 0xf; + } + + if (x == 15 && h->linbits) { + max = cb; + REFRESH_MASK; + x += ((unsigned int) mask) >> (BITSHIFT+8-h->linbits); + num -= h->linbits+1; + mask <<= h->linbits; + if(mask < 0) + *xrpnt++ = -gIsPow(x) * v; + else + *xrpnt++ = gIsPow(x) * v; + mask <<= 1; + } + else if(x) { + max = cb; + if(mask < 0) + *xrpnt++ = -gIsPow(x) * v; + else + *xrpnt++ = gIsPow(x) * v; + num--; + mask <<= 1; + } + else + *xrpnt++ = 0; + + if (y == 15 && h->linbits) { + max = cb; + REFRESH_MASK; + y += ((unsigned int) mask) >> (BITSHIFT+8-h->linbits); + num -= h->linbits+1; + mask <<= h->linbits; + if(mask < 0) + *xrpnt++ = -gIsPow(y) * v; + else + *xrpnt++ = gIsPow(y) * v; + mask <<= 1; + } + else if(y) { + max = cb; + if(mask < 0) + *xrpnt++ = -gIsPow(y) * v; + else + *xrpnt++ = gIsPow(y) * v; + num--; + mask <<= 1; + } + else + *xrpnt++ = 0; + } + } + + /* + * short (count1table) values + */ + for(;l3 && (part2remain+num > 0);l3--) { + struct newhuff *h = htc+gr_info->count1table_select; + register short *val = h->table,a; + + REFRESH_MASK; + while((a=*val++)<0) { + if (mask < 0) + val -= a; + num--; + mask <<= 1; + } + if(part2remain+num <= 0) { + num -= part2remain+num; + break; + } + + for(i=0;i<4;i++) { + if(!(i & 1)) { + if(!mc) { + mc = *m++; + cb = *m++; + if(cb == 21) + v = 0.0f; + else + v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift]; + } + mc--; + } + if ( (a & (0x8>>i)) ) { + max = cb; + if(part2remain+num <= 0) { + break; + } + if(mask < 0) + *xrpnt++ = -v; + else + *xrpnt++ = v; + num--; + mask <<= 1; + } + else + *xrpnt++ = 0; + } + } + + gr_info->maxbandl = max+1; + gr_info->maxb = gLongLimit[sfreq][gr_info->maxbandl]; + } + + part2remain += num; + backbits(num); + num = 0; + + while(xrpnt < &xr[SBLIMIT][0]) + { + *xrpnt++ = 0; + } + + while( part2remain > 16 ) + { + getBits(16); /* Dismiss stuffing Bits */ + part2remain -= 16; + } + if(part2remain > 0) + { + getBits(part2remain); + } + else if(part2remain < 0) + { + return FMOD_ERR_FILE_BAD; /* -> error */ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + read scalefactors + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_dequantize_sample_ms(float xr[2][SBLIMIT][SSLIMIT], int *scf, struct gr_info_s *gr_info, int sfreq, int part2bits) +{ + int shift = 1 + gr_info->scalefac_scale; + float *xrpnt = (float *) xr[1]; + float *xr0pnt = (float *) xr[0]; + int l[3], l3; + int part2remain = gr_info->part2_3_length - part2bits; + int *me; + + { + int bv = gr_info->big_values; + int region1 = gr_info->region1start; + int region2 = gr_info->region2start; + + l3 = ((576 >> 1) - bv) >> 1; +/* + * we may lose the 'odd' bit here !! + * check this later gain + */ + if (bv <= region1) + { + l[0] = bv; + l[1] = 0; + l[2] = 0; + } + else + { + l[0] = region1; + if (bv <= region2) + { + l[1] = bv - l[0]; + l[2] = 0; + } + else + { + l[1] = region2 - l[0]; + l[2] = bv - region2; + } + } + if (l[0] < 0) + { + l[0] = 0; + } + if (l[1] < 0) + { + l[1] = 0; + } + if (l[2] < 0) + { + l[2] = 0; + } + } + + if (gr_info->block_type == 2) + { + int i, max[4]; + int step = 0, lwin = 0, cb = 0; + register float v = 0.0f; + register int *m, mc = 0; + + if (gr_info->mixed_block_flag) + { + max[3] = -1; + max[0] = max[1] = max[2] = 2; + m = gMap[sfreq][0]; + me = gMapEnd[sfreq][0]; + } + else + { + max[0] = max[1] = max[2] = max[3] = -1; + /* max[3] not floatly needed in this case */ + m = gMap[sfreq][1]; + me = gMapEnd[sfreq][1]; + } + + for (i = 0; i < 2; i++) + { + int lp = l[i]; + struct newhuff *h = ht + gr_info->table_select[i]; + + for (; lp; lp--, mc--) + { + int x, y; + + if (!mc) + { + mc = *m++; + xrpnt = ((float *) xr[1]) + *m; + xr0pnt = ((float *) xr[0]) + *m++; + lwin = *m++; + cb = *m++; + if (lwin == 3) + { + v = gr_info->pow2gain[(*scf++) << shift]; + step = 1; + } + else + { + v = gr_info->full_gain[lwin][(*scf++) << shift]; + step = 3; + } + } + { + register short *val = h->table; + + while ((y = *val++) < 0) + { + if (get1Bit()) + { + val -= y; + } + part2remain--; + } + x = y >> 4; + y &= 0xf; + } + if (x == 15) + { + max[lwin] = cb; + part2remain -= h->linbits + 1; + x += getBits(h->linbits); + if (get1Bit()) + { + float a = gIsPow(x) * v; + + *xrpnt = *xr0pnt + a; + *xr0pnt -= a; + } + else + { + float a = gIsPow(x) * v; + + *xrpnt = *xr0pnt - a; + *xr0pnt += a; + } + } + else if (x) + { + max[lwin] = cb; + if (get1Bit()) + { + float a = gIsPow(x) * v; + + *xrpnt = *xr0pnt + a; + *xr0pnt -= a; + } + else + { + float a = gIsPow(x) * v; + + *xrpnt = *xr0pnt - a; + *xr0pnt += a; + } + part2remain--; + } + else + *xrpnt = *xr0pnt; + xrpnt += step; + xr0pnt += step; + + if (y == 15) + { + max[lwin] = cb; + part2remain -= h->linbits + 1; + y += getBits(h->linbits); + if (get1Bit()) + { + float a = gIsPow(y) * v; + + *xrpnt = *xr0pnt + a; + *xr0pnt -= a; + } + else + { + float a = gIsPow(y) * v; + + *xrpnt = *xr0pnt - a; + *xr0pnt += a; + } + } + else if (y) + { + max[lwin] = cb; + if (get1Bit()) + { + float a = gIsPow(y) * v; + + *xrpnt = *xr0pnt + a; + *xr0pnt -= a; + } + else + { + float a = gIsPow(y) * v; + + *xrpnt = *xr0pnt - a; + *xr0pnt += a; + } + part2remain--; + } + else + *xrpnt = *xr0pnt; + xrpnt += step; + xr0pnt += step; + } + } + + for (; l3 && (part2remain > 0); l3--) + { + struct newhuff *h = htc + gr_info->count1table_select; + register short *val = h->table, a; + + while ((a = *val++) < 0) + { + part2remain--; + if (part2remain < 0) + { + part2remain++; + a = 0; + break; + } + if (get1Bit()) + val -= a; + } + + for (i = 0; i < 4; i++) + { + if (!(i & 1)) + { + if (!mc) + { + mc = *m++; + xrpnt = ((float *) xr[1]) + *m; + xr0pnt = ((float *) xr[0]) + *m++; + lwin = *m++; + cb = *m++; + if (lwin == 3) + { + v = gr_info->pow2gain[(*scf++) << shift]; + step = 1; + } + else + { + v = gr_info->full_gain[lwin][(*scf++) << shift]; + step = 3; + } + } + mc--; + } + if ((a & (0x8 >> i))) + { + max[lwin] = cb; + part2remain--; + if (part2remain < 0) + { + part2remain++; + break; + } + if (get1Bit()) + { + *xrpnt = *xr0pnt + v; + *xr0pnt -= v; + } + else + { + *xrpnt = *xr0pnt - v; + *xr0pnt += v; + } + } + else + *xrpnt = *xr0pnt; + xrpnt += step; + xr0pnt += step; + } + } + + while (m < me) + { + if (!mc) + { + mc = *m++; + xrpnt = ((float *) xr[1]) + *m; + xr0pnt = ((float *) xr[0]) + *m++; + if (*m++ == 3) + step = 1; + else + step = 3; + m++; /* cb */ + } + mc--; + *xrpnt = *xr0pnt; + xrpnt += step; + xr0pnt += step; + *xrpnt = *xr0pnt; + xrpnt += step; + xr0pnt += step; + /* we could add a little opt. here: + * if we finished a band for window 3 or a int band + * further bands could copied in a simple loop without a + * special 'gMap' decoding + */ + } + + gr_info->maxband[0] = max[0] + 1; + gr_info->maxband[1] = max[1] + 1; + gr_info->maxband[2] = max[2] + 1; + gr_info->maxbandl = max[3] + 1; + + { + int rmax = max[0] > max[1] ? max[0] : max[1]; + + rmax = (rmax > max[2] ? rmax : max[2]) + 1; + gr_info->maxb = rmax ? gShortLimit[sfreq][rmax] : gLongLimit[sfreq][max[3] + 1]; + } + } + else + { + int *pretab = gr_info->preflag ? pretab1 : pretab2; + int i, max = -1; + int cb = 0; + register int mc = 0, *m = gMap[sfreq][2]; + register float v = 0.0f; + + for (i = 0; i < 3; i++) + { + int lp = l[i]; + struct newhuff *h = ht + gr_info->table_select[i]; + + for (; lp; lp--, mc--) + { + int x, y; + + if (!mc) + { + mc = *m++; + cb = *m++; + v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift]; + } + { + register short *val = h->table; + + while ((y = *val++) < 0) + { + if (get1Bit()) + val -= y; + part2remain--; + } + x = y >> 4; + y &= 0xf; + } + if (x == 15) + { + max = cb; + part2remain -= h->linbits + 1; + x += getBits(h->linbits); + + if (get1Bit()) + { + float a = gIsPow(x) * v; + + *xrpnt++ = *xr0pnt + a; + *xr0pnt++ -= a; + } + else + { + float a = gIsPow(x) * v; + + *xrpnt++ = *xr0pnt - a; + *xr0pnt++ += a; + } + } + else if (x) + { + max = cb; + if (get1Bit()) + { + float a = gIsPow(x) * v; + + *xrpnt++ = *xr0pnt + a; + *xr0pnt++ -= a; + } + else + { + float a = gIsPow(x) * v; + + *xrpnt++ = *xr0pnt - a; + *xr0pnt++ += a; + } + part2remain--; + } + else + *xrpnt++ = *xr0pnt++; + + if (y == 15) + { + max = cb; + part2remain -= h->linbits + 1; + y += getBits(h->linbits); + if (get1Bit()) + { + float a = gIsPow(y) * v; + + *xrpnt++ = *xr0pnt + a; + *xr0pnt++ -= a; + } + else + { + float a = gIsPow(y) * v; + + *xrpnt++ = *xr0pnt - a; + *xr0pnt++ += a; + } + } + else if (y) + { + max = cb; + if (get1Bit()) + { + float a = gIsPow(y) * v; + + *xrpnt++ = *xr0pnt + a; + *xr0pnt++ -= a; + } + else + { + float a = gIsPow(y) * v; + + *xrpnt++ = *xr0pnt - a; + *xr0pnt++ += a; + } + part2remain--; + } + else + *xrpnt++ = *xr0pnt++; + } + } + + for (; l3 && (part2remain > 0); l3--) + { + struct newhuff *h = htc + gr_info->count1table_select; + register short *val = h->table, a; + + while ((a = *val++) < 0) + { + part2remain--; + if (part2remain < 0) + { + part2remain++; + a = 0; + break; + } + if (get1Bit()) + val -= a; + } + + for (i = 0; i < 4; i++) + { + if (!(i & 1)) + { + if (!mc) + { + mc = *m++; + cb = *m++; + v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift]; + } + mc--; + } + if ((a & (0x8 >> i))) + { + max = cb; + part2remain--; + if (part2remain <= 0) + { + part2remain++; + break; + } + if (get1Bit()) + { + *xrpnt++ = *xr0pnt + v; + *xr0pnt++ -= v; + } + else + { + *xrpnt++ = *xr0pnt - v; + *xr0pnt++ += v; + } + } + else + *xrpnt++ = *xr0pnt++; + } + } + for (i = (int)(&xr[1][SBLIMIT][0] - xrpnt) >> 1; i; i--) + { + *xrpnt++ = *xr0pnt++; + *xrpnt++ = *xr0pnt++; + } + + gr_info->maxbandl = max + 1; + gr_info->maxb = gLongLimit[sfreq][gr_info->maxbandl]; + } + + while (part2remain > 16) + { + getBits(16); /* Dismiss stuffing Bits */ + part2remain -= 16; + } + + if (part2remain > 0) + { + getBits(part2remain); + } + else if (part2remain < 0) + { + return FMOD_ERR_FILE_BAD; /* -> error */ + } + + return FMOD_OK; +} +#endif // defined(PLATFORM_PS3_SPU_STREAMDECODE) || !defined(PLATFORM_PS3_SPU) + + +/* +[ + [DESCRIPTION] + III_stereo: calculate float channel values for Joint-I-Stereo-mode + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_i_stereo(float xr_buf[2][SBLIMIT][SSLIMIT], int *scalefac, struct gr_info_s *gr_info, int sfreq, int ms_stereo, int lsf) +{ + float (*xr)[SBLIMIT * SSLIMIT] = (float(*)[SBLIMIT * SSLIMIT]) xr_buf; + struct bandInfoStruct *bi = &gBandInfo[sfreq]; + float *tab1, *tab2; + + if (lsf) + { + int p = gr_info->scalefac_compress & 0x1; + + if (ms_stereo) + { + tab1 = gPow1_2[p]; + tab2 = gPow2_2[p]; + } + else + { + tab1 = gPow1_1[p]; + tab2 = gPow2_1[p]; + } + } + else + { + if (ms_stereo) + { + tab1 = gTan1_2; + tab2 = gTan2_2; + } + else + { + tab1 = gTan1_1; + tab2 = gTan2_1; + } + } + + if (gr_info->block_type == 2) + { + int lwin, do_l = 0; + + if (gr_info->mixed_block_flag) + do_l = 1; + + for (lwin = 0; lwin < 3; lwin++) /* process each window */ + { + /* get first band with zero values */ + int is_p, sb, idx, sfb = gr_info->maxband[lwin]; /* sfb is minimal 3 for mixed mode */ + + if (sfb > 3) + do_l = 0; + + for (; sfb < 12; sfb++) + { + is_p = scalefac[sfb * 3 + lwin - gr_info->mixed_block_flag]; /* scale: 0-15 */ + if (is_p != 7) + { + float t1, t2; + + sb = bi->shortDiff[sfb]; + idx = bi->shortIdx[sfb] + lwin; + t1 = tab1[is_p]; + t2 = tab2[is_p]; + for (; sb > 0; sb--, idx += 3) + { + float v = xr[0][idx]; + + xr[0][idx] = v * t1; + xr[1][idx] = v * t2; + } + } + } + +#if 1 +/* in the original: copy 10 to 11 , here: copy 11 to 12 + maybe still wrong??? (copy 12 to 13?) */ + is_p = scalefac[11 * 3 + lwin - gr_info->mixed_block_flag]; /* scale: 0-15 */ + sb = bi->shortDiff[12]; + idx = bi->shortIdx[12] + lwin; +#else + is_p = scalefac[10 * 3 + lwin - gr_info->mixed_block_flag]; /* scale: 0-15 */ + sb = bi->shortDiff[11]; + idx = bi->shortIdx[11] + lwin; +#endif + if (is_p != 7) + { + float t1, t2; + + t1 = tab1[is_p]; + t2 = tab2[is_p]; + for (; sb > 0; sb--, idx += 3) + { + float v = xr[0][idx]; + + xr[0][idx] = v * t1; + xr[1][idx] = v * t2; + } + } + } /* end for(lwin; .. ; . ) */ + + if (do_l) + { +/* also check l-part, if ALL bands in the three windows are 'empty' + * and mode = mixed_mode + */ + int sfb = gr_info->maxbandl; + int idx = bi->longIdx[sfb]; + + for (; sfb < 8; sfb++) + { + int sb = bi->longDiff[sfb]; + int is_p = scalefac[sfb]; /* scale: 0-15 */ + + if (is_p != 7) + { + float t1, t2; + + t1 = tab1[is_p]; + t2 = tab2[is_p]; + for (; sb > 0; sb--, idx++) + { + float v = xr[0][idx]; + + xr[0][idx] = v * t1; + xr[1][idx] = v * t2; + } + } + else + idx += sb; + } + } + } + else + /* ((gr_info->block_type != 2)) */ + { + int sfb = gr_info->maxbandl; + int is_p, idx = bi->longIdx[sfb]; + + for (; sfb < 21; sfb++) + { + int sb = bi->longDiff[sfb]; + + is_p = scalefac[sfb]; /* scale: 0-15 */ + if (is_p != 7) + { + float t1, t2; + + t1 = tab1[is_p]; + t2 = tab2[is_p]; + for (; sb > 0; sb--, idx++) + { + float v = xr[0][idx]; + + xr[0][idx] = v * t1; + xr[1][idx] = v * t2; + } + } + else + idx += sb; + } + + if (idx < SBLIMIT * SSLIMIT) + { + is_p = scalefac[20]; /* copy l-band 20 to l-band 21 */ + if (is_p != 7) + { + int sb; + float t1 = tab1[is_p], t2 = tab2[is_p]; + + for (sb = bi->longDiff[21]; sb > 0; sb--, idx++) + { + float v = xr[0][idx]; + + xr[0][idx] = v * t1; + xr[1][idx] = v * t2; + } + } + } + } /* ... */ + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_antialias(float xr[SBLIMIT][SSLIMIT], struct gr_info_s *gr_info) +{ + int sblim; + + if (gr_info->block_type == 2) + { + if (!gr_info->mixed_block_flag) + { + return FMOD_ERR_FILE_BAD; + } + sblim = 1; + } + else + { + sblim = gr_info->maxb - 1; + if (sblim < 0) + { + return FMOD_ERR_FILE_BAD; + } + } + + /* 31 alias-reduction operations between each pair of sub-bands */ + /* with 8 butterflies between each pair */ + + { + int sb; + float *xr1 = (float *) xr[1]; + + for (sb = sblim; sb; sb--, xr1 += 10) + { + int ss; + float *cs = gAaCs, *ca = gAaCa; + float *xr2 = xr1; + + for (ss = 7; ss >= 0; ss--) + { /* upper and lower butterfly inputs */ + register float bu = *--xr2, bd = *xr1; + + *xr2 = (bu * (*cs)) - (bd * (*ca)); + *xr1++ = (bd * (*cs++)) + (bu * (*ca++)); + } + } + } + + return FMOD_OK; +} + +/* + // This is an optimized DCT from Jeff Tsay's maplay 1.2+ package. + // Saved one multiplication by doing the 'twiddle factor' stuff + // together with the window mul. (MH) + // + // This uses Byeong Gi Lee's Fast Cosine Transform algorithm, but the + // 9 point IDCT needs to be reduced further. Unfortunately, I don't + // know how to do that, because 9 is not an even number. - Jeff. + // + ////////////////////////////////////////////////////////////////// + // + // 9 Point Inverse Discrete Cosine Transform + // + // This piece of code is Copyright 1997 Mikko Tommila and is freely usable + // by anybody. The algorithm itself is of course in the public domain. + // + // Again derived heuristically from the 9-point WFTA. + // + // The algorithm is optimized (?) for speed, not for small rounding errors or + // good readability. + // + // 36 additions, 11 multiplications + // + // Again this is very likely sub-optimal. + // + // The code is optimized to use a minimum number of temporary variables, + // so it should compile quite well even on 8-register Intel x86 processors. + // This makes the code quite obfuscated and very difficult to understand. + // + // References: + // [1] S. Winograd: "On Computing the Discrete Fourier Transform", + // Mathematics of Computation, Volume 32, Number 141, January 1978, + // Pages 175-199 + */ + + +/* +[ + [DESCRIPTION] + Calculation of the inverse MDCT + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void CodecMPEG::dct36(float * inbuf, float * o1, float * o2, float * wintab, float * tsbuf) +{ + float tmp[18]; + + { + register float *in = inbuf; + + in[17] += in[16]; + in[16] += in[15]; + in[15] += in[14]; + in[14] += in[13]; + in[13] += in[12]; + in[12] += in[11]; + in[11] += in[10]; + in[10] += in[9]; + in[9] += in[8]; + in[8] += in[7]; + in[7] += in[6]; + in[6] += in[5]; + in[5] += in[4]; + in[4] += in[3]; + in[3] += in[2]; + in[2] += in[1]; + in[1] += in[0]; + + in[17] += in[15]; + in[15] += in[13]; + in[13] += in[11]; + in[11] += in[9]; + in[9] += in[7]; + in[7] += in[5]; + in[5] += in[3]; + in[3] += in[1]; + + { + float t0, t1, t2, t3, t4, t5, t6, t7; + + t1 = gCos6_2 * in[12]; + t2 = gCos6_2 * (in[8] + in[16] - in[4]); + + t3 = in[0] + t1; + t4 = in[0] - t1 - t1; + t5 = t4 - t2; + + t0 = gCos9[0] * (in[4] + in[8]); + t1 = gCos9[1] * (in[8] - in[16]); + + tmp[4] = t4 + t2 + t2; + t2 = gCos9[2] * (in[4] + in[16]); + + t6 = t3 - t0 - t2; + t0 += t3 + t1; + t3 += t2 - t1; + + t2 = gCos18[0] * (in[2] + in[10]); + t4 = gCos18[1] * (in[10] - in[14]); + t7 = gCos6_1 * in[6]; + + t1 = t2 + t4 + t7; + tmp[0] = t0 + t1; + tmp[8] = t0 - t1; + t1 = gCos18[2] * (in[2] + in[14]); + t2 += t1 - t7; + + tmp[3] = t3 + t2; + t0 = gCos6_1 * (in[10] + in[14] - in[2]); + tmp[5] = t3 - t2; + + t4 -= t1 + t7; + + tmp[1] = t5 - t0; + tmp[7] = t5 + t0; + tmp[2] = t6 + t4; + tmp[6] = t6 - t4; + } + + { + float t0, t1, t2, t3, t4, t5, t6, t7; + + t1 = gCos6_2 * in[13]; + t2 = gCos6_2 * (in[9] + in[17] - in[5]); + + t3 = in[1] + t1; + t4 = in[1] - t1 - t1; + t5 = t4 - t2; + + t0 = gCos9[0] * (in[5] + in[9]); + t1 = gCos9[1] * (in[9] - in[17]); + + tmp[13] = (t4 + t2 + t2) * gTfCos36[17 - 13]; + t2 = gCos9[2] * (in[5] + in[17]); + + t6 = t3 - t0 - t2; + t0 += t3 + t1; + t3 += t2 - t1; + + t2 = gCos18[0] * (in[3] + in[11]); + t4 = gCos18[1] * (in[11] - in[15]); + t7 = gCos6_1 * in[7]; + + t1 = t2 + t4 + t7; + tmp[17] = (t0 + t1) * gTfCos36[17 - 17]; + tmp[9] = (t0 - t1) * gTfCos36[17 - 9]; + t1 = gCos18[2] * (in[3] + in[15]); + t2 += t1 - t7; + + tmp[14] = (t3 + t2) * gTfCos36[17 - 14]; + t0 = gCos6_1 * (in[11] + in[15] - in[3]); + tmp[12] = (t3 - t2) * gTfCos36[17 - 12]; + + t4 -= t1 + t7; + + tmp[16] = (t5 - t0) * gTfCos36[17 - 16]; + tmp[10] = (t5 + t0) * gTfCos36[17 - 10]; + tmp[15] = (t6 + t4) * gTfCos36[17 - 15]; + tmp[11] = (t6 - t4) * gTfCos36[17 - 11]; + } + +#define MACRO(v) { \ + float tmpval; \ + float sum0 = tmp[(v)]; \ + float sum1 = tmp[17-(v)]; \ + out2[9+(v)] = (tmpval = sum0 + sum1) * w[27+(v)]; \ + out2[8-(v)] = tmpval * w[26-(v)]; \ + sum0 -= sum1; \ + ts[SBLIMIT*(8-(v))] = out1[8-(v)] + sum0 * w[8-(v)]; \ + ts[SBLIMIT*(9+(v))] = out1[9+(v)] + sum0 * w[9+(v)]; \ +} + + { + register float *out2 = o2; + register float *w = wintab; + register float *out1 = o1; + register float *ts = tsbuf; + + MACRO(0); + MACRO(1); + MACRO(2); + MACRO(3); + MACRO(4); + MACRO(5); + MACRO(6); + MACRO(7); + MACRO(8); + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void CodecMPEG::dct12(float * in, float * rawout1, float * rawout2, register float * wi, register float * ts) +{ +#define DCT12_PART1 \ + in5 = in[5*3]; \ + in5 += (in4 = in[4*3]); \ + in4 += (in3 = in[3*3]); \ + in3 += (in2 = in[2*3]); \ + in2 += (in1 = in[1*3]); \ + in1 += (in0 = in[0*3]); \ + \ + in5 += in3; in3 += in1; \ + \ + in2 *= gCos6_1; \ + in3 *= gCos6_1; \ + +#define DCT12_PART2 \ + in0 += in4 * gCos6_2; \ + \ + in4 = in0 + in2; \ + in0 -= in2; \ + \ + in1 += in5 * gCos6_2; \ + \ + in5 = (in1 + in3) * gTfCos12[0]; \ + in1 = (in1 - in3) * gTfCos12[2]; \ + \ + in3 = in4 + in5; \ + in4 -= in5; \ + \ + in2 = in0 + in1; \ + in0 -= in1; + + { + float in0, in1, in2, in3, in4, in5; + register float *out1 = rawout1; + + ts[SBLIMIT * 0] = out1[0]; + ts[SBLIMIT * 1] = out1[1]; + ts[SBLIMIT * 2] = out1[2]; + ts[SBLIMIT * 3] = out1[3]; + ts[SBLIMIT * 4] = out1[4]; + ts[SBLIMIT * 5] = out1[5]; + + DCT12_PART1 + + { + float tmp0, tmp1 = (in0 - in4); + + { + float tmp2 = (in1 - in5) * gTfCos12[1]; + + tmp0 = tmp1 + tmp2; + tmp1 -= tmp2; + } + ts[(17 - 1) * SBLIMIT] = out1[17 - 1] + tmp0 * wi[11 - 1]; + ts[(12 + 1) * SBLIMIT] = out1[12 + 1] + tmp0 * wi[6 + 1]; + ts[(6 + 1) * SBLIMIT] = out1[6 + 1] + tmp1 * wi[1]; + ts[(11 - 1) * SBLIMIT] = out1[11 - 1] + tmp1 * wi[5 - 1]; + } + + DCT12_PART2 + + ts[(17 - 0) * SBLIMIT] = out1[17 - 0] + in2 * wi[11 - 0]; + ts[(12 + 0) * SBLIMIT] = out1[12 + 0] + in2 * wi[6 + 0]; + ts[(12 + 2) * SBLIMIT] = out1[12 + 2] + in3 * wi[6 + 2]; + ts[(17 - 2) * SBLIMIT] = out1[17 - 2] + in3 * wi[11 - 2]; + + ts[(6 + 0) * SBLIMIT] = out1[6 + 0] + in0 * wi[0]; + ts[(11 - 0) * SBLIMIT] = out1[11 - 0] + in0 * wi[5 - 0]; + ts[(6 + 2) * SBLIMIT] = out1[6 + 2] + in4 * wi[2]; + ts[(11 - 2) * SBLIMIT] = out1[11 - 2] + in4 * wi[5 - 2]; + } + + in++; + + { + float in0, in1, in2, in3, in4, in5; + register float *out2 = rawout2; + + DCT12_PART1 + + { + float tmp0, tmp1 = (in0 - in4); + + { + float tmp2 = (in1 - in5) * gTfCos12[1]; + + tmp0 = tmp1 + tmp2; + tmp1 -= tmp2; + } + out2[5 - 1] = tmp0 * wi[11 - 1]; + out2[0 + 1] = tmp0 * wi[6 + 1]; + ts[(12 + 1) * SBLIMIT] += tmp1 * wi[1]; + ts[(17 - 1) * SBLIMIT] += tmp1 * wi[5 - 1]; + } + + DCT12_PART2 + + out2[5 - 0] = in2 * wi[11 - 0]; + out2[0 + 0] = in2 * wi[6 + 0]; + out2[0 + 2] = in3 * wi[6 + 2]; + out2[5 - 2] = in3 * wi[11 - 2]; + + ts[(12 + 0) * SBLIMIT] += in0 * wi[0]; + ts[(17 - 0) * SBLIMIT] += in0 * wi[5 - 0]; + ts[(12 + 2) * SBLIMIT] += in4 * wi[2]; + ts[(17 - 2) * SBLIMIT] += in4 * wi[5 - 2]; + } + + in++; + + { + float in0, in1, in2, in3, in4, in5; + register float *out2 = rawout2; + + out2[12] = out2[13] = out2[14] = out2[15] = out2[16] = out2[17] = 0.0f; + + DCT12_PART1 + + { + float tmp0, tmp1 = (in0 - in4); + + { + float tmp2 = (in1 - in5) * gTfCos12[1]; + + tmp0 = tmp1 + tmp2; + tmp1 -= tmp2; + } + out2[11 - 1] = tmp0 * wi[11 - 1]; + out2[6 + 1] = tmp0 * wi[6 + 1]; + out2[0 + 1] += tmp1 * wi[1]; + out2[5 - 1] += tmp1 * wi[5 - 1]; + } + + DCT12_PART2 + + out2[11 - 0] = in2 * wi[11 - 0]; + out2[6 + 0] = in2 * wi[6 + 0]; + out2[6 + 2] = in3 * wi[6 + 2]; + out2[11 - 2] = in3 * wi[11 - 2]; + + out2[0 + 0] += in0 * wi[0]; + out2[5 - 0] += in0 * wi[5 - 0]; + out2[0 + 2] += in4 * wi[2]; + out2[5 - 2] += in4 * wi[5 - 2]; + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::III_hybrid(float fsIn[SBLIMIT][SSLIMIT], float tsOut[SSLIMIT][SBLIMIT], int ch, struct gr_info_s *gr_info) +{ + float *tspnt = (float *) tsOut; + float *rawout1, *rawout2; + int bt; + int sb = 0; + + { + int b = mMemoryBlock->mBlc[ch]; + + rawout1 = mMemoryBlock->mBlock[b][ch]; + b = -b + 1; + rawout2 = mMemoryBlock->mBlock[b][ch]; + mMemoryBlock->mBlc[ch] = b; + } + + if (gr_info->mixed_block_flag) + { +// printf("Mixed blocks\n"); + sb = 2; + dct36(fsIn[0], rawout1, rawout2, gWin[0], tspnt); + dct36(fsIn[1], rawout1 + 18, rawout2 + 18, gWin1[0], tspnt + 1); + rawout1 += 36; + rawout2 += 36; + tspnt += 2; + } + + bt = gr_info->block_type; + if (bt == 2) + { + for (; sb < (int)gr_info->maxb; sb += 2, tspnt += 2, rawout1 += 36, rawout2 += 36) + { + dct12(fsIn[sb], rawout1, rawout2, gWin[2], tspnt); + dct12(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, gWin1[2], tspnt + 1); + } + } + else + { + for (; sb < (int)gr_info->maxb; sb += 2, tspnt += 2, rawout1 += 36, rawout2 += 36) + { + dct36(fsIn[sb], rawout1, rawout2, gWin[bt], tspnt); + dct36(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, gWin1[bt], tspnt + 1); + } + } + + for (; sb < SBLIMIT; sb++, tspnt++) + { + int i; + + for (i = 0; i < SSLIMIT; i++) + { + tspnt[i * SBLIMIT] = *rawout1++; + *rawout2++ = 0.0f; + } + } + + return FMOD_OK; +} + + +/* + * main layer3 handler + */ +int scale[] = +{ + 1, 2, 3, 4, 6, 7, 9, 10, 12, 13, 14, 16, 17, 19, 20, 22, 23, + 25, 29, 33, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, + 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 131, + 135, 139, 143, 147, 151, 155, 166, 177, 188, 199, 210, 221, + 231, 242, 253, 264, 275, 286, 297, 308, 318, 329, 340, 351, + 362, 373, 384, 395, 406, 416, 575 +}; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::decodeLayer3(void *pcm_sample, unsigned int *outlen) +{ + FMOD_RESULT result; + int gr, ch, ss; + int scalefacs[2][39]; // max 39 for short[13][3] mode, mixed: 38, int : 22 (312bytes). + struct III_sideinfo sideinfo; + int channels = mMemoryBlock->mFrame.stereo; + int sfreq = mMemoryBlock->mFrame.sampling_frequency; + int ms_stereo, i_stereo; + int granules; + int inc = SBLIMIT * sizeof(signed short); + float hybridout[2][SSLIMIT][SBLIMIT]; + float hybridin[2][SBLIMIT][SSLIMIT] = { 0 }; + + *outlen = 0; + + FMOD_memset(&sideinfo, 0, sizeof(III_sideinfo)); + + if (mMemoryBlock->mFrame.mode == MPG_MD_JOINT_STEREO) + { + ms_stereo = mMemoryBlock->mFrame.mode_ext & 0x2; + i_stereo = mMemoryBlock->mFrame.mode_ext & 0x1; + } + else + { + ms_stereo = i_stereo = 0; + } + + if (mMemoryBlock->mFrame.lsf) + { + granules = 1; + result = III_get_side_info_2(&sideinfo, channels, ms_stereo, sfreq); + if (result != FMOD_OK) + { + return result; + } + } + else + { + granules = 2; + result = III_get_side_info_1(&sideinfo, channels, ms_stereo, sfreq); + if (result != FMOD_OK) + { + return result; + } + } + + if (!(mMemoryBlock->mFrameSizeOld < 0 && sideinfo.main_data_begin > 0)) + { + unsigned char *bsbufold; + + bsbufold = mMemoryBlock->mBSSpace[mMemoryBlock->mBSNum] + 512; + mMemoryBlock->mBSI.mWordPointer -= sideinfo.main_data_begin; + + if (sideinfo.main_data_begin) + { + int rewind = mMemoryBlock->mFrameSizeOld-sideinfo.main_data_begin; + FMOD_memcpy(mMemoryBlock->mBSI.mWordPointer,bsbufold+rewind,sideinfo.main_data_begin); + } + + mMemoryBlock->mBSI.mBitIndex = 0; + } + + + for (gr = 0; gr < granules; gr++) + { + struct gr_info_s *gr_info = &(sideinfo.ch[0].gr[gr]); + int part2bits; + + if (mMemoryBlock->mFrame.lsf) + { + III_get_scale_factors_2(scalefacs[0], gr_info, 0, &part2bits); + } + else + { + III_get_scale_factors_1(scalefacs[0], gr_info, &part2bits); + } + + #if defined(PLATFORM_PS3_SPU_STREAMDECODE) || !defined(PLATFORM_PS3_SPU) + result = III_dequantize_sample(hybridin[0], scalefacs[0], gr_info, sfreq, part2bits); + #else + result = gDequantizeSample(hybridin[0], scalefacs[0], gr_info, sfreq, part2bits, mMemoryBlock, gMap, gMapEnd, gLongLimit, gShortLimit, ht, htc, pretab1, pretab2); + #endif + if (result != FMOD_OK) + { + return result; + } + + if (channels == 2) + { + struct gr_info_s *gr_info = &(sideinfo.ch[1].gr[gr]); + int part2bits; + + if (mMemoryBlock->mFrame.lsf) + { + III_get_scale_factors_2(scalefacs[1], gr_info, i_stereo, &part2bits); + } + else + { + III_get_scale_factors_1(scalefacs[1], gr_info, &part2bits); + } + + #if defined(PLATFORM_PS3_SPU_STREAMDECODE) || !defined(PLATFORM_PS3_SPU) + + if (ms_stereo) + { + III_dequantize_sample_ms(hybridin, scalefacs[1], gr_info, sfreq, part2bits); + } + else + { + III_dequantize_sample(hybridin[1], scalefacs[1], gr_info, sfreq, part2bits); + } + + #else + + if (ms_stereo) + { + gDequantizeSampleMS(hybridin, scalefacs[1], gr_info, sfreq, part2bits, mMemoryBlock, gMap, gMapEnd, gLongLimit, gShortLimit, ht, htc, pretab1, pretab2); + } + else + { + gDequantizeSample(hybridin[1], scalefacs[1], gr_info, sfreq, part2bits, mMemoryBlock, gMap, gMapEnd, gLongLimit, gShortLimit, ht, htc, pretab1, pretab2); + } + + #endif + + if (i_stereo) + { + III_i_stereo(hybridin, scalefacs[1], gr_info, sfreq, ms_stereo, mMemoryBlock->mFrame.lsf); + } + + if (ms_stereo || i_stereo) // || (single == 3)) + { + if (gr_info->maxb > sideinfo.ch[0].gr[gr].maxb) + { + sideinfo.ch[0].gr[gr].maxb = gr_info->maxb; + } + else + { + gr_info->maxb = sideinfo.ch[0].gr[gr].maxb; + } + } + + } + + for (ch = 0; ch < channels; ch++) + { + struct gr_info_s *gr_info = &(sideinfo.ch[ch].gr[gr]); + + III_antialias(hybridin[ch], gr_info); + III_hybrid(hybridin[ch], hybridout[ch], ch, gr_info); + } + + for (ss = 0 ; ss < SSLIMIT ; ss++) + { + synth(pcm_sample, hybridout[0][ss], channels, waveformat->channels > 2 ? waveformat->channels : channels); + pcm_sample = (char *)pcm_sample + (inc * waveformat->channels); + } + } + + *outlen = (inc * channels) * SSLIMIT * granules; + + return FMOD_OK; +} + +#else // #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +/* +[ + [DESCRIPTION] + Use Sony Decoder. PS3 Only. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecMPEG::decodeLayer3(unsigned char *in, void *out, unsigned int *outlen) +{ +#ifdef PLATFORM_PS3_SPU + + int byteswritten; + int bytesread; + + char *decoderworkspace = gDSPandMixBufferMemory + MPEGDSPSIZE; + + short *data = cellMP3IntegratedDecoderSpuDecode(decoderworkspace, gMP3DecoderMram, in, &bytesread, &byteswritten, &mMemoryBlock->mContext1, &mMemoryBlock->mContext2, &mMemoryBlock->mCount); + + if (byteswritten <= 0) + { + // SPUprintf("decode: bytesred: %d byteswritten: %d, data: %08x\n", bytesread, byteswritten, data); + + *outlen = 0; + return FMOD_OK; + } + + if (mMemoryBlock->mStereo == 2) + { + /* + Interleave into decodebuffer + */ + short *left = data; + short *right = (short *)((unsigned int)data + 4096); + short *destination = (short *)out; + + for (int i = 0, j = 0; i < byteswritten / 2; i++) + { + *destination++ = left [i]; + *destination++ = right[i]; + } + } + else + { + memcpy(out, data, byteswritten); + } + + *outlen = byteswritten * mMemoryBlock->mStereo; +#endif + + return FMOD_OK; +} + +#endif // #ifndef FMOD_SUPPORT_MPEG_SONYDECODER + +} + +#endif // FMOD_SUPPORT_MPEG_LAYER3 + +#endif // FMOD_SUPPORT_MPEG + + + diff --git a/src/fmod_codec_oggvorbis.cpp b/src/fmod_codec_oggvorbis.cpp new file mode 100755 index 0000000..d0dfc51 --- /dev/null +++ b/src/fmod_codec_oggvorbis.cpp @@ -0,0 +1,823 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OGGVORBIS + +#include "fmod.h" +#include "fmod_codec_oggvorbis.h" +#include "fmod_codec_wav.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_string.h" + +#include "../lib/ogg_vorbis/vorbis/lib/window.h" + +#include + + +void * FMOD_OggVorbis_Malloc(void *context, int size) +{ + void *mem = FMOD_Memory_Alloc(size); + + if (mem && context) + { + FMOD::CodecOggVorbis *ogg = (FMOD::CodecOggVorbis *)context; + + ogg->mMemUsed += size; + } + + return mem; +} +void * FMOD_OggVorbis_Calloc(void *context, int count, int size) +{ + void *mem = FMOD_Memory_Calloc(count * size); + + if (mem && context) + { + FMOD::CodecOggVorbis *ogg = (FMOD::CodecOggVorbis *)context; + ogg->mMemUsed += (count * size); + } + + return mem; +} +void * FMOD_OggVorbis_ReAlloc(void *context, void *ptr, int size) +{ + void *mem; + FMOD::CodecOggVorbis *ogg = (FMOD::CodecOggVorbis *)context; + + if (ptr && ogg) + { + FMOD::MemBlockHeader *block = (FMOD::MemBlockHeader *)ptr; + + block--; + + ogg->mMemUsed -= block->mSize; + } + + mem = FMOD_Memory_ReAlloc(ptr, size); + if (mem && ogg) + { + ogg->mMemUsed += size; + } + + return mem; +} +void FMOD_OggVorbis_Free(void *context, void *ptr) +{ + if (ptr && context) + { + FMOD::MemBlockHeader *block = (FMOD::MemBlockHeader *)ptr; + FMOD::CodecOggVorbis *ogg = (FMOD::CodecOggVorbis *)context; + + block--; + + ogg->mMemUsed -= block->mSize; + } + + FMOD_Memory_Free(ptr); +} + + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX oggvorbiscodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecOggVorbis::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +/* + Globals +*/ + +bool CodecOggVorbis::gInitialized = false; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +size_t FMOD_OggVorbis_ReadCallback(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + FMOD_RESULT result; + unsigned int rd; + File *fp; + + fp = (File *)datasource; + + result = fp->read(ptr, (unsigned int)size, (unsigned int)nmemb, &rd); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return (size_t)-1; + } + + return rd; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +int FMOD_OggVorbis_SeekCallback(void *datasource, ogg_int64_t offset, int whence) +{ + File *fp = (File *)datasource; + + if (fp->mFlags & FMOD_FILE_SEEKABLE) + { + return fp->seek((int)offset, (signed char)whence); + } + else + { + return -1; + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ogg_int32_t FMOD_OggVorbis_TellCallback(void *datasource) +{ + File *fp = (File *)datasource; + unsigned int pos; + + fp->tell(&pos); + + return (ogg_int32_t)pos; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecOggVorbis::getDescriptionEx() +{ + FMOD_memset(&oggvorbiscodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + oggvorbiscodec.name = "FMOD Ogg Vorbis Codec"; + oggvorbiscodec.version = 0x00010100; + oggvorbiscodec.timeunits = FMOD_TIMEUNIT_PCM; + oggvorbiscodec.open = &CodecOggVorbis::openCallback; + oggvorbiscodec.close = &CodecOggVorbis::closeCallback; + oggvorbiscodec.read = &CodecOggVorbis::readCallback; + oggvorbiscodec.setposition = &CodecOggVorbis::setPositionCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + oggvorbiscodec.getmemoryused = &CodecOggVorbis::getMemoryUsedCallback; +#endif + + oggvorbiscodec.mType = FMOD_SOUND_TYPE_OGGVORBIS; + oggvorbiscodec.mSize = sizeof(CodecOggVorbis); + + return &oggvorbiscodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecOggVorbis::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + vorbis_info *vi; + char str[4]; + unsigned int lengthbytes = 0; + int oggresult; + ov_callbacks callbacks = + { + FMOD_OggVorbis_ReadCallback, /* size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); */ + FMOD_OggVorbis_SeekCallback, /* int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); */ + 0, /* int (*close_func) (void *datasource); */ + FMOD_OggVorbis_TellCallback, /* int (*tell_func) (void *datasource); */ + }; + bool manualsizecalc = false; + + init(FMOD_SOUND_TYPE_OGGVORBIS); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecOggVorbis::openInternal", "attempting to open as OGG..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + /* + Support RIFF wrapped MP3? + */ + { + CodecWav tempwav; + WAVE_CHUNK chunk; + FMOD_CODEC_WAVEFORMAT tempwaveformat; + + FMOD_memset(&tempwav, 0, sizeof(CodecWav)); + FMOD_memset(&tempwaveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + + tempwav.mFile = mFile; + tempwav.mSrcDataOffset = (unsigned int)-1; + tempwav.waveformat = &tempwaveformat; + + /* + Read header + */ + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp((const char *)chunk.id, "RIFF", 4)) + { + char wave[4]; + + result = mFile->read(wave, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp(wave, "WAVE", 4)) + { + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + result = tempwav.parseChunk(chunk.size); + if (result == FMOD_OK && tempwav.mSrcFormat && tempwav.mSrcDataOffset == (unsigned int)-1) + { + int format = tempwav.mSrcFormat->Format.wFormatTag; + + if (format == WAVE_FORMAT_OGGVORBIS) + { + mSrcDataOffset = tempwav.mSrcDataOffset; + lengthbytes = tempwav.waveformat[0].lengthbytes; + mLoopPoints[0] = tempwav.mLoopPoints[0]; + mLoopPoints[1] = tempwav.mLoopPoints[1]; + mSyncPoint = tempwav.mSyncPoint; + mNumSyncPoints = tempwav.mNumSyncPoints; + } + else + { + result = FMOD_ERR_FORMAT; + } + + if (result != FMOD_OK) + { + if (tempwav.mSrcFormat) + { + FMOD_Memory_Free(tempwav.mSrcFormat); + tempwav.mSrcFormat = 0; + } + if (tempwav.mSyncPoint && mSyncPoint != tempwav.mSyncPoint) + { + FMOD_Memory_Free(tempwav.mSyncPoint); + tempwav.mSyncPoint = 0; + } + return result; + } + } + + if (tempwav.mSrcFormat) + { + FMOD_Memory_Free(tempwav.mSrcFormat); + tempwav.mSrcFormat = 0; + } + if (tempwav.mSyncPoint && mSyncPoint != tempwav.mSyncPoint) + { + FMOD_Memory_Free(tempwav.mSyncPoint); + tempwav.mSyncPoint = 0; + } + } + } + + result = mFile->seek(mSrcDataOffset, SEEK_SET); + } + + result = mFile->read(str, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp(str, "OggS", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecOggVorbis::openInternal", "failed to open as ogg\n")); + + return FMOD_ERR_FORMAT; + } + + if (!gInitialized) + { + _FMOD_vorbis_window_init(); + gInitialized = true; + } + + /* + Get size of ogg file in bytes + */ + /* + If there wasnt a riff size chunk + */ + if (!lengthbytes) + { + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + manualsizecalc = true; + } + + result = mFile->seek(mSrcDataOffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + memset(&mVf, 0, sizeof(OggVorbis_File)); /* Just in case of an error code, it wont crash on invalid pointers trying to clean up. */ + + oggresult = ov_open_callbacks(this, mFile, &mVf, 0, 0, callbacks); + if (oggresult < 0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecOggVorbis::openInternal", "failed to open as ogg, format error.\n")); + + if (oggresult == OV_EVERSION) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecOggVorbis::openInternal", "OLD FLOOR0 TYPE OGG FILE. Please re-encode sound with a newer encoder.\n")); + return FMOD_ERR_FORMAT; + } + else if (oggresult == OV_EMEMORY) + { + return FMOD_ERR_MEMORY; + } + return FMOD_ERR_FORMAT; + } + + vi = ov_info(&mVf,-1); + + result = readVorbisComments(); + if (result != FMOD_OK) + { + return result; + } + + waveformat = &mWaveFormat; + waveformat[0].lengthbytes = lengthbytes; + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + waveformat[0].channels = vi->channels; + waveformat[0].frequency = vi->rate; + waveformat[0].blockalign = waveformat[0].channels * 2; /* THIS SHOULD BE EQUIVALENT TO THE OGG DECODE BUFFER SIZE? */ + + if (manualsizecalc && waveformat[0].lengthbytes != (unsigned int)-1) + { + waveformat[0].lengthbytes -= mSrcDataOffset; + } + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + int count, streams = ov_streams(&mVf); + + waveformat[0].lengthpcm = 0; + + for (count = 0; count < streams; count++) + { + waveformat[0].lengthpcm += (int)ov_pcm_total(&mVf, count); + } + + if (waveformat[0].lengthpcm <= 0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecOggVorbis::openInternal", "failed to open as ogg\n")); + + waveformat[0].lengthpcm = 0; + + return FMOD_ERR_FORMAT; + } + } + else + { + waveformat[0].lengthpcm = 0x7fffffff; + } + + if (!mSrcDataOffset) + { + mSrcDataOffset = (unsigned int)ov_raw_tell(&mVf); + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecOggVorbis::closeInternal() +{ + mVf.datasource = 0; /* this stops vorbis from trying to close our file */ + + ov_clear(this, &mVf); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecOggVorbis::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + int bigendian; + vorbis_comment *vc; + char *name, *value; + int i; + +#ifdef PLATFORM_ENDIAN_BIG + bigendian = 1; +#else + bigendian = 0; +#endif + + *bytesread = ov_read(this, &mVf, (char *)buffer, sizebytes, bigendian, 2, 1, 0); + if (((int)(*bytesread)) <= 0) + { + if ((int)*bytesread == OV_EINVAL) + { + *bytesread = 0; + return FMOD_ERR_INVALID_PARAM; + } + else if ((int)*bytesread == OV_EMEMORY) + { + return FMOD_ERR_MEMORY; + } + else if ((int)*bytesread != OV_HOLE) /* Don't bail if we encounter a hole. */ + { + *bytesread = 0; + return FMOD_ERR_FILE_EOF; + } + *bytesread = 0; + } + + vc = ov_comment(&mVf, -1); + + if (vc && vc->comments) + { + for (i=0;i < vc->comments;i++) + { + name = vc->user_comments[i]; + value = name; + for (;*value && (*value != '=');value++) ; + + if (*value == '=') + { + *value++ = 0; + } + else + { + value = name; + name = (char *)"NONAME"; + } + + metadata((FMOD_CODEC_STATE *)this, FMOD_TAGTYPE_VORBISCOMMENT, name, value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, true); + } + + FMOD_vorbis_comment_clear(this, vc); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecOggVorbis::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + int ret = ov_pcm_seek(this, &mVf, position); + if (ret < 0) + { + if (ret == OV_EMEMORY) + { + return FMOD_ERR_MEMORY; + } + return FMOD_ERR_FILE_COULDNOTSEEK; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecOggVorbis::readVorbisComments() +{ + FMOD_RESULT result; + char *p; + int count, i; + vorbis_comment *vc; + + vc = ov_comment(&mVf, -1); + if (!vc) + { + return FMOD_OK; + } + + for (count = 0; count < vc->comments; count++) + { + if (vc->comment_lengths[count]) + { + p = vc->user_comments[count]; + for (i=0;*p && (*p != '=');i++, p++) ; + if (*p == '=') + { + *p++ = 0; + result = metaData(FMOD_TAGTYPE_VORBISCOMMENT, vc->user_comments[count], p, FMOD_strlen(p) + 1, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + } + } + } + + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecOggVorbis::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_CODEC, mMemUsed); + + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecOggVorbis::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecOggVorbis *ogg = (CodecOggVorbis *)codec; + + return ogg->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecOggVorbis::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecOggVorbis *ogg = (CodecOggVorbis *)codec; + + return ogg->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecOggVorbis::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecOggVorbis *ogg = (CodecOggVorbis *)codec; + + return ogg->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecOggVorbis::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecOggVorbis *ogg = (CodecOggVorbis *)codec; + + return ogg->setPositionInternal(subsound, position, postype); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecOggVorbis::getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker) +{ + CodecOggVorbis *ogg = (CodecOggVorbis *)codec; + + return ogg->getMemoryUsed(tracker); +} +#endif + + +} + +#endif + + diff --git a/src/fmod_codec_oggvorbis.h b/src/fmod_codec_oggvorbis.h new file mode 100755 index 0000000..c0798ee --- /dev/null +++ b/src/fmod_codec_oggvorbis.h @@ -0,0 +1,54 @@ +#ifndef _FMOD_CODEC_OGGVORBIS_H +#define _FMOD_CODEC_OGGVORBIS_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OGGVORBIS + +#include "fmod_codeci.h" + +#include "../lib/ogg_vorbis/vorbis/include/vorbis/vorbisfile.h" + +namespace FMOD +{ + class SyncPointNamed; + + class CodecOggVorbis : public Codec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + OggVorbis_File mVf; + static bool gInitialized; + + SyncPointNamed *mSyncPoint; + int mNumSyncPoints; + FMOD_CODEC_WAVEFORMAT mWaveFormat; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT readVorbisComments(); + + public: + + unsigned int mMemUsed; + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int size, unsigned int *read); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_CODEC_STATE *codec, MemoryTracker *tracker); +#endif + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_OGGVORBIS */ + +#endif + diff --git a/src/fmod_codec_playlist.cpp b/src/fmod_codec_playlist.cpp new file mode 100755 index 0000000..25be666 --- /dev/null +++ b/src/fmod_codec_playlist.cpp @@ -0,0 +1,1580 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PLAYLIST + +#include "fmod.h" +#include "fmod_codec_playlist.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_string.h" + +#include + +const int MAX_TOKEN_SIZE = 512; + +namespace FMOD +{ + +FMOD_CODEC_DESCRIPTION_EX playlistcodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandatory for every fmod plugin. This is the symbol the register plugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecPlaylist::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecPlaylist::getDescriptionEx() +{ + FMOD_memset(&playlistcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + playlistcodec.name = "FMOD Playlist Reader Codec"; + playlistcodec.version = 0x00010100; + playlistcodec.timeunits = FMOD_TIMEUNIT_PCM; + playlistcodec.open = &CodecPlaylist::openCallback; + playlistcodec.close = &CodecPlaylist::closeCallback; + playlistcodec.read = &CodecPlaylist::readCallback; + playlistcodec.setposition = &CodecPlaylist::setPositionCallback; + + playlistcodec.mType = FMOD_SOUND_TYPE_PLAYLIST; + playlistcodec.mSize = sizeof(CodecPlaylist); + + return &playlistcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_ERR_FORMAT; + char playlistid[16]; + + init(FMOD_SOUND_TYPE_PLAYLIST); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecPlaylist::openInternal", "attempting to open playlist file\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get the first 12 characters to identify whether or + not this is a playlist file, and which type if it is. + */ + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + return result; + } + + FMOD_memset(playlistid, 0, 16); + result = mFile->read(playlistid, 12, 1); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + if (!FMOD_strnicmp("#EXTM3U", playlistid, 7)) + { + result = readM3U(); + if (result != FMOD_OK) + { + return result; + } + } + else if (!FMOD_strnicmp("[PLAYLIST]", playlistid, 10)) + { + result = readPLS(); + if (result != FMOD_OK) + { + return result; + } + } + else if (!FMOD_strnicmp("getName(&filename); + if (result != FMOD_OK) + { + return result; + } + + stringlength = FMOD_strlen(filename); + + if (!FMOD_strncmp(filename + (stringlength - 4), ".pls", 4) || + !FMOD_strncmp(filename + (stringlength - 4), ".m3u", 4) || + !FMOD_strncmp(filename + (stringlength - 4), ".asx", 4) || + //!FMOD_strncmp(filename + (stringlength - 4), ".wpl", 4) || + !FMOD_strncmp(filename + (stringlength - 4), ".wax", 4)) + { + result = readSimple(); + if (result != FMOD_OK) + { + return result; + } + } + else + { + return FMOD_ERR_FORMAT; + } + } + + FMOD_memset(&mWaveFormat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + + waveformat = &mWaveFormat; + + numsubsounds = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecPlaylist::openInternal", "open successful\n")); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readASX() +{ + FMOD_RESULT result; + + char tag [MAX_TOKEN_SIZE]; + char tagdata[MAX_TOKEN_SIZE]; + + int tagsize = MAX_TOKEN_SIZE; + int tagdatasize = MAX_TOKEN_SIZE; + + /* + + + <Author> + <Copyright> + <MoreInfo href="path"/> + <Entry> + <Duration value="00:00:00"> + <Logo href="path" Style="MARK or ICON"/> + <Banner href="path"/> + <Ref href="path"/> + */ + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + First tag should be <ASX VERSION="3.0"> + */ + result = getNextXMLTag(tag, &tagsize, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strnicmp("ASX VERSION", tag, 11)) + { + return FMOD_ERR_FORMAT; + } + + for (;;) + { + tagsize = MAX_TOKEN_SIZE; + tagdatasize = MAX_TOKEN_SIZE; + + result = getNextXMLTag(tag, &tagsize, tagdata, &tagdatasize); + if (result != FMOD_OK) + { + break; + } + + tag[tagsize] = '\0'; + tagdata[tagdatasize] = '\0'; + + if (!FMOD_strnicmp("ENTRY", tag, 5)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "ENTRY", 0, 0, FMOD_TAGDATATYPE_STRING, false); + } + else if (tagdatasize == 0) + { + char url[MAX_TOKEN_SIZE]; + int urlsize = MAX_TOKEN_SIZE; + + result = getQuoteData(tag, url, &urlsize); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strnicmp("REF HREF", tag, 8)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("MOREINFO HREF", tag, 13)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "MOREINFO", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("DURATION VALUE", tag, 14)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "DURATION", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("LOGO HREF", tag, 9)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "LOGO", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("BANNER HREF", tag, 11)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "BANNER", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + } + else + { + metaData(FMOD_TAGTYPE_PLAYLIST, FMOD_strupr(tag), tagdata, tagdatasize + 1, FMOD_TAGDATATYPE_STRING, false); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readWPL() +{ + FMOD_RESULT result; + char tag [MAX_TOKEN_SIZE]; + char tagdata[MAX_TOKEN_SIZE]; + + int tagsize = MAX_TOKEN_SIZE; + int tagdatasize = MAX_TOKEN_SIZE; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + First tag should be <?wpl version="1.0"?> + */ + result = getNextXMLTag(tag, &tagsize, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strnicmp("?WPL VERSION", tag, 12)) + { + return FMOD_ERR_FORMAT; + } + + /* + Skip to the <seq> tag + */ + do + { + tagsize = MAX_TOKEN_SIZE; + result = getNextXMLTag(tag, &tagsize, 0, 0); + + if (result != FMOD_OK) + { + return result; + } + } + while (FMOD_strnicmp("seq", tag, 3)); + + /* + Read the data from file + */ + for (;;) + { + tagsize = MAX_TOKEN_SIZE; + tagdatasize = MAX_TOKEN_SIZE; + + result = getNextXMLTag(tag, &tagsize, tagdata, &tagdatasize); + if (result != FMOD_OK) + { + break; + } + tag[tagsize] = '\0'; + tagdata[tagdatasize] = '\0'; + + if (tagdatasize == 0) + { + char url[MAX_TOKEN_SIZE]; + int urlsize = MAX_TOKEN_SIZE; + + getQuoteData(tag, url, &urlsize); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strnicmp("MEDIA SRC", tag, 8)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + } + else + { + metaData(FMOD_TAGTYPE_PLAYLIST, FMOD_strupr(tag), tagdata, tagdatasize + 1, FMOD_TAGDATATYPE_STRING, false); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readB4S() +{ + FMOD_RESULT result; + char tag [MAX_TOKEN_SIZE]; + char tagdata[MAX_TOKEN_SIZE]; + + int tagsize = MAX_TOKEN_SIZE; + int tagdatasize = MAX_TOKEN_SIZE; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + First tag should be <?xml version="1.0" encoding='UTF-8' standalone="yes"?> + */ + result = getNextXMLTag(tag, &tagsize, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strnicmp("?XML VERSION", tag, 12)) + { + return FMOD_ERR_FORMAT; + } + + /* + Find the first entry + */ + while (FMOD_strnicmp("ENTRY PLAYSTRING=", tag, 16)) + { + tagsize = MAX_TOKEN_SIZE; + tagdatasize = MAX_TOKEN_SIZE; + + result = getNextXMLTag(tag, &tagsize, tagdata, &tagdatasize); + if (result != FMOD_OK) + { + return FMOD_ERR_FORMAT; + } + tag[tagsize] = '\0'; + tagdata[tagdatasize] = '\0'; + } + + /* + Read the data from file + */ + for (;;) + { + if (tagdatasize == 0) + { + char url[MAX_TOKEN_SIZE]; + int urlsize; + + getQuoteData(tag, url, &urlsize); + + if (!FMOD_strnicmp("ENTRY PLAYSTRING=", tag, 17)) + { + if(!FMOD_strnicmp("FILE:", url, 5)) //Note sometimes B4S paths are "file:c:/..." instead of "c:/..." + { + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", url + 5, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + } + else if (!FMOD_strnicmp("NAME", tag, 13)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "NAME", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("LENGTH", tag, 14)) + { + metaData(FMOD_TAGTYPE_PLAYLIST, "LENGTH", url, urlsize + 1, FMOD_TAGDATATYPE_STRING, false); + } + } + else + { + metaData(FMOD_TAGTYPE_PLAYLIST, FMOD_strupr(tag), tagdata, tagdatasize + 1, FMOD_TAGDATATYPE_STRING, false); + } + + tagsize = MAX_TOKEN_SIZE; + tagdatasize = MAX_TOKEN_SIZE; + + result = getNextXMLTag(tag, &tagsize, tagdata, &tagdatasize); + if (result != FMOD_OK) + { + break; + } + tag[tagsize] = '\0'; + tagdata[tagdatasize] = '\0'; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Insanely simple parsing of xml for the sole purpose of ASX files. + Doesn't handle attributes. + Doesn't handle nesting. + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::getNextXMLTag(char *tagname, int *tagnamesize, char *tagdata, int *tagdatasize) +{ + FMOD_RESULT result; + int count = 0; + unsigned char c = 0; + int datasize = 0; + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + return result; + } + + /* + Get the tag + */ + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + } + while (c != '<'); + + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + if (count < *tagnamesize) + { + tagname[count++] = c; + } + } + while (c != '>'); + + *tagnamesize = count - 1; + + count = 0; + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + return result; + } + + if (tagdatasize) + { + datasize = *tagdatasize; + } + + /* + Get the tag data + */ + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + if (count < datasize) + { + tagdata[count++] = c; + } + } + while (c != '<'); + + if (tagdatasize) + { + *tagdatasize = count - 1; + } + + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + /* + Check if it is a closing tag + */ + if (c == '/') + { + /* + Its a closing tag, move file pointer to the end of it + */ + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + } + while (c != '>'); + } + else + { + result = mFile->seek(-2, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readM3U() +{ + FMOD_RESULT result; + int count = 0; + unsigned char c; + char token[MAX_TOKEN_SIZE]; + int length = 0; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get the first token, it should be '#EXTM3U' + */ + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK || count >= MAX_TOKEN_SIZE) + { + return FMOD_ERR_FORMAT; + } + + token[count++] = c; + } + while (!isNewLine(c)); + + if (FMOD_strnicmp(token, "#EXTM3U", 7)) + { + return FMOD_ERR_FORMAT; + } + + /* + #EXTINF:([0-9]*),(.*) + */ + for (;;) + { + count = 0; + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + break; + } + + /* + Get #EXTINF: + */ + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + break; + } + + if (count < MAX_TOKEN_SIZE) + { + token[count++] = c; + } + } + while (c != ':' && result == FMOD_OK); + + if (FMOD_strnicmp("#EXTINF", token, 7)) + { + return FMOD_ERR_FORMAT; + } + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + break; + } + + /* + Get LENGTH + */ + count = 0; + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + break; + } + + if (count < (MAX_TOKEN_SIZE - 1)) + { + token[count++] = c; + } + } + while (c != ',' && result == FMOD_OK); + + // Convert to int + token[count - 1] = '\0'; + length = atoi(token); + + metaData(FMOD_TAGTYPE_PLAYLIST, "LENGTH", &length, sizeof(int), FMOD_TAGDATATYPE_INT, false); + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + break; + } + + /* + Get TITLE + */ + count = 0; + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + break; + } + + if (c != '\n' && c != '\r' && count < (MAX_TOKEN_SIZE - 1)) + { + token[count++] = c; + } + } + while (!isNewLine(c)); + + token[count] = '\0'; + + metaData(FMOD_TAGTYPE_PLAYLIST, "TITLE", token, count + 1, FMOD_TAGDATATYPE_STRING, false); + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + break; + } + + /* + Get FILE + */ + count = 0; + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + break; + } + + if (c != '\n' && c != '\r' && count < (MAX_TOKEN_SIZE - 1)) + { + token[count++] = c; + } + } + while (!isNewLine(c) && result == FMOD_OK); + + token[count] = '\0'; + + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", token, count, FMOD_TAGDATATYPE_STRING, false); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readPLS() +{ + FMOD_RESULT result; + int tokensize; + char token[MAX_TOKEN_SIZE]; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get the first token, it should be '[playlist]' + */ + result = getPLSToken(token, MAX_TOKEN_SIZE, 0); + if (result != FMOD_OK) + { + return FMOD_ERR_FORMAT; + } + if (FMOD_strnicmp(token, "[playlist]", 10)) + { + return FMOD_ERR_FORMAT; + } + + /* + Get the playlist information + */ + for (;;) + { + result = getPLSToken(token, MAX_TOKEN_SIZE, 0); + if (result != FMOD_OK) + { + break; + } + + if (!FMOD_strnicmp("File", token, 4)) + { + result = getPLSToken(token, MAX_TOKEN_SIZE, &tokensize); + if (result != FMOD_OK) + { + break; + } + + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", token, tokensize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("Title", token, 5)) + { + result = getPLSToken(token, MAX_TOKEN_SIZE, &tokensize); + if (result != FMOD_OK) + { + break; + } + + metaData(FMOD_TAGTYPE_PLAYLIST, "TITLE", token, tokensize + 1, FMOD_TAGDATATYPE_STRING, false); + } + else if (!FMOD_strnicmp("Length", token, 6)) + { + int length = 0; + + result = getPLSToken(token, MAX_TOKEN_SIZE, &tokensize); + if (result != FMOD_OK) + { + break; + } + + // Convert to int + token[tokensize] = '\0'; + length = atoi(token); + + metaData(FMOD_TAGTYPE_PLAYLIST, "LENGTH", &length, sizeof(int), FMOD_TAGDATATYPE_INT, false); + } + + else if (!FMOD_strnicmp("NumberOfEntries", token, 15)) + { + result = getPLSToken(token, MAX_TOKEN_SIZE, 0); + if (result != FMOD_OK) + { + break; + } + } + else if (!FMOD_strnicmp("Version", token, 7)) + { + result = getPLSToken(token, MAX_TOKEN_SIZE, 0); + if (result != FMOD_OK) + { + break; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::getPLSToken(char *buffer, int length, int *tokensize) +{ + FMOD_RESULT result = FMOD_OK; + unsigned char c; + int count = 0; + int numwhitespaces = 0; + + /* + [playlist] + + File(0-9)*= + Title(0-9)*= + Length(0-9)*= + + NumberOfEntries= + Version= + + numberofentries= + version= + + (and data after the '=' sign) + */ + + result = skipWhiteSpace(&numwhitespaces); + if (result != FMOD_OK) + { + return result; + } + + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + if (c != '\n' && c != '\r' && count < length) + { + buffer[count++] = c; + } + + /* + It could be just a filename with an '=' in it, + Check if char at -count - numwhitespaces is a newline. + */ + if (c == '=') + { + result = mFile->seek(-1*count - numwhitespaces - 1, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(count + numwhitespaces, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + if (isNewLine(c)) + { + count--; + break; + } + } + + /* + It could be just a filename with a ']' in it, + check if char at -count is a '[' + */ + if (c == ']') + { + /* seek to start of line */ + result = mFile->seek(-1*count, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + /* get the first character (after whitespace) on this line */ + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + /* seek back to where we were */ + result = mFile->seek(count - 1, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + /* check if first character of line is a '[' */ + if (c == '[') + { + result = mFile->seek(2, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + break; + } + } + } + while (!isNewLine(c)); + + if (tokensize) + { + *tokensize = count; + } + + buffer[count] = '\0'; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readSimple() +{ + FMOD_RESULT result; + char line[MAX_TOKEN_SIZE]; + int linelength = 0; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + for (;;) + { + result = skipSimpleComments(); + if (result != FMOD_OK) + { + break; + } + + result = readLine(line, MAX_TOKEN_SIZE, &linelength); + if (result != FMOD_OK) + { + break; + } + + metaData(FMOD_TAGTYPE_PLAYLIST, "FILE", line, linelength + 1, FMOD_TAGDATATYPE_STRING, false); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::readLine(char *buffer, int length, int *linelength) +{ + FMOD_RESULT result; + int count = 0; + unsigned char c; + + result = skipWhiteSpace(0); + if (result != FMOD_OK) + { + return result; + } + + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + if (c != '\n' && c != '\r' && count < length) + { + buffer[count++] = c; + } + } + while (!isNewLine(c)); + + if (linelength) + { + *linelength = count; + } + + buffer[count] = '\0'; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::skipSimpleComments() +{ + FMOD_RESULT result = FMOD_OK; + int numwhitespaces = 0; + unsigned char c; + + for (;;) + { + result = skipWhiteSpace(&numwhitespaces); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + if (c == '#' || c == '[') + { + /* + Skip this comment + */ + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + } + while (!isNewLine(c)); + } + else + { + result = mFile->seek(-1, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::getQuoteData(const char *tag, char *buffer, int *datasize) +{ + char c = 0; + int count1 = 0; + int count2 = 0; + + /* + Find first quotation mark + */ + do + { + if (count1 < MAX_TOKEN_SIZE) + { + c = tag[count1++]; + } + } + while (c != '"'); + + /* + Find second quotation mark and copy data + */ + do + { + c = tag[count1++]; + if (c == '"') + { + break; + } + buffer[count2++] = c; + } + while (count1 < (MAX_TOKEN_SIZE - 1)); + + buffer[count2] = '\0'; + *datasize = count2; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::skipWhiteSpace(int *numwhitespaces) +{ + FMOD_RESULT result; + unsigned char c; + int whitespacecount = 0; + + do + { + result = mFile->getByte(&c); + if (result != FMOD_OK) + { + return result; + } + + whitespacecount++; + } + while (FMOD_isspace(c) || c == '\r'); + + result = mFile->seek(-1, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + if (numwhitespaces) + { + *numwhitespaces = whitespacecount - 1; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Checks if we are up to a newline or not. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Some text editors put a '\n' as a newline, + some use '\r' and some use '\r\n' :@ !!! + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +bool CodecPlaylist::isNewLine(char c) +{ + switch (c) + { + case '\n': + return true; + + case '\r': + { + unsigned char next; + + mFile->getByte(&next); + mFile->seek(-1, SEEK_CUR); + if (next == '\n') + { + return false; + } + else + { + return true; + } + } + + default: + return false; + } + + return false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecPlaylist::closeInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecPlaylist::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecPlaylist *playlist = (CodecPlaylist *)codec; + + return playlist->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecPlaylist::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecPlaylist *playlist = (CodecPlaylist *)codec; + + return playlist->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecPlaylist::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecPlaylist::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecPlaylist::soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + return FMOD_OK; +} + +} + +#endif + + diff --git a/src/fmod_codec_playlist.h b/src/fmod_codec_playlist.h new file mode 100755 index 0000000..2e11132 --- /dev/null +++ b/src/fmod_codec_playlist.h @@ -0,0 +1,53 @@ +#ifndef _FMOD_CODEC_PLAYLIST_H +#define _FMOD_CODEC_PLAYLIST_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PLAYLIST + +#include "fmod_codeci.h" + +namespace FMOD +{ + class CodecPlaylist : public Codec + { + private: + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + + FMOD_RESULT readASX(); + FMOD_RESULT readWPL(); + FMOD_RESULT readM3U(); + FMOD_RESULT readPLS(); + FMOD_RESULT readB4S(); + FMOD_RESULT readSimple(); + + FMOD_RESULT getNextXMLTag(char *tagname, int *tagnamesize, char *tagdata, int *tagdatasize); + FMOD_RESULT getPLSToken(char *buffer, int length, int *tokensize); + FMOD_RESULT readLine(char *buffer, int length, int *linelength); + FMOD_RESULT skipSimpleComments(); + FMOD_RESULT getQuoteData(const char *tag, char *buffer, int *datasize); + + FMOD_RESULT skipWhiteSpace(int *numwhitespaces); + bool isNewLine(char c); + + FMOD_CODEC_WAVEFORMAT mWaveFormat; + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_PLAYLIST */ + +#endif + + diff --git a/src/fmod_codec_raw.cpp b/src/fmod_codec_raw.cpp new file mode 100755 index 0000000..ace9fbb --- /dev/null +++ b/src/fmod_codec_raw.cpp @@ -0,0 +1,458 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_RAW + +#include "fmod.h" +#include "fmod_codec_raw.h" +#include "fmod_codec_wav.h" +#include "fmod_codec_wav_imaadpcm.h" +#include "fmod_dsp_codec.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#ifdef PLATFORM_PS3_SPU +#include "fmod_systemi_spu.h" +#else +#include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_codec_raw_pic_start[]; +#endif + + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX rawcodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecRaw::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecRaw::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&rawcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + rawcodec.name = "FMOD Raw Codec"; + rawcodec.version = 0x00010100; + rawcodec.timeunits = FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_RAWBYTES; + rawcodec.open = &CodecRaw::openCallback; + rawcodec.close = &CodecRaw::closeCallback; + rawcodec.read = &CodecRaw::readCallback; + rawcodec.setposition = &CodecRaw::setPositionCallback; + rawcodec.canpoint = &CodecRaw::canPointCallback; + + rawcodec.mType = FMOD_SOUND_TYPE_RAW; + rawcodec.mSize = sizeof(CodecRaw); + + #ifdef PLATFORM_PS3 + rawcodec.mModule = (FMOD_OS_LIBRARY *)_binary_spu_fmod_codec_raw_pic_start; + #endif +#else + rawcodec.read = &CodecRaw::readCallback; + rawcodec.setposition = &CodecRaw::setPositionCallback; +#endif + + return &rawcodec; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecRaw::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + + init(FMOD_SOUND_TYPE_RAW); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecRaw::openInternal", "attempting to open as RAW..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + waveformat = &mWaveFormat; + + /* + Get size of file in bytes + */ + result = mFile->getSize(&waveformat[0].lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + if (usermode & FMOD_SOFTWARE) + { + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + if (1 +#if defined(FMOD_SUPPORT_IMAADPCM) + && userexinfo->format != FMOD_SOUND_FORMAT_IMAADPCM +#endif + ) + { + return FMOD_ERR_FORMAT; + } + } + else if (userexinfo->format < FMOD_SOUND_FORMAT_PCM8 || userexinfo->format > FMOD_SOUND_FORMAT_PCMFLOAT) + { + return FMOD_ERR_FORMAT; + } + } + + waveformat[0].format = userexinfo->format; + waveformat[0].channels = userexinfo->numchannels; + waveformat[0].frequency = userexinfo->defaultfrequency; + SoundI::getSamplesFromBytes(waveformat[0].lengthbytes, &waveformat[0].lengthpcm, userexinfo->numchannels, userexinfo->format); + waveformat[0].blockalign = waveformat[0].channels * 16 / 8; + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + +#if defined(FMOD_SUPPORT_IMAADPCM) && defined(FMOD_SUPPORT_DSPCODEC) + if (waveformat[0].format == FMOD_SOUND_FORMAT_IMAADPCM) + { + int count; + + if (waveformat[0].channels > 2) + { + return FMOD_ERR_TOOMANYCHANNELS; /* Sorry we're only allocating memory for a pool of maximum stereo voices. */ + } + + mSamplesPerADPCMBlock = 64; + mReadBufferLength = 36 * waveformat[0].channels; + + if (!mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs) + { + result = mSystem->mDSPCodecPool_ADPCM.init(FMOD_DSP_CATEGORY_DSPCODECADPCM, 64, mSystem->mAdvancedSettings.maxADPCMcodecs ? mSystem->mAdvancedSettings.maxADPCMcodecs : FMOD_ADVANCEDSETTINGS_MAXADPCMCODECS); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_ADPCM.mPool[count]); + CodecWav *wav = (CodecWav *)dspcodec->mCodec; + + wav->mSrcFormat = &wav->mSrcFormatMemory; + wav->mReadBuffer = mSystem->mDSPCodecPool_ADPCM.mReadBuffer; + wav->mSrcFormat->Format.wFormatTag = WAVE_FORMAT_IMA_ADPCM; + } + } + } +#endif + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecRaw::closeInternal() +{ + return FMOD_OK; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecRaw::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result; + + if (waveformat[0].format == FMOD_SOUND_FORMAT_PCM16) + { + result = mFile->read(buffer, 2, sizebytes / 2, bytesread); + + *bytesread *= 2; /* convert from 16bit words back to bytes */ + } + else + { + result = mFile->read(buffer, 1, sizebytes, bytesread); + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecRaw::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + unsigned int raw; + + if (postype == FMOD_TIMEUNIT_RAWBYTES) + { + raw = position; + } + else + { + raw = (unsigned int)((FMOD_UINT64)position * (FMOD_UINT64)waveformat[0].lengthbytes / (FMOD_UINT64)waveformat[0].lengthpcm); + raw /= waveformat[0].blockalign; + raw *= waveformat[0].blockalign; + } + + result = mFile->seek(mSrcDataOffset + raw, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecRaw::canPointInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecRaw::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecRaw *raw = (CodecRaw *)codec; + + return raw->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecRaw::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecRaw *raw = (CodecRaw *)codec; + + return raw->closeInternal(); +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecRaw::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecRaw *raw = (CodecRaw *)codec; + + return raw->readInternal(buffer, sizebytes, bytesread); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecRaw::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecRaw *raw = (CodecRaw *)codec; + + return raw->setPositionInternal(subsound, position, postype); +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecRaw::canPointCallback(FMOD_CODEC_STATE *codec) +{ + CodecRaw *raw = (CodecRaw *)codec; + + return raw->canPointInternal(); +} +#endif + +} + +#endif + diff --git a/src/fmod_codec_raw.h b/src/fmod_codec_raw.h new file mode 100755 index 0000000..1e00c02 --- /dev/null +++ b/src/fmod_codec_raw.h @@ -0,0 +1,44 @@ +#ifndef _FMOD_CODEC_RAW_H +#define _FMOD_CODEC_RAW_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_RAW + +#include "fmod_codeci.h" + +namespace FMOD +{ + class CodecRaw : public Codec + { + friend class ChannelSoftware; + friend class ChannelOpenAL; + friend class DSPCodec; + + private: + + FMOD_CODEC_WAVEFORMAT mWaveFormat; + int mSamplesPerADPCMBlock; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT canPointInternal(); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK canPointCallback(FMOD_CODEC_STATE *codec); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_RAW */ + +#endif + diff --git a/src/fmod_codec_s3m.cpp b/src/fmod_codec_s3m.cpp new file mode 100755 index 0000000..cb625aa --- /dev/null +++ b/src/fmod_codec_s3m.cpp @@ -0,0 +1,2856 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_S3M + +#include "fmod.h" + +#include "fmod_channel_software.h" +#include "fmod_codec_s3m.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_file.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_string.h" + +#include <stdio.h> +#include <stdlib.h> + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX s3mcodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecS3M::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecS3M::getDescriptionEx() +{ + FMOD_memset(&s3mcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + s3mcodec.name = "FMOD S3M Codec"; + s3mcodec.version = 0x00010100; + s3mcodec.timeunits = (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_MODORDER | FMOD_TIMEUNIT_MODROW | FMOD_TIMEUNIT_MODPATTERN); + s3mcodec.defaultasstream = 1; + s3mcodec.open = &CodecS3M::openCallback; + s3mcodec.close = &CodecS3M::closeCallback; + s3mcodec.read = &CodecS3M::readCallback; + s3mcodec.getlength = &MusicSong::getLengthCallback; + s3mcodec.setposition = &CodecS3M::setPositionCallback; + s3mcodec.getposition = &MusicSong::getPositionCallback; + + s3mcodec.getmusicnumchannels = &MusicSong::getMusicNumChannelsCallback; + s3mcodec.setmusicchannelvolume = &MusicSong::setMusicChannelVolumeCallback; + s3mcodec.getmusicchannelvolume = &MusicSong::getMusicChannelVolumeCallback; + + s3mcodec.mType = FMOD_SOUND_TYPE_S3M; + s3mcodec.mSize = sizeof(CodecS3M); + + return &s3mcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::calculateLength() +{ + waveformat[0].lengthpcm = 0; + + play(); + + while (!(mFinished)) + { + update(false); + + waveformat[0].lengthpcm += mMixerSamplesPerTick; + } + + stop(); + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelS3M::volumeSlide() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + if (!(mVolumeSlide & 0xF)) + { + vcptr->mVolume += (mVolumeSlide >> 4); + } + if (!(mVolumeSlide >> 4)) + { + vcptr->mVolume -= (mVolumeSlide & 0xF); + } + + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelS3M::portamento() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + /* + Slide pitch down if it needs too. + */ + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency += (mPortaSpeed << 2); + if (vcptr->mFrequency > mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + } + } + + /* + Slide pitch up if it needs too. + */ + if (vcptr->mFrequency > mPortaTarget) + { + vcptr->mFrequency -= (mPortaSpeed << 2); + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + } + } + + /* + if (glissando[track]) + { + } + */ + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + to carry out a vibrato at a certain depth and speed + + [PARAMETERS] + track - the track number to do the vibrato too + + [RETURN_VALUE] + + [REMARKS] + AND'ing temp with 31 removes the sign bit giving the abs value + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelS3M::vibrato() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (unsigned char)(mVibPos & 31); + + switch (mWaveControl & 3) + { + case 0: + delta = gSineTable[temp]; /* sine */ + break; + case 1: + temp <<= 3; /* ramp down */ + if (mVibPos < 0) + { + temp=255-temp; + } + delta=temp; + break; + case 2: + delta = 255; /* square */ + break; + case 3: + delta = FMOD_RAND()&255; /* random */ + break; + default : + delta = 0; + break; + }; + + delta *= mVibDepth; + delta >>=7; + delta <<=2; /* we use 4*periods so make vibrato 4 times bigger */ + + if (mVibPos >= 0) + { + vcptr->mFrequencyDelta = delta; + } + else + { + vcptr->mFrequencyDelta = -delta; + } + + mVibPos += mVibSpeed; + if (mVibPos > 31) + { + mVibPos -= 64; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + To carry out a tremolo at a certain depth and speed + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelS3M::tremolo() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (mTremoloPosition & 31); + + switch((mWaveControl >> 4) & 3) + { + case 0: delta = gSineTable[temp]; /* sine */ + break; + case 1: temp <<= 3; /* ramp down */ + if (mTremoloPosition < 0) + { + temp=255-temp; + } + delta=temp; + break; + case 2: delta = 255; /* square */ + break; + case 3: delta = FMOD_RAND()&255; /* random */ + break; + default : + delta = 0; + break; + }; + + delta *= mTremoloDepth; + delta >>= 6; + + if (mTremoloPosition >= 0) + { + if (vcptr->mVolume+delta > 64) + { + delta = 64 - vcptr->mVolume; + } + vcptr->mVolumeDelta = delta; + } + else + { + if (((short)vcptr->mVolume-delta) < 0) + { + delta = vcptr->mVolume; + } + vcptr->mVolumeDelta = -delta; + } + + mTremoloPosition += mTremoloDepth; + if (mTremoloPosition > 31) + { + mTremoloPosition -=64; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelS3M::fineVibrato() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (mVibPos & 31); + + switch (mWaveControl & 3) + { + case 0: delta = gSineTable[temp]; /* sine */ + break; + case 1: temp <<= 3; /* ramp down */ + if (mVibPos < 0) + { + temp=255-temp; + } + delta=temp; + break; + case 2: delta = 255; /* square */ + break; + case 3: delta = FMOD_RAND()&255; /* random */ + break; + default : + delta = 0; + break; + }; + + delta *= mVibDepth; + delta >>=7; + + if (mVibPos >= 0) + { + vcptr->mFrequencyDelta = delta; + } + else + { + vcptr->mFrequencyDelta = -delta; + } + + mVibPos += mVibSpeed; + if (mVibPos > 31) + { + mVibPos -= 64; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::updateNote(bool audible) +{ + MusicNote *current; + bool breakflag = false; + bool jumpflag = false; + int count; + + /* + Point our note pointer to the correct pattern buffer, and to the + correct offset in this buffer indicated by row and number of channels + */ + current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels); + if (!current) + { + return FMOD_OK; + } + + if (mVisited) + { + if (mVisited[(mOrder * FMUSIC_MAXROWS) + mRow]) + { + mFinished = true; + return FMOD_OK; + } + mVisited[(mOrder * FMUSIC_MAXROWS) + mRow] = true; + } + + /* + Loop through each channel in the row until we have finished + */ + for (count = 0; count < mNumChannels; count++,current++) + { + MusicChannel *cptr = 0; + MusicVirtualChannel *vcptr = 0; + MusicSample *sptr = 0; + unsigned char paramx, paramy; + int oldvolume, oldfreq; + + paramx = current->mEffectParam >> 4; /* get effect param x */ + paramy = current->mEffectParam & 0xF; /* get effect param y */ + + cptr = mMusicChannel[count]; + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + /* + First store note and instrument number if there was one + */ + if (current->mNumber) + { + cptr->mInstrument = current->mNumber - 1; /* remember the Instrument # */ + +// if (mInstCallback[current->mNumber] && mInstCallback[current->mNumber]->callback) +// { +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_INSTRUMENT, current->mNumber); +// } + } + if (current->mNote && current->mNote != FMUSIC_KEYOFF) + { + cptr->mNote = current->mNote-1; /* remember the note */ + } + + /* + Set up sample pointer + */ + if (cptr->mInstrument < mNumSamples) + { + sptr = &mSample[cptr->mInstrument]; + } + else + { + sptr = &gDummySample; + } + + oldvolume = vcptr->mVolume; + oldfreq = vcptr->mFrequency; + + /* + if there is no more tremolo, set volume to volume + last tremolo delta + */ + if (cptr->mRecentEffect == FMUSIC_S3M_TREMOLO && current->mEffect != FMUSIC_S3M_TREMOLO) + { + vcptr->mVolume += vcptr->mVolumeDelta; + } + cptr->mRecentEffect = current->mEffect; + + vcptr->mVolumeDelta = 0; + vcptr->mNoteControl = 0; + + /* + PROCESS NOTE + */ + if (current->mNote && current->mNote != FMUSIC_KEYOFF) + { + if (vcptr == &gDummyVirtualChannel) + { + FMOD_RESULT result; + + result = spawnNewVirtualChannel(cptr, sptr, &vcptr); + if (result != FMOD_OK) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + } + + /* + get period according to relative note, c2spd and finetune + */ + cptr->mNote = current->mNote-1; /* now remember the note */ + if (sptr->mMiddleC) + { + cptr->mPeriod = 8363L * gPeriodTable[cptr->mNote] / sptr->mMiddleC; + } + else + { + cptr->mPeriod = gPeriodTable[cptr->mNote]; + } + + vcptr->mPan = mDefaultPan[count]; + + /* + Frequency only changes if there are no portamento effects + */ + if (current->mEffect != FMUSIC_S3M_PORTATO && + current->mEffect != FMUSIC_S3M_PORTATOVOLSLIDE) + { + vcptr->mFrequency = cptr->mPeriod; + } + + vcptr->mNoteControl = FMUSIC_TRIGGER; + } + + /* + PROCESS INSTRUMENT NUMBER + */ + if (current->mNumber) + { + vcptr->mVolume = sptr->mDefaultVolume; + cptr->mTremorPosition = 0; /* retrigger tremor count */ + + /* + Retrigger tremolo and vibrato waveforms + */ + if ((cptr->mWaveControl & 0xF) < 4) + { + cptr->mVibPos=0; + } + if ((cptr->mWaveControl >> 4) < 4) + { + cptr->mTremoloPosition = 0; + } + } + + vcptr->mFrequencyDelta = 0; + vcptr->mNoteControl |= FMUSIC_FREQ | FMUSIC_VOLUME | FMUSIC_PAN; + + /* + PROCESS VOLUME BYTE + */ + if (current->mVolume) + { + vcptr->mVolume = current->mVolume - 1; + } + + /* + PROCESS KEY OFF + */ + if (current->mNote == FMUSIC_KEYOFF) + { + vcptr->mVolume = 0; + } + + /* + TICK 0 EFFECTS + */ + switch (current->mEffect) + { + case FMUSIC_S3M_SETSPEED : + { + if (current->mEffectParam) + { + mSpeed = current->mEffectParam; + } + break; + } + case FMUSIC_S3M_PATTERNJUMP : /* --- 00 B00 : --- 00 D63 , should put us at ord=0, row=63 */ + { + mNextOrder = current->mEffectParam; + mNextRow = 0; + + if (mNextOrder >= mNumOrders) + { + mNextOrder = 0; + } + + jumpflag = 1; + break; + } + case FMUSIC_S3M_PATTERNBREAK : + { + mNextRow = (paramx*10) + paramy; + + if (mNextRow > 63) + { + mNextRow = 0; + } + if (!breakflag && !jumpflag) + { + mNextOrder = mOrder + 1; + } + if (mNextOrder >= mNumOrders) + { + mNextOrder=0; + } + break; + } + case FMUSIC_S3M_VOLUMESLIDE : + { + /* + Dxy - Volume slide, fine vol DFx = slide down, DxF = slide up + */ + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + + /* + DFF is classed as a slide up so it gets priority + */ + if ((cptr->mVolumeSlide &0xF) == 0xF) + { + vcptr->mVolume += (cptr->mVolumeSlide >> 4); + } + else if ((cptr->mVolumeSlide >>4 ) == 0xF) + { + vcptr->mVolume -= (cptr->mVolumeSlide & 0xF); + } + + /* + Perform an extra slide if using old fast vol slides! + */ + if (mMusicFlags == FMUSIC_FLAGS_FASTVOLUMESLIDES) + { + if (!(cptr->mVolumeSlide & 0xF)) + { + vcptr->mVolume +=(cptr->mVolumeSlide >> 4); + } + if (!(cptr->mVolumeSlide >> 4)) + { + vcptr->mVolume -=(cptr->mVolumeSlide & 0xF); + } + } + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + break; + } + case FMUSIC_S3M_PORTADOWN : + { + if (current->mEffectParam) + { + cptr->mPortaUpDown = current->mEffectParam; + } + + if ((cptr->mPortaUpDown >> 4)==0xF) + { + vcptr->mFrequency += ((cptr->mPortaUpDown & 0xF) << 2); + } + if ((cptr->mPortaUpDown >> 4)==0xE) + { + vcptr->mFrequency += (cptr->mPortaUpDown & 0xF); + } + break; + } + + case FMUSIC_S3M_PORTAUP : + { + if (current->mEffectParam) + { + cptr->mPortaUpDown = current->mEffectParam; + } + + if ((cptr->mPortaUpDown >>4)==0xF) + { + vcptr->mFrequency -= ((cptr->mPortaUpDown & 0xF) << 2); + } + if ((cptr->mPortaUpDown >>4)==0xE) + { + vcptr->mFrequency -= (cptr->mPortaUpDown & 0xF); + } + break; + } + case FMUSIC_S3M_PORTATO : + { + if (current->mEffectParam) + { + cptr->mPortaSpeed = current->mEffectParam; + } + + cptr->mPortaTarget = cptr->mPeriod; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + break; + } + case FMUSIC_S3M_VIBRATO : + { + if (paramx) + { + cptr->mVibSpeed = paramx; + } + if (paramy) + { + cptr->mVibDepth = paramy; + } + break; + } + case FMUSIC_S3M_TREMOR : + { + if (current->mEffectParam) + { + cptr->mTremorOn = (paramx+1); + cptr->mTremorOff = (paramy+1); + } + if (cptr->mTremorPosition >= cptr->mTremorOn) + { + vcptr->mVolumeDelta = -vcptr->mVolume; + } + cptr->mTremorPosition++; + if (cptr->mTremorPosition >= (cptr->mTremorOn + cptr->mTremorOff)) + { + cptr->mTremorPosition = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_S3M_PORTATOVOLSLIDE : + { + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + cptr->mPortaTarget = cptr->mPeriod; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + break; + } + case FMUSIC_S3M_VIBRATOVOLSLIDE : + { + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + break; + } + case FMUSIC_S3M_ARPEGGIO : + { + if (current->mEffectParam) + { + cptr->mArpeggio = current->mEffectParam; + } + break; + } + case FMUSIC_S3M_SETSAMPLEOFFSET : + { + unsigned int offset; + + offset = (int)(current->mEffectParam) << 8; + + if (offset >= sptr->mLoopStart + sptr->mLoopLength) + { + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + vcptr->mNoteControl |= FMUSIC_STOP; + } + else + { + vcptr->mSampleOffset = offset; + } + break; + } + case FMUSIC_S3M_RETRIGVOLSLIDE : + { + if (current->mEffectParam) + { + cptr->mRetrigX = paramx; + cptr->mRetrigY = paramy; + } + break; + } + case FMUSIC_S3M_TREMOLO : + { + if (paramx) + { + cptr->mTremoloDepth = paramx; + } + if (paramy) + { + cptr->mTremoloDepth = paramy; + } + break; + } + + case FMUSIC_S3M_SPECIAL : + { + switch (paramx) + { + case FMUSIC_S3M_SETFILTER : + case FMUSIC_S3M_NOTECUT : + case FMUSIC_S3M_FUNKREPEAT : + { + break; + } + case FMUSIC_S3M_SETGLISSANDO : + { + break; + } + case FMUSIC_S3M_SETFINETUNE : + { + fineTune2Hz(paramy, &sptr->mMiddleC); + break; + } + case FMUSIC_S3M_SETVIBRATOWAVE : + { + cptr->mWaveControl &= 0xF0; + cptr->mWaveControl |= paramy; + break; + } + case FMUSIC_S3M_SETTREMOLOWAVE : + { + cptr->mWaveControl &= 0xF; + cptr->mWaveControl |= (paramy<<4); + break; + } + case FMUSIC_S3M_SETPANPOSITION16 : + { + vcptr->mPan = paramy<<4; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_S3M_STEREOCONTROL : + { + if (paramy > 7) + { + paramy-=8; + } + else + { + paramy+=8; + } + + vcptr->mPan = paramy<<4; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_S3M_PATTERNLOOP : + { + if (paramy == 0) + { + cptr->mPatternLoopRow = mRow; + } + else + { + if (!cptr->mPatternLoopNumber) + { + cptr->mPatternLoopNumber = paramy; + } + else + { + cptr->mPatternLoopNumber--; + } + if (cptr->mPatternLoopNumber) + { + int count2; + + mNextRow = cptr->mPatternLoopRow; + + if (mVisited) + { + for (count2 = cptr->mPatternLoopRow; count2 <= mRow; count2++) + { + mVisited[(mOrder * FMUSIC_MAXROWS) + count2] = false; + } + } + } + } + break; + } + case FMUSIC_S3M_NOTEDELAY : + { + vcptr->mVolume = oldvolume; + vcptr->mFrequency = oldfreq; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + vcptr->mNoteControl &= ~FMUSIC_PAN; + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + break; + } + case FMUSIC_S3M_PATTERNDELAY : + { + mPatternDelay = paramy; + mPatternDelay *= mSpeed; + break; + } + }; + break; + } + case FMUSIC_S3M_FINEVIBRATO : + { + if (paramx) + { + cptr->mVibSpeed = paramx; + } + if (paramy) + { + cptr->mVibDepth = paramy; + } + break; + } + case FMUSIC_S3M_GLOBALVOLUME : + { + mGlobalVolume = current->mEffectParam; + if (mGlobalVolume > 64) + { + mGlobalVolume = 64; + } + break; + } + case FMUSIC_S3M_SETTEMPO : + { + if (current->mEffectParam >= 0x20) + { + setBPM(current->mEffectParam); + } + break; + } + case FMUSIC_S3M_SETPAN : + { + vcptr->mPan = current->mEffectParam*2; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_S3M_Z : + { +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ZXX, current->mEffectParam); + break; + } + + }; + + if (audible) + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + + if (!(vcptr->mFrequency+vcptr->mFrequencyDelta)) + { + vcptr->mNoteControl &= ~FMUSIC_FREQ; + } + + if (vcptr->mNoteControl & FMUSIC_TRIGGER) + { + playSound(sptr, vcptr, false); + } + + /* + Volume = 6bit, global = 6bit.. halve volume for clipping 1 bit + */ + if (vcptr->mNoteControl & FMUSIC_VOLUME) + { + vcptr->mChannel.setVolume((float)((vcptr->mVolume + vcptr->mVolumeDelta) * mGlobalVolume) / (64.0f * 64.0f) * 0.5f * cptr->mMasterVolume); + } + if (vcptr->mNoteControl & FMUSIC_PAN) + { + float finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation; + + vcptr->mChannel.setPan(finalpan / 128.0f); + } + if (vcptr->mNoteControl & FMUSIC_FREQ) + { + int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + + if (finalfreq < 1) + { + finalfreq = 1; + } + + vcptr->mChannel.setFrequency((float)period2HZ(finalfreq)); + } + if (vcptr->mNoteControl & FMUSIC_STOP) + { + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */ + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::updateEffects() +{ + MusicNote *current; + int count; + + current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels); + + if (!current) + { + return FMOD_OK; + } + + for (count=0; count < mNumChannels; count++,current++) + { + MusicChannelS3M *cptr = 0; + MusicVirtualChannel *vcptr = 0; + MusicSample *sptr = 0; + unsigned char effect, paramx, paramy; + + cptr = (MusicChannelS3M *)mMusicChannel[count]; + + if (cptr->mInstrument < mNumSamples) + { + sptr = &mSample[cptr->mInstrument]; + } + else + { + sptr = &gDummySample; + } + + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + effect = current->mEffect; /* grab the effect number */ + paramx = current->mEffectParam >> 4; /* grab the effect parameter x */ + paramy = current->mEffectParam & 0xF; /* grab the effect parameter y */ + + vcptr->mVolumeDelta = 0; /* this is for tremolo etc */ + vcptr->mFrequencyDelta = 0; /* this is for vibrato / arpeggio etc */ + vcptr->mNoteControl = 0; + + switch (effect) + { + case FMUSIC_S3M_VOLUMESLIDE : + { + cptr->volumeSlide(); + break; + } + case FMUSIC_S3M_PORTADOWN : + { + if (cptr->mPortaUpDown <0xE0) + { + vcptr->mFrequency += (cptr->mPortaUpDown << 2); + } + vcptr->mNoteControl |= FMUSIC_FREQ; + break; + } + case FMUSIC_S3M_PORTAUP : + { + if (cptr->mPortaUpDown <0xE0) + { + vcptr->mFrequency -= (cptr->mPortaUpDown << 2); + if (vcptr->mFrequency < 1) + { + vcptr->mNoteControl |= FMUSIC_STOP; + } + else + { + vcptr->mNoteControl |= FMUSIC_FREQ; + } + } + break; + } + case FMUSIC_S3M_PORTATO : + { + cptr->portamento(); + break; + } + case FMUSIC_S3M_VIBRATO : + { + cptr->vibrato(); + break; + } + case FMUSIC_S3M_TREMOR : + { + if (cptr->mTremorPosition >= cptr->mTremorOn) + { + vcptr->mVolumeDelta = -vcptr->mVolume; + } + + cptr->mTremorPosition++; + if (cptr->mTremorPosition >= cptr->mTremorOn + cptr->mTremorOff) + { + cptr->mTremorPosition = 0; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_S3M_ARPEGGIO : + { + if (cptr->mArpeggio > 0) + { + paramx = cptr->mArpeggio >> 4; + paramy = cptr->mArpeggio & 0xF; + + switch (mTick % 3) + { + case 1: + { + if (sptr->mMiddleC) + { + vcptr->mFrequencyDelta = (8363L * gPeriodTable[cptr->mNote+paramx] / sptr->mMiddleC) - (8363L * gPeriodTable[cptr->mNote] / sptr->mMiddleC); + } + else + { + vcptr->mFrequencyDelta = gPeriodTable[cptr->mNote+paramx] - gPeriodTable[cptr->mNote]; + } + break; + } + case 2: + { + if (sptr->mMiddleC) + { + vcptr->mFrequencyDelta = (8363L * gPeriodTable[cptr->mNote+paramy] / sptr->mMiddleC) - (8363L * gPeriodTable[cptr->mNote] / sptr->mMiddleC); + } + else + { + vcptr->mFrequencyDelta = gPeriodTable[cptr->mNote+paramy] - gPeriodTable[cptr->mNote]; + } + break; + } + }; + + vcptr->mNoteControl |= FMUSIC_FREQ; + } + break; + } + case FMUSIC_S3M_VIBRATOVOLSLIDE : + { + cptr->vibrato(); + cptr->volumeSlide(); + break; + } + case FMUSIC_S3M_PORTATOVOLSLIDE : + { + cptr->portamento(); + cptr->volumeSlide(); + break; + } + case FMUSIC_S3M_RETRIGVOLSLIDE : + { + if (!cptr->mRetrigY) + { + break; /* divide by 0 bugfix */ + } + + if (!(mTick % cptr->mRetrigY)) + { + if (cptr->mRetrigX) + { + switch (cptr->mRetrigX) + { + case 1: vcptr->mVolume--; + break; + case 2: vcptr->mVolume -= 2; + break; + case 3: vcptr->mVolume -= 4; + break; + case 4: vcptr->mVolume -= 8; + break; + case 5: vcptr->mVolume -= 16; + break; + case 6: vcptr->mVolume *= 2/3; + break; + case 7: vcptr->mVolume >>= 1; + break; + case 8: /* ? */ + break; + case 9: vcptr->mVolume++; + break; + case 0xA: vcptr->mVolume += 2; + break; + case 0xB: vcptr->mVolume += 4; + break; + case 0xC: vcptr->mVolume += 8; + break; + case 0xD: vcptr->mVolume += 16; + break; + case 0xE: vcptr->mVolume *= 3/2; + break; + case 0xF: vcptr->mVolume <<= 1; + break; + }; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + + vcptr->mPan = mDefaultPan[count]; + vcptr->mFrequency = cptr->mPeriod; + vcptr->mFrequencyDelta= 0; + + vcptr->mNoteControl |= FMUSIC_VOLUME; + vcptr->mNoteControl |= FMUSIC_PAN; + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mNoteControl |= FMUSIC_TRIGGER; + } + break; + } + case FMUSIC_S3M_TREMOLO : + { + cptr->tremolo(); + break; + } + case FMUSIC_S3M_SPECIAL : + { + switch (paramx) + { + case FMUSIC_S3M_NOTECUT: + { + if (mTick==paramy) + { + vcptr->mVolume = 0; + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + break; + } + case FMUSIC_S3M_NOTEDELAY : + { + if (mTick == paramy) + { + if (vcptr == &gDummyVirtualChannel) + { + FMOD_RESULT result; + + result = spawnNewVirtualChannel(cptr, sptr, &vcptr); + if (result != FMOD_OK) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + } + + if (current->mNumber) + { + vcptr->mVolume = sptr->mDefaultVolume; + /* + Retrigger tremolo and vibrato waveforms + */ + if ((cptr->mWaveControl & 0xF) < 4) + { + cptr->mVibPos = 0; + } + if ((cptr->mWaveControl >> 4) < 4) + { + cptr->mTremoloPosition = 0; + } + + cptr->mTremorPosition = 0; /* retrigger tremor count */ + + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + + vcptr->mPan = mDefaultPan[count]; + vcptr->mFrequency = cptr->mPeriod; + vcptr->mFrequencyDelta= 0; + + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mNoteControl |= FMUSIC_PAN; + if (current->mVolume) + { + vcptr->mVolume = current->mVolume - 1; + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + vcptr->mNoteControl |= FMUSIC_TRIGGER; + } + else + { + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + vcptr->mNoteControl &= ~FMUSIC_PAN; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + } + break; + } + }; + break; + } + case FMUSIC_S3M_FINEVIBRATO : + { + cptr->fineVibrato(); + break; + } + + }; + + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + + if (!(vcptr->mFrequency+vcptr->mFrequencyDelta)) + { + vcptr->mNoteControl &= ~FMUSIC_FREQ; + } + + if (vcptr->mNoteControl & FMUSIC_TRIGGER) + { + playSound(sptr, vcptr, false); + } + + /* + volume = 6bit, global = 6bit.. halve volume for clipping 1 bit + */ + if (vcptr->mNoteControl & FMUSIC_VOLUME) + { + vcptr->mChannel.setVolume((float)((vcptr->mVolume + vcptr->mVolumeDelta) * mGlobalVolume) / (64.0f * 64.0f) * 0.5f * cptr->mMasterVolume); + } + if (vcptr->mNoteControl & FMUSIC_PAN) + { + float finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation; + + vcptr->mChannel.setPan(finalpan / 128.0f); + } + if (vcptr->mNoteControl & FMUSIC_FREQ) + { + int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + + if (finalfreq < 1) + { + finalfreq = 1; + } + + vcptr->mChannel.setFrequency((float)period2HZ(finalfreq)); + } + if (vcptr->mNoteControl & FMUSIC_STOP) + { + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */ + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::update(bool audible) +{ + if (mTick == 0) /* new note */ + { + if (mFinished && !mLooping) + { + stop(); + } + else + { + /* + Process any commands to set the next order/row from previous row + */ + if (mNextOrder >= 0) + { + mOrder = mNextOrder; +// checkCallback(FMUSIC_CALLBACK_ORDER, (unsigned char)mOrder); + if (mNextOrder >= 0) + { + mOrder = mNextOrder; + } + mNextOrder = -1; + } + if (mNextRow >= 0) + { + mRow = mNextRow; +// checkCallback(FMUSIC_CALLBACK_ROW, (unsigned char)mRow); + if (mNextRow >= 0) + { + mRow = mNextRow; + } + mNextRow = -1; + } + + updateNote(audible); /* Update and play the note */ + + /* + If there were no row commands + */ + if (mNextRow == -1) + { + mNextRow = mRow+1; + if (mNextRow >= 64) /* if end of pattern */ + { + mNextOrder = mOrder+1; /* so increment the order */ + if (mNextOrder >= mNumOrders) + { + mNextOrder = mRestart; + } + + mNextRow = 0; /* start at top of pattern */ + } + } + } + } + else if (audible) + { + updateEffects(); /* Else update the inbetween row effects */ + } + + mTick++; + if (mTick >= mSpeed + mPatternDelay) + { + mPatternDelay = 0; + mTick = 0; + } + + mPCMOffset += mMixerSamplesPerTick; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + unsigned char remap[32], st3pan, mastervol, temp[4]; + unsigned short temp16, songlength, filenumpatterns; + unsigned short parapoint[355]; + unsigned int samplepoint[99], lengthbytes; + int count; + FMOD_RESULT result = FMOD_OK; + + if (!mFile->mFlags & FMOD_FILE_SEEKABLE) + { + return FMOD_ERR_FORMAT; + } + + init(FMOD_SOUND_TYPE_S3M); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecS3M::openInternal", "attempting to open as S3M..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get size of file in bytes + */ + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + /* + Verify Format + */ + result = mFile->seek(0x2C, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&temp, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + if (FMOD_strncmp((char *)temp, "SCRM", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecS3M::openInternal", "'SCRM' ID check failed [%c%c%c%c]\n", temp[0], temp[1], temp[2], temp[3])); + return FMOD_ERR_FORMAT; + } + + /* + Set a few default values for this format + */ + for (count = 0; count < MUSIC_MAXCHANNELS; count++) + { + mMusicChannel[count] = 0; + } + mPattern = 0; + mPanSeparation = 0.8f; + mMasterSpeed = 1.0f; + mDefaultSpeed = 6; + mDefaultBPM = 125; + mDefaultGlobalVolume = 64; + mNumPatterns = 0; + mRestart = 0; + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(mSongName, 1, 28, 0); /* read in module name. */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(0x20, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&songlength); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mNumSamples); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&filenumpatterns); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&temp16); + if (result != FMOD_OK) + { + return result; + } + + if (temp16 & 64) + { + mMusicFlags = FMUSIC_FLAGS_FASTVOLUMESLIDES; + } + else + { + mMusicFlags = FMUSIC_FLAGS_NORMALVOLUMESLIDES; + } + + result = mFile->getWord(&temp16); + if (result != FMOD_OK) + { + return result; + } + if ((temp16 & 0xFFF) == 300) + { + mMusicFlags = FMUSIC_FLAGS_FASTVOLUMESLIDES; /* fast volume slides? */ + } + + /* + Seek to offset 02Ch for SCRM tag + */ + result = mFile->seek(0x2C, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&temp, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + if (FMOD_strncmp((char *)temp, "SCRM", 4)) + { + return FMOD_ERR_FORMAT; + } + + result = mFile->getByte(&mDefaultGlobalVolume); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&mDefaultSpeed); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&mDefaultBPM); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&mastervol); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&st3pan); + if (result != FMOD_OK) + { + return result; + } + + /* + Find the number of channels and remap the used channels linearly + */ + result = mFile->seek(0x40, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + mNumChannels = 0; + FMOD_memset(remap, 255, 32); + + for (count=0; count<32; count++) + { + unsigned char temp; + + result = mFile->getByte(&temp); + if (result != FMOD_OK) + { + return result; + } + if (temp < 16) + { + remap[count] = (unsigned char)mNumChannels; + if (temp <= 7) + { + mDefaultPan[mNumChannels] = 0; + } + else + { + mDefaultPan[mNumChannels] = 255; + } + + mNumChannels++; + } + } + + result = metaData(FMOD_TAGTYPE_FMOD, "Number of channels", &mNumChannels, sizeof(mNumChannels), FMOD_TAGDATATYPE_INT, false); + if (result != FMOD_OK) + { + return result; + } + + /* + ALLOCATE MUSIC CHANNELS + */ + for (count = 0; count < mNumChannels; count++) + { + mMusicChannel[count] = FMOD_Object_Calloc(MusicChannelS3M); + if (!mMusicChannel[count]) + { + return FMOD_ERR_MEMORY; + } + } + + /* + Load order data & calculate number of physical patterns + */ + result = mFile->seek(0x60, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(mOrderList, 1, songlength, 0); + if (result != FMOD_OK) + { + return result; + } + mNumOrders = 0; + mNumPatterns = 0; + for (count=0; count<songlength; count++) + { + mOrderList[mNumOrders] = mOrderList[count]; + if (mOrderList[count]<254) + { + mNumOrders++; + if (mOrderList[count] > mNumPatterns) + { + mNumPatterns = mOrderList[count]; + } + } + } + mNumPatterns++; + + /* + Load in instrument and pattern parapointers + */ + result = mFile->read(parapoint, 2, mNumSamples + filenumpatterns, 0); + if (result != FMOD_OK) + { + return result; + } + + /* + Check for default panning if the panning flag is set (252 = 0xFC :) + */ + if (st3pan == 252) + { + for (count=0; count<32; count++) + { + unsigned char temp; + + result = mFile->getByte(&temp); + if (result != FMOD_OK) + { + return result; + } + if (temp & 0x10) + { + mDefaultPan[remap[count]] = ((temp & 0xF)<<4); + } + } + } + + /* + If stereo flag is not set then make song mono + */ + if (!(mastervol & 128)) + { + for (count=0; count<32; count++) + { + mDefaultPan[count]= 128; + } + } + + /* + Load instrument information + */ + for (count = 0; count< mNumSamples; count++) + { + char sample_name[28]; + FMOD_MODE sample_mode; + unsigned int sample_length; + FMOD_SOUND_FORMAT sample_format; + unsigned char instflag; + unsigned char temp; + int channels = 1; + + FMOD_memset(&mSample[count], 0, sizeof(MusicSample)); + + /* + Jump to instrument parapointer. + */ + result = mFile->seek((unsigned int)((parapoint[count]) << 4), SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(13, SEEK_CUR); /* skip filename */ + if (result != FMOD_OK) + { + return result; + } + + /* + Find parapointer to actual sample data (3 bytes) + */ + result = mFile->getByte(&temp); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&temp16); + if (result != FMOD_OK) + { + return result; + } + samplepoint[count] = ((unsigned int )temp << 16) + temp16; + + /* + Get rest of information + */ + result = mFile->read(&sample_length, 4, 1, 0); /* get length */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mSample[count].mLoopStart, 4, 1, 0); /* loop start */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mSample[count].mLoopLength, 4, 1, 0); /* loop end */ + if (result != FMOD_OK) + { + return result; + } + mSample[count].mLoopLength -= mSample[count].mLoopStart; + + result = mFile->getByte(&mSample[count].mDefaultVolume); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&instflag); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mSample[count].mMiddleC); + if (result != FMOD_OK) + { + return result; + } + result = mFile->seek(14, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(sample_name, 28, 1, 0); /* Inst name */ + if (result != FMOD_OK) + { + return result; + } + + { + char s[256]; + + sprintf(s, "Sample name %d", count); + + result = metaData(FMOD_TAGTYPE_FMOD, s, sample_name, 28, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + } + + result = mFile->seek(4, SEEK_CUR); /* 'SCRS' */ + if (result != FMOD_OK) + { + return result; + } + + /* + Any samples with a loop length of 2 or less have no loop. + */ + sample_mode = FMOD_SOFTWARE | FMOD_2D; + + if (instflag & 1 && mSample[count].mLoopLength > 2) + { + sample_mode |= FMOD_LOOP_NORMAL; + } + else + { + sample_mode |= FMOD_LOOP_OFF; + mSample[count].mLoopStart = 0; + mSample[count].mLoopLength = sample_length; + } + + sample_format = FMOD_SOUND_FORMAT_PCM8; + if (instflag & 4) + { + sample_format = FMOD_SOUND_FORMAT_PCM16; + sample_length <<= 1; + } + if (instflag & 2) + { + channels = 2; + sample_length <<= 1; + } + + /* + ALLOCATE MEMORY FOR THE SAMPLE BUFFER + */ + if (sample_length) + { + FMOD_CREATESOUNDEXINFO exinfo; + + FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.length = sample_length; + exinfo.numchannels = channels; + exinfo.defaultfrequency = mSample[count].mMiddleC; + exinfo.format = sample_format; + + result = mSystem->createSound(0, sample_mode | FMOD_OPENUSER, &exinfo, &mSample[count].mSound); + if (result != FMOD_OK) + { + return result; + } + + if (sample_mode & FMOD_LOOP_NORMAL) + { + result = mSample[count].mSound->setLoopPoints(mSample[count].mLoopStart, FMOD_TIMEUNIT_PCM, mSample[count].mLoopStart + mSample[count].mLoopLength - 1, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + } + + } + + /* + Alloc pattern array + */ + mNumPatternsMem = (mNumPatterns > filenumpatterns ? mNumPatterns : filenumpatterns); + mPattern = (MusicPattern *)FMOD_Memory_Calloc(mNumPatternsMem * sizeof(MusicPattern)); + if (!mPattern) + { + return FMOD_ERR_MEMORY; + } + + + /* + Load the pattern data + */ + for (count=0; count < filenumpatterns; count++) + { + MusicPattern *pptr; + MusicNote *nptr; + int row,len; + + /* + Jump to pattern parapointer +2, skipping length bytes. + */ + result = mFile->seek((((unsigned int )parapoint[mNumSamples+count])<<4) + 2, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Allocate memory for the pattern header + */ + pptr = &mPattern[count]; + pptr->mRows = 64; + + /* + Allocate memory for pattern buffer + */ + len = mNumChannels * pptr->mRows * sizeof(MusicNote); + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(len); + if (!pptr->mData) + { + return FMOD_ERR_MEMORY; + } + + + /* + Unpack one pattern (pattern number 'count') + */ + for (row=0; row<64;) + { + unsigned char temp, channel; + MusicNote dummy; + + result = mFile->getByte(&temp); + if (result != FMOD_OK) + { + return result; + } + + /* + If it not 0, unpack note, if it is 0 then end of row. + */ + if(temp) + { + channel=remap[temp & 31]; + + /* + If it has told us to use a channel outside of the number of channels, load it into a dummy + */ + if(channel < mNumChannels) + { + nptr = pptr->mData + (mNumChannels * row) + channel; + } + else + { + nptr = &dummy; + } + + /* + If there is a note + */ + if(temp & 32) + { + result = mFile->getByte(&temp16); + if (result != FMOD_OK) + { + return result; + } + + /* + Convert s3m note to internal type note + */ + + switch(temp16) + { + case 255 : nptr->mNote = 0; + break; + case 254 : nptr->mNote = FMUSIC_KEYOFF; + break; + default : nptr->mNote = ((temp16>>4)*12)+(temp16&0xf) + 1; /* starts at 1 */ + }; + result = mFile->getByte(&nptr->mNumber); + if (result != FMOD_OK) + { + return result; + } + } + + /* + If there is a volume byte + */ + if (temp & 64) + { + result = mFile->getByte(&nptr->mVolume); + if (result != FMOD_OK) + { + return result; + } + nptr->mVolume++; + } + + /* + If there is an effect + */ + if(temp & 128) + { + result = mFile->getByte(&nptr->mEffect); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&nptr->mEffectParam); + if (result != FMOD_OK) + { + return result; + } + } + } + else row++; + } + } + + + /* + Allocate and clean out any extra patterns + */ + if (mNumPatterns > filenumpatterns) + { + for (count = filenumpatterns; count < mNumPatterns; count++) + { + MusicPattern *pptr; + + pptr = &mPattern[count]; + pptr->mRows = 64; + + /* + Allocate memory for pattern buffer + */ + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); + if (!pptr->mData) + { + return FMOD_ERR_MEMORY; + } + } + } + + /* + Load sample data + */ + for (count = 0; count < mNumSamples; count++) + { + MusicSample *sptr; + + result = mFile->seek(samplepoint[count] << 4, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + sptr = &mSample[count]; + if (sptr->mSound) + { + void *ptr1, *ptr2; + unsigned int len1, len2, lenbytes; + + result = sptr->mSound->getLength(&lenbytes, FMOD_TIMEUNIT_PCMBYTES); + if (result != FMOD_OK) + { + return result; + } + + result = sptr->mSound->lock(0, lenbytes, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + if (ptr1 && len1) + { + unsigned int count2; + + if (sptr->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16) + { + if (sptr->mSound->mChannels == 1) + { + result = mFile->read(ptr1, 2, len1 >> 1); + } + else + { + unsigned int count2; + + /* + S3M stereo samples are not interleaved! + */ + unsigned int off, len; + + /* LEFT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed short tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 2, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed short *)ptr1)[(off + count2) * 2] = tmp[count2]; + } + + len -= r; + off += r; + } + + /* RIGHT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed short tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 2, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed short *)ptr1)[((off + count2) * 2) + 1] = tmp[count2]; + } + + len -= r; + off += r; + } + } + + for (count2 = 0; count2 < len1 / 2; count2++) + { + ((unsigned short *)ptr1)[count2] ^= 32768; + } + } + else + { + if (sptr->mSound->mChannels == 1) + { + result = mFile->read(ptr1, 1, len1); + if (result != FMOD_OK) + { + return result; + } + } + else + { + unsigned int count2; + + /* + S3M stereo samples are not interleaved! + */ + unsigned int off, len; + + /* LEFT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed char tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 1, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed char *)ptr1)[(off + count2) * 2] = tmp[count2]; + } + + len -= r; + off += r; + } + + /* RIGHT */ + off = 0; + len = len1 >> 2; + while (len) + { + unsigned int r; + signed char tmp[512]; + + r = len > 512 ? 512 : len; + + result = mFile->read(tmp, 1, r); + + for (count2 = 0; count2 < r; count2++) + { + ((signed char *)ptr1)[((off + count2) * 2) + 1] = tmp[count2]; + } + + len -= r; + off += r; + } + } + + for (count2 = 0; count2 < len1; count2++) + { + ((unsigned char *)ptr1)[count2] ^= 128; + } + } + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + } + + result = sptr->mSound->unlock(ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + return result; + } + } + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + + waveformat = mWaveFormatMemory; + waveformat[0].lengthbytes = lengthbytes; + + /* + Set up general codec parameters. + */ + if (userexinfo && userexinfo->format != FMOD_SOUND_FORMAT_NONE) + { + waveformat[0].format = userexinfo->format; + } + #ifndef PLATFORM_PS3 + else if (usermode & FMOD_SOFTWARE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + #endif + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + waveformat[0].channels = 2; + FMOD_strncpy(waveformat[0].name, mSongName, FMOD_STRING_MAXNAMELEN); + + result = mSystem->getSoftwareFormat(&waveformat[0].frequency, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, waveformat[0].channels, waveformat[0].format); + + /* + Create a head unit that software channels connect to. + */ + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD S3M Target Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = waveformat[0].channels; + descriptionex.mFormat = waveformat[0].format; + descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + + result = mSystem->createDSP(&descriptionex, &mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + mDSPHead->mDefaultFrequency = (float)waveformat[0].frequency; + } + + /* + Create a pool of virtual channels. + */ + { + mNumVirtualChannels = mNumChannels; + mVirtualChannel = (MusicVirtualChannel *)FMOD_Memory_Calloc(sizeof(MusicVirtualChannel) * mNumVirtualChannels); + if (!mVirtualChannel) + { + return FMOD_ERR_MEMORY; + } + for (count = 0; count < mNumVirtualChannels; count++) + { + new (&mVirtualChannel[count]) MusicVirtualChannel; + } + } + + /* + Create a pool of real channels. 2x the number of virtual channels for double buffer channel usage. + */ + { + int numrealchannels = mNumVirtualChannels * 2; + + mChannelPool = FMOD_Object_Calloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, 0, numrealchannels); + if (result != FMOD_OK) + { + return result; + } + + mChannelSoftware = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * numrealchannels); + if (!mChannelSoftware) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < numrealchannels; count++) + { + new (&mChannelSoftware[count]) ChannelSoftware; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannelSoftware[count], mDSPHead)); + CHECK_RESULT(mChannelSoftware[count].allowReverb(false)); + } + } + + if ((usermode & FMOD_ACCURATETIME) || (usermode & FMOD_CREATESAMPLE)) + { + mVisited = (bool *)FMOD_Memory_Calloc(sizeof(bool) * mNumOrders * FMUSIC_MAXROWS); + if (!mVisited) + { + return FMOD_ERR_MEMORY; + } + + calculateLength(); + } + else + { + mVisited = 0; + waveformat[0].lengthpcm = (unsigned int)-1; /* Thanks to those stupid comment markers! */ + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + play(true); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::closeInternal() +{ + int count; + + stop(); + + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + + if (mDSPHead) + { + mDSPHead->release(); + mDSPHead = 0; + } + + for (count = 0; count < mNumSamples; count++) + { + if (mSample[count].mSound) + { + mSample[count].mSound->release(); + mSample[count].mSound = 0; + } + } + + if (mVirtualChannel) + { + FMOD_Memory_Free(mVirtualChannel); + mVirtualChannel = 0; + } + + if (mChannelSoftware) + { + FMOD_Memory_Free(mChannelSoftware); + mChannelSoftware = 0; + } + + if (mPattern) + { + for (count = 0; count < mNumPatternsMem; count++) + { + if (mPattern[count].mData) + { + FMOD_Memory_Free(mPattern[count].mData); + mPattern[count].mData = 0; + } + } + + FMOD_Memory_Free(mPattern); + mPattern = 0; + } + + for (count = 0; count < mNumChannels; count++) + { + if (mMusicChannel[count]) + { + FMOD_Memory_Free(mMusicChannel[count]); + mMusicChannel[count] = 0; + } + } + + if (mVisited) + { + FMOD_Memory_Free(mVisited); + mVisited = 0; + } + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int numsamples; + int numchannels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + numchannels = waveformat[0].channels; + + SoundI::getSamplesFromBytes(sizebytes, &numsamples, numchannels, waveformat[0].format); + + if (mPlaying && mMasterSpeed) + { + unsigned int mixedsofar = 0; + unsigned int mixedleft = mMixerSamplesLeft; + unsigned int samplestomix; + char *destptr; + + /* + Keep resetting the mix pointer to the beginning of this portion of the ring buffer + */ + destptr = (char *)buffer; + + while (mixedsofar < numsamples) + { + unsigned int read, bytes; + + if (!mixedleft) + { + result = update(true); + if (result != FMOD_OK) + { + return result; + } + + samplestomix = mMixerSamplesPerTick; + mixedleft = samplestomix; + } + else + { + samplestomix = mixedleft; + } + + if (mixedsofar + samplestomix > numsamples) + { + samplestomix = numsamples - mixedsofar; + } + + read = samplestomix; + + criticalsection.enter(); + if (buffer) + { + result = mDSPHead->read(destptr, &read, FMOD_SPEAKERMODE_STEREO_LINEAR, 2, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + mDSPTick++; + } + + SoundI::getBytesFromSamples(read, &bytes, numchannels, waveformat[0].format); + + /* + Buffer returned from the DSP head execute may not be the one we sent in (it may be + one of the global buffers). Don't leave mDSPCrit until after we have copied data out + */ + criticalsection.leave(); + + mixedsofar += read; + destptr += bytes; + mixedleft -= read; + } + + mMixerSamplesLeft = mixedleft; + } + + if (bytesread) + { + *bytesread = sizebytes; + } + + return result; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecS3M::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + if (postype == FMOD_TIMEUNIT_MODORDER) + { + play(); + mNextOrder = mOrder = position; + + return FMOD_OK; + } + else if (postype == FMOD_TIMEUNIT_PCM) + { + bool restarted = false; + + if (position == mPCMOffset) + { + return FMOD_OK; + } + + /* + Want to seek backwards, start from the start. + */ + if (position < mPCMOffset) + { + play(); + restarted = true; + } + + while (mPCMOffset < position) + { + update(true); + } + + if (restarted) + { + bool oldplaying = mPlaying; + bool oldfinished = mFinished; + + stop(); + + mPlaying = oldplaying; + mFinished = oldfinished; + } + + return FMOD_OK; + } + + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecS3M::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecS3M *cs3m = (CodecS3M *)codec; + + return cs3m->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecS3M::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecS3M *cs3m = (CodecS3M *)codec; + + return cs3m->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecS3M::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecS3M *cs3m = (CodecS3M *)codec; + + return cs3m->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecS3M::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype ) +{ + CodecS3M *cs3m = (CodecS3M *)codec; + + return cs3m->setPositionInternal(subsound, position, postype); +} + +} + +#endif + + diff --git a/src/fmod_codec_s3m.h b/src/fmod_codec_s3m.h new file mode 100755 index 0000000..182394a --- /dev/null +++ b/src/fmod_codec_s3m.h @@ -0,0 +1,111 @@ +#ifndef _FMOD_CODEC_S3M_H +#define _FMOD_CODEC_S3M_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_S3M + +#include "fmod_music.h" + +namespace FMOD +{ + class DSPI; + class ChannelPool; + + enum FMUSIC_S3MCOMMANDS + { + FMUSIC_S3M_SETSPEED = 1, + FMUSIC_S3M_PATTERNJUMP, + FMUSIC_S3M_PATTERNBREAK, + FMUSIC_S3M_VOLUMESLIDE, + FMUSIC_S3M_PORTADOWN, + FMUSIC_S3M_PORTAUP, + FMUSIC_S3M_PORTATO, + FMUSIC_S3M_VIBRATO, + FMUSIC_S3M_TREMOR, + FMUSIC_S3M_ARPEGGIO, + FMUSIC_S3M_VIBRATOVOLSLIDE, + FMUSIC_S3M_PORTATOVOLSLIDE, + FMUSIC_S3M_M, + FMUSIC_S3M_N, + FMUSIC_S3M_SETSAMPLEOFFSET, + FMUSIC_S3M_P, + FMUSIC_S3M_RETRIGVOLSLIDE, + FMUSIC_S3M_TREMOLO, + FMUSIC_S3M_SPECIAL, + FMUSIC_S3M_SETTEMPO, + FMUSIC_S3M_FINEVIBRATO, + FMUSIC_S3M_GLOBALVOLUME, + FMUSIC_S3M_W, + FMUSIC_S3M_SETPAN, + FMUSIC_S3M_Y, + FMUSIC_S3M_Z + }; + + enum FMUSIC_S3MCOMMANDSSPECIAL + { + FMUSIC_S3M_SETFILTER, + FMUSIC_S3M_SETGLISSANDO, + FMUSIC_S3M_SETFINETUNE, + FMUSIC_S3M_SETVIBRATOWAVE, + FMUSIC_S3M_SETTREMOLOWAVE, + FMUSIC_S3M_S5, + FMUSIC_S3M_S6, + FMUSIC_S3M_S7, + FMUSIC_S3M_SETPANPOSITION16, + FMUSIC_S3M_S9, + FMUSIC_S3M_STEREOCONTROL, + FMUSIC_S3M_PATTERNLOOP, + FMUSIC_S3M_NOTECUT, + FMUSIC_S3M_NOTEDELAY, + FMUSIC_S3M_PATTERNDELAY, + FMUSIC_S3M_FUNKREPEAT + }; + + const int S3M_MAXROWS = 64; + const int S3M_MAXSAMPLES = 99; + + class MusicChannelS3M : public MusicChannel + { + public: + FMOD_RESULT volumeSlide(); + FMOD_RESULT portamento(); + FMOD_RESULT vibrato(); + FMOD_RESULT tremolo(); + FMOD_RESULT fineVibrato(); + }; + + class CodecS3M : public MusicSong + { + private: + + MusicSample mSample[S3M_MAXSAMPLES]; + + FMOD_RESULT calculateLength(); + + FMOD_RESULT updateNote(bool audible); + FMOD_RESULT updateEffects(); + FMOD_RESULT update(bool audible); + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + + }; +} + +#endif /* FMOD_SUPPORT_S3M */ + +#endif + + diff --git a/src/fmod_codec_sf2.cpp b/src/fmod_codec_sf2.cpp new file mode 100755 index 0000000..5951eab --- /dev/null +++ b/src/fmod_codec_sf2.cpp @@ -0,0 +1,410 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SF2 + +#include "fmod.h" +#include "fmod_codec_sf2.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#include <stdio.h> + +namespace FMOD +{ + +FMOD_CODEC_DESCRIPTION_EX sf2codec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandatory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecSF2::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecSF2::getDescriptionEx() +{ + FMOD_memset(&sf2codec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + sf2codec.name = "FMOD SoundFont 2.0 Codec"; + sf2codec.version = 0x00010100; + sf2codec.timeunits = FMOD_TIMEUNIT_PCM; + sf2codec.open = &CodecSF2::openCallback; + sf2codec.close = &CodecSF2::closeCallback; + sf2codec.read = &CodecSF2::readCallback; + sf2codec.setposition = &CodecSF2::setPositionCallback; + sf2codec.soundcreate = &CodecSF2::soundcreateCallback; + + sf2codec.mType = FMOD_SOUND_TYPE_SF2; + sf2codec.mSize = sizeof(CodecSF2); + + return &sf2codec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecSF2::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + + init(FMOD_SOUND_TYPE_SF2); + + FLOG((LOG_NORMAL, __FILE__, __LINE__, "CodecSF2::openInternal", "attempting to open as SF2..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + + mSrcDataOffset = 0; + +// if (mHeader.numsamples < 1) + { + return FMOD_ERR_FORMAT; + } + +// mShdr = (FMOD_SF2_SAMPLE_HEADER **)FMOD_Memory_Calloc(sizeof(void *) * mHeader.numsamples); + // if (!mShdr) + { + return FMOD_ERR_MEMORY; + } + +// numsubsounds = mHeader.numsamples; + waveformat = mWaveFormat; + + /* + Get size of file in bytes + */ + result = mFile->getSize(&mSrcDataSize); + if (result != FMOD_OK) + { + return result; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecSF2::closeInternal() +{ + if (waveformat) + { + FMOD_Memory_Free(waveformat); + waveformat = 0; + } + + if (mDataOffset) + { + FMOD_Memory_Free(mDataOffset); + mDataOffset = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecSF2::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + + result = mFile->read(buffer, 1, sizebytes, bytesread); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + { + unsigned int count; + signed short *wptr = (signed short *)buffer; + + for (count=0; count < *bytesread >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + + } + #endif + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecSF2::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + + if (subsound < 0 || (numsubsounds && subsound >= numsubsounds)) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFile->mSeekable) + { + unsigned int posbytes; + + if (subsound != mCurrentIndex) + { + mCurrentIndex = subsound; + } + + result = SoundI::getBytesFromSamples(position, &posbytes, waveformat[mCurrentIndex].channels, waveformat[mCurrentIndex].format); + if (result != FMOD_OK) + { + return result; + } + + posbytes += mDataOffset[mCurrentIndex]; + + result = mFile->seek(posbytes, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecSF2::soundcreateInternal(int subsound, FMOD_SOUND *sound) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecSF2::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecSF2 *sf2 = (CodecSF2 *)codec; + + return sf2->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecSF2::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecSF2 *sf2 = (CodecSF2 *)codec; + + return sf2->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecSF2::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecSF2 *sf2 = (CodecSF2 *)codec; + + return sf2->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecSF2::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecSF2 *sf2 = (CodecSF2 *)codec; + + return sf2->setPositionInternal(subsound, position, postype); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecSF2::soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + CodecSF2 *sf2 = (CodecSF2 *)codec; + + return sf2->soundcreateInternal(subsound, sound); +} + +} + +#endif + diff --git a/src/fmod_codec_sf2.h b/src/fmod_codec_sf2.h new file mode 100755 index 0000000..6edc569 --- /dev/null +++ b/src/fmod_codec_sf2.h @@ -0,0 +1,40 @@ +#ifndef _FMOD_CODEC_SF2_H +#define _FMOD_CODEC_SF2_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SF2 + +#include "fmod_codeci.h" + +namespace FMOD +{ + class CodecSF2 : public Codec + { + private: + + unsigned int *mDataOffset; /* array of offsets to raw sample data */ + int mCurrentIndex; /* current SF2 index */ + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT soundcreateInternal(int subsound, FMOD_SOUND *sound); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_SF2 */ + +#endif + diff --git a/src/fmod_codec_swvag.cpp b/src/fmod_codec_swvag.cpp new file mode 100755 index 0000000..da0db41 --- /dev/null +++ b/src/fmod_codec_swvag.cpp @@ -0,0 +1,455 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_VAG + +#include "fmod.h" +#include "fmod_codec_swvag.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#include <stdio.h> + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX vagcodec; + +#if defined(PLUGIN_EXPORTS) && !defined(PLUGIN_FSB) + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescriptionEx is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecVAG::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecVAG::getDescriptionEx() +{ + FMOD_memset(&vagcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + vagcodec.name = "FMOD VAG Codec"; + vagcodec.version = 0x00010100; + vagcodec.timeunits = FMOD_TIMEUNIT_PCM; + vagcodec.open = &CodecVAG::openCallback; + vagcodec.close = &CodecVAG::closeCallback; + vagcodec.read = &CodecVAG::readCallback; + vagcodec.setposition = &CodecVAG::setPositionCallback; + + vagcodec.mType = FMOD_SOUND_TYPE_VAG; + vagcodec.mSize = sizeof(CodecVAG); + + return &vagcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecVAG::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + FMOD_VAG_HDR hdr; + + init(FMOD_SOUND_TYPE_VAG); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecVAG::openInternal", "attempting to open as VAG..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&hdr, 1, sizeof(FMOD_VAG_HDR)); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp((char *)hdr.format, "VAG", 3)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecVAG::openInternal", "'VAG' ID check failed [%c%c%c]\n", hdr.format[0], hdr.format[1], hdr.format[2])); + + return FMOD_ERR_FORMAT; + } + +#ifdef PLATFORM_ENDIAN_LITTLE + hdr.fs = FMOD_SWAPENDIAN_DWORD(hdr.fs); + hdr.size = FMOD_SWAPENDIAN_DWORD(hdr.size); +#endif + + mSrcDataOffset = sizeof(FMOD_VAG_HDR); + + waveformat = &mWaveFormat; + + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + waveformat[0].channels = 1; + waveformat[0].frequency = hdr.fs; + waveformat[0].lengthbytes= hdr.size; + waveformat[0].lengthpcm = waveformat[0].lengthbytes * 28 / 16; + + mPCMBufferLength = 28; + mPCMBufferLengthBytes = 56; + + #ifdef PLATFORM_PS3 + mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mPCMBlock, 16); + #else + mPCMBuffer = (unsigned char *)mPCMBlock; + #endif + + numsubsounds = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecVAG::openInternal", "successfully opened vag file..\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecVAG::closeInternal() +{ + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecVAG::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result; + char src[16]; + unsigned char *dest = (unsigned char *)buffer; + int channel; + + static float f[5][2] = + { + { 0.0f, 0.0f }, + { 60.0f / 64.0f, 0.0f }, + { 115.0f / 64.0f, -52.0f / 64.0f }, + { 98.0f / 64.0f, -55.0f / 64.0f }, + { 122.0f / 64.0f, -60.0f / 64.0f } + }; + + channel = 0; + while (sizebytes) + { + unsigned char *p; + int predict_nr, shift_factor, flags; + int i; + int d, s; + float samples[28]; + + result = mFile->read(src, 16, 1, 0); + if (result != FMOD_OK) + { + return result; + } + + p=(unsigned char *)src; + + predict_nr = *p++; + shift_factor = predict_nr & 0xf; + predict_nr >>= 4; + + flags = *p++; + +#if 0 + /* + Doesn't make sense to break out because certain things are set. + */ + if ( flags == (FMOD_VAG_LOOP | FMOD_VAG_START | FMOD_VAG_END) ) + { + break; + } + if (flags == (FMOD_VAG_LOOP | FMOD_VAG_END)) + { + break; + } + if (flags == FMOD_VAG_START) + { + break; + } + if (flags == (FMOD_VAG_START | FMOD_VAG_LOOP)) + { + break; + } +#endif + + + for ( i = 0; i < 28; i += 2 ) + { + d = *p++; + s = ( d & 0xf ) << 12; + if ( s & 0x8000 ) + { + s |= 0xffff0000; + } + samples[i] = (float) ( s >> shift_factor); + s = ( d & 0xf0 ) << 8; + if ( s & 0x8000 ) + { + s |= 0xffff0000; + } + samples[i+1] = (float) ( s >> shift_factor); + } + + unsigned char *destptr = dest + (channel * sizeof(signed short)); + + for ( i = 0; i < 28; i++ ) + { + samples[i] = samples[i] + mContext[channel].mS1 * f[predict_nr][0] + mContext[channel].mS2 * f[predict_nr][1]; + mContext[channel].mS2 = mContext[channel].mS1; + mContext[channel].mS1 = samples[i]; + d = (int) ( samples[i] + 0.5f ); + destptr[0]=(d & 0xff); + destptr[1]=(d >> 8); + destptr += waveformat->channels * sizeof(signed short); + } + + sizebytes -= 56; + *bytesread += 56; + channel ++; + if (channel >= waveformat->channels) + { + channel = 0; + dest += 28 * sizeof(short) * waveformat->channels; + } + } + + #ifdef PLATFORM_ENDIAN_BIG + + signed short *wptr = (signed short *)buffer; + + int count = *bytesread / 2; + + for (int i = 0; i < count; i++) + { + wptr[i] = FMOD_SWAPENDIAN_WORD(wptr[i]); + } + + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecVAG::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + int bits, count; + unsigned int bytes; + + result = SoundI::getBitsFromFormat(waveformat[0].format, &bits); + if (result != FMOD_OK) + { + return result; + } + + SoundI::getBytesFromSamples(position, &bytes, waveformat[0].channels, FMOD_SOUND_FORMAT_VAG); + + result = mFile->seek(mSrcDataOffset + bytes, SEEK_SET); + + for (count = 0; count < 16; count++) + { + mContext[count].mS1 = mContext[count].mS2 = 0; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecVAG::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecVAG *vag = (CodecVAG *)codec; + + return vag->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecVAG::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecVAG *vag = (CodecVAG *)codec; + + return vag->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecVAG::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecVAG *vag = (CodecVAG *)codec; + + return vag->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecVAG::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecVAG *vag = (CodecVAG *)codec; + + return vag->setPositionInternal(subsound, position, postype); +} + +} + +#endif + + diff --git a/src/fmod_codec_swvag.h b/src/fmod_codec_swvag.h new file mode 100755 index 0000000..c74de19 --- /dev/null +++ b/src/fmod_codec_swvag.h @@ -0,0 +1,90 @@ +#ifndef _FMOD_CODEC_VAG_H +#define _FMOD_CODEC_VAG_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_VAG + +#include "fmod_codeci.h" +#include "fmod_types.h" + +namespace FMOD +{ + + #define FMOD_VAG_START 0x04 + #define FMOD_VAG_LOOP 0x02 + #define FMOD_VAG_END 0x01 + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + typedef struct + { + unsigned char format[4] FMOD_PACKED_INTERNAL; /* always 'VAGp' for identifying*/ + unsigned int ver FMOD_PACKED_INTERNAL; /* format version (2) */ + unsigned int ssa FMOD_PACKED_INTERNAL; /* Source Start Address, always 0 (reserved for VAB format) */ + unsigned int size FMOD_PACKED_INTERNAL; /* Sound Data Size in byte */ + + unsigned int fs FMOD_PACKED_INTERNAL; /* sampling frequency, 44100(>pt1000), 32000(>pt), 22000(>pt0800)... */ + unsigned short volL FMOD_PACKED_INTERNAL; /* base volume for Left channel */ + unsigned short volR FMOD_PACKED_INTERNAL; /* base volume for Right channel */ + unsigned short pitch FMOD_PACKED_INTERNAL; /* base pitch (includes fs modulation)*/ + unsigned short ADSR1 FMOD_PACKED_INTERNAL; /* base ADSR1 (see SPU manual) */ + unsigned short ADSR2 FMOD_PACKED_INTERNAL; /* base ADSR2 (see SPU manual) */ + unsigned short reserved FMOD_PACKED_INTERNAL; /* not in use */ + + char name[16] FMOD_PACKED_INTERNAL; + + } FMOD_PACKED FMOD_VAG_HDR; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + + struct CodecVAG_Context + { + float mS1, mS2; + }; + + class CodecVAG : public Codec + { + friend class CodecFSB; + + private: + + FMOD_CODEC_WAVEFORMAT mWaveFormat; + + #ifdef PLATFORM_PS3 + unsigned char mPCMBlock[56 + 16]; /* +16 for alignment */ + #else + unsigned char mPCMBlock[56]; + #endif + + CodecVAG_Context mContext[16]; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_VAG */ + +#endif + + diff --git a/src/fmod_codec_tag.cpp b/src/fmod_codec_tag.cpp new file mode 100755 index 0000000..1096d84 --- /dev/null +++ b/src/fmod_codec_tag.cpp @@ -0,0 +1,1337 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_TAGS + +#include "fmod.h" +#include "fmod_codec_tag.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_string.h" + +#include <stdio.h> +#include <stdlib.h> + + +namespace FMOD +{ + +FMOD_CODEC_DESCRIPTION_EX tagcodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandatory for every fmod plugin. This is the symbol the register plugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecTag::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +#if defined(FMOD_SUPPORT_DSHOW) || defined(FMOD_SUPPORT_ASF) + +static const FMOD_GUID ASF_Header_Object = { 0x75B22630,0x668E,0x11CF,{0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C}}; +static const FMOD_GUID ASF_Content_Description_Object = { 0x75B22633,0x668E,0x11CF,{0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C}}; +static const FMOD_GUID ASF_Extended_Content_Description_Object = { 0xD2D0A440,0xE307,0x11D2,{0x97,0xF0,0x00,0xA0,0xC9,0x5E,0xA8,0x50}}; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + +typedef struct +{ + FMOD_GUID guid FMOD_PACKED_INTERNAL; + FMOD_UINT64 size FMOD_PACKED_INTERNAL; +} FMOD_PACKED ASF_CHUNK; + +typedef struct +{ + int number FMOD_PACKED_INTERNAL; + char Reserved1 FMOD_PACKED_INTERNAL; + char Reserved2 FMOD_PACKED_INTERNAL; +} FMOD_PACKED ASF_HEADEROBJECT; + +#ifdef FSOUND_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif +#endif + +#endif + + +#define ID3V2_FLAGS_UNSYNCHRONISATION 0x80 +#define ID3V2_FLAGS_EXTHEADER 0x40 +#define ID3V2_FLAGS_EXPERIMENTAL 0x20 +#define ID3V2_FLAGS_FOOTER 0x10 + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecTag::getDescriptionEx() +{ + FMOD_memset(&tagcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + tagcodec.name = "FMOD Tag Reader Codec"; + tagcodec.version = 0x00010100; + tagcodec.timeunits = FMOD_TIMEUNIT_PCM; + tagcodec.open = &CodecTag::openCallback; + tagcodec.close = &CodecTag::closeCallback; + tagcodec.read = &CodecTag::readCallback; + tagcodec.setposition = &CodecTag::setPositionCallback; + + tagcodec.mType = FMOD_SOUND_TYPE_TAG; + tagcodec.mSize = sizeof(CodecTag); + + return &tagcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int startoffset, filepos; + + init(FMOD_SOUND_TYPE_TAG); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecTag::openInternal", "attempting to open ID3 or ASF tags..\n")); + + if (usermode & FMOD_IGNORETAGS) + { + return FMOD_ERR_FORMAT; + } + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = readTags(); + if (result == FMOD_OK) + { + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getStartOffset(&startoffset); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->setStartOffset(startoffset + filepos); + if (result != FMOD_OK) + { + return result; + } + } + + /* + NOTE: If the tag reading failed for some reason then reset the file pointer and start trying to load the file + from the beginning. This way we keep any tags we did successfully read and we might also be able to open + the file if it's something tolerant like an mp3. Other formats (i.e. Ogg) won't open but there's nothing + we can do about that. + */ + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Always return FMOD_ERR_FORMAT so the real file format codecs get called later + */ + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::closeInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::readTags() +{ + FMOD_RESULT result = FMOD_OK; + int wavdatapos = 0; + char header[16]; + unsigned int filepos, itemsread; + + /* + Find tags from the end of the file + */ + wavdatapos = 0; + for (;;) + { + /* + Look for ID3v1 + */ + result = mFile->seek(wavdatapos - 128, SEEK_END); + if (result != FMOD_OK) + { + break; + } + + result = mFile->read(header, 1, 3, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 3) + { + return FMOD_ERR_FILE_BAD; + } + + if (!FMOD_strncmp(header, "TAG", 3)) /* ID3v1 */ + { + result = readID3v1(); + if (result != FMOD_OK) + { + return result; + } + + wavdatapos -= 128; + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + if (filepos <= 128) + { + break; + } + } + else + { + /* + Look for ID3v2 footer + */ + result = mFile->seek(wavdatapos - 10, SEEK_END); + if (result != FMOD_OK) + { + if (result == FMOD_ERR_FILE_COULDNOTSEEK) + { + break; /* possibly netstream, try tags at start of file, we might be able to do it. */ + } + return result; + } + + result = mFile->read(header, 1, 3, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 3) + { + return FMOD_ERR_FILE_BAD; + } + + if (!FMOD_strncmp(header, "3DI", 3)) /* ID3v2 footer */ + { + result = readID3v2FromFooter(); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + wavdatapos = filepos; + } + else + { + break; + } + } + } + + /* + Find tags from the start of the file + */ + wavdatapos = 0; + result = mFile->seek(wavdatapos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + for (;;) + { + result = mFile->read(header, 1, 16, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 16) + { + return FMOD_ERR_FILE_BAD; + } + + if (!FMOD_strncmp(header, "TAG", 3)) /* ID3v1 */ + { + result = mFile->seek(-13, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + result = readID3v1(); + if (result != FMOD_OK) + { + return result; + } + } + else if (!FMOD_strncmp(header, "ID3", 3)) /* ID3v2 */ + { + result = mFile->seek(-13, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + result = readID3v2(); + if (result != FMOD_OK) + { + return result; + } + } +#if defined(FMOD_SUPPORT_DSHOW) || defined(FMOD_SUPPORT_ASF) + else if (!memcmp(header, &ASF_Header_Object, 16)) /* ASF */ + { + FMOD_GUID *guid = (FMOD_GUID *)header; + + result = readASF(); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(wavdatapos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + break; + } +#endif + else + { + result = mFile->seek(wavdatapos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + break; /* No tag found, leave search loop */ + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + wavdatapos = filepos; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::readID3v1() +{ + FMOD_RESULT result; + char value[31], tmp[8]; + unsigned int itemsread; + + FMOD_memset(value, 0, 31); + result = mFile->read(value, 1, 30, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 30) + { + return FMOD_ERR_FILE_BAD; + } + if (FMOD_strlen(value)) + { + metaData(FMOD_TAGTYPE_ID3V1, "TITLE", value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, false); + } + + FMOD_memset(value, 0, 31); + result = mFile->read(value, 1, 30, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 30) + { + return FMOD_ERR_FILE_BAD; + } + if (FMOD_strlen(value)) + { + metaData(FMOD_TAGTYPE_ID3V1, "ARTIST", value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, false); + } + + FMOD_memset(value, 0, 31); + result = mFile->read(value, 1, 30, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 30) + { + return FMOD_ERR_FILE_BAD; + } + if (FMOD_strlen(value)) + { + metaData(FMOD_TAGTYPE_ID3V1, "ALBUM", value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, false); + } + + FMOD_memset(value, 0, 31); + result = mFile->read(value, 1, 4, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 4) + { + return FMOD_ERR_FILE_BAD; + } + if (FMOD_strlen(value)) + { + metaData(FMOD_TAGTYPE_ID3V1, "YEAR", value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, false); + } + + FMOD_memset(value, 0, 31); + result = mFile->read(value, 1, 30, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 30) + { + return FMOD_ERR_FILE_BAD; + } + if (FMOD_strlen(value)) + { + metaData(FMOD_TAGTYPE_ID3V1, "COMMENT", value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, false); + } + + if ((value[28] == 0) && (value[29] != 0)) + { + /* + Must be ID3v1.1 + */ + sprintf(tmp, "%d", (int)(value[29] & 0xff)); + metaData(FMOD_TAGTYPE_ID3V1, "TRACK", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + } + + FMOD_memset(value, 0, 31); + result = mFile->read(value, 1, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + sprintf(tmp, "%d", (int)(value[0] & 0xff)); + metaData(FMOD_TAGTYPE_ID3V1, "GENRE", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::readID3v2() +{ + FMOD_RESULT result; + unsigned char size[4], flags; + unsigned short version; + unsigned int taglen, tagdatalen = 0, tagoff, filepos, itemsread; + int wavdatapos; + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + wavdatapos = filepos; + + result = mFile->read(&version, 1, 2, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 2) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(&flags, 1, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(size, 1, 4, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 4) + { + return FMOD_ERR_FILE_BAD; + } + + taglen = ((unsigned int)size[0] << 21) + ((unsigned int)size[1] << 14) + ((unsigned int)size[2] << 7) + (unsigned int)size[3]; + + if (flags & ID3V2_FLAGS_FOOTER) + { + taglen += 10; + } + + wavdatapos += ((taglen + 10) - 3); /* + 10 for header, - 3 for 'ID3' */ + tagoff = 10; + + do + { + unsigned char tagdataid[5]; + unsigned short tagdataflags; + signed char ascii; + + tagdataid[0] = tagdataid[1] = tagdataid[2] = tagdataid[3] = tagdataid[4] = 0; + + /* + ID3v2.2.x and lower have a different frame header than ID3v2.3.x and above + */ + if (version <= 2) + { + result = mFile->read(tagdataid, 3, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(size, 3, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + tagdatalen = ((unsigned int)size[0] << 16) | ((unsigned int)size[1] << 8) | (unsigned int)size[2]; + } + else if (version >= 3) + { + result = mFile->read(tagdataid, 4, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(size, 4, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(&tagdataflags, 2, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + tagdatalen = ((unsigned int)size[0] << 24) + ((unsigned int)size[1] << 16) + ((unsigned int)size[2] << 8) + (unsigned int)size[3]; + } + + #define ISASCII(_x) (((_x) >= 32 && (_x) < 128) || ((_x) == 0)) + + ascii = (ISASCII(tagdataid[0]) && ISASCII(tagdataid[1]) && ISASCII(tagdataid[2]) && ISASCII(tagdataid[3])); + + if (ascii && tagdatalen > 0 && tagdatalen < (1*1024*1024)) /* dont support erroneous or huge tag lengths */ + { + FMOD_TAGDATATYPE datatype = FMOD_TAGDATATYPE_BINARY; + char *tagdata; + int taglen = tagdatalen; + + tagdata = (char *)FMOD_Memory_Alloc(tagdatalen); + if (!tagdata) + { + result = mFile->seek(wavdatapos, SEEK_SET); + return FMOD_ERR_MEMORY; + } + result = mFile->read(tagdata, 1, tagdatalen, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != tagdatalen) + { + FMOD_Memory_Free(tagdata); + return result; + } + + /* + Parse some common tag types a little to make it easier on the user + */ + if (tagdataid[0] == 'T') + { + switch (tagdata[0]) + { + case 0x00 : + datatype = FMOD_TAGDATATYPE_STRING; + break; + case 0x01 : + datatype = FMOD_TAGDATATYPE_STRING_UTF16; + break; + case 0x02 : + datatype = FMOD_TAGDATATYPE_STRING_UTF16BE; + break; + case 0x03 : + datatype = FMOD_TAGDATATYPE_STRING_UTF8; + break; + default : + break; + } + + FMOD_memcpy(tagdata, tagdata + 1, tagdatalen - 1); + tagdata[tagdatalen - 1] = 0; + taglen--; + } + else if (tagdataid[0] == 'W') + { + //AJS url tag - but it's enc, desc, text... + } + + metaData(FMOD_TAGTYPE_ID3V2, (char *)tagdataid, tagdata, taglen, datatype, false); + + FMOD_Memory_Free(tagdata); + } + + tagoff += (tagdatalen + 10); + + } while (tagoff < taglen); + + result = mFile->seek(wavdatapos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::readID3v2FromFooter() +{ + FMOD_RESULT result; + char size[4]; + unsigned short version; + unsigned char flags; + int taglen, pos; + unsigned int itemsread, filepos; + + result = mFile->read(&version, 1, 2, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 2) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(&flags, 1, 1, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 1) + { + return FMOD_ERR_FILE_BAD; + } + + result = mFile->read(size, 1, 4, &itemsread); + if (result != FMOD_OK) + { + return result; + } + if (itemsread != 4) + { + return FMOD_ERR_FILE_BAD; + } + + taglen = ((int )size[0] << 21) + ((int )size[1] << 14) + ((int )size[2] << 7) + size[3]; + + if (flags & ID3V2_FLAGS_FOOTER) + { + taglen += 10; + } + + result = mFile->seek(-taglen + 3, SEEK_CUR); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + pos = filepos - 3; + + result = readID3v2(); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(pos, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_ASF +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTag::readASF() +{ + FMOD_RESULT result; + FMOD_UINT64 offset = 0; + FMOD_UINT64 size = -1; + ASF_CHUNK chunk; + unsigned int itemsread, filepos; + + /* + Read header + */ + result = mFile->seek(0, SEEK_END); + if (result != FMOD_OK) + { + return result; + } + result = mFile->tell(&filepos); + if (result != FMOD_OK) + { + return result; + } + size = filepos; + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Decode chunks + */ + do + { + result = mFile->seek((int)offset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&chunk, 1, sizeof(ASF_CHUNK), &itemsread); + if ((result != FMOD_OK) || (itemsread != sizeof(ASF_CHUNK))) + { + return result; + } + + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_QWORD(chunk.size); + #endif + + /* + DATA CHUNK + */ + if (!memcmp(&chunk.guid, &ASF_Header_Object, sizeof(FMOD_GUID))) + { + ASF_CHUNK subchunk; + ASF_HEADEROBJECT headerobject; + int count; + FMOD_UINT64 suboffset; + + result = mFile->read(&headerobject, 1, sizeof(headerobject), &itemsread); + if ((result != FMOD_OK) || (itemsread != sizeof(headerobject))) + { + return result; + } + + suboffset = sizeof(ASF_CHUNK) + sizeof(headerobject); + + for (count=0; count < headerobject.number; count++) + { + result = mFile->seek((int)(offset + suboffset), SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&subchunk, 1, sizeof(ASF_CHUNK), &itemsread); + if ((result != FMOD_OK) || (itemsread != sizeof(ASF_CHUNK))) + { + return result; + } + + if (!memcmp(&subchunk.guid, &ASF_Content_Description_Object, sizeof(FMOD_GUID))) + { + static char tmp[4098]; + static char tmp2[4098]; + int title_length = 0; + int author_length = 0; + int copyright_length = 0; + int description_length = 0; + int rating_length = 0; + + result = mFile->read(&title_length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + result = mFile->read(&author_length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + result = mFile->read(©right_length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + result = mFile->read(&description_length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + result = mFile->read(&rating_length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + + if (title_length) + { + result = mFile->read(tmp, 1, title_length, &itemsread); + if ((result != FMOD_OK) || (itemsread != (unsigned int)title_length)) + { + return result; + } + + if (wcstombs(tmp2, (const wchar_t *)tmp, 4096) == ((title_length / 2) - 1)) + { + result = metaData(FMOD_TAGTYPE_ASF, "TITLE", tmp2, (title_length / 2) - 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + result = metaData(FMOD_TAGTYPE_ASF, "TITLE", tmp, title_length - 2, FMOD_TAGDATATYPE_STRING_UTF16, false); + } + if (result != FMOD_OK) + { + return result; + } + } + + if (author_length) + { + result = mFile->read(tmp, 1, author_length, &itemsread); + if ((result != FMOD_OK) || (itemsread != (unsigned int)author_length)) + { + return result; + } + + if (wcstombs(tmp2, (const wchar_t *)tmp, 4096) == ((author_length / 2) - 1)) + { + result = metaData(FMOD_TAGTYPE_ASF, "AUTHOR", tmp2, (author_length / 2) - 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + result = metaData(FMOD_TAGTYPE_ASF, "AUTHOR", tmp, author_length - 2, FMOD_TAGDATATYPE_STRING_UTF16, false); + } + if (result != FMOD_OK) + { + return result; + } + } + + if (copyright_length) + { + result = mFile->read(tmp, 1, copyright_length, &itemsread); + if ((result != FMOD_OK) || (itemsread != (unsigned int)copyright_length)) + { + return result; + } + + if (wcstombs(tmp2, (const wchar_t *)tmp, 4096) == ((copyright_length / 2) - 1)) + { + result = metaData(FMOD_TAGTYPE_ASF, "COPYRIGHT", tmp2, (copyright_length / 2) - 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + result = metaData(FMOD_TAGTYPE_ASF, "COPYRIGHT", tmp, copyright_length - 2, FMOD_TAGDATATYPE_STRING_UTF16, false); + } + if (result != FMOD_OK) + { + return result; + } + } + + if (description_length) + { + result = mFile->read(tmp, 1, description_length, &itemsread); + if ((result != FMOD_OK) || (itemsread != (unsigned int)description_length)) + { + return result; + } + + if (wcstombs(tmp2, (const wchar_t *)tmp, 4096) == ((description_length / 2) - 1)) + { + result = metaData(FMOD_TAGTYPE_ASF, "DESCRIPTION", tmp2, (description_length / 2) - 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + result = metaData(FMOD_TAGTYPE_ASF, "DESCRIPTION", tmp, description_length - 2, FMOD_TAGDATATYPE_STRING_UTF16, false); + } + if (result != FMOD_OK) + { + return result; + } + } + + if (rating_length) + { + result = mFile->read(tmp, 1, rating_length, &itemsread); + if ((result != FMOD_OK) || (itemsread != (unsigned int)rating_length)) + { + return result; + } + + if (wcstombs(tmp2, (const wchar_t *)tmp, 4096) == ((rating_length / 2) - 1)) + { + result = metaData(FMOD_TAGTYPE_ASF, "RATING", tmp2, (rating_length / 2) - 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + result = metaData(FMOD_TAGTYPE_ASF, "RATING", tmp, rating_length - 2, FMOD_TAGDATATYPE_STRING_UTF16, false); + } + if (result != FMOD_OK) + { + return result; + } + } + } + else if (!memcmp(&subchunk.guid, &ASF_Extended_Content_Description_Object, sizeof(FMOD_GUID))) + { + unsigned short descriptorcount; + int count; + static char tmp[4098]; + static char tmp2[4098]; + + result = mFile->read(&descriptorcount, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + + for (count = 0; count < descriptorcount; count++) + { + unsigned short Descriptor_Name_Length; + unsigned short Descriptor_Value_Data_Type; + unsigned short Descriptor_Value_Length; + char *Descriptor_Value; + + result = mFile->read(&Descriptor_Name_Length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + result = mFile->read(&tmp2, 1, Descriptor_Name_Length, &itemsread); + if ((result != FMOD_OK) || (itemsread != Descriptor_Name_Length)) + { + return result; + } + wcstombs(tmp, (const wchar_t *)tmp2, 4096); + + result = mFile->read(&Descriptor_Value_Data_Type, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + result = mFile->read(&Descriptor_Value_Length, 2, 1, &itemsread); + if ((result != FMOD_OK) || (itemsread != 1)) + { + return result; + } + + Descriptor_Value = (char *)FMOD_Memory_Calloc(Descriptor_Value_Length); + if (!Descriptor_Value) + { + return FMOD_ERR_MEMORY; + } + + result = mFile->read(Descriptor_Value, 1, Descriptor_Value_Length, &itemsread); + if ((result != FMOD_OK) || (itemsread != Descriptor_Value_Length)) + { + return result; + } + + switch (Descriptor_Value_Data_Type) + { + case 0x0000: /* Unicode String */ + { + if (wcstombs(tmp2, (const wchar_t *)Descriptor_Value, 4096) == ((Descriptor_Value_Length / 2) - 1)) + { + result = metaData(FMOD_TAGTYPE_ASF, tmp, tmp2, (Descriptor_Value_Length / 2) - 1, FMOD_TAGDATATYPE_STRING, false); + } + else + { + result = metaData(FMOD_TAGTYPE_ASF, tmp, Descriptor_Value, Descriptor_Value_Length - 2, FMOD_TAGDATATYPE_STRING_UTF16, false); + } + if (result != FMOD_OK) + { + return result; + } + break; + } + case 0x0002: /* Bool */ + { + result = metaData(FMOD_TAGTYPE_ASF, tmp, Descriptor_Value, Descriptor_Value_Length, FMOD_TAGDATATYPE_BINARY, false); + if (result != FMOD_OK) + { + return result; + } + break; + } + case 0x0003: /* DWORD */ + case 0x0004: /* QWORD */ + case 0x0005: /* WORD */ + { + result = metaData(FMOD_TAGTYPE_ASF, tmp, Descriptor_Value, Descriptor_Value_Length, FMOD_TAGDATATYPE_INT, false); + if (result != FMOD_OK) + { + return result; + } + break; + } + default: + { + result = metaData(FMOD_TAGTYPE_ASF, tmp, Descriptor_Value, Descriptor_Value_Length, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + break; + } + } + + FMOD_Memory_Free(Descriptor_Value); + } + + } + + suboffset += subchunk.size; + } + } + + offset += chunk.size; + + } while (offset < size && chunk.size); + + return FMOD_OK; +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTag::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecTag *tag = (CodecTag *)codec; + + return tag->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTag::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecTag *tag = (CodecTag *)codec; + + return tag->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTag::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTag::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTag::soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + return FMOD_OK; +} + +} + +#endif + + diff --git a/src/fmod_codec_tag.h b/src/fmod_codec_tag.h new file mode 100755 index 0000000..4a3f9e0 --- /dev/null +++ b/src/fmod_codec_tag.h @@ -0,0 +1,42 @@ +#ifndef _FMOD_CODEC_TAG_H +#define _FMOD_CODEC_TAG_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_TAGS + +#include "fmod_codeci.h" + +namespace FMOD +{ + const FMOD_SOUND_TYPE FMOD_SOUND_TYPE_TAG = (FMOD_SOUND_TYPE)1000; + + class CodecTag : public Codec + { + private: + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readTags(); + FMOD_RESULT readID3v1(); + FMOD_RESULT readID3v2(); + FMOD_RESULT readID3v2FromFooter(); + FMOD_RESULT readASF(); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK soundcreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_TAGS */ + +#endif + + diff --git a/src/fmod_codec_tremor.cpp b/src/fmod_codec_tremor.cpp new file mode 100755 index 0000000..5d74314 --- /dev/null +++ b/src/fmod_codec_tremor.cpp @@ -0,0 +1,826 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_TREMOR + +#include "fmod.h" +#include "fmod_codec_tremor.h" +#include "fmod_codec_wav.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_metadata.h" +#include "fmod_string.h" + +//#include "../lib/ogg_vorbis/vorbis/lib/window.h" + +#include <string.h> + + +/* + *************************************************************************************************************** + *************************************************************************************************************** + *************************************************************************************************************** + + NOTES ON OPTIMIZATION + --------------------- + + - Throw away floor0 and res0 and res1. Only keep floor1 and res2. + - Special case all the transform stuff to the two blocksizes used (if you use two of-em at all). + - You can also make smaller or algoritmically decodable Huffman tables instead of the big lookups, + if you so choose (you'll have to tune the encoder to generate correct streams for you, in that case, though). + - You can even hardwire the modes into the decoder (no headers needed anymore --> good for small samples, no setup + overhead). + + - Remove floor 0: Floor 0 is not to be considered deprecated, but it is of + limited modern use. No known Vorbis encoder past Xiph.Org's own beta + 4 makes use of floor 0. Floor 1 is also considerably less expensive to + decode than floor 0 [9]. Removing support for floor 0 will preserve + memory. + - Window sizes: Remove all look-up tables for IMDCT support of + window lengths other than 256 and 2048. This is possible since these are + the only windows lengths that is used by most (if not all) encoders. Since + the current Vorbis standard features window lengths of up to 8192 + samples the memory preserve is significant. + - Low accuracy: If Tremor is compiled with _LOW_ACCURACY_ + defined it will run in low accuracy mode, which means that all + calculations during decode is limited to a maximum of 32 bit precision. + Otherwise, 64 bit values are used to store intermediate 32-bit multiply + results. And the look up tables for windows and IMDCT are converted + from 32 bits to 8 bits. Sound quality will be reduced but the processor + load will be reduced. + - Rewrite data structures: To further reduce memory usage, and possibly + gain some speed, data structures can be optimized by determining the + actual needed sizes as well as removing unnecessary or redundant + members. And due to the alignment restrictions of some processor + architectures, struct members can be rearranged to not wasted memory + due to padding. For example. when an odd number of int is followed by a + long. This has also been done in a preciously thesis projects and it gained + some memory [2]. + + + *************************************************************************************************************** + *************************************************************************************************************** + *************************************************************************************************************** +*/ + + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX tremorcodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecTremor::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +size_t FMOD_Tremor_ReadCallback(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + FMOD_RESULT result; + unsigned int rd; + File *fp; + + fp = (File *)datasource; + + result = fp->read(ptr, (unsigned int)size, (unsigned int)nmemb, &rd); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return (size_t)-1; + } + + return rd; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +int FMOD_Tremor_SeekCallback(void *datasource, ogg_int64_t offset, int whence) +{ + File *fp = (File *)datasource; + + if (fp->mFlags & FMOD_FILE_SEEKABLE) + { + return fp->seek((int)offset, (signed char)whence); + } + else + { + return -1; + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +int FMOD_Tremor_TellCallback(void *datasource) +{ + File *fp = (File *)datasource; + unsigned int pos; + + fp->tell(&pos); + + return (ogg_int32_t)pos; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void * FMOD_Tremor_Malloc(int size) +{ + return FMOD_Memory_Alloc(size); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void * FMOD_Tremor_Calloc(int count, int size) +{ + return FMOD_Memory_Calloc(count * size); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void * FMOD_Tremor_ReAlloc(void *ptr, int size) +{ + return FMOD_Memory_ReAlloc(ptr, size); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void FMOD_Tremor_Free(void *ptr) +{ + FMOD_Memory_Free(ptr); +} + +extern "C" +{ + void * (*_ogg_malloc) (int size) = FMOD_Tremor_Malloc; + void * (*_ogg_calloc) (int count, int size) = FMOD_Tremor_Calloc; + void * (*_ogg_realloc) (void *ptr, int size) = FMOD_Tremor_ReAlloc; + void (*_ogg_free) (void *ptr) = FMOD_Tremor_Free; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecTremor::getDescriptionEx() +{ + memset(&tremorcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + tremorcodec.name = "FMOD Tremor Codec"; + tremorcodec.version = 0x00010100; + tremorcodec.timeunits = FMOD_TIMEUNIT_PCM; + tremorcodec.open = &CodecTremor::openCallback; + tremorcodec.close = &CodecTremor::closeCallback; + tremorcodec.read = &CodecTremor::readCallback; + tremorcodec.setposition = &CodecTremor::setPositionCallback; + + tremorcodec.mType = FMOD_SOUND_TYPE_OGGVORBIS; + tremorcodec.mSize = sizeof(CodecTremor); + + return &tremorcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTremor::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + vorbis_info *vi; + char str[4]; + unsigned int lengthbytes = 0; + int oggresult; + ov_callbacks callbacks = + { + FMOD_Tremor_ReadCallback, /* size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); */ + FMOD_Tremor_SeekCallback, /* int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); */ + 0, /* int (*close_func) (void *datasource); */ + FMOD_Tremor_TellCallback, /* int (*tell_func) (void *datasource); */ + }; + bool manualsizecalc = false; + + init(FMOD_SOUND_TYPE_OGGVORBIS); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecTremor::openInternal", "attempting to open as OGG..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + /* + Support RIFF wrapped MP3? + */ + { + CodecWav tempwav; + WAVE_CHUNK chunk; + FMOD_CODEC_WAVEFORMAT tempwaveformat; + + memset(&tempwav, 0, sizeof(CodecWav)); + memset(&tempwaveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + + tempwav.mFile = mFile; + tempwav.mSrcDataOffset = (unsigned int)-1; + tempwav.waveformat = &tempwaveformat; + + /* + Read header + */ + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp((const char *)chunk.id, "RIFF", 4)) + { + char wave[4]; + + result = mFile->read(wave, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_strncmp(wave, "WAVE", 4)) + { + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + result = tempwav.parseChunk(chunk.size); + if (result == FMOD_OK && tempwav.mSrcFormat && tempwav.mSrcDataOffset == (unsigned int)-1) + { + int format = tempwav.mSrcFormat->Format.wFormatTag; + + if (format == WAVE_FORMAT_OGGVORBIS) + { + mSrcDataOffset = tempwav.mSrcDataOffset; + lengthbytes = tempwav.waveformat[0].lengthbytes; + mLoopPoints[0] = tempwav.mLoopPoints[0]; + mLoopPoints[1] = tempwav.mLoopPoints[1]; + mSyncPoint = tempwav.mSyncPoint; + mNumSyncPoints = tempwav.mNumSyncPoints; + } + else + { + result = FMOD_ERR_FORMAT; + } + + if (result != FMOD_OK) + { + if (tempwav.mSrcFormat) + { + FMOD_Memory_Free(tempwav.mSrcFormat); + tempwav.mSrcFormat = 0; + } + if (tempwav.mSyncPoint && mSyncPoint != tempwav.mSyncPoint) + { + FMOD_Memory_Free(tempwav.mSyncPoint); + tempwav.mSyncPoint = 0; + } + return result; + } + } + + if (tempwav.mSrcFormat) + { + FMOD_Memory_Free(tempwav.mSrcFormat); + tempwav.mSrcFormat = 0; + } + if (tempwav.mSyncPoint && mSyncPoint != tempwav.mSyncPoint) + { + FMOD_Memory_Free(tempwav.mSyncPoint); + tempwav.mSyncPoint = 0; + } + } + } + + result = mFile->seek(mSrcDataOffset, SEEK_SET); + } + + result = mFile->read(str, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp(str, "OggS", 4)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecTremor::openInternal", "failed to open as ogg\n")); + + return FMOD_ERR_FORMAT; + } + + /* + Get size of ogg file in bytes + */ + /* + If there wasnt a riff size chunk + */ + if (!lengthbytes) + { + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + manualsizecalc = true; + } + + result = mFile->seek(mSrcDataOffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + oggresult = ov_open_callbacks(mFile, &mVf, 0, 0, callbacks); + if (oggresult < 0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecTremor::openInternal", "failed to open as ogg\n")); + + if (oggresult == -139) //OV_EMEMORY) + { + return FMOD_ERR_MEMORY; + } + return FMOD_ERR_FORMAT; + } + + vi = ov_info(&mVf,-1); + + result = readVorbisComments(); + if (result != FMOD_OK) + { + return result; + } + + waveformat = &mWaveFormat; + waveformat[0].lengthbytes = lengthbytes; + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + waveformat[0].channels = vi->channels; + waveformat[0].frequency = vi->rate; + waveformat[0].blockalign = waveformat[0].channels * 2; /* THIS SHOULD BE EQUIVALENT TO THE OGG DECODE BUFFER SIZE? */ + + if (manualsizecalc && waveformat[0].lengthbytes != (unsigned int)-1) + { + waveformat[0].lengthbytes -= mSrcDataOffset; + } + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + int count, streams = ov_streams(&mVf); + + waveformat[0].lengthpcm = 0; + + for (count = 0; count < streams; count++) + { + waveformat[0].lengthpcm += (int)ov_pcm_total(&mVf, count); + } + + if (waveformat[0].lengthpcm <= 0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecTremor::openInternal", "failed to open as ogg\n")); + + waveformat[0].lengthpcm = 0; + + return FMOD_ERR_FORMAT; + } + } + else + { + waveformat[0].lengthpcm = 0x7fffffff; + } + + if (!mSrcDataOffset) + { + mSrcDataOffset = (unsigned int)ov_raw_tell(&mVf); + } + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTremor::closeInternal() +{ + mVf.datasource = 0; /* this stops vorbis from trying to close our file */ + + ov_clear(&mVf); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTremor::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + int bigendian; + vorbis_comment *vc; + char *name, *value; + int i; + +#ifdef PLATFORM_ENDIAN_BIG + bigendian = 1; +#else + bigendian = 0; +#endif + + *bytesread = ov_read(&mVf, buffer, sizebytes, 0); + if (!*bytesread) + { + return FMOD_ERR_FILE_EOF; + } + + vc = ov_comment(&mVf, -1); + + if (vc && vc->comments) + { + for (i=0;i < vc->comments;i++) + { + name = vc->user_comments[i]; + value = name; + for (;*value && (*value != '=');value++) ; + + if (*value == '=') + { + *value++ = 0; + } + else + { + value = name; + name = "NONAME"; + } + + metadata((FMOD_CODEC_STATE *)this, FMOD_TAGTYPE_VORBISCOMMENT, name, value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, true); + } + +#ifndef FMOD_MODIFIED + vorbis_comment_clear(vc); +#endif + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTremor::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + + if (ov_pcm_seek(&mVf, position) < 0) + { + return FMOD_ERR_MEMORY; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecTremor::readVorbisComments() +{ + FMOD_RESULT result; + char *p; + int count, i; + vorbis_comment *vc; + + vc = ov_comment(&mVf, -1); + if (!vc) + { + return FMOD_OK; + } + + for (count = 0; count < vc->comments; count++) + { + if (vc->comment_lengths[count]) + { + p = vc->user_comments[count]; + for (i=0;*p && (*p != '=');i++, p++) ; + if (*p == '=') + { + *p++ = 0; + result = metaData(FMOD_TAGTYPE_VORBISCOMMENT, vc->user_comments[count], p, FMOD_strlen(p) + 1, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTremor::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecTremor *ogg = (CodecTremor *)codec; + + return ogg->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTremor::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecTremor *ogg = (CodecTremor *)codec; + + return ogg->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTremor::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecTremor *ogg = (CodecTremor *)codec; + + return ogg->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecTremor::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecTremor *ogg = (CodecTremor *)codec; + + return ogg->setPositionInternal(subsound, position, postype); +} + +} + +#endif + + diff --git a/src/fmod_codec_tremor.h b/src/fmod_codec_tremor.h new file mode 100755 index 0000000..29c178b --- /dev/null +++ b/src/fmod_codec_tremor.h @@ -0,0 +1,47 @@ +#ifndef _FMOD_CODEC_TREMOR_H +#define _FMOD_CODEC_TREMOR_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_TREMOR + +#include "fmod_codeci.h" + +#include "../lib/tremor_lowmem/ivorbisfile.h" + +namespace FMOD +{ + class SyncPointNamed; + + class CodecTremor : public Codec + { + private: + + OggVorbis_File mVf; + static bool gInitialized; + + SyncPointNamed *mSyncPoint; + int mNumSyncPoints; + FMOD_CODEC_WAVEFORMAT mWaveFormat; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT readVorbisComments(); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int size, unsigned int *read); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_TREMOR */ + +#endif + diff --git a/src/fmod_codec_user.cpp b/src/fmod_codec_user.cpp new file mode 100755 index 0000000..b74757b --- /dev/null +++ b/src/fmod_codec_user.cpp @@ -0,0 +1,330 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_USERCODEC + +#include "fmod.h" +#include "fmod_codec_user.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#include <stdio.h> + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX usercodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecUser::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecUser::getDescriptionEx() +{ + FMOD_memset(&usercodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + usercodec.name = "FMOD User Reader Codec"; + usercodec.version = 0x00010100; + usercodec.timeunits = FMOD_TIMEUNIT_PCM; + usercodec.open = &CodecUser::openCallback; + usercodec.close = &CodecUser::closeCallback; + usercodec.read = &CodecUser::readCallback; + usercodec.setposition = &CodecUser::setPositionCallback; + + usercodec.mType = FMOD_SOUND_TYPE_USER; + usercodec.mSize = sizeof(CodecUser); + + return &usercodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecUser::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + + init(FMOD_SOUND_TYPE_USER); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecUser::openInternal", "attempting to open user codec..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + waveformat = &mWaveFormat; + +#if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + if (userexinfo->format == FMOD_SOUND_FORMAT_VAG) + { + /* Do nothing */ + } + else +#endif + if (userexinfo->format < FMOD_SOUND_FORMAT_PCM8 || userexinfo->format > FMOD_SOUND_FORMAT_PCMFLOAT) + { + return FMOD_ERR_FORMAT; + } + + /* + Get size of file in bytes + */ + result = mFile->getSize(&waveformat[0].lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + /* + TODO : Open and fill in needed codec values + */ + mSrcDataOffset = 0; + + if (userexinfo->length) + { + mFlags |= FMOD_CODEC_USERLENGTH; + } + + waveformat[0].format = userexinfo->format; + waveformat[0].channels = userexinfo->numchannels; + waveformat[0].frequency = userexinfo->defaultfrequency; + SoundI::getSamplesFromBytes(userexinfo->length, &waveformat[0].lengthpcm, userexinfo->numchannels, userexinfo->format); + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, userexinfo->numchannels, userexinfo->format); + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecUser::openInternal", "Done. format = %d, channels %d, frequency %d, lengthpcm %d, blockalign %d.\n", waveformat[0].format, waveformat[0].channels, waveformat[0].frequency, waveformat[0].lengthpcm, waveformat[0].blockalign)); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecUser::closeInternal() +{ + /* + TODO : Close codec / cleanup + */ + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecUser::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + + *bytesread = sizebytes; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecUser::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecUser::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecUser *cuser = (CodecUser *)codec; + + return cuser->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecUser::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecUser *cuser = (CodecUser *)codec; + + return cuser->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecUser::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecUser *cuser = (CodecUser *)codec; + + return cuser->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecUser::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecUser *cuser = (CodecUser *)codec; + + return cuser->setPositionInternal(subsound, position, postype); +} + +} + +#endif + diff --git a/src/fmod_codec_user.h b/src/fmod_codec_user.h new file mode 100755 index 0000000..b5fe768 --- /dev/null +++ b/src/fmod_codec_user.h @@ -0,0 +1,37 @@ +#ifndef _FMOD_CODEC_USER_H +#define _FMOD_CODEC_USER_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_USERCODEC + +#include "fmod_codeci.h" + +namespace FMOD +{ + class CodecUser : public Codec + { + private: + + FMOD_CODEC_WAVEFORMAT mWaveFormat; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_USERCODEC */ + +#endif + diff --git a/src/fmod_codec_wav.cpp b/src/fmod_codec_wav.cpp new file mode 100755 index 0000000..5bd9a2f --- /dev/null +++ b/src/fmod_codec_wav.cpp @@ -0,0 +1,1120 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_IMAADPCM) + +#include "fmod_codec_wav.h" +#ifdef FMOD_SUPPORT_IMAADPCM +#include "fmod_codec_wav_imaadpcm.h" +#endif +#include "fmod_debug.h" +#include "fmod_dsp_codec.h" +#include "fmod_file.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" + +#if defined(PLATFORM_WINDOWS) && !defined(PLUGIN_FSB) + #include <windows.h> + #include <mmreg.h> + #include <msacm.h> +#endif + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_codec_adpcm_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_CODEC_DESCRIPTION_EX wavcodec; + +#if defined(PLUGIN_EXPORTS) && !defined(PLUGIN_FSB) + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecWav::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +static const FMOD_GUID _KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9b, 0x71 }}; +static const FMOD_GUID _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9b, 0x71 }}; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecWav::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&wavcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + wavcodec.name = "FMOD Wav Codec"; + wavcodec.version = 0x00010100; + wavcodec.timeunits = FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_RAWBYTES; + + #if defined(FMOD_SUPPORT_WAV) && !defined(PLATFORM_PS3_SPU) + #if !defined(PLUGIN_FSB) + wavcodec.open = &CodecWav::openCallback; + #endif + wavcodec.close = &CodecWav::closeCallback; + #endif + + #if defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_IMAADPCM) + wavcodec.read = &CodecWav::readCallback; + wavcodec.setposition = &CodecWav::setPositionCallback; + #endif + + #if defined(FMOD_SUPPORT_WAV) + + #ifndef PLATFORM_PS3_SPU + wavcodec.soundcreate = &CodecWav::soundCreateCallback; + #endif + wavcodec.canpoint = &CodecWav::canPointCallback; + + #endif + + wavcodec.mType = FMOD_SOUND_TYPE_WAV; + wavcodec.mSize = sizeof(CodecWav); + + #if defined (PLATFORM_PS3) + wavcodec.mModule = (FMOD_OS_LIBRARY *)_binary_spu_fmod_codec_adpcm_pic_start; + #endif +#else + wavcodec.read = &CodecWav::readCallback; + wavcodec.setposition = &CodecWav::setPositionCallback; +#endif + return &wavcodec; +} + +#if defined(FMOD_SUPPORT_WAV) && !defined(PLATFORM_PS3_SPU) +#if !defined(PLUGIN_FSB) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result; + int tag; + char wave[4]; + WAVE_CHUNK chunk; + + init(FMOD_SOUND_TYPE_WAV); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "attempting to open as WAV..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp((const char *)chunk.id, "RIFF", 4)) + { + return FMOD_ERR_FORMAT; + } + + result = mFile->read(wave, 1, 4, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp(wave, "WAVE", 4)) + { + return FMOD_ERR_FORMAT; + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + waveformat = mWaveFormatMemory; + + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + mSrcDataOffset = (unsigned int)-1; + mSyncPoint = 0; + mNumSyncPoints = 0; + + result = parseChunk(chunk.size); + if (result != FMOD_OK) + { + return result; + } + + /* + Didnt find the format chunk! + */ + if (!mSrcFormat) + { + return FMOD_ERR_FORMAT; + } + + /* + Didnt find the data chunk? + */ + if (mSrcDataOffset == (unsigned int)-1) + { + FMOD_Memory_Free(mSrcFormat); + mSrcFormat = 0; + mSrcDataOffset = 0; + return FMOD_ERR_FORMAT; + } + + /* + GET A DESTINATION FORMAT USING ACM + */ + FMOD_memset(&mDestFormat, 0, sizeof(WAVE_FORMATEXTENSIBLE)); + mDestFormat.Format.wFormatTag = WAVE_FORMAT_PCM; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->wFormatTag = %d\n", mSrcFormat->Format.wFormatTag)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->nChannels = %d\n", mSrcFormat->Format.nChannels)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->nSamplesPerSec = %d\n", mSrcFormat->Format.nSamplesPerSec)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->nAvgBytesPerSec = %d\n", mSrcFormat->Format.nAvgBytesPerSec)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->nBlockAlign = %d\n", mSrcFormat->Format.nBlockAlign)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->wBitsPerSample = %d\n", mSrcFormat->Format.wBitsPerSample)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::openInternal", "mSrcFormat->cbSize = %d\n", mSrcFormat->Format.cbSize)); + + tag = mSrcFormat->Format.wFormatTag; + + if (tag == WAVE_FORMAT_MPEG || tag == WAVE_FORMAT_MPEGLAYER3) + { + return FMOD_ERR_FORMAT; /* We want the fmod decoder to take care of mp3 decoding, not windows codecs */ + } + + if (tag == WAVE_FORMAT_EXTENSIBLE) + { + if (!memcmp(&mSrcFormat->SubFormat, &_KSDATAFORMAT_SUBTYPE_PCM, sizeof(FMOD_GUID)) || + !memcmp(&mSrcFormat->SubFormat, &_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FMOD_GUID)) ) + { + FMOD_memcpy(&mDestFormat, mSrcFormat, sizeof(WAVE_FORMATEXTENSIBLE)); /* copy src format into mDestFormat */ + + waveformat[0].lengthpcm = (unsigned int)((FMOD_UINT64)waveformat[0].lengthbytes * (FMOD_UINT64)8 / (FMOD_UINT64)mDestFormat.Format.wBitsPerSample / (FMOD_UINT64)mDestFormat.Format.nChannels); + waveformat[0].channelmask = mDestFormat.dwChannelMask; + + if (tag == WAVE_FORMAT_IEEE_FLOAT || !memcmp(&mSrcFormat->SubFormat, &_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FMOD_GUID))) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + if (mDestFormat.Format.wBitsPerSample != 32) + { + return FMOD_ERR_FORMAT; + } + } + else + { + result = SoundI::getFormatFromBits(mDestFormat.Format.wBitsPerSample, &waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + return FMOD_ERR_FORMAT; + } + } + else if (tag == WAVE_FORMAT_PCM || tag == WAVE_FORMAT_IEEE_FLOAT) + { + FMOD_memcpy(&mDestFormat, mSrcFormat, sizeof(WAVE_FORMATEX)); /* copy src format into mDestFormat */ + + waveformat[0].lengthpcm = (unsigned int)((FMOD_UINT64)waveformat[0].lengthbytes * (FMOD_UINT64)8 / (FMOD_UINT64)mDestFormat.Format.wBitsPerSample / (FMOD_UINT64)mDestFormat.Format.nChannels); + + if (tag == WAVE_FORMAT_IEEE_FLOAT) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + if (mDestFormat.Format.wBitsPerSample != 32) + { + return FMOD_ERR_FORMAT; + } + } + else + { + result = SoundI::getFormatFromBits(mDestFormat.Format.wBitsPerSample, &waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + } + } +#ifdef FMOD_SUPPORT_IMAADPCM + else if (tag == WAVE_FORMAT_IMA_ADPCM || tag == WAVE_FORMAT_XBOX_ADPCM) + { + if (false + #if defined(PLATFORM_XBOX) + || (mMode & FMOD_HARDWARE && tag == WAVE_FORMAT_XBOX_ADPCM) + #endif + ) + { + FMOD_memcpy(&mDestFormat, mSrcFormat, sizeof(WAVE_FORMATEXTENSIBLE)); /* copy src format into mDestFormat */ + waveformat[0].lengthpcm = waveformat[0].lengthbytes / 36 * 64; + + waveformat[0].format = FMOD_SOUND_FORMAT_IMAADPCM; + } + else + { + WAVE_FORMAT_IMAADPCM *formatadpcm = (WAVE_FORMAT_IMAADPCM *)mSrcFormat; + int numblocks; + + #if defined(PLATFORM_XBOX) + if (usermode & FMOD_HARDWARE && usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + return FMOD_ERR_NEEDSSOFTWARE; /* XADPCM yes, IMA ADPCM no. */ + } + #endif + + numblocks = waveformat[0].lengthbytes / formatadpcm->wfx.nBlockAlign; + waveformat[0].lengthpcm = numblocks * formatadpcm->wSamplesPerBlock; + + FMOD_memcpy(&mDestFormat, mSrcFormat, sizeof(WAVE_FORMATEX)); /* copy src format into mDestFormat */ + + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_IMAADPCM; + } + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + mDestFormat.Format.wFormatTag = WAVE_FORMAT_PCM; + mDestFormat.Format.wBitsPerSample = 16; + mDestFormat.Format.nBlockAlign = mDestFormat.Format.nChannels * mDestFormat.Format.wBitsPerSample / 8; + mDestFormat.Format.nAvgBytesPerSec = mDestFormat.Format.nBlockAlign * mDestFormat.Format.nSamplesPerSec; + + /* + Now calculate an intermediate bufferlength for file reading and its equivalent decode buffer. + */ + mSamplesPerADPCMBlock = formatadpcm->wSamplesPerBlock; + mPCMBufferLength = mSamplesPerADPCMBlock; + if (usermode & FMOD_CREATECOMPRESSEDSAMPLE) + { + mPCMBufferLengthBytes = 0; + mReadBufferLength = 0; + } + else + { + mPCMBufferLengthBytes = mPCMBufferLength * mDestFormat.Format.wBitsPerSample / 8 * mDestFormat.Format.nChannels; + mReadBufferLength = mSrcFormat->Format.nBlockAlign; + } + } + } +#endif + else + { +#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__) && !defined(PLUGIN_FSB) + MMRESULT hr; + HACMSTREAM has; + + /* + Now find the best destination format + */ + hr = acmFormatSuggest(NULL, (WAVEFORMATEX *)mSrcFormat, (WAVEFORMATEX *)&mDestFormat, sizeof(WAVE_FORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG); + if (hr) + { + return FMOD_ERR_FORMAT; + } + + hr = acmStreamOpen(&has, NULL, (WAVEFORMATEX *)mSrcFormat, (WAVEFORMATEX *)&mDestFormat, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME); + if (hr) + { + return FMOD_ERR_FORMAT; + } + mACMCodec = has; + + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + + /* + Get the length of the whole buffer in decompressed pcm samples. + */ + acmStreamSize(has, waveformat[0].lengthbytes, (unsigned long *)&waveformat[0].lengthpcm, ACM_STREAMSIZEF_SOURCE); + + waveformat[0].lengthpcm = (unsigned int)((FMOD_UINT64)waveformat[0].lengthpcm * (FMOD_UINT64)8 / (FMOD_UINT64)mDestFormat.Format.wBitsPerSample / (FMOD_UINT64)mDestFormat.Format.nChannels); + + /* + Now calculate an intermediate bufferlength for file reading and its equivalent decode buffer. + */ + if (mSrcFormat->Format.nBlockAlign) + { + mReadBufferLength = mSrcFormat->Format.nBlockAlign; + if (mReadBufferLength <= 1) + { + mReadBufferLength = 1024; + } + + acmStreamSize(has, mReadBufferLength, (unsigned long *)&mPCMBufferLengthBytes, ACM_STREAMSIZEF_SOURCE); + if (!mPCMBufferLengthBytes) + { + return FMOD_ERR_MEMORY; + } + } +#else + return FMOD_ERR_FORMAT; +#endif + } + +#ifdef PLATFORM_PS3 + if (waveformat[0].format != FMOD_SOUND_FORMAT_PCM16) + { + return FMOD_ERR_FORMAT; + } +#endif + + if (mReadBufferLength) + { + mReadBuffer = (unsigned char *)FMOD_Memory_Calloc(mReadBufferLength); + if (!mReadBuffer) + { + return FMOD_ERR_MEMORY; + } + } + + if (mPCMBufferLengthBytes) + { + #ifdef PLATFORM_PS3 + + mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes + 16); + if (!mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mPCMBuffer = (unsigned char *)FMOD_ALIGNPOINTER(mPCMBufferMemory, 16); + + #else + + mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes); + if (!mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mPCMBuffer = mPCMBufferMemory; + + #endif + } + + waveformat[0].channels = mSrcFormat->Format.nChannels; + waveformat[0].frequency = mSrcFormat->Format.nSamplesPerSec; + waveformat[0].blockalign = mSrcFormat->Format.nBlockAlign; + waveformat[0].loopstart = mLoopPoints[0]; + waveformat[0].loopend = mLoopPoints[1]; + + if (mLoopPoints[1] > mLoopPoints[0]) + { + waveformat[0].mode = FMOD_LOOP_NORMAL; + } + +#ifdef FMOD_SUPPORT_IMAADPCM + if (waveformat[0].format == FMOD_SOUND_FORMAT_IMAADPCM) + { + if (waveformat[0].channels > 2) + { + return FMOD_ERR_TOOMANYCHANNELS; /* Sorry we're only allocating memory for a pool of maximum stereo voices. */ + } + + mReadBufferLength = mSrcFormat->Format.nBlockAlign; + + #ifdef PLATFORM_XBOX + if (!(usermode & FMOD_HARDWARE)) + #endif + { + #ifdef FMOD_SUPPORT_DSPCODEC + int count; + + if (!mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs) + { + result = mSystem->mDSPCodecPool_ADPCM.init(FMOD_DSP_CATEGORY_DSPCODECADPCM, 64, mSystem->mAdvancedSettings.maxADPCMcodecs ? mSystem->mAdvancedSettings.maxADPCMcodecs : FMOD_ADVANCEDSETTINGS_MAXADPCMCODECS); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_ADPCM.mPool[count]); + CodecWav *wav = (CodecWav *)dspcodec->mCodec; + + wav->mSrcFormat = &wav->mSrcFormatMemory; + wav->mReadBuffer = mSystem->mDSPCodecPool_ADPCM.mReadBuffer; + wav->mSrcFormat->Format.wFormatTag = WAVE_FORMAT_IMA_ADPCM; + } + } + + /* + This form of wav needs PCM buffers allocated, as the decode block size is variable. + */ + for (count = 0; count < mSystem->mDSPCodecPool_ADPCM.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mSystem->mDSPCodecPool_ADPCM.mPool[count]); + CodecWav *wav = (CodecWav *)dspcodec->mCodec; + + if (!wav->mPCMBufferMemory) + { + wav->mPCMBufferMemory = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLength * mDestFormat.Format.wBitsPerSample / 8 * 2); /* *2 = maximum channels = 2 */ + if (!wav->mPCMBufferMemory) + { + return FMOD_ERR_MEMORY; + } + + wav->mPCMBuffer = wav->mPCMBufferMemory; + } + } + #else + return FMOD_ERR_UNSUPPORTED; + #endif + } + } +#endif + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + return result; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::closeInternal() +{ + if (mSrcFormat && mSrcFormat != &mSrcFormatMemory) + { + FMOD_Memory_Free(mSrcFormat); + mSrcFormat = 0; + } + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + if (mReadBuffer) + { + FMOD_Memory_Free(mReadBuffer); + mReadBuffer = 0; + } + mReadBufferLength = 0; + if (mSyncPoint) + { + FMOD_Memory_Free(mSyncPoint); + mSyncPoint = 0; + } + mNumSyncPoints = 0; + + if (mPCMBufferMemory) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWAV::release", "Free PCM Buffer\n")); + + FMOD_Memory_Free(mPCMBufferMemory); + mPCMBuffer = mPCMBufferMemory = 0; + } + mPCMBufferLengthBytes = 0; + + return FMOD_OK; +} +#endif // FMOD_SUPPORT_WAV + + +#if defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_IMAADPCM) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result; + bool finished = false; + + if (mSrcFormat->Format.wFormatTag == WAVE_FORMAT_PCM + || mSrcFormat->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT + || mSrcFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE + #ifdef PLATFORM_XBOX + || (mSrcFormat->Format.wFormatTag == WAVE_FORMAT_XBOX_ADPCM && waveformat[0].format == FMOD_SOUND_FORMAT_IMAADPCM && !mPCMBuffer) + #endif + ) + { + unsigned int pos; + + mFile->tell(&pos); + + if (pos >= (mSrcDataOffset + waveformat[0].lengthbytes)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecWav::readInternal", "ERROR! File position was past end of data! pos = %d : end = %d\n", pos, mSrcDataOffset + waveformat[0].lengthbytes)); + return FMOD_ERR_FILE_EOF; + } + if (pos + sizebytes > mSrcDataOffset + waveformat[0].lengthbytes) + { + sizebytes = (mSrcDataOffset + waveformat[0].lengthbytes) - pos; + finished = true; + } + + if (waveformat[0].format == FMOD_SOUND_FORMAT_PCM8) + { + unsigned char *ptr = (unsigned char *)buffer; + unsigned int len; + + result = mFile->read(buffer, 1, sizebytes, bytesread); + + len = *bytesread >> 2; + while (len) + { + ptr[0] ^= 128; /* Convert from unsigned to signed */ + ptr[1] ^= 128; /* Convert from unsigned to signed */ + ptr[2] ^= 128; /* Convert from unsigned to signed */ + ptr[3] ^= 128; /* Convert from unsigned to signed */ + ptr+=4; + len--; + } + len = *bytesread & 3; + while (len) + { + ptr[0] ^= 128; /* Convert from unsigned to signed */ + ptr++; + len--; + } + } + else if (waveformat[0].format == FMOD_SOUND_FORMAT_PCM16) + { + result = mFile->read(buffer, 2, sizebytes / 2, bytesread); + + *bytesread *= 2; /* convert from 16bit words back to bytes */ + } + else + { + result = mFile->read(buffer, 1, sizebytes, bytesread); + } + + if (finished) + { + result = FMOD_ERR_FILE_EOF; + } + + if (result != FMOD_OK) + { + return result; + } + } + else +#ifdef FMOD_SUPPORT_IMAADPCM + if (mSrcFormat->Format.wFormatTag == WAVE_FORMAT_IMA_ADPCM || mSrcFormat->Format.wFormatTag == WAVE_FORMAT_XBOX_ADPCM) + { + int blockalign = waveformat[0].blockalign; + signed short *destbuff = (signed short *)buffer; + unsigned char *readbuff = (unsigned char *)FMOD_alloca(mReadBufferLength); + + if (!readbuff) + { + return FMOD_ERR_MEMORY; + } + + result = mFile->read(readbuff, 1, mReadBufferLength, 0); + if (result != FMOD_OK) + { + return result; + } + + if (waveformat[0].format == FMOD_SOUND_FORMAT_IMAADPCM) + { + if (waveformat[0].channels == 1) + { + result = IMAAdpcm_DecodeM16(readbuff, destbuff, 1, blockalign, mSamplesPerADPCMBlock, 1); + } + else if (waveformat[0].channels == 2) + { + result = IMAAdpcm_DecodeS16(readbuff, destbuff, 1, blockalign, mSamplesPerADPCMBlock); + } + else + { + int count = 0; + + blockalign /= waveformat[0].channels; + + for (count = 0; count < waveformat[0].channels; count++) + { + signed short tempin[4096]; + int count2; + + for (count2 = 0; count2 < (int)mReadBufferLength / waveformat[0].channels; count2++) + { + tempin[count2] = ((signed short *)readbuff)[(count2 * waveformat[0].channels) + count]; + } + + result = IMAAdpcm_DecodeM16((unsigned char *)tempin, destbuff + count, 1, blockalign, mSamplesPerADPCMBlock, waveformat[0].channels); + } + } + + *bytesread = mSamplesPerADPCMBlock * sizeof(signed short) * waveformat[0].channels; + } + else + { + if (waveformat[0].channels == 1) + { + result = IMAAdpcm_DecodeM16(readbuff, destbuff, 1, blockalign, mSamplesPerADPCMBlock, 1); + } + else if (waveformat[0].channels == 2) + { + result = IMAAdpcm_DecodeS16(readbuff, destbuff, 1, blockalign, mSamplesPerADPCMBlock); + } + else + { + int count = 0; + + blockalign /= waveformat[0].channels; + + for (count = 0; count < waveformat[0].channels; count++) + { + signed short tempin[4096]; + int count2; + + for (count2 = 0; count2 < (int)mReadBufferLength / waveformat[0].channels; count2++) + { + tempin[count2] = ((signed short *)readbuff)[(count2 * waveformat[0].channels) + count]; + } + + result = IMAAdpcm_DecodeM16((unsigned char *)tempin, destbuff + count, 1, blockalign, mSamplesPerADPCMBlock, waveformat[0].channels); + } + } + + *bytesread = mSamplesPerADPCMBlock * sizeof(signed short) * waveformat[0].channels; + } + } + else +#endif +#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__) && !defined(PLUGIN_FSB) + if (mACMCodec) + { + ACMSTREAMHEADER ash; + MMRESULT mmresult; + + result = mFile->read(mReadBuffer, 1, mReadBufferLength, 0); + if (result != FMOD_OK) + { + return result; + } + + FMOD_memset(&ash, 0, sizeof(ACMSTREAMHEADER)); + ash.cbStruct = sizeof(ACMSTREAMHEADER); + ash.pbSrc = mReadBuffer; + ash.cbSrcLength = mReadBufferLength; + ash.pbDst = (unsigned char *)buffer; + ash.cbDstLength = sizebytes; + + mmresult = acmStreamPrepareHeader(mACMCodec, &ash, 0); + + mmresult = acmStreamConvert(mACMCodec, &ash, ACM_STREAMCONVERTF_BLOCKALIGN); + + *bytesread = ash.cbDstLengthUsed; + + mmresult = acmStreamUnprepareHeader(mACMCodec, &ash, 0); + } + else +#endif + { + return FMOD_ERR_PLUGIN_MISSING; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + unsigned int raw = 0; + unsigned int pcmbytes = 0; + unsigned int pcmbytesaligned = 0; + unsigned int pcmaligned = 0; + unsigned int excessbytes = 0; + + if (postype == FMOD_TIMEUNIT_RAWBYTES) + { + result = mFile->seek(mSrcDataOffset + position, SEEK_SET); + return result; + } + + if (position) + { + raw = (unsigned int)((FMOD_UINT64)position * (FMOD_UINT64)waveformat[0].lengthbytes / (FMOD_UINT64)waveformat[0].lengthpcm); + raw /= waveformat[0].blockalign; + raw *= waveformat[0].blockalign; + + pcmaligned = (unsigned int)((FMOD_UINT64)raw * (FMOD_UINT64)waveformat[0].lengthpcm / (FMOD_UINT64)waveformat[0].lengthbytes); + + result = SoundI::getBytesFromSamples(position, &pcmbytes, waveformat[0].channels, waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + + result = SoundI::getBytesFromSamples(pcmaligned, &pcmbytesaligned, waveformat[0].channels, waveformat[0].format); + if (result != FMOD_OK) + { + return result; + } + } + + result = mFile->seek(mSrcDataOffset + raw, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + excessbytes = pcmbytes - pcmbytesaligned; + + while (excessbytes) + { + char buff[4096]; + unsigned int read = 0, toread = 1000; + + if (toread > excessbytes) + { + toread = excessbytes; + } + + result = Codec::read(buff, toread, &read); + if (result != FMOD_OK) + { + break; + } + + if (excessbytes >= read) + { + excessbytes -= read; + } + else + { + excessbytes = 0; + } + } + + return result; +} +#endif // defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_IMAADPCM) + + +#if defined(FMOD_SUPPORT_WAV) && !defined(PLATFORM_PS3_SPU) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::soundCreateInternal(int subsound, FMOD_SOUND *sound) +{ + FMOD_RESULT result = FMOD_OK; + SoundI *s = (SoundI *)sound; + + if (mNumSyncPoints && mSyncPoint) + { + int count; + + for (count = 0; count < mNumSyncPoints; count++) + { + SyncPointNamed *point = &mSyncPoint[count]; + s->addSyncPointInternal(point->mOffset, FMOD_TIMEUNIT_PCM, point->mName, (FMOD_SYNCPOINT **)&point, 0, false); + } + + s->syncPointFixIndicies(); + + s->mSyncPointMemory = mSyncPoint; /* Transfer pointer into the sound. Sound will free it. */ + mSyncPoint = 0; + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::canPointInternal() +{ +#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__) && !defined(PLUGIN_FSB) + if (mACMCodec || !mSrcFormat) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } +#else + if (!mSrcFormat) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } +#endif + + if (mSrcFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + if (memcmp(&mSrcFormat->SubFormat, &_KSDATAFORMAT_SUBTYPE_PCM, sizeof(FMOD_GUID)) && + memcmp(&mSrcFormat->SubFormat, &_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FMOD_GUID)) ) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + } + else if (mSrcFormat->Format.wFormatTag != WAVE_FORMAT_PCM && mSrcFormat->Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + + + + if (mSrcFormat->Format.wBitsPerSample == 8) + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + + return FMOD_OK; +} + + +#if !defined(PLUGIN_FSB) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecWav::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecWav *wav = (CodecWav *)codec; + + return wav->openInternal(usermode, userexinfo); +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecWav::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecWav *wav = (CodecWav *)codec; + + return wav->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecWav::soundCreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + CodecWav *wav = (CodecWav *)codec; + + return wav->soundCreateInternal(subsound, sound); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecWav::canPointCallback(FMOD_CODEC_STATE *codec) +{ + CodecWav *wav = (CodecWav *)codec; + + return wav->canPointInternal(); +} + +#endif //FMOD_SUPPORT_WAV + + +#if defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_IMAADPCM) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecWav::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecWav *wav = (CodecWav *)codec; + + return wav->readInternal(buffer, sizebytes, bytesread); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecWav::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecWav *wav = (CodecWav *)codec; + + return wav->setPositionInternal(subsound, position, postype); +} +#endif + + +} + +#endif + + diff --git a/src/fmod_codec_wav.h b/src/fmod_codec_wav.h new file mode 100755 index 0000000..08a2d2c --- /dev/null +++ b/src/fmod_codec_wav.h @@ -0,0 +1,180 @@ +#ifndef _FMOD_CODEC_WAV_H +#define _FMOD_CODEC_WAV_H + +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_IMAADPCM) + +#include "fmod_codeci.h" +#include "fmod_memory.h" +#include "fmod_types.h" + +#if defined(PLATFORM_WINDOWS) + #include <windows.h> + #include <mmreg.h> + #include <msacm.h> +#endif + +namespace FMOD +{ + #ifndef WAVE_FORMAT_PCM + #define WAVE_FORMAT_PCM 1 + #endif + + #ifndef WAVE_FORMAT_IEEE_FLOAT + #define WAVE_FORMAT_IEEE_FLOAT 3 + #endif + + #ifndef WAVE_FORMAT_MPEG + #define WAVE_FORMAT_MPEG 0x0050 /* Microsoft Corporation */ + #endif + + #ifndef WAVE_FORMAT_MPEGLAYER3 + #define WAVE_FORMAT_MPEGLAYER3 0x0055 + #endif + + #ifndef WAVE_FORMAT_OGGVORBIS + #define WAVE_FORMAT_OGGVORBIS 0x6750 + #endif + + #ifndef WAVE_FORMAT_EXTENSIBLE + #define WAVE_FORMAT_EXTENSIBLE 0xFFFE + #endif + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + #define F_WSMP_NO_TRUNCATION 0x0001 + #define F_WSMP_NO_COMPRESSION 0x0002 + #define WLOOP_TYPE_FORWARD 0 + + typedef struct + { + signed char id[4] FMOD_PACKED_INTERNAL; + unsigned int size FMOD_PACKED_INTERNAL; + } FMOD_PACKED WAVE_CHUNK; + + typedef struct + { + unsigned short wFormatTag FMOD_PACKED_INTERNAL; /* format type */ + unsigned short nChannels FMOD_PACKED_INTERNAL; /* number of channels (i.e. mono, stereo...) */ + unsigned int nSamplesPerSec FMOD_PACKED_INTERNAL; /* sample rate */ + unsigned int nAvgBytesPerSec FMOD_PACKED_INTERNAL; /* for buffer estimation */ + unsigned short nBlockAlign FMOD_PACKED_INTERNAL; /* block size of data */ + unsigned short wBitsPerSample FMOD_PACKED_INTERNAL; /* number of bits per sample of mono data */ + unsigned short cbSize FMOD_PACKED_INTERNAL; /* the count in bytes of the size of extra information (after cbSize) */ + } FMOD_PACKED WAVE_FORMATEX; + + typedef struct + { + WAVE_FORMATEX Format FMOD_PACKED_INTERNAL; + union + { + unsigned short wValidBitsPerSample FMOD_PACKED_INTERNAL; /* bits of precision */ + unsigned short wSamplesPerBlock FMOD_PACKED_INTERNAL; /* valid if wBitsPerSample==0 */ + unsigned short wReserved FMOD_PACKED_INTERNAL; /* If neither applies, set to zero. */ + } FMOD_PACKED Samples; + unsigned int dwChannelMask FMOD_PACKED_INTERNAL; /* which channels are */ + FMOD_GUID SubFormat FMOD_PACKED_INTERNAL; + } FMOD_PACKED WAVE_FORMATEXTENSIBLE; + + typedef struct + { + unsigned int Manufacturer FMOD_PACKED_INTERNAL; + unsigned int Product FMOD_PACKED_INTERNAL; + unsigned int SamplePeriod FMOD_PACKED_INTERNAL; + unsigned int Note FMOD_PACKED_INTERNAL; + unsigned int FineTune FMOD_PACKED_INTERNAL; + unsigned int SMPTEFormat FMOD_PACKED_INTERNAL; + unsigned int SMPTEOffset FMOD_PACKED_INTERNAL; + unsigned int Loops FMOD_PACKED_INTERNAL; + unsigned int SamplerData FMOD_PACKED_INTERNAL; + struct + { + unsigned int Identifier FMOD_PACKED_INTERNAL; + unsigned int Type FMOD_PACKED_INTERNAL; + unsigned int Start FMOD_PACKED_INTERNAL; + unsigned int End FMOD_PACKED_INTERNAL; + unsigned int Fraction FMOD_PACKED_INTERNAL; + unsigned int Count FMOD_PACKED_INTERNAL; + } FMOD_PACKED Loop; + } FMOD_PACKED WAVE_SMPLHEADER; + + typedef struct + { + int dwIdentifier FMOD_PACKED_INTERNAL; + int dwPosition FMOD_PACKED_INTERNAL; + char fccChunk[4] FMOD_PACKED_INTERNAL; + int dwChunkStart FMOD_PACKED_INTERNAL; + int dwBlockStart FMOD_PACKED_INTERNAL; + int dwSampleOffset FMOD_PACKED_INTERNAL; + } FMOD_PACKED WAVE_CUEPOINT; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + + class SyncPointNamed; + class ChannelSoftware; + class ChannelOpenAL; + + class CodecWav : public Codec + { + friend class CodecMPEG; + friend class CodecMPEGPSP; + friend class CodecOggVorbis; + friend class CodecTremor; + friend class CodecDLS; + friend class CodecFSB; + friend class ChannelSoftware; + friend class ChannelOpenAL; + friend class DSPCodec; + friend class DSPCodecPool; + friend class SystemI; + + private: + +#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__) + HACMSTREAM mACMCodec; +#endif + + WAVE_FORMATEXTENSIBLE mDestFormat; + + int mNumSyncPoints; + SyncPointNamed *mSyncPoint; + int mSamplesPerADPCMBlock; + + FMOD_RESULT parseChunk(unsigned int chunksize); + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT soundCreateInternal(int subsound, FMOD_SOUND *sound); + FMOD_RESULT canPointInternal(); + + public: + + WAVE_FORMATEXTENSIBLE mSrcFormatMemory; + WAVE_FORMATEXTENSIBLE *mSrcFormat; + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int size, unsigned int *read); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK soundCreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + static FMOD_RESULT F_CALLBACK canPointCallback(FMOD_CODEC_STATE *codec); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_WAV */ + +#endif + + diff --git a/src/fmod_codec_wav_imaadpcm.cpp b/src/fmod_codec_wav_imaadpcm.cpp new file mode 100755 index 0000000..3a8da25 --- /dev/null +++ b/src/fmod_codec_wav_imaadpcm.cpp @@ -0,0 +1,787 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_IMAADPCM + +#include "fmod_codec_wav_imaadpcm.h" + +namespace FMOD +{ + +#undef LOWORD +#undef HIWORD + +#undef LOWORD +#undef HIWORD +#undef MAKEULONG +#undef MAKELONG + +#define LOWORD(_l) ((unsigned short)(_l)) +#define HIWORD(_l) ((unsigned short)(((unsigned int)(_l) >> 16) & 0xFFFF)) + +#ifdef PLATFORM_ENDIAN_BIG + #define MAKEULONG(l, h) ((unsigned int)(((unsigned short)(h)) | ((unsigned int)((unsigned short)(l))) << 16)) +#else + #define MAKEULONG(l, h) ((unsigned int)(((unsigned short)(l)) | ((unsigned int)((unsigned short)(h))) << 16)) +#endif + +#define MAKELONG(l, h) ((int)MAKEULONG(l, h)) + +#define NUMELMS(a) (sizeof(a) / sizeof(a[0])) + +static const int IMAAdpcm_IndexTab[16] = +{ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +// +// This array contains the array of step sizes used to encode the ADPCM +// samples. The step index in each ADPCM block is an index to this array. +// +static const short IMAAdpcm_StepTab[89] = +{ + 7, 8, 9, 10, 11, 12, 13, + 14, 16, 17, 19, 21, 23, 25, + 28, 31, 34, 37, 41, 45, 50, + 55, 60, 66, 73, 80, 88, 97, + 107, 118, 130, 143, 157, 173, 190, + 209, 230, 253, 279, 307, 337, 371, + 408, 449, 494, 544, 598, 658, 724, + 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, + 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, + 11487, 12635, 13899, 15289, 16818, 18500, 20350, + 22385, 24623, 27086, 29794, 32767 +}; + + +FMOD_INLINE int IMAAdpcm_NextStepIndex(int nEncodedSample, int nStepIndex) +{ + nStepIndex += IMAAdpcm_IndexTab[nEncodedSample]; + + if(nStepIndex < 0) + { + nStepIndex = 0; + } + else if (nStepIndex >= (int)NUMELMS(IMAAdpcm_StepTab)) + { + nStepIndex = NUMELMS(IMAAdpcm_StepTab) - 1; + } + + return nStepIndex; +} + +FMOD_INLINE signed char ValidStepIndex(int nStepIndex) +{ + return (nStepIndex >= 0) && (nStepIndex < (int)NUMELMS(IMAAdpcm_StepTab)); +} + + +/**************************************************************************** + * + * IMAAdpcm_DecodeSample + * + * Description: + * Decodes an encoded sample. + * + * Arguments: + * int [in]: the sample to be decoded. + * int [in]: the predicted value of the sample. + * int [i]: the quantization step size used to encode the sample. + * + * Returns: + * int: the decoded PCM sample. + * + ****************************************************************************/ +FMOD_INLINE int IMAAdpcm_DecodeSample(int nEncodedSample, int nPredictedSample, int nStepSize) +{ + int lDifference; + int lNewSample; + + lDifference = nStepSize >> 3; + +#if 1 + + switch (nEncodedSample) + { + case 0: + { + break; + } + case 1: + { + lDifference += nStepSize >> 2; + break; + } + case 2: + { + lDifference += nStepSize >> 1; + break; + } + case 3: + { + lDifference += nStepSize >> 1; + lDifference += nStepSize >> 2; + break; + } + case 4: + { + lDifference += nStepSize; + break; + } + case 5: + { + lDifference += nStepSize; + lDifference += nStepSize >> 2; + break; + } + case 6: + { + lDifference += nStepSize; + lDifference += nStepSize >> 1; + break; + } + case 7: + { + lDifference += nStepSize; + lDifference += nStepSize >> 1; + lDifference += nStepSize >> 2; + break; + } + case 8: + { + lDifference = -lDifference; + break; + } + case 9: + { + lDifference += nStepSize >> 2; + lDifference = -lDifference; + break; + } + case 10: + { + lDifference += nStepSize >> 1; + lDifference = -lDifference; + break; + } + case 11: + { + lDifference += nStepSize >> 1; + lDifference += nStepSize >> 2; + lDifference = -lDifference; + break; + } + case 12: + { + lDifference += nStepSize; + lDifference = -lDifference; + break; + } + case 13: + { + lDifference += nStepSize; + lDifference += nStepSize >> 2; + lDifference = -lDifference; + break; + } + case 14: + { + lDifference += nStepSize; + lDifference += nStepSize >> 1; + lDifference = -lDifference; + break; + } + case 15: + { + lDifference += nStepSize; + lDifference += nStepSize >> 1; + lDifference += nStepSize >> 2; + lDifference = -lDifference; + break; + } + default: + { + break; + } + } +#else + if(nEncodedSample & 4) + { + lDifference += nStepSize; + } + + if(nEncodedSample & 2) + { + lDifference += nStepSize >> 1; + } + + if(nEncodedSample & 1) + { + lDifference += nStepSize >> 2; + } + + if(nEncodedSample & 8) + { + lDifference = -lDifference; + } +#endif + + lNewSample = nPredictedSample + lDifference; + + if((int)(short)lNewSample != lNewSample) + { + if(lNewSample < -32768) + { + lNewSample = -32768; + } + else + { + lNewSample = 32767; + } + } + + return (int)lNewSample; +} + + + +FMOD_RESULT IMAAdpcm_DecodeM16(unsigned char *pbSrc, signed short *pbDst, unsigned int cBlocks, unsigned int nBlockAlignment, unsigned int cSamplesPerBlock, int channels) +{ + FMOD_RESULT result = FMOD_OK; + unsigned char *pbBlock; + unsigned int cSamples; + unsigned char bSample; + int nStepSize; + int nEncSample; + int nPredSample; + int nStepIndex; + unsigned int dwHeader; + + // + // Enter the main loop + // + + while(cBlocks--) + { + pbBlock = pbSrc; + cSamples = cSamplesPerBlock - 1; + + // + // Block header + // + + dwHeader = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwHeader = FMOD_SWAPENDIAN_DWORD(dwHeader); +#endif + pbBlock += sizeof(unsigned int); + + nPredSample = (int)(short)LOWORD(dwHeader); + nStepIndex = (int)(unsigned char)HIWORD(dwHeader); + + if(!ValidStepIndex(nStepIndex)) + { + // + // The step index is out of range - this is considered a fatal + // error as the input stream is corrupted. We fail by returning + // zero chars converted. + // + + result = FMOD_ERR_FILE_BAD; + break; + } + + // + // Write out first sample + // + + *pbDst = (short)nPredSample; + pbDst += channels; + + // + // Enter the block loop + // + + while(cSamples) + { + bSample = *pbBlock++; + + // + // Sample 1 + // + + nEncSample = (bSample & (unsigned char)0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndex]; + nPredSample = IMAAdpcm_DecodeSample(nEncSample, nPredSample, nStepSize); + nStepIndex = IMAAdpcm_NextStepIndex(nEncSample, nStepIndex); + + *pbDst = (short)nPredSample; + pbDst += channels; + + cSamples--; + + // + // Sample 2 + // + + if(cSamples) + { + nEncSample = (bSample >> 4); + nStepSize = IMAAdpcm_StepTab[nStepIndex]; + nPredSample = IMAAdpcm_DecodeSample(nEncSample, nPredSample, nStepSize); + nStepIndex = IMAAdpcm_NextStepIndex(nEncSample, nStepIndex); + + *pbDst = (short)nPredSample; + pbDst += channels; + + cSamples--; + } + } + + // + // Skip padding + // + + pbSrc += nBlockAlignment; + } + + return result; +} + + +FMOD_RESULT IMAAdpcm_DecodeS16(unsigned char *pbSrc, signed short *pbDst, unsigned int cBlocks, unsigned int nBlockAlignment, unsigned int cSamplesPerBlock) +{ + FMOD_RESULT result = FMOD_OK; + unsigned char *pbBlock; + unsigned int cSamples; + unsigned int cSubSamples; + int nStepSize; + unsigned int dwHeader; + unsigned int dwLeft; + unsigned int dwRight; + int nEncSampleL; + int nPredSampleL; + int nStepIndexL; + int nEncSampleR; + int nPredSampleR; + int nStepIndexR; + unsigned int i; + + // + // Enter the main loop + // + + while(cBlocks--) + { + pbBlock = pbSrc; + cSamples = cSamplesPerBlock - 1; + + // + // LEFT channel header + // + + dwHeader = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwHeader = FMOD_SWAPENDIAN_DWORD(dwHeader); +#endif + pbBlock += sizeof(unsigned int); + + nPredSampleL = (int)(short)LOWORD(dwHeader); + nStepIndexL = (int)(unsigned char)HIWORD(dwHeader); + + if(!ValidStepIndex(nStepIndexL)) + { + // + // The step index is out of range - this is considered a fatal + // error as the input stream is corrupted. We fail by returning + // zero bytes converted. + // + + result = FMOD_ERR_FILE_BAD; + break; + } + + // + // RIGHT channel header + // + + dwHeader = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwHeader = FMOD_SWAPENDIAN_DWORD(dwHeader); +#endif + pbBlock += sizeof(unsigned int); + + nPredSampleR = (int)(short)LOWORD(dwHeader); + nStepIndexR = (int)(unsigned char)HIWORD(dwHeader); + + if(!ValidStepIndex(nStepIndexR)) + { + // + // The step index is out of range - this is considered a fatal + // error as the input stream is corrupted. We fail by returning + // zero bytes converted. + // + + result = FMOD_ERR_FILE_BAD; + break; + } + + // + // Write out first sample + // + + *pbDst++ = nPredSampleL; + *pbDst++ = nPredSampleR; + + // + // The first DWORD contains 4 left samples, the second DWORD + // contains 4 right samples. We process the source in 8-byte + // chunks to make it easy to interleave the output correctly. + // + + while(cSamples) + { + dwLeft = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwLeft = FMOD_SWAPENDIAN_DWORD(dwLeft); +#endif + pbBlock += sizeof(unsigned int); + dwRight = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwRight = FMOD_SWAPENDIAN_DWORD(dwRight); +#endif + pbBlock += sizeof(unsigned int); + +// cSubSamples = min(cSamples, 8); + cSubSamples = 8; + if (cSamples < cSubSamples) + { + cSubSamples = cSamples; + } + + for(i = 0; i < cSubSamples; i++) + { + // + // LEFT channel + // + + nEncSampleL = (dwLeft & 0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndexL]; + nPredSampleL = IMAAdpcm_DecodeSample(nEncSampleL, nPredSampleL, nStepSize); + nStepIndexL = IMAAdpcm_NextStepIndex(nEncSampleL, nStepIndexL); + + // + // RIGHT channel + // + + nEncSampleR = (dwRight & 0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndexR]; + nPredSampleR = IMAAdpcm_DecodeSample(nEncSampleR, nPredSampleR, nStepSize); + nStepIndexR = IMAAdpcm_NextStepIndex(nEncSampleR, nStepIndexR); + + // + // Write out sample + // + + *pbDst++ = nPredSampleL; + *pbDst++ = nPredSampleR; + + // + // Shift the next input sample into the low-order 4 bits. + // + + dwLeft >>= 4; + dwRight >>= 4; + } + + cSamples -= cSubSamples; + } + + // + // Skip padding + // + + pbSrc += nBlockAlignment; + } + + return result; +} + + +FMOD_RESULT IMAAdpcm_DecodeM16(unsigned char *pbSrc, float *pbDst, unsigned int cBlocks, unsigned int nBlockAlignment, unsigned int cSamplesPerBlock, int channels) +{ + FMOD_RESULT result = FMOD_OK; + unsigned char *pbBlock; + unsigned int cSamples; + unsigned char bSample; + int nStepSize; + int nEncSample; + int nPredSample; + int nStepIndex; + unsigned int dwHeader; + + // + // Enter the main loop + // + + while(cBlocks--) + { + pbBlock = pbSrc; + cSamples = cSamplesPerBlock - 1; + + // + // Block header + // + + dwHeader = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwHeader = FMOD_SWAPENDIAN_DWORD(dwHeader); +#endif + pbBlock += sizeof(unsigned int); + + nPredSample = (int)(short)LOWORD(dwHeader); + nStepIndex = (int)(unsigned char)HIWORD(dwHeader); + + if(!ValidStepIndex(nStepIndex)) + { + // + // The step index is out of range - this is considered a fatal + // error as the input stream is corrupted. We fail by returning + // zero chars converted. + // + + result = FMOD_ERR_FILE_BAD; + break; + } + + // + // Write out first sample + // + + *pbDst = nPredSample * (1.0f / 32768.0f); + pbDst += channels; + + // + // Enter the block loop + // + while(cSamples > 1) + { + bSample = *pbBlock++; + + // + // Sample 1 + // + + nEncSample = (bSample & (unsigned char)0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndex]; + nPredSample = IMAAdpcm_DecodeSample(nEncSample, nPredSample, nStepSize); + nStepIndex = IMAAdpcm_NextStepIndex(nEncSample, nStepIndex); + + pbDst[0] = nPredSample * (1.0f / 32768.0f); + + // + // Sample 2 + // + nEncSample = (bSample >> 4); + nStepSize = IMAAdpcm_StepTab[nStepIndex]; + nPredSample = IMAAdpcm_DecodeSample(nEncSample, nPredSample, nStepSize); + nStepIndex = IMAAdpcm_NextStepIndex(nEncSample, nStepIndex); + + pbDst[channels] = nPredSample * (1.0f / 32768.0f); + + cSamples -=2; + pbDst += channels*2; + } + + if (cSamples) + { + bSample = *pbBlock++; + + // + // Sample 1 + // + + nEncSample = (bSample & (unsigned char)0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndex]; + nPredSample = IMAAdpcm_DecodeSample(nEncSample, nPredSample, nStepSize); + nStepIndex = IMAAdpcm_NextStepIndex(nEncSample, nStepIndex); + + pbDst[0] = nPredSample * (1.0f / 32768.0f); + cSamples --; + pbDst += channels; + } + + // + // Skip padding + // + + pbSrc += nBlockAlignment; + } + + return result; +} + + +FMOD_RESULT IMAAdpcm_DecodeS16(unsigned char *pbSrc, float *pbDst, unsigned int cBlocks, unsigned int nBlockAlignment, unsigned int cSamplesPerBlock) +{ + FMOD_RESULT result = FMOD_OK; + unsigned char *pbBlock; + unsigned int cSamples; + unsigned int cSubSamples; + int nStepSize; + unsigned int dwHeader; + unsigned int dwLeft; + unsigned int dwRight; + int nEncSampleL; + int nPredSampleL; + int nStepIndexL; + int nEncSampleR; + int nPredSampleR; + int nStepIndexR; + unsigned int i; + + // + // Enter the main loop + // + + while(cBlocks--) + { + pbBlock = pbSrc; + cSamples = cSamplesPerBlock - 1; + + // + // LEFT channel header + // + + dwHeader = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwHeader = FMOD_SWAPENDIAN_DWORD(dwHeader); +#endif + pbBlock += sizeof(unsigned int); + + nPredSampleL = (int)(short)LOWORD(dwHeader); + nStepIndexL = (int)(unsigned char)HIWORD(dwHeader); + + if(!ValidStepIndex(nStepIndexL)) + { + // + // The step index is out of range - this is considered a fatal + // error as the input stream is corrupted. We fail by returning + // zero bytes converted. + // + + result = FMOD_ERR_FILE_BAD; + break; + } + + // + // RIGHT channel header + // + + dwHeader = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwHeader = FMOD_SWAPENDIAN_DWORD(dwHeader); +#endif + pbBlock += sizeof(unsigned int); + + nPredSampleR = (int)(short)LOWORD(dwHeader); + nStepIndexR = (int)(unsigned char)HIWORD(dwHeader); + + if(!ValidStepIndex(nStepIndexR)) + { + // + // The step index is out of range - this is considered a fatal + // error as the input stream is corrupted. We fail by returning + // zero bytes converted. + // + + result = FMOD_ERR_FILE_BAD; + break; + } + + // + // Write out first sample + // + + *pbDst++ = nPredSampleL * (1.0f / 32768.0f); + *pbDst++ = nPredSampleR * (1.0f / 32768.0f); + + // + // The first DWORD contains 4 left samples, the second DWORD + // contains 4 right samples. We process the source in 8-byte + // chunks to make it easy to interleave the output correctly. + // + + while(cSamples) + { + dwLeft = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwLeft = FMOD_SWAPENDIAN_DWORD(dwLeft); +#endif + pbBlock += sizeof(unsigned int); + dwRight = *(unsigned int *)pbBlock; +#ifdef PLATFORM_ENDIAN_BIG + dwRight = FMOD_SWAPENDIAN_DWORD(dwRight); +#endif + pbBlock += sizeof(unsigned int); + +// cSubSamples = min(cSamples, 8); + cSubSamples = 8; + if (cSamples < cSubSamples) + { + cSubSamples = cSamples; + } + + for(i = 0; i < cSubSamples; i++) + { + // + // LEFT channel + // + + nEncSampleL = (dwLeft & 0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndexL]; + nPredSampleL = IMAAdpcm_DecodeSample(nEncSampleL, nPredSampleL, nStepSize); + nStepIndexL = IMAAdpcm_NextStepIndex(nEncSampleL, nStepIndexL); + + // + // RIGHT channel + // + + nEncSampleR = (dwRight & 0x0F); + nStepSize = IMAAdpcm_StepTab[nStepIndexR]; + nPredSampleR = IMAAdpcm_DecodeSample(nEncSampleR, nPredSampleR, nStepSize); + nStepIndexR = IMAAdpcm_NextStepIndex(nEncSampleR, nStepIndexR); + + // + // Write out sample + // + + *pbDst++ = nPredSampleL * (1.0f / 32768.0f); + *pbDst++ = nPredSampleR * (1.0f / 32768.0f); + + // + // Shift the next input sample into the low-order 4 bits. + // + + dwLeft >>= 4; + dwRight >>= 4; + } + + cSamples -= cSubSamples; + } + + // + // Skip padding + // + + pbSrc += nBlockAlignment; + } + + return result; +} + +} +#endif + diff --git a/src/fmod_codec_wav_imaadpcm.h b/src/fmod_codec_wav_imaadpcm.h new file mode 100755 index 0000000..655ae8f --- /dev/null +++ b/src/fmod_codec_wav_imaadpcm.h @@ -0,0 +1,47 @@ +#ifndef _FORMAT_IMAADPCM_H +#define _FORMAT_IMAADPCM_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_IMAADPCM + +#include "fmod_codec_wav.h" + +namespace FMOD +{ + #ifndef WAVE_FORMAT_IMA_ADPCM + #define WAVE_FORMAT_IMA_ADPCM 0x0011 + #endif + #ifndef WAVE_FORMAT_XBOX_ADPCM + #define WAVE_FORMAT_XBOX_ADPCM 0x0069 + #endif + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + typedef struct + { + WAVE_FORMATEX wfx FMOD_PACKED_INTERNAL; + unsigned short wSamplesPerBlock FMOD_PACKED_INTERNAL; + } FMOD_PACKED WAVE_FORMAT_IMAADPCM; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + + + FMOD_RESULT IMAAdpcm_DecodeM16(unsigned char *pbSrc, signed short *pbDst, unsigned int cBlocks, unsigned int nBlockAlignment, unsigned int cSamplesPerBlock, int channels); + FMOD_RESULT IMAAdpcm_DecodeS16(unsigned char *pbSrc, signed short *pbDst, unsigned int cBlocks, unsigned int nBlockAlignment, unsigned int cSamplesPerBlock); +} + + +#endif /* FMOD_SUPPORT_IMAADPCM */ + +#endif /* _FORMAT_IMAADPCM_H */ + + diff --git a/src/fmod_codec_wav_riff.cpp b/src/fmod_codec_wav_riff.cpp new file mode 100755 index 0000000..54c5b31 --- /dev/null +++ b/src/fmod_codec_wav_riff.cpp @@ -0,0 +1,337 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_WAV) || defined(FMOD_SUPPORT_MPEG) + +#include "fmod_codec_wav.h" +#include "fmod_file.h" +#include "fmod_syncpoint.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecWav::parseChunk(unsigned int chunksize) +{ + unsigned int offset, fileoffset; + FMOD_RESULT result; + bool done = false; + + result = mFile->tell(&fileoffset); + if (result != FMOD_OK) + { + return result; + } + + offset = 4; + fileoffset -= sizeof(WAVE_CHUNK); + + /* + Decode chunks + */ + do + { + WAVE_CHUNK chunk; + + result = mFile->seek(fileoffset + sizeof(WAVE_CHUNK), SEEK_SET); + if (result != FMOD_OK) + { + break; + } + + result = mFile->read(&chunk, 1, sizeof(WAVE_CHUNK), 0); + if (result != FMOD_OK) + { + break; + } + + #ifdef PLATFORM_ENDIAN_BIG + chunk.size = FMOD_SWAPENDIAN_DWORD(chunk.size); + #endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::parseRIFF","chunk : id %c%c%c%c size %d\n", chunk.id[0],chunk.id[1],chunk.id[2],chunk.id[3], chunk.size)); + + /* + FORMAT CHUNK + */ + if (!FMOD_strncmp((const char *)chunk.id, "fmt ", 4)) + { + mSrcFormat = (WAVE_FORMATEXTENSIBLE *)FMOD_Memory_Calloc(chunk.size < sizeof(WAVE_FORMATEXTENSIBLE) ? sizeof(WAVE_FORMATEXTENSIBLE) : chunk.size); + if (!mSrcFormat) + { + return FMOD_ERR_MEMORY; + } + + result = mFile->read(mSrcFormat, 1, chunk.size, 0); + if (result != FMOD_OK) + { + break; + } + + #ifdef PLATFORM_ENDIAN_BIG + mSrcFormat->Format.wFormatTag = FMOD_SWAPENDIAN_WORD(mSrcFormat->Format.wFormatTag); + mSrcFormat->Format.nChannels = FMOD_SWAPENDIAN_WORD(mSrcFormat->Format.nChannels); + mSrcFormat->Format.nSamplesPerSec = FMOD_SWAPENDIAN_DWORD(mSrcFormat->Format.nSamplesPerSec); + mSrcFormat->Format.nAvgBytesPerSec = FMOD_SWAPENDIAN_DWORD(mSrcFormat->Format.nAvgBytesPerSec); + mSrcFormat->Format.nBlockAlign = FMOD_SWAPENDIAN_WORD(mSrcFormat->Format.nBlockAlign); + mSrcFormat->Format.wBitsPerSample = FMOD_SWAPENDIAN_WORD(mSrcFormat->Format.wBitsPerSample); + mSrcFormat->Format.cbSize = FMOD_SWAPENDIAN_WORD(mSrcFormat->Format.cbSize); + + mSrcFormat->Samples.wValidBitsPerSample = FMOD_SWAPENDIAN_WORD(mSrcFormat->Samples.wValidBitsPerSample); + mSrcFormat->dwChannelMask = FMOD_SWAPENDIAN_DWORD(mSrcFormat->dwChannelMask); + + if (mSrcFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + mSrcFormat->SubFormat.Data1 = FMOD_SWAPENDIAN_DWORD(mSrcFormat->SubFormat.Data1); + mSrcFormat->SubFormat.Data2 = FMOD_SWAPENDIAN_WORD(mSrcFormat->SubFormat.Data2); + mSrcFormat->SubFormat.Data3 = FMOD_SWAPENDIAN_WORD(mSrcFormat->SubFormat.Data3); + } + + #endif + + } + + /* + CUE CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "cue ", 4)) + { + result = mFile->read(&mNumSyncPoints, 4, 1, 0); + if (mNumSyncPoints) + { + int count; + + if (mSyncPoint) + { + FMOD_Memory_Free(mSyncPoint); + } + + mSyncPoint = (SyncPointNamed *)FMOD_Memory_Calloc(mNumSyncPoints * sizeof(SyncPointNamed)); + if (!mSyncPoint) + { + return FMOD_ERR_MEMORY; + } + + for (count=0; count < mNumSyncPoints; count++) + { + WAVE_CUEPOINT cue; + SyncPointNamed *point = &mSyncPoint[count]; + + result = mFile->read(&cue, 1, sizeof(WAVE_CUEPOINT), 0); + if (result != FMOD_OK) + { + break; + } + + point->mName = point->mNameMemory; + + #ifdef PLATFORM_ENDIAN_BIG + cue.dwSampleOffset = FMOD_SWAPENDIAN_DWORD(cue.dwSampleOffset); + cue.dwIdentifier = FMOD_SWAPENDIAN_DWORD(cue.dwIdentifier); + #endif + + point->mOffset = cue.dwSampleOffset; + point->mIndex = cue.dwIdentifier; + } + } + } + + /* + FACT CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "fact", 4)) + { + unsigned int fact; + + result = mFile->read(&fact, 4, 1, 0); + if (result != FMOD_OK) + { + break; + } + + waveformat[0].lengthpcm = fact; + } + + /* + LIST CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "LIST", 4)) + { + char listid[4]; + + result = mFile->read(listid, 1, 4, 0); + if (result != FMOD_OK) + { + break; + } + + result = parseChunk(chunk.size); + if (result != FMOD_OK) + { + break; + } + } + + /* + LABL CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "labl", 4)) + { + if (mSyncPoint) + { + int id, count; + + result = mFile->read(&id, 4, 1, 0); + if (result != FMOD_OK) + { + break; + } + + for (count = 0; count < mNumSyncPoints; count++) + { + SyncPoint *p = &mSyncPoint[count]; + + if (p->mIndex == id) + { + result = mFile->read(p->mName, 1, chunk.size-4); + if (result != FMOD_OK) + { + break; + } + break; + } + } + } + } + + /* + SAMPLER CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "smpl", 4)) + { + WAVE_SMPLHEADER smpl; + unsigned int rd; + + result = mFile->read(&smpl, 1, sizeof(WAVE_SMPLHEADER), &rd); + if (result != FMOD_OK) + { + break; + } + + if (rd == sizeof(WAVE_SMPLHEADER)) + { + #ifdef PLATFORM_ENDIAN_BIG + smpl.Manufacturer = FMOD_SWAPENDIAN_DWORD(smpl.Manufacturer); + smpl.Product = FMOD_SWAPENDIAN_DWORD(smpl.Product); + smpl.SamplePeriod = FMOD_SWAPENDIAN_DWORD(smpl.SamplePeriod); + smpl.Note = FMOD_SWAPENDIAN_DWORD(smpl.Note); + smpl.FineTune = FMOD_SWAPENDIAN_DWORD(smpl.FineTune); + smpl.SMPTEFormat = FMOD_SWAPENDIAN_DWORD(smpl.SMPTEFormat); + smpl.SMPTEOffset = FMOD_SWAPENDIAN_DWORD(smpl.SMPTEOffset); + smpl.Loops = FMOD_SWAPENDIAN_DWORD(smpl.Loops); + smpl.SamplerData = FMOD_SWAPENDIAN_DWORD(smpl.SamplerData); + + smpl.Loop.Identifier = FMOD_SWAPENDIAN_DWORD(smpl.Loop.Identifier); + smpl.Loop.Type = FMOD_SWAPENDIAN_DWORD(smpl.Loop.Type); + smpl.Loop.Start = FMOD_SWAPENDIAN_DWORD(smpl.Loop.Start); + smpl.Loop.End = FMOD_SWAPENDIAN_DWORD(smpl.Loop.End); + smpl.Loop.Fraction = FMOD_SWAPENDIAN_DWORD(smpl.Loop.Fraction); + smpl.Loop.Count = FMOD_SWAPENDIAN_DWORD(smpl.Loop.Count); + #endif + + if (smpl.Loops) + { + mLoopPoints[0] = smpl.Loop.Start; + mLoopPoints[1] = smpl.Loop.End; + } + } + } + + + /* + DATA CHUNK + */ + else if (!FMOD_strncmp((const char *)chunk.id, "data", 4)) + { + if (mSrcDataOffset == (unsigned int)-1) + { + waveformat[0].lengthbytes = chunk.size; + + result = mFile->tell(&mSrcDataOffset); + if (result != FMOD_OK) + { + break; + } + } + + if (mFile->mFlags & FMOD_FILE_SEEKABLE) + { + result = mFile->seek(chunk.size, SEEK_CUR); + if (result != FMOD_OK) + { + break; + } + } + else + { + done = true; + } + } + else + { + mFile->seek(chunk.size, SEEK_CUR); + if (result != FMOD_OK) + { + break; + } + } + + offset += (chunk.size+sizeof(WAVE_CHUNK)); + fileoffset += (chunk.size+sizeof(WAVE_CHUNK)); + + if (chunk.size & 1) + { + offset++; + fileoffset++; + } + + if (chunk.size < 0) + { + break; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecWav::parseRIFF", "offset = %d / %d\n", offset, chunksize)); + + } while (offset < chunksize && offset > 0 && !done); + + + /* + There is a data chunk, but we had a truncated wav.. we will still load what's there. + */ + if (result == FMOD_ERR_FILE_EOF) + { + result = FMOD_OK; + } + + + return result; +} + +} + +#endif + diff --git a/src/fmod_codec_xm.cpp b/src/fmod_codec_xm.cpp new file mode 100755 index 0000000..c0d327c --- /dev/null +++ b/src/fmod_codec_xm.cpp @@ -0,0 +1,3962 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_XM + +#include "fmod.h" + +#include "fmod_channel_software.h" +#include "fmod_codec_xm.h" +#include "fmod_debug.h" +#include "fmod_dspi.h" +#include "fmod_file.h" +#include "fmod_localcriticalsection.h" +#include "fmod_outputi.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_string.h" + +#include <stdio.h> +#include <stdlib.h> + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX xmcodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecXM::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecXM::getDescriptionEx() +{ + FMOD_memset(&xmcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + xmcodec.name = "FMOD XM Codec"; + xmcodec.version = 0x00010100; + xmcodec.timeunits = (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_MODORDER | FMOD_TIMEUNIT_MODROW | FMOD_TIMEUNIT_MODPATTERN); + xmcodec.defaultasstream = 1; + xmcodec.open = &CodecXM::openCallback; + xmcodec.close = &CodecXM::closeCallback; + xmcodec.read = &CodecXM::readCallback; + xmcodec.getlength = &MusicSong::getLengthCallback; + xmcodec.setposition = &CodecXM::setPositionCallback; + xmcodec.getposition = &MusicSong::getPositionCallback; + + xmcodec.getmusicnumchannels = &MusicSong::getMusicNumChannelsCallback; + xmcodec.setmusicchannelvolume = &MusicSong::setMusicChannelVolumeCallback; + xmcodec.getmusicchannelvolume = &MusicSong::getMusicChannelVolumeCallback; + xmcodec.gethardwaremusicchannel = &MusicSong::getHardwareMusicChannelCallback; + xmcodec.update = CodecXM::updateCallback; + + xmcodec.mType = FMOD_SOUND_TYPE_XM; + xmcodec.mSize = sizeof(CodecXM); + + return &xmcodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::calculateLength() +{ + waveformat[0].lengthpcm = 0; + + play(); + + while (!mFinished) + { + CodecXM::update(false); + + waveformat[0].lengthpcm += mMixerSamplesPerTick; + } + + stop(); + + return FMOD_OK; +} + + + +#ifdef FMOD_NO_FPU + #define FMUSIC_XMLINEARPERIOD2HZ(_per) (FMUSIC_LinearTable[(_per) % 768] >> ((_per) / 768)) +#else + #include <math.h> + /* + Frequency = 8363*2^((6*12*16*4 - Period) / (12*16*4)); + */ + #define FMUSIC_XMLINEARPERIOD2HZ(_per) ( (int)(8363.0f * FMOD_POW(2.0f, ((6.0f*12.0f*16.0f*4.0f - _per) / (float)(12*16*4)))) ) +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::spawnNewChannel(MusicChannel *cptr, MusicVirtualChannel *oldvcptr, MusicSample *sptr, MusicVirtualChannel **newvcptr) +{ + if (oldvcptr == &gDummyVirtualChannel) /* bugfix - fmod 3.11 - instrument starting with no channel was just ignoring settings. */ + { + spawnNewVirtualChannel(cptr, sptr, newvcptr); + } + else + { + *newvcptr = oldvcptr; + } + + if (!newvcptr) + { + *newvcptr = oldvcptr; +/* + newvcptr = &gDummyVirtualChannel; + newvcptr->channelid = -1; + newvcptr->cptr = &FMUSIC_DummyChannel; + newvcptr->sptr = &gDummySample; + newvcptr->cptr->sptr = &gDummySample; +*/ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::updateFlags(MusicChannel *cptr, MusicVirtualChannel *vcptr, MusicSample *sptr) +{ + if (!(vcptr->mFrequency + vcptr->mFrequencyDelta)) + { + vcptr->mNoteControl &= ~FMUSIC_FREQ; /* divide by 0 check */ + } + + if (vcptr->mNoteControl & FMUSIC_TRIGGER) + { + FMOD_RESULT result; + result = playSound(sptr, vcptr, false); + } + + if (vcptr->mNoteControl & FMUSIC_VOLUME) + { + #ifdef FMOD_NO_FPU + unsigned int finalvol; + + finalvol = (unsigned int)vcptr->mEnvVolume.mValue; /* 6 bits ( 64) */ + finalvol *= (unsigned int)(vcptr->mVolume + vcptr->mVolumeDelta); /* 6 bits ( 64) */ + finalvol *= (unsigned int)vcptr->mFadeOutVolume; /* 16 bits (65536) */ + finalvol >>= 11; + finalvol *= (unsigned int)mGlobalVolume; /* 6 bits ( 64) */ + finalvol >>= 15; + /* ============== */ + /* 34 bits */ + finalvol >>= 1; /* gain */ + + #else + + float finalvol; + + finalvol = (float)vcptr->mEnvVolume.mValue; /* 6 bits ( 64) */ + finalvol *= (vcptr->mVolume + vcptr->mVolumeDelta); /* 6 bits ( 64) */ + finalvol *= vcptr->mFadeOutVolume; /* 16 bits (65536) */ + finalvol *= mGlobalVolume; /* 6 bits ( 64) */ + /* ============== */ + /* 34 bits */ + + /* + Any half arsed compiler will convert this into 1 constant at compile time. + */ + finalvol *= (1.0f / (64.0f * 64.0f * 65536.0f * 64.0f) * 0.5f); + #endif + vcptr->mChannel.setVolume(finalvol * cptr->mMasterVolume); + } + if (vcptr->mNoteControl & FMUSIC_PAN) + { + int p; + float finalpan; + + p = vcptr->mPan - 128; + if (p < 0) + { + p = -p; + } + + finalpan = (float)(vcptr->mPan + ( (vcptr->mEnvPan.mValue-32) * ( (128-p)/32 ) )); + finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation; + + vcptr->mChannel.setPan(finalpan / 127.0f); + } + if (vcptr->mNoteControl & FMUSIC_FREQ) + { + int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta; + + if (finalfreq < 1) + { + finalfreq = 1; + } + + if (mMusicFlags & FMUSIC_XMFLAGS_LINEARFREQUENCY) + { + finalfreq = FMUSIC_XMLINEARPERIOD2HZ(finalfreq); + } + else + { + finalfreq = period2HZ(finalfreq); + } + + vcptr->mChannel.setFrequency((float)finalfreq); + } + if (vcptr->mNoteControl & FMUSIC_STOP) + { + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + #ifdef FMOD_SUPPORT_SOFTWARE + mSystem->flushDSPConnectionRequests(); + #endif + vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelXM::portamento() +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + /* + Slide pitch down if it needs too. + */ + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency += (int )mPortaSpeed << 2; + if (vcptr->mFrequency > mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + } + } + + /* + Slide pitch up if it needs too. + */ + else if (vcptr->mFrequency > mPortaTarget) + { + vcptr->mFrequency -= (int )mPortaSpeed << 2; + if (vcptr->mFrequency < mPortaTarget) + { + vcptr->mFrequency = mPortaTarget; + } + } + + /* + if (glissando[track]) + { + } + */ + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + to carry out a vibrato at a certain depth and speed + + [PARAMETERS] + track - the track number to do the vibrato too + + [RETURN_VALUE] + + [REMARKS] + AND'ing temp with 31 removes the sign bit giving the abs value + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelXM::vibrato() +{ + int delta; + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (unsigned char)(mVibPos & 31); + + switch (mWaveControl & 3) + { + case 0: delta = gSineTable[temp]; /* sine */ + break; + case 1: temp <<= 3; /* ramp down */ + if (mVibPos < 0) + { + temp=255-temp; + } + delta=temp; + break; + case 2: delta = 255; /* square */ + break; + case 3: delta = FMOD_RAND()&255; /* random */ + break; + default : + delta = 0; + break; + }; + + delta *= mVibDepth; + delta >>=7; + delta <<=2; /* we use 4*periods so make vibrato 4 times bigger */ + + if (mVibPos >= 0) + { + vcptr->mFrequencyDelta = -delta; + } + else + { + vcptr->mFrequencyDelta = delta; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + To carry out a tremolo at a certain depth and speed + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelXM::tremolo() +{ + unsigned char temp; + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + temp = (unsigned char)(mTremoloPosition & 31); + + switch ((mWaveControl>>4) & 3) + { + case 0: vcptr->mVolumeDelta = gSineTable[temp]; /* sine */ + break; + case 1: temp <<= 3; /* ramp down */ + if (mTremoloPosition < 0) + { + temp=255-temp; + } + vcptr->mVolumeDelta=temp; + break; + case 2: vcptr->mVolumeDelta = 255; /* square */ + break; + case 3: vcptr->mVolumeDelta = gSineTable[temp]; /* random (just use sine for now) */ + break; + }; + + vcptr->mVolumeDelta *= mTremoloDepth; + vcptr->mVolumeDelta >>= 6; + + if (mTremoloPosition >= 0) + { + if (vcptr->mVolume + vcptr->mVolumeDelta > 64) + { + vcptr->mVolumeDelta = 64-vcptr->mVolume; + } + } + else + { + if ((short)(vcptr->mVolume-vcptr->mVolumeDelta) < 0) + { + vcptr->mVolumeDelta = vcptr->mVolume; + } + vcptr->mVolumeDelta = -vcptr->mVolumeDelta; + } + + mTremoloPosition += mTremoloSpeed; + if (mTremoloPosition > 31) + { + mTremoloPosition -=64; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::processEnvelope(MusicEnvelopeState *env, MusicVirtualChannel *vcptr, int Inumpoints, unsigned short *points, int type, int loopstart, int loopend, unsigned char ISustain, unsigned char control) +{ + if (env->mPosition < Inumpoints) + { + if (!env->mTick || env->mTick == points[env->mPosition<<1]) /* if we are at the correct tick for the position */ + { + int currpos, nextpos; + int currtick, nexttick; + int currval, nextval, tickdiff; + + restartenv: + + /* + Handle loop + */ + if ((type & FMUSIC_ENVELOPE_LOOP) && env->mPosition == loopend) + { + env->mPosition = loopstart; + env->mTick = points[env->mPosition <<1]; + } + + currpos = env->mPosition; + nextpos = env->mPosition + 1; + + currtick = points[currpos<<1]; /* get tick at this point */ + nexttick = points[nextpos<<1]; /* get tick at next point */ + + currval = points[(currpos<<1)+1] << 16; /* get val at this point << 16 */ + nextval = points[(nextpos<<1)+1] << 16; /* get val at next point << 16 */ + + /* + If it is at the last position, abort the envelope and continue last val + */ + if (env->mPosition == Inumpoints - 1) + { + env->mValue = points[(currpos<<1)+1]; + env->mStopped = true; + vcptr->mNoteControl |= control; + return FMOD_OK; + } + + /* + Sustain + */ + if ((type & FMUSIC_ENVELOPE_SUSTAIN) && currpos == ISustain && !vcptr->mKeyOff) + { + env->mValue = points[(currpos<<1)+1]; + vcptr->mNoteControl |= control; + return FMOD_OK; + } + + /* + Interpolate 2 points to find delta step + */ + tickdiff = nexttick - currtick; + if (tickdiff) + { + env->mDelta = (nextval-currval) / tickdiff; + } + else + { + env->mDelta = 0; + } + + env->mFraction = currval; + + env->mPosition++; + + if (env->mTick == points[env->mPosition<<1] && env->mPosition < Inumpoints) + { + goto restartenv; + } + } + else + { + env->mFraction += env->mDelta; /* interpolate */ + } + } + + env->mValue = env->mFraction >> 16; + env->mTick++; + + vcptr->mNoteControl |= control; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + to carry out a vibrato at a certain depth and speed + + [PARAMETERS] + track - the track number to do the vibrato too + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelXM::instrumentVibrato(MusicInstrument *iptr) +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + int delta; + + switch (iptr->mVibratoType) + { + case 0: delta = (int)gFineSineTable[vcptr->mIVibPos]; /* sine */ + break; + case 1: if (vcptr->mIVibPos < 128) + { + delta=64; /* square */ + } + else + { + delta = -64; + } + break; + case 2: delta = (128-((vcptr->mIVibPos+128)%256))>>1; + break; + case 3: delta = (128-(((256-vcptr->mIVibPos)+128)%256))>>1; + break; + default : + delta = 0; + break; + }; + + delta *= iptr->mVibratoDepth; + if (iptr->mVibratoSweep) + { + delta = delta * vcptr->mIVibSweepPos / iptr->mVibratoSweep; + } + delta >>=6; + + vcptr->mFrequencyDelta += delta; + + vcptr->mIVibSweepPos++; + if (vcptr->mIVibSweepPos > iptr->mVibratoSweep) + { + vcptr->mIVibSweepPos = iptr->mVibratoSweep; + } + + vcptr->mIVibPos += iptr->mVibratoRate; + if (vcptr->mIVibPos > 255) + { + vcptr->mIVibPos -= 256; + } + + vcptr->mNoteControl |= FMUSIC_FREQ; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicChannelXM::processVolumeByte(unsigned char volume) +{ + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext(); + + if (volume >= 0x10 && volume <= 0x50) + { + vcptr->mVolume = volume-0x10; + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + + else + { + switch (volume >> 4) + { + case 0x6 : + { + vcptr->mVolume -= (volume & 0xF); + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case 0x7 : + { + vcptr->mVolume += (volume & 0xF); + if (vcptr->mVolume > 0x40) + { + vcptr->mVolume = 0x40; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case 0x8 : + { + vcptr->mVolume -= (volume & 0xF); + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case 0x9 : + { + vcptr->mVolume += (volume & 0xF); + if (vcptr->mVolume > 0x40) + { + vcptr->mVolume = 0x40; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case 0xa : + { + mVibSpeed = (volume & 0xF); + break; + } + case 0xb : + { + mVibDepth = (volume & 0xF); + break; + } + case 0xc : + { + vcptr->mPan = (volume & 0xF) << 4; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case 0xd : + { + vcptr->mPan -= (volume & 0xF); + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case 0xe : + { + vcptr->mPan += (volume & 0xF); + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case 0xf : + { + if (volume & 0xF) + { + mPortaSpeed = (volume & 0xF) << 4; + } + mPortaTarget = mPeriod; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + break; + } + }; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::getAmigaPeriod(int note, int finetune, int *period) +{ + *period = gPeriodTable[note]; + + /* + Interpolate for finer tuning + */ + if (finetune < 0 && note) + { + int diff = *period - gPeriodTable[note-1]; + diff *= -finetune; + diff /= 128; + *period -= diff; + } + else + { + int diff = gPeriodTable[note+1] - *period; + diff *= finetune; + diff /= 128; + *period += diff; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::processNote(MusicNote *current, MusicChannelXM *cptr, MusicVirtualChannel *vcptr, MusicInstrument *iptr, MusicSample *sptr) +{ + /* + PROCESS INSTRUMENT NUMBER + */ + if (current->mNumber) + { + vcptr->mVolume = sptr->mDefaultVolume; + vcptr->mPan = sptr->mDefaultPan; + + vcptr->mEnvVolume.mValue = 64; + vcptr->mEnvVolume.mPosition = 0; + vcptr->mEnvVolume.mTick = 0; + vcptr->mEnvVolume.mDelta = 0; + + vcptr->mEnvPan.mValue = 32; + vcptr->mEnvPan.mPosition = 0; + vcptr->mEnvPan.mTick = 0; + vcptr->mEnvPan.mDelta = 0; + + vcptr->mFadeOutVolume = 65536; + vcptr->mEnvVolume.mStopped = false; + vcptr->mEnvPan.mStopped = false; + vcptr->mKeyOff = false; + + vcptr->mIVibSweepPos = 0; + vcptr->mIVibPos = 0; + + /* + Retrigger tremolo and vibrato waveforms + */ + if ((cptr->mWaveControl & 0xF) < 4) + { + cptr->mVibPos=0; + } + if ((cptr->mWaveControl >> 4) < 4) + { + cptr->mTremoloPosition = 0; + } + cptr->mTremorPosition = 0; /* retrigger tremor count */ + + vcptr->mNoteControl |= FMUSIC_VOLUME; + vcptr->mNoteControl |= FMUSIC_PAN; + } + + /* + PROCESS VOLUME BYTE + */ + if (current->mVolume) + { + cptr->processVolumeByte(current->mVolume); + } + + /* + PROCESS KEY OFF + */ + if (current->mNote == FMUSIC_KEYOFF || current->mEffect == FMUSIC_XM_KEYOFF) + { + vcptr->mKeyOff = true; + } + + /* + PROCESS ENVELOPES + */ + if (iptr->mVolumeType & FMUSIC_ENVELOPE_ON) + { + if (!vcptr->mEnvVolume.mStopped) + { + processEnvelope(&vcptr->mEnvVolume, vcptr, iptr->mVolumeNumPoints, iptr->mVolumePoints, iptr->mVolumeType, iptr->mVolumeLoopStart, iptr->mVolumeLoopEnd, iptr->mVolumeSustain, FMUSIC_VOLUME); + } + } + else if (vcptr->mKeyOff) + { + vcptr->mEnvVolume.mValue = 0; + } + + if (iptr->mPanType & FMUSIC_ENVELOPE_ON && !vcptr->mEnvPan.mStopped) + { + processEnvelope(&vcptr->mEnvPan, vcptr, iptr->mPanNumPoints, iptr->mPanPoints, iptr->mPanType, iptr->mPanLoopStart, iptr->mPanLoopEnd, iptr->mPanSustain, FMUSIC_PAN); + } + + /* + PROCESS VOLUME FADEOUT + */ + if (vcptr->mKeyOff) + { + vcptr->mFadeOutVolume -= iptr->mVolumeFade; + if (vcptr->mFadeOutVolume < 0) + { + vcptr->mFadeOutVolume = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + + return FMOD_OK; +} +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::updateNote(bool audible) +{ + MusicNote *current; + bool breakflag = false; + bool jumpflag = false; + int count; + + /* + Point our note pointer to the correct pattern buffer, and to the + correct offset in this buffer indicated by row and number of channels + */ + current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels); + if (!current) + { + return FMOD_OK; + } + + if (mVisited) + { + if (mVisited[(mOrder * FMUSIC_MAXROWS) + mRow]) + { + mFinished = true; + return FMOD_OK; + } + mVisited[(mOrder * FMUSIC_MAXROWS) + mRow] = true; + } + + /* + Loop through each channel in the row until we have finished + */ + for (count = 0; count < mNumChannels; count++,current++) + { + MusicChannelXM *cptr = 0; + MusicVirtualChannel *vcptr = 0; + MusicSample *sptr = 0; + MusicInstrument *iptr = 0; + unsigned char paramx, paramy; + int oldvolume, oldfreq, oldpan; + bool porta = false; + + paramx = current->mEffectParam >> 4; /* get effect param x */ + paramy = current->mEffectParam & 0xF; /* get effect param y */ + + cptr = (MusicChannelXM *)mMusicChannel[count]; + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + porta = (current->mEffect == FMUSIC_XM_PORTATO || current->mEffect == FMUSIC_XM_PORTATOVOLSLIDE); + if (porta && vcptr == &gDummyVirtualChannel) + { + porta = false; /* Note hasn't been triggered yet */ + } + + /* first store note and instrument number if there was one */ + if (current->mNumber) + { + if (!porta) /* bugfix 3.20 (&& !porta) */ + { + cptr->mInstrument = current->mNumber-1; /* remember the Instrument # */ + } +// if (mInstCallback[current->mNumber] && mInstCallback[current->mNumber]->callback) +// { +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_INSTRUMENT, current->mNumber); +// } + } + + if (current->mNote && current->mNote != FMUSIC_KEYOFF && !porta) /* bugfix 3.20 (&& !porta) */ + { + cptr->mNote = current->mNote-1; /* remember the note */ + } + + if (cptr->mInstrument >= mNumInstruments) + { + iptr = &gDummyInstrument; + sptr = &gDummySample; + sptr->mSound = 0; + } + else + { + /* + Set up some instrument and sample pointers + */ + iptr = &mInstrument[cptr->mInstrument]; + if (iptr->mKeyMap[cptr->mNote] >= 16) + { + sptr = &gDummySample; + } + else + { + sptr = &iptr->mSample[iptr->mKeyMap[cptr->mNote]]; + } + + if (!porta) + { + vcptr->mSample = sptr; + } + } + + oldvolume = vcptr->mVolume; + oldfreq = vcptr->mFrequency; + oldpan = vcptr->mPan; + + /* + If there is no more tremolo, set volume to volume + last tremolo delta + */ + if (cptr->mRecentEffect == FMUSIC_XM_TREMOLO && current->mEffect != FMUSIC_XM_TREMOLO) + { + vcptr->mVolume += vcptr->mVolumeDelta; + } + cptr->mRecentEffect = current->mEffect; + + vcptr->mVolumeDelta = 0; + vcptr->mNoteControl = 0; + + /* + PROCESS NOTE + */ + if (current->mNote && current->mNote != FMUSIC_KEYOFF) + { + if (!porta || vcptr == &gDummyVirtualChannel) + { + spawnNewChannel(cptr, vcptr, sptr, &vcptr); + } + + if (!vcptr) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + vcptr->mSample = &gDummySample; + } + + /* + Get note according to mRelative note + */ + cptr->mRealNote = current->mNote + sptr->mRelative - 1; + + /* + Get period according to mRealNote and finetune + */ + if (mMusicFlags & FMUSIC_XMFLAGS_LINEARFREQUENCY) + { + cptr->mPeriod = (10*12*16*4) - (cptr->mRealNote*16*4) - (sptr->mFineTune / 2); + } + else + { + getAmigaPeriod(cptr->mRealNote, sptr->mFineTune, &cptr->mPeriod); + } + + /* + Frequency only changes if there are no portamento effects + */ + if (!(current->mEffect == FMUSIC_XM_PORTATO || current->mEffect == FMUSIC_XM_PORTATOVOLSLIDE)) + { + vcptr->mFrequency = cptr->mPeriod; + } + + vcptr->mNoteControl = FMUSIC_TRIGGER; + } + + vcptr->mFrequencyDelta = 0; + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mNoteControl |= FMUSIC_VOLUME; + + processNote(current, cptr, vcptr, iptr, sptr); + + /* + PROCESS TICK 0 EFFECTS + */ + switch (current->mEffect) + { + /* + Not processed on tick 0 + */ + case FMUSIC_XM_ARPEGGIO : + { + break; + } + case FMUSIC_XM_PORTAUP : + { + if (current->mEffectParam) + { + cptr->mPortaUp = current->mEffectParam; + } + break; + } + case FMUSIC_XM_PORTADOWN : + { + if (current->mEffectParam) + { + cptr->mPortaDown = current->mEffectParam; + } + break; + } + case FMUSIC_XM_PORTATO : + { + if (current->mEffectParam) + { + cptr->mPortaSpeed = current->mEffectParam; + } + cptr->mPortaTarget = cptr->mPeriod; + if (porta) + { + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + } + vcptr->mNoteControl &= ~FMUSIC_FREQ; + break; + } + case FMUSIC_XM_PORTATOVOLSLIDE : + { + cptr->mPortaTarget = cptr->mPeriod; + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + if (porta) + { + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + } + vcptr->mNoteControl &= ~FMUSIC_FREQ; + break; + } + case FMUSIC_XM_VIBRATO : + { + if (paramx) + { + cptr->mVibSpeed = paramx; + } + if (paramy) + { + cptr->mVibDepth = paramy; + } + cptr->vibrato(); + break; + } + case FMUSIC_XM_VIBRATOVOLSLIDE : + { + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + cptr->vibrato(); + break; /* not processed on tick 0 */ + } + case FMUSIC_XM_TREMOLO : + { + if (paramx) + { + cptr->mTremoloSpeed = paramx; + } + if (paramy) + { + cptr->mTremoloDepth = paramy; + } + break; + } + case FMUSIC_XM_SETPANPOSITION : + { + vcptr->mPan = current->mEffectParam; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_XM_SETSAMPLEOFFSET : + { + unsigned int offset; + + if (current->mEffectParam) + { + cptr->mSampleOffset = current->mEffectParam; + } + + offset = cptr->mSampleOffset << 8; + + if (offset >= sptr->mLoopStart + sptr->mLoopLength) + { + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + vcptr->mNoteControl |= FMUSIC_STOP; + } + else + { + vcptr->mSampleOffset = offset; + } + break; + } + case FMUSIC_XM_VOLUMESLIDE : + { + if (current->mEffectParam) + { + cptr->mVolumeSlide = current->mEffectParam; + } + break; + } + case FMUSIC_XM_PATTERNJUMP : /* --- 00 B00 : --- 00 D63 , should put us at ord=0, row=63 */ + { + mNextOrder = current->mEffectParam; + mNextRow = 0; + if (mNextOrder >= mNumOrders) + { + mNextOrder = 0; + mFinished = true; + } + jumpflag = 1; + break; + } + case FMUSIC_XM_SETVOLUME : + { + vcptr->mVolume = current->mEffectParam; + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_PATTERNBREAK : + { + mNextRow = (paramx*10) + paramy; + if (mNextRow > 63) + { + mNextRow = 0; + } + if (!breakflag && !jumpflag) + { + mNextOrder = mOrder+1; + } + if (mNextOrder >= mNumOrders) + { + mNextOrder=0; + } + break; + } + case FMUSIC_XM_SPECIAL : + { + switch (paramx) + { + /* + Not processed on tick 0 / unsupported + */ + case FMUSIC_XM_RETRIG : + case FMUSIC_XM_NOTECUT : + case FMUSIC_XM_SETFILTER : + case FMUSIC_XM_FUNKREPEAT : + case FMUSIC_XM_SETGLISSANDO : + { + break; + } + case FMUSIC_XM_FINEPORTAUP : + { + if (paramy) /* BUGFIX 3.30 - fineporta wasnt remembering parameter */ + { + cptr->mFinePortaUp = paramy; + } + vcptr->mFrequency -= (cptr->mFinePortaUp << 2); + break; + } + case FMUSIC_XM_FINEPORTADOWN : + { + if (paramy) /* BUGFIX 3.30 - fineporta wasnt remembering parameter */ + { + cptr->mFinePortaDown = paramy; + } + vcptr->mFrequency += (cptr->mFinePortaDown << 2); + break; + } + case FMUSIC_XM_SETVIBRATOWAVE : + { + cptr->mWaveControl &= 0xF0; + cptr->mWaveControl |= paramy; + break; + } + case FMUSIC_XM_SETFINETUNE : + { + sptr->mFineTune = paramy; + break; + } + case FMUSIC_XM_PATTERNLOOP : + { + if (paramy == 0) + { + cptr->mPatternLoopRow = mRow; + } + else + { + if (!cptr->mPatternLoopNumber) + { + cptr->mPatternLoopNumber = paramy; + } + else + { + cptr->mPatternLoopNumber--; + } + if (cptr->mPatternLoopNumber) + { + int count2; + + mNextRow = cptr->mPatternLoopRow; + + if (mVisited) + { + for (count2 = cptr->mPatternLoopRow; count2 <= mRow; count2++) + { + mVisited[(mOrder * FMUSIC_MAXROWS) + count2] = false; + } + } + } + } + break; + } + case FMUSIC_XM_SETTREMOLOWAVE : + { + cptr->mWaveControl &= 0xF; + cptr->mWaveControl |= (paramy<<4); + break; + } + case FMUSIC_XM_SETPANPOSITION16 : + { + vcptr->mPan = paramy<<4; + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_XM_FINEVOLUMESLIDEUP : + { + if (paramy) + { + cptr->mFineVolumeSlideUp = paramy; + } + + vcptr->mVolume += cptr->mFineVolumeSlideUp; + + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_FINEVOLUMESLIDEDOWN : + { + if (paramy) + { + cptr->mFineVolumeSlideUp = paramy; + } + + vcptr->mVolume -= cptr->mFineVolumeSlideUp; + + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_NOTEDELAY : + { + vcptr->mVolume = oldvolume; + vcptr->mFrequency = oldfreq; + vcptr->mPan = oldpan; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + vcptr->mNoteControl &= ~FMUSIC_PAN; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + break; + } + case FMUSIC_XM_PATTERNDELAY : + { + mPatternDelay = paramy; + mPatternDelay *= mSpeed; + break; + } + }; + break; + } + case FMUSIC_XM_SETSPEED : + { + if (current->mEffectParam < 0x20) + { + if (current->mEffectParam) + { + mSpeed = current->mEffectParam; + } + } + else + { + setBPM(current->mEffectParam); + } + break; + } + + case FMUSIC_XM_SETGLOBALVOLUME : + { + mGlobalVolume = current->mEffectParam; + if (mGlobalVolume > 64) + { + mGlobalVolume=64; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_GLOBALVOLSLIDE : + { + if (current->mEffectParam) + { + mGlobalVolumeSlide = current->mEffectParam; + } + break; + } + case FMUSIC_XM_SETENVELOPEPOS : + { + int currpos, nextpos; + int currtick, nexttick; + int currvol, nextvol, tickdiff; + + if (!(iptr->mVolumeType & FMUSIC_ENVELOPE_ON)) + { + break; + } + + currpos = 0; + + /* + Search and reinterpolate new envelope position + */ + while (current->mEffectParam > iptr->mVolumePoints[(currpos+1)<<1] && + currpos < iptr->mVolumeNumPoints) + { + currpos++; + } + + vcptr->mEnvVolume.mPosition = currpos; + + /* + If it is at the last position, abort the envelope and continue last volume + */ + if (vcptr->mEnvVolume.mPosition >= iptr->mVolumeNumPoints - 1) + { + vcptr->mEnvVolume.mValue = iptr->mVolumePoints[((iptr->mVolumeNumPoints-1)<<1)+1]; + vcptr->mEnvVolume.mStopped = true; + break; + } + + vcptr->mEnvVolume.mStopped = false; + vcptr->mEnvVolume.mTick = current->mEffectParam; + + nextpos = vcptr->mEnvVolume.mPosition + 1; + + currtick = iptr->mVolumePoints[currpos<<1]; /* get tick at this point */ + nexttick = iptr->mVolumePoints[nextpos<<1]; /* get tick at next point */ + + currvol = iptr->mVolumePoints[(currpos<<1)+1] << 16; /* get VOL at this point << 16 */ + nextvol = iptr->mVolumePoints[(nextpos<<1)+1] << 16; /* get VOL at next point << 16 */ + + /* + Interpolate 2 points to find delta step + */ + tickdiff = nexttick - currtick; + if (tickdiff) vcptr->mEnvVolume.mDelta = (nextvol-currvol) / tickdiff; + else vcptr->mEnvVolume.mDelta = 0; + + tickdiff = vcptr->mEnvVolume.mTick - currtick; + + vcptr->mEnvVolume.mFraction = currvol + (vcptr->mEnvVolume.mDelta * tickdiff); + vcptr->mEnvVolume.mValue = vcptr->mEnvVolume.mFraction >> 16; + vcptr->mEnvVolume.mPosition++; + break; + } + case FMUSIC_XM_PANSLIDE : + { + if (current->mEffectParam) + { + cptr->mPanSlide = current->mEffectParam; + vcptr->mNoteControl |= FMUSIC_PAN; + } + break; + } + case FMUSIC_XM_MULTIRETRIG: + { + if (current->mEffectParam) + { + cptr->mRetrigX = paramx; + cptr->mRetrigY = paramy; + } + break; + } + case FMUSIC_XM_TREMOR : + { + if (current->mEffectParam) + { + cptr->mTremorOn = (paramx+1); + cptr->mTremorOff = (paramy+1); + } + if (cptr->mTremorPosition >= cptr->mTremorOn) + { + vcptr->mVolumeDelta = -vcptr->mVolume; + } + cptr->mTremorPosition++; + if (cptr->mTremorPosition >= (cptr->mTremorOn + cptr->mTremorOff)) + { + cptr->mTremorPosition = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_EXTRAFINEPORTA : + { + + if (paramx == 1) + { + if (paramy) + { + cptr->mXtraPortaUp = paramy; + } + vcptr->mFrequency -= cptr->mXtraPortaUp; + } + else if (paramx == 2) + { + if (paramy) + { + cptr->mXtraPortaDown = paramy; + } + vcptr->mFrequency += cptr->mXtraPortaDown; + } + break; + } + case FMUSIC_XM_Z : + { +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ZXX, current->mEffectParam); + break; + } + }; + + /* + INSTRUMENT VIBRATO + */ + cptr->instrumentVibrato(iptr); /* this gets added to previous freqdeltas */ + updateFlags(cptr, (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(), sptr); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::updateEffects() +{ + MusicNote *current; + int count; + + current = mPattern[mOrderList[mOrder]].mData + (mRow*mNumChannels); + + if (!current) + { + return FMOD_OK; + } + + for (count=0; count < mNumChannels; count++,current++) + { + MusicChannelXM *cptr = 0; + MusicVirtualChannel *vcptr = 0; + MusicInstrument *iptr = 0; + MusicSample *sptr = 0; + unsigned char effect, paramx, paramy; + + cptr = (MusicChannelXM *)mMusicChannel[count]; + + if (cptr->mVirtualChannelHead.isEmpty()) + { + vcptr = &gDummyVirtualChannel; /* no channels allocated yet */ + } + else + { + vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + } + + if (cptr->mInstrument >= mNumInstruments) + { + iptr = &gDummyInstrument; + sptr = &gDummySample; + sptr->mSound = 0; + } + else + { + iptr = &mInstrument[cptr->mInstrument]; + if (iptr->mKeyMap[cptr->mNote] >= 16) + { + sptr = &gDummySample; + } + else + { + sptr = &iptr->mSample[iptr->mKeyMap[cptr->mNote]]; + } + + if (!sptr) + { + sptr = &gDummySample; + } + } + + effect = current->mEffect; /* grab the effect number */ + paramx = current->mEffectParam >> 4; /* grab the effect parameter x */ + paramy = current->mEffectParam & 0xF; /* grab the effect parameter y */ + + vcptr->mVolumeDelta = 0; /* this is for tremolo / tremor etc */ + vcptr->mFrequencyDelta = 0; /* this is for vibrato / arpeggio etc */ + vcptr->mNoteControl = 0; + + /* + PROCESS ENVELOPES + */ + if (iptr->mVolumeType & FMUSIC_ENVELOPE_ON && !vcptr->mEnvVolume.mStopped) + { + processEnvelope(&vcptr->mEnvVolume, vcptr, iptr->mVolumeNumPoints, iptr->mVolumePoints, iptr->mVolumeType, iptr->mVolumeLoopStart, iptr->mVolumeLoopEnd, iptr->mVolumeSustain, FMUSIC_VOLUME); + } + if (iptr->mPanType & FMUSIC_ENVELOPE_ON && !vcptr->mEnvPan.mStopped) + { + processEnvelope(&vcptr->mEnvPan, vcptr, iptr->mPanNumPoints, iptr->mPanPoints, iptr->mPanType, iptr->mPanLoopStart, iptr->mPanLoopEnd, iptr->mPanSustain, FMUSIC_PAN); + } + + /* + PROCESS VOLUME FADEOUT + */ + if (vcptr->mKeyOff) + { + vcptr->mFadeOutVolume -= iptr->mVolumeFade; + if (vcptr->mFadeOutVolume < 0) + { + vcptr->mFadeOutVolume = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + + switch (current->mVolume >> 4) + { + case 0x6: + { + vcptr->mVolume -= (current->mVolume & 0xF); + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case 0x7 : + { + vcptr->mVolume += (current->mVolume & 0xF); + if (vcptr->mVolume > 0x40) + { + vcptr->mVolume = 0x40; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case 0xb : + { + cptr->mVibDepth = (current->mVolume & 0xF); + cptr->vibrato(); + cptr->mVibPos += cptr->mVibSpeed; + if (cptr->mVibPos > 31) + { + cptr->mVibPos -= 64; + } + break; + } + case 0xd : + { + vcptr->mPan -= (current->mVolume & 0xF); + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case 0xe : + { + vcptr->mPan += (current->mVolume & 0xF); + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case 0xf : + { + cptr->portamento(); + break; + } + }; + + + switch(effect) + { + case FMUSIC_XM_ARPEGGIO : + { + if (current->mEffectParam > 0) + { + switch (mTick % 3) + { + case 1: + { + if (mMusicFlags & FMUSIC_XMFLAGS_LINEARFREQUENCY) + { + vcptr->mFrequencyDelta = -paramx << 6; + } + else + { + int per1, per2; + + getAmigaPeriod(cptr->mRealNote + paramx, sptr->mFineTune, &per1); + getAmigaPeriod(cptr->mRealNote, sptr->mFineTune, &per2); + + vcptr->mFrequencyDelta = per1 - per2; + } + break; + } + case 2: + { + if (mMusicFlags & FMUSIC_XMFLAGS_LINEARFREQUENCY) + { + vcptr->mFrequencyDelta = -paramy << 6; + } + else + { + int per1, per2; + + getAmigaPeriod(cptr->mRealNote + paramy, sptr->mFineTune, &per1); + getAmigaPeriod(cptr->mRealNote, sptr->mFineTune, &per2); + + vcptr->mFrequencyDelta = per1 - per2; + } + break; + } + }; + vcptr->mNoteControl |= FMUSIC_FREQ; + } + break; + } + case FMUSIC_XM_PORTAUP : + { + vcptr->mFrequencyDelta = 0; + + vcptr->mFrequency -= cptr->mPortaUp << 2; /* subtract freq */ + if (vcptr->mFrequency < 56) + { + vcptr->mFrequency=56; /* stop at B#8 */ + } + vcptr->mNoteControl |= FMUSIC_FREQ; + break; + } + case FMUSIC_XM_PORTADOWN : + { + vcptr->mFrequencyDelta = 0; + + vcptr->mFrequency += cptr->mPortaDown << 2; /* subtract freq */ + vcptr->mNoteControl |= FMUSIC_FREQ; + break; + } + case FMUSIC_XM_PORTATO : + { + vcptr->mFrequencyDelta = 0; + + cptr->portamento(); + break; + } + case FMUSIC_XM_VIBRATO : + { + cptr->vibrato(); + cptr->mVibPos += cptr->mVibSpeed; + if (cptr->mVibPos > 31) + { + cptr->mVibPos -= 64; + } + break; + } + case FMUSIC_XM_PORTATOVOLSLIDE : + { + vcptr->mFrequencyDelta = 0; + + cptr->portamento(); + + paramx = cptr->mVolumeSlide >> 4; /* grab the effect parameter x */ + paramy = cptr->mVolumeSlide & 0xF; /* grab the effect parameter y */ + + /* + Slide up takes precedence over down + */ + if (paramx) + { + vcptr->mVolume += paramx; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + } + else if (paramy) + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_VIBRATOVOLSLIDE : + { + cptr->vibrato(); + cptr->mVibPos += cptr->mVibSpeed; + if (cptr->mVibPos > 31) + { + cptr->mVibPos -= 64; + } + + paramx = cptr->mVolumeSlide >> 4; /* grab the effect parameter x */ + paramy = cptr->mVolumeSlide & 0xF; /* grab the effect parameter y */ + + /* + Slide up takes precedence over down + */ + if (paramx) + { + vcptr->mVolume += paramx; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + } + else if (paramy) + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + case FMUSIC_XM_TREMOLO : + { + cptr->tremolo(); + break; + } + case FMUSIC_XM_VOLUMESLIDE : + { + paramx = cptr->mVolumeSlide >> 4; /* grab the effect parameter x */ + paramy = cptr->mVolumeSlide & 0xF; /* grab the effect parameter y */ + + /* + Slide up takes precedence over down + */ + if (paramx) + { + vcptr->mVolume += paramx; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + } + else if (paramy) + { + vcptr->mVolume -= paramy; + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + + /* + Extended PT effects + */ + case FMUSIC_XM_SPECIAL: + { + switch (paramx) + { + case FMUSIC_XM_NOTECUT: + { + if (mTick==paramy) + { + vcptr->mVolume = 0; + vcptr->mNoteControl |= FMUSIC_VOLUME; + } + break; + } + case FMUSIC_XM_RETRIG : + { + if (!paramy) + { + break; /* divide by 0 bugfix */ + } + if (!(mTick % paramy)) + { + vcptr->mNoteControl |= FMUSIC_TRIGGER; + vcptr->mNoteControl |= FMUSIC_VOLUME; + vcptr->mNoteControl |= FMUSIC_FREQ; + } + break; + } + case FMUSIC_XM_NOTEDELAY : + { + if (mTick == paramy) + { + spawnNewChannel(cptr, vcptr, sptr, &vcptr); + + vcptr->mFrequency = cptr->mPeriod; + vcptr->mNoteControl |= FMUSIC_FREQ; + vcptr->mNoteControl |= FMUSIC_TRIGGER; + + processNote(current, cptr, vcptr, iptr, sptr); + } + else + { + vcptr->mNoteControl &= ~FMUSIC_VOLUME; + vcptr->mNoteControl &= ~FMUSIC_FREQ; + vcptr->mNoteControl &= ~FMUSIC_PAN; + vcptr->mNoteControl &= ~FMUSIC_TRIGGER; + } + break; + } + }; + break; + } + + case FMUSIC_XM_MULTIRETRIG : + { + if (!cptr->mRetrigY) + { + break; /* divide by 0 bugfix */ + } + + if (!(mTick % cptr->mRetrigY)) + { + if (cptr->mRetrigX) + { + switch (cptr->mRetrigX) + { + case 1: vcptr->mVolume--; + break; + case 2: vcptr->mVolume -= 2; + break; + case 3: vcptr->mVolume -= 4; + break; + case 4: vcptr->mVolume -= 8; + break; + case 5: vcptr->mVolume -= 16; + break; + case 6: vcptr->mVolume = vcptr->mVolume * 2 / 3; + break; + case 7: vcptr->mVolume >>= 1; + break; + case 8: /* ? */ + break; + case 9: vcptr->mVolume++; + break; + case 0xA: vcptr->mVolume += 2; + break; + case 0xB: vcptr->mVolume += 4; + break; + case 0xC: vcptr->mVolume += 8; + break; + case 0xD: vcptr->mVolume += 16; + break; + case 0xE: vcptr->mVolume = vcptr->mVolume * 3 / 2; + break; + case 0xF: vcptr->mVolume <<= 1; + break; + }; + if (vcptr->mVolume > 64) + { + vcptr->mVolume = 64; + } + if (vcptr->mVolume < 0) + { + vcptr->mVolume = 0; + } + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + vcptr->mNoteControl |= FMUSIC_TRIGGER; + } + break; + } + case FMUSIC_XM_GLOBALVOLSLIDE : + { + paramx = mGlobalVolumeSlide >> 4; /* grab the effect parameter x */ + paramy = mGlobalVolumeSlide & 0xF; /* grab the effect parameter y */ + + /* + Slide up takes precedence over down + */ + if (paramx) + { + mGlobalVolume += paramx; + if (mGlobalVolume > 64) + { + mGlobalVolume = 64; + } + } + else if (paramy) + { + mGlobalVolume -= paramy; + if (mGlobalVolume < 0) + { + mGlobalVolume = 0; + } + } + break; + } + case FMUSIC_XM_PANSLIDE : + { + paramx = cptr->mPanSlide >> 4; /* grab the effect parameter x */ + paramy = cptr->mPanSlide & 0xF; /* grab the effect parameter y */ + + /* + Slide right takes precedence over left + */ + if (paramx) + { + vcptr->mPan += paramx; + if (vcptr->mPan > 255) + { + vcptr->mPan = 255; + } + } + else if (paramy) + { + vcptr->mPan -= paramy; + if (vcptr->mPan < 0) + { + vcptr->mPan = 0; + } + } + + vcptr->mNoteControl |= FMUSIC_PAN; + break; + } + case FMUSIC_XM_TREMOR : + { + if (cptr->mTremorPosition >= cptr->mTremorOn) + { + vcptr->mVolumeDelta = -vcptr->mVolume; + } + cptr->mTremorPosition++; + if (cptr->mTremorPosition >= cptr->mTremorOn + cptr->mTremorOff) + { + cptr->mTremorPosition = 0; + } + vcptr->mNoteControl |= FMUSIC_VOLUME; + break; + } + }; + + /* + INSTRUMENT VIBRATO + */ + cptr->instrumentVibrato(iptr); /* this gets added to previous freqdeltas */ + updateFlags(cptr, (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(), sptr); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::update(bool audible) +{ + //FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecXM::update", "tick %d row %d\n", mTick, mRow)); + + if (mTick == 0) /* new note */ + { + if (mFinished && !mLooping) + { + stop(); + } + else + { + /* process any rows commands to set the next order/row */ + if (mNextOrder >= 0) + { + mOrder = mNextOrder; +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ORDER, (unsigned char)mOrder); + if (mNextOrder >= 0) + { + mOrder = mNextOrder; + } + mNextOrder = -1; + } + if (mNextRow >= 0) + { + mRow = mNextRow; +// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ROW, (unsigned char)mRow); + if (mNextRow >= 0) + { + mRow = mNextRow; + } + mNextRow = -1; + } + + updateNote(audible); /* Update and play the note */ + + /* + If there were no row commands + */ + if (mNextRow == -1) + { + mNextRow = mRow+1; + if (mNextRow >= mPattern[mOrderList[mOrder]].mRows) /* if end of pattern */ + { + mNextOrder = mOrder+1; /* so increment the order */ + if (mNextOrder >= mNumOrders) + { + mNextOrder = mRestart; + } + mNextRow = 0; /* start at top of pattern */ + } + } + } + } + else if (audible) + { + updateEffects(); /* Else update the inbetween row effects */ + } + + if (mSpeed) + { + mTick++; + if (mTick >= mSpeed + mPatternDelay) + { + mPatternDelay = 0; + mTick = 0; + } + } + else + { + mFinished = true; + } + + mPCMOffset += mMixerSamplesPerTick; + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_VAG + +static float EncodeVAG_f[5][2] = +{ + { 0.0f , 0.0f }, + { -60.0f / 64.0f, 0.0f }, + { -115.0f / 64.0f, 52.0f / 64.0f }, + { -98.0f / 64.0f, 55.0f / 64.0f }, + { -122.0f / 64.0f, 60.0f / 64.0f } +}; + +void CodecXM::EncodeVAG_FindPredict( short *samples, float *d_samples, int *predict_nr, int *shift_factor ) +{ + int i, j; + float buffer[28][5]; + float min = 1e10; + float max[5]; + float ds; + int min2; + int shift_mask; + static float _s_1 = 0.0f; // s[t-1] + static float _s_2 = 0.0f; // s[t-2] + float s_0, s_1, s_2; + + for ( i = 0; i < 5; i++ ) + { + max[i] = 0.0f; + s_1 = _s_1; + s_2 = _s_2; + for ( j = 0; j < 28; j ++ ) + { + s_0 = (float) samples[j]; // s[t-0] + if ( s_0 > 30719.0f ) + s_0 = 30719.0f; + if ( s_0 < - 30720.0f ) + s_0 = -30720.0f; + ds = s_0 + s_1 * EncodeVAG_f[i][0] + s_2 * EncodeVAG_f[i][1]; + buffer[j][i] = ds; + if ( FMOD_FABS( ds ) > max[i] ) + max[i] = FMOD_FABS( ds ); + s_2 = s_1; // new s[t-2] + s_1 = s_0; // new s[t-1] + } + + if ( max[i] < min ) + { + min = max[i]; + *predict_nr = i; + } + if ( min <= 7 ) + { + *predict_nr = 0; + break; + } + } + + // store s[t-2] and s[t-1] in a static variable + // these than used in the next function call + + _s_1 = s_1; + _s_2 = s_2; + + for ( i = 0; i < 28; i++ ) + d_samples[i] = buffer[i][*predict_nr]; + + min2 = ( int ) min; + shift_mask = 0x4000; + *shift_factor = 0; + + while( *shift_factor < 12 ) + { + if ( shift_mask & ( min2 + ( shift_mask >> 3 ) ) ) + break; + (*shift_factor)++; + shift_mask = shift_mask >> 1; + } + +} + +void CodecXM::EncodeVAG_pack( float *d_samples, short *four_bit, int predict_nr, int shift_factor ) +{ + float ds; + int di; + float s_0; + static float s_1 = 0.0f; + static float s_2 = 0.0f; + int i; + + for ( i = 0; i < 28; i++ ) + { + s_0 = d_samples[i] + s_1 * EncodeVAG_f[predict_nr][0] + s_2 * EncodeVAG_f[predict_nr][1]; + ds = s_0 * (float) ( 1 << shift_factor ); + + di = ( (int) ds + 0x800 ) & 0xfffff000; + + if ( di > 32767 ) + di = 32767; + if ( di < -32768 ) + di = -32768; + + four_bit[i] = (short) di; + + di = di >> shift_factor; + s_2 = s_1; + s_1 = (float) di - s_0; + } +} + +int CodecXM::EncodeVAG_pcm2vag(unsigned char *vag, short *wave, int sample_len, int wavebits) +{ + short *ptr; + float d_samples[28]; + short four_bit[28]; + short temp_samples[28]; + int predict_nr; + int shift_factor; + int flags=0; + int numframes, i, j, k; + unsigned char d; + unsigned char *vag_ptr = vag; + + sample_len = sample_len * 8 / wavebits; + + for( i = 0; i < 16; i++ ) + { + *vag_ptr = 0; vag_ptr++; + } + + numframes = sample_len / 28; + + if ( sample_len % 28 ) + { +// for ( j = sample_len % 28; j < 28; j++ ) +// wave[28*numframes+j] = 0; + numframes++; + } + + for ( j = 0; j < numframes; j++ ) + { + if (wavebits == 8) + { + int count; + signed char *ptr8 = (signed char *)wave + j * 28; + + for (count = 0; count < 28; count++) + { + temp_samples[count] = ptr8[count] << 8; + } + ptr = temp_samples; + } + else + { + ptr = wave + j * 28; + } + + EncodeVAG_FindPredict( ptr, d_samples, &predict_nr, &shift_factor ); + EncodeVAG_pack( d_samples, four_bit, predict_nr, shift_factor ); + d = ( predict_nr << 4 ) | shift_factor; + + *vag_ptr++ = d; + *vag_ptr++ = flags; + + for ( k = 0; k < 28; k += 2 ) + { + d = ( ( four_bit[k+1] >> 8 ) & 0xf0 ) | ( ( four_bit[k] >> 12 ) & 0xf ); + *vag_ptr++ = d; + } + sample_len -= 28; + if ( sample_len < 28 ) + flags = 1; + } + + *vag_ptr++ = ( predict_nr << 4 ) | shift_factor; + *vag_ptr++ = 7; + + for ( i = 0; i < 14; i++ ) + { + *vag_ptr++ = 0; + } + + //FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecXM::EncodeVAG_pcm2vag", "encoded len = %d. numframes = %d\n", (vag_ptr-vag), numframes)); + + return (vag_ptr-vag); +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + unsigned short filenumpatterns=0; + int count; + unsigned int mainHDRsize, lengthbytes; + char str[256]; + unsigned char temp; + FMOD_RESULT result = FMOD_OK; + + if (!(mFile->mFlags & FMOD_FILE_SEEKABLE)) + { + return FMOD_ERR_FORMAT; + } + + init(FMOD_SOUND_TYPE_XM); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::openInternal", "attempting to open as XM..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + /* + Get size of file in bytes + */ + result = mFile->getSize(&lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + /* + Verify Format + */ + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(str, 1, 17, 0); + if (result != FMOD_OK) + { + return result; + } + + if (FMOD_strncmp((char *)str, "Extended Module: ", 17)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::openInternal", "'Extended Module: ' ID check failed [%c%c%c%c]\n", str[0], str[1], str[2], str[3])); + + return FMOD_ERR_FORMAT; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::openInternal", "Loading XM header\n")); + + /* + Set a few default values for this format + */ + for (count = 0; count < MUSIC_MAXCHANNELS; count++) + { + mMusicChannel[count] = 0; + } + mPattern = 0; + mPanSeparation = 1.0f; + mMasterSpeed = 1.0f; + mDefaultSpeed = 6; + mDefaultBPM = 125; + mDefaultGlobalVolume = 64; + mNumPatterns = 0; + mRestart = 0; + mNumSamples = 0; + + result = mFile->read(mSongName, 1, 20, 0); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->getByte(&temp); + if (result != FMOD_OK) + { + return result; + } + if (temp != 0x1a) + { + return FMOD_ERR_FORMAT; + } + + /* + Skip tracker name and version number + */ + result = mFile->seek(60, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mainHDRsize, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mNumOrders); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mRestart); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mNumChannels); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&filenumpatterns); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mNumInstruments); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mMusicFlags); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mDefaultSpeed); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&mDefaultBPM); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&mOrderList, 1, 256, 0); + if (result != FMOD_OK) + { + return result; + } + + result = metaData(FMOD_TAGTYPE_FMOD, "Number of channels", &mNumChannels, sizeof(mNumChannels), FMOD_TAGDATATYPE_INT, false); + if (result != FMOD_OK) + { + return result; + } + + /* + ALLOCATE MUSIC CHANNELS + */ + for (count = 0; count < mNumChannels; count++) + { + mMusicChannel[count] = FMOD_Object_Calloc(MusicChannelXM); + if (!mMusicChannel[count]) + { + return FMOD_ERR_MEMORY; + } + } + + /* + Seek to patterndata + */ + result = mFile->seek(60L+mainHDRsize, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + mNumPatterns = 0; + for (count=0; count<mNumOrders; count++) + { + if (mOrderList[count] >= mNumPatterns) + { + mNumPatterns = mOrderList[count]+1; + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::openInternal", "Loading XM patterns\n")); + + /* + Alloc pattern array (whatever is bigger.. filenumpatterns or mNumpatterns) + */ + mNumPatternsMem = (mNumPatterns > filenumpatterns ? mNumPatterns : filenumpatterns); + mPattern = (MusicPattern *)FMOD_Memory_Calloc(mNumPatternsMem * sizeof(MusicPattern)); + if (!mPattern) + { + return FMOD_ERR_MEMORY; + } + + /* + Unpack and read patterns + */ + for (count=0; count < filenumpatterns; count++) + { + MusicPattern *pptr; + unsigned short patternsize, rows; + unsigned int patternHDRsize; + + pptr = &mPattern[count]; + + result = mFile->read(&patternHDRsize, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); /* should be 0 */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&rows); + if (result != FMOD_OK) + { + return result; + } + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&patternsize); + if (result != FMOD_OK) + { + return result; + } + if (result != FMOD_OK) + { + return result; + } + + pptr->mRows = rows; + + /* + Allocate memory for pattern buffer + */ + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); + if (!pptr->mData) + { + return FMOD_ERR_MEMORY; + } + + if (patternsize > 0) + { + int count2; + MusicNote *nptr; + + nptr = pptr->mData; + + for (count2=0; count2< (pptr->mRows * mNumChannels); count2++) + { + unsigned char dat; + + result = mFile->getByte(&dat); + if (result != FMOD_OK) + { + return result; + } + if (dat & 0x80) + { + if (dat & 1) + { + result = mFile->getByte(&nptr->mNote); + if (result != FMOD_OK) + { + return result; + } + } + if (dat & 2) + { + result = mFile->getByte(&nptr->mNumber); + if (result != FMOD_OK) + { + return result; + } + } + if (dat & 4) + { + result = mFile->getByte(&nptr->mVolume); + if (result != FMOD_OK) + { + return result; + } + } + if (dat & 8) + { + result = mFile->getByte(&nptr->mEffect); + if (result != FMOD_OK) + { + return result; + } + } + if (dat & 16) + { + result = mFile->getByte(&nptr->mEffectParam); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + if (dat) + { + nptr->mNote = dat; + } + result = mFile->getByte(&nptr->mNumber); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&nptr->mVolume); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&nptr->mEffect); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&nptr->mEffectParam); + if (result != FMOD_OK) + { + return result; + } + } + if (nptr->mNote == 97) + { + nptr->mNote = FMUSIC_KEYOFF; + } + + if (nptr->mNumber > 0x80) + { + nptr->mNumber = 0; + } + + nptr++; + } + } + } + + /* + Allocate and clean out any extra patterns + */ + if (mNumPatterns > filenumpatterns) + { + for (count=filenumpatterns; count < mNumPatterns; count++) + { + MusicPattern *pptr; + + pptr = &mPattern[count]; + pptr->mRows = 64; + + /* + Allocate memory for pattern buffer + */ + pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote)); + if (!pptr->mData) + { + return FMOD_ERR_MEMORY; + } + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::openInternal", "Loading XM instruments\n")); + + /* + Alloc instrument array + */ + mInstrument = (MusicInstrument *)FMOD_Memory_Calloc((mNumInstruments) * sizeof(MusicInstrument)); + if (!mInstrument) + { + return FMOD_ERR_MEMORY; + } + + /* + Load instrument information + */ + for (count = 0; count < mNumInstruments; count++) + { + unsigned int count2; + MusicInstrument *iptr; + unsigned int instHDRsize; + unsigned short numsamples; + unsigned char *buff = 0; + unsigned int largestsample = 0; + unsigned int firstsampleoffset; + + /* + Point a pointer to that particular instrument + */ + iptr = &mInstrument[count]; + + mFile->tell(&firstsampleoffset); + + result = mFile->read(&instHDRsize, 4, 1, 0); /* instrument size */ + if (result != FMOD_OK) + { + break; + } + firstsampleoffset += instHDRsize; + + result = mFile->read(iptr->mName, 1, 22, 0); /* instrument name */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&numsamples); + if (result != FMOD_OK) + { + return result; + } + + iptr->mNumSamples = numsamples; + + if (numsamples > 0) + { + unsigned int sampHDRsize; + + result = mFile->read(&sampHDRsize, 4, 1, 0); /* sampleheader size */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(iptr->mKeyMap, 1, 96, 0); /* sample numbers */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(iptr->mVolumePoints, 2, 24, 0); /* Volume Envelope (24 words) */ + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(iptr->mPanPoints, 2, 24, 0); /* Panning Envelope (24 words) */ + if (result != FMOD_OK) + { + return result; + } + + result = mFile->getByte(&iptr->mVolumeNumPoints); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanNumPoints); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeSustain); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeLoopStart); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeLoopEnd); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanSustain ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanLoopStart ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanLoopEnd ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVolumeType ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mPanType ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVibratoType ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVibratoSweep ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVibratoDepth ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&iptr->mVibratoRate ); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getWord(&iptr->mVolumeFade); + if (result != FMOD_OK) + { + return result; + } + iptr->mVolumeFade *= 2; /* i DONT KNOW why i needed this.. it just made it work */ + + if (iptr->mVolumeNumPoints < 2) + { + iptr->mVolumeType = FMUSIC_ENVELOPE_OFF; + } + if (iptr->mPanNumPoints < 2) + { + iptr->mPanType = FMUSIC_ENVELOPE_OFF; + } + + /* + Seek to first sample + */ + result = mFile->seek(firstsampleoffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::openInternal", "Loading XM Instrument %s. %d samples\n", iptr->mName, numsamples)); + + for (count2 = 0; count2 < numsamples; count2++) + { + MusicSample *sptr; + char sample_name[22]; + FMOD_MODE sample_mode; + unsigned int sample_length; + int sample_channels; + unsigned char dat; + + sptr = &iptr->mSample[count2]; + FMOD_memset(sptr, 0, sizeof(MusicSample)); + mNumSamples++; + + result = mFile->read(&sample_length, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mLoopStart, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + result = mFile->read(&sptr->mLoopLength, 4, 1, 0); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mDefaultVolume); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mFineTune); + if (result != FMOD_OK) + { + return result; + } + + /* + Type of sample + */ + sample_mode = FMOD_SOFTWARE | FMOD_2D; + sample_channels = 1; + sptr->mOriginalFormat = FMOD_SOUND_FORMAT_PCM8; + + result = mFile->getByte(&dat); + if (result != FMOD_OK) + { + return result; + } + if (dat & 1) + { + sample_mode |= FMOD_LOOP_NORMAL; + } + if (dat & 2) + { + sample_mode &= ~FMOD_LOOP_NORMAL; + sample_mode |= FMOD_LOOP_BIDI; + } + if (dat & 16) + { + sptr->mOriginalFormat = FMOD_SOUND_FORMAT_PCM16; + } + if (dat & 32) + { + sample_channels = 2; + } + + if (!(sample_mode & FMOD_LOOP_NORMAL) && !(sample_mode & FMOD_LOOP_BIDI)) + { + sptr->mLoopStart = 0; + sptr->mLoopLength = sample_length; + } + + if (!sptr->mLoopLength) + { + sptr->mLoopStart = 0; + sptr->mLoopLength = sample_length; + + sample_mode &= ~FMOD_LOOP_NORMAL; + sample_mode &= ~FMOD_LOOP_BIDI; + sample_mode |= FMOD_LOOP_OFF; + } + + sptr->mRawLength = sample_length; + sptr->mLoopStart = sptr->mLoopStart / (sptr->mOriginalFormat == FMOD_SOUND_FORMAT_PCM8 ? 1 : 2) / sample_channels; + sptr->mLoopLength = sptr->mLoopLength / (sptr->mOriginalFormat == FMOD_SOUND_FORMAT_PCM8 ? 1 : 2) / sample_channels; + + result = mFile->getByte(&sptr->mDefaultPan); + if (result != FMOD_OK) + { + return result; + } + result = mFile->getByte(&sptr->mRelative); + if (result != FMOD_OK) + { + return result; + } + + /* reserved */ + result = mFile->getByte(); + if (result != FMOD_OK) + { + return result; + } + + /* sample name */ + result = mFile->read(sample_name, 1, 22, 0); + if (result != FMOD_OK) + { + return result; + } + + { + char s[256]; + + sprintf(s, "Sample name %d", count); + + result = metaData(FMOD_TAGTYPE_FMOD, s, sample_name, 28, FMOD_TAGDATATYPE_STRING, false); + if (result != FMOD_OK) + { + return result; + } + } + + + /* + ALLOCATE MEMORY FOR THE SAMPLE BUFFER + */ + if (sample_length) + { + FMOD_CREATESOUNDEXINFO exinfo; + + FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.length = sample_length; + exinfo.numchannels = 1; + exinfo.defaultfrequency = 44100; + exinfo.format = sptr->mOriginalFormat; + + #ifdef FMOD_SUPPORT_HARDWAREXM + if (usermode & FMOD_HARDWARE) + { + sample_mode &= ~FMOD_SOFTWARE; + sample_mode |= FMOD_HARDWARE; + + #if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + exinfo.format = FMOD_SOUND_FORMAT_VAG; + + if (sample_mode & FMOD_LOOP_BIDI) + { + exinfo.length <<= 1; /* fake bidi by doubling length and flipping 2nd half. */ + } + + if (sptr->mOriginalFormat == FMOD_SOUND_FORMAT_PCM16) + { + exinfo.length >>= 1; + } + + if (exinfo.length < 28) + { + exinfo.length = 28; + } + + exinfo.length /= 28; + exinfo.length *= 16; + + exinfo.length += 32; /* Extra few rows for end? */ + #endif + } + #endif + + result = mSystem->createSound(0, sample_mode | FMOD_OPENUSER | FMOD_OPENONLY, &exinfo, &sptr->mSound); + if (result != FMOD_OK) + { + return result; + } + + if (sample_mode & FMOD_LOOP_NORMAL || sample_mode & FMOD_LOOP_BIDI) + { + result = sptr->mSound->setLoopPoints(sptr->mLoopStart, FMOD_TIMEUNIT_PCM, sptr->mLoopStart + sptr->mLoopLength - 1, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { +// return result; + } + } + } + } + + /* + Clear out the rest of the samples + */ + for (;count2<16; count2++) + { + iptr->mSample[count2].mSound = 0; + } + + /* + Load sample data + */ + for (count2 = 0; count2 < numsamples; count2++) + { + MusicSample *sptr = &iptr->mSample[count2]; + unsigned int lenbytes = 0; + int channels; + + if (sptr->mSound) + { + lenbytes = sptr->mRawLength; + result = sptr->mSound->getFormat(0, 0, &channels, 0); + } + + if (lenbytes) + { + #ifdef FMOD_SUPPORT_HARDWAREXM + if (usermode & FMOD_HARDWARE && sptr->mSound->mMode & FMOD_LOOP_BIDI && (lenbytes * 2) > largestsample) + { + buff = (unsigned char *)FMOD_Memory_ReAlloc(buff, (lenbytes * 2) + 16 + 56); + if (!buff) + { + return FMOD_ERR_MEMORY; + } + largestsample = lenbytes * 2; + } + else + #endif + { + buff = (unsigned char *)FMOD_Memory_ReAlloc(buff, lenbytes + 16 + 56); + if (!buff) + { + return FMOD_ERR_MEMORY; + } + largestsample = lenbytes; + } + + result = mFile->read(buff, 1, lenbytes, 0); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + if (!FMOD_strncmp((char *)buff + 4, "OggS", 4)) + { + FMOD_MODE mode; + FMOD_CREATESOUNDEXINFO exinfo; + + FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.length = lenbytes; + exinfo.numchannels = sptr->mSound->mChannels; + exinfo.defaultfrequency = (int)sptr->mSound->mDefaultFrequency; + exinfo.format = sptr->mSound->mFormat; + + mode = sptr->mSound->mMode; + + sptr->mSound->release(); + sptr->mSound = 0; + + mode &= ~FMOD_OPENUSER; + mode &= ~FMOD_OPENONLY; + mode |= FMOD_OPENMEMORY; + + result = mSystem->createSound((char *)buff + 4, mode, &exinfo, &sptr->mSound); + if (result == FMOD_OK) + { + if (mode & FMOD_LOOP_NORMAL || mode & FMOD_LOOP_BIDI) + { + result = sptr->mSound->setLoopPoints(sptr->mLoopStart, FMOD_TIMEUNIT_PCM, sptr->mLoopStart + sptr->mLoopLength - 1, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + } + } + else + { + int oldval, newval; + unsigned int count3; + + #ifdef PLATFORM_ENDIAN_BIG + if (sptr->mOriginalFormat == FMOD_SOUND_FORMAT_PCM16) + { + unsigned int count3; + signed short *wptr = (signed short *)buff; + + for (count3=0; count3 < lenbytes >> 1; count3++) + { + wptr[count3] = FMOD_SWAPENDIAN_WORD(wptr[count3]); + } + } + #endif + + + oldval = 0; + if (sptr->mOriginalFormat == FMOD_SOUND_FORMAT_PCM8) + { + unsigned char *bptr = (unsigned char *)buff; + for (count3 = 0; count3 < lenbytes; count3++) + { + newval = *bptr + oldval; + *bptr = (signed char)newval; + oldval = newval; + bptr++; + } + + #ifdef FMOD_SUPPORT_HARDWAREXM + if (usermode & FMOD_HARDWARE) + { + int len = lenbytes; + + if (sptr->mSound->mMode & FMOD_LOOP_BIDI) + { + unsigned char *backptr = bptr - 1; + + for (count3 = 0; count3 < lenbytes>>1; count3++) + { + *bptr = *backptr; + bptr++; + backptr--; + } + + len <<= 1; + } + + #if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + void *tempbuff = FMOD_Memory_Calloc(sptr->mSound->mLengthBytes + 32); + if (!tempbuff) + { + FMOD_Memory_Free(buff); + return FMOD_ERR_MEMORY; + } + int wrote = EncodeVAG_pcm2vag((unsigned char *)tempbuff, (signed short *)buff, len - 32, 8); + + FMOD_memcpy(buff, tempbuff, wrote); + + FMOD_Memory_Free(tempbuff); + #endif + } + #endif + } + else if (sptr->mOriginalFormat == FMOD_SOUND_FORMAT_PCM16) + { + unsigned short *wptr = (unsigned short *)buff; + for (count3 = 0; count3 < lenbytes>>1; count3++) + { + newval = *wptr + oldval; + *wptr = (short)newval; + oldval = newval; + wptr++; + } + + #ifdef FMOD_SUPPORT_HARDWAREXM + if (usermode & FMOD_HARDWARE) + { + int len = lenbytes; + + if (sptr->mSound->mMode & FMOD_LOOP_BIDI) + { + unsigned short *backptr = wptr - 1; + + for (count3 = 0; count3 < lenbytes>>1; count3++) + { + *wptr = *backptr; + wptr++; + backptr--; + } + + len <<= 1; + } + + #if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + void *tempbuff = FMOD_Memory_Calloc(sptr->mSound->mLengthBytes); + if (!tempbuff) + { + FMOD_Memory_Free(buff); + return FMOD_ERR_MEMORY; + } + + int wrote = EncodeVAG_pcm2vag((unsigned char *)tempbuff, (signed short *)buff, len - 32, 16); + + FMOD_memcpy(buff, tempbuff, wrote); + + FMOD_Memory_Free(tempbuff); + #endif + } + #endif + } + + int towrite = sptr->mSound->mLengthBytes, offset = 0; + unsigned char *buffptr = buff; + do + { + void *ptr1, *ptr2; + unsigned int len1, len2; + + result = sptr->mSound->lock(offset, towrite, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "CodecXM::load", "copy %d bytes to %d\n", len1, offset)); + + FMOD_memcpy(ptr1, buffptr, len1); + + result = sptr->mSound->unlock(ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + return result; + } + + towrite -= len1; + offset += len1; + buffptr += len1; + + } while (towrite); + } + } + } + + if (buff) + { + FMOD_Memory_Free(buff); + } + } + else + { + /* + Clear out the rest of the samples + */ + for (count2=0; count2<16; count2++) + { + iptr->mSample[count2].mSound = 0; + } + + result = mFile->seek(firstsampleoffset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + } + } + + /* + Alloc sample array + */ + mSample = 0; + if (mNumSamples) + { + mSample = (MusicSample **)FMOD_Memory_Calloc(mNumSamples * sizeof(MusicSample *)); + if (!mSample) + { + return FMOD_ERR_MEMORY; + } + } + + /* + Set up the linear sample array by scanning instrument list and copying sample pointers into the list incrementally + */ + { + int numsamples=0,count2; + + for (count=0; count < mNumInstruments; count++) + { + MusicInstrument *iptr = &mInstrument[count]; + + for (count2=0; count2 < iptr->mNumSamples; count2++) + { + MusicSample *sptr = &iptr->mSample[count2]; + + mSample[numsamples] = sptr; + numsamples ++; + } + } + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + + waveformat = mWaveFormatMemory; + waveformat[0].lengthbytes = lengthbytes; + + /* + Set up general codec parameters. + */ + if (userexinfo && userexinfo->format != FMOD_SOUND_FORMAT_NONE) + { + waveformat[0].format = userexinfo->format; + } + #ifdef FMOD_SUPPORT_HARDWAREXM + if (usermode & FMOD_HARDWARE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_NONE; + waveformat[0].blockalign = 0; + waveformat[0].channels = 0; + mFlags |= FMOD_CODEC_HARDWAREMUSICVOICES; + } + #endif + #ifndef PLATFORM_PS3 + else if (usermode & FMOD_SOFTWARE) + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT; + } + #endif + else + { + waveformat[0].format = FMOD_SOUND_FORMAT_PCM16; + } + + waveformat[0].channels = 2; + FMOD_strncpy(waveformat[0].name, mSongName, FMOD_STRING_MAXNAMELEN); + + result = mSystem->getSoftwareFormat(&waveformat[0].frequency, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mSrcDataOffset = 0; + + SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, waveformat[0].channels, waveformat[0].format); + + /* + Create a head unit that software channels connect to. + */ + #ifdef FMOD_SUPPORT_HARDWAREXM + if (!(usermode & FMOD_HARDWARE)) + #endif + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD XM Target Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = waveformat[0].channels; + descriptionex.mFormat = waveformat[0].format; + descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + + result = mSystem->createDSP(&descriptionex, &mDSPHead); + if (result != FMOD_OK) + { + return result; + } + + mDSPHead->mDefaultFrequency = (float)waveformat[0].frequency; + } + + /* + Create a pool of virtual channels. + */ + { + mNumVirtualChannels = mNumChannels; + mVirtualChannel = (MusicVirtualChannel *)FMOD_Memory_Calloc(sizeof(MusicVirtualChannel) * mNumVirtualChannels); + if (!mVirtualChannel) + { + return FMOD_ERR_MEMORY; + } + for (count = 0; count < mNumVirtualChannels; count++) + { + new (&mVirtualChannel[count]) MusicVirtualChannel; + } + } + + /* + Create a pool of real channels. 2x the number of virtual channels for double buffer channel usage. + */ + #ifdef FMOD_SUPPORT_HARDWAREXM + if (usermode & FMOD_HARDWARE) + { + mChannelPool = mSystem->mOutput->mChannelPool; + mChannelPoolMemory = 0; + } + else + #endif + { + int numrealchannels = mNumVirtualChannels * 2; + + mChannelPoolMemory = mChannelPool = FMOD_Object_Calloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, 0, numrealchannels); + if (result != FMOD_OK) + { + return result; + } + + mChannelSoftware = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * numrealchannels); + if (!mChannelSoftware) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < numrealchannels; count++) + { + new (&mChannelSoftware[count]) ChannelSoftware; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannelSoftware[count], mDSPHead)); + CHECK_RESULT(mChannelSoftware[count].allowReverb(false)); + } + } + + if ((usermode & FMOD_ACCURATETIME) || (usermode & FMOD_CREATESAMPLE)) + { + mVisited = (bool *)FMOD_Memory_Calloc(sizeof(bool) * mNumOrders * FMUSIC_MAXROWS); + if (!mVisited) + { + return FMOD_ERR_MEMORY; + } + + calculateLength(); + } + else + { + mVisited = 0; + waveformat[0].lengthpcm = (unsigned int)-1; + } + + numsubsounds = 0; + + play(true); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::closeInternal() +{ + int count; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::closeInternal", "%d\n", __LINE__)); + + stop(); + + if (mChannelPoolMemory) + { + mChannelPoolMemory->release(); + mChannelPoolMemory = 0; + } + mChannelPool = 0; + + if (mDSPHead) + { + mDSPHead->release(); + mDSPHead = 0; + } + + if (mSample) + { + for (count = 0; count < mNumSamples; count++) + { + if (mSample[count] && mSample[count]->mSound) + { + mSample[count]->mSound->release(); + mSample[count]->mSound = 0; + mSample[count] = 0; + } + } + + FMOD_Memory_Free(mSample); + mSample = 0; + } + + if (mInstrument) + { + FMOD_Memory_Free(mInstrument); + mInstrument = 0; + } + + if (mVirtualChannel) + { + FMOD_Memory_Free(mVirtualChannel); + mVirtualChannel = 0; + } + + if (mChannelSoftware) + { + FMOD_Memory_Free(mChannelSoftware); + mChannelSoftware = 0; + } + + if (mPattern) + { + for (count = 0; count < mNumPatternsMem; count++) + { + if (mPattern[count].mData) + { + FMOD_Memory_Free(mPattern[count].mData); + mPattern[count].mData = 0; + } + } + + FMOD_Memory_Free(mPattern); + mPattern = 0; + } + + for (count = 0; count < mNumChannels; count++) + { + if (mMusicChannel[count]) + { + FMOD_Memory_Free(mMusicChannel[count]); + mMusicChannel[count] = 0; + } + } + + if (mVisited) + { + FMOD_Memory_Free(mVisited); + mVisited = 0; + } + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecXM::closeInternal", "done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int numsamples; + int numchannels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + numchannels = waveformat[0].channels; + + SoundI::getSamplesFromBytes(sizebytes, &numsamples, numchannels, waveformat[0].format); + + if (mPlaying && mMasterSpeed) + { + unsigned int mixedsofar = 0; + unsigned int mixedleft = mMixerSamplesLeft; + unsigned int samplestomix; + char *destptr; + + /* + Keep resetting the mix pointer to the beginning of this portion of the ring buffer + */ + destptr = (char *)buffer; + + while (mixedsofar < numsamples) + { + unsigned int read, bytes; + + if (!mixedleft) + { + result = CodecXM::update(true); + if (result != FMOD_OK) + { + return result; + } + + samplestomix = mMixerSamplesPerTick; + mixedleft = samplestomix; + } + else + { + samplestomix = mixedleft; + } + + if (mixedsofar + samplestomix > numsamples) + { + samplestomix = numsamples - mixedsofar; + } + + read = samplestomix; + + criticalsection.enter(); + if (buffer) + { + result = mDSPHead->read(destptr, &read, FMOD_SPEAKERMODE_STEREO_LINEAR, 2, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + mDSPTick++; + } + + SoundI::getBytesFromSamples(read, &bytes, numchannels, waveformat[0].format); + + + /* + Buffer returned from the DSP head execute may not be the one we sent in (it may be + one of the global buffers). Don't leave mDSPCrit until after we have copied data out + */ + criticalsection.leave(); + + mixedsofar += read; + destptr += bytes; + mixedleft -= read; + } + + mMixerSamplesLeft = mixedleft; + } + + if (bytesread) + { + *bytesread = sizebytes; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecXM::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + if (postype == FMOD_TIMEUNIT_MODORDER) + { + play(); + mNextOrder = mOrder = position; + + return FMOD_OK; + } + else if (postype == FMOD_TIMEUNIT_PCM) + { + bool restarted = false; + + if (position == mPCMOffset) + { + return FMOD_OK; + } + + /* + Want to seek backwards, start from the start. + */ + if (position < mPCMOffset) + { + play(); + restarted = true; + } + + while (mPCMOffset < position) + { + update(true); + } + + if (restarted) + { + bool oldplaying = mPlaying; + bool oldfinished = mFinished; + + stop(); + + mPlaying = oldplaying; + mFinished = oldfinished; + } + + return FMOD_OK; + } + + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecXM::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecXM *cxm = (CodecXM *)codec; + + return cxm->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecXM::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecXM *cxm = (CodecXM *)codec; + + return cxm->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecXM::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecXM *cxm = (CodecXM *)codec; + + return cxm->readInternal(buffer, sizebytes, bytesread); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecXM::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype ) +{ + CodecXM *cxm = (CodecXM *)codec; + + return cxm->setPositionInternal(subsound, position, postype); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecXM::updateCallback(FMOD_CODEC_STATE *codec) +{ + CodecXM *cxm = (CodecXM *)codec; + + return cxm->update(true); +} + +} + +#endif + + diff --git a/src/fmod_codec_xm.h b/src/fmod_codec_xm.h new file mode 100755 index 0000000..05cc561 --- /dev/null +++ b/src/fmod_codec_xm.h @@ -0,0 +1,172 @@ +#ifndef _FMOD_CODEC_XM_H +#define _FMOD_CODEC_XM_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_XM + +#include "fmod_music.h" + +namespace FMOD +{ + class DSPI; + class ChannelPool; + + enum FMUSIC_XMCOMMANDS + { + FMUSIC_XM_ARPEGGIO, + FMUSIC_XM_PORTAUP, + FMUSIC_XM_PORTADOWN, + FMUSIC_XM_PORTATO, + FMUSIC_XM_VIBRATO, + FMUSIC_XM_PORTATOVOLSLIDE, + FMUSIC_XM_VIBRATOVOLSLIDE, + FMUSIC_XM_TREMOLO, + FMUSIC_XM_SETPANPOSITION, + FMUSIC_XM_SETSAMPLEOFFSET, + FMUSIC_XM_VOLUMESLIDE, + FMUSIC_XM_PATTERNJUMP, + FMUSIC_XM_SETVOLUME, + FMUSIC_XM_PATTERNBREAK, + FMUSIC_XM_SPECIAL, + FMUSIC_XM_SETSPEED, + FMUSIC_XM_SETGLOBALVOLUME, + FMUSIC_XM_GLOBALVOLSLIDE, + FMUSIC_XM_I, + FMUSIC_XM_J, + FMUSIC_XM_KEYOFF, + FMUSIC_XM_SETENVELOPEPOS, + FMUSIC_XM_M, + FMUSIC_XM_N, + FMUSIC_XM_O, + FMUSIC_XM_PANSLIDE, + FMUSIC_XM_Q, + FMUSIC_XM_MULTIRETRIG, + FMUSIC_XM_S, + FMUSIC_XM_TREMOR, + FMUSIC_XM_U, + FMUSIC_XM_V, + FMUSIC_XM_W, + FMUSIC_XM_EXTRAFINEPORTA, + FMUSIC_XM_Y, + FMUSIC_XM_Z + }; + + + enum FMUSIC_XMCOMMANDSSPECIAL + { + FMUSIC_XM_SETFILTER, + FMUSIC_XM_FINEPORTAUP, + FMUSIC_XM_FINEPORTADOWN, + FMUSIC_XM_SETGLISSANDO, + FMUSIC_XM_SETVIBRATOWAVE, + FMUSIC_XM_SETFINETUNE, + FMUSIC_XM_PATTERNLOOP, + FMUSIC_XM_SETTREMOLOWAVE, + FMUSIC_XM_SETPANPOSITION16, + FMUSIC_XM_RETRIG, + FMUSIC_XM_FINEVOLUMESLIDEUP, + FMUSIC_XM_FINEVOLUMESLIDEDOWN, + FMUSIC_XM_NOTECUT, + FMUSIC_XM_NOTEDELAY, + FMUSIC_XM_PATTERNDELAY, + FMUSIC_XM_FUNKREPEAT + }; + + #define FMUSIC_XMFLAGS_LINEARFREQUENCY 1 + + typedef struct + { + unsigned int instSize; /* instrument size */ + signed char instName[22]; /* instrument filename */ + unsigned char instType; /* instrument type (now 0) */ + unsigned short numSamples; /* number of samples in instrument */ + } FMUSIC_XM_INSTHEADER; + + typedef struct + { + unsigned int headerSize; /* sample header size */ + unsigned char noteSmpNums[96]; /* sample numbers for notes */ + unsigned short volEnvelope[2*12]; /* volume envelope points */ + unsigned short panEnvelope[2*12]; /* panning envelope points */ + unsigned char numVolPoints; /* number of volume envelope points */ + unsigned char numPanPoints; /* number of panning env. points */ + unsigned char volSustain; /* volume sustain point */ + unsigned char volLoopStart; /* volume loop start point */ + unsigned char volLoopEnd; /* volume loop end point */ + unsigned char panSustain; /* panning sustain point */ + unsigned char panLoopStart; /* panning loop start point */ + unsigned char panLoopEnd; /* panning loop end point */ + unsigned char volEnvFlags; /* volume envelope flags */ + unsigned char panEnvFlags; /* panning envelope flags */ + + unsigned char vibType; /* vibrato type */ + unsigned char vibSweep; /* vibrato sweep */ + unsigned char vibDepth; /* vibrato depth */ + unsigned char vibRate; /* vibrato rate */ + unsigned short volFadeout; /* volume fadeout */ + unsigned short reserved; + } FMUSIC_XM_INSTSAMPLEHEADER; + + const int XM_MAXROWS = 256; + const int XM_MAXSAMPLES = 256; + + class MusicChannelXM : public MusicChannel + { + public: + FMOD_RESULT volumeSlide(); + FMOD_RESULT portamento(); + FMOD_RESULT vibrato(); + FMOD_RESULT tremolo(); + FMOD_RESULT fineVibrato(); + FMOD_RESULT processVolumeByte(unsigned char volume); + FMOD_RESULT instrumentVibrato(MusicInstrument *iptr); + }; + + class CodecXM : public MusicSong + { + private: + + MusicSample **mSample; + ChannelPool *mChannelPoolMemory; /* ChannelPool */ + + FMOD_RESULT calculateLength(); + + FMOD_RESULT updateNote(bool audible); + FMOD_RESULT updateEffects(); + FMOD_RESULT update(bool audible); + + FMOD_RESULT spawnNewChannel(MusicChannel *cptr, MusicVirtualChannel *oldvcptr, MusicSample *sptr, MusicVirtualChannel **newvcptr); + FMOD_RESULT processEnvelope(MusicEnvelopeState *env, MusicVirtualChannel *vcptr, int Inumpoints, unsigned short *points, int type, int loopstart, int loopend, unsigned char ISustain, unsigned char control); + FMOD_RESULT getAmigaPeriod(int note, int finetune, int *period); + FMOD_RESULT processNote(MusicNote *current, MusicChannelXM *cptr, MusicVirtualChannel *vcptr, MusicInstrument *iptr, MusicSample *sptr); + FMOD_RESULT updateFlags(MusicChannel *cptr, MusicVirtualChannel *vcptr, MusicSample *sptr); + +#ifdef FMOD_SUPPORT_VAG + void EncodeVAG_FindPredict( short *samples, float *d_samples, int *predict_nr, int *shift_factor ); + static void EncodeVAG_pack( float *d_samples, short *four_bit, int predict_nr, int shift_factor ); + int EncodeVAG_pcm2vag(unsigned char *vag, short *wave, int sample_len, int wavebits); +#endif + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK updateCallback(FMOD_CODEC_STATE *codec); + }; +} + +#endif /* FMOD_SUPPORT_XM */ + +#endif + + diff --git a/src/fmod_codeci.h b/src/fmod_codeci.h new file mode 100755 index 0000000..6ea2dc9 --- /dev/null +++ b/src/fmod_codeci.h @@ -0,0 +1,216 @@ +#ifndef _FMOD_CODECI_H +#define _FMOD_CODECI_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_file_memory.h" +#include "fmod_memory.h" +#include "fmod_os_misc.h" +#include "fmod_plugin.h" + +#include <stdio.h> + +namespace FMOD +{ + class Metadata; + class ChannelReal; + class MemoryTracker; + + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_INITCALLBACK) (FMOD_CODEC_STATE *codec, int numstreams); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_RESETCALLBACK)(FMOD_CODEC_STATE *codec); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_CANPOINTCALLBACK)(FMOD_CODEC_STATE *codec); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETMUSICNUMCHANNELS)(FMOD_CODEC_STATE *codec, int *numchannels); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_SETMUSICCHANNELVOLUME)(FMOD_CODEC_STATE *codec, int channel, float volume); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETMUSICCHANNELVOLUME)(FMOD_CODEC_STATE *codec, int channel, float *volume); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETHARDWAREMUSICHANNEL)(FMOD_CODEC_STATE *codec, ChannelReal **realchannel); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_UPDATE)(FMOD_CODEC_STATE *codec); + typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETMEMORYUSED)(FMOD_CODEC_STATE *codec, MemoryTracker *tracker); + + struct FMOD_CODEC_DESCRIPTION_EX : public FMOD_CODEC_DESCRIPTION, public SortedLinkedListNode + { + FMOD_SOUND_TYPE mType; + int mSize; + FMOD_OS_LIBRARY *mModule; + unsigned int mHandle; + + FMOD_CODEC_INITCALLBACK init; + FMOD_CODEC_RESETCALLBACK reset; + FMOD_CODEC_CANPOINTCALLBACK canpoint; + FMOD_CODEC_GETMUSICNUMCHANNELS getmusicnumchannels; + FMOD_CODEC_SETMUSICCHANNELVOLUME setmusicchannelvolume; + FMOD_CODEC_GETMUSICCHANNELVOLUME getmusicchannelvolume; + FMOD_CODEC_GETHARDWAREMUSICHANNEL gethardwaremusicchannel; + FMOD_CODEC_UPDATE update; + FMOD_CODEC_GETMEMORYUSED getmemoryused; + }; + + + typedef unsigned int FMOD_CODEC_FLAG; + + #define FMOD_CODEC_ACCURATELENGTH 0x00000001 + #define FMOD_CODEC_FROMFSB 0x00000002 + #define FMOD_CODEC_SEEKING 0x00000004 + #define FMOD_CODEC_PADDED 0x00000008 + #define FMOD_CODEC_USERLENGTH 0x00000010 + #define FMOD_CODEC_HARDWAREMUSICVOICES 0x00000020 + #define FMOD_CODEC_FSBXMAMIXEDCHANNELS 0x00000040 + #define FMOD_CODEC_PADDED4 0x00000080 + + typedef struct + { + unsigned int offset; + char name[256]; + } SYNCDATA; + + typedef struct + { + unsigned int offset; + } SYNCDATA_NONAME; + + class ChannelReal; + + class Codec : public Plugin, public FMOD_CODEC_STATE + { + DECLARE_MEMORYTRACKER_NONVIRTUAL /* Codecs are plugins and go through mDescription callback interface. */ + + public: + + FMOD_CODEC_WAVEFORMAT *mWaveFormatMemory; /* NULL for FMOD_CREATECOMPRESSED codecs (they point to the sound's wave format), but alloced for each sound codec. */ + + FMOD_SOUND_TYPE mType; + FMOD_CODEC_DESCRIPTION_EX mDescription; + unsigned int mSrcDataOffset; + unsigned int mLoopPoints[2]; + int mSubSoundIndex; + FMOD_CODEC_FLAG mFlags; + unsigned int mBlockAlign; + + /* + Temporary buffer for reading compressed data into. Not used on all codecs. + */ + unsigned char *mReadBuffer; /* For reading one block worth of compressed data into */ + unsigned int mReadBufferLength; + + /* + Buffer Logic for decompressing chunks of data into a temporary PCM buffer; + */ + unsigned char *mPCMBuffer; /* For decompressing that one block of data into */ + unsigned char *mPCMBufferMemory; + unsigned int mPCMBufferLength; + unsigned int mPCMBufferLengthBytes; + unsigned int mPCMBufferOffsetBytes; + unsigned int mPCMBufferFilledBytes; + + /* + Stuff that is passed into the driver for internal use + */ + FMOD_MODE mMode; /* This mode may have been changed by createSound */ + FMOD_MODE mOriginalMode; /* Original mode as passed in to createSound by user */ + Metadata *mMetadata; + File *mFile; + bool mNonInterleaved; + + #ifdef FMOD_SUPPORT_XMA + bool mXMASeekable; + #endif + + static FMOD_INLINE FMOD_RESULT F_CALLBACK defaultFileRead (void *handle, void *buffer, unsigned int size, unsigned int *read, void *userdata) { return ((File *)handle)->read(buffer, 1, size, read); } + static FMOD_INLINE FMOD_RESULT F_CALLBACK defaultFileSeek (void *handle, unsigned int pos, void *userdata) { return ((File *)handle)->seek(pos, SEEK_SET); } +#ifndef PLATFORM_PS3_SPU + static FMOD_INLINE FMOD_RESULT F_CALLBACK defaultMetaData (FMOD_CODEC_STATE *codec, FMOD_TAGTYPE type, char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, int unique) { return ((Codec *)codec)->metaData(type, name, data, datalen, datatype, unique == 1 ? true : false); } +#endif + static FMOD_INLINE FMOD_RESULT F_CALLBACK defaultGetWaveFormat(FMOD_CODEC_STATE *codec, int index, FMOD_CODEC_WAVEFORMAT *waveformat_out) + { + if (!codec->waveformat) + { + return FMOD_ERR_INTERNAL; + } + + if (index < 0 || (codec->numsubsounds == 0 && index > 0) || (codec->numsubsounds > 0 && index >= codec->numsubsounds)) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(waveformat_out, &codec->waveformat[index], sizeof(FMOD_CODEC_WAVEFORMAT)); + + return FMOD_OK; + } + + public: + + Codec() + { + mType = FMOD_SOUND_TYPE_UNKNOWN; + mMetadata = 0; + fileread = &defaultFileRead; + fileseek = &defaultFileSeek; +#ifndef PLATFORM_PS3_SPU + metadata = &defaultMetaData; +#endif + } + +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT init(FMOD_SOUND_TYPE type) + { + Plugin::init(); + + mType = type; + mMetadata = 0; + numsubsounds = 0; + waveformat = 0; + + return FMOD_OK; + } + + FMOD_RESULT release(); + FMOD_RESULT getLength(unsigned int *length, FMOD_TIMEUNIT lengthtype); + FMOD_RESULT getPosition(unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT metaData(FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, bool unique); + FMOD_RESULT getMetadataFromFile(); + FMOD_RESULT canPointTo() + { + if (mDescription.canpoint) + { + return mDescription.canpoint(this); + } + else + { + return FMOD_ERR_MEMORY_CANTPOINT; + } + } +#endif + FMOD_RESULT reset() + { + mPCMBufferOffsetBytes = 0; + if (mPCMBuffer) + { + FMOD_memset(mPCMBuffer, 0, mPCMBufferLengthBytes); + } + if (mDescription.reset) + { + return mDescription.reset(this); + } + return FMOD_OK; + } + FMOD_RESULT read(void *buffer, unsigned int size, unsigned int *bytesread); + FMOD_RESULT setPosition(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getHardwareMusicChannel(ChannelReal **realchannel) + { + if (mDescription.gethardwaremusicchannel) + { + return mDescription.gethardwaremusicchannel(this, realchannel); + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + } + }; +} + +#endif + + diff --git a/src/fmod_debug.cpp b/src/fmod_debug.cpp new file mode 100755 index 0000000..8d5fbda --- /dev/null +++ b/src/fmod_debug.cpp @@ -0,0 +1,397 @@ +#include "fmod_settings.h" + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "fmod.h" +#include "fmod_debug.h" +#include "fmod_globals.h" +#include "fmod_os_misc.h" +#include "fmod_time.h" +#include "fmod_string.h" +#include "fmod_systemi.h" + +extern "C" +{ + +/* +[API] +[ + [DESCRIPTION] + Sets the level of debug logging to the tty / output for logging versions of FMOD Ex. + + [PARAMETERS] + 'level' Logging level to set. + + [RETURN_VALUE] + + [REMARKS] + This only has an effect with 'logging' versions of FMOD Ex. For example on windows it must be via fmodexL.dll, not fmodex.dll.<br> + On Xbox it would be fmodxboxL.lib not fmodxbox.lib.<br> + FMOD_ERR_UNSUPPORTED will be returned on non logging versions of FMOD Ex (ie full release).<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Debug_GetLevel + FMOD_DEBUGLEVEL +] +*/ +FMOD_RESULT F_API FMOD_Debug_SetLevel(FMOD_DEBUGLEVEL level) +{ + #ifdef FMOD_DEBUG + if (!FMOD::gGlobal) + { + #if !defined(FMOD_STATICFORPLUGINS) && !defined(PLATFORM_PS2_IOP) + FMOD::SystemI::getGlobals(&FMOD::gGlobal); + #else + return FMOD_ERR_UNINITIALIZED; + #endif + } + FMOD::gGlobal->gDebugLevel = (FMOD_DEBUGLEVEL)level; + return FMOD_OK; + #else + return FMOD_ERR_UNSUPPORTED; + #endif +} + +/* +[API] +[ + [DESCRIPTION] + Retrieves the current debug logging level. + + [PARAMETERS] + 'level' Address of a variable to receieve current debug level. + + [RETURN_VALUE] + + [REMARKS] + This only has an effect with 'logging' versions of FMOD Ex. For example on windows it must be via fmodexL.dll, not fmodex.dll.<br> + On Xbox it would be fmodxboxL.lib not fmodxbox.lib.<br> + FMOD_ERR_UNSUPPORTED will be returned on non logging versions of FMOD Ex (ie full release).<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Debug_SetLevel + FMOD_DEBUGLEVEL +] +*/ +FMOD_RESULT F_API FMOD_Debug_GetLevel(FMOD_DEBUGLEVEL *level) +{ + if (!level) + { + return FMOD_ERR_INVALID_PARAM; + } + #ifdef FMOD_DEBUG + *level = FMOD::gGlobal->gDebugLevel; + return FMOD_OK; + #else + *level = 0; + return FMOD_ERR_UNSUPPORTED; + #endif +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + 'mode' Bitfield containing debug settings. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Debug_GetMode +] +*/ +FMOD_RESULT F_API FMOD_Debug_SetMode(unsigned int mode) +{ + #ifdef FMOD_DEBUG + if (!FMOD::gGlobal) + { + return FMOD_ERR_UNINITIALIZED; + } + FMOD::gGlobal->gDebugMode = (FMOD::FMOD_DEBUGMODE)mode; + #endif + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + 'mode' Bitfield containing debug settings. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Debug_SetMode +] +*/ +FMOD_RESULT F_API FMOD_Debug_GetMode(unsigned int *mode) +{ + if (!mode) + { + return FMOD_ERR_INVALID_PARAM; + } + #ifdef FMOD_DEBUG + *mode = (unsigned int )FMOD::gGlobal->gDebugMode; + #endif + return FMOD_OK; +} + +} + + +#ifdef FMOD_DEBUG + +namespace FMOD +{ + +static int gDebugMemory = 0; +int *gDebugIndent = &gDebugMemory; + + +#ifndef PLATFORM_PS2_IOP + +#ifdef PLATFORM_PS2_EE + +#include <eekernel.h> +#include <sifdev.h> + +void DebugFile(const char *s) +{ + static int debugFP = -666; + static bool debugfirsttime = true; + + if (debugfirsttime) + { + debugFP = sceOpen(FMOD::gGlobal->gDebugFilename, SCE_CREAT | SCE_RDWR); + debugfirsttime = false; + } + + if (debugFP >= 0) + { + sceWrite(debugFP, s, FMOD_strlen(s)); + } + else + { + char s[256]; + + sprintf(s, "ERROR - failed to open %s! (result = %d)\n", FMOD::gGlobal->gDebugFilename, debugFP); + FMOD_OS_Debug_OutputStr(s); + } +} + +#else + +void DebugFile(const char *s) +{ + FILE *debugFP; + const char *openstr = "atc"; + static bool debugfirstfime = true; + + if (debugfirstfime) + { + debugfirstfime = false; + openstr = "wt"; + } + + debugFP = fopen(FMOD::gGlobal->gDebugFilename, openstr); + if (debugFP) + { + fputs(s, debugFP); + fflush(debugFP); + fclose(debugFP); + } +} + +#endif +#endif + +void Debug(FMOD_DEBUGLEVEL type, const char *file, const int line, const char *fnname, const char *format, ...) +{ + va_list arglist; + unsigned int now; + static unsigned int oldtime = 0; + const int BUFFER_SIZE = 256; + const int INDENT_SIZE = 64; + char s[BUFFER_SIZE], tmp[BUFFER_SIZE], indent[INDENT_SIZE]; + + if (!(type & FMOD::gGlobal->gDebugLevel)) + { + return; + } + + /* + Filter out users if one is specified. + */ + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_USER_ALL && type & FMOD_DEBUG_USER_ALL) + { + if (!((type & FMOD_DEBUG_USER_ALL) & (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_USER_ALL))) + { + return; + } + } + + FMOD_OS_Time_GetMs(&now); + if (oldtime == 0) + { + oldtime = now; + } + + va_start(arglist, format); + + FMOD_vsnprintf(s, BUFFER_SIZE, format, arglist); + + FMOD_snprintf(indent, INDENT_SIZE, "%*s", *gDebugIndent, ""); + + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_LINENUMBERS) + { + char tmp2[BUFFER_SIZE]; + + FMOD_snprintf(tmp, BUFFER_SIZE, "%s(%d)", file, line); + + #ifdef PLATFORM_PS2 + #define LEADINGSPACES 40 + #else + #define LEADINGSPACES 60 + #endif + + if (FMOD_strlen(tmp) < LEADINGSPACES) + { + FMOD_strncat(tmp, " ", LEADINGSPACES - FMOD_strlen(tmp)); + } + + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_TIMESTAMPS) + { + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_THREAD) + { + FMOD_UINT_NATIVE id; + FMOD_OS_Thread_GetCurrentID(&id), + FMOD_snprintf(tmp2, BUFFER_SIZE, ": [THREADID %d] [%8d ms delta = %4d] %-30s : %s%s", id, now, now - oldtime, fnname, indent, s); + } + else + { + FMOD_snprintf(tmp2, BUFFER_SIZE, ": [%8d ms delta = %4d] %-30s : %s%s", now, now - oldtime, fnname, indent, s); + } + } + else + { + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_THREAD) + { + FMOD_UINT_NATIVE id; + FMOD_OS_Thread_GetCurrentID(&id), + FMOD_snprintf(tmp2, BUFFER_SIZE, ": [THREADID %d] %-30s : %s%s", id, fnname, indent, s); + } + else + { + FMOD_snprintf(tmp2, BUFFER_SIZE, ": %-30s : %s%s", fnname, indent, s); + } + } + + FMOD_strncat(tmp, tmp2, BUFFER_SIZE - FMOD_strlen(tmp) - 1); + } + else + { + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_TIMESTAMPS) + { + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_THREAD) + { + FMOD_UINT_NATIVE id; + FMOD_OS_Thread_GetCurrentID(&id), + FMOD_snprintf(tmp, BUFFER_SIZE, "FMOD: [THREADID %d] [%8d ms delta = %4d] %-30s : %s%s", id, now, now - oldtime, fnname, indent, s); + } + else + { + FMOD_snprintf(tmp, BUFFER_SIZE, "FMOD: [%8d ms delta = %4d] %-30s : %s%s", now, now - oldtime, fnname, indent, s); + } + } + else + { + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_THREAD) + { + FMOD_UINT_NATIVE id; + FMOD_OS_Thread_GetCurrentID(&id), + FMOD_snprintf(tmp, BUFFER_SIZE, "FMOD: [THREADID %d] %-30s : %s%s", id, fnname, indent, s); + } + else + { + FMOD_snprintf(tmp, BUFFER_SIZE, "FMOD: %-30s : %s%s", fnname, indent, s); + } + } + } + + if (FMOD::gGlobal->gDebugLevel & FMOD_DEBUG_DISPLAY_COMPRESS) + { + static char lastmessage[BUFFER_SIZE] = ""; + static char lasts[BUFFER_SIZE] = ""; + static int numrepeats = 0; + + if (!FMOD_strcmp(lasts, s) && (numrepeats < 100)) + { + numrepeats++; + if (numrepeats > 5) + { + return; + } + } + else + { + if (numrepeats > 5) + { + char p[64]; + FMOD_snprintf(p, 64, "FMOD: Last message repeated %d times\n", numrepeats); +#ifndef PLATFORM_PS2_IOP + if (FMOD::gGlobal->gDebugMode == DEBUG_FILE) + { + DebugFile(p); + } + else +#endif + { + FMOD_OS_Debug_OutputStr(p); + } + } + + FMOD_strcpy(lasts, s); + FMOD_strcpy(lastmessage, tmp); + numrepeats = 0; + } + } + +#ifndef PLATFORM_PS2_IOP + if (FMOD::gGlobal->gDebugMode == DEBUG_FILE) + { + DebugFile(tmp); + } + else +#endif + { + FMOD_OS_Debug_OutputStr(tmp); + } + + va_end( arglist ); + + oldtime = now; +} + +} + +#endif diff --git a/src/fmod_debug.h b/src/fmod_debug.h new file mode 100755 index 0000000..6e24ffd --- /dev/null +++ b/src/fmod_debug.h @@ -0,0 +1,42 @@ +#ifndef _FMOD_DEBUG_H +#define _FMOD_DEBUG_H + +#include "fmod_settings.h" + +#ifdef FMOD_DEBUG + +#include "fmod.h" + +namespace FMOD +{ + typedef enum + { + DEBUG_STDOUT, + DEBUG_FILE + } FMOD_DEBUGMODE; + + #define FMOD_DEBUG_USER_BRETT 0x10000000 + #define FMOD_DEBUG_USER_ANDREW 0x20000000 + #define FMOD_DEBUG_USER_CHENPO 0x40000000 + #define FMOD_DEBUG_USER_PETER 0x80000000 + #define FMOD_DEBUG_USER_ALL 0xF0000000 + + void Debug(FMOD_DEBUGLEVEL level, const char *file, const int line, const char *fnname, const char *format, ...); + + extern int *gDebugIndent; + + #define FLOG(_x) Debug _x + #define FLOGC(_x) FMOD::Debug _x + #define FLOG_INDENT(_x) (*gDebugIndent) += (_x) +} + +#else + + #define FLOG(_x) ; + #define FLOGC(_x) ; + #define FLOG_INDENT(_x) ; + +#endif + +#endif + diff --git a/src/fmod_downmix.h b/src/fmod_downmix.h new file mode 100755 index 0000000..cd2f8c8 --- /dev/null +++ b/src/fmod_downmix.h @@ -0,0 +1,49 @@ +#ifndef _FMOD_DOWNMIX_H +#define _FMOD_DOWNMIX_H + +#include "fmod_settings.h" + +#include "fmod.h" + +namespace FMOD +{ + class Downmix + { + public: + virtual FMOD_RESULT init (unsigned int dspbuffersize, unsigned int rate, FMOD_SPEAKERMODE outputspeakermode, void *extradata = 0) = 0; + virtual FMOD_RESULT shutdown () { return FMOD_OK; } + virtual FMOD_RESULT encode (float *inbuffer, float *outbuffer, unsigned int numsamples) = 0; + + FMOD_RESULT getOutputChannels (int *outputchannels) + { + if (outputchannels) + { + *outputchannels = mNumOutputChannels; + } + return FMOD_OK; + } + FMOD_RESULT getInputChannels (int *inputchannels) + { + if (inputchannels) + { + *inputchannels = mNumInputChannels; + } + return FMOD_OK; + } + FMOD_RESULT getOutputSpeakerMode (FMOD_SPEAKERMODE *speakermode) + { + if (speakermode) + { + *speakermode = mOutputSpeakerMode; + } + return FMOD_OK; + } + + protected: + int mNumInputChannels; + int mNumOutputChannels; + FMOD_SPEAKERMODE mOutputSpeakerMode; + }; +} + +#endif // _FMOD_DOWNMIX_H diff --git a/src/fmod_downmix_myears.cpp b/src/fmod_downmix_myears.cpp new file mode 100755 index 0000000..1147e83 --- /dev/null +++ b/src/fmod_downmix_myears.cpp @@ -0,0 +1,756 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MYEARS + +#include "fmod_downmix_myears.h" +#include "fmod_memory.h" +#include <memory.h> + +#define MYEARS_LOGGING +#define MYEARS_SUPPORT_EXTERNALISE_FILTERS +//#define MYEARS_5POINT1_MODE + +#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_WINDOWS64) +#define MYEARS_DATA_FILE "\\myears\\data.myears" +#define MYEARS_LOG_FILE "\\myears\\myears.log" +#elif defined(PLATFORM_MAC) +#define MYEARS_DATA_FILE "" // <- todo +#undef MYEARS_LOGGING +#elif defined(PLATFORM_IPHONE) +#define MYEARS_DATA_FILE "" // <- todo +#undef MYEARS_LOGGING +#endif + +#ifdef MYEARS_LOGGING + +#include <stdarg.h> +#include <time.h> + +#ifdef PLATFORM_WINDOWS +#include <windows.h> +#endif + +#ifdef PLATFORM_WINDOWS64 +#include <windows.h> +#endif + +#endif // MYEARS_LOGGING + + + +namespace FMOD +{ + +const int DownmixMyEars::MYEARS_EXTERNALISE_DELAY = 1056; // 22ms @ 48kHz +const float DownmixMyEars::MYEARS_EXTERNALISE_GAIN = 0.031622f; // -30dB +const float DownmixMyEars::MYEARS_LFE_CUTOFF = 120.0f; +const float DownmixMyEars::MYEARS_LFE_GAIN = 1.0f; + +bool DownmixMyEars::dataExists() +{ + char filename[256]; + +#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_WINDOWS64) + // Hack! + GetSystemDirectory(filename, 256); + filename[2] = 0; + strcat(filename, MYEARS_DATA_FILE); + + FILE* file = fopen(filename, "r"); + if (file) + { + fclose(file); + return true; + } +#endif + + return false; +} + +int DownmixMyEars::getSampleRate() +{ + return MYEARS_SAMPLE_RATE; +} + +bool DownmixMyEars::log(char* fmtstr, ...) +{ +#ifdef MYEARS_LOGGING + char dirname[256]; + +#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_WINDOWS64) + GetSystemDirectory(dirname, 256); + dirname[2] = 0; + strcat(dirname, MYEARS_LOG_FILE); + + FILE* logfile = fopen(dirname, "a"); + if (logfile) + { + char str[256]; + va_list argptr; + va_start(argptr, fmtstr); + vsprintf(str, fmtstr, argptr); + + fprintf(logfile, str); + fclose(logfile); + + return true; + } +#endif + + return false; +#else + return true; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DownmixMyEars::init(unsigned int dspbuffersize, unsigned int rate, FMOD_SPEAKERMODE outputspeakermode, void *extradata) +{ + FMOD_RESULT result; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixMyEars::init", "dspbuffersize: %d rate: %d.\n", dspbuffersize, rate)); + + mOutputSpeakerMode = outputspeakermode; + for (int i = 0; i < MYEARS_SPEAKER_COUNT; ++i) + { + mLeftFilter[i] = 0; + mRightFilter[i] = 0; + mLeftFilterExt[i] = 0; + mRightFilterExt[i] = 0; + } + mUseExtFilters = false; + + if (outputspeakermode != FMOD_SPEAKERMODE_STEREO) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixMyEars::init", "outputspeakermode must be FMOD_SPEAKERMODE_STEREO\n")); + + return FMOD_ERR_INVALID_PARAM; + } + + if (rate != MYEARS_SAMPLE_RATE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixMyEars::init", "rate must be %d\n", MYEARS_SAMPLE_RATE)); + + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef MYEARS_5POINT1_MODE + mNumInputChannels = 6; +#else + mNumInputChannels = 8; +#endif + mNumOutputChannels = 2; + + mLFECutoff = MYEARS_LFE_CUTOFF; + mLFEGain = MYEARS_LFE_GAIN; + + if (extradata) + { + MyEarsExtraData *myearsdata = (MyEarsExtraData *)extradata; + // read initialisation data here + } + + if ((result = loadFile(MYEARS_DATA_FILE)) != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixMyEars::init", "Error opening MyEars file: %s\n", MYEARS_DATA_FILE)); + + return result; + } + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixMyEars::init", "Successfully loaded MyEars file: %s\n", MYEARS_DATA_FILE)); + + initLFEFilter(); + +#ifdef MYEARS_LOGGING + time_t now = time(0); + log("%s Loaded '%s'\n Externalisation = %s\n LFEGain = %f\n", ctime(&now), MYEARS_DATA_FILE, mUseExtFilters ? "On" : "Off", mLFEGain); +#endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixMyEars::init", "done.\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DownmixMyEars::shutdown() +{ + for (int i = 0; i < MYEARS_SPEAKER_COUNT; ++i) + { + unloadFilter(mLeftFilter[i]); + unloadFilter(mRightFilter[i]); + unloadFilter(mLeftFilterExt[i]); + unloadFilter(mRightFilterExt[i]); + + mLeftFilter[i] = 0; + mRightFilter[i] = 0; + mLeftFilterExt[i] = 0; + mRightFilterExt[i] = 0; + } + + return FMOD_OK; +} + + +void DownmixMyEars::initFilter(MyEarsFilter *filter) +{ + if (filter->buffer) + { + FMOD_memset(filter->buffer, 0, filter->delay * sizeof(float)); + filter->bufferpos = filter->buffer; + } + FMOD_memset(filter->state, 0, MYEARS_FILTER_ORDER * sizeof(float)); +} + + +float DownmixMyEars::applyFilter(float sample, MyEarsFilter *filter) +{ + // apply tiny dc offset to prevent float denorm values + + sample += 1.0e-20f; + + // apply ITD delay + + if (filter->buffer) + { + *filter->bufferpos = sample; + if (++filter->bufferpos >= filter->bufferend) + { + filter->bufferpos = filter->buffer; + } + sample = *filter->bufferpos; + } + + float *state = filter->state; + float *nextstate = state + 1; + float *a = filter->a + 1; + float *b = filter->b; + + // apply filter in transposed Direct Form II + + float y = *(b++) * sample + *state; // 0 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 1 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 2 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 3 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 4 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 5 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 6 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 7 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 8 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 9 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 10 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 11 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 12 + *(state++) = *(b++) * sample - *(a++) * y + *nextstate; // 13 + *state = *b * sample - *a * y; // 14 + + return y; +} + + +#if MYEARS_LFE_ORDER == 2 + +void DownmixMyEars::initLFEFilter() +{ + float damp = sqrtf(0.5f); + float c = 1.0f / FMOD_TAN(FMOD_PI * mLFECutoff / ((float)MYEARS_SAMPLE_RATE)); + float cc = c * c; + + mLFEFilter.b[0] = 1.0f / (1.0f + 2.0f*damp*c + cc) * mLFEGain; + mLFEFilter.b[1] = 2.0f * mLFEFilter.b[0]; + mLFEFilter.b[2] = mLFEFilter.b[0]; + + mLFEFilter.a[0] = 1.0f; + mLFEFilter.a[1] = 2.0f * mLFEFilter.b[0] * (1.0f - cc); + mLFEFilter.a[2] = mLFEFilter.b[0] * (1.0f - 2.0f * damp*c + cc); + + mLFEFilter.state[0] = 0.0f; + mLFEFilter.state[1] = 0.0f; +} + + +float DownmixMyEars::applyLFEFilter(float sample) +{ + // apply tiny dc offset to prevent float denorm values + + sample += 1.0e-20f; + + // apply filter in transposed Direct Form II + + float y = mLFEFilter.b[0] * sample + mLFEFilter.state[0]; // 0 + mLFEFilter.state[0] = mLFEFilter.b[1] * sample - mLFEFilter.a[1] * y + mLFEFilter.state[1]; // 1 + mLFEFilter.state[1] = mLFEFilter.b[2] * sample - mLFEFilter.a[2] * y; // 2 + + return y; +} + +#elif MYEARS_LFE_ORDER == 4 + +static int convolve(float *in1, int len1, float *in2, int len2, float *out) +{ + int convlen = len1 + len2 - 1; + + for (int i = 0; i < convlen; i++) + { + float sum = 0.0f; + + int n_lo = i - len2 + 1; + if (n_lo < 0) + { + n_lo = 0; + } + + int n_hi = len1 - 1; + if (n_hi > i) + { + n_hi = i; + } + + float *p1 = in1 + n_lo; + float *p2 = in2 + i - n_lo; + for (int n = n_lo; n <= n_hi; n++) + { + sum += *p1 * *p2; + p1++; + p2--; + } + + *out++ = sum; + } + + return convlen; +} + +// 4th order Linkwitz-Riley +void DownmixMyEars::initLFEFilter() +{ + float damp = sqrtf(0.5f); + float c = 1.0f / FMOD_TAN(FMOD_PI * mLFECutoff / ((float)MYEARS_SAMPLE_RATE)); + float cc = c * c; + + float b[3]; + float a[3]; + + b[0] = 1.0f / (1.0f + 2.0f * damp * c + cc); + b[1] = 2.0f * b[0]; + b[2] = b[0]; + + a[0] = 1.0f; + a[1] = 2.0f * b[0] * (1.0f - cc); + a[2] = b[0] * (1.0f - 2.0f * damp*c + cc); + + convolve(b, 3, b, 3, mLFEFilter.b); + convolve(a, 3, a, 3, mLFEFilter.a); + + for (int i = 0; i < MYEARS_LFE_ORDER; i++) + { + mLFEFilter.state[i] = 0.0f; + } +} + + +float DownmixMyEars::applyLFEFilter(float sample) +{ + // apply tiny dc offset to prevent float denorm values + static float dither = 1.0e-20f; + + sample += dither; + dither = -dither; + + float *state = mLFEFilter.state; + float *nextstate = state + 1; + float *b = mLFEFilter.b; + float *a = mLFEFilter.a + 1; + + // apply filter in transposed Direct Form II + + float y = *(b++) * sample + *state; // 0 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 1 + *(state++) = *(b++) * sample - *(a++) * y + *(nextstate++); // 2 + *(state++) = *(b++) * sample - *(a++) * y + *nextstate; // 3 + *state = *b * sample - *a * y; // 4 + + return y * mLFEGain; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DownmixMyEars::encode(float *inbuffer, float *outbuffer, unsigned int numsamples) +{ + float *in = inbuffer; + float *out = outbuffer; + +#ifdef MYEARS_SUPPORT_EXTERNALISE_FILTERS + if (mUseExtFilters) + { + switch(mNumInputChannels) + { + case 8: + for (unsigned int i = 0; i < numsamples; i++) + { + float extleft; + float extright; + float lfe; + + out[0] = applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_LEFT]); + out[1] = applyFilter(*in , mRightFilter[FMOD_SPEAKER_FRONT_LEFT]); + extleft = applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_FRONT_LEFT]); + extright = applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_FRONT_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_RIGHT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_FRONT_RIGHT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_FRONT_RIGHT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_FRONT_RIGHT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_CENTER]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_FRONT_CENTER]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_FRONT_CENTER]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_FRONT_CENTER]); + lfe = applyLFEFilter(*in++); + out[0] += lfe; + out[1] += lfe; + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_LEFT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_BACK_LEFT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_BACK_LEFT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_BACK_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_RIGHT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_BACK_RIGHT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_BACK_RIGHT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_BACK_RIGHT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_SIDE_LEFT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_SIDE_LEFT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_SIDE_LEFT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_SIDE_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_SIDE_RIGHT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_SIDE_RIGHT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_SIDE_RIGHT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_SIDE_RIGHT]); + + out[0] += extleft * MYEARS_EXTERNALISE_GAIN; + out[1] += extright * MYEARS_EXTERNALISE_GAIN; + + out[0] *= 0.5f; + out[1] *= 0.5f; + + out += 2; + } + break; + + case 6: + for (unsigned int i = 0; i < numsamples; i++) + { + float extleft; + float extright; + float lfe; + + out[0] = applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_LEFT]); + out[1] = applyFilter(*in , mRightFilter[FMOD_SPEAKER_FRONT_LEFT]); + extleft = applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_FRONT_LEFT]); + extright = applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_FRONT_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_RIGHT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_FRONT_RIGHT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_FRONT_RIGHT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_FRONT_RIGHT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_CENTER]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_FRONT_CENTER]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_FRONT_CENTER]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_FRONT_CENTER]); + lfe = applyLFEFilter(*in++); + out[0] += lfe; + out[1] += lfe; + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_LEFT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_BACK_LEFT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_BACK_LEFT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_BACK_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_RIGHT]); + out[1] += applyFilter(*in , mRightFilter[FMOD_SPEAKER_BACK_RIGHT]); + extleft += applyFilter(*in , mLeftFilterExt[FMOD_SPEAKER_BACK_RIGHT]); + extright += applyFilter(*in++, mRightFilterExt[FMOD_SPEAKER_BACK_RIGHT]); + + out[0] += extleft * MYEARS_EXTERNALISE_GAIN; + out[1] += extright * MYEARS_EXTERNALISE_GAIN; + + out[0] *= 0.5f; + out[1] *= 0.5f; + + out += 2; + } + break; + + default: + return FMOD_ERR_INTERNAL; + + } + } + else +#endif + { + switch(mNumInputChannels) + { + case 8: + for (unsigned int i = 0; i < numsamples; i++) + { + float lfe; + + out[0] = applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_LEFT]); + out[1] = applyFilter(*in++, mRightFilter[FMOD_SPEAKER_FRONT_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_RIGHT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_FRONT_RIGHT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_CENTER]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_FRONT_CENTER]); + lfe = applyLFEFilter(*in++); + out[0] += lfe; + out[1] += lfe; + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_LEFT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_BACK_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_RIGHT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_BACK_RIGHT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_SIDE_LEFT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_SIDE_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_SIDE_RIGHT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_SIDE_RIGHT]); + + out[0] *= 0.5f; + out[1] *= 0.5f; + + out += 2; + } + break; + + case 6: + for (unsigned int i = 0; i < numsamples; i++) + { + float lfe; + + out[0] = applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_LEFT]); + out[1] = applyFilter(*in++, mRightFilter[FMOD_SPEAKER_FRONT_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_RIGHT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_FRONT_RIGHT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_FRONT_CENTER]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_FRONT_CENTER]); + lfe = applyLFEFilter(*in++); + out[0] += lfe; + out[1] += lfe; + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_LEFT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_BACK_LEFT]); + out[0] += applyFilter(*in , mLeftFilter[FMOD_SPEAKER_BACK_RIGHT]); + out[1] += applyFilter(*in++, mRightFilter[FMOD_SPEAKER_BACK_RIGHT]); + + out[0] *= 0.5f; + out[1] *= 0.5f; + + out += 2; + } + break; + + default: + return FMOD_ERR_INTERNAL; + + } + } + + return FMOD_OK; +} + + +FMOD_RESULT DownmixMyEars::loadFile(const char *filename) +{ + FMOD_RESULT result; + + FILE* file = fopen(filename, "r"); + if (!file) + { + return FMOD_ERR_FILE_NOTFOUND; + } + + for(int i = 0; i < MYEARS_SPEAKER_COUNT; ++i) + { + if (i != FMOD_SPEAKER_LOW_FREQUENCY) + { + if ((result = loadFilter(file, &mRightFilter[i])) != FMOD_OK) + { + fclose(file); + return result; + } + if ((result = loadFilter(file, &mLeftFilter[i])) != FMOD_OK) + { + fclose(file); + return result; + } + if ((result = loadFilter(file, &mRightFilterExt[i], true)) != FMOD_OK) + { + fclose(file); + return result; + } + if ((result = loadFilter(file, &mLeftFilterExt[i], true)) != FMOD_OK) + { + fclose(file); + return result; + } + + initFilter(mRightFilter[i]); + initFilter(mLeftFilter[i]); + initFilter(mRightFilterExt[i]); + initFilter(mLeftFilterExt[i]); + } + else + { + mRightFilter[i] = 0; + mLeftFilter[i] = 0; + mRightFilterExt[i] = 0; + mLeftFilterExt[i] = 0; + } + } + + int ext; + if (fscanf(file, "%d", &ext) == EOF) // enable externalisation filters + { + fclose(file); + return FMOD_ERR_FORMAT; + } + + if (fscanf(file, ",%f", &mLFEGain) == EOF) // LFE gain (optional) + { + mLFEGain = MYEARS_LFE_GAIN; + } + + mUseExtFilters = (ext != 0); + + fclose(file); + + return FMOD_OK; +} + + +FMOD_RESULT DownmixMyEars::loadFilter(FILE *file, MyEarsFilter **filter, bool externaliser) +{ + int dummy; + int i; + + *filter = (MyEarsFilter *)FMOD_Memory_Calloc(sizeof(MyEarsFilter)); + if (!filter) + { + return FMOD_ERR_MEMORY; + } + + // 'a' coefficients + if (fscanf(file, "%d,", &dummy) == EOF) // ignore 'a' coefficient delays + { + goto MYEARS_FILTER_LOAD_ERROR; + } + for (i = 0; i < MYEARS_FILTER_ORDER; ++i) + { + if (fscanf(file, "%f,", &(*filter)->a[i]) == EOF) + { + goto MYEARS_FILTER_LOAD_ERROR; + } + } + if (fscanf(file, "%f\n", &(*filter)->a[i]) == EOF) + { + goto MYEARS_FILTER_LOAD_ERROR; + } + + // 'b' coefficients + if (fscanf(file, "%d,", &(*filter)->delay) == EOF) + { + goto MYEARS_FILTER_LOAD_ERROR; + } + for (i = 0; i < MYEARS_FILTER_ORDER; ++i) + { + if (fscanf(file, "%f,", &(*filter)->b[i]) == EOF) + { + goto MYEARS_FILTER_LOAD_ERROR; + } + } + if (fscanf(file, "%f\n", &(*filter)->b[i]) == EOF) + { + goto MYEARS_FILTER_LOAD_ERROR; + } + + if (externaliser) + { + (*filter)->delay += MYEARS_EXTERNALISE_DELAY; + } + + if ((*filter)->delay) + { + (*filter)->buffer = (float *)FMOD_Memory_Calloc((*filter)->delay * sizeof(float)); + if (!(*filter)->buffer) + { + FMOD_Memory_Free(*filter); + return FMOD_ERR_MEMORY; + } + + (*filter)->bufferend = (*filter)->buffer + (*filter)->delay; + } + else + { + (*filter)->buffer = 0; + } + + return FMOD_OK; + +MYEARS_FILTER_LOAD_ERROR: + FMOD_Memory_Free(*filter); + return FMOD_ERR_FORMAT; +} + + +void DownmixMyEars::unloadFilter(MyEarsFilter *filter) +{ + if (filter) + { + if (filter->buffer) + { + FMOD_Memory_Free(filter->buffer); + } + FMOD_Memory_Free(filter); + } +} + + +} + +#endif //FMOD_SUPPORT_MYEARS diff --git a/src/fmod_downmix_myears.h b/src/fmod_downmix_myears.h new file mode 100755 index 0000000..c034c9a --- /dev/null +++ b/src/fmod_downmix_myears.h @@ -0,0 +1,83 @@ +#ifndef _FMOD_DOWNMIX_MYEARS_H +#define _FMOD_DOWNMIX_MYEARS_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_MYEARS + +#include "fmod_downmix.h" + +#include <stdio.h> + +#define MYEARS_FILTER_ORDER 14 +#define MYEARS_LFE_ORDER 4 +#define MYEARS_SPEAKER_COUNT 8 +#define MYEARS_SAMPLE_RATE 48000 + + +namespace FMOD +{ + class DownmixMyEars : public Downmix + { + public: + FMOD_RESULT init (unsigned int dspbuffersize, unsigned int rate, FMOD_SPEAKERMODE outputspeakermode, void *extradata = 0); + FMOD_RESULT shutdown (); + FMOD_RESULT encode (float *inbuffer, float *outbuffer, unsigned int numsamples); + + static bool dataExists (); + static int getSampleRate (); + + typedef struct MyEarsFilter + { + float a[MYEARS_FILTER_ORDER + 1]; + float b[MYEARS_FILTER_ORDER + 1]; + float state[MYEARS_FILTER_ORDER]; + int delay; + float *buffer; + float *bufferend; + float *bufferpos; + }; + + typedef struct LFEFilter + { + float a[MYEARS_LFE_ORDER + 1]; + float b[MYEARS_LFE_ORDER + 1]; + float state[MYEARS_LFE_ORDER]; + }; + + typedef struct MyEarsExtraData + { + // data for Downmix::init() + }; + + private: + static const int MYEARS_EXTERNALISE_DELAY; + static const float MYEARS_EXTERNALISE_GAIN; + static const float MYEARS_LFE_CUTOFF; + static const float MYEARS_LFE_GAIN; + + bool mUseExtFilters; + float mLFEGain; + float mLFECutoff; + + MyEarsFilter *mLeftFilter[MYEARS_SPEAKER_COUNT]; + MyEarsFilter *mRightFilter[MYEARS_SPEAKER_COUNT]; + MyEarsFilter *mLeftFilterExt[MYEARS_SPEAKER_COUNT]; + MyEarsFilter *mRightFilterExt[MYEARS_SPEAKER_COUNT]; + LFEFilter mLFEFilter; + + static bool log(char* str, ...); + + FMOD_RESULT loadFile(const char *filename); + FMOD_RESULT loadFilter(FILE *file, MyEarsFilter **filter, bool externaliser = false); + void unloadFilter(MyEarsFilter *filter); + void initFilter(MyEarsFilter *filter); + void initLFEFilter(); + __forceinline float applyFilter(float sample, MyEarsFilter *filter); + __forceinline float applyLFEFilter(float sample); + }; +} + +#endif + +#endif // _FMOD_DOWNMIX_MYEARS_H diff --git a/src/fmod_downmix_neuralthx.cpp b/src/fmod_downmix_neuralthx.cpp new file mode 100755 index 0000000..1f151fc --- /dev/null +++ b/src/fmod_downmix_neuralthx.cpp @@ -0,0 +1,231 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NEURAL + +#include "fmod_downmix_neuralthx.h" +#include "fmod_memory.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DownmixNeural::init(unsigned int dspbuffersize, unsigned int rate, FMOD_SPEAKERMODE outputspeakermode, void *extradata) +{ + int err; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixNeural::init", "dspbuffersize: %d rate: %d.\n", dspbuffersize, rate)); + + if (dspbuffersize % NEURAL_FRAMESIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixNeural::init", "dspbuffersize: %d must be multiple of 256\n", dspbuffersize)); + + return FMOD_ERR_INVALID_PARAM; + } + + mOutputSpeakerMode = outputspeakermode; + switch(mOutputSpeakerMode) + { + case FMOD_SPEAKERMODE_STEREO: + { + mNeuralMode = NEURAL_THX_7_2_GAMING; + mNumInputChannels = 8; + mNumOutputChannels = 2; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixNeural::init", "Initialising to NEURAL_7_2_GAMING.\n")); + + break; + } + case FMOD_SPEAKERMODE_5POINT1: + { + mNeuralMode = NEURAL_THX_7_5_GAMING; + mNumInputChannels = 8; + mNumOutputChannels = 6; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixNeural::init", "Initialising to NEURAL_7_5_GAMING.\n")); + + break; + } + default: + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixNeural::init", "outputspeakermode must be FMOD_SPEAKERMODE_STEREO or FMOD_SPEAKERMODE_5POINT1\n")); + return FMOD_ERR_INVALID_PARAM; + } + } + + bool usefinallimiting = false; + float lfecutoff = 0.0f; + + if (extradata) + { + NeuralExtraData *neuraldata = (NeuralExtraData *)extradata; + + usefinallimiting = neuraldata->usefinallimiting; + lfecutoff = neuraldata->lfecutoff; + } + + mNeuralParameters.Init(mNeuralMode); + mNeuralSettings.nChanConfig = mNeuralMode; + mNeuralSettings.nSampleRate = rate; + + err = mNeuralEncoder.Init(mNeuralSettings, mNeuralParameters); + if (err != NRLTHX_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixNeural::init", "Neural Init returned: %d\n", err)); + return FMOD_ERR_OUTPUT_INIT; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DownmixNeural::init", "done.\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DownmixNeural::shutdown() +{ + mNeuralEncoder.Shutdown(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DownmixNeural::encode(float *inbuffer, float *outbuffer, unsigned int numsamples) +{ + int err; + Neural_THX_Channel_Format channels_in, channels_out; + int numiterations = numsamples / NEURAL_FRAMESIZE; + + float *in = inbuffer; + float *out = outbuffer; + + for (int i = 0; i < numiterations; i++) + { + if (mNumInputChannels == 8) + { + for (int j = 0; j < NEURAL_FRAMESIZE; j++) + { + channels_in.m_fL [j] = in[0]; // front left + channels_in.m_fR [j] = in[1]; // front right + channels_in.m_fC [j] = in[2]; // center + channels_in.m_fLFE[j] = in[3]; // lfe + + #ifdef PLATFORM_PS3 + /* + Back and sides swapped around on PS3 + */ + channels_in.m_fLs [j] = in[4]; // surround left + channels_in.m_fRs [j] = in[5]; // surround right + + channels_in.m_fLb [j] = in[6]; // back left + channels_in.m_fRb [j] = in[7]; // back right + #else + channels_in.m_fLb [j] = in[4]; // back left + channels_in.m_fRb [j] = in[5]; // back right + + channels_in.m_fLs [j] = in[6]; // side left + channels_in.m_fRs [j] = in[7]; // side right + #endif + + in += 8; + } + } + else if (mNumInputChannels == 6) + { + for (int j = 0; j < NEURAL_FRAMESIZE; j++) + { + channels_in.m_fL [j] = in[0]; // front left + channels_in.m_fR [j] = in[1]; // front right + channels_in.m_fC [j] = in[2]; // center + channels_in.m_fLFE[j] = in[3]; // lfe + channels_in.m_fLs [j] = in[4]; // surround left + channels_in.m_fRs [j] = in[5]; // surround right + + in += 6; + } + } + + /* + Encode + */ + err = mNeuralEncoder.Encode(channels_in, channels_out, mNeuralSettings, mNeuralParameters); + if (err != NRLTHX_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DownmixNeural::init", "Neural Encode returned: %d\n", err)); + return FMOD_ERR_INVALID_PARAM; + } + + if (mNumOutputChannels == 6) + { + for (int j = 0; j < NEURAL_FRAMESIZE; j++) + { + out[0] = channels_out.m_fL [j]; // front left + out[1] = channels_out.m_fR [j]; // front right + out[2] = channels_out.m_fC [j]; // center + out[3] = channels_out.m_fLFE[j]; // lfe + out[4] = channels_out.m_fLs [j]; // surround left + out[5] = channels_out.m_fRs [j]; // surround right + + out += 6; + } + } + else if (mNumOutputChannels == 2) + { + for (int j = 0; j < NEURAL_FRAMESIZE; j++) + { + out[0] = channels_out.m_fL [j]; // front left + out[1] = channels_out.m_fR [j]; // front right + + out += 2; + } + } + } + + return FMOD_OK; +} + +} + +#endif //FMOD_INIT_DTS_NEURALSURROUND diff --git a/src/fmod_downmix_neuralthx.h b/src/fmod_downmix_neuralthx.h new file mode 100755 index 0000000..e5fdd98 --- /dev/null +++ b/src/fmod_downmix_neuralthx.h @@ -0,0 +1,39 @@ +#ifndef _FMOD_DOWNMIX_NEURAL_H +#define _FMOD_DOWNMIX_NEURAL_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NEURAL + +#include "fmod.h" + +#include "fmod_downmix.h" +#include "../lib/neural_thx/Neural_THX_Interface.h" + +namespace FMOD +{ + class DownmixNeural : public Downmix + { + public: + FMOD_RESULT init (unsigned int dspbuffersize, unsigned int rate, FMOD_SPEAKERMODE outputspeakermode, void *extradata = 0); + FMOD_RESULT shutdown (); + FMOD_RESULT encode (float *inbuffer, float *outbuffer, unsigned int numsamples); + + typedef struct NeuralExtraData + { + bool usefinallimiting; + float lfecutoff; + }; + + private: + NEURAL_THX_ENCODER mNeuralEncoder; + Neural_THX_Encoder_Settings mNeuralSettings; + Neural_THX_Encoder_Params mNeuralParameters; + + unsigned int mNeuralMode; + }; +} + +#endif + +#endif // _FMOD_DOWNMIX_NEURAL_H diff --git a/src/fmod_dsp.cpp b/src/fmod_dsp.cpp new file mode 100755 index 0000000..2c038a9 --- /dev/null +++ b/src/fmod_dsp.cpp @@ -0,0 +1,507 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod_dspi.h" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT DSP::release() +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->release(); + } +} + + +FMOD_RESULT DSP::getSystemObject(System **system) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getSystemObject(system); + } +} + + +FMOD_RESULT DSP::addInput(DSP *target, DSPConnection **connection) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->addInput((DSPI *)target, (DSPConnectionI **)connection); + } +} + + +FMOD_RESULT DSP::disconnectFrom(DSP *target) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->disconnectFrom((DSPI *)target); + } +} + + +FMOD_RESULT DSP::disconnectAll(bool inputs, bool outputs) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->disconnectAll(inputs, outputs); + } +} + + +FMOD_RESULT DSP::remove() +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->remove(); + } +} + + +FMOD_RESULT DSP::getNumInputs(int *numinputs) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getNumInputs(numinputs); + } +} + + +FMOD_RESULT DSP::getNumOutputs(int *numoutputs) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getNumOutputs(numoutputs); + } +} + + +FMOD_RESULT DSP::getInput(int index, DSP **input, DSPConnection **inputconnection) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getInput(index, (DSPI **)input, (DSPConnectionI **)inputconnection); + } +} + + +FMOD_RESULT DSP::getOutput(int index, DSP **output, DSPConnection **outputconnection) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getOutput(index, (DSPI **)output, (DSPConnectionI **)outputconnection); + } +} + + +FMOD_RESULT DSP::setActive(bool active) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->setActive(active); + } +} + + +FMOD_RESULT DSP::getActive(bool *active) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getActive(active); + } +} + + +FMOD_RESULT DSP::setBypass(bool bypass) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->setBypass(bypass); + } +} + + +FMOD_RESULT DSP::getBypass(bool *bypass) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getBypass(bypass); + } +} + + +FMOD_RESULT DSP::setSpeakerActive(FMOD_SPEAKER speaker, bool active) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->setSpeakerActive(speaker, active); + } +} + + +FMOD_RESULT DSP::getSpeakerActive(FMOD_SPEAKER speaker, bool *active) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getSpeakerActive(speaker, active); + } +} + + +FMOD_RESULT DSP::reset() +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->reset(); + } +} + + +FMOD_RESULT DSP::setParameter(int index, float value) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->setParameter(index, value); + } +} + + +FMOD_RESULT DSP::getParameter(int index, float *value, char *valuestr, int valuestrlen) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getParameter(index, value, valuestr, valuestrlen); + } +} + + +FMOD_RESULT DSP::getNumParameters(int *numparams) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getNumParameters(numparams); + } +} + + +FMOD_RESULT DSP::getParameterInfo(int index, char *name, char *label, char *description, int descriptionlen, float *min, float *max) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getParameterInfo(index, name, label, description, descriptionlen, min, max); + } +} + + +FMOD_RESULT DSP::showConfigDialog(void *hwnd, bool show) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->showConfigDialog(hwnd, show); + } +} + + +FMOD_RESULT DSP::getInfo(char *name, unsigned int *version, int *channels, int *configwidth, int *configheight) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getInfo(name, version, channels, configwidth, configheight); + } +} + + +FMOD_RESULT DSP::getType(FMOD_DSP_TYPE *type) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getType(type); + } +} + + +FMOD_RESULT DSP::setDefaults(float frequency, float volume, float pan, int priority) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->setDefaults(frequency, volume, pan, priority); + } +} + + +FMOD_RESULT DSP::getDefaults(float *frequency, float *volume, float *pan, int *priority) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getDefaults(frequency, volume, pan, priority); + } +} + + +FMOD_RESULT DSP::setUserData(void *_userdata) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->setUserData(_userdata); + } +} + + +FMOD_RESULT DSP::getUserData(void **_userdata) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getUserData(_userdata); + } +} + + +FMOD_RESULT DSP::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + DSPI *dspi; + + result = DSPI::validate(this, &dspi); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_dsp_chorus.cpp b/src/fmod_dsp_chorus.cpp new file mode 100755 index 0000000..193d3a3 --- /dev/null +++ b/src/fmod_dsp_chorus.cpp @@ -0,0 +1,771 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CHORUS + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_chorus.h" +#include "fmod_systemi.h" + +#include <stdio.h> + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspchorus; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPChorus::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +FMOD_DSP_PARAMETERDESC dspchorus_param[8] = +{ + { 0.0f, 1.0f, 0.50f, "Dry mix", "", "Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.5." }, + { 0.0f, 1.0f, 0.50f, "Wet mix tap 1", "", "Volume of 1st chorus tap. 0.0 to 1.0. Default = 0.5." }, + { 0.0f, 1.0f, 0.50f, "Wet mix tap 2", "", "Volume of 2nd chorus tap. This tap is 90 degrees out of phase of the first tap. 0.0 to 1.0. Default = 0.5." }, + { 0.0f, 1.0f, 0.50f, "Wet mix tap 3", "", "Volume of 3rd chorus tap. This tap is 90 degrees out of phase of the second tap. 0.0 to 1.0. Default = 0.5." }, + { 0.0f, 100.0f, 40.00f, "Delay", "ms", "Chorus delay in ms. 0.1 to 100.0. Default = 40.0 ms." }, + { 0.0f, 20.0f, 0.80f, "Rate", "hz", "Chorus modulation rate in hz. 0.0 to 20.0. Default = 0.8 hz." }, + { 0.0f, 1.0f, 0.03f, "Depth", "", "Chorus modulation depth. 0.0 to 1.0. Default = 0.03." }, + { 0.0f, 1.0f, 0.00f, "Feedback", "", "Chorus feedback. Controls how much of the wet signal gets fed back into the chorus buffer. 0.0 to 1.0. Default = 0.0." } +}; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPChorus::getDescriptionEx() +{ + FMOD_memset(&dspchorus, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspchorus.name, "FMOD Chorus"); + dspchorus.version = 0x00010100; + dspchorus.create = DSPChorus::createCallback; + dspchorus.release = DSPChorus::releaseCallback; + dspchorus.reset = DSPChorus::resetCallback; + dspchorus.read = DSPChorus::readCallback; + + dspchorus.numparameters = sizeof(dspchorus_param) / sizeof(dspchorus_param[0]); + dspchorus.paramdesc = dspchorus_param; + dspchorus.setparameter = DSPChorus::setParameterCallback; + dspchorus.getparameter = DSPChorus::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspchorus.getmemoryused = &DSPChorus::getMemoryUsedCallback; +#endif + + dspchorus.mType = FMOD_DSP_TYPE_CHORUS; + dspchorus.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspchorus.mSize = sizeof(DSPChorus); + + return &dspchorus; +} + +#ifdef DSP_CHORUS_USECOSTAB + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPChorus::cosine(float x) +{ + int y; + + x *= DSP_CHORUS_TABLERANGE; + y = (int)x; + if (y < 0) + { + y = -y; + } + + y &= DSP_CHORUS_TABLEMASK; + switch (y >> DSP_CHORUS_COSTABBITS) + { + case 0 : return mCosTab[y]; + case 1 : return -mCosTab[(DSP_CHORUS_COSTABSIZE - 1) - (y - (DSP_CHORUS_COSTABSIZE * 1))]; + case 2 : return -mCosTab[ (y - (DSP_CHORUS_COSTABSIZE * 2))]; + case 3 : return mCosTab[(DSP_CHORUS_COSTABSIZE - 1) - (y - (DSP_CHORUS_COSTABSIZE * 3))]; + } + + return 0.0f; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPChorus::sine(float x) +{ + return cosine(x - 0.25f); +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPChorus::createInternal() +{ + FMOD_RESULT result; + int inchannels, outchannels, channels, count; + + init(); + mOldSpeakerMask = 0xFFFF; + +#ifdef DSP_CHORUS_USECOSTAB + { + int count; + + for (count = 0; count < DSP_CHORUS_COSTABSIZE; count++) + { + mCosTab[count] = (float)FMOD_COS(FMOD_PI_2 * (float)count / (float)DSP_CHORUS_COSTABSIZE); + } + } +#endif + + result = mSystem->getSoftwareFormat(&mOutputRate, 0, &outchannels, &inchannels, 0, 0); + if (result != FMOD_OK) + { + return result; + } + channels = inchannels > outchannels ? inchannels : outchannels; + + mChorusBufferLengthBytes = (int)((float)mOutputRate * DSP_CHORUS_MAXBUFFERLENGTHMS) / 1000; + mChorusBufferLengthBytes *= channels; + mChorusBufferLengthBytes *= sizeof(signed short); + mChorusBufferLengthBytes += 1024; + + mChorusBuffer = (signed short *)FMOD_Memory_Calloc(mChorusBufferLengthBytes); + if (!mChorusBuffer) + { + return FMOD_ERR_MEMORY; + } + + mChorusTick = 0; + + for (count = 0; count < mDescription.numparameters; count++) + { + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPChorus::releaseInternal() +{ + if (mChorusBuffer) + { + FMOD_Memory_Free(mChorusBuffer); + mChorusBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPChorus::resetInternal() +{ + mChorusBufferPosition = 0; + + if (mChorusBuffer) + { + FMOD_memset(mChorusBuffer, 0, mChorusBufferLengthBytes); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPChorus::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + int halfchorusbufferlength = mChorusBufferLength / 2; + float halfdepth = mDepth * 0.5f; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (speakermask != mOldSpeakerMask) /* a channel has been deactivated: clear the buffer for that channels so that there isn't any artifacts if/when it is reenabled. */ + { + unsigned int diff = mOldSpeakerMask ^ speakermask; + for (count = 0; count < (unsigned int)inchannels; count++) + { + if (diff & (1 << count)) + { + short *buff = mChorusBuffer + count; + for (count=0; count < mChorusBufferLength; count++) + { + buff[0]=0; + buff += inchannels; + } + } + } + mOldSpeakerMask = speakermask; + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + for (count=0; count < length; count++) + { + unsigned int p1[3], p2[3]; + float frac[3]; + int count2; + + p1[0] = (mChorusBufferPosition + (unsigned int)mChorusPosition[0]) % mChorusBufferLength; + p1[1] = (mChorusBufferPosition + (unsigned int)mChorusPosition[1]) % mChorusBufferLength; + p1[2] = (mChorusBufferPosition + (unsigned int)mChorusPosition[2]) % mChorusBufferLength; + p2[0] = p1[0] + 1; /* the first sample of the buffer has been duplicated to the end so it wont click. */ + p2[1] = p1[1] + 1; /* the first sample of the buffer has been duplicated to the end so it wont click. */ + p2[2] = p1[2] + 1; /* the first sample of the buffer has been duplicated to the end so it wont click. */ + + frac[0] = mChorusPosition[0] - (int)mChorusPosition[0]; + frac[1] = mChorusPosition[1] - (int)mChorusPosition[1]; + frac[2] = mChorusPosition[2] - (int)mChorusPosition[2]; + + for (count2 = 0; count2 < inchannels; count2++) + { + int offset = (count * inchannels) + count2; + + if (!((1 << count2) & speakermask)) + { + outbuffer[offset] = inbuffer[offset]; + } + else + { + float val; + float chorusval; + + val = inbuffer[offset] * mDryMix * 32768.0f; + + val += ((mChorusBuffer[(p1[0] * inchannels) + count2] * (1.0f - frac[0])) + (mChorusBuffer[(p2[0] * inchannels) + count2] * frac[0])) * mWetMix1; + val += ((mChorusBuffer[(p1[1] * inchannels) + count2] * (1.0f - frac[1])) + (mChorusBuffer[(p2[1] * inchannels) + count2] * frac[1])) * mWetMix2; + val += ((mChorusBuffer[(p1[2] * inchannels) + count2] * (1.0f - frac[2])) + (mChorusBuffer[(p2[2] * inchannels) + count2] * frac[2])) * mWetMix3; + + chorusval = (inbuffer[offset] * 32767.0f) + val * mFeedback; + + mChorusBuffer[(mChorusBufferPosition * inchannels) + count2] = chorusval < -32768.0f ? -32768 : chorusval > 32767.0f ? 32767 : (signed short)chorusval; + + outbuffer[offset] = val / 32767.0f; + } + } + + if (!mChorusBufferPosition) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if ((1 << count2) & speakermask) + { + mChorusBuffer[(mChorusBufferLength * inchannels) + count2] = mChorusBuffer[count2]; + } + } + } + + mChorusBufferPosition++; + if (mChorusBufferPosition >= mChorusBufferLength) + { + mChorusBufferPosition = 0; + } + + #ifdef DSP_CHORUS_USECOSTAB + mChorusPosition[0] = (1.0f + sine(mChorusTick + 0.00f)) * halfdepth; + mChorusPosition[1] = (1.0f + sine(mChorusTick + 0.25f)) * halfdepth; + mChorusPosition[2] = (1.0f + sine(mChorusTick + 0.50f)) * halfdepth; + #else + mChorusPosition[0] = (1.0f + sine((mChorusTick + 0.00f) * FMOD_PI2)) * halfdepth; + mChorusPosition[1] = (1.0f + sine((mChorusTick + 0.25f) * FMOD_PI2)) * halfdepth; + mChorusPosition[2] = (1.0f + sine((mChorusTick + 0.50f) * FMOD_PI2)) * halfdepth; + #endif + + mChorusPosition[0] = halfchorusbufferlength + (mChorusPosition[0] * mChorusBufferLength); + mChorusPosition[1] = halfchorusbufferlength + (mChorusPosition[1] * mChorusBufferLength); + mChorusPosition[2] = halfchorusbufferlength + (mChorusPosition[2] * mChorusBufferLength); + + mChorusTick += mChorusSpeed; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPChorus::setParameterInternal(int index, float value) +{ + float olddelay; + + olddelay = mDelay; + + mSystem->lockDSP(); + + switch (index) + { + case FMOD_DSP_CHORUS_DRYMIX: + { + mDryMix = value; + break; + } + case FMOD_DSP_CHORUS_WETMIX1: + { + mWetMix1 = value; + break; + } + case FMOD_DSP_CHORUS_WETMIX2: + { + mWetMix2 = value; + break; + } + case FMOD_DSP_CHORUS_WETMIX3: + { + mWetMix3 = value; + break; + } + case FMOD_DSP_CHORUS_DELAY: + { + mDelay = value; + break; + } + case FMOD_DSP_CHORUS_RATE: + { + mRateHz = value; + break; + } + case FMOD_DSP_CHORUS_DEPTH: + { + mDepth = value; + break; + } + case FMOD_DSP_CHORUS_FEEDBACK: + { + mFeedback = value; + break; + } + } + + if (mDelay != olddelay) + { + mChorusBufferLength = (int)((float)mOutputRate * mDelay / 1000.0f) * 2; + if (mChorusBufferLength < 4) + { + mChorusBufferLength = 4; + } + + resetInternal(); + } + + mChorusSpeed = mRateHz / (float)mOutputRate; + + mSystem->unlockDSP(); + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPChorus::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_CHORUS_DRYMIX: + { + *value = mDryMix; + sprintf(valuestr, "%.02f", mDryMix); + break; + } + case FMOD_DSP_CHORUS_WETMIX1: + { + *value = mWetMix1; + sprintf(valuestr, "%.02f", mWetMix1); + break; + } + case FMOD_DSP_CHORUS_WETMIX2: + { + *value = mWetMix2; + sprintf(valuestr, "%.02f", mWetMix2); + break; + } + case FMOD_DSP_CHORUS_WETMIX3: + { + *value = mWetMix3; + sprintf(valuestr, "%.02f", mWetMix3); + break; + } + case FMOD_DSP_CHORUS_DELAY: + { + *value = mDelay; + sprintf(valuestr, "%.02f", mDelay); + break; + } + case FMOD_DSP_CHORUS_RATE: + { + *value = mRateHz; + sprintf(valuestr, "%.02f", mRateHz); + break; + } + case FMOD_DSP_CHORUS_DEPTH: + { + *value = mDepth; + sprintf(valuestr, "%.02f", mDepth); + break; + } + case FMOD_DSP_CHORUS_FEEDBACK: + { + *value = mFeedback; + sprintf(valuestr, "%.02f", mFeedback); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPChorus::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + if (mChorusBuffer) + { + tracker->add(false, FMOD_MEMBITS_DSP, mChorusBufferLengthBytes); + } + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPChorus::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPChorus *chorus = (DSPChorus *)dsp; + + return chorus->DSPChorus::getMemoryUsed(tracker); +} +#endif + + +} + +#endif diff --git a/src/fmod_dsp_chorus.h b/src/fmod_dsp_chorus.h new file mode 100755 index 0000000..1691695 --- /dev/null +++ b/src/fmod_dsp_chorus.h @@ -0,0 +1,105 @@ +#ifndef _FMOD_DSP_CHORUS_H +#define _FMOD_DSP_CHORUS_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CHORUS + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + const int DSP_CHORUS_COSTABBITS = 13; + const int DSP_CHORUS_COSTABSIZE = (1 << DSP_CHORUS_COSTABBITS); + const int DSP_CHORUS_TABLERANGE = (DSP_CHORUS_COSTABSIZE * 4); + const int DSP_CHORUS_TABLEMASK = (DSP_CHORUS_TABLERANGE - 1); + const float DSP_CHORUS_MAXBUFFERLENGTHMS = 200.0f; + + #define TEMPBUFFSAMPLES 256 + #define TEMPBUFFHALFSAMPLES TEMPBUFFSAMPLES / 2 + + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + #define DSP_CHORUS_USECOSTAB + #endif + + class DSPChorus : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + FMOD_PPCALIGN16(float mDepth); + FMOD_PPCALIGN16(float mDryMix); + FMOD_PPCALIGN16(float mWetMix1); + FMOD_PPCALIGN16(float mWetMix2); + FMOD_PPCALIGN16(float mWetMix3); + FMOD_PPCALIGN16(float mFeedback); + FMOD_PPCALIGN16(float mDelay); + FMOD_PPCALIGN16(float mRateHz); + FMOD_PPCALIGN16(signed short *mChorusBuffer); + FMOD_PPCALIGN16(signed short *mChorusBufferMemory); + FMOD_PPCALIGN16(unsigned int mChorusBufferLength); + FMOD_PPCALIGN16(unsigned int mChorusBufferLengthBytes); + FMOD_PPCALIGN16(unsigned int mChorusBufferPosition); + FMOD_PPCALIGN16(float mChorusPosition[3]); + FMOD_PPCALIGN16(float mChorusTick); + FMOD_PPCALIGN16(float mChorusSpeed); + FMOD_PPCALIGN16(int mOutputRate); + unsigned short mOldSpeakerMask; + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + FMOD_PPCALIGN16(int mMinPos0_A); + FMOD_PPCALIGN16(int mMaxPos0_A); + FMOD_PPCALIGN16(int mMinPos0_B); + FMOD_PPCALIGN16(int mMaxPos0_B); + FMOD_PPCALIGN16(int mMinPos1_A); + FMOD_PPCALIGN16(int mMaxPos1_A); + FMOD_PPCALIGN16(int mMinPos1_B); + FMOD_PPCALIGN16(int mMaxPos1_B); + FMOD_PPCALIGN16(int mMinPos2_A); + FMOD_PPCALIGN16(int mMaxPos2_A); + FMOD_PPCALIGN16(int mMinPos2_B); + FMOD_PPCALIGN16(int mMaxPos2_B); + FMOD_PPCALIGN16(signed short mTempBuff0[4096 + 8]); + FMOD_PPCALIGN16(signed short mTempBuff1[4096 + 8]); + FMOD_PPCALIGN16(signed short mTempBuff2[4096 + 8]); +#endif + +#ifdef DSP_CHORUS_USECOSTAB + float mCosTab[DSP_CHORUS_COSTABSIZE]; + + FMOD_INLINE const float cosine(float x); + FMOD_INLINE const float sine(float x); +#else + FMOD_INLINE const float cosine(float x) { return FMOD_COS(x); } + FMOD_INLINE const float sine(float x) { return FMOD_SIN(x); } +#endif + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_codec.cpp b/src/fmod_dsp_codec.cpp new file mode 100755 index 0000000..1f12da2 --- /dev/null +++ b/src/fmod_dsp_codec.cpp @@ -0,0 +1,1124 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSPCODEC + +#include "fmod.h" +#include "fmod_channel_software.h" +#include "fmod_codeci.h" +#include "fmod_dspi.h" +#include "fmod_dsp_codec.h" +#include "fmod_sample_software.h" +#include "fmod_soundi.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" + #include <cell/dma.h> +#else + #include "fmod_systemi.h" +#endif + +#ifdef FMOD_SUPPORT_SENTENCING + #include "fmod_codec_fsb.h" +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspcodec; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPCodec::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPCodec::getDescriptionEx() +{ + FMOD_memset(&dspcodec, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspcodec.name, "FMOD DSP Codec"); + dspcodec.version = 0x00010100; + dspcodec.create = DSPCodec::createCallback; + dspcodec.release = DSPCodec::releaseCallback; + dspcodec.reset = DSPCodec::resetCallback; + dspcodec.read = DSPCodec::readCallback; + dspcodec.setposition = DSPCodec::setPositionCallback; + + dspcodec.numparameters = 0; + dspcodec.paramdesc = 0; + dspcodec.setparameter = DSPCodec::setParameterCallback; + dspcodec.getparameter = DSPCodec::getParameterCallback; + + dspcodec.mType = (FMOD_DSP_TYPE)FMOD_DSP_TYPE_CODECREADER; + dspcodec.mSize = sizeof(DSPCodec); + + return &dspcodec; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::createInternal() +{ + init(); + + mNoDMA->mNewPosition = mNewPosition = 0xffffffff; + mNoDMA->mSetPosIncrement = mSetPosIncrementPrev = 0; + mNoDMA->mLoopCountIncrement = mLoopCountIncrementPrev = 0; + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::release(bool freethis) +{ + FMOD_RESULT result; + + result = mCodec->mDescription.close(mCodec); + if (result != FMOD_OK) + { + return result; + } + +#ifdef PLATFORM_PS3 + result = DSPResampler::release(freethis); +#else + result = DSPResampler::release(false); + + if (freethis) + { + FMOD_Memory_FreeType(this, mWaveFormat.format == FMOD_SOUND_FORMAT_XMA ? FMOD_MEMORY_XBOX360_PHYSICAL : FMOD_MEMORY_NORMAL); + } +#endif + + return result; +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::releaseInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::resetInternal() +{ + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_SENTENCING +FMOD_RESULT DSPCodec::updateDSPCodec(SoundI *sound, int subsoundindex) +{ + Codec *chancodec, *soundcodec; + SampleSoftware *sample; + + #ifdef PLATFORM_PS3_SPU + + char samplesoftwaredma [sizeof(SampleSoftware) + 32] __attribute__ ((aligned (16))); // 336 + 32 bytes + char codecdma [sizeof(CodecFSB) + 32] __attribute__ ((aligned (16))); // 220 + 32 bytes + char waveformatdma [sizeof(FMOD_CODEC_WAVEFORMAT) + 32] __attribute__ ((aligned (16))); // 296 + 32 bytes + + unsigned int subsoundpointer = cellDmaGetUint32((uint64_t)&sound->mSubSound[subsoundindex], TAG1, TID, RID); + + FMOD_PS3_SPU_AlignedDMA((void **)&samplesoftwaredma, subsoundpointer, sizeof(SampleSoftware)); + + sample = (SampleSoftware *)(samplesoftwaredma); + chancodec = (Codec *)mCodec; + + FMOD_PS3_SPU_AlignedDMA((void **)&codecdma, (unsigned int)(sample->mCodec ? sample->mCodec : sample->mSubSoundParent->mCodec), sizeof(CodecFSB)); + + soundcodec = (Codec *)codecdma; + + + /* + Now, need to point the codec pointers to the right stuff + */ + + if (soundcodec->mType == FMOD_SOUND_TYPE_FSB) + { + soundcodec->mDescription.getwaveformat = CodecFSB::getWaveFormatCallback; + } + else + { + FMOD_PS3_SPU_AlignedDMA((void **)&waveformatdma, (unsigned int)soundcodec->waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + + soundcodec->waveformat = (FMOD_CODEC_WAVEFORMAT *)waveformatdma; + soundcodec->mDescription.getwaveformat = Codec::defaultGetWaveFormat; + } + + #else + + sample = SAFE_CAST(SampleSoftware, sound->mSubSound[subsoundindex]); /* PS3 - DMA THIS IN FROM PPU ADDRESS */ + chancodec = SAFE_CAST(Codec, mCodec); /* PS3 - DMA THIS IN FROM PPU ADDRESS */ + soundcodec = sample->mCodec ? sample->mCodec : sample->mSubSoundParent->mCodec; /* PS3 - DMA THIS IN FROM PPU ADDRESS */ + + #endif + + if (!soundcodec || !chancodec->waveformat) + { + return FMOD_ERR_INTERNAL; + } + + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + if (!(sample->mMode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + return FMOD_ERR_FORMAT; + } + #endif + + soundcodec->mDescription.getwaveformat(soundcodec, sample->mSubSoundIndex, chancodec->waveformat); /* Update the channelcodec using the sound codec wavedata */ + + /* + Reset the 'file' pointer to point to the new sound's buffer, and reset pos/length etc. + */ + #ifdef PLATFORM_PS3 + mMemoryFile.init(mSystem, sample->mLengthBytes, 2048); + mMemoryFile.mBuffer = mMemoryFile.mBufferMemoryPS3; + #else + mMemoryFile.init(mSystem, sample->mLengthBytes, 0); + #endif + + mMemoryFile.mPosition = 0; + mMemoryFile.mMem = sample->mBuffer; + + /* + Copy codec specific things from the source sound to the dspcodec used in this channel. + */ + #if defined(FMOD_SUPPORT_FSB) && defined(FMOD_SUPPORT_XMA) + if (sample->mType == FMOD_SOUND_TYPE_FSB && sample->mFormat == FMOD_SOUND_FORMAT_XMA) + { + CodecFSB *fsb = (CodecFSB *)soundcodec; + CodecXMA *destxma = (CodecXMA *)chancodec; + int **seektable = (int **)fsb->plugindata; + + if (destxma->mXMASeekable) + { + destxma->mSeekTable = seektable[sample->mSubSoundIndex]; + } + + fsb->mDescription.getwaveformat(fsb, sample->mSubSoundIndex, chancodec->waveformat); + } + #endif + + return FMOD_OK; +} +#endif + +#if defined(PLATFORM_PS3_SPU) && defined(FMOD_SUPPORT_SENTENCING) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void DSPCodec_dmasubsoundlist(SoundI **soundi, void *soundimem, void *soundlistmem, int subsoundlistnum) +{ + void *lsaddress = soundimem; + + FMOD_PS3_SPU_AlignedDMA(&lsaddress, (unsigned int)*soundi, sizeof(SampleSoftware)); + + *soundi = (SoundI *)lsaddress; + + lsaddress = soundlistmem; + + FMOD_PS3_SPU_AlignedDMA(&lsaddress, (unsigned int)(*soundi)->mSubSoundList, subsoundlistnum * sizeof(SoundSentenceEntry)); + + (*soundi)->mSubSoundList = (SoundSentenceEntry *)lsaddress; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::readInternal(signed short *inbuffer, signed short *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + FMOD_RESULT result; + unsigned int size = length; + unsigned int bytespersample = 0; + int retries = 0; + SoundI *sound = (SoundI *)mDescription.userdata; + + SoundI::getBytesFromSamples(1, &bytespersample, mDescription.channels, mDescription.mFormat); + + if (mFlags & FMOD_DSP_FLAG_QUEUEDFORDISCONNECT) + { + FMOD_memset(outbuffer, 0, length * bytespersample); + return FMOD_OK; + } + + /* + mNewPositon for if setPosition is called on SPU (this happens with negative frequency playback on PS3) + */ + if ((mNoDMA->mSetPosIncrement > mSetPosIncrementPrev && mNoDMA->mNewPosition != 0xFFFFFFFF) || mNewPosition != 0xFFFFFFFF) + { + unsigned int newpos = (mNewPosition != 0xFFFFFFFF) ? mNewPosition : mNoDMA->mNewPosition; + +#ifdef FMOD_SUPPORT_SENTENCING + if (sound) + { + unsigned int soundoffset; + SoundSentenceEntry *entry; + + soundoffset = 0; + mSubSoundListCurrent = 0; + + #ifdef PLATFORM_PS3_SPU + + char soundidmamem[sizeof(SampleSoftware) + 32]; // 336 + 32 bytes + char *soundidma = (char *)FMOD_ALIGNPOINTER(soundidmamem, 16); + + char subsoundlistdmamem[FMOD_PS3_SUBSOUNDLISTMAXITEMS * sizeof(SoundSentenceEntry) + 32]; // 4096 + 32 bytes + char *subsoundlistdma = (char *)FMOD_ALIGNPOINTER(subsoundlistdmamem, 16); + + DSPCodec_dmasubsoundlist(&sound, soundidma, subsoundlistdma, mSubSoundListNum); + + #endif + + entry = &sound->mSubSoundList[0]; + + while (soundoffset + entry[mSubSoundListCurrent].mLength < newpos) + { + soundoffset += entry[mSubSoundListCurrent].mLength; + mSubSoundListCurrent++; + } + + newpos -= soundoffset; + + result = updateDSPCodec(sound, entry[mSubSoundListCurrent].mIndex); /* Reset the codec pointer with the new subsound's info. */ + if (result != FMOD_OK) + { + return result; + } + + } +#endif + mPosition = (mNewPosition != 0xFFFFFFFF) ? mNewPosition : mNoDMA->mNewPosition; + + mCodec->reset(); + mCodec->setPosition(0, newpos, FMOD_TIMEUNIT_PCM); + + mSetPosIncrementPrev = mNoDMA->mSetPosIncrement; + mNewPosition = 0xFFFFFFFF; + } + + if (mNoDMA->mNewLoopCount >= -1 && mNoDMA->mLoopCountIncrement > mLoopCountIncrementPrev) + { + mLoopCount = mNoDMA->mNewLoopCount; + mNoDMA->mNewLoopCount = -2; + + mLoopCountIncrementPrev = mNoDMA->mLoopCountIncrement; + } + + while (size) + { + unsigned int endpoint, toread; + unsigned int read; + + if (mNoDMA->mMode & FMOD_LOOP_NORMAL && mLoopCount) + { + endpoint = mNoDMA->mLoopStart + mNoDMA->mLoopLength - 1; + } + else + { + if (mCodec->mFlags & FMOD_CODEC_ACCURATELENGTH) + { + endpoint = mLength - 1; + } + else + { + endpoint = (unsigned int)-1; + } + } + + toread = size; + + if (mPosition > endpoint) + { + toread = 0; + } + else if (mPosition + toread > endpoint) + { + toread = (endpoint - mPosition) + 1; + } + + result = mCodec->read(outbuffer, toread * bytespersample, &read); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + read /= bytespersample; + + outbuffer += (read * inchannels); + mPosition += read; + + if (read <= size) + { + size -= read; + } + else + { + size = 0; + } + +#if 0 + if (!read) + { + if (retries > 255) /* Sometimes the XMA codec returns 0 if it is not ready yet. 20 is the worst i've seen playing 32 xmas so 255 should be plenty. */ + { + result = FMOD_ERR_FILE_EOF; + #ifdef PLATFORM_XENON + OutputDebugString("FMOD ************* ERROR! XMA hardware timeout.\n"); + #endif + } + retries++; + } +#endif + + if ((mDirection == DSPRESAMPLER_SPEEDDIR_BACKWARDS && mPosition == read) || + (result == FMOD_ERR_FILE_EOF || mPosition > endpoint)) + { +#ifdef FMOD_SUPPORT_SENTENCING + if (mDirection == DSPRESAMPLER_SPEEDDIR_FORWARDS && sound && mPosition < endpoint && mSubSoundListCurrent < mSubSoundListNum - 1) + { + SoundSentenceEntry *entry; + int firstcurrent = 0; + + mSubSoundListCurrent++; + + /* PS3 - DMA SOUNDI, then DMA SUBSOUNDLIST (make a function to keep it neat) */ + + #ifdef PLATFORM_PS3_SPU + + char soundidmamem[sizeof(SampleSoftware) + 32]; // 336 + 32 bytes + char *soundidma = (char *)FMOD_ALIGNPOINTER(soundidmamem, 16); + + char subsoundlistdmamem[FMOD_PS3_SUBSOUNDLISTMAXITEMS * sizeof(SoundSentenceEntry) + 32]; // 4096 + 32 bytes + char *subsoundlistdma = (char *)FMOD_ALIGNPOINTER(subsoundlistdmamem, 16); + + DSPCodec_dmasubsoundlist(&sound, soundidma, subsoundlistdma, mSubSoundListNum); + + #endif + + entry = &sound->mSubSoundList[0]; + + + firstcurrent = mSubSoundListCurrent; + while (!entry[mSubSoundListCurrent].mLength) + { + mSubSoundListCurrent++; + + if (mSubSoundListCurrent >= mSubSoundListNum) + { + mSubSoundListCurrent = 0; + } + + if (mSubSoundListCurrent == firstcurrent) /* Went right around and found no entries. EOF */ + { + return FMOD_ERR_FILE_EOF; + } + } + + result = updateDSPCodec(sound, sound->mSubSoundList[mSubSoundListCurrent].mIndex); /* Reset the codec pointer with the new subsound's info. */ + if (result != FMOD_OK) + { + return result; + } + } + else +#endif + + if (mNoDMA->mMode & FMOD_LOOP_NORMAL && mLoopCount && retries <= 255) + { + unsigned int newpos = mNoDMA->mLoopStart; + +#ifdef FMOD_SUPPORT_SENTENCING + if (sound) + { + unsigned int soundoffset; + SoundSentenceEntry *entry; + + soundoffset = 0; + mSubSoundListCurrent = 0; + + /* PS3 - DMA SOUNDI, then DMA SUBSOUNDLIST (make a function to keep it neat) */ + + #ifdef PLATFORM_PS3_SPU + + char soundidmamem[sizeof(SampleSoftware) + 32]; // 336 + 32 bytes + char *soundidma = (char *)FMOD_ALIGNPOINTER(soundidmamem, 16); + + char subsoundlistdmamem[FMOD_PS3_SUBSOUNDLISTMAXITEMS * sizeof(SoundSentenceEntry) + 32]; // 4096 + 32 bytes + char *subsoundlistdma = (char *)FMOD_ALIGNPOINTER(subsoundlistdmamem, 16); + + DSPCodec_dmasubsoundlist(&sound, soundidma, subsoundlistdma, mSubSoundListNum); + + #endif + + entry = &sound->mSubSoundList[0]; + + while (soundoffset + entry[mSubSoundListCurrent].mLength < newpos || !entry[mSubSoundListCurrent].mLength) + { + soundoffset += entry[mSubSoundListCurrent].mLength; + mSubSoundListCurrent++; + + if (mSubSoundListCurrent >= mSubSoundListNum) + { + return FMOD_ERR_FILE_EOF; + } + } + + newpos -= soundoffset; + + result = updateDSPCodec(sound, entry[mSubSoundListCurrent].mIndex); /* Reset the codec pointer with the new subsound's info. */ + if (result != FMOD_OK) + { + return result; + } + + } +#endif + mPosition = mNoDMA->mLoopStart; + mCodec->setPosition(0, newpos, FMOD_TIMEUNIT_PCM); + + if (mLoopCount > 0) + { + mLoopCount--; + } + } + else + { + if (size) + { + FMOD_memset(outbuffer, 0, size * bytespersample); + } + return FMOD_ERR_FILE_EOF; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::setPositionInternal(unsigned int position, bool fromspu) +{ +#ifdef PLATFORM_PS3 + if (fromspu) + { + mNewPosition = position; + } + else +#endif + { + mNoDMA->mNewPosition = position; + + mNoDMA->mSetPosIncrement++; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::getPositionInternal(unsigned int *position) +{ + if (mNoDMA->mSetPosIncrement > mSetPosIncrementPrev) + { + *position = mNoDMA->mNewPosition; + } + else + { + *position = mPosition; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::setParameterInternal(int index, float value) +{ + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodec::getParameterInternal(int index, float *value, char *valuestr) +{ + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->readInternal((signed short *)inbuffer, (signed short *)outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::setPositionCallback(FMOD_DSP_STATE *dsp, unsigned int pos) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->setPositionInternal(pos); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCodec::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPCodec *dspcodec = (DSPCodec *)dsp; + + return dspcodec->getParameterInternal(index, value, valuestr); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPCodec::getMemoryUsedImpl(MemoryTracker *tracker) +{ + #ifdef PLATFORM_PS3 +// DMAFile mMemoryFile; + #else +// MemoryFile mMemoryFile; + #endif + +// Codec *mCodec; + + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +#ifdef FMOD_SUPPORT_MPEG + +FMOD_RESULT DSPCodecMPEG::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSPCODEC, sizeof(*this)); + +// CodecMPEG mCodecMemory; +// CHECK_RESULT(mCodecMemory.getMemoryUsed(tracker)); + +//??? already counted up here CHECK_RESULT(DSPCodec::getMemoryUsed(tracker)); + + return FMOD_OK; +} + +#endif +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +#ifdef FMOD_SUPPORT_IMAADPCM + +FMOD_RESULT DSPCodecADPCM::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSPCODEC, sizeof(*this)); + +// char mResampleBufferMemory[((64 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. +// CodecWav mCodecMemory; + +// CHECK_RESULT(DSPCodec::getMemoryUsed(tracker)); + + return FMOD_OK; +} + +#endif +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +#ifdef FMOD_SUPPORT_XMA + +FMOD_RESULT DSPCodecXMA::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSPCODEC, sizeof(*this)); + +// char mResampleBufferMemory[((512 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. +// CodecXMA mCodecMemory; +// CodecXMA_DecoderHW mCodecMemoryBlock; + +// CHECK_RESULT(DSPCodec::getMemoryUsed(tracker)); + + return FMOD_OK; +} + +#endif +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +#ifdef FMOD_SUPPORT_CELT + +FMOD_RESULT DSPCodecCELT::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSPCODEC, sizeof(*this)); + +// char mResampleBufferMemory[((512 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. +// CodecXMA mCodecMemory; +// CodecXMA_DecoderHW mCodecMemoryBlock; + +// CHECK_RESULT(DSPCodec::getMemoryUsed(tracker)); + + return FMOD_OK; +} + +#endif +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +#ifdef FMOD_SUPPORT_RAW + +FMOD_RESULT DSPCodecRaw::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSPCODEC, sizeof(*this)); + +// char mResampleBufferMemory[((256 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 16 * 2) + 16]; // *16 = 16 channel max. *2 = double buffer. +// CodecRaw mCodecMemory; + +// CHECK_RESULT(DSPCodec::getMemoryUsed(tracker)); + + return FMOD_OK; +} + +#endif +#endif + +} + +#endif diff --git a/src/fmod_dsp_codec.h b/src/fmod_dsp_codec.h new file mode 100755 index 0000000..544477f --- /dev/null +++ b/src/fmod_dsp_codec.h @@ -0,0 +1,170 @@ +#ifndef _FMOD_DSP_CODEC_H +#define _FMOD_DSP_CODEC_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSPCODEC + +#include "fmod.h" +#include "fmod_dsp_codecpool.h" +#include "fmod_dsp_resampler.h" + +#ifdef PLATFORM_PS3 +#include "fmod_file_dma.h" +#else +#include "fmod_file_memory.h" +#endif + +#ifdef FMOD_SUPPORT_MPEG + #include "fmod_codec_mpeg.h" +#endif + +#ifdef FMOD_SUPPORT_IMAADPCM + #include "fmod_codec_wav.h" +#endif + +#ifdef FMOD_SUPPORT_XMA + #include "fmod_codec_xma.h" +#endif + +#ifdef FMOD_SUPPORT_CELT + #include "fmod_codec_celt.h" +#endif + +#include "fmod_codec_raw.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class DSPCodecPool; + class DSPCodecPoolInitCleanup; + class SoundI; + + class DSPCodec : public DSPResampler + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class DSPCodecPool; + friend class DSPResampler; + friend class DSPCodecPoolInitCleanup; + + private: + + FMOD_RESULT createInternal(); + FMOD_RESULT release(bool freethis = true); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(signed short *inbuffer, signed short *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setPositionInternal(unsigned int pos, bool fromspu = false); + FMOD_RESULT getPositionInternal(unsigned int *pos); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_SENTENCING + FMOD_RESULT updateDSPCodec(SoundI *sound, int subsoundindex); +#endif + + public: + + DSPCodecPool *mPool; + + #ifdef PLATFORM_PS3 + DMAFile mMemoryFile; + #else + MemoryFile mMemoryFile; + #endif + + FMOD_PPCALIGN16(unsigned int mPosition); + FMOD_PPCALIGN16(unsigned int mNewPosition); + FMOD_PPCALIGN16(unsigned int mSetPosIncrementPrev); + FMOD_PPCALIGN16(unsigned int mLoopCountIncrementPrev); + FMOD_PPCALIGN16(int mPoolIndex); + FMOD_PPCALIGN16(int mSubSoundListCurrent); + FMOD_PPCALIGN16(int mSubSoundListNum); + + FMOD_CODEC_WAVEFORMAT mWaveFormat; + Codec *mCodec; + + FMOD_RESULT freeFromPool() { mPool->mAllocated[mPoolIndex] = false; return FMOD_OK; } + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_DSP_STATE *dsp, unsigned int pos); + static FMOD_RESULT F_CALLBACK getPositionCallback(FMOD_DSP_STATE *dsp, unsigned int *pos); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); + }; + +#ifdef FMOD_SUPPORT_MPEG + class DSPCodecMPEG : public DSPCodec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + char mResampleBufferMemory[((1152 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. + CodecMPEG mCodecMemory; + FMOD_PPCALIGN16(CodecMPEG_MemoryBlock mCodecMemoryBlock); /* CellMP3Context for Sony decoder needs to be 16 byte aligned */ + }; +#endif + +#ifdef FMOD_SUPPORT_IMAADPCM + class DSPCodecADPCM : public DSPCodec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + char mResampleBufferMemory[((64 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. + CodecWav mCodecMemory; + }; +#endif + +#ifdef FMOD_SUPPORT_XMA + class DSPCodecXMA : public DSPCodec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + char mResampleBufferMemory[((512 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. + CodecXMA mCodecMemory; + #ifdef FMOD_SUPPORT_XMA_NEWHAL + CodecXMA_DecoderData mCodecMemoryBlock; + #else + CodecXMA_DecoderHW mCodecMemoryBlock; + #endif + }; +#endif + +#ifdef FMOD_SUPPORT_CELT + class DSPCodecCELT : public DSPCodec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + char mResampleBufferMemory[((FMOD_CELT_FRAMESIZESAMPLES + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 2 * 2) + 16]; // *2 = stereo max. *2 = double buffer. + CodecCELT mCodecMemory; + }; +#endif + +#ifdef FMOD_SUPPORT_RAW + class DSPCodecRaw : public DSPCodec + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + char mResampleBufferMemory[((256 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 16 * 2) + 16]; // *16 = 16 channel max. *2 = double buffer. + CodecRaw mCodecMemory; + }; +#endif + +} + +#endif + +#endif + diff --git a/src/fmod_dsp_codecpool.cpp b/src/fmod_dsp_codecpool.cpp new file mode 100755 index 0000000..8101395 --- /dev/null +++ b/src/fmod_dsp_codecpool.cpp @@ -0,0 +1,520 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSPCODEC + +#include "fmod_channel_software.h" +#ifdef FMOD_SUPPORT_MPEG +#include "fmod_codec_mpeg.h" +#endif +#ifdef FMOD_SUPPORT_XMA +#include "fmod_codec_xma.h" +#endif +#ifdef FMOD_SUPPORT_IMAADPCM +#include "fmod_codec_wav_imaadpcm.h" +#endif +#include "fmod_autocleanup.h" +#include "fmod_codeci.h" +#include "fmod_dspi.h" +#include "fmod_dsp_codec.h" +#include "fmod_dsp_codecpool.h" +#include "fmod_pluginfactory.h" +#include "fmod_systemi.h" +#include "fmod_localcriticalsection.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +class DSPCodecPoolInitCleanup +{ +public: + DSPCodecPoolInitCleanup() + : mPool(0), mPoolSize(0) + { } + + void setPool(DSPCodec **pool, int poolsize) + { + mPool = pool; + mPoolSize = poolsize; + } + + void cleanup(bool) + { + if (mPool) + { + for (int i = 0; i < mPoolSize; ++i) + { + if (mPool[i]) + { + mPool[i]->release(); + } + } + } + } + +private: + DSPCodec **mPool; + int mPoolSize; +}; + +FMOD_RESULT DSPCodecPool::init(FMOD_DSP_CATEGORY category, int resamplerpcmblocksize, int numdspcodecs) +{ + int count; + FMOD_DSP_DESCRIPTION_EX descriptionex; + AutoCleanup<bool, DSPCodecPoolInitCleanup> initCleanup(true); + + if (!mSystem->mSoftware) + { + return FMOD_ERR_NEEDSSOFTWARE; + } + + if (numdspcodecs >= FMOD_DSP_CODECPOOL_MAXCODECS) + { + return FMOD_ERR_INVALID_PARAM; + } + + LocalCriticalSection crit(mSystem->mDSPCodecPoolInitCrit, true); + + if (mNumDSPCodecs > 0) + { + // already initialised (probably by another thread) + if(mNumDSPCodecs != numdspcodecs) + { + return FMOD_ERR_INTERNAL; + } + else + { + return FMOD_OK; + } + } + + mPool = (DSPCodec **)FMOD_Memory_CallocType(sizeof(DSPCodec *) * numdspcodecs, FMOD_MEMORY_PERSISTENT); + if (!mPool) + { + return FMOD_ERR_MEMORY; + } + initCleanup.setPool(mPool, numdspcodecs); + + #if defined(FMOD_SUPPORT_XMA) && defined(FMOD_SUPPORT_XMA_NEWHAL) + if (category == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + mFileBufferPool = (unsigned char *)FMOD_Memory_CallocType(((2048 * 2) * numdspcodecs) + 2048, FMOD_MEMORY_PERSISTENT | FMOD_MEMORY_XBOX360_PHYSICAL); /* + 2048 for alignment. */ + if (!mFileBufferPool) + { + return FMOD_ERR_MEMORY; + } + } + #endif + + descriptionex = *DSPCodec::getDescriptionEx(); + + for (count = 0; count < numdspcodecs; count++) + { + FMOD_RESULT result; + DSPI *dsp; + DSPCodec *dspcodec; + + descriptionex.channels = 2; // Maximum possible stereo? This is for memory allocation. + descriptionex.mFormat = FMOD_SOUND_FORMAT_PCM16; + descriptionex.mCategory = category; + descriptionex.mResamplerBlockLength = resamplerpcmblocksize; + + if (0) + { + } + #ifdef FMOD_SUPPORT_MPEG + if (category == FMOD_DSP_CATEGORY_DSPCODECMPEG) + { + descriptionex.mSize = sizeof(DSPCodecMPEG); + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (category == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + descriptionex.mSize = sizeof(DSPCodecXMA); + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (category == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { + descriptionex.mSize = sizeof(DSPCodecADPCM); + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (category == FMOD_DSP_CATEGORY_DSPCODECCELT) + { + descriptionex.mSize = sizeof(DSPCodecCELT); + } + #endif + #ifdef FMOD_SUPPORT_RAW + else if (category == FMOD_DSP_CATEGORY_DSPCODECRAW) + { + descriptionex.mResamplerBlockLength = 256; + descriptionex.mSize = sizeof(DSPCodecRaw); + } + #endif + else + { + return FMOD_ERR_FORMAT; + } + + result = mSystem->createDSP(&descriptionex, &dsp); + if (result != FMOD_OK) + { + return result; + } + AutoRelease<DSPI> dsp_cleanup(dsp); + + dspcodec = SAFE_CAST(DSPCodec, dsp); + + dspcodec->mFlags = 0; + + if (0) + { + } + #ifdef FMOD_SUPPORT_MPEG + if (category == FMOD_DSP_CATEGORY_DSPCODECMPEG) + { + DSPCodecMPEG *dspcodecmpeg = SAFE_CAST(DSPCodecMPEG, dspcodec); + CodecMPEG *mpeg = &dspcodecmpeg->mCodecMemory; + FMOD_CODEC_DESCRIPTION_EX *desc; + + result = mSystem->mPluginFactory->getCodec(mSystem->mMPEGPluginHandle, &desc); + if (result != FMOD_OK) + { + return result; + } + FMOD_memcpy(&mpeg->mDescription, desc, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + mpeg->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + dspcodec->mCodec = mpeg; + dspcodec->mWaveFormat.format = FMOD_SOUND_FORMAT_MPEG; + + mpeg->mMemoryBlock = &dspcodecmpeg->mCodecMemoryBlock; + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (category == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + DSPCodecXMA *dspcodecxma = SAFE_CAST(DSPCodecXMA, dspcodec); + CodecXMA *xma = &dspcodecxma->mCodecMemory; + + FMOD_memcpy(&xma->mDescription, CodecXMA::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + xma->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + dspcodec->mCodec = xma; + dspcodec->mWaveFormat.format = FMOD_SOUND_FORMAT_XMA; + +#ifdef FMOD_SUPPORT_XMA_NEWHAL + xma->mDecoder = &dspcodecxma->mCodecMemoryBlock; + xma->mDecoder->mFileBuffer = (unsigned char *)(FMOD_ALIGNPOINTER(mFileBufferPool, 2048) + (2048 * 2 * count)); +#else + xma->mDecoder = xma->mDecoderHW = &dspcodecxma->mCodecMemoryBlock; +#endif + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (category == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { + DSPCodecADPCM *dspcodecadpcm = SAFE_CAST(DSPCodecADPCM, dspcodec); + CodecWav *wav = &dspcodecadpcm->mCodecMemory; + FMOD_CODEC_DESCRIPTION_EX *desc; + + result = mSystem->mPluginFactory->getCodec(mSystem->mWAVPluginHandle, &desc); + if (result != FMOD_OK) + { + return result; + } + FMOD_memcpy(&wav->mDescription, desc, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + wav->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + dspcodec->mCodec = wav; + dspcodec->mWaveFormat.format = FMOD_SOUND_FORMAT_IMAADPCM; + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (category == FMOD_DSP_CATEGORY_DSPCODECCELT) + { + DSPCodecCELT *dspcodeccelt = SAFE_CAST(DSPCodecCELT, dspcodec); + CodecCELT *celt = &dspcodeccelt->mCodecMemory; + FMOD_CODEC_DESCRIPTION_EX *desc; + + result = mSystem->mPluginFactory->getCodec(mSystem->mCELTPluginHandle, &desc); + if (result != FMOD_OK) + { + return result; + } + + FMOD_memcpy(&celt->mDescription, desc, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + celt->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + dspcodec->mCodec = celt; + dspcodec->mWaveFormat.format = FMOD_SOUND_FORMAT_CELT; + } + #endif + #ifdef FMOD_SUPPORT_RAW + else if (category == FMOD_DSP_CATEGORY_DSPCODECRAW) + { + DSPCodecRaw *dspcodecraw = SAFE_CAST(DSPCodecRaw, dspcodec); + CodecRaw *raw = &dspcodecraw->mCodecMemory; + + FMOD_memcpy(&raw->mDescription, CodecRaw::getDescriptionEx(), sizeof(FMOD_CODEC_DESCRIPTION_EX)); + raw->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + + dspcodec->mCodec = raw; + dspcodec->mWaveFormat.format = FMOD_SOUND_FORMAT_PCM16; + } + #endif + else + { + return FMOD_ERR_FORMAT; + } + + dspcodec->mCodec->mFile = &dspcodec->mMemoryFile; + dspcodec->mCodec->waveformat = &dspcodec->mWaveFormat; + dspcodec->mCodec->mSrcDataOffset = 0; /* Raw data is going to be placed right at the start. */ + dspcodec->mCodec->mFlags |= FMOD_CODEC_ACCURATELENGTH; + dspcodec->mPool = this; + dspcodec->mPoolIndex = count; + dspcodec->setFinished(true, true); /* Start off finished so that it can be allocated. */ + mAllocated[count] = false; + + dsp_cleanup.releasePtr(); + mPool[count] = dspcodec; + } + + // set this last so places that access it don't jump in too early + mNumDSPCodecs = numdspcodecs; + + initCleanup.releasePtr(); + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodecPool::close() +{ + if (mPool) + { + int count; + + for (count = 0; count < mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mPool[count]); + + if (dspcodec) + { + dspcodec->mCodec->mFile = 0; /* Stop it trying to free our static memory file. */ + dspcodec->mCodec->mReadBuffer = 0; /* Stop it trying to free the global read buffer multiple times if it happens to be pointing to it. */ + dspcodec->mCodec->mWaveFormatMemory = 0; /* Stop it trying to free the sound's wave format if it happens to be pointing to it. */ + + mPool[count]->release(); + } + } + + FMOD_Memory_Free(mPool); + mPool = 0; + mNumDSPCodecs = 0; + } + + if (mReadBuffer) + { + FMOD_Memory_Free(mReadBuffer); + mReadBuffer = 0; + } + + #if defined(FMOD_SUPPORT_XMA) && defined(FMOD_SUPPORT_XMA_NEWHAL) + if (mFileBufferPool) + { + FMOD_Memory_FreeType(mFileBufferPool, FMOD_MEMORY_PERSISTENT | FMOD_MEMORY_XBOX360_PHYSICAL); + mFileBufferPool = 0; + } + #endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodecPool::alloc(DSPCodec **dspcodec) +{ + int count; + + for (count = 0; count < mNumDSPCodecs; count++) + { + bool finished; + + mPool[count]->getFinished(&finished); + + if (!mAllocated[count] && finished) + { + mAllocated[count] = true; + *dspcodec = mPool[count]; + return FMOD_OK; + } + } + + return FMOD_ERR_CHANNEL_ALLOC; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCodecPool::areAnyFree() +{ + int count; + + for (count = 0; count < mNumDSPCodecs; count++) + { + bool finished; + + mPool[count]->getFinished(&finished); + + if (!mAllocated[count] && finished) + { + return FMOD_OK; + } + } + + return FMOD_ERR_CHANNEL_ALLOC; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPCodecPool::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mPool) + { + tracker->add(false, FMOD_MEMBITS_DSPCODEC, sizeof(DSPCodec *) * mNumDSPCodecs); + + for (int i=0; i < mNumDSPCodecs; i++) + { + if (mPool[i]) + { + switch (mPool[i]->mDescription.mCategory) + { +#ifdef FMOD_SUPPORT_MPEG + case FMOD_DSP_CATEGORY_DSPCODECMPEG : + { + CHECK_RESULT(((DSPCodecMPEG *)mPool[i])->getMemoryUsed(tracker)); + break; + } +#endif + +#ifdef FMOD_SUPPORT_IMAADPCM + case FMOD_DSP_CATEGORY_DSPCODECADPCM : + { + CHECK_RESULT(((DSPCodecADPCM *)mPool[i])->getMemoryUsed(tracker)); + break; + } +#endif + +#ifdef FMOD_SUPPORT_XMA + case FMOD_DSP_CATEGORY_DSPCODECXMA : + { + CHECK_RESULT(((DSPCodecXMA *)mPool[i])->getMemoryUsed(tracker)); + break; + } +#endif + +#ifdef FMOD_SUPPORT_CELT + case FMOD_DSP_CATEGORY_DSPCODECCELT : + { + CHECK_RESULT(((DSPCodecCELT *)mPool[i])->getMemoryUsed(tracker)); + break; + } +#endif + +#ifdef FMOD_SUPPORT_RAW + case FMOD_DSP_CATEGORY_DSPCODECRAW : + { + CHECK_RESULT(((DSPCodecRaw *)mPool[i])->getMemoryUsed(tracker)); + break; + } +#endif + + default : + break; + } + } + } + } + + return FMOD_OK; +} + +#endif + +} + +#endif diff --git a/src/fmod_dsp_codecpool.h b/src/fmod_dsp_codecpool.h new file mode 100755 index 0000000..b39067e --- /dev/null +++ b/src/fmod_dsp_codecpool.h @@ -0,0 +1,50 @@ +#ifndef _FMOD_DSP_CODECPOOL_H +#define _FMOD_DSP_CODECPOOL_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSPCODEC + +#include "fmod_dspi.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class DSPI; + class SystemI; + class DSPCodec; + + #define FMOD_DSP_CODECPOOL_MAXCODECS 256 + + class DSPCodecPool + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class DSPCodec; + + public: + + SystemI *mSystem; + int mNumDSPCodecs; + DSPCodec **mPool; + bool mAllocated[FMOD_DSP_CODECPOOL_MAXCODECS]; + unsigned char *mReadBuffer; +#if defined(FMOD_SUPPORT_XMA) && defined(FMOD_SUPPORT_XMA_NEWHAL) + unsigned char *mFileBufferPool; +#endif + + FMOD_RESULT init(FMOD_DSP_CATEGORY category, int resamplerpcmblocksize, int numdspcodecs); + FMOD_RESULT close(); + + FMOD_RESULT alloc(DSPCodec **dspcodec); + FMOD_RESULT areAnyFree(); + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_compressor.cpp b/src/fmod_dsp_compressor.cpp new file mode 100755 index 0000000..8511ca4 --- /dev/null +++ b/src/fmod_dsp_compressor.cpp @@ -0,0 +1,655 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_COMPRESSOR + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_compressor.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_compressor_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspcompressor; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPCompressor::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#define hold_constant (0.0025f) + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dspcompressor_param[4] = +{ + { -60.0f, 0.0f, 0.0f, "Threshold", "dB", "Compressor threshold [-60dB, 0dB] Default = 0dB." }, + { 10.0f, 200.0f, 50.0f, "Attack", "ms", "Compressor attack time. [10ms,200ms] Default = 50ms." }, + { 20.0f, 1000.0f, 50.0f, "Release", "ms", "Compressor release time. [10ms,1000ms] Default = 50ms." }, + { 0.0f, 30.0f, 0.0f, "Make up gain", "dB", "Compressor make up gain [0dB, 30dB] Default = 0dB." } +}; + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPCompressor::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dspcompressor, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspcompressor.name, "FMOD Compressor"); + dspcompressor.version = 0x00010100; + dspcompressor.create = DSPCompressor::createCallback; + + #ifdef PLATFORM_PS3 + dspcompressor.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_compressor_pic_start; /* SPU PIC entry address */ + #else + dspcompressor.read = DSPCompressor::readCallback; + #endif + + dspcompressor.numparameters = sizeof(dspcompressor_param) / sizeof(dspcompressor_param[0]); + dspcompressor.paramdesc = dspcompressor_param; + dspcompressor.setparameter = DSPCompressor::setParameterCallback; + dspcompressor.getparameter = DSPCompressor::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspcompressor.getmemoryused = &DSPCompressor::getMemoryUsedCallback; +#endif + + dspcompressor.mType = FMOD_DSP_TYPE_COMPRESSOR; + dspcompressor.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspcompressor.mSize = sizeof(DSPCompressor); +#else + dspcompressor.read = DSPCompressor::readCallback; /* We only care about read function on SPU */ +#endif + return &dspcompressor; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCompressor::createInternal() +{ + init(); + + int count; + /* + Setup ... + */ + for (count = 0; count < mDescription.numparameters; count++) + { + setParameter(count, mDescription.paramdesc[count].defaultval); + } + + for(count = 0; count < DSP_MAXLEVELS_MAX; count++) + { + mMaxChannelIn[count] = 0.0f; + } + mGain = 1.0f; + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCompressor::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int sample; + int channel; + float in_abs, x; + float holdconstant = mHoldConstant; /* Copy members for better thread safety. */ + float threshold_lin = mThreshold_lin; + float attack_constant = mAttack_constant; + float release_constant = mRelease_constant; + float gainmakeup_lin = mGainMakeup_lin; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No speaker channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + /* + if ( (++peakTimer_ >= peakHold_) || (keyLink > maxPeak_) ) { + // if either condition is met: + peakTimer_ = 0; // reset peak timer + maxPeak_ = keyLink; // assign new peak to max peak + } + // attack/release + if ( maxPeak_ > env_ ) + att_.run( maxPeak_, env_ ); // run attack phase + else + rel_.run( maxPeak_, env_ ); // run release phase + */ + + if ((speakermask & ((1 << inchannels)-1)) != ((1 << inchannels)-1)) + { + for (sample = 0; sample < length; sample++) + { + unsigned int base = sample * inchannels; + float *sample_in = &(inbuffer[base]); + float *sample_out = &(outbuffer[base]); + float in_max = 0.0f; + float over; + + for (channel = 0; channel < inchannels; channel++) + { + if ((1 << channel) & speakermask) + { + float *m; + + x = sample_in[channel]; + m = &(mMaxChannelIn[channel]); + + in_abs = (x<0.0f) ? -x : x; // ABS input + + *m -= holdconstant; // Leak hold + + if (in_abs > *m) // Hold new max + { + *m = in_abs; + } + + if (*m > in_max) // Update total max + { + in_max = *m; + } + } + } + + // Note: This uses linear gain curves, not dB + // dB is better but more costly + + // Over = linear ratio of input:threshold + over = in_max/threshold_lin; + + // Apply attack or release curve to over + // to obtain gain reduction + if (over > 1.0f) + { + mGain = over + attack_constant * (mGain-over); + } + else + { + mGain = over + release_constant * (mGain-over); + } + + // Apply gain reduction to output if reduction exists. + if (mGain > 1.0f) + { + for (channel = 0; channel < inchannels; channel++) + { + if (!((1 << channel) & speakermask)) + { + sample_out[channel] = sample_in[channel]; + } + else + { + sample_out[channel] = gainmakeup_lin * sample_in[channel] / mGain; + } + } + } + else + { + for (channel = 0; channel < inchannels; channel++) + { + if (!((1 << channel) & speakermask)) + { + sample_out[channel] = sample_in[channel]; + } + else + { + sample_out[channel] = gainmakeup_lin * sample_in[channel]; + } + } + } + } + } + else + { + for (sample = 0; sample < length; sample++) + { + unsigned int base = sample * inchannels; + float *sample_in = &(inbuffer[base]); + float *sample_out = &(outbuffer[base]); + float in_max = 0.0f; + float over; + + for (channel = 0; channel < inchannels; channel++) + { + float *m; + + x = sample_in[channel]; + m = &(mMaxChannelIn[channel]); + + in_abs = (x<0.0f) ? -x : x; // ABS input + + *m -= holdconstant; // Leak hold + + if (in_abs > *m) // Hold new max + { + *m = in_abs; + } + + if (*m > in_max) // Update total max + { + in_max = *m; + } + } + + // Note: This uses linear gain curves, not dB + // dB is better but more costly + + // Over = linear ratio of input:threshold + over = in_max/threshold_lin; + + // Apply attack or release curve to over + // to obtain gain reduction + if (over > 1.0f) + { + mGain = over + attack_constant * (mGain-over); + } + else + { + mGain = over + release_constant * (mGain-over); + } + + // Apply gain reduction to output if reduction exists. + if (mGain > 1.0f) + { + for (channel = 0; channel < inchannels; channel++) + { + sample_out[channel] = gainmakeup_lin * sample_in[channel] / mGain; + } + } + else + { + for (channel = 0; channel < inchannels; channel++) + { + sample_out[channel] = gainmakeup_lin * sample_in[channel]; + } + } + } + } + + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCompressor::setParameterInternal(int index, float value) +{ + FMOD_RESULT result; + int outputrate; + + result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mHoldConstant = 10.0f / (float)outputrate; // 10Hz + + switch (index) + { + + case FMOD_DSP_COMPRESSOR_THRESHOLD: + { + mThreshold_dB = value; + mThreshold_lin = FMOD_POW(10.0f, mThreshold_dB/20.0f); + break; + } + case FMOD_DSP_COMPRESSOR_ATTACK: + { + mAttack_ms = value; + mAttack_constant = FMOD_EXP(-1000.0f / (mAttack_ms * outputrate) ); + break; + } + case FMOD_DSP_COMPRESSOR_RELEASE: + { + mRelease_ms = value; + mRelease_constant = FMOD_EXP(-1000.0f / (mRelease_ms * outputrate) ); + break; + } + case FMOD_DSP_COMPRESSOR_GAINMAKEUP: + { + mGainMakeup_dB = value; + mGainMakeup_lin = FMOD_POW(10.0f, mGainMakeup_dB/20.0f); + break; + } + } +/* + if (mResonance >= 1.0f) + { + unsigned int nInd; + float a0, a1, a2, b0, b1, b2; + float fs; // Sampling frequency, cutoff frequency + float k[2]; // overall gain factor + float ktotal; + float *coef; + + k[0] = 1.0f; // Set overall filter gain + k[1] = 1.0f; + ktotal = 1.0f; + coef = mCoefficients + 1; // Skip k, or gain + fs = (float)outputrate; // Sampling frequency (Hz) + + + + // Update overall filter gain in coef array + mCoefficients[0] = ktotal; + } +*/ + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPCompressor::getParameterInternal(int index, float *value, char *valuestr) +{ + + switch (index) + { + case FMOD_DSP_COMPRESSOR_THRESHOLD: + { + *value = mThreshold_dB; + sprintf(valuestr, "%.02f", mThreshold_dB); + break; + } + case FMOD_DSP_COMPRESSOR_ATTACK: + { + *value = mAttack_ms; + sprintf(valuestr, "%.02f", mAttack_ms); + break; + } + case FMOD_DSP_COMPRESSOR_RELEASE: + { + *value = mRelease_ms; + sprintf(valuestr, "%.02f", mRelease_ms); + break; + } + case FMOD_DSP_COMPRESSOR_GAINMAKEUP: + { + *value = mGainMakeup_dB; + sprintf(valuestr, "%.02f", mGainMakeup_dB); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPCompressor::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCompressor::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPCompressor *compressor = (DSPCompressor *)dsp; + + return compressor->createInternal(); +} + +#endif //!PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCompressor::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPCompressor *compressor = (DSPCompressor *)dsp; + + return compressor->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCompressor::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPCompressor *compressor = (DSPCompressor *)dsp; + + return compressor->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCompressor::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPCompressor *compressor = (DSPCompressor *)dsp; + + return compressor->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPCompressor::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPCompressor *compressor = (DSPCompressor *)dsp; + + return compressor->DSPCompressor::getMemoryUsed(tracker); +} +#endif + +#endif //!PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_compressor.h b/src/fmod_dsp_compressor.h new file mode 100755 index 0000000..b64c44e --- /dev/null +++ b/src/fmod_dsp_compressor.h @@ -0,0 +1,55 @@ +#ifndef _FMOD_DSP_COMPRESSOR_H +#define _FMOD_DSP_COMPRESSOR_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_COMPRESSOR + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPCompressor : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + FMOD_RESULT createInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + // Parameters with internal units versions + float mHoldConstant; + float mAttack_ms; + float mAttack_constant; + float mRelease_ms; + float mRelease_constant; + float mThreshold_dB; + float mThreshold_lin; + float mGainMakeup_dB; + float mGainMakeup_lin; + + // Internal state + float mGain; + float mMaxChannelIn[DSP_MAXLEVELS_MAX]; + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK seekCallback(FMOD_DSP_STATE *dsp, unsigned int seeklen); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_connection.cpp b/src/fmod_dsp_connection.cpp new file mode 100755 index 0000000..3dccb20 --- /dev/null +++ b/src/fmod_dsp_connection.cpp @@ -0,0 +1,169 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_dsp_connectioni.h" +#include "fmod_systemi.h" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT DSPConnection::getInput(DSP **input) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->getInput((DSPI **)input); + } +} + + +FMOD_RESULT DSPConnection::getOutput(DSP **output) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->getOutput((DSPI **)output); + } +} + + +FMOD_RESULT DSPConnection::setMix(float volume) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->setMix(volume); + } +} + + +FMOD_RESULT DSPConnection::getMix(float *volume) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->getMix(volume); + } +} + + +FMOD_RESULT DSPConnection::setLevels(FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->setLevels(speaker, levels, numlevels); + } +} + + +FMOD_RESULT DSPConnection::getLevels(FMOD_SPEAKER speaker, float *levels, int numlevels) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->getLevels(speaker, levels, numlevels); + } +} + + +FMOD_RESULT DSPConnection::setUserData(void *_userdata) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->setUserData(_userdata); + } +} + + +FMOD_RESULT DSPConnection::getUserData(void **_userdata) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->getUserData(_userdata); + } +} + + +FMOD_RESULT DSPConnection::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + DSPConnectionI *dspconnectioni; + + result = DSPConnectionI::validate(this, &dspconnectioni); + if (result != FMOD_OK) + { + return result; + } + else + { + return dspconnectioni->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_dsp_connectioni.cpp b/src/fmod_dsp_connectioni.cpp new file mode 100755 index 0000000..7d8cfb6 --- /dev/null +++ b/src/fmod_dsp_connectioni.cpp @@ -0,0 +1,2613 @@ +#include "fmod_settings.h" + +#include "fmod_dspi.h" +#include "fmod_dsp_connectioni.h" +#include "fmod_globals.h" +#include "fmod_localcriticalsection.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +namespace FMOD +{ + +#ifdef FMOD_SUPPORT_SIMD +extern "C" +{ + void FMOD_DSP_Connection_MixMonoToStereo_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float lvolume, float rvolume); + void FMOD_DSP_Connection_MixStereoToStereo_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float lvolume, float rvolume); + void FMOD_DSP_Connection_MixMonoTo5_1_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to1, float *volume2to5); + void FMOD_DSP_Connection_Mix5_1To5_1_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to1, float *volume2to5); + void FMOD_DSP_Connection_MixMonoTo7_1_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to7); + void FMOD_DSP_Connection_Mix7_1To7_1_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to7); + void FMOD_DSP_Connection_MixStereoTo7_1_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume01to31, float *volume4to7, float *volume41to71); +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::validate(DSPConnection *dspconnection, DSPConnectionI **dspconnectioni) +{ + if (!dspconnectioni) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!dspconnection) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *dspconnectioni = (DSPConnectionI *)dspconnection; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::init(DSP_LEVEL_TYPE * &levelmemory, int maxoutputlevels, int maxinputlevels) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + mMaxOutputLevels = maxoutputlevels; + mMaxInputLevels = maxinputlevels; + + if (maxoutputlevels < 2) + { + maxoutputlevels = 2; + } + + if (mMaxInputLevels < mMaxOutputLevels) + { + mMaxInputLevels = mMaxOutputLevels; + } + + #ifdef PLATFORM_PS3_PPU + mMramAddress = (unsigned int)this; + mMramAddressLevels = (unsigned int)levelmemory; + #endif + + int count; + for (count = 0; count < DSP_MAXLEVELS_OUT; count++) + { + if (count < mMaxOutputLevels) + { + mLevel[count] = levelmemory; + levelmemory += mMaxInputLevels; + mLevelCurrent[count] = levelmemory; + levelmemory += mMaxInputLevels; + mLevelDelta[count] = levelmemory; + levelmemory += mMaxInputLevels; + } + else + { + mLevel[count] = 0; + mLevelCurrent[count] = 0; + mLevelDelta[count] = 0; + } + } + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getInput(DSPI **input) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + if (!input) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mInputUnit) + { + *input = 0; + return FMOD_ERR_NOTREADY; + } + + *input = mInputUnit; + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getOutput(DSPI **output) +{ + if (!output) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputUnit) + { + *output = 0; + return FMOD_ERR_NOTREADY; + } + + *output = mOutputUnit; + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_SOFTWARE + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::reset() +{ + int count, count2; + + mVolume = 1.0f; + mUserData = 0; + mRampCount = 0; + + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + mLevel [count][count2] = 0; + mLevelCurrent[count][count2] = 0; + mLevelDelta [count][count2] = 0; + } + } + + mSetLevelsUsed = false; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::setUnity() +{ + int count, count2; + + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + if (count == count2) + { + mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(1.0f); + mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f); + } + else + { + mLevel[count][count2] = 0; + mLevelCurrent[count][count2] = 0; + } + } + } + mVolume = 1.0f; + mRampCount = 0; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::mix(float * FMOD_RESTRICT outbuffer, float * FMOD_RESTRICT inbuffer, int outchannels, int inchannels, unsigned int length) +{ + unsigned int count; + float l00, l10; + float l01, l11; +#if (DSP_MAXLEVELS_OUT > 2) + float l20, l30, l40, l50; + float l21, l31, l41, l51; + float l22, l33, l44, l55; +#endif +#if (DSP_MAXLEVELS_OUT > 6) + float l60, l70; + float l61, l71; + float l66, l77; +#endif + bool supportssimd; + float levelcurrent[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN] = { 0 }; + + if (!inbuffer) + { + return FMOD_OK; + } + + supportssimd = FMOD_OS_SupportsSIMD(); + + if (mRampCount) + { + unsigned int len = mRampCount; + + if (len > length) + { + len = length; + } + + mixAndRamp(outbuffer, inbuffer, outchannels, inchannels, len); + + length -= len; + outbuffer += (len * outchannels); + inbuffer += (len * inchannels); + } + + if (!length) + { + return FMOD_OK; + } + + if (FMOD_FABS(mVolume) < DSP_LEVEL_SMALLVAL) + { + return FMOD_OK; + } + + for (count = 0; count < (unsigned int)mMaxOutputLevels; count++) + { + int count2; + + if (mLevelCurrent[count]) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + levelcurrent[count][count2] = DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][count2]); + } + } + } + + /* + Because mLevelCurrent could be misaligned this could hopefully put the values into registers. (confirmed on ps2). + */ + + l00 = levelcurrent[0][0]; + l10 = levelcurrent[1][0]; + +#if (DSP_MAXLEVELS_OUT > 2) + l20 = levelcurrent[2][0]; + l30 = levelcurrent[3][0]; + l40 = levelcurrent[4][0]; + l50 = levelcurrent[5][0]; +#endif +#if (DSP_MAXLEVELS_OUT > 6) + l60 = levelcurrent[6][0]; + l70 = levelcurrent[7][0]; +#endif + + l01 = levelcurrent[0][1]; + l11 = levelcurrent[1][1]; + +#if (DSP_MAXLEVELS_OUT > 2) + l21 = levelcurrent[2][1]; + l31 = levelcurrent[3][1]; + l41 = levelcurrent[4][1]; + l51 = levelcurrent[5][1]; + + l22 = levelcurrent[2][2]; + l33 = levelcurrent[3][3]; + l44 = levelcurrent[4][4]; + l55 = levelcurrent[5][5]; +#endif +#if (DSP_MAXLEVELS_OUT > 6) + l61 = levelcurrent[6][1]; + l71 = levelcurrent[7][1]; + + l66 = levelcurrent[6][6]; + l77 = levelcurrent[7][7]; +#endif + + /* + The most common configuration is mono or stereo coming in, and stereo going out, so we special case these for speed. + Other combinations go through generic for loop code. + */ + if (0) + { + } +#if !defined(PLATFORM_PS3_SPU) + else if (outchannels == 2 && (inchannels == 1 || inchannels == 2)) + { + if (inchannels == 1) + { + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + /* + First 16byte align destination for simd. + */ + while ((((unsigned int)outbuffer & 0xF) || ((unsigned int)inbuffer & 0xF)) && length) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + inbuffer ++; + outbuffer += 2; + length --; + } + FMOD_DSP_Connection_MixMonoToStereo_SIMD(inbuffer, outbuffer, length, l00, l10); + } + else + #endif + { + unsigned int len; + + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[2] += (inbuffer[1] * l00); + outbuffer[3] += (inbuffer[1] * l10); + outbuffer[4] += (inbuffer[2] * l00); + outbuffer[5] += (inbuffer[2] * l10); + outbuffer[6] += (inbuffer[3] * l00); + outbuffer[7] += (inbuffer[3] * l10); + inbuffer += 4; + outbuffer += 8; + len --; + } + len = length & 3; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + inbuffer ++; + outbuffer += 2; + len --; + } + } + } + else if (inchannels == 2) + { + unsigned int len; + + if (FMOD_FABS(l01) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l10) < DSP_LEVEL_SMALLVAL) /* Pan matrix looks like [l 0] which is the normal way. */ + { /* [0 r] */ + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + /* + First 16byte align destination for simd. + */ + while ((unsigned int)outbuffer & 0xF && length) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); + inbuffer += 2; + outbuffer += 2; + length --; + } + FMOD_DSP_Connection_MixStereoToStereo_SIMD(inbuffer, outbuffer, length, l00, l11); + } + else + #endif + { + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); + + outbuffer[2] += (inbuffer[2] * l00); + outbuffer[3] += (inbuffer[3] * l11); + + outbuffer[4] += (inbuffer[4] * l00); + outbuffer[5] += (inbuffer[5] * l11); + + outbuffer[6] += (inbuffer[6] * l00); + outbuffer[7] += (inbuffer[7] * l11); + inbuffer += 8; + outbuffer += 8; + len --; + } + + len = length & 3; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); + inbuffer += 2; + outbuffer += 2; + len --; + } + } + } + else /* Pan matrix looks like [l ?] must have used setSpeakerMix. */ + { /* [? r] */ + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[0] += (inbuffer[1] * l01); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[1] += (inbuffer[1] * l11); + + outbuffer[2] += (inbuffer[2] * l00); + outbuffer[2] += (inbuffer[3] * l01); + outbuffer[3] += (inbuffer[2] * l10); + outbuffer[3] += (inbuffer[3] * l11); + + outbuffer[4] += (inbuffer[4] * l00); + outbuffer[4] += (inbuffer[5] * l01); + outbuffer[5] += (inbuffer[4] * l10); + outbuffer[5] += (inbuffer[5] * l11); + + outbuffer[6] += (inbuffer[6] * l00); + outbuffer[6] += (inbuffer[7] * l01); + outbuffer[7] += (inbuffer[6] * l10); + outbuffer[7] += (inbuffer[7] * l11); + inbuffer += 8; + outbuffer += 8; + len --; + } + + len = length & 3; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[0] += (inbuffer[1] * l01); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[1] += (inbuffer[1] * l11); + inbuffer += 2; + outbuffer += 2; + len --; + } + + } + } + } +#if (DSP_MAXLEVELS_OUT > 2) && !defined(PLATFORM_PS3_PPU) + else if (outchannels == 6 && (inchannels == 1 || inchannels == 2 || inchannels == 6))//(inchannels == 6 && checkUnity(outchannels, inchannels) == FMOD_OK))) + { + if (inchannels == 1) + { + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + float volume0to3[4] = { l00, l10, l20, l30 }; + float volume4to1[4] = { l40, l50, l00, l10 }; + float volume2to5[4] = { l20, l30, l40, l50 }; + + /* + First 16byte align destination for simd. + */ + while ((unsigned int)outbuffer & 0xF && length) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[5] += (inbuffer[0] * l50); + + inbuffer ++; + outbuffer +=6; + length --; + } + + FMOD_DSP_Connection_MixMonoTo5_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to1, volume2to5); + } + else + #endif + { + unsigned int len; + + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[3] += (inbuffer[0] * l30); + + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[6] += (inbuffer[1] * l00); + outbuffer[7] += (inbuffer[1] * l10); + + outbuffer[8] += (inbuffer[1] * l20); + outbuffer[9] += (inbuffer[1] * l30); + outbuffer[10] += (inbuffer[1] * l40); + outbuffer[11] += (inbuffer[1] * l50); + + outbuffer[12] += (inbuffer[2] * l00); + outbuffer[13] += (inbuffer[2] * l10); + outbuffer[14] += (inbuffer[2] * l20); + outbuffer[15] += (inbuffer[2] * l30); + + outbuffer[16] += (inbuffer[2] * l40); + outbuffer[17] += (inbuffer[2] * l50); + outbuffer[18] += (inbuffer[3] * l00); + outbuffer[19] += (inbuffer[3] * l10); + + outbuffer[20] += (inbuffer[3] * l20); + outbuffer[21] += (inbuffer[3] * l30); + outbuffer[22] += (inbuffer[3] * l40); + outbuffer[23] += (inbuffer[3] * l50); + + outbuffer += 24; + inbuffer += 4; + + len--; + } + + len = length & 3; + if (len) + { + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[5] += (inbuffer[0] * l50); + + inbuffer ++; + outbuffer +=6; + len --; + } + } + } + } + else if (inchannels == 2) + { + unsigned int len; + + /* Pan matrix looks like [l 0] which is the normal way. */ + /* [0 r] */ + /* [0 0] */ + /* [0 0] */ + /* [0 0] */ + /* [0 0] */ + if (FMOD_FABS(l01) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l10) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l20) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l21) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l30) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l31) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l40) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l41) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l50) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l51) < DSP_LEVEL_SMALLVAL) + { + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); +// outbuffer[2 += 0; +// outbuffer[3] += 0; +// outbuffer[4] += 0; +// outbuffer[5] += 0; + + outbuffer[6] += (inbuffer[2] * l00); + outbuffer[7] += (inbuffer[3] * l11); +// outbuffer[8] += 0; +// outbuffer[9] += 0; +// outbuffer[10] += 0; +// outbuffer[11] += 0; + + outbuffer[12] += (inbuffer[4] * l00); + outbuffer[13] += (inbuffer[5] * l11); +// outbuffer[14] += 0; +// outbuffer[15] += 0; +// outbuffer[16] += 0; +// outbuffer[17] += 0; + + outbuffer[18] += (inbuffer[6] * l00); + outbuffer[19] += (inbuffer[7] * l11); +// outbuffer[20] += 0; +// outbuffer[21] += 0; +// outbuffer[22] += 0; +// outbuffer[23] += 0; + + outbuffer += 24; + inbuffer += 8; + len--; + } + + len = length & 3; + if (len) + { + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); + //outbuffer[2] += 0; + //outbuffer[3] += 0; + //outbuffer[4] += 0; + //outbuffer[5] += 0; + + inbuffer += 2; + outbuffer += 6; + len --; + } + } + } + else /* Pan matrix looks like [l ?] must have used setSpeakerMix. */ + { /* [? r] */ + len = length >> 1; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[0] += (inbuffer[1] * l01); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[1] += (inbuffer[1] * l11); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[2] += (inbuffer[1] * l21); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[3] += (inbuffer[1] * l31); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[4] += (inbuffer[1] * l41); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[5] += (inbuffer[1] * l51); + + outbuffer[6] += (inbuffer[2] * l00); + outbuffer[6] += (inbuffer[3] * l01); + outbuffer[7] += (inbuffer[2] * l10); + outbuffer[7] += (inbuffer[3] * l11); + outbuffer[8] += (inbuffer[2] * l20); + outbuffer[8] += (inbuffer[3] * l21); + outbuffer[9] += (inbuffer[2] * l30); + outbuffer[9] += (inbuffer[3] * l31); + outbuffer[10] += (inbuffer[2] * l40); + outbuffer[10] += (inbuffer[3] * l41); + outbuffer[11] += (inbuffer[2] * l50); + outbuffer[11] += (inbuffer[3] * l51); + + inbuffer += 4; + outbuffer += 12; + len --; + } + + len = length & 1; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[0] += (inbuffer[1] * l01); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[1] += (inbuffer[1] * l11); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[2] += (inbuffer[1] * l21); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[3] += (inbuffer[1] * l31); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[4] += (inbuffer[1] * l41); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[5] += (inbuffer[1] * l51); + + inbuffer += 2; + outbuffer += 6; + len --; + } + } + } + else if (inchannels == 6) + { + FMOD_RESULT unity = checkUnity(outchannels, inchannels); + FMOD_RESULT mono = FMOD_ERR_TOOMANYCHANNELS; + + if (unity != FMOD_OK) + { + mono = checkMono(outchannels, inchannels); + } + + if (unity == FMOD_OK || mono == FMOD_OK) + { + float l[6]; + + if (unity == FMOD_OK) + { + l[0] = l00; + l[1] = l11; + l[2] = l22; + l[3] = l33; + l[4] = l44; + l[5] = l55; + } + else + { + l[0] = l00; + l[1] = l10; + l[2] = l20; + l[3] = l30; + l[4] = l40; + l[5] = l50; + } + + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + float volume0to3[4] = { l[0], l[1], l[2], l[3] }; + float volume4to1[4] = { l[4], l[5], l[0], l[1] }; + float volume2to5[4] = { l[2], l[3], l[4], l[5] }; + + /* + First 16byte align destination for simd. + */ + while ((unsigned int)outbuffer & 0xF && length) + { + outbuffer[0] += (inbuffer[0] * l[0]); + outbuffer[1] += (inbuffer[1] * l[1]); + outbuffer[2] += (inbuffer[2] * l[2]); + outbuffer[3] += (inbuffer[3] * l[3]); + outbuffer[4] += (inbuffer[4] * l[4]); + outbuffer[5] += (inbuffer[5] * l[5]); + + inbuffer +=6; + outbuffer +=6; + length --; + } + + FMOD_DSP_Connection_Mix5_1To5_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to1, volume2to5); + } + else + #endif + { + unsigned int len; + + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l[0]); + outbuffer[1] += (inbuffer[1] * l[1]); + outbuffer[2] += (inbuffer[2] * l[2]); + outbuffer[3] += (inbuffer[3] * l[3]); + outbuffer[4] += (inbuffer[4] * l[4]); + outbuffer[5] += (inbuffer[5] * l[5]); + outbuffer[6] += (inbuffer[6] * l[0]); + outbuffer[7] += (inbuffer[7] * l[1]); + outbuffer[8] += (inbuffer[8] * l[2]); + outbuffer[9] += (inbuffer[9] * l[3]); + outbuffer[10] += (inbuffer[10] * l[4]); + outbuffer[11] += (inbuffer[11] * l[5]); + + outbuffer[12] += (inbuffer[12] * l[0]); + outbuffer[13] += (inbuffer[13] * l[1]); + outbuffer[14] += (inbuffer[14] * l[2]); + outbuffer[15] += (inbuffer[15] * l[3]); + outbuffer[16] += (inbuffer[16] * l[4]); + outbuffer[17] += (inbuffer[17] * l[5]); + outbuffer[18] += (inbuffer[18] * l[0]); + outbuffer[19] += (inbuffer[19] * l[1]); + outbuffer[20] += (inbuffer[20] * l[2]); + outbuffer[21] += (inbuffer[21] * l[3]); + outbuffer[22] += (inbuffer[22] * l[4]); + outbuffer[23] += (inbuffer[23] * l[5]); + + outbuffer += 24; + inbuffer += 24; + len--; + } + + len = length & 3; + if (len) + { + while (len) + { + outbuffer[0] += (inbuffer[0] * l[0]); + outbuffer[1] += (inbuffer[1] * l[1]); + outbuffer[2] += (inbuffer[2] * l[2]); + outbuffer[3] += (inbuffer[3] * l[3]); + outbuffer[4] += (inbuffer[4] * l[4]); + outbuffer[5] += (inbuffer[5] * l[5]); + + inbuffer +=6; + outbuffer +=6; + len --; + } + } + } + } + else + { + #if 0 + for (count = 0; count < length; count++) + { + int count2; + + for (count2 = 0; count2 < outchannels; count2++) + { + float srcval = 0; + float destval = *outbuffer; + int count3; + + for (count3 = 0; count3 < inchannels; count3++) + { + srcval += (inbuffer[count3] * levelcurrent[count2][count3]); + } + + destval += srcval; + *outbuffer++ = destval; + } + + inbuffer += inchannels; + } + #else + for (count = 0; count < length; count++) + { + float srcval; + + srcval = (inbuffer[0] * levelcurrent[0][0]); + srcval += (inbuffer[1] * levelcurrent[0][1]); + srcval += (inbuffer[2] * levelcurrent[0][2]); + srcval += (inbuffer[3] * levelcurrent[0][3]); + srcval += (inbuffer[4] * levelcurrent[0][4]); + srcval += (inbuffer[5] * levelcurrent[0][5]); + outbuffer[0] += srcval; + + srcval = (inbuffer[0] * levelcurrent[1][0]); + srcval += (inbuffer[1] * levelcurrent[1][1]); + srcval += (inbuffer[2] * levelcurrent[1][2]); + srcval += (inbuffer[3] * levelcurrent[1][3]); + srcval += (inbuffer[4] * levelcurrent[1][4]); + srcval += (inbuffer[5] * levelcurrent[1][5]); + outbuffer[1] += srcval; + + srcval = (inbuffer[0] * levelcurrent[2][0]); + srcval += (inbuffer[1] * levelcurrent[2][1]); + srcval += (inbuffer[2] * levelcurrent[2][2]); + srcval += (inbuffer[3] * levelcurrent[2][3]); + srcval += (inbuffer[4] * levelcurrent[2][4]); + srcval += (inbuffer[5] * levelcurrent[2][5]); + outbuffer[2] += srcval; + + srcval = (inbuffer[0] * levelcurrent[3][0]); + srcval += (inbuffer[1] * levelcurrent[3][1]); + srcval += (inbuffer[2] * levelcurrent[3][2]); + srcval += (inbuffer[3] * levelcurrent[3][3]); + srcval += (inbuffer[4] * levelcurrent[3][4]); + srcval += (inbuffer[5] * levelcurrent[3][5]); + outbuffer[3] += srcval; + + srcval = (inbuffer[0] * levelcurrent[4][0]); + srcval += (inbuffer[1] * levelcurrent[4][1]); + srcval += (inbuffer[2] * levelcurrent[4][2]); + srcval += (inbuffer[3] * levelcurrent[4][3]); + srcval += (inbuffer[4] * levelcurrent[4][4]); + srcval += (inbuffer[5] * levelcurrent[4][5]); + outbuffer[4] += srcval; + + srcval = (inbuffer[0] * levelcurrent[5][0]); + srcval += (inbuffer[1] * levelcurrent[5][1]); + srcval += (inbuffer[2] * levelcurrent[5][2]); + srcval += (inbuffer[3] * levelcurrent[5][3]); + srcval += (inbuffer[4] * levelcurrent[5][4]); + srcval += (inbuffer[5] * levelcurrent[5][5]); + outbuffer[5] += srcval; + + outbuffer += 6; + inbuffer += 6; + } + #endif + } + } + } +#endif +#endif /* #if (DSP_MAXLEVELS_OUT != 8) */ + +#if (DSP_MAXLEVELS_OUT > 6) && !defined(PLATFORM_PS3_PPU) + else if (outchannels == 8 && (inchannels == 1 || inchannels == 2 || inchannels == 8)) // && checkUnity(outchannels, inchannels) == FMOD_OK))) + { + if (inchannels == 1) + { + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + float volume0to3[4] = { l00, l10, l20, l30 }; + float volume4to7[4] = { l40, l50, l60, l70 }; + + FMOD_DSP_Connection_MixMonoTo7_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to7); + } + else + #endif + { + unsigned int len; + + len = length >> 1; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[6] += (inbuffer[0] * l60); + outbuffer[7] += (inbuffer[0] * l70); + + outbuffer[8] += (inbuffer[1] * l00); + outbuffer[9] += (inbuffer[1] * l10); + outbuffer[10] += (inbuffer[1] * l20); + outbuffer[11] += (inbuffer[1] * l30); + outbuffer[12] += (inbuffer[1] * l40); + outbuffer[13] += (inbuffer[1] * l50); + outbuffer[14] += (inbuffer[1] * l60); + outbuffer[15] += (inbuffer[1] * l70); + + outbuffer += 16; + inbuffer += 2; + + len--; + } + + len = length & 1; + if (len) + { + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[6] += (inbuffer[0] * l60); + outbuffer[7] += (inbuffer[0] * l70); + + inbuffer ++; + outbuffer +=8; + len --; + } + } + } + } + else if (inchannels == 2) + { + unsigned int len; + + /* Pan matrix looks like [l 0] which is the normal way. */ + /* [0 r] */ + /* [0 0] */ + /* [0 0] */ + /* [0 0] */ + /* [0 0] */ + if (FMOD_FABS(l01) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l10) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l20) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l21) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l30) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l31) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l40) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l41) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l50) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l51) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l60) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l61) < DSP_LEVEL_SMALLVAL && + FMOD_FABS(l70) < DSP_LEVEL_SMALLVAL && FMOD_FABS(l71) < DSP_LEVEL_SMALLVAL) + { + len = length >> 2; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); +// outbuffer[2 += 0; +// outbuffer[3] += 0; +// outbuffer[4] += 0; +// outbuffer[5] += 0; +// outbuffer[6] += 0; +// outbuffer[7] += 0; + + outbuffer[8] += (inbuffer[2] * l00); + outbuffer[9] += (inbuffer[3] * l11); +// outbuffer[10] += 0; +// outbuffer[11] += 0; +// outbuffer[12] += 0; +// outbuffer[13] += 0; +// outbuffer[14] += 0; +// outbuffer[15] += 0; + + outbuffer[16] += (inbuffer[4] * l00); + outbuffer[17] += (inbuffer[5] * l11); +// outbuffer[18] += 0; +// outbuffer[19] += 0; +// outbuffer[20] += 0; +// outbuffer[21] += 0; +// outbuffer[22] += 0; +// outbuffer[23] += 0; + + outbuffer[24] += (inbuffer[6] * l00); + outbuffer[25] += (inbuffer[7] * l11); +// outbuffer[26] += 0; +// outbuffer[27] += 0; +// outbuffer[28] += 0; +// outbuffer[29] += 0; +// outbuffer[30] += 0; +// outbuffer[31] += 0; + + outbuffer += 32; + inbuffer += 8; + len--; + } + + len = length & 3; + if (len) + { + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[1] += (inbuffer[1] * l11); + //outbuffer[2] += 0; + //outbuffer[3] += 0; + //outbuffer[4] += 0; + //outbuffer[5] += 0; + //outbuffer[6] += 0; + //outbuffer[7] += 0; + + inbuffer += 2; + outbuffer += 8; + len --; + } + } + } + else /* Pan matrix looks like [l ?] must have used setSpeakerMix. */ + { /* [? r] */ +#ifdef PLATFORM_XENON + float volume0to3[4] = { l00, l10, l20, l30 }; + float volume01to31[4] = { l01, l11, l21, l31 }; + float volume4to7[4] = { l40, l50, l60, l70 }; + float volume41to71[4] = { l41, l51, l61, l71 }; + + FMOD_DSP_Connection_MixStereoTo7_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume01to31, volume4to7, volume41to71); +#else + len = length >> 1; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[0] += (inbuffer[1] * l01); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[1] += (inbuffer[1] * l11); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[2] += (inbuffer[1] * l21); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[3] += (inbuffer[1] * l31); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[4] += (inbuffer[1] * l41); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[5] += (inbuffer[1] * l51); + outbuffer[6] += (inbuffer[0] * l60); + outbuffer[6] += (inbuffer[1] * l61); + outbuffer[7] += (inbuffer[0] * l70); + outbuffer[7] += (inbuffer[1] * l71); + + outbuffer[8] += (inbuffer[2] * l00); + outbuffer[8] += (inbuffer[3] * l01); + outbuffer[9] += (inbuffer[2] * l10); + outbuffer[9] += (inbuffer[3] * l11); + outbuffer[10] += (inbuffer[2] * l20); + outbuffer[10] += (inbuffer[3] * l21); + outbuffer[11] += (inbuffer[2] * l30); + outbuffer[11] += (inbuffer[3] * l31); + outbuffer[12] += (inbuffer[2] * l40); + outbuffer[12] += (inbuffer[3] * l41); + outbuffer[13] += (inbuffer[2] * l50); + outbuffer[13] += (inbuffer[3] * l51); + outbuffer[14] += (inbuffer[2] * l60); + outbuffer[14] += (inbuffer[3] * l61); + outbuffer[15] += (inbuffer[2] * l70); + outbuffer[15] += (inbuffer[3] * l71); + + inbuffer += 4; + outbuffer += 16; + len --; + } +#endif + + len = length & 1; + while (len) + { + outbuffer[0] += (inbuffer[0] * l00); + outbuffer[0] += (inbuffer[1] * l01); + outbuffer[1] += (inbuffer[0] * l10); + outbuffer[1] += (inbuffer[1] * l11); + outbuffer[2] += (inbuffer[0] * l20); + outbuffer[2] += (inbuffer[1] * l21); + outbuffer[3] += (inbuffer[0] * l30); + outbuffer[3] += (inbuffer[1] * l31); + outbuffer[4] += (inbuffer[0] * l40); + outbuffer[4] += (inbuffer[1] * l41); + outbuffer[5] += (inbuffer[0] * l50); + outbuffer[5] += (inbuffer[1] * l51); + outbuffer[6] += (inbuffer[0] * l60); + outbuffer[6] += (inbuffer[1] * l61); + outbuffer[7] += (inbuffer[0] * l70); + outbuffer[7] += (inbuffer[1] * l71); + + inbuffer += 2; + outbuffer += 8; + len --; + } + } + } + else if (inchannels == 8) + { +#if 0 + if (mUnity) + { + mUnity = mUnity; + } + else +#endif + FMOD_RESULT unity = checkUnity(outchannels, inchannels); + FMOD_RESULT mono = FMOD_ERR_TOOMANYCHANNELS; + + if (unity != FMOD_OK) + { + mono = checkMono(outchannels, inchannels); + } + + if (unity == FMOD_OK || mono == FMOD_OK) + { + float l[8]; + + if (unity == FMOD_OK) + { + l[0] = l00; + l[1] = l11; + l[2] = l22; + l[3] = l33; + l[4] = l44; + l[5] = l55; + l[6] = l66; + l[7] = l77; + } + else + { + l[0] = l00; + l[1] = l10; + l[2] = l20; + l[3] = l30; + l[4] = l40; + l[5] = l50; + l[6] = l60; + l[7] = l70; + } + + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + float volume0to3[4] = { l[0], l[1], l[2], l[3] }; + float volume4to7[4] = { l[4], l[5], l[6], l[7] }; + + FMOD_DSP_Connection_Mix7_1To7_1_SIMD(inbuffer, outbuffer, length, volume0to3, volume4to7); + } + else + #endif + { + unsigned int len; + + len = length >> 1; + while (len) + { + outbuffer[0] += (inbuffer[0] * l[0]); + outbuffer[1] += (inbuffer[1] * l[1]); + outbuffer[2] += (inbuffer[2] * l[2]); + outbuffer[3] += (inbuffer[3] * l[3]); + outbuffer[4] += (inbuffer[4] * l[4]); + outbuffer[5] += (inbuffer[5] * l[5]); + outbuffer[6] += (inbuffer[6] * l[6]); + outbuffer[7] += (inbuffer[7] * l[7]); + + outbuffer[8] += (inbuffer[8] * l[0]); + outbuffer[9] += (inbuffer[9] * l[1]); + outbuffer[10] += (inbuffer[10] * l[2]); + outbuffer[11] += (inbuffer[11] * l[3]); + outbuffer[12] += (inbuffer[12] * l[4]); + outbuffer[13] += (inbuffer[13] * l[5]); + outbuffer[14] += (inbuffer[14] * l[6]); + outbuffer[15] += (inbuffer[15] * l[7]); + + outbuffer += 16; + inbuffer += 16; + len--; + } + + len = length & 1; + if (len) + { + while (len) + { + outbuffer[0] += (inbuffer[0] * l[0]); + outbuffer[1] += (inbuffer[1] * l[1]); + outbuffer[2] += (inbuffer[2] * l[2]); + outbuffer[3] += (inbuffer[3] * l[3]); + outbuffer[4] += (inbuffer[4] * l[4]); + outbuffer[5] += (inbuffer[5] * l[5]); + outbuffer[6] += (inbuffer[6] * l[6]); + outbuffer[7] += (inbuffer[7] * l[7]); + + inbuffer +=8; + outbuffer +=8; + len --; + } + } + } + } + else + { + for (count = 0; count < length; count++) + { + int count2; + + for (count2 = 0; count2 < outchannels; count2++) + { + float srcval = 0; + float destval = *outbuffer; + int count3; + + for (count3 = 0; count3 < inchannels; count3++) + { + srcval += (inbuffer[count3] * levelcurrent[count2][count3]); + } + + destval += srcval; + *outbuffer++ = destval; + } + inbuffer += inchannels; + } + } + } + } +#endif + else + { + for (count = 0; count < length; count++) + { + int count2; + + for (count2 = 0; count2 < outchannels; count2++) + { + float srcval = 0; + float destval = *outbuffer; + int count3; + + for (count3 = 0; count3 < inchannels; count3++) + { + srcval += (inbuffer[count3] * levelcurrent[count2][count3]); + } + + destval += srcval; + *outbuffer++ = destval; + } + inbuffer += inchannels; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::mixAndRamp(float * FMOD_RESTRICT outbuffer, float * FMOD_RESTRICT inbuffer, int outchannels, int inchannels, unsigned int length) +{ + unsigned int count; + #if (DSP_LEVEL_PRECISION == 16) + float levelcurrent[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN]; + float leveldelta[DSP_MAXLEVELS_OUT][DSP_MAXLEVELS_IN]; + + for (count = 0; count < (unsigned int)outchannels; count++) + { + int count2; + + for (count2 = 0; count2 < inchannels; count2++) + { + levelcurrent[count][count2] = DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][count2]); + leveldelta[count][count2] = DSP_LEVEL_DECOMPRESS(mLevelDelta[count][count2]); + } + } + #endif + + for (count = 0; count < length; count++) + { + int count2; + + for (count2 = 0; count2 < outchannels; count2++) + { + float srcval = 0; + float destval = *outbuffer; + int count3; + + for (count3 = 0; count3 < inchannels; count3++) + { + #if (DSP_LEVEL_PRECISION == 32) + srcval += (inbuffer[count3] * mLevelCurrent[count2][count3]); + mLevelCurrent[count2][count3] += mLevelDelta[count2][count3]; + #else + srcval += (inbuffer[count3] * levelcurrent[count2][count3]); + levelcurrent[count2][count3] += leveldelta[count2][count3]; + #endif + } + + destval += srcval; + *outbuffer++ = destval; + } + + inbuffer += inchannels; + } + + #if (DSP_LEVEL_PRECISION == 16) + for (count = 0; count < (unsigned int)outchannels; count++) + { + int count2; + + for (count2 = 0; count2 < inchannels; count2++) + { + mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(levelcurrent[count][count2]); + mLevelDelta[count][count2] = DSP_LEVEL_COMPRESS(leveldelta[count][count2]); + } + } + #endif + + mRampCount -= length; + + /* + Snap the current to the mlevel + */ + if (!mRampCount) + { + int count, count2; + + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(DSP_LEVEL_DECOMPRESS(mLevel[count][count2]) * mVolume); + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::rampTo() +{ + int count; + float oorampcount, delta = 0; + + oorampcount = 1.0f / DSP_RAMPCOUNT; + + if (mMaxInputLevels == 6) + { + for (count = 0; count < mMaxOutputLevels; count++) + { + float value; + + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][0]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][0])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][0] = DSP_LEVEL_COMPRESS(value); + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][1]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][1])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][1] = DSP_LEVEL_COMPRESS(value); + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][2]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][2])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][2] = DSP_LEVEL_COMPRESS(value); + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][3]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][3])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][3] = DSP_LEVEL_COMPRESS(value); + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][4]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][4])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][4] = DSP_LEVEL_COMPRESS(value); + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][5]) * mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][5])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][5] = DSP_LEVEL_COMPRESS(value); + } + } + else + { + int count2; + + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + float value; + + value = ((DSP_LEVEL_DECOMPRESS(mLevel[count][count2]) *mVolume) - DSP_LEVEL_DECOMPRESS(mLevelCurrent[count][count2])) * oorampcount; + delta += FMOD_FABS(value); + mLevelDelta[count][count2] = DSP_LEVEL_COMPRESS(value); + } + } + } + + if (DSP_LEVEL_COMPRESS(delta) >= 0.000001f) + { + mRampCount = DSP_RAMPCOUNT; /* Only trigger the ramp if anything has changed. */ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::checkUnity(int outchannels, int inchannels) +{ + int count, count2; + + if (inchannels != outchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mRampCount != 0) + { + return FMOD_ERR_PAN; + } + + if (mVolume != 1.0f) + { + return FMOD_ERR_PAN; + } + + for (count = 0; count < outchannels; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if (count == count2 && DSP_LEVEL_DECOMPRESS(mLevel[count][count2]) != 1.0f) + { + return FMOD_ERR_PAN; + } + else if (count != count2 && mLevel[count][count2] != 0) + { + return FMOD_ERR_PAN; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::checkMono(int outchannels, int inchannels) +{ + int count, count2; + + if (inchannels == 1) + { + return FMOD_OK; + } + + for (count = 0; count < outchannels; count++) + { + for (count2 = 1; count2 < inchannels; count2++) + { + if (mLevel[count][count2] != 0) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::setPan(float pan, int outchannels, int inchannels, FMOD_SPEAKERMODE speakermode) +{ + int count, count2; + +#ifndef PLATFORM_PS3_SPU + if (!mInputUnit->mSystem) + { + return FMOD_ERR_INTERNAL; + } +#endif + + /* + First clear out all levels to 0. + */ + for (count = 0; count < outchannels; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + mLevel[count][count2] = 0; + } + } + + switch (speakermode) + { +#ifdef FMOD_SUPPORT_SPEAKERMODE_RAW + case FMOD_SPEAKERMODE_RAW: + { + /* + Map 1 input channel to each output channel sequentially. For example. + + 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 + 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 + 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 + 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 + 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 + */ + for (count = 0; count < outchannels; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if (count == count2) + { + mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f); + } + } + } + break; + } +#endif +#ifdef FMOD_SUPPORT_SPEAKERMODE_MONO + case FMOD_SPEAKERMODE_MONO: + { + for (count = 0; count < inchannels; count++) + { + mLevel[0][count] = DSP_LEVEL_COMPRESS(1.0f); + } + break; + } +#endif +#ifdef FMOD_SUPPORT_SPEAKERMODE_STEREO + case FMOD_SPEAKERMODE_STEREO: + case FMOD_SPEAKERMODE_STEREO_LINEAR: + { + float l,r; + + pan = (pan + 1.0f) / 2.0f; + + if (inchannels == 1) + { + l = 1.0f - pan; + r = pan; + + if (speakermode == FMOD_SPEAKERMODE_STEREO) + { + l = FMOD_SQRT(l); + r = FMOD_SQRT(r); + } + + mLevel[0][0] = DSP_LEVEL_COMPRESS(l); + mLevel[1][0] = DSP_LEVEL_COMPRESS(r); + } + else if (inchannels == 2 || inchannels == 4 || inchannels == 6 || inchannels == 8) + { + /* + Stereo source panning is 0 = 1.0:0.0. 0.5 = 1.0:1.0. 1.0 = 0:1.0. + */ + if (pan <= 0.5f) + { + l = 1.0f; + r = pan * 2.0f; + } + else + { + l = (1.0f - pan) * 2.0f; + r = 1.0f; + } + + if (inchannels == 2) + { + mLevel[0][0] = DSP_LEVEL_COMPRESS(l); + mLevel[1][1] = DSP_LEVEL_COMPRESS(r); + } + else if (inchannels == 4) + { + /* + Prologic downmix. Assuming LCRS + */ + mLevel[0][0] = DSP_LEVEL_COMPRESS( 1.0f * l); + mLevel[0][1] = DSP_LEVEL_COMPRESS( 0.0f * l); + mLevel[0][2] = DSP_LEVEL_COMPRESS( 0.707f * l); + mLevel[0][3] = DSP_LEVEL_COMPRESS( -0.707f * l); /* Discard LFE */ + + mLevel[1][0] = DSP_LEVEL_COMPRESS( 0.0f * r); + mLevel[1][1] = DSP_LEVEL_COMPRESS( 1.0f * r); + mLevel[1][2] = DSP_LEVEL_COMPRESS( 0.707f * r); + mLevel[1][3] = DSP_LEVEL_COMPRESS( -0.707f * r); /* Discard LFE */ + } + else if (inchannels == 6) + { + /* + Prologic 2 downmix. + */ + mLevel[0][0] = DSP_LEVEL_COMPRESS( 1.0f * l); + mLevel[0][1] = DSP_LEVEL_COMPRESS( 0.0f * l); + mLevel[0][2] = DSP_LEVEL_COMPRESS( 0.707f * l); + mLevel[0][3] = DSP_LEVEL_COMPRESS( 0.0f * l); /* Discard LFE */ + mLevel[0][4] = DSP_LEVEL_COMPRESS(-0.872f * l); + mLevel[0][5] = DSP_LEVEL_COMPRESS(-0.49f * l); + + mLevel[1][0] = DSP_LEVEL_COMPRESS( 0.0f * r); + mLevel[1][1] = DSP_LEVEL_COMPRESS( 1.0f * r); + mLevel[1][2] = DSP_LEVEL_COMPRESS( 0.707f * r); + mLevel[1][3] = DSP_LEVEL_COMPRESS( 0.0f * r); /* Discard LFE */ + mLevel[1][4] = DSP_LEVEL_COMPRESS( 0.49f * r); + mLevel[1][5] = DSP_LEVEL_COMPRESS( 0.872f * r); + } + else if (inchannels == 8) + { + /* + Prologic 2x downmix? + */ + mLevel[0][0] = DSP_LEVEL_COMPRESS( 1.0f * l); + mLevel[0][1] = DSP_LEVEL_COMPRESS( 0.0f * l); + mLevel[0][2] = DSP_LEVEL_COMPRESS( 0.707f * l); + mLevel[0][3] = DSP_LEVEL_COMPRESS( 0.0f * l); /* Discard LFE */ + mLevel[0][4] = DSP_LEVEL_COMPRESS(-0.872f * l); + mLevel[0][5] = DSP_LEVEL_COMPRESS(-0.49f * l); + + mLevel[0][6] = DSP_LEVEL_COMPRESS( 1.0f * l); /* Not sure here. Need PLIIx coefficients for 7.1? */ + mLevel[0][7] = DSP_LEVEL_COMPRESS( 0.0f * l); /* Not sure here. Need PLIIx coefficients for 7.1? */ + + mLevel[1][0] = DSP_LEVEL_COMPRESS( 0.0f * r); + mLevel[1][1] = DSP_LEVEL_COMPRESS( 1.0f * r); + mLevel[1][2] = DSP_LEVEL_COMPRESS( 0.707f * r); + mLevel[1][3] = DSP_LEVEL_COMPRESS( 0.0f * r); /* Discard LFE */ + mLevel[1][4] = DSP_LEVEL_COMPRESS( 0.49f * r); + mLevel[1][5] = DSP_LEVEL_COMPRESS( 0.872f * r); + + mLevel[1][6] = DSP_LEVEL_COMPRESS( 0.0f * r); /* Not sure here. Need PLIIx coefficients for 7.1? */ + mLevel[1][7] = DSP_LEVEL_COMPRESS( 1.0f * r); /* Not sure here. Need PLIIx coefficients for 7.1? */ + } + } + else + { + /* + Map 1 input channel to each output channel sequentially. For example. + + 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 + 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 + 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 + 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 + 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 + */ + for (count = 0; count < outchannels; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if (count == count2) + { + mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f); + } + } + } + } + break; + } +#endif + case FMOD_SPEAKERMODE_QUAD: + case FMOD_SPEAKERMODE_SURROUND: + case FMOD_SPEAKERMODE_5POINT1: + case FMOD_SPEAKERMODE_7POINT1: + { + float l,r; + + pan = (pan + 1.0f) / 2.0f; + + if (inchannels == 1) + { + l = 1.0f - pan; + r = pan; + + l = FMOD_SQRT(l); + r = FMOD_SQRT(r); + + mLevel[0][0] = DSP_LEVEL_COMPRESS(l); + mLevel[1][0] = DSP_LEVEL_COMPRESS(r); + } + else if (inchannels == 2) + { + /* + Stereo source panning is 0.0 = 1.0:0.0. 0.5 = 1.0:1.0. 1.0 = 0.0:1.0. + */ + if (pan <= 0.5f) + { + l = 1.0f; + r = pan * 2.0f; + } + else + { + l = (1.0f - pan) * 2.0f; + r = 1.0f; + } + + mLevel[0][0] = DSP_LEVEL_COMPRESS(l); + mLevel[1][1] = DSP_LEVEL_COMPRESS(r); + } + else + { + /* + Map 1 input channel to each output channel sequentially. For example. + + 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 + 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 + 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 + 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 + 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 + */ + for (count = 0; count < outchannels; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if (count == count2) + { + mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f); + } + } + } + } + break; + } +#ifdef FMOD_SUPPORT_SPEAKERMODE_PROLOGIC + case FMOD_SPEAKERMODE_PROLOGIC: + { + float l,r; + + pan = (pan + 1.0f) / 2.0f; + + if (inchannels == 1) + { + l = 1.0f - pan; + r = pan; + + l = FMOD_SQRT(l); + r = FMOD_SQRT(r); + + mLevel[0][0] = DSP_LEVEL_COMPRESS(l); + mLevel[1][0] = DSP_LEVEL_COMPRESS(r); + } + else if (inchannels == 2) + { + /* + Stereo source panning is 0.0 = 1.0:0.0. 0.5 = 1.0:1.0. 1.0 = 0:1.0. + */ + if (pan < 0.5f) + { + l = 1.0f; + r = pan * 2.0f; + } + else + { + l = (1.0f - pan) * 2.0f; + r = 1.0f; + } + + mLevel[0][0] = DSP_LEVEL_COMPRESS(l); + mLevel[1][1] = DSP_LEVEL_COMPRESS(r); + } + else + { + /* + Map 1 input channel to each output channel sequentially. For example. + + 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 or 1 0 0 0 0 0 0 0 or 1 0 0 0 or 1 0 or 1 + 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 + 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 + 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 + 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 + */ + for (count = 0; count < outchannels; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if (count == count2) + { + mLevel[count][count2] = DSP_LEVEL_COMPRESS(1.0f); + } + } + } + } + break; + } +#endif + default: + { + break; + } + }; + + mSetLevelsUsed = true; + + return rampTo(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + +1 [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::setLevels(float *levels, int numinputlevels) +{ + int count, count2; + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!numinputlevels) + { + return FMOD_OK; + } + +#if (DSP_MAXLEVELS_OUT > 2) + if (mMaxOutputLevels == 6 && numinputlevels < 3) + { + if (numinputlevels == 1) + { + mLevel[0][0] = DSP_LEVEL_COMPRESS(levels[0]); + mLevel[1][0] = DSP_LEVEL_COMPRESS(levels[1]); + mLevel[2][0] = DSP_LEVEL_COMPRESS(levels[2]); + mLevel[3][0] = DSP_LEVEL_COMPRESS(levels[3]); + mLevel[4][0] = DSP_LEVEL_COMPRESS(levels[4]); + mLevel[5][0] = DSP_LEVEL_COMPRESS(levels[5]); + } + else + { + mLevel[0][0] = DSP_LEVEL_COMPRESS(levels[0]); + mLevel[0][1] = DSP_LEVEL_COMPRESS(levels[1]); + mLevel[1][0] = DSP_LEVEL_COMPRESS(levels[2]); + mLevel[1][1] = DSP_LEVEL_COMPRESS(levels[3]); + mLevel[2][0] = DSP_LEVEL_COMPRESS(levels[4]); + mLevel[2][1] = DSP_LEVEL_COMPRESS(levels[5]); + mLevel[3][0] = DSP_LEVEL_COMPRESS(levels[6]); + mLevel[3][1] = DSP_LEVEL_COMPRESS(levels[7]); + mLevel[4][0] = DSP_LEVEL_COMPRESS(levels[8]); + mLevel[4][1] = DSP_LEVEL_COMPRESS(levels[9]); + mLevel[5][0] = DSP_LEVEL_COMPRESS(levels[10]); + mLevel[5][1] = DSP_LEVEL_COMPRESS(levels[11]); + } + } + else +#endif + { + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + if (count2 < numinputlevels) + { + mLevel[count][count2] = DSP_LEVEL_COMPRESS(levels[(count * numinputlevels) + count2]); + } + else + { + mLevel[count][count2] = 0; + } + } + } + } + + mSetLevelsUsed = true; + + return rampTo(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getLevels(float *levels, int numinputlevels) +{ + int count, count2; + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < numinputlevels; count2++) + { + if (count2 < mMaxInputLevels) + { + levels[(count * numinputlevels) + count2] = DSP_LEVEL_DECOMPRESS(mLevel[count][count2]); + } + else + { + levels[(count * numinputlevels) + count2] = 0; + } + } + } + + return FMOD_OK; +} + +#endif /* #ifdef FMOD_SUPPORT_SOFTWARE */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::setMix(float volume) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + if (volume < -1.0f) + { + volume = -1.0f; + } + if (volume > 1.0f) + { + volume = 1.0f; + } + + if (mVolume == volume) + { + return FMOD_OK; + } + + mVolume = volume; + + return rampTo(); +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getMix(float *volume) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + if (!volume) + { + return FMOD_ERR_INVALID_PARAM; + } + + *volume = mVolume; + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::setLevels(FMOD_SPEAKER speaker, float *levels, int numinputlevels) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + int count; + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!numinputlevels) + { + return FMOD_OK; + } + + if (speaker >= mMaxOutputLevels) + { + return FMOD_ERR_INVALID_SPEAKER; + } + + for (count = 0; count < mMaxInputLevels; count++) + { + if (count < numinputlevels) + { + mLevel[speaker][count] = DSP_LEVEL_COMPRESS(levels[count]); + } + else + { + mLevel[speaker][count] = 0; + } + } + + mSetLevelsUsed = true; + + return rampTo(); +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getLevels(FMOD_SPEAKER speaker, float *levels, int numinputlevels) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + int count; + + if (!levels) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0; count < numinputlevels; count++) + { + if (count < mMaxInputLevels) + { + levels[count] = DSP_LEVEL_DECOMPRESS(mLevel[speaker][count]); + } + else + { + levels[count] = 0; + } + } + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_SOFTWARE +FMOD_RESULT DSPConnectionI::copy(DSPConnectionI *source) +{ + int count, count2; + + mVolume = source->mVolume; + + for (count = 0; count < mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < mMaxInputLevels; count2++) + { + mLevel [count][count2] = source->mLevel [count][count2]; + mLevelCurrent[count][count2] = source->mLevelCurrent[count][count2]; + mLevelDelta [count][count2] = source->mLevelDelta [count][count2]; + } + } + + mRampCount = source->mRampCount; + mSetLevelsUsed = source->mSetLevelsUsed; + + return FMOD_OK; +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#if defined(FMOD_SUPPORT_MEMORYTRACKER) && !defined(PLATFORM_PS3_SPU) + +FMOD_RESULT DSPConnectionI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSPCONNECTION, sizeof(*this)); + +// LinkedListNode mInputNode; +// LinkedListNode mOutputNode; +// LinkedListNode *mNode; /* Make it a pointer so we can store the node data externally. PS3 will corrupt it otherwise. */ + +// DSP_LEVEL_TYPE *mLevel[DSP_MAXLEVELS_OUT]; +// DSP_LEVEL_TYPE *mLevelCurrent[DSP_MAXLEVELS_OUT]; +// DSP_LEVEL_TYPE *mLevelDelta[DSP_MAXLEVELS_OUT]; + +// DSPI *mInputUnit; +// DSPI *mOutputUnit; + + return FMOD_OK; +} + +#endif + +#if (DSP_LEVEL_PRECISION == 16) && !defined(PLATFORM_XENON) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +unsigned short FMOD_DSP_ConnectionI_Single2HalfP(float source) +{ + typedef union + { + float f; + unsigned int i; + } floatdata; + + floatdata x; + unsigned short hs, he, hm; + unsigned int xs, xe, xm; + int hes; + + x.f = source; + + if( (x.i & 0x7FFFFFFFu) == 0 ) + { // Signed zero + return (unsigned short) (x.i >> 16); // Return the signed zero + } + else + { // Not zero + xs = x.i & 0x80000000u; // Pick off sign bit + xe = x.i & 0x7F800000u; // Pick off exponent bits + xm = x.i & 0x007FFFFFu; // Pick off mantissa bits + if( xe == 0 ) + { // Denormal will underflow, return a signed zero + return (unsigned short) (xs >> 16); + } else if( xe == 0x7F800000u ) + { // Inf or NaN (all the exponent bits are set) + if( xm == 0 ) + { // If mantissa is zero ... + return (unsigned short) ((xs >> 16) | 0x7C00u); // Signed Inf + } else + { + return (unsigned short) 0xFE00u; // NaN, only 1st mantissa bit set + } + } else + { // Normalized number + hs = (unsigned short) (xs >> 16); // Sign bit + hes = ((int)(xe >> 23)) - 127 + 15; // Exponent unbias the single, then bias the halfp + if( hes >= 0x1F ) + { // Overflow + return (unsigned short) ((xs >> 16) | 0x7C00u); // Signed Inf + } else if( hes <= 0 ) + { // Underflow + if( (14 - hes) > 24 ) + { // Mantissa shifted all the way off & no rounding possibility + hm = (unsigned short) 0u; // Set mantissa to zero + } + else + { + xm |= 0x00800000u; // Add the hidden leading bit + hm = (unsigned short) (xm >> (14 - hes)); // Mantissa + if( (xm >> (13 - hes)) & 0x00000001u ) // Check for rounding + hm += (unsigned short) 1u; // Round, might overflow into exp bit, but this is OK + } + return (hs | hm); // Combine sign bit and mantissa bits, biased exponent is zero + } else + { + he = (unsigned short) (hes << 10); // Exponent + hm = (unsigned short) (xm >> 13); // Mantissa + if( xm & 0x00001000u ) // Check for rounding + return (hs | he | hm) + (unsigned short) 1u; // Round, might overflow to inf, this is OK + else + return (hs | he | hm); // No rounding + } + } + } + + return 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +float FMOD_DSP_ConnectionI_HalfP2Single(unsigned short h) +{ + typedef union + { + float f; + unsigned int i; + } floatdata; + + unsigned short hs, he, hm; + unsigned int xs, xe, xm; + signed int xes; + floatdata xp; + int e; + + if( (h & 0x7FFFu) == 0 ) + { // Signed zero + xp.i = ((unsigned int) h) << 16; // Return the signed zero + } + else + { // Not zero + hs = h & 0x8000u; // Pick off sign bit + he = h & 0x7C00u; // Pick off exponent bits + hm = h & 0x03FFu; // Pick off mantissa bits + if( he == 0 ) + { // Denormal will convert to normalized + e = -1; // The following loop figures out how much extra to adjust the exponent + do + { + e++; + hm <<= 1; + } while( (hm & 0x0400u) == 0 ); // Shift until leading bit overflows into exponent bit + xs = ((unsigned int) hs) << 16; // Sign bit + xes = ((signed int) (he >> 10)) - 15 + 127 - e; // Exponent unbias the halfp, then bias the single + xe = (unsigned int) (xes << 23); // Exponent + xm = ((unsigned int) (hm & 0x03FFu)) << 13; // Mantissa + xp.i = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits + } + else if( he == 0x7C00u ) + { // Inf or NaN (all the exponent bits are set) + if( hm == 0 ) + { // If mantissa is zero ... + xp.i = (((unsigned int) hs) << 16) | ((unsigned int) 0x7F800000u); // Signed Inf + } else + { + xp.i = (unsigned int) 0xFFC00000u; // NaN, only 1st mantissa bit set + } + } + else + { // Normalized number + xs = ((unsigned int) hs) << 16; // Sign bit + xes = ((signed int) (he >> 10)) - 15 + 127; // Exponent unbias the halfp, then bias the single + xe = (unsigned int) (xes << 23); // Exponent + xm = ((unsigned int) hm) << 13; // Mantissa + xp.i = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits + } + } + return xp.f; +} +#endif + +} diff --git a/src/fmod_dsp_connectioni.h b/src/fmod_dsp_connectioni.h new file mode 100755 index 0000000..195d151 --- /dev/null +++ b/src/fmod_dsp_connectioni.h @@ -0,0 +1,143 @@ +#ifndef _FMOD_DSP_CONNECTION_H +#define _FMOD_DSP_CONNECTION_H + +#include "fmod_settings.h" +#include "fmod.hpp" +#include "fmod_channeli.h" +#include "fmod_linkedlist.h" + +#include <stdlib.h> + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + #if defined(PLATFORM_PSP) || defined(PLATFORM_PS2) || defined(PLATFORM_GC) || defined(PLATFORM_IPHONE) + #define DSP_MAXLEVELS_OUT 2 /* Save memory on these platforms. They'll never see more than stereo. */ + #elif (defined(PLATFORM_XENON) && !defined(FMOD_SUPPORT_NEURAL)) || defined(PLATFORM_XBOX) || defined(PLATFORM_WII) /* DPL2 for Wii */ + #define DSP_MAXLEVELS_OUT 6 /* Never see more than 5.1. */ + #elif defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) || (defined(PLATFORM_XENON) && defined(FMOD_SUPPORT_NEURAL)) + #define DSP_MAXLEVELS_OUT 8 /* Never see more than 7.1. */ + #else + #define DSP_MAXLEVELS_OUT 16 + #endif + + #define DSP_MAXLEVELS_IN FMOD_CHANNEL_MAXINPUTCHANNELS + #define DSP_MAXLEVELS_MAX 16 /* This should be whatever is the largest out of DSP_MAXLEVELS_IN and DSP_MAXLEVELS_OUT */ + + #define DSP_DEFAULTLEVELS_IN 6 /* Enough for a 5.1 input sound. */ + + const int DSP_RAMPCOUNT = 64; /* All volume ramps will happen over this number of samples to remove clicks. */ + + #ifdef PLATFORM_XENON + #define DSP_LEVEL_PRECISION 16 /* Half floats - 5 bit exponent, 15bit mantissa. */ + #else + #define DSP_LEVEL_PRECISION 32 + #endif + + #define DSP_LEVEL_SMALLVAL 0.00002f + + #if (DSP_LEVEL_PRECISION == 32) + #define DSP_LEVEL_TYPE float + + #define DSP_LEVEL_COMPRESS(_val) _val + #define DSP_LEVEL_DECOMPRESS(_val) _val + #else + #define DSP_LEVEL_TYPE unsigned short + + #ifdef PLATFORM_XENON + FMOD_INLINE unsigned short FMOD_DSP_ConnectionI_Single2HalfP(float source) + { + __vector4 a; + a.y = source; + return __vpkd3d( a, a, VPACK_FLOAT16_4, VPACK_64LO, 0 ).u[2]; + } + FMOD_INLINE float FMOD_DSP_ConnectionI_HalfP2Single(unsigned short h) + { + __vector4 a; + a.u[3] = h; + return __vupkd3d( a, VPACK_FLOAT16_4).w; + } + #else + unsigned short FMOD_DSP_ConnectionI_Single2HalfP(float source); + float FMOD_DSP_ConnectionI_HalfP2Single(unsigned short h); + #endif + + #define DSP_LEVEL_COMPRESS(_val) FMOD_DSP_ConnectionI_Single2HalfP(_val) + #define DSP_LEVEL_DECOMPRESS(_val) FMOD_DSP_ConnectionI_HalfP2Single(_val) + #endif + + class DSPI; + + class DSPConnectionI + { + DECLARE_MEMORYTRACKER + + public: + + LinkedListNode mInputNode; + LinkedListNode mOutputNode; + LinkedListNode *mNode; /* Make it a pointer so we can store the node data externally. PS3 will corrupt it otherwise. */ + + short mMaxOutputLevels FMOD_PACKED; + short mMaxInputLevels FMOD_PACKED; + + DSP_LEVEL_TYPE *mLevel[DSP_MAXLEVELS_OUT]; + DSP_LEVEL_TYPE *mLevelCurrent[DSP_MAXLEVELS_OUT]; + DSP_LEVEL_TYPE *mLevelDelta[DSP_MAXLEVELS_OUT]; + + public: + + DSPI *mInputUnit; + DSPI *mOutputUnit; + + short mRampCount FMOD_PACKED; + short mSetLevelsUsed FMOD_PACKED; + + #ifdef PLATFORM_PS3 + unsigned short mInputUnitSize FMOD_PACKED; + unsigned short mOutputUnitSize FMOD_PACKED; + + FMOD_PPCALIGN16(unsigned int mMramAddress); + FMOD_PPCALIGN16(unsigned int mMramAddressLevels); + + #endif + + float mVolume; + void *mUserData; + + static FMOD_RESULT validate (DSPConnection *dspconnection, DSPConnectionI **dspconnectioni); + + FMOD_RESULT init (DSP_LEVEL_TYPE * &levelmemory, int maxoutputlevels, int maxinputlevels); + + FMOD_RESULT mix (float * FMOD_RESTRICT outbuffer, float * FMOD_RESTRICT inbuffer, int outchannels, int inchannels, unsigned int length); + FMOD_RESULT mixAndRamp (float * FMOD_RESTRICT outbuffer, float * FMOD_RESTRICT inbuffer, int outchannels, int inchannels, unsigned int length); + + FMOD_RESULT getInput (DSPI **input); + FMOD_RESULT getOutput (DSPI **output); + FMOD_RESULT reset (); + FMOD_RESULT setUnity (); + FMOD_RESULT rampTo (); + FMOD_RESULT checkUnity (int outchannels, int inchannels); + FMOD_RESULT checkMono (int outchannels, int inchannels); + FMOD_RESULT setPan (float pan, int outchannels, int inchannels, FMOD_SPEAKERMODE speakermode); + FMOD_RESULT setMix (float volume); + FMOD_RESULT getMix (float *volume); + FMOD_RESULT setLevels (float *levels, int numinputlevels); + FMOD_RESULT getLevels (float *levels, int numinputlevels); + FMOD_RESULT setLevels (FMOD_SPEAKER speaker, float *levels, int numlevels); + FMOD_RESULT getLevels (FMOD_SPEAKER speaker, float *levels, int numlevels); + FMOD_RESULT setUserData (void *userdata); + FMOD_RESULT getUserData (void **userdata); + + FMOD_RESULT copy (DSPConnectionI *source); + + FMOD_RESULT getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + }; + +} + +#endif + diff --git a/src/fmod_dsp_connectionpool.cpp b/src/fmod_dsp_connectionpool.cpp new file mode 100755 index 0000000..026f895 --- /dev/null +++ b/src/fmod_dsp_connectionpool.cpp @@ -0,0 +1,381 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_dsp_connectionpool.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionPool::init(SystemI *system, int numconnections, int numoutputlevels, int numinputlevels) +{ + DSP_LEVEL_TYPE *leveldata; + int count; + unsigned int alignment = 0; + + if (numconnections < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (int i=0; i < DSP_MAX_CONNECTION_BLOCKS; i++) + { + mConnection[i] = 0; + mLevelData[i] = 0; + } + + numconnections += 128; + numconnections /= 128; + numconnections *= 128; + + mNumConnections = numconnections; + mConnectionMemory[0] = (DSPConnectionI *)FMOD_Memory_Calloc(mNumConnections * sizeof(DSPConnectionI) + 16); + if (!mConnectionMemory[0]) + { + return FMOD_ERR_MEMORY; + } + mConnection[0] = (DSPConnectionI *)FMOD_ALIGNPOINTER(mConnectionMemory[0], 16); + + mNodeMemory[0] = (LinkedListNode *)FMOD_Memory_Calloc(mNumConnections * sizeof(LinkedListNode)); + if (!mNodeMemory[0]) + { + return FMOD_ERR_MEMORY; + } + + mNumOutputLevels = numoutputlevels; + mNumInputLevels = numinputlevels; + + #ifdef PLATFORM_PS3 + alignment = 128; + #endif + mLevelDataMemory[0] = (DSP_LEVEL_TYPE *)FMOD_Memory_Calloc(mNumConnections /* How many connections are we going to allow maximum. */ + * (mNumOutputLevels < 2 ? 2 : mNumOutputLevels) /* Number of rows. This should be at least 2 for panning. */ + * (mNumInputLevels < mNumOutputLevels ? mNumOutputLevels : mNumInputLevels) /* Because pan matricies can be [outputlevels][outputlevels] we need to have at least this */ + * sizeof(DSP_LEVEL_TYPE) /* Float values of course. Maybe lower? */ + * 3 /* *3 = mLevel, mLevelCurrent, mLevelDelta */ + + alignment /* PS3 - 128 byte aligned for DMA */ + ); + if (!mLevelDataMemory[0]) + { + return FMOD_ERR_MEMORY; + } + #ifdef PLATFORM_PS3 + mLevelData[0] = (DSP_LEVEL_TYPE *)FMOD_ALIGNPOINTER(mLevelDataMemory[0], 128); + #else + mLevelData[0] = mLevelDataMemory[0]; + #endif + leveldata = mLevelData[0]; + + mFreeListHead.initNode(); + for (count = 0; count < mNumConnections; count++) + { + DSPConnectionI *conn = &mConnection[0][count]; + + new (conn) DSPConnectionI; + + conn->init(leveldata, numoutputlevels, numinputlevels); + conn->mNode = &mNodeMemory[0][count]; + conn->mNode->setData(conn); + conn->mNode->addAfter(&mFreeListHead); + } + + mSystem = system; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionPool::close() +{ + int i; + + for (i = 0; i < DSP_MAX_CONNECTION_BLOCKS; i++) + { + if (mConnectionMemory[i]) + { + FMOD_Memory_Free(mConnectionMemory[i]); + mConnectionMemory[i] = 0; + } + mConnection[i] = 0; + + if (mLevelDataMemory[i]) + { + FMOD_Memory_Free(mLevelDataMemory[i]); + mLevelDataMemory[i] = 0; + } + mLevelData[i] = 0; + + if (mNodeMemory[i]) + { + FMOD_Memory_Free(mNodeMemory[i]); + mNodeMemory[i] = 0; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionPool::alloc(DSPConnectionI **connection, bool protect) +{ + LocalCriticalSection crit(mSystem->mDSPConnectionCrit); + DSPConnectionI *newconnection; + unsigned int alignment = 0; + + if (!mSystem) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!connection) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (protect) + { + crit.enter(); + } + + if (mFreeListHead.isEmpty()) + { + int newblock, count; + DSP_LEVEL_TYPE *leveldata = 0; + + for (newblock = 0; newblock < DSP_MAX_CONNECTION_BLOCKS; newblock++) + { + if (!mConnectionMemory[newblock]) + { + break; + } + } + if (newblock >= DSP_MAX_CONNECTION_BLOCKS) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "DSPConnectionPool::alloc", "DSP_MAX_CONNECTION_BLOCKS exceeded! Memory will leak!\n")); + return FMOD_ERR_INTERNAL; + } + + mConnectionMemory[newblock] = (DSPConnectionI *)FMOD_Memory_Calloc(mNumConnections * sizeof(DSPConnectionI) + 16); + if (!mConnectionMemory[newblock]) + { + return FMOD_ERR_MEMORY; + } + mConnection[newblock] = (DSPConnectionI *)FMOD_ALIGNPOINTER(mConnectionMemory[newblock], 16); + + #ifdef PLATFORM_PS3 + alignment = 128; + #endif + mNodeMemory[newblock] = (LinkedListNode *)FMOD_Memory_Calloc(mNumConnections * sizeof(LinkedListNode)); + if (!mNodeMemory[newblock]) + { + return FMOD_ERR_MEMORY; + } + + mLevelDataMemory[newblock] = (DSP_LEVEL_TYPE *)FMOD_Memory_Calloc(mNumConnections /* How many connections are we going to allow maximum. */ + * (mNumOutputLevels < 2 ? 2 : mNumOutputLevels) /* Number of rows. This should be at least 2 for panning. */ + * (mNumInputLevels < mNumOutputLevels ? mNumOutputLevels : mNumInputLevels) /* Because pan matricies can be [outputlevels][outputlevels] we need to have at least this */ + * sizeof(DSP_LEVEL_TYPE) /* Float values of course. Maybe lower? */ + * 3 /* *3 = mLevel, mLevelCurrent, mLevelDelta */ + + alignment /* PS3 - 128 byte aligned for DMA */ + ); + if (!mLevelDataMemory[newblock]) + { + return FMOD_ERR_MEMORY; + } + #ifdef PLATFORM_PS3 + mLevelData[newblock] = (DSP_LEVEL_TYPE *)FMOD_ALIGNPOINTER(mLevelDataMemory[newblock], 128); + #else + mLevelData[newblock] = mLevelDataMemory[newblock]; + #endif + + leveldata = mLevelData[newblock]; + + for (count = 0; count < mNumConnections; count++) + { + DSPConnectionI *conn = &mConnection[newblock][count]; + + new (conn) DSPConnectionI; + + conn->init(leveldata, mNumOutputLevels, mNumInputLevels); + conn->mNode = &mNodeMemory[newblock][count]; + conn->mNode->setData(conn); + conn->mNode->addAfter(&mFreeListHead); + } + } + + newconnection = (DSPConnectionI *)mFreeListHead.getNext()->getData(); + + newconnection->mInputNode.setData(newconnection); + newconnection->mOutputNode.setData(newconnection); + + newconnection->mNode->removeNode(); + newconnection->mNode->addAfter(&mUsedListHead); + + if (protect) + { + crit.leave(); + } + + *connection = newconnection; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPConnectionPool::free(DSPConnectionI *connection, bool protect) +{ + LocalCriticalSection crit(mSystem->mDSPConnectionCrit); + + if (!connection) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (protect) + { + crit.enter(); + } + + connection->mInputNode.removeNode(); + connection->mOutputNode.removeNode(); + + connection->mInputUnit = 0; + connection->mOutputUnit = 0; + + connection->mNode->removeNode(); + connection->mNode->addAfter(&mFreeListHead); + connection->mNode->setData(connection); + + if (protect) + { + crit.leave(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPConnectionPool::getMemoryUsedImpl(MemoryTracker *tracker) +{ + for (int i=0; i < DSP_MAX_CONNECTION_BLOCKS; i++) + { + if (mConnectionMemory[i]) + { + tracker->add(false, FMOD_MEMBITS_DSPCONNECTION, mNumConnections * sizeof(DSPConnectionI) + 16); + } + + if (mNodeMemory[i]) + { + tracker->add(false, FMOD_MEMBITS_DSPCONNECTION, mNumConnections * sizeof(LinkedListNode)); + } + + if (mLevelDataMemory[i]) + { + unsigned int alignment = 0; + + #ifdef PLATFORM_PS3 + alignment = 128; + #endif + + tracker->add(false, FMOD_MEMBITS_DSPCONNECTION, mNumConnections /* How many connections are we going to allow maximum. */ + * (mNumOutputLevels < 2 ? 2 : mNumOutputLevels) /* Number of rows. This should be at least 2 for panning. */ + * (mNumInputLevels < mNumOutputLevels ? mNumOutputLevels : mNumInputLevels) /* Because pan matricies can be [outputlevels][outputlevels] we need to have at least this */ + * sizeof(DSP_LEVEL_TYPE) /* Float values of course. Maybe lower? */ + * 3 /* *3 = mLevel, mLevelCurrent, mLevelDelta */ + + alignment /* PS3 - 128 byte aligned for DMA */ + ); + } + } + + return FMOD_OK; +} + +#endif + +} + +#endif diff --git a/src/fmod_dsp_connectionpool.h b/src/fmod_dsp_connectionpool.h new file mode 100755 index 0000000..ffdbf1e --- /dev/null +++ b/src/fmod_dsp_connectionpool.h @@ -0,0 +1,48 @@ +#ifndef _FMOD_DSP_CONNECTIONPOOL_H +#define _FMOD_DSP_CONNECTIONPOOL_H + +#include "fmod_settings.h" + +#include "fmod_dsp_connectioni.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +#define DSP_MAX_CONNECTION_BLOCKS 128 + +namespace FMOD +{ + class SystemI; + class DSPConnectionI; + + class DSPConnectionPool + { + DECLARE_MEMORYTRACKER + + private: + + SystemI *mSystem; + DSPConnectionI *mConnection[DSP_MAX_CONNECTION_BLOCKS]; + DSPConnectionI *mConnectionMemory[DSP_MAX_CONNECTION_BLOCKS]; + LinkedListNode *mNodeMemory[DSP_MAX_CONNECTION_BLOCKS]; + int mNumInputLevels; + int mNumOutputLevels; + int mNumConnections; + LinkedListNode mUsedListHead; + LinkedListNode mFreeListHead; + DSP_LEVEL_TYPE *mLevelData[DSP_MAX_CONNECTION_BLOCKS]; + DSP_LEVEL_TYPE *mLevelDataMemory[DSP_MAX_CONNECTION_BLOCKS]; + + public: + + FMOD_RESULT init(SystemI *system, int numconnections, int numoutputlevels, int numinputlevels); + FMOD_RESULT close(); + + FMOD_RESULT alloc(DSPConnectionI **connection, bool protect = true); + FMOD_RESULT free(DSPConnectionI *connection, bool protect = true); + }; +} + +#endif + diff --git a/src/fmod_dsp_convert.cpp b/src/fmod_dsp_convert.cpp new file mode 100755 index 0000000..bdccdde --- /dev/null +++ b/src/fmod_dsp_convert.cpp @@ -0,0 +1,373 @@ +#include "fmod_settings.h" + +#include "fmod_dspi.h" +#include "fmod_dsp_filter.h" +#include "fmod_dsp_resampler.h" +#include "fmod_dsp_soundcard.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_soundi.h" +#include "fmod_string.h" +#include "fmod_systemi.h" + +#ifdef FMOD_SUPPORT_SIMD +extern "C" +{ + void FMOD_DSP_Convert_FloatToPCM16(short *outbuffer, float *inbuffer, unsigned int length, int destchannelstep, int srcchannelstep, float volume); +} +#endif + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::convert(void *outbuffer, void *inbuffer, FMOD_SOUND_FORMAT outformat, FMOD_SOUND_FORMAT informat, unsigned int length, int destchannelstep, int srcchannelstep, float volume) +{ + bool supportssimd; + + supportssimd = FMOD_OS_SupportsSIMD(); + + if (outformat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + float *destptr = (float *)outbuffer; + + switch (informat) + { + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *srcptr = (signed char *)inbuffer; + unsigned int len; + + volume /= (float)(1<<7); + + len = length >> 3; + while (len) + { + destptr[0 * destchannelstep] = (float)srcptr[0 * srcchannelstep] * volume; + destptr[1 * destchannelstep] = (float)srcptr[1 * srcchannelstep] * volume; + destptr[2 * destchannelstep] = (float)srcptr[2 * srcchannelstep] * volume; + destptr[3 * destchannelstep] = (float)srcptr[3 * srcchannelstep] * volume; + destptr[4 * destchannelstep] = (float)srcptr[4 * srcchannelstep] * volume; + destptr[5 * destchannelstep] = (float)srcptr[5 * srcchannelstep] * volume; + destptr[6 * destchannelstep] = (float)srcptr[6 * srcchannelstep] * volume; + destptr[7 * destchannelstep] = (float)srcptr[7 * srcchannelstep] * volume; + destptr += (8 * destchannelstep); + srcptr += (8 * srcchannelstep); + len--; + } + len = length & 7; + while (len) + { + destptr[0] = (float)srcptr[0 * srcchannelstep] * volume; + destptr += destchannelstep; + srcptr += srcchannelstep; + len--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *srcptr = (signed short *)inbuffer; + unsigned int len; + + volume /= (float)(1<<15); + + len = length >> 3; + while (len) + { + destptr[0 * destchannelstep] = (float)srcptr[0 * srcchannelstep] * volume; + destptr[1 * destchannelstep] = (float)srcptr[1 * srcchannelstep] * volume; + destptr[2 * destchannelstep] = (float)srcptr[2 * srcchannelstep] * volume; + destptr[3 * destchannelstep] = (float)srcptr[3 * srcchannelstep] * volume; + destptr[4 * destchannelstep] = (float)srcptr[4 * srcchannelstep] * volume; + destptr[5 * destchannelstep] = (float)srcptr[5 * srcchannelstep] * volume; + destptr[6 * destchannelstep] = (float)srcptr[6 * srcchannelstep] * volume; + destptr[7 * destchannelstep] = (float)srcptr[7 * srcchannelstep] * volume; + destptr += (8 * destchannelstep); + srcptr += (8 * srcchannelstep); + len--; + } + len = length & 7; + while (len) + { + destptr[0] = (float)srcptr[0] * volume; + destptr += destchannelstep; + srcptr += srcchannelstep; + len--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM24: + { + FMOD_INT24 *srcptr = (FMOD_INT24 *)inbuffer; + unsigned int count; + + volume /= (float)(1<<23); + + for (count = 0; count < length; count++) + { + signed int val; + + val = ((unsigned int)srcptr->val[0 * srcchannelstep] << 8); + val |= ((unsigned int)srcptr->val[1 * srcchannelstep] << 16); + val |= ((unsigned int)srcptr->val[2 * srcchannelstep] << 24); + val >>= 8; + + destptr[0] = (float)val * volume; + srcptr += srcchannelstep; + destptr += destchannelstep; + } + break; + } + case FMOD_SOUND_FORMAT_PCM32: + { + signed int *srcptr = (signed int *)inbuffer; + unsigned int len; + + volume /= (float)(1U<<31); + + len = length >> 2; + while (len) + { + destptr[0 * destchannelstep] = (float)(srcptr[0 * srcchannelstep]) * volume; + destptr[1 * destchannelstep] = (float)(srcptr[1 * srcchannelstep]) * volume; + destptr[2 * destchannelstep] = (float)(srcptr[2 * srcchannelstep]) * volume; + destptr[3 * destchannelstep] = (float)(srcptr[3 * srcchannelstep]) * volume; + destptr += (4 * destchannelstep); + srcptr += (4 * srcchannelstep); + len--; + } + len = length & 3; + while (len) + { + destptr[0] = (float)(srcptr[0]) * volume; + destptr += destchannelstep; + srcptr += srcchannelstep; + len--; + } + break; + } + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + float *srcptr = (float *)inbuffer; + unsigned int len; + + len = length >> 2; + while (len) + { + destptr[0 * destchannelstep] = srcptr[0 * srcchannelstep] * volume; + destptr[1 * destchannelstep] = srcptr[1 * srcchannelstep] * volume; + destptr[2 * destchannelstep] = srcptr[2 * srcchannelstep] * volume; + destptr[3 * destchannelstep] = srcptr[3 * srcchannelstep] * volume; + destptr += (4 * destchannelstep); + srcptr += (4 * srcchannelstep); + len--; + } + + len = length & 3; + while (len) + { + destptr[0] = srcptr[0] * volume; + destptr += destchannelstep; + srcptr += srcchannelstep; + len--; + } + break; + } + default: + { + break; + } + } + } + else + { + float *srcptr = (float *)inbuffer; + + /* + Source must be float! + */ + if (informat != FMOD_SOUND_FORMAT_PCMFLOAT) + { + return FMOD_ERR_DSP_FORMAT; + } + + switch (outformat) + { + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *destptr = (signed char *)outbuffer; + unsigned int len; + + volume *= (float)(1<<7); + + len = length >> 2; + while (len) + { + signed int val[4]; + + val[0] = (signed int)(srcptr[0 * srcchannelstep] * volume); + val[1] = (signed int)(srcptr[1 * srcchannelstep] * volume); + val[2] = (signed int)(srcptr[2 * srcchannelstep] * volume); + val[3] = (signed int)(srcptr[3 * srcchannelstep] * volume); + + destptr[0 * destchannelstep] = val[0] < -128 ? -128 : val[0] > 127 ? 127 : (signed char)val[0]; + destptr[1 * destchannelstep] = val[1] < -128 ? -128 : val[1] > 127 ? 127 : (signed char)val[1]; + destptr[2 * destchannelstep] = val[2] < -128 ? -128 : val[2] > 127 ? 127 : (signed char)val[2]; + destptr[3 * destchannelstep] = val[3] < -128 ? -128 : val[3] > 127 ? 127 : (signed char)val[3]; + + srcptr += (4 * srcchannelstep); + destptr += (4 * destchannelstep); + len--; + } + + len = length & 3; + while (len) + { + signed int val; + + val = (signed int)(srcptr[0] * volume); + + destptr[0] = val < -128 ? -128 : val > 127 ? 127 : (signed char)val; + + srcptr += srcchannelstep; + destptr += destchannelstep; + len--; + } + break; + break; + } + case FMOD_SOUND_FORMAT_PCM16: + { + volume *= (float)(1<<15); + + #ifdef FMOD_SUPPORT_SIMD + if (supportssimd) + { + FMOD_DSP_Convert_FloatToPCM16((short *)outbuffer, (float *)inbuffer, length, destchannelstep, srcchannelstep, volume); + } + else + #endif + { + signed short *destptr = (signed short *)outbuffer; + unsigned int len; + + len = length >> 2; + while (len) + { + signed int val[4]; + + val[0] = (signed int)(srcptr[0 * srcchannelstep] * volume); + val[1] = (signed int)(srcptr[1 * srcchannelstep] * volume); + val[2] = (signed int)(srcptr[2 * srcchannelstep] * volume); + val[3] = (signed int)(srcptr[3 * srcchannelstep] * volume); + + destptr[0 * destchannelstep] = val[0] < -32768 ? -32768 : val[0] > 32767 ? 32767 : (signed short)val[0]; + destptr[1 * destchannelstep] = val[1] < -32768 ? -32768 : val[1] > 32767 ? 32767 : (signed short)val[1]; + destptr[2 * destchannelstep] = val[2] < -32768 ? -32768 : val[2] > 32767 ? 32767 : (signed short)val[2]; + destptr[3 * destchannelstep] = val[3] < -32768 ? -32768 : val[3] > 32767 ? 32767 : (signed short)val[3]; + + srcptr += (4 * srcchannelstep); + destptr += (4 * destchannelstep); + len--; + } + + len = length & 3; + while (len) + { + signed int val; + + val = (signed int)(srcptr[0] * volume); + + destptr[0] = val < -32768 ? -32768 : val > 32767 ? 32767 : (signed short)val; + + srcptr += srcchannelstep; + destptr += destchannelstep; + len--; + } + } + break; + } + case FMOD_SOUND_FORMAT_PCM24: + { + FMOD_INT24 *destptr = (FMOD_INT24 *)outbuffer; + unsigned int count; + + volume *= (float)(1<<23); + + for (count = 0; count < length; count++) + { + signed int val; + + val = (signed int)(srcptr[0] * volume); + val = val < -8388608 ? -8388608 : val > 8388607 ? 8388607 : val; + + destptr->val[0] = (val >> 0) & 0xFF; + destptr->val[1] = (val >> 8) & 0xFF; + destptr->val[2] = (val >> 16) & 0xFF; + destptr += destchannelstep; + srcptr += srcchannelstep; + } + break; + } + case FMOD_SOUND_FORMAT_PCM32: + { + signed int *destptr = (signed int *)outbuffer; + unsigned int count; + + volume *= (float)(1U<<31); + + for (count = 0; count < length; count++) + { + float val = srcptr[0] * volume; + destptr[0] = val < -2147483648.0f ? -(signed int)2147483647 : val > 2147483647.0f ? 2147483647 : (signed int)val; + + destptr += destchannelstep; + srcptr += srcchannelstep; + } + break; + } + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + float *destptr = (float *)outbuffer; + unsigned int count; + + for (count = 0; count < length; count++) + { + float val = srcptr[0] * volume; + destptr[0] = val < -1.0f ? -1.0f : val > 1.0f ? 1.0f : val; + + destptr += destchannelstep; + srcptr += srcchannelstep; + } + break; + } + default: + { + break; + } + } + } + + return FMOD_OK; +} + + +} + diff --git a/src/fmod_dsp_delay.cpp b/src/fmod_dsp_delay.cpp new file mode 100755 index 0000000..dcf0f75 --- /dev/null +++ b/src/fmod_dsp_delay.cpp @@ -0,0 +1,1575 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DELAY + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_delay.h" +#include "fmod_systemi.h" + +#include <stdio.h> + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspdelay_desc; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPDelay::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +FMOD_DSP_PARAMETERDESC dspdelay_param[DELAY_MAX_CHANNELS+1] = +{ + { 0.0f, 10000.0f, 0.0f, "Delay ch0", "ms", "Channel #0 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch1", "ms", "Channel #1 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch2", "ms", "Channel #2 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch3", "ms", "Channel #3 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch4", "ms", "Channel #4 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch5", "ms", "Channel #5 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch6", "ms", "Channel #6 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch7", "ms", "Channel #7 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch8", "ms", "Channel #8 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch9", "ms", "Channel #9 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch10", "ms", "Channel #10 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch11", "ms", "Channel #11 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch12", "ms", "Channel #12 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch13", "ms", "Channel #13 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch14", "ms", "Channel #14 delay in ms. 0 to 10000. Default = 0." }, + { 0.0f, 10000.0f, 0.0f, "Delay ch15", "ms", "Channel #15 delay in ms. 0 to 10000. Default = 0." }, + { 1.0f, 10000.0f, 10.0f, "Max delay", "ms", "Maximum delay in ms. 1 to 10000. Default = 10." }, +}; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPDelay::getDescriptionEx() +{ + FMOD_memset(&dspdelay_desc, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspdelay_desc.name, "FMOD Delay"); + dspdelay_desc.version = 0x00010100; + dspdelay_desc.create = DSPDelay::createCallback; + dspdelay_desc.release = DSPDelay::releaseCallback; + dspdelay_desc.reset = DSPDelay::resetCallback; + dspdelay_desc.read = DSPDelay::readCallback; + + dspdelay_desc.numparameters = sizeof(dspdelay_param) / sizeof(dspdelay_param[0]); + dspdelay_desc.paramdesc = dspdelay_param; + dspdelay_desc.setparameter = DSPDelay::setParameterCallback; + dspdelay_desc.getparameter = DSPDelay::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspdelay_desc.getmemoryused = &DSPDelay::getMemoryUsedCallback; +#endif + + dspdelay_desc.mType = FMOD_DSP_TYPE_DELAY; + dspdelay_desc.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspdelay_desc.mSize = sizeof(DSPDelay); + + return &dspdelay_desc; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDelay::createInternal() +{ + int count; + + init(); + + mChannels = 0; + mOldSpeakerMask = 0xFFFF; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mMaxDelay = mMaxDelayUpdate; + { + int outputrate = 0; + + mSystem->getSoftwareFormat(&outputrate, 0, &mChannels, 0, 0, 0); + + for (count = 0; count < DELAY_MAX_CHANNELS; count++) + { + mDelay[count] = (mDelayUpdate[count] < mMaxDelay) ? mDelayUpdate[count] : mMaxDelay; + mOffset[count] = (int)(((float)outputrate * mDelay[count] / 1000.0f ) + 0.5f); + } + /* + need one extra sample as input is written to buffer before output is read + allowing for a delay of zero samples + */ + mDelayBufferLength = (int)(((float)outputrate * mMaxDelay / 1000.0f) + 0.5f) + 1; + + if (mDelayBufferMemory) + { + FMOD_Memory_Free(mDelayBufferMemory); + mDelayBufferMemory = mDelayBuffer = 0; + } + + mDelayBufferLengthBytes = mDelayBufferLength; + mDelayBufferLengthBytes *= mChannels; + + if (!mDelayBufferMemory) + { +#ifdef DELAY_USEFLOAT + mDelayBufferLengthBytes *= sizeof(float); + + mDelayBufferMemory = (float *)FMOD_Memory_Calloc(mDelayBufferLengthBytes + 16); + mDelayBuffer = (float *)FMOD_ALIGNPOINTER(mDelayBufferMemory, 16); +#else + mDelayBufferLengthBytes *= sizeof(signed short); + + mDelayBufferMemory = (signed short *)FMOD_Memory_Calloc(mDelayBufferLengthBytes + 16); + mDelayBuffer = (signed short *)FMOD_ALIGNPOINTER(mDelayBufferMemory, 16); +#endif + } + if (!mDelayBufferMemory) + { + return FMOD_ERR_MEMORY; + } + + resetInternal(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDelay::releaseInternal() +{ + if (mDelayBufferMemory) + { + FMOD_Memory_Free(mDelayBufferMemory); + mDelayBufferMemory = mDelayBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDelay::resetInternal() +{ +#ifdef DELAY_INTERLEAVED + mWritePosition = 0; + for (int count = 0; count < DELAY_MAX_CHANNELS; count++) + { + mReadPosition[count] = (mOffset[count] == 0) ? 0 : mDelayBufferLength - mOffset[count]; + } +#else + for (int count = 0; count < DELAY_MAX_CHANNELS; count++) + { + mBufferStart[count] = mDelayBufferLength * count; + mWritePosition[count] = mReadPosition[count] = mBufferStart[count]; + mReadPosition[count] += (mOffset[count] == 0) ? 0 : mDelayBufferLength - mOffset[count]; + } + mBufferStart[DELAY_MAX_CHANNELS] = mDelayBufferLength * DELAY_MAX_CHANNELS; +#endif + + if (mDelayBuffer) + { + FMOD_memset(mDelayBuffer, 0, mDelayBufferLengthBytes); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDelay::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + bool reallocbuffer = false; + + #define OO_32767 (1.0f / 32767.0f) + + if (!inbuffer) + { + return FMOD_OK; + } + + int outputrate = 0; + + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + + /* + Update parameters + */ + if (mChannels != inchannels) + { + mChannels = inchannels; + + reallocbuffer = true; + } + if (mMaxDelay != mMaxDelayUpdate) + { + mMaxDelay = mMaxDelayUpdate; + + reallocbuffer = true; + } + + if (reallocbuffer || !mDelayBuffer) + { + /* + need one extra sample as input is written to buffer before output is read + allowing for a delay of zero samples + */ + mDelayBufferLength = (int)(((float)outputrate * mMaxDelay / 1000.0f) + 0.5f) + 1; + + if (mDelayBufferMemory) + { + FMOD_Memory_Free(mDelayBufferMemory); + mDelayBufferMemory = mDelayBuffer = 0; + } + + mDelayBufferLengthBytes = mDelayBufferLength; + mDelayBufferLengthBytes *= mChannels; + + if (!mDelayBufferMemory) + { +#ifdef DELAY_USEFLOAT + mDelayBufferLengthBytes *= sizeof(float); + + mDelayBufferMemory = (float *)FMOD_Memory_Calloc(mDelayBufferLengthBytes + 16); + mDelayBuffer = (float *)FMOD_ALIGNPOINTER(mDelayBufferMemory, 16); +#else + mDelayBufferLengthBytes *= sizeof(signed short); + + mDelayBufferMemory = (signed short *)FMOD_Memory_Calloc(mDelayBufferLengthBytes + 16); + mDelayBuffer = (signed short *)FMOD_ALIGNPOINTER(mDelayBufferMemory, 16); +#endif + } + if (!mDelayBufferMemory) + { + return FMOD_ERR_MEMORY; + } + } + + for (count = 0; count < (unsigned int)inchannels; count++ ) + { + if (mDelay[count] != mDelayUpdate[count]) + { + if (mDelayUpdate[count] > mMaxDelay) + { + mDelayUpdate[count] = mMaxDelay; + } + mDelay[count] = mDelayUpdate[count]; + mOffset[count] = (int)(((float)outputrate * mDelay[count] / 1000.f) + 0.5f); +#ifdef DELAY_INTERLEAVED + mReadPosition[count] = mWritePosition - mOffset[count]; + if (mReadPosition[count] < 0) + { + mReadPosition[count] += mDelayBufferLength; + } +#else + mReadPosition[count] = mWritePosition[count] - mOffset[count]; + if (mReadPosition[count] < mBufferStart[count]) + { + mReadPosition[count] += mDelayBufferLength; + } +#endif + } + } + + if (reallocbuffer || !mDelayBuffer) + { + resetInternal(); + } + + if (speakermask != mOldSpeakerMask) /* A speaker has been disabled: clear the echo buffer for that speaker to prevent artifacts if/when the speaker is reenabled */ + { + int count2; + unsigned short diff = (mOldSpeakerMask ^ speakermask); + + for (count = 0; count < (unsigned int)inchannels; count++) + { + if (diff & (1 << count)) + { + int len = mDelayBufferLength * inchannels; + for(count2 = count; count2 < len; count2 += inchannels) + { + mDelayBuffer[count2] = 0; + } + } + } + mOldSpeakerMask = speakermask; + } + + if (!mDelayBuffer || !(speakermask & ((1 << inchannels)-1)) ) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_OK; + } + +#ifdef DELAY_INTERLEAVED + if (inchannels == 1 && (speakermask & 1)) + { + unsigned int len; + float *out, *in; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite; + float *delaybuffread; +#else + short *delaybuffwrite; + short *delaybuffread; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite = mDelayBuffer + (mWritePosition * 1); + delaybuffread = (mDelayBuffer+0) + (mReadPosition[0] * 1); + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + +#ifdef DELAY_USEFLOAT + delaybuffwrite[0] = in0; + out[0] = delaybuffread[0]; +#else + delaybuffwrite[0] = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + out[0] = (float)delaybuffread[0] * OO_32767; +#endif + in++; + out++; + delaybuffwrite++; + if (++mReadPosition[0] >= mDelayBufferLength) + { + mReadPosition[0] = 0; + delaybuffread = (mDelayBuffer+0); + } + else + { + delaybuffread++; + } + } + + mWritePosition += len2; + if (mWritePosition >= mDelayBufferLength) + { + mWritePosition = 0; + delaybuffwrite = mDelayBuffer; + } + len -= len2; + } + } + else if (inchannels == 2 && (speakermask & 0x3)==0x3) + { + unsigned int len; + float *out, *in; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite; + float *delaybuffread[2]; +#else + short *delaybuffwrite; + short *delaybuffread[2]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite = mDelayBuffer + (mWritePosition * 2); + delaybuffread[0] = (mDelayBuffer+0) + (mReadPosition[0] * 2); + delaybuffread[1] = (mDelayBuffer+1) + (mReadPosition[1] * 2); + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + +#ifdef DELAY_USEFLOAT + delaybuffwrite[0] = in0; + delaybuffwrite[1] = in1; + out[0] = *delaybuffread[0]; + out[1] = *delaybuffread[1]; +#else + delaybuffwrite[0] = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + delaybuffwrite[1] = in1 > 1.0f ? 32767 : in1 < -1.0f ? -32768: (signed short)(in1 * 32767.0f); + out[0] = (float)*delaybuffread[0] * OO_32767; + out[1] = (float)*delaybuffread[1] * OO_32767; +#endif + in += 2; + out += 2; + delaybuffwrite += 2; + if (++mReadPosition[0] >= mDelayBufferLength) + { + mReadPosition[0] = 0; + delaybuffread[0] = (mDelayBuffer+0); + } + else + { + delaybuffread[0] += 2; + } + if (++mReadPosition[1] >= mDelayBufferLength) + { + mReadPosition[1] = 0; + delaybuffread[1] = (mDelayBuffer+1); + } + else + { + delaybuffread[1] += 2; + } + } + + mWritePosition += len2; + if (mWritePosition >= mDelayBufferLength) + { + mWritePosition = 0; + delaybuffwrite = mDelayBuffer; + } + len -= len2; + } + } + else if (inchannels == 6 && (speakermask & 0x3F)==0x3F) + { + unsigned int len; + float *out, *in; + int ch; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite; + float *delaybuffread[6]; +#else + short *delaybuffwrite; + short *delaybuffread[6]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite = mDelayBuffer + (mWritePosition * 6); + delaybuffread[0] = (mDelayBuffer+0) + (mReadPosition[0] * 6); + delaybuffread[1] = (mDelayBuffer+1) + (mReadPosition[1] * 6); + delaybuffread[2] = (mDelayBuffer+2) + (mReadPosition[2] * 6); + delaybuffread[3] = (mDelayBuffer+3) + (mReadPosition[3] * 6); + delaybuffread[4] = (mDelayBuffer+4) + (mReadPosition[4] * 6); + delaybuffread[5] = (mDelayBuffer+5) + (mReadPosition[5] * 6); + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + float in2 = in[2]; + float in3 = in[3]; + float in4 = in[4]; + float in5 = in[5]; + +#ifdef DELAY_USEFLOAT + delaybuffwrite[0] = in0; + delaybuffwrite[1] = in1; + delaybuffwrite[2] = in2; + delaybuffwrite[3] = in3; + delaybuffwrite[4] = in4; + delaybuffwrite[5] = in5; + out[0] = *delaybuffread[0]; + out[1] = *delaybuffread[1]; + out[2] = *delaybuffread[2]; + out[3] = *delaybuffread[3]; + out[4] = *delaybuffread[4]; + out[5] = *delaybuffread[5]; +#else + delaybuffwrite[0] = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + delaybuffwrite[1] = in1 > 1.0f ? 32767 : in1 < -1.0f ? -32768: (signed short)(in1 * 32767.0f); + delaybuffwrite[2] = in2 > 1.0f ? 32767 : in2 < -1.0f ? -32768: (signed short)(in2 * 32767.0f); + delaybuffwrite[3] = in3 > 1.0f ? 32767 : in3 < -1.0f ? -32768: (signed short)(in3 * 32767.0f); + delaybuffwrite[4] = in4 > 1.0f ? 32767 : in4 < -1.0f ? -32768: (signed short)(in4 * 32767.0f); + delaybuffwrite[5] = in5 > 1.0f ? 32767 : in5 < -1.0f ? -32768: (signed short)(in5 * 32767.0f); + out[0] = (float)*delaybuffread[0] * OO_32767; + out[1] = (float)*delaybuffread[1] * OO_32767; + out[2] = (float)*delaybuffread[2] * OO_32767; + out[3] = (float)*delaybuffread[3] * OO_32767; + out[4] = (float)*delaybuffread[4] * OO_32767; + out[5] = (float)*delaybuffread[5] * OO_32767; +#endif + in += 6; + out += 6; + delaybuffwrite += 6; + for (ch = 0; ch < 6; ch++) + { + if (++mReadPosition[ch] >= mDelayBufferLength) + { + mReadPosition[ch] = 0; + delaybuffread[ch] = (mDelayBuffer+ch); + } + else + { + delaybuffread[ch] += 6; + } + } + } + + mWritePosition += len2; + if (mWritePosition >= mDelayBufferLength) + { + mWritePosition = 0; + delaybuffwrite = mDelayBuffer; + } + len -= len2; + } + } + else if (inchannels == 8 && (speakermask & 0xFF)==0xFF) + { + unsigned int len; + float *out, *in; + int ch; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite; + float *delaybuffread[8]; +#else + short *delaybuffwrite; + short *delaybuffread[8]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite = mDelayBuffer + (mWritePosition * 8); + delaybuffread[0] = (mDelayBuffer+0) + (mReadPosition[0] * 8); + delaybuffread[1] = (mDelayBuffer+1) + (mReadPosition[1] * 8); + delaybuffread[2] = (mDelayBuffer+2) + (mReadPosition[2] * 8); + delaybuffread[3] = (mDelayBuffer+3) + (mReadPosition[3] * 8); + delaybuffread[4] = (mDelayBuffer+4) + (mReadPosition[4] * 8); + delaybuffread[5] = (mDelayBuffer+5) + (mReadPosition[5] * 8); + delaybuffread[6] = (mDelayBuffer+6) + (mReadPosition[6] * 8); + delaybuffread[7] = (mDelayBuffer+7) + (mReadPosition[7] * 8); + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + float in2 = in[2]; + float in3 = in[3]; + float in4 = in[4]; + float in5 = in[5]; + float in6 = in[6]; + float in7 = in[7]; + +#ifdef DELAY_USEFLOAT + delaybuffwrite[0] = in0; + delaybuffwrite[1] = in1; + delaybuffwrite[2] = in2; + delaybuffwrite[3] = in3; + delaybuffwrite[4] = in4; + delaybuffwrite[5] = in5; + delaybuffwrite[6] = in6; + delaybuffwrite[7] = in7; + out[0] = *delaybuffread[0]; + out[1] = *delaybuffread[1]; + out[2] = *delaybuffread[2]; + out[3] = *delaybuffread[3]; + out[4] = *delaybuffread[4]; + out[5] = *delaybuffread[5]; + out[6] = *delaybuffread[6]; + out[7] = *delaybuffread[7]; +#else + delaybuffwrite[0] = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + delaybuffwrite[1] = in1 > 1.0f ? 32767 : in1 < -1.0f ? -32768: (signed short)(in1 * 32767.0f); + delaybuffwrite[2] = in2 > 1.0f ? 32767 : in2 < -1.0f ? -32768: (signed short)(in2 * 32767.0f); + delaybuffwrite[3] = in3 > 1.0f ? 32767 : in3 < -1.0f ? -32768: (signed short)(in3 * 32767.0f); + delaybuffwrite[4] = in4 > 1.0f ? 32767 : in4 < -1.0f ? -32768: (signed short)(in4 * 32767.0f); + delaybuffwrite[5] = in5 > 1.0f ? 32767 : in5 < -1.0f ? -32768: (signed short)(in5 * 32767.0f); + delaybuffwrite[6] = in6 > 1.0f ? 32767 : in6 < -1.0f ? -32768: (signed short)(in6 * 32767.0f); + delaybuffwrite[7] = in7 > 1.0f ? 32767 : in7 < -1.0f ? -32768: (signed short)(in7 * 32767.0f); + out[0] = (float)*delaybuffread[0] * OO_32767; + out[1] = (float)*delaybuffread[1] * OO_32767; + out[2] = (float)*delaybuffread[2] * OO_32767; + out[3] = (float)*delaybuffread[3] * OO_32767; + out[4] = (float)*delaybuffread[4] * OO_32767; + out[5] = (float)*delaybuffread[5] * OO_32767; + out[6] = (float)*delaybuffread[6] * OO_32767; + out[7] = (float)*delaybuffread[7] * OO_32767; +#endif + in += 8; + out += 8; + delaybuffwrite += 8; + for (ch = 0; ch < 8; ch++) + { + if (++mReadPosition[ch] >= mDelayBufferLength) + { + mReadPosition[ch] = 0; + delaybuffread[ch] = (mDelayBuffer+ch); + } + else + { + delaybuffread[ch] += 8; + } + } + } + + mWritePosition += len2; + if (mWritePosition >= mDelayBufferLength) + { + mWritePosition = 0; + delaybuffwrite = mDelayBuffer; + } + len -= len2; + } + } + else + { + unsigned int len; + float *out, *in; + int ch; + +#ifdef DELAY_USEFLOAT + float *delaybuffwrite; + float *delaybuffread[DELAY_MAX_CHANNELS]; +#else + short *delaybuffwrite; + short *delaybuffread[DELAY_MAX_CHANNELS]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite = mDelayBuffer + (mWritePosition * inchannels); + for (ch = 0; ch < inchannels; ch++) + { + delaybuffread[ch] = (mDelayBuffer+ch) + (mReadPosition[ch] * inchannels); + } + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition; + } + + for (count = 0; count < len2; count++) + { +#ifdef DELAY_USEFLOAT + for (ch = 0; ch < inchannels; ch++) + { + delaybuffwrite[ch] = in[ch]; + out[ch] = *delaybuffread[ch]; + } +#else + for (ch = 0; ch < inchannels; ch++) + { + delaybuffwrite[ch] = in[ch] > 1.0f ? 32767 : in[ch] < -1.0f ? -32768: (signed short)(in[ch] * 32767.0f); + out[ch] = (float)*delaybuffread[ch] * OO_32767; + } +#endif + in += inchannels; + out += inchannels; + delaybuffwrite += inchannels; + for (ch = 0; ch < inchannels; ch++) + { + if (++mReadPosition[ch] >= mDelayBufferLength) + { + mReadPosition[ch] = 0; + delaybuffread[ch] = (mDelayBuffer+ch); + } + else + { + delaybuffread[ch] += inchannels; + } + } + } + + mWritePosition += len2; + if (mWritePosition >= mDelayBufferLength) + { + mWritePosition = 0; + delaybuffwrite = mDelayBuffer; + } + len -= len2; + } + } +#else // !DELAY_INTERLEAVED + if (inchannels == 1 && (speakermask & 1)) + { + unsigned int len; + float *out, *in; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite; + float *delaybuffread; +#else + short *delaybuffwrite; + short *delaybuffread; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite = mDelayBuffer + mWritePosition[0]; + delaybuffread = mDelayBuffer + mReadPosition[0]; + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition[0] + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition[0]; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + +#ifdef DELAY_USEFLOAT + *(delaybuffwrite++) = in0; + out[0] = *delaybuffread; +#else + *(delaybuffwrite++) = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + out[0] = (float)*delaybuffread * OO_32767; +#endif + in++; + out++; + if (++mReadPosition[0] >= mBufferStart[1]) + { + mReadPosition[0] = 0; + delaybuffread = mDelayBuffer; + } + else + { + delaybuffread++; + } + } + + mWritePosition[0] += len2; + if (mWritePosition[0] >= mDelayBufferLength) + { + mWritePosition[0] = 0; + delaybuffwrite = mDelayBuffer; + } + len -= len2; + } + } + else if (inchannels == 2 && (speakermask & 0x3)==0x3) + { + unsigned int len; + float *out, *in; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite[2]; + float *delaybuffread[2]; +#else + short *delaybuffwrite[2]; + short *delaybuffread[2]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite[0] = mDelayBuffer + mWritePosition[0]; + delaybuffwrite[1] = mDelayBuffer + mWritePosition[1]; + delaybuffread[0] = mDelayBuffer + mReadPosition[0]; + delaybuffread[1] = mDelayBuffer + mReadPosition[1]; + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition[0] + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition[0]; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + +#ifdef DELAY_USEFLOAT + *(delaybuffwrite[0]++) = in0; + *(delaybuffwrite[1]++) = in1; + out[0] = *delaybuffread[0]; + out[1] = *delaybuffread[1]; +#else + *(delaybuffwrite[0]++) = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + *(delaybuffwrite[1]++) = in1 > 1.0f ? 32767 : in1 < -1.0f ? -32768: (signed short)(in1 * 32767.0f); + out[0] = (float)*delaybuffread[0] * OO_32767; + out[1] = (float)*delaybuffread[1] * OO_32767; +#endif + in += 2; + out += 2; + if (++mReadPosition[0] >= mBufferStart[1]) + { + mReadPosition[0] = 0; + delaybuffread[0] = mDelayBuffer; + } + else + { + delaybuffread[0]++; + } + if (++mReadPosition[1] >= mBufferStart[2]) + { + mReadPosition[1] = mBufferStart[1]; + delaybuffread[1] = mDelayBuffer + mBufferStart[1]; + } + else + { + delaybuffread[1]++; + } + } + + if (mWritePosition[0] + (int)len2 >= mDelayBufferLength) + { + mWritePosition[0] = 0; + mWritePosition[1] = mBufferStart[1]; + delaybuffwrite[0] = mDelayBuffer; + delaybuffwrite[1] = mDelayBuffer + mBufferStart[1]; + } + else + { + mWritePosition[0] += len2; + mWritePosition[1] += len2; + } + len -= len2; + } + } + else if (inchannels == 6 && (speakermask & 0x3F)==0x3F) + { + unsigned int len; + float *out, *in; + int ch; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite[6]; + float *delaybuffread[6]; +#else + short *delaybuffwrite[6]; + short *delaybuffread[6]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite[0] = mDelayBuffer + mWritePosition[0]; + delaybuffwrite[1] = mDelayBuffer + mWritePosition[1]; + delaybuffwrite[2] = mDelayBuffer + mWritePosition[2]; + delaybuffwrite[3] = mDelayBuffer + mWritePosition[3]; + delaybuffwrite[4] = mDelayBuffer + mWritePosition[4]; + delaybuffwrite[5] = mDelayBuffer + mWritePosition[5]; + delaybuffread[0] = mDelayBuffer + mReadPosition[0]; + delaybuffread[1] = mDelayBuffer + mReadPosition[1]; + delaybuffread[2] = mDelayBuffer + mReadPosition[2]; + delaybuffread[3] = mDelayBuffer + mReadPosition[3]; + delaybuffread[4] = mDelayBuffer + mReadPosition[4]; + delaybuffread[5] = mDelayBuffer + mReadPosition[5]; + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition[0] + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition[0]; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + float in2 = in[2]; + float in3 = in[3]; + float in4 = in[4]; + float in5 = in[5]; + +#ifdef DELAY_USEFLOAT + *(delaybuffwrite[0]++) = in0; + *(delaybuffwrite[1]++) = in1; + *(delaybuffwrite[2]++) = in2; + *(delaybuffwrite[3]++) = in3; + *(delaybuffwrite[4]++) = in4; + *(delaybuffwrite[5]++) = in5; + out[0] = *delaybuffread[0]; + out[1] = *delaybuffread[1]; + out[2] = *delaybuffread[2]; + out[3] = *delaybuffread[3]; + out[4] = *delaybuffread[4]; + out[5] = *delaybuffread[5]; +#else + *(delaybuffwrite[0]++) = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + *(delaybuffwrite[1]++) = in1 > 1.0f ? 32767 : in1 < -1.0f ? -32768: (signed short)(in1 * 32767.0f); + *(delaybuffwrite[2]++) = in2 > 1.0f ? 32767 : in2 < -1.0f ? -32768: (signed short)(in2 * 32767.0f); + *(delaybuffwrite[3]++) = in3 > 1.0f ? 32767 : in3 < -1.0f ? -32768: (signed short)(in3 * 32767.0f); + *(delaybuffwrite[4]++) = in4 > 1.0f ? 32767 : in4 < -1.0f ? -32768: (signed short)(in4 * 32767.0f); + *(delaybuffwrite[5]++) = in5 > 1.0f ? 32767 : in5 < -1.0f ? -32768: (signed short)(in5 * 32767.0f); + out[0] = (float)*delaybuffread[0] * OO_32767; + out[1] = (float)*delaybuffread[1] * OO_32767; + out[2] = (float)*delaybuffread[2] * OO_32767; + out[3] = (float)*delaybuffread[3] * OO_32767; + out[4] = (float)*delaybuffread[4] * OO_32767; + out[5] = (float)*delaybuffread[5] * OO_32767; +#endif + in += 6; + out += 6; + for (ch = 0; ch < 6; ch++) + { + if (++mReadPosition[ch] >= mBufferStart[ch+1]) + { + mReadPosition[ch] = mBufferStart[ch]; + delaybuffread[ch] = mDelayBuffer + mBufferStart[ch]; + } + else + { + delaybuffread[ch]++; + } + } + } + + if (mWritePosition[0] + (int)len2 >= mDelayBufferLength) + { + for (ch = 0; ch < 6; ch++) + { + mWritePosition[ch] = mBufferStart[ch]; + delaybuffwrite[ch] = mDelayBuffer + mBufferStart[ch]; + } + } + else + { + for (ch = 0; ch < 6; ch++) + { + mWritePosition[ch] += len2; + } + } + len -= len2; + } + } + else if (inchannels == 8 && (speakermask & 0xFF)==0xFF) + { + unsigned int len; + float *out, *in; + int ch; +#ifdef DELAY_USEFLOAT + float *delaybuffwrite[8]; + float *delaybuffread[8]; +#else + short *delaybuffwrite[8]; + short *delaybuffread[8]; +#endif + in = inbuffer; + out = outbuffer; + + delaybuffwrite[0] = mDelayBuffer + mWritePosition[0]; + delaybuffwrite[1] = mDelayBuffer + mWritePosition[1]; + delaybuffwrite[2] = mDelayBuffer + mWritePosition[2]; + delaybuffwrite[3] = mDelayBuffer + mWritePosition[3]; + delaybuffwrite[4] = mDelayBuffer + mWritePosition[4]; + delaybuffwrite[5] = mDelayBuffer + mWritePosition[5]; + delaybuffwrite[6] = mDelayBuffer + mWritePosition[6]; + delaybuffwrite[7] = mDelayBuffer + mWritePosition[7]; + delaybuffread[0] = mDelayBuffer + mReadPosition[0]; + delaybuffread[1] = mDelayBuffer + mReadPosition[1]; + delaybuffread[2] = mDelayBuffer + mReadPosition[2]; + delaybuffread[3] = mDelayBuffer + mReadPosition[3]; + delaybuffread[4] = mDelayBuffer + mReadPosition[4]; + delaybuffread[5] = mDelayBuffer + mReadPosition[5]; + delaybuffread[6] = mDelayBuffer + mReadPosition[6]; + delaybuffread[7] = mDelayBuffer + mReadPosition[7]; + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition[0] + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition[0]; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + float in2 = in[2]; + float in3 = in[3]; + float in4 = in[4]; + float in5 = in[5]; + float in6 = in[6]; + float in7 = in[7]; + +#ifdef DELAY_USEFLOAT + *(delaybuffwrite[0]++) = in0; + *(delaybuffwrite[1]++) = in1; + *(delaybuffwrite[2]++) = in2; + *(delaybuffwrite[3]++) = in3; + *(delaybuffwrite[4]++) = in4; + *(delaybuffwrite[5]++) = in5; + *(delaybuffwrite[6]++) = in6; + *(delaybuffwrite[7]++) = in7; + out[0] = *delaybuffread[0]; + out[1] = *delaybuffread[1]; + out[2] = *delaybuffread[2]; + out[3] = *delaybuffread[3]; + out[4] = *delaybuffread[4]; + out[5] = *delaybuffread[5]; + out[6] = *delaybuffread[6]; + out[7] = *delaybuffread[7]; +#else + *(delaybuffwrite[0]++) = in0 > 1.0f ? 32767 : in0 < -1.0f ? -32768: (signed short)(in0 * 32767.0f); + *(delaybuffwrite[1]++) = in1 > 1.0f ? 32767 : in1 < -1.0f ? -32768: (signed short)(in1 * 32767.0f); + *(delaybuffwrite[2]++) = in2 > 1.0f ? 32767 : in2 < -1.0f ? -32768: (signed short)(in2 * 32767.0f); + *(delaybuffwrite[3]++) = in3 > 1.0f ? 32767 : in3 < -1.0f ? -32768: (signed short)(in3 * 32767.0f); + *(delaybuffwrite[4]++) = in4 > 1.0f ? 32767 : in4 < -1.0f ? -32768: (signed short)(in4 * 32767.0f); + *(delaybuffwrite[5]++) = in5 > 1.0f ? 32767 : in5 < -1.0f ? -32768: (signed short)(in5 * 32767.0f); + *(delaybuffwrite[6]++) = in6 > 1.0f ? 32767 : in6 < -1.0f ? -32768: (signed short)(in6 * 32767.0f); + *(delaybuffwrite[7]++) = in7 > 1.0f ? 32767 : in7 < -1.0f ? -32768: (signed short)(in7 * 32767.0f); + out[0] = (float)*delaybuffread[0] * OO_32767; + out[1] = (float)*delaybuffread[1] * OO_32767; + out[2] = (float)*delaybuffread[2] * OO_32767; + out[3] = (float)*delaybuffread[3] * OO_32767; + out[4] = (float)*delaybuffread[4] * OO_32767; + out[5] = (float)*delaybuffread[5] * OO_32767; + out[6] = (float)*delaybuffread[6] * OO_32767; + out[7] = (float)*delaybuffread[7] * OO_32767; +#endif + in += 8; + out += 8; + for (ch = 0; ch < 8; ch++) + { + if (++mReadPosition[ch] >= mBufferStart[ch+1]) + { + mReadPosition[ch] = mBufferStart[ch]; + delaybuffread[ch] = mDelayBuffer + mBufferStart[ch]; + } + else + { + delaybuffread[ch]++; + } + } + } + + if (mWritePosition[0] + (int)len2 >= mDelayBufferLength) + { + for (ch = 0; ch < 8; ch++) + { + mWritePosition[ch] = mBufferStart[ch]; + delaybuffwrite[ch] = mDelayBuffer + mBufferStart[ch]; + } + } + else + { + for (ch = 0; ch < 8; ch++) + { + mWritePosition[ch] += len2; + } + } + len -= len2; + } + } + else + { + unsigned int len; + float *out, *in; + int ch; + +#ifdef DELAY_USEFLOAT + float *delaybuffwrite[DELAY_MAX_CHANNELS]; + float *delaybuffread[DELAY_MAX_CHANNELS]; +#else + short *delaybuffwrite[DELAY_MAX_CHANNELS]; + short *delaybuffread[DELAY_MAX_CHANNELS]; +#endif + in = inbuffer; + out = outbuffer; + + for (ch = 0; ch < inchannels; ch++) + { + delaybuffwrite[ch] = mDelayBuffer + mWritePosition[ch]; + delaybuffread[ch] = mDelayBuffer + mReadPosition[ch]; + } + + len = length; + while (len) + { + unsigned int len2 = len; + + if (mWritePosition[0] + (int)len > mDelayBufferLength) + { + len2 = mDelayBufferLength - mWritePosition[0]; + } + + for (count = 0; count < len2; count++) + { +#ifdef DELAY_USEFLOAT + for (ch = 0; ch < inchannels; ch++) + { + *(delaybuffwrite[ch]++) = in[ch]; + out[ch] = *delaybuffread[ch]; + } +#else + for (ch = 0; ch < inchannels; ch++) + { + *(delaybuffwrite[ch]++) = in[ch] > 1.0f ? 32767 : in[ch] < -1.0f ? -32768: (signed short)(in[ch] * 32767.0f); + out[ch] = (float)*delaybuffread[ch] * OO_32767; + } +#endif + in += inchannels; + out += inchannels; + for (ch = 0; ch < inchannels; ch++) + { + if (++mReadPosition[ch] >= mBufferStart[ch+1]) + { + mReadPosition[ch] = mBufferStart[ch]; + delaybuffread[ch] = mDelayBuffer + mBufferStart[ch]; + } + else + { + delaybuffread[ch]++; + } + } + } + + if (mWritePosition[0] + (int)len2 >= mDelayBufferLength) + { + for (ch = 0; ch < inchannels; ch++) + { + mWritePosition[ch] = mBufferStart[ch]; + delaybuffwrite[ch] = mDelayBuffer + mBufferStart[ch]; + } + } + else + { + for (ch = 0; ch < inchannels; ch++) + { + mWritePosition[ch] += len2; + } + } + len -= len2; + } + } +#endif // DELAY_INTERLEAVED + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDelay::setParameterInternal(int index, float value) +{ + switch (index) + { + case FMOD_DSP_DELAY_MAXDELAY: + { + mMaxDelayUpdate = value; + break; + } + default: + { + if ((index >= FMOD_DSP_DELAY_CH0) && (index <= FMOD_DSP_DELAY_CH15)) + { + mDelayUpdate[index-FMOD_DSP_DELAY_CH0] = value; + } + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDelay::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_DELAY_MAXDELAY: + { + *value = mMaxDelayUpdate; + sprintf(valuestr, "%.02f", mMaxDelayUpdate); + break; + } + default: + { + if ((index >= FMOD_DSP_DELAY_CH0) && (index <= FMOD_DSP_DELAY_CH15)) + { + *value = mDelayUpdate[index-FMOD_DSP_DELAY_CH0]; + sprintf(valuestr, "%.02f", mDelayUpdate[index-FMOD_DSP_DELAY_CH0]); + } + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPDelay::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + if (mDelayBufferMemory) + { + tracker->add(false, FMOD_MEMBITS_DSP, mDelayBufferLengthBytes + 16); + } + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDelay::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPDelay *delay = (DSPDelay *)dsp; + + return delay->DSPDelay::getMemoryUsed(tracker); +} +#endif + + +} + +#endif diff --git a/src/fmod_dsp_delay.h b/src/fmod_dsp_delay.h new file mode 100755 index 0000000..bba1c77 --- /dev/null +++ b/src/fmod_dsp_delay.h @@ -0,0 +1,87 @@ +#ifndef _FMOD_DSP_DELAY_H +#define _FMOD_DSP_DELAY_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DELAY + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +#if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) +#define DELAY_INTERLEAVED +#define DELAY_USEFLOAT +#endif +#define DELAY_MAX_CHANNELS 16 + +namespace FMOD +{ + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + class DSPDelay : public DSPI + #else + class DSPDelay : public DSPFilter + #endif + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float FMOD_PPCALIGN16(mMaxDelay); + float FMOD_PPCALIGN16(mMaxDelayUpdate); + float FMOD_PPCALIGN16(mDelay[DELAY_MAX_CHANNELS]); + float FMOD_PPCALIGN16(mDelayUpdate[DELAY_MAX_CHANNELS]); + int FMOD_PPCALIGN16(mOffset[DELAY_MAX_CHANNELS]); + +#ifdef DELAY_USEFLOAT + float *FMOD_PPCALIGN16(mDelayBuffer); + float *FMOD_PPCALIGN16(mDelayBufferMemory); +#else + signed short *FMOD_PPCALIGN16(mDelayBuffer); + signed short *FMOD_PPCALIGN16(mDelayBufferMemory); +#endif + unsigned int FMOD_PPCALIGN16(mDelayBufferLengthBytes); + int FMOD_PPCALIGN16(mDelayBufferLength); +#ifdef DELAY_INTERLEAVED + int FMOD_PPCALIGN16(mWritePosition); +#else + int FMOD_PPCALIGN16(mWritePosition[DELAY_MAX_CHANNELS]); + int FMOD_PPCALIGN16(mBufferStart[DELAY_MAX_CHANNELS+1]); +#endif + int FMOD_PPCALIGN16(mReadPosition[DELAY_MAX_CHANNELS]); + int FMOD_PPCALIGN16(mOutputRate); + int FMOD_PPCALIGN16(mChannels); + +#ifdef PLATFORM_PS3 + short FMOD_PPCALIGN16(mTempWriteMem[DELAY_MAX_CHANNELS][256]); // 8192 bytes + short FMOD_PPCALIGN16(mTempReadMem [DELAY_MAX_CHANNELS][256]); // 8192 bytes +#endif + + unsigned short mOldSpeakerMask; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_distortion.h b/src/fmod_dsp_distortion.h new file mode 100755 index 0000000..63d74a1 --- /dev/null +++ b/src/fmod_dsp_distortion.h @@ -0,0 +1,48 @@ +#ifndef _FMOD_DSP_DISTORTION_H +#define _FMOD_DSP_DISTORTION_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DISTORTION + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPDistortion : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float mLevel; + bool mSupportsSIMD; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_echo.cpp b/src/fmod_dsp_echo.cpp new file mode 100755 index 0000000..40cb7df --- /dev/null +++ b/src/fmod_dsp_echo.cpp @@ -0,0 +1,1062 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ECHO + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_echo.h" +#include "fmod_systemi.h" + +#include <stdio.h> + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspecho_desc; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPEcho::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +FMOD_DSP_PARAMETERDESC dspecho_param[5] = +{ + { 1.0f, 5000.0f, 500.0f, "Delay", "ms", "Echo delay in ms. 10 to 5000. Default = 500." }, + { 0.0f, 1.0f, 0.5f, "Decay", "%", "Echo decay per delay. 0 to 1. 1.0 = No decay, 0.0 = total decay. Default = 0.5." }, + { 0.0f, 16.0f, 0.0f, "Max channels", "channels", "Maximum channels supported. 0 to 16. 0 = same as fmod's default output polyphony, 1 = mono, 2 = stereo etc. Default = 0. It is suggested to leave at 0!" }, + { 0.0f, 1.0f, 1.0f, "Drymix", "%", "Volume of original signal to pass to output. 0.0 to 1.0. Default = 1.0." }, + { 0.0f, 1.0f, 1.0f, "Wetmix", "%", "Volume of echo delay signal to pass to output. 0.0 to 1.0. Default = 1.0." }, +}; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPEcho::getDescriptionEx() +{ + FMOD_memset(&dspecho_desc, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspecho_desc.name, "FMOD Echo"); + dspecho_desc.version = 0x00010100; + dspecho_desc.create = DSPEcho::createCallback; + dspecho_desc.release = DSPEcho::releaseCallback; + dspecho_desc.reset = DSPEcho::resetCallback; + dspecho_desc.read = DSPEcho::readCallback; + + dspecho_desc.numparameters = sizeof(dspecho_param) / sizeof(dspecho_param[0]); + dspecho_desc.paramdesc = dspecho_param; + dspecho_desc.setparameter = DSPEcho::setParameterCallback; + dspecho_desc.getparameter = DSPEcho::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspecho_desc.getmemoryused = &DSPEcho::getMemoryUsedCallback; +#endif + + dspecho_desc.mType = FMOD_DSP_TYPE_ECHO; + dspecho_desc.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspecho_desc.mSize = sizeof(DSPEcho); + + return &dspecho_desc; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPEcho::createInternal() +{ + int count; + + init(); + + mChannels = 0; + mOldSpeakerMask = 0xFFFF; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mChannels = mChannelsUpdate; + mDelay = mDelayUpdate; + mDecayRatio = mDecayRatioUpdate; + mDryMix = mDryMixUpdate; + mWetMix = mWetMixUpdate; + { + int outputrate = 0; + + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + + mEchoLength = (int)((float)outputrate * mDelay) / 1000; + + if (mEchoBufferMemory) + { + FMOD_Memory_Free(mEchoBufferMemory); + mEchoBufferMemory = mEchoBuffer = 0; + } + + mEchoBufferLengthBytes = mEchoLength; + mEchoBufferLengthBytes *= mChannels; + + if (!mEchoBufferMemory) + { + #ifdef ECHO_USEFLOAT + mEchoBufferLengthBytes *= sizeof(float); + + mEchoBufferMemory = (float *)FMOD_Memory_Calloc(mEchoBufferLengthBytes + 16); + mEchoBuffer = (float *)FMOD_ALIGNPOINTER(mEchoBufferMemory, 16); + #else + mEchoBufferLengthBytes *= sizeof(signed short); + + mEchoBufferMemory = (signed short *)FMOD_Memory_Calloc(mEchoBufferLengthBytes + 16); + mEchoBuffer = (signed short *)FMOD_ALIGNPOINTER(mEchoBufferMemory, 16); + #endif + } + if (!mEchoBufferMemory) + { + return FMOD_ERR_MEMORY; + } + + resetInternal(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPEcho::releaseInternal() +{ + if (mEchoBufferMemory) + { + FMOD_Memory_Free(mEchoBufferMemory); + mEchoBufferMemory = mEchoBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPEcho::resetInternal() +{ + mEchoPosition = 0; + + if (mEchoBuffer) + { + FMOD_memset(mEchoBuffer, 0, mEchoBufferLengthBytes); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPEcho::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + bool reallocbuffer = false; + + #define OO_32767 (1.0f / 32767.0f) + + if (!inbuffer) + { + return FMOD_OK; + } + + /* + Update parameters + */ + if (mChannels != mChannelsUpdate) + { + mChannels = mChannelsUpdate; + + reallocbuffer = true; + } + if (mDelay != mDelayUpdate) + { + mDelay = mDelayUpdate; + + reallocbuffer = true; + } + if (mDecayRatio != mDecayRatioUpdate) + { + mDecayRatio = mDecayRatioUpdate; + } + if (mDryMix != mDryMixUpdate) + { + mDryMix = mDryMixUpdate; + } + if (mWetMix != mWetMixUpdate) + { + mWetMix = mWetMixUpdate; + } + + if (reallocbuffer || !mEchoBuffer) + { + int outputrate = 0; + + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + + mEchoLength = (int)((float)outputrate * mDelay) / 1000; + + if (mEchoBufferMemory) + { + FMOD_Memory_Free(mEchoBufferMemory); + mEchoBufferMemory = mEchoBuffer = 0; + } + + mEchoBufferLengthBytes = mEchoLength; + mEchoBufferLengthBytes *= mChannels; + + if (!mEchoBufferMemory) + { + #ifdef ECHO_USEFLOAT + mEchoBufferLengthBytes *= sizeof(float); + + mEchoBufferMemory = (float *)FMOD_Memory_Calloc(mEchoBufferLengthBytes + 16); + mEchoBuffer = (float *)FMOD_ALIGNPOINTER(mEchoBufferMemory, 16); + #else + mEchoBufferLengthBytes *= sizeof(signed short); + + mEchoBufferMemory = (signed short *)FMOD_Memory_Calloc(mEchoBufferLengthBytes + 16); + mEchoBuffer = (signed short *)FMOD_ALIGNPOINTER(mEchoBufferMemory, 16); + #endif + } + if (!mEchoBufferMemory) + { + return FMOD_ERR_MEMORY; + } + + resetInternal(); + } + + + if (speakermask != mOldSpeakerMask) /* A speaker has been disabled: clear the echo buffer for that speaker to prevent artifacts if/when the speaker is reenabled */ + { + int count2; + unsigned short diff = (mOldSpeakerMask ^ speakermask); + + for (count = 0; count < (unsigned int)inchannels; count++) + { + if (diff & (1 << count)) + { + int len = mEchoLength * inchannels; + for(count2 = count; count2 < len; count2 += inchannels) + { + mEchoBuffer[count2] = 0; + } + } + } + mOldSpeakerMask = speakermask; + } + + if ((inchannels > mChannels) || !mEchoBuffer || !(speakermask & ((1 << inchannels)-1)) ) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_OK; + } + + if (inchannels == 1 && (speakermask & 1)) + { + unsigned int len; + float *out, *in; + + in = inbuffer; + out = outbuffer; + + len = length; + while (len) + { + unsigned int len2 = len; +#ifdef ECHO_USEFLOAT + float *echobuff; +#else + short *echobuff; +#endif + + echobuff = mEchoBuffer + (mEchoPosition * 1); + + if (mEchoPosition + len > mEchoLength) + { + len2 = mEchoLength - mEchoPosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + +#ifdef ECHO_USEFLOAT + out[0] = (in0 * mDryMix) + (echobuff[0] * mWetMix); + echobuff[0] = (in0 ) + (echobuff[0] * mDecayRatio); +#else + float e0 = (float)echobuff[0] * OO_32767; + + out[0] = (in0 * mDryMix) + (e0 * mWetMix); + + e0 = in0 + (e0 * mDecayRatio); + echobuff[0] = e0 > 1.0f ? 32767 : e0 < -1.0f ? -32768: (signed short)(e0 * 32767.0f); +#endif + in++; + out++; + echobuff++; + } + + mEchoPosition += len2; + if (mEchoPosition >= mEchoLength) + { + mEchoPosition = 0; + } + len -= len2; + } + } + else if (inchannels == 2 && (speakermask & 0x3)==0x3) + { + unsigned int len; + float *out, *in; + + in = inbuffer; + out = outbuffer; + + len = length; + while (len) + { + unsigned int len2 = len; +#ifdef ECHO_USEFLOAT + float *echobuff; +#else + short *echobuff; +#endif + echobuff = mEchoBuffer + (mEchoPosition * 2); + + if (mEchoPosition + len > mEchoLength) + { + len2 = mEchoLength - mEchoPosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + +#ifdef ECHO_USEFLOAT + out[0] = (in0 * mDryMix) + (echobuff[0] * mWetMix); + out[1] = (in1 * mDryMix) + (echobuff[1] * mWetMix); + echobuff[0] = in0 + (echobuff[0] * mDecayRatio); + echobuff[1] = in1 + (echobuff[1] * mDecayRatio); +#else + float e0 = (float)echobuff[0] * OO_32767; + float e1 = (float)echobuff[1] * OO_32767; + + out[0] = (in0 * mDryMix) + (e0 * mWetMix); + out[1] = (in1 * mDryMix) + (e1 * mWetMix); + e0 = in0 + (e0 * mDecayRatio); + e1 = in1 + (e1 * mDecayRatio); + echobuff[0] = e0 > 1.0f ? 32767 : e0 < -1.0f ? -32768: (signed short)(e0 * 32767.0f);; + echobuff[1] = e1 > 1.0f ? 32767 : e1 < -1.0f ? -32768: (signed short)(e1 * 32767.0f);; +#endif + + in+=2; + out+=2; + echobuff+=2; + } + + mEchoPosition += len2; + if (mEchoPosition >= mEchoLength) + { + mEchoPosition = 0; + } + len -= len2; + } + } + else if (inchannels == 6 && (speakermask & 0x3F)==0x3F) + { + unsigned int len; + float *out, *in; + + in = inbuffer; + out = outbuffer; + + len = length; + while (len) + { + unsigned int len2 = len; +#ifdef ECHO_USEFLOAT + float *echobuff; +#else + short *echobuff; +#endif + echobuff = mEchoBuffer + (mEchoPosition * 6); + + if (mEchoPosition + len > mEchoLength) + { + len2 = mEchoLength - mEchoPosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + float in2 = in[2]; + float in3 = in[3]; + float in4 = in[4]; + float in5 = in[5]; + +#ifdef ECHO_USEFLOAT + out[0] = (in0 * mDryMix) + (echobuff[0] * mWetMix); + out[1] = (in1 * mDryMix) + (echobuff[1] * mWetMix); + out[2] = (in2 * mDryMix) + (echobuff[2] * mWetMix); + out[3] = (in3 * mDryMix) + (echobuff[3] * mWetMix); + out[4] = (in4 * mDryMix) + (echobuff[4] * mWetMix); + out[5] = (in5 * mDryMix) + (echobuff[5] * mWetMix); + echobuff[0] = in0 + (echobuff[0] * mDecayRatio); + echobuff[1] = in1 + (echobuff[1] * mDecayRatio); + echobuff[2] = in2 + (echobuff[2] * mDecayRatio); + echobuff[3] = in3 + (echobuff[3] * mDecayRatio); + echobuff[4] = in4 + (echobuff[4] * mDecayRatio); + echobuff[5] = in5 + (echobuff[5] * mDecayRatio); +#else + float e0 = (float)echobuff[0] * OO_32767; + float e1 = (float)echobuff[1] * OO_32767; + float e2 = (float)echobuff[2] * OO_32767; + float e3 = (float)echobuff[3] * OO_32767; + float e4 = (float)echobuff[4] * OO_32767; + float e5 = (float)echobuff[5] * OO_32767; + + out[0] = (in0 * mDryMix) + (e0 * mWetMix); + out[1] = (in1 * mDryMix) + (e1 * mWetMix); + out[2] = (in2 * mDryMix) + (e2 * mWetMix); + out[3] = (in3 * mDryMix) + (e3 * mWetMix); + out[4] = (in4 * mDryMix) + (e4 * mWetMix); + out[5] = (in5 * mDryMix) + (e5 * mWetMix); + + e0 = in0 + (e0 * mDecayRatio); + e1 = in1 + (e1 * mDecayRatio); + e2 = in2 + (e2 * mDecayRatio); + e3 = in3 + (e3 * mDecayRatio); + e4 = in4 + (e4 * mDecayRatio); + e5 = in5 + (e5 * mDecayRatio); + echobuff[0] = e0 > 1.0f ? 32767 : e0 < -1.0f ? -32768: (signed short)(e0 * 32767.0f); + echobuff[1] = e1 > 1.0f ? 32767 : e1 < -1.0f ? -32768: (signed short)(e1 * 32767.0f); + echobuff[2] = e2 > 1.0f ? 32767 : e2 < -1.0f ? -32768: (signed short)(e2 * 32767.0f); + echobuff[3] = e3 > 1.0f ? 32767 : e3 < -1.0f ? -32768: (signed short)(e3 * 32767.0f); + echobuff[4] = e4 > 1.0f ? 32767 : e4 < -1.0f ? -32768: (signed short)(e4 * 32767.0f); + echobuff[5] = e5 > 1.0f ? 32767 : e5 < -1.0f ? -32768: (signed short)(e5 * 32767.0f); +#endif + + in+=6; + out+=6; + echobuff+=6; + } + + mEchoPosition += len2; + if (mEchoPosition >= mEchoLength) + { + mEchoPosition = 0; + } + len -= len2; + } + } + else if (inchannels == 8 && (speakermask & 0xFF)==0xFF) + { + unsigned int len; + float *out, *in; + + in = inbuffer; + out = outbuffer; + + len = length; + while (len) + { + unsigned int len2 = len; +#ifdef ECHO_USEFLOAT + float *echobuff; +#else + short *echobuff; +#endif + echobuff = mEchoBuffer + (mEchoPosition * 8); + + if (mEchoPosition + len > mEchoLength) + { + len2 = mEchoLength - mEchoPosition; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + float in1 = in[1]; + float in2 = in[2]; + float in3 = in[3]; + float in4 = in[4]; + float in5 = in[5]; + float in6 = in[6]; + float in7 = in[7]; + +#ifdef ECHO_USEFLOAT + out[0] = (in0 * mDryMix) + (echobuff[0] * mWetMix); + out[1] = (in1 * mDryMix) + (echobuff[1] * mWetMix); + out[2] = (in2 * mDryMix) + (echobuff[2] * mWetMix); + out[3] = (in3 * mDryMix) + (echobuff[3] * mWetMix); + out[4] = (in4 * mDryMix) + (echobuff[4] * mWetMix); + out[5] = (in5 * mDryMix) + (echobuff[5] * mWetMix); + out[6] = (in6 * mDryMix) + (echobuff[6] * mWetMix); + out[7] = (in7 * mDryMix) + (echobuff[7] * mWetMix); + + echobuff[0] = in0 + (echobuff[0] * mDecayRatio); + echobuff[1] = in1 + (echobuff[1] * mDecayRatio); + echobuff[2] = in2 + (echobuff[2] * mDecayRatio); + echobuff[3] = in3 + (echobuff[3] * mDecayRatio); + echobuff[4] = in4 + (echobuff[4] * mDecayRatio); + echobuff[5] = in5 + (echobuff[5] * mDecayRatio); + echobuff[6] = in6 + (echobuff[6] * mDecayRatio); + echobuff[7] = in7 + (echobuff[7] * mDecayRatio); +#else + float e0 = (float)echobuff[0] * OO_32767; + float e1 = (float)echobuff[1] * OO_32767; + float e2 = (float)echobuff[2] * OO_32767; + float e3 = (float)echobuff[3] * OO_32767; + float e4 = (float)echobuff[4] * OO_32767; + float e5 = (float)echobuff[5] * OO_32767; + float e6 = (float)echobuff[6] * OO_32767; + float e7 = (float)echobuff[7] * OO_32767; + + out[0] = (in0 * mDryMix) + (e0 * mWetMix); + out[1] = (in1 * mDryMix) + (e1 * mWetMix); + out[2] = (in2 * mDryMix) + (e2 * mWetMix); + out[3] = (in3 * mDryMix) + (e3 * mWetMix); + out[4] = (in4 * mDryMix) + (e4 * mWetMix); + out[5] = (in5 * mDryMix) + (e5 * mWetMix); + out[6] = (in6 * mDryMix) + (e6 * mWetMix); + out[7] = (in7 * mDryMix) + (e7 * mWetMix); + + e0 = in0 + (e0 * mDecayRatio); + e1 = in1 + (e1 * mDecayRatio); + e2 = in2 + (e2 * mDecayRatio); + e3 = in3 + (e3 * mDecayRatio); + e4 = in4 + (e4 * mDecayRatio); + e5 = in5 + (e5 * mDecayRatio); + e6 = in6 + (e6 * mDecayRatio); + e7 = in7 + (e7 * mDecayRatio); + + echobuff[0] = e0 > 1.0f ? 32767 : e0 < -1.0f ? -32768: (signed short)(e0 * 32767.0f); + echobuff[1] = e1 > 1.0f ? 32767 : e1 < -1.0f ? -32768: (signed short)(e1 * 32767.0f); + echobuff[2] = e2 > 1.0f ? 32767 : e2 < -1.0f ? -32768: (signed short)(e2 * 32767.0f); + echobuff[3] = e3 > 1.0f ? 32767 : e3 < -1.0f ? -32768: (signed short)(e3 * 32767.0f); + echobuff[4] = e4 > 1.0f ? 32767 : e4 < -1.0f ? -32768: (signed short)(e4 * 32767.0f); + echobuff[5] = e5 > 1.0f ? 32767 : e5 < -1.0f ? -32768: (signed short)(e5 * 32767.0f); + echobuff[6] = e6 > 1.0f ? 32767 : e6 < -1.0f ? -32768: (signed short)(e6 * 32767.0f); + echobuff[7] = e7 > 1.0f ? 32767 : e7 < -1.0f ? -32768: (signed short)(e7 * 32767.0f); +#endif + in+=8; + out+=8; + echobuff+=8; + } + + mEchoPosition += len2; + if (mEchoPosition >= mEchoLength) + { + mEchoPosition = 0; + } + len -= len2; + } + } + else + { + unsigned int len; + float *out, *in; + + in = inbuffer; + out = outbuffer; + + len = length; + while (len) + { + int count2; + unsigned int len2 = len; +#ifdef ECHO_USEFLOAT + float *echobuff; +#else + short *echobuff; +#endif + + echobuff = mEchoBuffer + (mEchoPosition * inchannels); + + if (mEchoPosition + len > mEchoLength) /* if (position + echo length) exceed the size of the echo buffer.. */ + { + len2 = mEchoLength - mEchoPosition; + } + + for (count = 0; count < len2; count++) + { + for (count2 = 0; count2 < inchannels; count2++) + { + if (!((1 << count2) & speakermask)) + { + out[count2] = in[count2]; + } + else + { + float in0 = in[count2]; + +#ifdef ECHO_USEFLOAT + out[count2] = (in0 * mDryMix) + (echobuff[count2] * mWetMix); + echobuff[count2] = (in0 ) + (echobuff[count2] * mDecayRatio); +#else + float e0 = (float)echobuff[count2] * OO_32767; + + out[count2] = (in0 * mDryMix) + (e0 * mWetMix); + e0 = in0 + (e0 * mDecayRatio); + echobuff[count2] = e0 > 1.0f ? 32767 : e0 < -1.0f ? -32768: (signed short)(e0 * 32767.0f); +#endif + } + } + + in+=inchannels; + out+=inchannels; + echobuff+=inchannels; + } + + mEchoPosition += len2; + if (mEchoPosition >= mEchoLength) + { + mEchoPosition = 0; + } + len -= len2; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPEcho::setParameterInternal(int index, float value) +{ + FMOD_RESULT result; + + result = mSystem->getSoftwareFormat(0, 0, &mChannelsUpdate, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + switch (index) + { + case FMOD_DSP_ECHO_DELAY: + { + mDelayUpdate = value; + break; + } + case FMOD_DSP_ECHO_DECAYRATIO: + { + mDecayRatioUpdate = value; + break; + } + case FMOD_DSP_ECHO_MAXCHANNELS: + { + mMaxChannels = (int)value; + if (mMaxChannels) + { + mChannelsUpdate = mMaxChannels; + } + break; + } + case FMOD_DSP_ECHO_DRYMIX: + { + mDryMixUpdate = value; + break; + } + case FMOD_DSP_ECHO_WETMIX: + { + mWetMixUpdate = value; + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPEcho::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_ECHO_DELAY: + { + *value = mDelayUpdate; + sprintf(valuestr, "%.02f", mDelayUpdate); + break; + } + case FMOD_DSP_ECHO_DECAYRATIO: + { + *value = mDecayRatioUpdate; + sprintf(valuestr, "%.1f", mDecayRatioUpdate * 100.0f); + break; + } + case FMOD_DSP_ECHO_MAXCHANNELS: + { + *value = (float)mMaxChannels; + sprintf(valuestr, "%d", mMaxChannels); + break; + } + case FMOD_DSP_ECHO_DRYMIX: + { + *value = mDryMixUpdate; + sprintf(valuestr, "%.1f", mDryMixUpdate * 100.0f); + break; + } + case FMOD_DSP_ECHO_WETMIX: + { + *value = mWetMixUpdate; + sprintf(valuestr, "%.1f", mWetMixUpdate * 100.0f); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPEcho::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + if (mEchoBufferMemory) + { + tracker->add(false, FMOD_MEMBITS_DSP, mEchoBufferLengthBytes + 16); + } + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPEcho::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPEcho *echo = (DSPEcho *)dsp; + + return echo->DSPEcho::getMemoryUsed(tracker); +} +#endif + + +} + +#endif diff --git a/src/fmod_dsp_echo.h b/src/fmod_dsp_echo.h new file mode 100755 index 0000000..a05987e --- /dev/null +++ b/src/fmod_dsp_echo.h @@ -0,0 +1,76 @@ +#ifndef _FMOD_DSP_ECHO_H +#define _FMOD_DSP_ECHO_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ECHO + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + class DSPEcho : public DSPI + #else + class DSPEcho : public DSPFilter + #endif + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float FMOD_PPCALIGN16(mDelay); + float FMOD_PPCALIGN16(mDecayRatio); + float FMOD_PPCALIGN16(mDryMix); + float FMOD_PPCALIGN16(mWetMix); + int FMOD_PPCALIGN16(mMaxChannels); + + float FMOD_PPCALIGN16(mDelayUpdate); + float FMOD_PPCALIGN16(mDecayRatioUpdate); + float FMOD_PPCALIGN16(mDryMixUpdate); + float FMOD_PPCALIGN16(mWetMixUpdate); +#ifdef ECHO_USEFLOAT + float *FMOD_PPCALIGN16(mEchoBuffer); + float *FMOD_PPCALIGN16(mEchoBufferMemory); +#else + signed short *FMOD_PPCALIGN16(mEchoBuffer); + signed short *FMOD_PPCALIGN16(mEchoBufferMemory); +#endif + unsigned int FMOD_PPCALIGN16(mEchoBufferLengthBytes); + unsigned int FMOD_PPCALIGN16(mEchoPosition); + unsigned int FMOD_PPCALIGN16(mEchoLength); + unsigned int FMOD_PPCALIGN16(mMaxLength); + int FMOD_PPCALIGN16(mOutputRate); + int FMOD_PPCALIGN16(mChannels); + int FMOD_PPCALIGN16(mChannelsUpdate); + + unsigned short mOldSpeakerMask; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_fft.cpp b/src/fmod_dsp_fft.cpp new file mode 100755 index 0000000..f712856 --- /dev/null +++ b/src/fmod_dsp_fft.cpp @@ -0,0 +1,403 @@ +#include "fmod_settings.h" + +#include "fmod_dsp_fft.h" + +#ifdef FMOD_SUPPORT_GETSPECTRUM + +#include "fmod.h" +#include "fmod_3d.h" +#include "fmod_dsp_fft.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +DSPFFT::DSPFFT() +{ +#ifdef USECOSTAB + int count; + + for (count = 0; count < DSPFFT_COSTABSIZE; count++) + { + mCosTab[count] = (float)FMOD_COS(FMOD_PI_2 * (float)count / (float)DSPFFT_COSTABSIZE); + } +#endif +} + +#ifdef USECOSTAB + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPFFT::cosine(float x) +{ + int y; + + x *= DSPFFT_TABLERANGE; + y = (int)x; + if (y < 0) + { + y = -y; + } + + y &= DSPFFT_TABLEMASK; + switch (y >> DSPFFT_COSTABBITS) + { + case 0 : return mCosTab[y]; + case 1 : return -mCosTab[(DSPFFT_COSTABSIZE - 1) - (y - (DSPFFT_COSTABSIZE * 1))]; + case 2 : return -mCosTab[ (y - (DSPFFT_COSTABSIZE * 2))]; + case 3 : return mCosTab[(DSPFFT_COSTABSIZE - 1) - (y - (DSPFFT_COSTABSIZE * 3))]; + } + + return 0.0f; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPFFT::sine(float x) +{ + return cosine(x - 0.25f); +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const unsigned int DSPFFT::reverse(unsigned int val, int bits) +{ + unsigned int retn = 0; + + while (bits--) + { + retn <<= 1; + retn |= (val & 1); + val >>= 1; + } + + return (retn); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFFT::process(int bits) +{ + register int count, count2, count3; + unsigned i1; /* going to right shift this */ + int i2, i3, i4, y; + int fftlen = 1 << bits; + float a1, a2, b1, b2, z1, z2; + float oneoverN= 1.0f / fftlen; + + i1 = fftlen / 2; + i2 = 1; + + /* perform the butterfly's */ + + for (count = 0; count < bits; count++) + { + i3 = 0; + i4 = i1; + + for (count2 = 0; count2 < i2; count2++) + { + y = reverse(i3 / (int)i1, bits); + + z1 = cosine((float)y * oneoverN); + z2 = -sine((float)y * oneoverN); + + for (count3 = i3; count3 < i4; count3++) + { + a1 = mFFTBuffer[count3].re; + a2 = mFFTBuffer[count3].im; + + b1 = (z1 * mFFTBuffer[count3+i1].re) - (z2 * mFFTBuffer[count3+i1].im); + b2 = (z2 * mFFTBuffer[count3+i1].re) + (z1 * mFFTBuffer[count3+i1].im); + + mFFTBuffer[count3].re = a1 + b1; + mFFTBuffer[count3].im = a2 + b2; + + mFFTBuffer[count3+i1].re = a1 - b1; + mFFTBuffer[count3+i1].im = a2 - b2; + } + + i3 += (i1 << 1); + i4 += (i1 << 1); + } + + i1 >>= 1; + i2 <<= 1; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFFT::getSpectrum(float *pcmbuffer, unsigned int pcmposition, unsigned int pcmlength, float *spectrum, int length, int channel, int numchannels, FMOD_DSP_FFT_WINDOW windowtype) +{ + int count, bits, bitslength, nyquist; + + bitslength = length; + bits = 0; + while (bitslength > 1) + { + bitslength >>= 1; + bits++; + } + + /* + Convert the PCM data into complex data. + */ + switch (windowtype) + { + case FMOD_DSP_FFT_WINDOW_TRIANGLE: + { + for (count = 0; count < length; count++) + { + float window; + float percent = (float)count / (float)length; + + window = (percent * 2.0f) - 1.0f; /* -1.0 to 0.0 to 1.0 */ + window = FMOD_ABS(window); /* 1.0 to 0.0 to 1.0 */ + window = 1.0f - window; /* 0.0 to 1.0 to 1.0 */ + + mFFTBuffer[count].re = pcmbuffer[(pcmposition * numchannels) + channel] * window; + mFFTBuffer[count].re /= (float)length; + mFFTBuffer[count].im = 0.00000001f; /* Giving it a very small number besides 0 avoids domain exceptions = big cpu usage */ + + pcmposition++; + if (pcmposition >= pcmlength) + { + pcmposition = 0; + } + } + break; + } + case FMOD_DSP_FFT_WINDOW_HAMMING: + { + for (count = 0; count < length; count++) + { + float window; + float percent = (float)count / (float)length; + + window = 0.54f - (0.46f * cosine(percent) ); + + mFFTBuffer[count].re = pcmbuffer[(pcmposition * numchannels) + channel] * window; + mFFTBuffer[count].re /= (float)length; + mFFTBuffer[count].im = 0.00000001f; /* Giving it a very small number besides 0 avoids domain exceptions = big cpu usage */ + + pcmposition++; + if (pcmposition >= pcmlength) + { + pcmposition = 0; + } + } + break; + } + case FMOD_DSP_FFT_WINDOW_HANNING: + { + for (count = 0; count < length; count++) + { + float window; + float percent = (float)count / (float)length; + + window = 0.5f * (1.0f - cosine(percent) ); + + mFFTBuffer[count].re = pcmbuffer[(pcmposition * numchannels) + channel] * window; + mFFTBuffer[count].re /= (float)length; + mFFTBuffer[count].im = 0.00000001f; /* Giving it a very small number besides 0 avoids domain exceptions = big cpu usage */ + + pcmposition++; + if (pcmposition >= pcmlength) + { + pcmposition = 0; + } + } + break; + } + case FMOD_DSP_FFT_WINDOW_BLACKMAN: + { + for (count = 0; count < length; count++) + { + float window; + float percent = (float)count / (float)length; + + window = 0.42f - (0.5f * cosine(percent) ) + (0.08f * cosine(2.0f * percent) ); + + mFFTBuffer[count].re = pcmbuffer[(pcmposition * numchannels) + channel] * window; + mFFTBuffer[count].re /= (float)length; + mFFTBuffer[count].im = 0.00000001f; /* Giving it a very small number besides 0 avoids domain exceptions = big cpu usage */ + + pcmposition++; + if (pcmposition >= pcmlength) + { + pcmposition = 0; + } + } + break; + } + case FMOD_DSP_FFT_WINDOW_BLACKMANHARRIS: + { + float a0 = 0.35875f; + float a1 = 0.48829f; + float a2 = 0.14128f; + float a3 = 0.01168f; + + for (count = 0; count < length; count++) + { + float window; + float percent = (float)count / (float)length; + + window = a0 - (a1 * cosine(1.0f * percent) ) + + (a2 * cosine(2.0f * percent) ) - + (a3 * cosine(3.0f * percent) ); + + mFFTBuffer[count].re = pcmbuffer[(pcmposition * numchannels) + channel] * window; + mFFTBuffer[count].re /= (float)length; + mFFTBuffer[count].im = 0.00000001f; /* Giving it a very small number besides 0 avoids domain exceptions = big cpu usage */ + + pcmposition++; + if (pcmposition >= pcmlength) + { + pcmposition = 0; + } + } + break; + } + case FMOD_DSP_FFT_WINDOW_RECT: + default: + { + for (count = 0; count < length; count++) + { + mFFTBuffer[count].re = pcmbuffer[(pcmposition * numchannels) + channel]; + mFFTBuffer[count].re /= (float)length; + mFFTBuffer[count].im = 0.00000001f; /* Giving it a very small number besides 0 avoids domain exceptions = big cpu usage */ + + pcmposition++; + if (pcmposition >= pcmlength) + { + pcmposition = 0; + } + } + break; + } + }; + + /* + Do the FFT + */ + process(bits); + + /* + Now prepare the data into a readable format + */ + nyquist = length / 2; + for (count=0; count < nyquist-1; count++) + { + float magnitude; + int n = count; + + n = reverse(n, bits); + + #ifdef FMOD_NO_FPU + { + FMOD_VECTOR v; + + v.x = mFFTBuffer[n].re; + v.y = mFFTBuffer[n].im; + v.z = 0.0f; + + magnitude = FMOD_Vector_GetLengthFast(&v); + } + #else + magnitude = FMOD_SQRT((mFFTBuffer[n].re * mFFTBuffer[n].re) + (mFFTBuffer[n].im * mFFTBuffer[n].im)); + #endif + + // phaseangle = (float)atan2(v.x, v.y); + + magnitude *= 2.5f; /* This brings it roughly to the same db level as the input. Not sure why though */ + + if (magnitude > 1.0f) + { + magnitude = 1.0f; + } + + spectrum[count] = magnitude; + } + + return FMOD_OK; +} + +} + +#endif + diff --git a/src/fmod_dsp_fft.h b/src/fmod_dsp_fft.h new file mode 100755 index 0000000..5bd55b3 --- /dev/null +++ b/src/fmod_dsp_fft.h @@ -0,0 +1,48 @@ +#ifndef _FMOD_DSP_FFT +#define _FMOD_DSP_FFT + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +namespace FMOD +{ + const int DSPFFT_COSTABBITS = 13; + const int DSPFFT_COSTABSIZE = (1 << DSPFFT_COSTABBITS); + const int DSPFFT_TABLERANGE = (DSPFFT_COSTABSIZE * 4); + const int DSPFFT_TABLEMASK = (DSPFFT_TABLERANGE - 1); + + class DSPFFT + { + private: + + FMOD_COMPLEX mFFTBuffer[16 * 1024]; + +#define USECOSTAB + +#ifdef USECOSTAB + float mCosTab[DSPFFT_COSTABSIZE]; + + FMOD_INLINE const float cosine(float x); + FMOD_INLINE const float sine(float x); +#else + FMOD_INLINE const float cosine(float x) { return FMOD_COS(x * FMOD_PI * 2.0f); } + FMOD_INLINE const float sine(float x) { return FMOD_SIN(x * FMOD_PI * 2.0f); } +#endif + FMOD_INLINE const unsigned int reverse(unsigned int val, int bits); + + FMOD_RESULT process(int bits); + + public: + + DSPFFT(); + + FMOD_RESULT getSpectrum(float *pcmbuffer, unsigned int pcmposition, unsigned int pcmlength, float *spectrum, int length, int channel, int numchannels, FMOD_DSP_FFT_WINDOW windowtype); + + }; +} + + +#endif /* _FMOD_DSP_FFT */ + diff --git a/src/fmod_dsp_filter.cpp b/src/fmod_dsp_filter.cpp new file mode 100755 index 0000000..939e67c --- /dev/null +++ b/src/fmod_dsp_filter.cpp @@ -0,0 +1,531 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_dsp_filter.h" +#include "fmod_localcriticalsection.h" +#include "fmod_historybuffer_pool.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFilter::release(bool freethis) +{ + stopBuffering(); + + return DSPI::release(freethis); +} + +#if !defined(FMOD_SUPPORT_MIXER_NONRECURSIVE) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFilter::read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + FMOD_RESULT result = FMOD_OK; + + if (*length > mSystem->mDSPBlockSize) + { + *length = mSystem->mDSPBlockSize; + } + + mFlags |= FMOD_DSP_FLAG_IDLE; + *outbuffer = 0; + *outchannels = 0; + + if (mDSPTick != tick) + { + LinkedListNode *current = 0; + bool hasmixed = false; + int connectionnumber = 0; + unsigned int starttime = 0; + unsigned int endtime = 0; + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + /* + Loop through inputs and read from them, mixing into 'mixbuffer' as we go. + */ + current = mInputHead.getNext(); + + while (current != &mInputHead) + { + DSPConnectionI *connection = (DSPConnectionI *)current->getData(); + + /* + If the input is disabled, skip it. + */ + if (!(connection->mInputUnit->mFlags & FMOD_DSP_FLAG_ACTIVE) || connection->mInputUnit->mFlags & (FMOD_DSP_FLAG_FINISHED | FMOD_DSP_FLAG_QUEUEDFORDISCONNECT)) + { + connection->mInputUnit->mFlags |= FMOD_DSP_FLAG_IDLE; + } + else + { + /* + Execute is recursive, so don't time inclusive of inputs + */ + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsageTemp += endtime - starttime; + } + + result = connection->mInputUnit->read(outbuffer, outchannels, length, speakermode, speakermodechannels, tick); + if (result != FMOD_OK) + { + break; + } + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + /* + Check if we should mix or not. + */ + bool hastomix = true; + + if (connection->mInputUnit->mFlags & FMOD_DSP_FLAG_IDLE) + { + hastomix = false; + } + else + { + mFlags &= ~FMOD_DSP_FLAG_IDLE; + + if (mNumInputs <= 1 && connection->mVolume == 1.0f) + { + hastomix = false; + + if (mDescription.mCategory == FMOD_DSP_CATEGORY_SOUNDCARD && *outchannels != speakermodechannels) + { + hastomix = true; + } + else if (connection->mSetLevelsUsed && connection->checkUnity(*outchannels, speakermodechannels) != FMOD_OK) + { + hastomix = true; + } + else if (mDescription.read && mDescription.channels && mDescription.channels != *outchannels) + { + hastomix = true; /* This is only because we need to need to set the channel count to that of mDescription.channels? */ + } + } + } + if (hastomix) + { + /* + If a new connection happened and no pan has been set, set it here. + */ + if (!connection->mSetLevelsUsed) + { + if (speakermodechannels == *outchannels && connection->mVolume == 1.0f) + { + connection->setUnity(); + } + else + { + connection->setPan(0.0f, speakermodechannels, *outchannels, speakermode); + } + connection->mSetLevelsUsed = true; + } + + /* + If it is the first input, give it a blank buffer to mix to. + */ + if (!hasmixed) + { + FMOD_memset(mBuffer, 0, *length * speakermodechannels * sizeof(float)); + } + + /* + Mix from the input's buffer into this units mix destination + */ + connection->mix(mBuffer, (float *)*outbuffer, speakermodechannels, *outchannels, *length); + + hasmixed = true; + } + else + { + if (connection->mRampCount) + { + int count, count2; + + for (count = 0; count < connection->mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < connection->mMaxInputLevels; count2++) + { + connection->mLevelCurrent[count][count2] = DSP_LEVEL_COMPRESS(DSP_LEVEL_DECOMPRESS(connection->mLevel[count][count2]) * connection->mVolume); + connection->mLevelDelta [count][count2] = 0; + } + } + connection->mRampCount = 0; + } + } + } + + connection->mInputUnit->mDSPTick = tick; + current = current->getNext(); + connectionnumber++; + } + + if (hasmixed) + { + *outbuffer = mBuffer; + *outchannels = speakermodechannels; + } + + /* + Call the plugin callback to actually process or generate the data for this unit. + */ + if (mDescription.read && !(mFlags & FMOD_DSP_FLAG_BYPASS)) + { + float *src = *outbuffer; /* source is whatever was passed to us from above. */ + + if (src == mBuffer || !src) /* If the src is the same as the dest, we need to copy src into a temp buffer. */ + { + if (src) + { + FMOD_memcpy(mSystem->mDSPTempBuff, src, *length * *outchannels * sizeof(float)); + } + src = mSystem->mDSPTempBuff; + } + + if (mDescription.channels) + { + *outchannels = mDescription.channels; + FMOD_memset(src, 0, *length * mDescription.channels * sizeof(float)); + } + else if (!*outchannels) + { + *outchannels = speakermodechannels; + } + + if (mFlags & FMOD_DSP_FLAG_IDLE) + { + FMOD_memset(src, 0, *length * *outchannels * sizeof(float)); + } + + + instance = (FMOD_DSP *)this; + mDescription.read((FMOD_DSP_STATE *)this, src, mBuffer, *length, *outchannels, *outchannels); + + *outbuffer = mBuffer; + + mFlags &= ~FMOD_DSP_FLAG_IDLE; + } + + if (mNumOutputs > 1 && mDescription.mCategory != FMOD_DSP_CATEGORY_RESAMPLER) /* A multiinput resampler doesnt need its mbuffer stomped on. */ + { + if (!hasmixed && *outbuffer != mBuffer) + { + if (*outbuffer) + { + FMOD_memcpy(mBuffer, (float *)*outbuffer, *length * *outchannels * sizeof(float)); + } + else + { + FMOD_memset(mBuffer, 0, *length * *outchannels * sizeof(float)); + } + + *outbuffer = mBuffer; /* Return the already processed buffer */ + } + + mBufferChannels = *outchannels; + mFlags &= ~FMOD_DSP_FLAG_IDLE; + } + + /* + If the history buffer option is set, buffer off this data into a separate ring buffer. + */ + if (mHistoryBuffer) + { + float *srcptr, *destptr; + int len; + + destptr = mHistoryBuffer; + srcptr = (float *)*outbuffer; + + if (!*outchannels) + { + *outchannels = speakermodechannels; + } + + len = *length; + while (len) + { + int size = len; + if (mHistoryPosition + size > FMOD_HISTORYBUFFERLEN) + { + size = FMOD_HISTORYBUFFERLEN - mHistoryPosition; + } + + if (!*outbuffer) + { + FMOD_memset(destptr + (mHistoryPosition * *outchannels), 0, size * *outchannels * sizeof(float)); + } + else + { + FMOD_memcpy(destptr + (mHistoryPosition * *outchannels), srcptr, size * *outchannels * sizeof(float)); + } + + len -= size; + srcptr += (size * *outchannels); + + mHistoryPosition += size; + if (mHistoryPosition >= FMOD_HISTORYBUFFERLEN) + { + mHistoryPosition = 0; + } + } + } + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsageTemp += endtime - starttime; + +#if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) && !defined(PLATFORM_PS3) + if (mDescription.mCategory != FMOD_DSP_CATEGORY_SOUNDCARD) + { + calculatePeaks(*outbuffer, *length, *outchannels); + } +#endif + if (mDescription.mCategory != FMOD_DSP_CATEGORY_RESAMPLER && mDescription.mCategory != FMOD_DSP_CATEGORY_SOUNDCARD) + { + mCPUUsage = mCPUUsageTemp; + mCPUUsageTemp = 0; + } + } + } + else + { + *outbuffer = mBuffer; /* Return the already processed buffer */ + *outchannels = mBufferChannels; + mFlags &= ~FMOD_DSP_FLAG_IDLE; + } + + return result; +} +#endif + + +#if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFilter::startBuffering() +{ + FMOD_RESULT result; + int channels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + if (mHistoryBuffer) + { + return FMOD_OK; + } + + criticalsection.enter(); + + result = mSystem->getSoftwareFormat(0, 0, &channels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (mHistoryBuffer) + { + result = releaseHistoryBuffer(mHistoryBuffer); + if (result != FMOD_OK) + { + return result; + } + } + + mHistoryPosition = 0; + + if (channels < mSystem->mMaxInputChannels) + { + channels = mSystem->mMaxInputChannels; + } + + result = createHistoryBuffer(&mHistoryBuffer, channels); + if (result != FMOD_OK) + { + return result; + } + + criticalsection.leave(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFilter::getHistoryBuffer(float **buffer, unsigned int *position, unsigned int *length) +{ + if (buffer) + { + *buffer = mHistoryBuffer; + } + + if (position) + { + *position = mHistoryPosition; + } + + if (length) + { + *length = FMOD_HISTORYBUFFERLEN; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFilter::stopBuffering() +{ + if (mHistoryBuffer) + { + FMOD_RESULT result; + + LocalCriticalSection criticalsection(mSystem->mDSPCrit, true); + result = releaseHistoryBuffer(mHistoryBuffer); + if (result != FMOD_OK) + { + return FMOD_OK; + } + mHistoryBuffer = 0; + } + + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPFilter::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mHistoryBuffer) + { + int channels = 0; + + CHECK_RESULT(mSystem->getSoftwareFormat(0, 0, &channels, 0, 0, 0)); + + if (channels < mSystem->mMaxInputChannels) + { + channels = mSystem->mMaxInputChannels; + } + tracker->add(false, FMOD_MEMBITS_DSP, FMOD_HISTORYBUFFERLEN * channels * sizeof(float)); + } + + return FMOD_OK; +} + +#endif + +} + +#endif diff --git a/src/fmod_dsp_filter.h b/src/fmod_dsp_filter.h new file mode 100755 index 0000000..fee9e2a --- /dev/null +++ b/src/fmod_dsp_filter.h @@ -0,0 +1,46 @@ +#ifndef _FMOD_DSP_FILTER_H +#define _FMOD_DSP_FILTER_H + +#include "fmod_settings.h" + +#include "fmod_dspi.h" +#include "fmod_os_misc.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class DSPFilter : public DSPI + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float *mHistoryBuffer; + unsigned int mHistoryPosition; + int mBufferChannels; + + protected: + + FMOD_RESULT release(bool freethis = true); + +#ifndef FMOD_SUPPORT_MIXER_NONRECURSIVE + virtual FMOD_RESULT read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); +#endif + + public: + + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + + FMOD_RESULT startBuffering(); + FMOD_RESULT getHistoryBuffer(float **buffer, unsigned int *position, unsigned int *length); + FMOD_RESULT stopBuffering(); + + #endif + }; +} + +#endif + diff --git a/src/fmod_dsp_flange.cpp b/src/fmod_dsp_flange.cpp new file mode 100755 index 0000000..0de9280 --- /dev/null +++ b/src/fmod_dsp_flange.cpp @@ -0,0 +1,754 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FLANGE + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_flange.h" + +#ifdef PLATFORM_PS3_SPU +#include "fmod_systemi_spu.h" +#else +#include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_flange_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspflange; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPFlange::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dspflange_param[4] = +{ + { 0.0f, 1.0f, 0.45f, "Drymix", "%", "Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.45." }, + { 0.0f, 1.0f, 0.55f, "Wetmix", "%", "Volume of flange signal to pass to output. 0.0 to 1.0. Default = 0.55." }, + { 0.01f, 1.0f, 1.00f, "Depth", "", "Flange depth. 0.01 to 1.0. Default = 1.0." }, + { 0.0f, 20.0f, 0.10f, "Rate", "hz", "Flange speed in hz. 0.0 to 20.0. Default = 0.1." } +}; + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPFlange::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dspflange, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspflange.name, "FMOD Flange"); + dspflange.version = 0x00010100; + dspflange.create = DSPFlange::createCallback; + dspflange.release = DSPFlange::releaseCallback; + dspflange.reset = DSPFlange::resetCallback; + + #ifdef PLATFORM_PS3 + dspflange.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_flange_pic_start; /* SPU PIC entry address */ + #else + dspflange.read = DSPFlange::readCallback; + #endif + + dspflange.numparameters = sizeof(dspflange_param) / sizeof(dspflange_param[0]); + dspflange.paramdesc = dspflange_param; + dspflange.setparameter = DSPFlange::setParameterCallback; + dspflange.getparameter = DSPFlange::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspflange.getmemoryused = &DSPFlange::getMemoryUsedCallback; +#endif + + dspflange.mType = FMOD_DSP_TYPE_FLANGE; + dspflange.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspflange.mSize = sizeof(DSPFlange); +#else + dspflange.read = DSPFlange::readCallback; /* We only care about read function on SPU */ +#endif + return &dspflange; +} + +#ifdef DSP_FLANGE_USECOSTAB + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPFlange::cosine(float x) +{ + int y; + + x *= DSP_FLANGE_TABLERANGE; + y = (int)x; + if (y < 0) + { + y = -y; + } + + y &= DSP_FLANGE_TABLEMASK; + switch (y >> DSP_FLANGE_COSTABBITS) + { + case 0 : return mCosTab[y]; + case 1 : return -mCosTab[(DSP_FLANGE_COSTABSIZE - 1) - (y - (DSP_FLANGE_COSTABSIZE * 1))]; + case 2 : return -mCosTab[ (y - (DSP_FLANGE_COSTABSIZE * 2))]; + case 3 : return mCosTab[(DSP_FLANGE_COSTABSIZE - 1) - (y - (DSP_FLANGE_COSTABSIZE * 3))]; + } + + return 0.0f; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPFlange::sine(float x) +{ + return cosine(x - 0.25f); +} + +#endif + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFlange::createInternal() +{ + FMOD_RESULT result; + int channels, count; + + init(); + +#ifdef DSP_FLANGE_USECOSTAB + { + int count; + + for (count = 0; count < DSP_FLANGE_COSTABSIZE; count++) + { + mCosTab[count] = (float)FMOD_COS(FMOD_PI_2 * (float)count / (float)DSP_FLANGE_COSTABSIZE); + } + } +#endif + + result = mSystem->getSoftwareFormat(&mOutputRate, 0, &channels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mFlangeBufferLengthBytes = (int)((float)mOutputRate * DSP_FLANGE_MAXBUFFERLENGTHMS) / 1000; + mFlangeBufferLengthBytes *= channels; + + #ifdef FLANGE_USEFLOAT + + mFlangeBufferLengthBytes *= sizeof(float); + mFlangeBufferLengthBytes += 1024; + mFlangeBuffer = (float *)FMOD_Memory_Calloc(mFlangeBufferLengthBytes); + + #else + + mFlangeBufferLengthBytes *= sizeof(short); + mFlangeBufferLengthBytes += 1024; + + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + mFlangeBuffer = (short *)FMOD_Memory_Calloc(mFlangeBufferLengthBytes); + #endif + + #endif + + if (!mFlangeBuffer) + { + return FMOD_ERR_MEMORY; + } + + mFlangeTick = 0; + + for (count = 0; count < mDescription.numparameters; count++) + { + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFlange::releaseInternal() +{ + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + if (mFlangeBuffer) + { + FMOD_Memory_Free(mFlangeBuffer); + mFlangeBuffer = 0; + } + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFlange::resetInternal() +{ + mFlangeBufferPosition = 0; + + if (mFlangeBuffer) + { + FMOD_memset(mFlangeBuffer, 0, mFlangeBufferLengthBytes); + } + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFlange::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + float halfdepth = mDepth * 0.5f; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No speaker channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + for (count = 0; count < length; count++) + { + unsigned int p1, p2; + float frac; + int count2; + + p1 = (mFlangeBufferPosition + (unsigned int)mFlangePosition) % mFlangeBufferLength; + p2 = p1 + 1; /* the first sample of the buffer has been duplicated to the end so it wont click. */ + + frac = mFlangePosition - (int)mFlangePosition; + + for (count2 = 0; count2 < inchannels; count2++) + { + int offset = (count * inchannels) + count2; + + if (!(speakermask & (1<<count2))) + { + outbuffer[offset] = inbuffer[offset]; + } + else + { + float val; + + val = inbuffer[offset] * mDryMix; + + #ifdef FLANGE_USEFLOAT + + val += ((mFlangeBuffer[(p1 * inchannels) + count2] * (1.0f - frac)) + (mFlangeBuffer[(p2 * inchannels) + count2] * frac)) * mWetMix; + + mFlangeBuffer[(mFlangeBufferPosition * inchannels) + count2] = inbuffer[offset]; + + #else + + val += (((mFlangeBuffer[(p1 * inchannels) + count2] / 32768.0f) * (1.0f - frac)) + ((mFlangeBuffer[(p2 * inchannels) + count2] / 32768.0f) * frac)) * mWetMix; + + mFlangeBuffer[(mFlangeBufferPosition * inchannels) + count2] = (signed short)(inbuffer[offset] * 32768.0f); + + #endif + + outbuffer[offset] = val; + } + } + + if (!mFlangeBufferPosition) + { + for (count2 = 0; count2 < inchannels; count2++) + { + mFlangeBuffer[(mFlangeBufferLength * inchannels) + count2] = mFlangeBuffer[count2]; + } + } + + mFlangeBufferPosition++; + if (mFlangeBufferPosition >= mFlangeBufferLength) + { + mFlangeBufferPosition = 0; + } + + #ifdef DSP_FLANGE_USECOSTAB + mFlangePosition = (1.0f + sine(mFlangeTick + 0.00f)) * halfdepth; + #else + mFlangePosition = (1.0f + sine((mFlangeTick + 0.00f) * FMOD_PI2)) * halfdepth; + #endif + + mFlangePosition = mFlangePosition * (mFlangeBufferLength - 1); + mFlangeTick += mFlangeSpeed; + } + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFlange::setParameterInternal(int index, float value) +{ + float olddepth; + + olddepth = mDepth; + + mSystem->lockDSP(); + + switch (index) + { + case FMOD_DSP_FLANGE_DRYMIX: + { + mDryMix = value; + break; + } + case FMOD_DSP_FLANGE_WETMIX: + { + mWetMix = value; + break; + } + case FMOD_DSP_FLANGE_DEPTH: + { + mDepth = value; + break; + } + case FMOD_DSP_FLANGE_RATE: + { + mRateHz = value; + break; + } + } + + if (mDepth != olddepth) + { + float delay = mDepth * 10.0f; + + mFlangeBufferLength = (int)((float)mOutputRate * delay / 1000.0f); + if (mFlangeBufferLength < 4) + { + mFlangeBufferLength = 4; + } + + resetInternal(); + } + + mFlangeSpeed = mRateHz / (float)mOutputRate; + + mSystem->unlockDSP(); + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPFlange::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_FLANGE_DRYMIX: + { + *value = mDryMix; + sprintf(valuestr, "%.1f", mDryMix * 100.0f); + break; + } + case FMOD_DSP_FLANGE_WETMIX: + { + *value = mWetMix; + sprintf(valuestr, "%.1f", mWetMix * 100.0f); + break; + } + case FMOD_DSP_FLANGE_DEPTH: + { + *value = mDepth; + sprintf(valuestr, "%.02f", mDepth); + break; + } + case FMOD_DSP_FLANGE_RATE: + { + *value = mRateHz; + sprintf(valuestr, "%.02f", mRateHz); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPFlange::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + if (mFlangeBuffer) + { + tracker->add(false, FMOD_MEMBITS_DSP, mFlangeBufferLengthBytes); + } + #endif + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->resetInternal(); +} +#endif //!PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPFlange::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPFlange *flange = (DSPFlange *)dsp; + + return flange->DSPFlange::getMemoryUsed(tracker); +} +#endif + +#endif //!PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_flange.h b/src/fmod_dsp_flange.h new file mode 100755 index 0000000..dc39191 --- /dev/null +++ b/src/fmod_dsp_flange.h @@ -0,0 +1,87 @@ +#ifndef _FMOD_DSP_FLANGE_H +#define _FMOD_DSP_FLANGE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FLANGE + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + const int DSP_FLANGE_COSTABBITS = 13; + const int DSP_FLANGE_COSTABSIZE = (1 << DSP_FLANGE_COSTABBITS); + const int DSP_FLANGE_TABLERANGE = (DSP_FLANGE_COSTABSIZE * 4); + const int DSP_FLANGE_TABLEMASK = (DSP_FLANGE_TABLERANGE - 1); + const float DSP_FLANGE_MAXBUFFERLENGTHMS = 40.0f; + + #if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + #define DSP_FLANGE_USECOSTAB + #endif + + class DSPFlange : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float mDepth; + float mDryMix; + float mWetMix; + float mRateHz; + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + signed short FMOD_PPCALIGN16(mFlangeBuffer[15872]); //31744 bytes +#else + #ifdef FLANGE_USEFLOAT + float *mFlangeBuffer; + #else + short *mFlangeBuffer; + #endif +#endif + unsigned int FMOD_PPCALIGN16(mFlangeBufferLength); + unsigned int FMOD_PPCALIGN16(mFlangeBufferLengthBytes); + unsigned int FMOD_PPCALIGN16(mFlangeBufferPosition); + float FMOD_PPCALIGN16(mFlangePosition); + float FMOD_PPCALIGN16(mFlangeTick); + float FMOD_PPCALIGN16(mFlangeSpeed); + int FMOD_PPCALIGN16(mOutputRate); + +#ifdef DSP_FLANGE_USECOSTAB + float mCosTab[DSP_FLANGE_COSTABSIZE]; + + FMOD_INLINE const float cosine(float x); + FMOD_INLINE const float sine(float x); +#else + FMOD_INLINE const float cosine(float x) { return FMOD_COS(x); } + FMOD_INLINE const float sine(float x) { return FMOD_SIN(x); } +#endif + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_highpass.cpp b/src/fmod_dsp_highpass.cpp new file mode 100755 index 0000000..01054d1 --- /dev/null +++ b/src/fmod_dsp_highpass.cpp @@ -0,0 +1,878 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_HIGHPASS + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_highpass.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_highpass_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsphighpass; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPHighPass::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dsphighpass_param[2] = +{ + { 1.0f, 22000.0f, 5000.0f, "Cutoff freq", "hz", "Highpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0." }, + { 1.0f, 10.0f, 1.0f, "Resonance", "", "Highpass resonance Q value. 1.0 to 10.0. Default = 1.0." } +}; + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPHighPass::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dsphighpass, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsphighpass.name, "FMOD Highpass"); + dsphighpass.version = 0x00010100; + dsphighpass.create = DSPHighPass::createCallback; + dsphighpass.reset = DSPHighPass::resetCallback; + + #ifdef PLATFORM_PS3 + dsphighpass.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_highpass_pic_start; /* SPU PIC entry address */ + #else + dsphighpass.read = DSPHighPass::readCallback; + #endif + + dsphighpass.numparameters = sizeof(dsphighpass_param) / sizeof(dsphighpass_param[0]); + dsphighpass.paramdesc = dsphighpass_param; + dsphighpass.setparameter = DSPHighPass::setParameterCallback; + dsphighpass.getparameter = DSPHighPass::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsphighpass.getmemoryused = &DSPHighPass::getMemoryUsedCallback; +#endif + + dsphighpass.mType = FMOD_DSP_TYPE_HIGHPASS; + dsphighpass.mCategory = FMOD_DSP_CATEGORY_FILTER; + dsphighpass.mSize = sizeof(DSPHighPass); +#else + dsphighpass.read = DSPHighPass::readCallback; /* We only care about read function on SPU */ +#endif + + return &dsphighpass; +} + +#ifndef PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::createInternal() +{ + int count; + + init(); + + /* + Calculat the max cutoff rate + */ + int outputrate = 0; + FMOD_RESULT result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + mCutoffHzMaximum = ((float)outputrate / 2.0f) - 10.0f; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + resetInternal(); + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mResonance = mResonanceUpdate; + mCutoffHz = mCutoffHzUpdate; + updateCoefficients(mResonance, mCutoffHz); + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::resetInternal() +{ + int count; + + for (count=0; count < DSP_MAXLEVELS_MAX; count++) + { + mIn1[count] = mIn2[count] = 0; + mOut1[count] = mOut2[count] = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::updateCoefficients(float resonance, float cutoff) +{ + int outputrate; + float w; + +#ifdef PLATFORM_PS3_SPU + outputrate = 48000; +#else + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); +#endif + + w = FMOD_PI2 * (float)cutoff / outputrate; + + mCoefficient_B0 = (1.0f + FMOD_COS(w)) / 2.0f; + mCoefficient_B1 = -(1.0f + FMOD_COS(w)); + mCoefficient_B2 = (1.0f + FMOD_COS(w)) / 2.0f; + mCoefficient_A0 = 1.0f + FMOD_SIN(w) / (2 * resonance); + mCoefficient_A1 = -2.0f * FMOD_COS(w); + mCoefficient_A2 = 1.0f - FMOD_SIN(w) / (2 * resonance); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::process(float *inbuffer, float *outbuffer, unsigned int length, int channels) +{ + unsigned int count; + int count2; + static float dc = (float)1E-20; + + if (channels == 1 && (speakermask & 1)) + { + for (count = 0; count < length; count++) + { + float in0, out0; + + in0 = inbuffer[count] + dc; + + out0 = (mCoefficient_B0 * in0 + mCoefficient_B1 * mIn1[0] + mCoefficient_B2 * mIn2[0] - mCoefficient_A1 * mOut1[0] - mCoefficient_A2 * mOut2[0]) / mCoefficient_A0; + + mIn2[0] = mIn1[0]; + mIn1[0] = in0; + + mOut2[0] = mOut1[0]; + mOut1[0] = out0; + + outbuffer[count] = out0; + + dc = -dc; + } + } + else if (channels == 2 && (speakermask & 0x3) == 0x3) + { + for (count = 0; count < length; count++) + { + float in0l, out0l; + float in0r, out0r; + + in0l = inbuffer[count*2 + 0] + dc; + in0r = inbuffer[count*2 + 1] + dc; + + out0l = (mCoefficient_B0 * in0l + mCoefficient_B1 * mIn1[0] + mCoefficient_B2 * mIn2[0] - mCoefficient_A1 * mOut1[0] - mCoefficient_A2 * mOut2[0]) / mCoefficient_A0; + out0r = (mCoefficient_B0 * in0r + mCoefficient_B1 * mIn1[1] + mCoefficient_B2 * mIn2[1] - mCoefficient_A1 * mOut1[1] - mCoefficient_A2 * mOut2[1]) / mCoefficient_A0; + + mIn2[0] = mIn1[0]; + mIn1[0] = in0l; + mIn2[1] = mIn1[1]; + mIn1[1] = in0r; + + mOut2[0] = mOut1[0]; + mOut1[0] = out0l; + mOut2[1] = mOut1[1]; + mOut1[1] = out0r; + + outbuffer[count*2 + 0] = out0l; + outbuffer[count*2 + 1] = out0r; + + dc = -dc; + } + } + else if (channels == 6 && (speakermask & 0x3F) == 0x3F) + { + for (count = 0; count < length; count++) + { + float in0, out0; + float in1, out1; + float in2, out2; + float in3, out3; + float in4, out4; + float in5, out5; + + in0 = inbuffer[count*6 + 0] + dc; + in1 = inbuffer[count*6 + 1] + dc; + in2 = inbuffer[count*6 + 2] + dc; + in3 = inbuffer[count*6 + 3] + dc; + in4 = inbuffer[count*6 + 4] + dc; + in5 = inbuffer[count*6 + 5] + dc; + + out0 = (mCoefficient_B0 * in0 + mCoefficient_B1 * mIn1[0] + mCoefficient_B2 * mIn2[0] - mCoefficient_A1 * mOut1[0] - mCoefficient_A2 * mOut2[0]) / mCoefficient_A0; + out1 = (mCoefficient_B0 * in1 + mCoefficient_B1 * mIn1[1] + mCoefficient_B2 * mIn2[1] - mCoefficient_A1 * mOut1[1] - mCoefficient_A2 * mOut2[1]) / mCoefficient_A0; + out2 = (mCoefficient_B0 * in2 + mCoefficient_B1 * mIn1[2] + mCoefficient_B2 * mIn2[2] - mCoefficient_A1 * mOut1[2] - mCoefficient_A2 * mOut2[2]) / mCoefficient_A0; + out3 = (mCoefficient_B0 * in3 + mCoefficient_B1 * mIn1[3] + mCoefficient_B2 * mIn2[3] - mCoefficient_A1 * mOut1[3] - mCoefficient_A2 * mOut2[3]) / mCoefficient_A0; + out4 = (mCoefficient_B0 * in4 + mCoefficient_B1 * mIn1[4] + mCoefficient_B2 * mIn2[4] - mCoefficient_A1 * mOut1[4] - mCoefficient_A2 * mOut2[4]) / mCoefficient_A0; + out5 = (mCoefficient_B0 * in5 + mCoefficient_B1 * mIn1[5] + mCoefficient_B2 * mIn2[5] - mCoefficient_A1 * mOut1[5] - mCoefficient_A2 * mOut2[5]) / mCoefficient_A0; + + mIn2[0] = mIn1[0]; + mIn1[0] = in0; + mIn2[1] = mIn1[1]; + mIn1[1] = in1; + mIn2[2] = mIn1[2]; + mIn1[2] = in2; + mIn2[3] = mIn1[3]; + mIn1[3] = in3; + mIn2[4] = mIn1[4]; + mIn1[4] = in4; + mIn2[5] = mIn1[5]; + mIn1[5] = in5; + + mOut2[0] = mOut1[0]; + mOut1[0] = out0; + mOut2[1] = mOut1[1]; + mOut1[1] = out1; + mOut2[2] = mOut1[2]; + mOut1[2] = out2; + mOut2[3] = mOut1[3]; + mOut1[3] = out3; + mOut2[4] = mOut1[4]; + mOut1[4] = out4; + mOut2[5] = mOut1[5]; + mOut1[5] = out5; + + outbuffer[count*6 + 0] = out0; + outbuffer[count*6 + 1] = out1; + outbuffer[count*6 + 2] = out2; + outbuffer[count*6 + 3] = out3; + outbuffer[count*6 + 4] = out4; + outbuffer[count*6 + 5] = out5; + + dc = -dc; + } + } + else if (channels == 8 && (speakermask & 0xFF) == 0xFF) + { + for (count = 0; count < length; count++) + { + float in0, out0; + float in1, out1; + float in2, out2; + float in3, out3; + float in4, out4; + float in5, out5; + float in6, out6; + float in7, out7; + + in0 = inbuffer[count*8 + 0] + dc; + in1 = inbuffer[count*8 + 1] + dc; + in2 = inbuffer[count*8 + 2] + dc; + in3 = inbuffer[count*8 + 3] + dc; + in4 = inbuffer[count*8 + 4] + dc; + in5 = inbuffer[count*8 + 5] + dc; + in6 = inbuffer[count*8 + 6] + dc; + in7 = inbuffer[count*8 + 7] + dc; + + out0 = (mCoefficient_B0 * in0 + mCoefficient_B1 * mIn1[0] + mCoefficient_B2 * mIn2[0] - mCoefficient_A1 * mOut1[0] - mCoefficient_A2 * mOut2[0]) / mCoefficient_A0; + out1 = (mCoefficient_B0 * in1 + mCoefficient_B1 * mIn1[1] + mCoefficient_B2 * mIn2[1] - mCoefficient_A1 * mOut1[1] - mCoefficient_A2 * mOut2[1]) / mCoefficient_A0; + out2 = (mCoefficient_B0 * in2 + mCoefficient_B1 * mIn1[2] + mCoefficient_B2 * mIn2[2] - mCoefficient_A1 * mOut1[2] - mCoefficient_A2 * mOut2[2]) / mCoefficient_A0; + out3 = (mCoefficient_B0 * in3 + mCoefficient_B1 * mIn1[3] + mCoefficient_B2 * mIn2[3] - mCoefficient_A1 * mOut1[3] - mCoefficient_A2 * mOut2[3]) / mCoefficient_A0; + out4 = (mCoefficient_B0 * in4 + mCoefficient_B1 * mIn1[4] + mCoefficient_B2 * mIn2[4] - mCoefficient_A1 * mOut1[4] - mCoefficient_A2 * mOut2[4]) / mCoefficient_A0; + out5 = (mCoefficient_B0 * in5 + mCoefficient_B1 * mIn1[5] + mCoefficient_B2 * mIn2[5] - mCoefficient_A1 * mOut1[5] - mCoefficient_A2 * mOut2[5]) / mCoefficient_A0; + out6 = (mCoefficient_B0 * in6 + mCoefficient_B1 * mIn1[6] + mCoefficient_B2 * mIn2[6] - mCoefficient_A1 * mOut1[6] - mCoefficient_A2 * mOut2[6]) / mCoefficient_A0; + out7 = (mCoefficient_B0 * in7 + mCoefficient_B1 * mIn1[7] + mCoefficient_B2 * mIn2[7] - mCoefficient_A1 * mOut1[7] - mCoefficient_A2 * mOut2[7]) / mCoefficient_A0; + + mIn2[0] = mIn1[0]; + mIn1[0] = in0; + mIn2[1] = mIn1[1]; + mIn1[1] = in1; + mIn2[2] = mIn1[2]; + mIn1[2] = in2; + mIn2[3] = mIn1[3]; + mIn1[3] = in3; + mIn2[4] = mIn1[4]; + mIn1[4] = in4; + mIn2[5] = mIn1[5]; + mIn1[5] = in5; + mIn2[6] = mIn1[6]; + mIn1[6] = in6; + mIn2[7] = mIn1[7]; + mIn1[7] = in7; + + mOut2[0] = mOut1[0]; + mOut1[0] = out0; + mOut2[1] = mOut1[1]; + mOut1[1] = out1; + mOut2[2] = mOut1[2]; + mOut1[2] = out2; + mOut2[3] = mOut1[3]; + mOut1[3] = out3; + mOut2[4] = mOut1[4]; + mOut1[4] = out4; + mOut2[5] = mOut1[5]; + mOut1[5] = out5; + mOut2[6] = mOut1[6]; + mOut1[6] = out6; + mOut2[7] = mOut1[7]; + mOut1[7] = out7; + + outbuffer[count*8 + 0] = out0; + outbuffer[count*8 + 1] = out1; + outbuffer[count*8 + 2] = out2; + outbuffer[count*8 + 3] = out3; + outbuffer[count*8 + 4] = out4; + outbuffer[count*8 + 5] = out5; + outbuffer[count*8 + 6] = out6; + outbuffer[count*8 + 7] = out7; + + dc = -dc; + } + } + else + { + for (count2 = 0; count2 < channels; count2++) + { + float *in = inbuffer + count2; + float *out = outbuffer + count2; + int len; + + if (!((1 << count2) & speakermask)) + { + int inc; + int offset1, offset2, offset3; + offset1 = channels; + offset2 = channels * 2; + offset3 = channels * 3; + len = length >> 2; + inc = channels << 2; + + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + + len--; + in += inc; + out += inc; + } + + len = length & 3; + + while (len) + { + out[0] = in[0]; + len--; + in += channels; + out += channels; + } + continue; + } + else + { + for (count = 0; count < length; count++) + { + float in0, out0; + + in0 = inbuffer[(count * channels) + count2] + dc; + + out0 = (mCoefficient_B0 * in0 + mCoefficient_B1 * mIn1[count2] + mCoefficient_B2 * mIn2[count2] - mCoefficient_A1 * mOut1[count2] - mCoefficient_A2 * mOut2[count2]) / mCoefficient_A0; + + mIn2[count2] = mIn1[count2]; + mIn1[count2] = in0; + + mOut2[count2] = mOut1[count2]; + mOut1[count2] = out0; + + outbuffer[(count * channels) + count2] = out0; + + dc = -dc; + } + } + } + } + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + if (!inbuffer) + { + return FMOD_OK; + } + + while (mResonance != mResonanceUpdate || mCutoffHz != mCutoffHzUpdate) + { + float cutoffdelta = 10.0f + (mCutoffHz / 100.0f); + + if (mResonance < mResonanceUpdate) + { + mResonance += 1.0f; + if (mResonance >= mResonanceUpdate) + { + mResonance = mResonanceUpdate; + } + } + else if (mResonance > mResonanceUpdate) + { + mResonance -= 1.0f; + if (mResonance <= mResonanceUpdate) + { + mResonance = mResonanceUpdate; + } + } + if (mCutoffHz < mCutoffHzUpdate) + { + mCutoffHz += cutoffdelta; + if (mCutoffHz >= mCutoffHzUpdate) + { + mCutoffHz = mCutoffHzUpdate; + } + } + else if (mCutoffHz > mCutoffHzUpdate) + { + mCutoffHz -= cutoffdelta; + if (mCutoffHz <= mCutoffHzUpdate) + { + mCutoffHz = mCutoffHzUpdate; + } + } + + updateCoefficients(mResonance, mCutoffHz); + + process(inbuffer, outbuffer, 1, inchannels); + + inbuffer+=inchannels; + outbuffer+=inchannels; + length--; + if (!length) + { + return FMOD_OK; + } + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + if (mCutoffHz < 2.0f) + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + else if (mCutoffHz >= mCutoffHzMaximum) + { + int count; + + FMOD_memset(outbuffer, 0, sizeof(float)*length*inchannels); + + for (count = 0; count < inchannels; count++) + { + mIn1[count] = mIn2[count] = 0; + mOut1[count] = mOut2[count] = 0; + } + return FMOD_OK; + } + + return process(inbuffer, outbuffer, length, inchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::setParameterInternal(int index, float value) +{ + switch (index) + { + case FMOD_DSP_HIGHPASS_CUTOFF: + { + mCutoffHzUpdate = value; + if (mCutoffHzUpdate >= mCutoffHzMaximum) + { + mCutoffHzUpdate = mCutoffHzMaximum; + } + break; + } + case FMOD_DSP_HIGHPASS_RESONANCE: + { + mResonanceUpdate = value; + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPHighPass::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_HIGHPASS_CUTOFF: + { + *value = mCutoffHzUpdate; + sprintf(valuestr, "%.02f", mCutoffHzUpdate); + break; + } + case FMOD_DSP_HIGHPASS_RESONANCE: + { + *value = mResonanceUpdate; + sprintf(valuestr, "%.02f", mResonanceUpdate); + break; + } + } + + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPHighPass::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPHighPass::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPHighPass *highpass = (DSPHighPass *)dsp; + + return highpass->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPHighPass::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPHighPass *highpass = (DSPHighPass *)dsp; + + return highpass->resetInternal(); +} + +#endif //!PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPHighPass::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPHighPass *highpass = (DSPHighPass *)dsp; + + return highpass->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPHighPass::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPHighPass *highpass = (DSPHighPass *)dsp; + + return highpass->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPHighPass::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPHighPass *highpass = (DSPHighPass *)dsp; + + return highpass->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPHighPass::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPHighPass *highpass = (DSPHighPass *)dsp; + + return highpass->DSPHighPass::getMemoryUsed(tracker); +} +#endif + +#endif //!PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_highpass.h b/src/fmod_dsp_highpass.h new file mode 100755 index 0000000..d9c9ffa --- /dev/null +++ b/src/fmod_dsp_highpass.h @@ -0,0 +1,63 @@ +#ifndef _FMOD_DSP_HIGHPASS_H +#define _FMOD_DSP_HIGHPASS_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_HIGHPASS + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPHighPass : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float FMOD_PPCALIGN16(mCutoffHz); + float FMOD_PPCALIGN16(mCutoffHzUpdate); + float FMOD_PPCALIGN16(mCutoffHzMaximum); + float FMOD_PPCALIGN16(mResonance); + float FMOD_PPCALIGN16(mResonanceUpdate); + + float FMOD_PPCALIGN16(mIn1[DSP_MAXLEVELS_MAX]); + float FMOD_PPCALIGN16(mIn2[DSP_MAXLEVELS_MAX]); + float FMOD_PPCALIGN16(mOut1[DSP_MAXLEVELS_MAX]); + float FMOD_PPCALIGN16(mOut2[DSP_MAXLEVELS_MAX]); + float FMOD_PPCALIGN16(mCoefficient_A0); + float FMOD_PPCALIGN16(mCoefficient_A1); + float FMOD_PPCALIGN16(mCoefficient_A2); /* numerator coefficients */ + float FMOD_PPCALIGN16(mCoefficient_B0); + float FMOD_PPCALIGN16(mCoefficient_B1); + float FMOD_PPCALIGN16(mCoefficient_B2); /* denominator coefficients */ + + FMOD_RESULT createInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT process(float *inbuffer, float *outbuffer, unsigned int length, int channels); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + FMOD_RESULT updateCoefficients(float resonance, float cutoff); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_itecho.cpp b/src/fmod_dsp_itecho.cpp new file mode 100755 index 0000000..6d8ed3f --- /dev/null +++ b/src/fmod_dsp_itecho.cpp @@ -0,0 +1,806 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ITECHO + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_itecho.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include "fmod_common_spu.h" + #include "fmod_spu_printf.h" + #include <cell/dma.h> + #include <vmx2spu.h> +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_itecho_pic_start[]; +#endif + +#ifdef PLATFORM_PS3_SPU +#define USETEMPBUFFERS // Don't want to use TEMPBUFFERS if it is processing on the PPU for IT +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspitecho; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPITEcho::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +FMOD_DSP_PARAMETERDESC dspitecho_param[5] = +{ + { 0.0f, 100.0f, 50.0f, "WetDryMix", "", "Ratio of wet (processed) signal to dry (unprocessed) signal. Must be in the range from 0.0 through 100.0 (all wet). The default value is 50." }, + { 0.0f, 100.0f, 50.0f, "Feedback", "%", "Percentage of output fed back into input, in the range from 0.0 through 100.0. The default value is 50." }, + { 1.0f, 2000.0f, 500.0f, "LeftDelay", "ms", "Delay for left channel, in milliseconds, in the range from 1.0 through 2000.0. The default value is 500 ms." }, + { 1.0f, 2000.0f, 500.0f, "RightDelay", "ms", "Delay for right channel, in milliseconds, in the range from 1.0 through 2000.0. The default value is 500 ms." }, + { 0.0f, 1.0f, 0.0f, "PanDelay", "", "Value that specifies whether to swap left and right delays with each successive echo. The default value is zero, meaning no swap. Possible values are defined as 0.0 (equivalent to FALSE) and 1.0 (equivalent to TRUE)." }, +}; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPITEcho::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dspitecho, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspitecho.name, "FMOD IT Echo"); + dspitecho.version = 0x00010100; + dspitecho.create = DSPITEcho::createCallback; + dspitecho.release = DSPITEcho::releaseCallback; + dspitecho.reset = DSPITEcho::resetCallback; + + #ifdef PLATFORM_PS3 + dspitecho.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_itecho_pic_start; /* SPU PIC entry address */ + #else + dspitecho.read = DSPITEcho::readCallback; + #endif + + dspitecho.numparameters = sizeof(dspitecho_param) / sizeof(dspitecho_param[0]); + dspitecho.paramdesc = dspitecho_param; + dspitecho.setparameter = DSPITEcho::setParameterCallback; + dspitecho.getparameter = DSPITEcho::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspitecho.getmemoryused = &DSPITEcho::getMemoryUsedCallback; +#endif + + dspitecho.mType = FMOD_DSP_TYPE_ITECHO; + dspitecho.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspitecho.mSize = sizeof(DSPITEcho); +#else + dspitecho.read = DSPITEcho::readCallback; /* We only care about read function on SPU */ +#endif + return &dspitecho; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPITEcho::createInternal() +{ + int count; + mOldSpeakerMask = 0xFFFF; + + init(); + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPITEcho::releaseInternal() +{ + int count; + + for (count = 0; count < 2; count++) + { + if (mEchoBufferMem[count]) + { + FMOD_Memory_Free(mEchoBufferMem[count]); + mEchoBuffer[count] = mEchoBufferMem[count] = 0; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPITEcho::resetInternal() +{ + mEchoPosition[0] = 0; + mEchoPosition[1] = 0; + + if (mEchoBuffer[0]) + { + FMOD_memset(mEchoBuffer[0], 0, mEchoBufferLengthBytes[0]); + } + if (mEchoBuffer[1]) + { + FMOD_memset(mEchoBuffer[1], 0, mEchoBufferLengthBytes[1]); + } + + return FMOD_OK; +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPITEcho::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + int channel; + int inchannels2 = (inchannels > 2) ? 2 : inchannels; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (speakermask != mOldSpeakerMask) /* A speaker has been disabled: clear the echo buffer for that speaker to prevent artifacts if/when the speaker is reenabled */ + { + unsigned short diff = (mOldSpeakerMask ^ speakermask); + + for (channel = 0; channel < inchannels2 ; channel++) + { + if (diff & (1 << channel)) + { + FMOD_memset(mEchoBuffer[channel], 0, mEchoBufferLengthBytes[channel]); + } + } + mOldSpeakerMask = speakermask; + } + + if (!(speakermask & ((1 << inchannels2)-1))) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_OK; + } + + // Prevent output buffer crosstalk by ensuring every sample is set + if (inchannels > 2) + { + // Prevent output buffer crosstalk by ensuring every sample is set + FMOD_memcpy(outbuffer, inbuffer, inchannels*length*sizeof(float)); + } + + for (channel = 0; channel < inchannels2; channel++) + { + unsigned int len; + float *out, *in; + + in = inbuffer + channel; + out = outbuffer + channel; + if (!((1 << channel) & speakermask)) + { + int inc, offset1, offset2, offset3; + len = length >> 2; + offset1 = inchannels2; + offset2 = inchannels2 * 2; + offset3 = inchannels2 * 3; + inc = inchannels2 << 2; + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + + in+=inc; + out+=inc; + len--; + } + + len = length & 3; + while (len) + { + out[0] = in[0]; + len--; + in += inchannels2; + out += inchannels2; + } + continue; + } + else + { + #ifdef USETEMPBUFFERS + float tempbuff_m[8 * 256 + 4]; // 8k + float *tempbuff = (float *)FMOD_ALIGNPOINTER(tempbuff_m, 16); + + unsigned int oldpos = mEchoPosition[channel]; + + #ifdef PLATFORM_PS3_SPU + + cellDmaGet((void *)tempbuff, (uint64_t)(mEchoBuffer[channel] + mEchoPosition[channel]), length * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + + #else + + FMOD_memcpy((void *)tempbuff, mEchoBuffer[channel] + mEchoPosition[channel], length * sizeof(float)); + + #endif + + #endif + + len = length; + + while (len) + { + unsigned int len2 = len; + float *echobuff; + + #ifdef USETEMPBUFFERS + echobuff = tempbuff; + #else + echobuff = mEchoBuffer[channel] + mEchoPosition[channel]; + #endif + + if (mEchoPosition[channel] + len > mEchoLength[channel]) + { + len2 = mEchoLength[channel] - mEchoPosition[channel]; + } + + for (count = 0; count < len2; count++) + { + float in0 = in[0]; + out[0] = (in0 * (1.0f - mWetDryMix)) + (echobuff[0] * mWetDryMix); + echobuff[0] = (in0 ) + (echobuff[0] * mFeedback); + + in +=inchannels; + out +=inchannels; + echobuff++; + } + + mEchoPosition[channel] += len2; + if (mEchoPosition[channel] >= mEchoLength[channel]) + { + mEchoPosition[channel] = 0; + } + len -= len2; + } + + #ifdef USETEMPBUFFERS + + #ifdef PLATFORM_PS3_SPU + + cellDmaPut((void *)tempbuff, (uint64_t)(mEchoBuffer[channel] + oldpos), length * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + + #else + + FMOD_memcpy((void *)(mEchoBuffer[channel] + oldpos), (void *)tempbuff, length * sizeof(float)); + + #endif + #endif + + } + } + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPITEcho::setParameterInternal(int index, float value) +{ + FMOD_RESULT result; + int channel; + float olddelay[2]; + bool reset = false; + + olddelay[0] = mDelay[0]; + olddelay[1] = mDelay[1]; + + result = mSystem->getSoftwareFormat(&mOutputRate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mSystem->lockDSP(); + + switch (index) + { + case FMOD_DSP_ITECHO_WETDRYMIX: + { + mWetDryMix = value / 100.0f; + break; + } + case FMOD_DSP_ITECHO_FEEDBACK: + { + mFeedback = value / 100.0f; + break; + } + case FMOD_DSP_ITECHO_LEFTDELAY: + { + mDelay[0] = value; + break; + } + case FMOD_DSP_ITECHO_RIGHTDELAY: + { + mDelay[1] = value; + break; + } + case FMOD_DSP_ITECHO_PANDELAY: + { + mPanDelay = value < 0.5f ? false : true; + break; + } + } + + for (channel = 0; channel < 2; channel++) + { + if (mDelay[channel] != olddelay[channel] || !mEchoBuffer[channel]) + { + mEchoLength[channel] = (int)((float)mOutputRate * mDelay[channel]) / 1000; + + #ifdef PLATFORM_PS3 + /* + Keep things aligned to the blocksize otherwise we will get wrapping + with tempbuffers. + */ + if (mEchoLength[channel] < mSystem->mDSPBlockSize) + { + mEchoLength[channel] = mSystem->mDSPBlockSize; + } + else if (mEchoLength[channel] > mSystem->mDSPBlockSize) + { + mEchoLength[channel] /= mSystem->mDSPBlockSize; + mEchoLength[channel] *= mSystem->mDSPBlockSize; + } + #endif + + if (mEchoBufferMem[channel]) + { + FMOD_Memory_Free(mEchoBufferMem[channel]); + mEchoBuffer[channel] = mEchoBufferMem[channel] = 0; + } + + mEchoBufferLengthBytes[channel] = mEchoLength[channel]; + mEchoBufferLengthBytes[channel] *= sizeof(float); + + #ifdef PLATFORM_PS3 + + mEchoBufferMem[channel] = (float *)FMOD_Memory_Calloc(mEchoBufferLengthBytes[channel] + 128); + if (!mEchoBufferMem[channel]) + { + mSystem->unlockDSP(); + return FMOD_ERR_MEMORY; + } + mEchoBuffer[channel] = (float *)FMOD_ALIGNPOINTER(mEchoBufferMem[channel], 128); + + #else + + mEchoBufferMem[channel] = (float *)FMOD_Memory_Calloc(mEchoBufferLengthBytes[channel]); + if (!mEchoBufferMem[channel]) + { + mSystem->unlockDSP(); + return FMOD_ERR_MEMORY; + } + mEchoBuffer[channel] = mEchoBufferMem[channel]; + + #endif + + reset = true; + } + } + + if (reset) + { + resetInternal(); + } + + mSystem->unlockDSP(); + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPITEcho::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_ITECHO_WETDRYMIX: + { + *value = mWetDryMix * 100.0f; + sprintf(valuestr, "%.1f", mWetDryMix * 100.0f); + break; + } + case FMOD_DSP_ITECHO_FEEDBACK: + { + *value = mFeedback * 100.0f; + sprintf(valuestr, "%.1f", mFeedback * 100.0f); + break; + } + case FMOD_DSP_ITECHO_LEFTDELAY: + { + *value = mDelay[0]; + sprintf(valuestr, "%.02f", mDelay[0]); + break; + } + case FMOD_DSP_ITECHO_RIGHTDELAY: + { + *value = mDelay[1]; + sprintf(valuestr, "%.02f", mDelay[1]); + break; + } + case FMOD_DSP_ITECHO_PANDELAY: + { + *value = mPanDelay ? 1.0f : 0.0f; + sprintf(valuestr, "%s", mPanDelay ? "on" : "off"); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPITEcho::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + for (int channel = 0; channel < 2; channel++) + { + if (mEchoBufferMem[channel]) + { + #ifdef PLATFORM_PS3 + tracker->add(false, FMOD_MEMBITS_DSP, mEchoBufferLengthBytes[channel] + 128); + #else + tracker->add(false, FMOD_MEMBITS_DSP, mEchoBufferLengthBytes[channel]); + #endif + } + } + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPITEcho *echo = (DSPITEcho *)dsp; + + return echo->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPITEcho *echo = (DSPITEcho *)dsp; + + return echo->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPITEcho *echo = (DSPITEcho *)dsp; + + return echo->resetInternal(); +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPITEcho *echo = (DSPITEcho *)dsp; + + return echo->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPITEcho *echo = (DSPITEcho *)dsp; + + return echo->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPITEcho *echo = (DSPITEcho *)dsp; + + return echo->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPITEcho::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPITEcho *itecho = (DSPITEcho *)dsp; + + return itecho->DSPITEcho::getMemoryUsed(tracker); +} +#endif + +#endif + +} + +#endif diff --git a/src/fmod_dsp_itecho.h b/src/fmod_dsp_itecho.h new file mode 100755 index 0000000..8d72da0 --- /dev/null +++ b/src/fmod_dsp_itecho.h @@ -0,0 +1,59 @@ +#ifndef _FMOD_DSP_ITECHO_H +#define _FMOD_DSP_ITECHO_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ITECHO + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPITEcho : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float mWetDryMix; + float mFeedback; + float mDelay[2]; + bool mPanDelay; + + float *mEchoBuffer[2]; + float *mEchoBufferMem[2]; + unsigned int mEchoBufferLengthBytes[2]; + unsigned int mEchoPosition[2]; + unsigned int mEchoLength[2]; + unsigned int mMaxLength; + int mOutputRate; + unsigned short mOldSpeakerMask; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_lowpass.cpp b/src/fmod_dsp_lowpass.cpp new file mode 100755 index 0000000..210d338 --- /dev/null +++ b/src/fmod_dsp_lowpass.cpp @@ -0,0 +1,1189 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_LOWPASS + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_lowpass.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> +#include <math.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_lowpass_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsplowpass; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPLowPass::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dsplowpass_param[2] = +{ + { 10.0f, 22000.0f, 5000.0, "Cutoff freq", "hz", "Lowpass cutoff frequency in hz. 1.0 to output 22000.0. Default = 5000.0." }, + { 1.0f, 10.0f, 1.0, "Resonance", "", "Lowpass resonance Q value. 1.0 to 10.0. Default = 1.0." } +}; + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPLowPass::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dsplowpass, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsplowpass.name, "FMOD Lowpass"); + dsplowpass.version = 0x00010100; + dsplowpass.create = DSPLowPass::createCallback; + + #ifdef PLATFORM_PS3 + dsplowpass.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_lowpass_pic_start; /* SPU PIC entry address */ + #else + dsplowpass.read = DSPLowPass::readCallback; + #endif + + dsplowpass.numparameters = sizeof(dsplowpass_param) / sizeof(dsplowpass_param[0]); + dsplowpass.paramdesc = dsplowpass_param; + dsplowpass.setparameter = DSPLowPass::setParameterCallback; + dsplowpass.getparameter = DSPLowPass::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsplowpass.getmemoryused = &DSPLowPass::getMemoryUsedCallback; +#endif + + dsplowpass.mType = FMOD_DSP_TYPE_LOWPASS; + dsplowpass.mCategory = FMOD_DSP_CATEGORY_FILTER; + dsplowpass.mSize = sizeof(DSPLowPass); +#else + dsplowpass.read = DSPLowPass::readCallback; /* We only care about read function on SPU */ +#endif + + return &dsplowpass; +} + +#ifndef PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass::createInternal() +{ + int count; + + init(); + + /* + Setup filter s-domain coefficients + */ + /* Section 1 */ + mProtoCoef[0].a0 = 1.0f; + mProtoCoef[0].a1 = 0; + mProtoCoef[0].a2 = 0; + mProtoCoef[0].b0 = 1.0f; + mProtoCoef[0].b1 = 0.765367f; + mProtoCoef[0].b2 = 1.0f; + mGainFactor[0] = 1.0f; + + /* Section 2 */ + mProtoCoef[1].a0 = 1.0f; + mProtoCoef[1].a1 = 0; + mProtoCoef[1].a2 = 0; + mProtoCoef[1].b0 = 1.0f; + mProtoCoef[1].b1 = 1.847759f; + mProtoCoef[1].b2 = 1.0f; + mGainFactor[1] = 1.0f; + + /* + Calculat the max cutoff rate + */ + int outputrate = 0; + FMOD_RESULT result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + mCutoffHzMaximum = ((float)outputrate / 2.0f) - 10.0f; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mResonance = mResonanceUpdate; + mCutoffHz = mCutoffHzUpdate; + + updateState(mResonance, mCutoffHz); + + return FMOD_OK; +} + +#endif + + +/* + * -------------------------------------------------------------------- + * + * iir_filter - Perform IIR filtering sample by sample on floats + * + * Implements cascaded direct form II second order sections. + * Requires FILTER structure for history and coefficients. + * The length in the filter structure specifies the number of sections. + * The size of the history array is 2 * LOWPASS_FILTER_SECTIONS. + * The size of the coefficient array is 4 * LOWPASS_FILTER_SECTIONS + 1 because + * the first coefficient is the overall scale factor for the filter. + * Returns one output sample for each input sample. Allocates history + * array if not previously allocated. + * + * float iir_filter(float input,FILTER *iir) + * + * float input new float input sample + * FILTER *iir pointer to FILTER structure + * + * Returns float value giving the current output. + * + * Allocation errors cause an error message and a call to exit. + * -------------------------------------------------------------------- + */ +FMOD_INLINE float DSPLowPass::filter(float input, int channel) +{ + int i; + float *history1,*history2,*coef_ptr; + float output,new_hist; + static float dc = (float)1E-20; + + input += dc; + dc = -dc; + + /* allocate history array if different size than last call */ + + coef_ptr = mCoefficients; /* coefficient pointer */ + + history1 = mHistory[channel]; /* first history */ + history2 = history1 + 1; /* next history */ + + /* 1st number of coefficients array is overall input scale factor, + * or filter gain */ + output = input * (*coef_ptr++); + + for (i = 0 ; i < LOWPASS_FILTER_SECTIONS; i++) + { + output = output - history1[0] * coef_ptr[0]; + new_hist = output - history2[0] * coef_ptr[1]; /* poles */ + + output = new_hist + history1[0] * coef_ptr[2]; + output = output + history2[0] * coef_ptr[3]; /* zeros */ + + coef_ptr += 4; + *history2++ = *history1; + *history1++ = new_hist; + history1++; + history2++; + } + + return(output); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass::process(float *inbuffer, float *outbuffer, unsigned int length, int channels) +{ + unsigned int count; + static float dc = (float)1E-20; + + if (channels == 1 && (speakermask & 1)) + { + float *history; + + history = mHistory[0]; + + while (length) + { + float a,b,c; + + a = ((inbuffer[0] + dc) * mCoefficients[0]) - (history[0] * mCoefficients[1]) - (history[1] * mCoefficients[2]); + b = (history[0] * mCoefficients[3]) + (history[1] * mCoefficients[4]) - (history[2] * mCoefficients[5]) - (history[3] * mCoefficients[6]); + c = (history[2] * mCoefficients[7]) + (history[3] * mCoefficients[8]); + + history[1] = history[0]; + history[3] = history[2]; + + dc = -dc; + length--; + inbuffer++; + outbuffer++; + + history[0] = a; + history[2] = a + b; + outbuffer[-1] = a + b + c; + } + } + else if (channels == 2 && ((speakermask & 0x03) == 0x03)) + { + float *historyL; + float *historyR; + + historyL = mHistory[0]; + historyR = mHistory[1]; + + while (length) + { + float aL,bL,cL; + float aR,bR,cR; + + aL = ((inbuffer[0] + dc) * mCoefficients[0]) - (historyL[0] * mCoefficients[1]) - (historyL[1] * mCoefficients[2]); + aR = ((inbuffer[1] + dc) * mCoefficients[0]) - (historyR[0] * mCoefficients[1]) - (historyR[1] * mCoefficients[2]); + bL = (historyL[0] * mCoefficients[3]) + (historyL[1] * mCoefficients[4]) - (historyL[2] * mCoefficients[5]) - (historyL[3] * mCoefficients[6]); + bR = (historyR[0] * mCoefficients[3]) + (historyR[1] * mCoefficients[4]) - (historyR[2] * mCoefficients[5]) - (historyR[3] * mCoefficients[6]); + cL = (historyL[2] * mCoefficients[7]) + (historyL[3] * mCoefficients[8]); + cR = (historyR[2] * mCoefficients[7]) + (historyR[3] * mCoefficients[8]); + + dc = -dc; + length--; + inbuffer+=2; + outbuffer+=2; + + historyL[1] = historyL[0]; + historyL[3] = historyL[2]; + historyL[0] = aL; + historyL[2] = aL + bL; + historyR[1] = historyR[0]; + historyR[3] = historyR[2]; + historyR[0] = aR; + historyR[2] = aR + bR; + + outbuffer[-2] = aL + bL + cL; + outbuffer[-1] = aR + bR + cR; + } + } + else if (channels == 6 && ((speakermask & 0x3F) == 0x3F)) + { + float *history0 = mHistory[0]; + float *history1 = mHistory[1]; + float *history2 = mHistory[2]; + float *history3 = mHistory[3]; + float *history4 = mHistory[4]; + float *history5 = mHistory[5]; + + while (length) + { + float a0,b0,c0; + float a1,b1,c1; + float a2,b2,c2; + float a3,b3,c3; + float a4,b4,c4; + float a5,b5,c5; + + a0 = ((inbuffer[0] + dc) * mCoefficients[0]) - (history0[0] * mCoefficients[1]) - (history0[1] * mCoefficients[2]); + a1 = ((inbuffer[1] + dc) * mCoefficients[0]) - (history1[0] * mCoefficients[1]) - (history1[1] * mCoefficients[2]); + a2 = ((inbuffer[2] + dc) * mCoefficients[0]) - (history2[0] * mCoefficients[1]) - (history2[1] * mCoefficients[2]); + a3 = ((inbuffer[3] + dc) * mCoefficients[0]) - (history3[0] * mCoefficients[1]) - (history3[1] * mCoefficients[2]); + a4 = ((inbuffer[4] + dc) * mCoefficients[0]) - (history4[0] * mCoefficients[1]) - (history4[1] * mCoefficients[2]); + a5 = ((inbuffer[5] + dc) * mCoefficients[0]) - (history5[0] * mCoefficients[1]) - (history5[1] * mCoefficients[2]); + b0 = (history0[0] * mCoefficients[3]) + (history0[1] * mCoefficients[4]) - (history0[2] * mCoefficients[5]) - (history0[3] * mCoefficients[6]); + b1 = (history1[0] * mCoefficients[3]) + (history1[1] * mCoefficients[4]) - (history1[2] * mCoefficients[5]) - (history1[3] * mCoefficients[6]); + b2 = (history2[0] * mCoefficients[3]) + (history2[1] * mCoefficients[4]) - (history2[2] * mCoefficients[5]) - (history2[3] * mCoefficients[6]); + b3 = (history3[0] * mCoefficients[3]) + (history3[1] * mCoefficients[4]) - (history3[2] * mCoefficients[5]) - (history3[3] * mCoefficients[6]); + b4 = (history4[0] * mCoefficients[3]) + (history4[1] * mCoefficients[4]) - (history4[2] * mCoefficients[5]) - (history4[3] * mCoefficients[6]); + b5 = (history5[0] * mCoefficients[3]) + (history5[1] * mCoefficients[4]) - (history5[2] * mCoefficients[5]) - (history5[3] * mCoefficients[6]); + c0 = (history0[2] * mCoefficients[7]) + (history0[3] * mCoefficients[8]); + c1 = (history1[2] * mCoefficients[7]) + (history1[3] * mCoefficients[8]); + c2 = (history2[2] * mCoefficients[7]) + (history2[3] * mCoefficients[8]); + c3 = (history3[2] * mCoefficients[7]) + (history3[3] * mCoefficients[8]); + c4 = (history4[2] * mCoefficients[7]) + (history4[3] * mCoefficients[8]); + c5 = (history5[2] * mCoefficients[7]) + (history5[3] * mCoefficients[8]); + + dc = -dc; + length--; + inbuffer+=6; + outbuffer+=6; + + history0[1] = history0[0]; + history0[3] = history0[2]; + history0[0] = a0; + history0[2] = a0 + b0; + history1[1] = history1[0]; + history1[3] = history1[2]; + history1[0] = a1; + history1[2] = a1 + b1; + history2[1] = history2[0]; + history2[3] = history2[2]; + history2[0] = a2; + history2[2] = a2 + b2; + history3[1] = history3[0]; + history3[3] = history3[2]; + history3[0] = a3; + history3[2] = a3 + b3; + history4[1] = history4[0]; + history4[3] = history4[2]; + history4[0] = a4; + history4[2] = a4 + b4; + history5[1] = history5[0]; + history5[3] = history5[2]; + history5[0] = a5; + history5[2] = a5 + b5; + + outbuffer[-6] = a0 + b0 + c0; + outbuffer[-5] = a1 + b1 + c1; + outbuffer[-4] = a2 + b2 + c2; + outbuffer[-3] = a3 + b3 + c3; + outbuffer[-2] = a4 + b4 + c4; + outbuffer[-1] = a5 + b5 + c5; + } + } + else if (channels == 8 && ((speakermask & 0xFF) == 0xFF)) + { + float *history0 = mHistory[0]; + float *history1 = mHistory[1]; + float *history2 = mHistory[2]; + float *history3 = mHistory[3]; + float *history4 = mHistory[4]; + float *history5 = mHistory[5]; + float *history6 = mHistory[6]; + float *history7 = mHistory[7]; + + while (length) + { + float a0,b0,c0; + float a1,b1,c1; + float a2,b2,c2; + float a3,b3,c3; + float a4,b4,c4; + float a5,b5,c5; + float a6,b6,c6; + float a7,b7,c7; + + a0 = ((inbuffer[0] + dc) * mCoefficients[0]) - (history0[0] * mCoefficients[1]) - (history0[1] * mCoefficients[2]); + a1 = ((inbuffer[1] + dc) * mCoefficients[0]) - (history1[0] * mCoefficients[1]) - (history1[1] * mCoefficients[2]); + a2 = ((inbuffer[2] + dc) * mCoefficients[0]) - (history2[0] * mCoefficients[1]) - (history2[1] * mCoefficients[2]); + a3 = ((inbuffer[3] + dc) * mCoefficients[0]) - (history3[0] * mCoefficients[1]) - (history3[1] * mCoefficients[2]); + a4 = ((inbuffer[4] + dc) * mCoefficients[0]) - (history4[0] * mCoefficients[1]) - (history4[1] * mCoefficients[2]); + a5 = ((inbuffer[5] + dc) * mCoefficients[0]) - (history5[0] * mCoefficients[1]) - (history5[1] * mCoefficients[2]); + a6 = ((inbuffer[6] + dc) * mCoefficients[0]) - (history6[0] * mCoefficients[1]) - (history6[1] * mCoefficients[2]); + a7 = ((inbuffer[7] + dc) * mCoefficients[0]) - (history7[0] * mCoefficients[1]) - (history7[1] * mCoefficients[2]); + b0 = (history0[0] * mCoefficients[3]) + (history0[1] * mCoefficients[4]) - (history0[2] * mCoefficients[5]) - (history0[3] * mCoefficients[6]); + b1 = (history1[0] * mCoefficients[3]) + (history1[1] * mCoefficients[4]) - (history1[2] * mCoefficients[5]) - (history1[3] * mCoefficients[6]); + b2 = (history2[0] * mCoefficients[3]) + (history2[1] * mCoefficients[4]) - (history2[2] * mCoefficients[5]) - (history2[3] * mCoefficients[6]); + b3 = (history3[0] * mCoefficients[3]) + (history3[1] * mCoefficients[4]) - (history3[2] * mCoefficients[5]) - (history3[3] * mCoefficients[6]); + b4 = (history4[0] * mCoefficients[3]) + (history4[1] * mCoefficients[4]) - (history4[2] * mCoefficients[5]) - (history4[3] * mCoefficients[6]); + b5 = (history5[0] * mCoefficients[3]) + (history5[1] * mCoefficients[4]) - (history5[2] * mCoefficients[5]) - (history5[3] * mCoefficients[6]); + b6 = (history6[0] * mCoefficients[3]) + (history6[1] * mCoefficients[4]) - (history6[2] * mCoefficients[5]) - (history6[3] * mCoefficients[6]); + b7 = (history7[0] * mCoefficients[3]) + (history7[1] * mCoefficients[4]) - (history7[2] * mCoefficients[5]) - (history7[3] * mCoefficients[6]); + c0 = (history0[2] * mCoefficients[7]) + (history0[3] * mCoefficients[8]); + c1 = (history1[2] * mCoefficients[7]) + (history1[3] * mCoefficients[8]); + c2 = (history2[2] * mCoefficients[7]) + (history2[3] * mCoefficients[8]); + c3 = (history3[2] * mCoefficients[7]) + (history3[3] * mCoefficients[8]); + c4 = (history4[2] * mCoefficients[7]) + (history4[3] * mCoefficients[8]); + c5 = (history5[2] * mCoefficients[7]) + (history5[3] * mCoefficients[8]); + c6 = (history6[2] * mCoefficients[7]) + (history6[3] * mCoefficients[8]); + c7 = (history7[2] * mCoefficients[7]) + (history7[3] * mCoefficients[8]); + + dc = -dc; + length--; + inbuffer+=8; + outbuffer+=8; + + history0[1] = history0[0]; + history0[3] = history0[2]; + history0[0] = a0; + history0[2] = a0 + b0; + history1[1] = history1[0]; + history1[3] = history1[2]; + history1[0] = a1; + history1[2] = a1 + b1; + history2[1] = history2[0]; + history2[3] = history2[2]; + history2[0] = a2; + history2[2] = a2 + b2; + history3[1] = history3[0]; + history3[3] = history3[2]; + history3[0] = a3; + history3[2] = a3 + b3; + history4[1] = history4[0]; + history4[3] = history4[2]; + history4[0] = a4; + history4[2] = a4 + b4; + history5[1] = history5[0]; + history5[3] = history5[2]; + history5[0] = a5; + history5[2] = a5 + b5; + history6[1] = history6[0]; + history6[3] = history6[2]; + history6[0] = a6; + history6[2] = a6 + b6; + history7[1] = history7[0]; + history7[3] = history7[2]; + history7[0] = a7; + history7[2] = a7 + b7; + + outbuffer[-8] = a0 + b0 + c0; + outbuffer[-7] = a1 + b1 + c1; + outbuffer[-6] = a2 + b2 + c2; + outbuffer[-5] = a3 + b3 + c3; + outbuffer[-4] = a4 + b4 + c4; + outbuffer[-3] = a5 + b5 + c5; + outbuffer[-2] = a6 + b6 + c6; + outbuffer[-1] = a7 + b7 + c7; + } + } + else + { + for (count = 0; count < (unsigned int)channels; count++) + { + unsigned int len; + int offset1, offset2, offset3, inc; + float *in = inbuffer + count; + float *out = outbuffer + count; + offset1 = channels; + offset2 = channels * 2; + offset3 = channels * 3; + inc = channels << 2; + len = length >> 2; + + if (!((1 << count) & speakermask)) + { + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + + len--; + in += inc; + out += inc; + } + + len = length & 3; + + while (len) + { + out[0] = in[0]; + len--; + in += channels; + out += channels; + } + continue; + } + else + { + while(len) + { + out[0] = filter(in[0], count); + out[offset1] = filter(in[offset1], count); + out[offset2] = filter(in[offset2], count); + out[offset3] = filter(in[offset3], count); + + out += inc; + in += inc; + len--; + } + + len = length & 3; + + while (len) + { + out[0] = filter(in[0],count); + len--; + in += channels; + out += channels; + } + } + } + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + if (!inbuffer) + { + return FMOD_OK; + } + + while (mResonance != mResonanceUpdate || mCutoffHz != mCutoffHzUpdate) + { + float cutoffdelta = 10.0f + (mCutoffHz / 100.0f); + + if (mResonance < mResonanceUpdate) + { + mResonance += 1.0f; + if (mResonance >= mResonanceUpdate) + { + mResonance = mResonanceUpdate; + } + } + else if (mResonance > mResonanceUpdate) + { + mResonance -= 1.0f; + if (mResonance <= mResonanceUpdate) + { + mResonance = mResonanceUpdate; + } + } + if (mCutoffHz < mCutoffHzUpdate) + { + mCutoffHz += cutoffdelta; + if (mCutoffHz >= mCutoffHzUpdate) + { + mCutoffHz = mCutoffHzUpdate; + } + } + else if (mCutoffHz > mCutoffHzUpdate) + { + mCutoffHz -= cutoffdelta; + if (mCutoffHz <= mCutoffHzUpdate) + { + mCutoffHz = mCutoffHzUpdate; + } + } + + updateState(mResonance, mCutoffHz); + + process(inbuffer, outbuffer, 1, inchannels); + + inbuffer+=inchannels; + outbuffer+=inchannels; + length--; + if (!length) + { + return FMOD_OK; + } + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + if (mCutoffHz >= mCutoffHzMaximum) + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + else if (mCutoffHz < 10.0f) + { + int count; + + FMOD_memset(outbuffer, 0, sizeof(float)*length*inchannels); + + for (count = 0; count < inchannels; count++) + { + for ( int i = 0; i < 2 * LOWPASS_FILTER_SECTIONS; i++ ) + { + mHistory[count][i] = 0; + } + } + return FMOD_OK; + } + + return process(inbuffer, outbuffer, length, inchannels); +} + + +/* + * ---------------------------------------------------------- + * bilinear.c + * + * Perform bilinear transformation on s-domain coefficients + * of 2nd order biquad section. + * First design an analog filter and use s-domain coefficients + * as input to szxform() to convert them to z-domain. + * + * Here's the butterworth polinomials for 2nd, 4th and 6th order sections. + * When we construct a 24 db/oct filter, we take to 2nd order + * sections and compute the coefficients separately for each section. + * + * n Polinomials + * -------------------------------------------------------------------- + * 2 s^2 + 1.4142s +1 + * 4 (s^2 + 0.765367s + 1) (s^2 + 1.847759s + 1) + * 6 (s^2 + 0.5176387s + 1) (s^2 + 1.414214 + 1) (s^2 + 1.931852s + 1) + * + * Where n is a filter order. + * For n=4, or two second order sections, we have following equasions for each + * 2nd order stage: + * + * (1 / (s^2 + (1/Q) * 0.765367s + 1)) * (1 / (s^2 + (1/Q) * 1.847759s + 1)) + * + * Where Q is filter quality factor in the range of + * 1 to 1000. The overall filter Q is a product of all + * 2nd order stages. For example, the 6th order filter + * (3 stages, or biquads) with individual Q of 2 will + * have filter Q = 2 * 2 * 2 = 8. + * + * The nominator part is just 1. + * The denominator coefficients for stage 1 of filter are: + * b2 = 1; b1 = 0.765367; b0 = 1; + * numerator is + * a2 = 0; a1 = 0; a0 = 1; + * + * The denominator coefficients for stage 1 of filter are: + * b2 = 1; b1 = 1.847759; b0 = 1; + * numerator is + * a2 = 0; a1 = 0; a0 = 1; + * + * These coefficients are used directly by the szxform() + * and bilinear() functions. For all stages the numerator + * is the same and the only thing that is different between + * different stages is 1st order coefficient. The rest of + * coefficients are the same for any stage and equal to 1. + * + * Any filter could be constructed using this approach. + * + * References: + * Van Valkenburg, "Analog Filter Design" + * Oxford University Press 1982 + * ISBN 0-19-510734-9 + * + * C Language Algorithms for Digital Signal Processing + * Paul Embree, Bruce Kimble + * Prentice Hall, 1991 + * ISBN 0-13-133406-9 + * + * Digital Filter Designer's Handbook + * With C++ Algorithms + * Britton Rorabaugh + * McGraw Hill, 1997 + * ISBN 0-07-053806-9 + * ---------------------------------------------------------- + */ + +/* + * ---------------------------------------------------------- + * Pre-warp the coefficients of a numerator or denominator. + * Note that a0 is assumed to be 1, so there is no wrapping + * of it. + * ---------------------------------------------------------- + */ +FMOD_RESULT DSPLowPass::prewarp(float *a0, float *a1, float *a2, float fc, float fs) +{ + float wp, pi; + + pi = 4.0f * FMOD_ATAN(1.0f); + wp = 2.0f * fs * FMOD_TAN(pi * fc / fs); + + *a2 = (*a2) / (wp * wp); + *a1 = (*a1) / wp; + + return FMOD_OK; +} + + +/* + * ---------------------------------------------------------- + * bilinear() + * + * Transform the numerator and denominator coefficients + * of s-domain biquad section into corresponding + * z-domain coefficients. + * + * Store the 4 IIR coefficients in array pointed by coef + * in following order: + * beta1, beta2 (denominator) + * alpha1, alpha2 (numerator) + * + * Arguments: + * a0-a2 - s-domain numerator coefficients + * b0-b2 - s-domain denominator coefficients + * k - filter gain factor. initially set to 1 + * and modified by each biquad section in such + * a way, as to make it the coefficient by + * which to multiply the overall filter gain + * in order to achieve a desired overall filter gain, + * specified in initial value of k. + * fs - sampling rate (Hz) + * coef - array of z-domain coefficients to be filled in. + * + * Return: + * On return, set coef z-domain coefficients + * ---------------------------------------------------------- + */ +FMOD_RESULT DSPLowPass::bilinear( + float a0, float a1, float a2, /* numerator coefficients */ + float b0, float b1, float b2, /* denominator coefficients */ + float *k, /* overall gain factor */ + float fs, /* sampling rate */ + float *coef /* pointer to 4 iir coefficients */ +) +{ + float ad, bd; + + /* alpha (Numerator in s-domain) */ + ad = 4.0f * a2 * fs * fs + 2.0f * a1 * fs + a0; + /* beta (Denominator in s-domain) */ + bd = 4.0f * b2 * fs * fs + 2.0f * b1* fs + b0; + + /* update gain constant for this section */ + *k *= ad/bd; + + /* Denominator */ + *coef++ = (float)((2.0f * b0 - 8.0f * b2 * fs * fs) / bd); /* beta1 */ + *coef++ = (float)((4.0f * b2 * fs * fs - 2.0f * b1 * fs + b0) / bd); /* beta2 */ + + /* Nominator */ + *coef++ = (float)((2.0f * a0 - 8.0f * a2 * fs * fs) / ad); /* alpha1 */ + *coef = (float)((4.0f * a2 * fs * fs - 2.0f * a1 * fs + a0) / ad); /* alpha2 */ + + return FMOD_OK; +} + + +/* + * ---------------------------------------------------------- + * Transform from s to z domain using bilinear transform + * with prewarp. + * + * Arguments: + * For argument description look at bilinear() + * + * coef - pointer to array of floating point coefficients, + * corresponding to output of bilinear transofrm + * (z domain). + * + * Note: frequencies are in Hz. + * ---------------------------------------------------------- + */ +FMOD_RESULT DSPLowPass::szxform( + float *a0, float *a1, float *a2, /* numerator coefficients */ + float *b0, float *b1, float *b2, /* denominator coefficients */ + float fc, /* Filter cutoff frequency */ + float fs, /* sampling rate */ + float *k, /* overall gain factor */ + float *coef) /* pointer to 4 iir coefficients */ +{ + /* Calculate a1 and a2 and overwrite the original values */ + prewarp(a0, a1, a2, fc, fs); + prewarp(b0, b1, b2, fc, fs); + bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef); + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass::setParameterInternal(int index, float value) +{ + if (index == FMOD_DSP_LOWPASS_CUTOFF) + { + mCutoffHzUpdate = value; + if (mCutoffHzUpdate >= mCutoffHzMaximum) + { + mCutoffHzUpdate = mCutoffHzMaximum; + } + } + else if (index == FMOD_DSP_LOWPASS_RESONANCE) + { + mResonanceUpdate = value; + } + + return FMOD_OK;; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_LOWPASS_CUTOFF: + { + *value = mCutoffHzUpdate; + sprintf(valuestr, "%.02f", mCutoffHzUpdate); + break; + } + case FMOD_DSP_LOWPASS_RESONANCE: + { + *value = mResonanceUpdate; + sprintf(valuestr, "%.02f", mResonanceUpdate); + break; + } + } + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass::updateState(float resonance, float cutoff) +{ + FMOD_RESULT result; + int outputrate; + +#ifdef PLATFORM_PS3_SPU + outputrate = 48000; +#else + result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } +#endif + + if (resonance >= 1.0f) + { + int nInd; + float a0, a1, a2, b0, b1, b2; + float fs; /* Sampling frequency, cutoff frequency */ + float k[2]; /* overall gain factor */ + float ktotal; + float *coef; + + k[0] = 1.0f; /* Set overall filter gain */ + k[1] = 1.0f; + ktotal = 1.0f; + coef = mCoefficients + 1; /* Skip k, or gain */ + fs = (float)outputrate; /* Sampling frequency (Hz) */ + + if (cutoff > fs / 2) + { + cutoff = (fs / 2) - 10.0f; /* Don't go above Nyquist Frequency */ + } + + /* + * Compute z-domain coefficients for each biquad section + * for new Cutoff Frequency and Resonance + */ + for (nInd = 0; nInd < LOWPASS_FILTER_SECTIONS; nInd++) + { + a0 = mProtoCoef[nInd].a0; + a1 = mProtoCoef[nInd].a1; + a2 = mProtoCoef[nInd].a2; + + b0 = mProtoCoef[nInd].b0; + b1 = mProtoCoef[nInd].b1 / resonance; /* Divide by resonance or Q */ + b2 = mProtoCoef[nInd].b2; + szxform(&a0, &a1, &a2, &b0, &b1, &b2, (float)cutoff, fs, &k[nInd], coef); + + // Scale the history by the ratio of current gain vs old gain + if (mGainFactor[nInd] != 0.0f) + { + for(int lout=0; lout < DSP_MAXLEVELS_MAX; lout++) + { + mHistory[lout][nInd] *= k[nInd]/mGainFactor[nInd]; + } + } + mGainFactor[nInd] = k[nInd]; + ktotal *= k[nInd]; + + coef += 4; /* Point to next filter section */ + } + + /* Update overall filter gain in coef array */ + mCoefficients[0] = ktotal; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPLowPass::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPLowPass *lowpass = (DSPLowPass *)dsp; + + return lowpass->createInternal(); +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPLowPass *lowpass = (DSPLowPass *)dsp; + + return lowpass->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPLowPass *lowpass = (DSPLowPass *)dsp; + + return lowpass->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPLowPass *lowpass = (DSPLowPass *)dsp; + + return lowpass->getParameterInternal(index, value, valuestr); +} + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPLowPass *lowpass = (DSPLowPass *)dsp; + + return lowpass->DSPLowPass::getMemoryUsed(tracker); +} +#endif + +#endif + +} + +#endif diff --git a/src/fmod_dsp_lowpass.h b/src/fmod_dsp_lowpass.h new file mode 100755 index 0000000..4c05ed7 --- /dev/null +++ b/src/fmod_dsp_lowpass.h @@ -0,0 +1,76 @@ +#ifndef _FMOD_DSP_LOWPASS_H +#define _FMOD_DSP_LOWPASS_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_LOWPASS + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + const int LOWPASS_FILTER_SECTIONS = 2; /* 2 filter sections for 24 db/oct filter */ + + typedef struct + { + float a0, a1, a2; /* numerator coefficients */ + float b0, b1, b2; /* denominator coefficients */ + } LOWPASS_BIQUAD; + + class DSPLowPass : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float FMOD_PPCALIGN16(mResonance); + float FMOD_PPCALIGN16(mResonanceUpdate); + float FMOD_PPCALIGN16(mCutoffHz); + float FMOD_PPCALIGN16(mCutoffHzUpdate); + float FMOD_PPCALIGN16(mCutoffHzMaximum); + float FMOD_PPCALIGN16(mGainFactor[LOWPASS_FILTER_SECTIONS]); + float FMOD_PPCALIGN16(mHistory[DSP_MAXLEVELS_MAX][2 * LOWPASS_FILTER_SECTIONS]); /* history in filter */ + float FMOD_PPCALIGN16(mCoefficients[4 * LOWPASS_FILTER_SECTIONS + 1]); /* pointer to coefficients of filter */ + LOWPASS_BIQUAD FMOD_PPCALIGN16(mProtoCoef[LOWPASS_FILTER_SECTIONS]); /* Filter prototype coefficients */ + + FMOD_INLINE float filter(float input, int channel); + FMOD_RESULT prewarp(float *a0, float *a1, float *a2, float fc, float fs); + FMOD_RESULT szxform(float *a0, float *a1, float *a2, /* numerator coefficients */ + float *b0, float *b1, float *b2, /* denominator coefficients */ + float fc, /* Filter cutoff frequency */ + float fs, /* sampling rate */ + float *k, /* overall gain factor */ + float *coef); /* pointer to 4 iir coefficients */ + FMOD_RESULT bilinear(float a0, float a1, float a2, /* numerator coefficients */ + float b0, float b1, float b2, /* denominator coefficients */ + float *k, /* overall gain factor */ + float fs, /* sampling rate */ + float *coef); /* pointer to 4 iir coefficients */ + + FMOD_RESULT createInternal(); + FMOD_RESULT process(float *inbuffer, float *outbuffer, unsigned int length, int channels); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + FMOD_RESULT updateState(float resonance, float cutoff); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK seekCallback(FMOD_DSP_STATE *dsp, unsigned int seeklen); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_lowpass2.cpp b/src/fmod_dsp_lowpass2.cpp new file mode 100755 index 0000000..1ddd08b --- /dev/null +++ b/src/fmod_dsp_lowpass2.cpp @@ -0,0 +1,836 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_LOWPASS2 + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_lowpass2.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> +#include <math.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_lowpass2_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsplowpass2; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPLowPass2::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dsplowpass2_param[2] = +{ + { 1.0f, 22000.0f, 5000.0f, "Cutoff freq", "hz", "Lowpass cutoff frequency in hz. 1.0 to 22000.0. Default = 5000.0" }, + { 1.0f, 127.0f, 1.0f, "Resonance", "", "Lowpass resonance Q value. 0.0 to 127.0. Default = 1.0" } +}; + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPLowPass2::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dsplowpass2, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsplowpass2.name, "FMOD IT Lowpass"); + dsplowpass2.version = 0x00010100; + dsplowpass2.create = DSPLowPass2::createCallback; + dsplowpass2.reset = DSPLowPass2::resetCallback; + + #ifdef PLATFORM_PS3 + dsplowpass2.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_lowpass2_pic_start; /* SPU PIC entry address */ + #else + dsplowpass2.read = DSPLowPass2::readCallback; + #endif + + dsplowpass2.numparameters = sizeof(dsplowpass2_param) / sizeof(dsplowpass2_param[0]); + dsplowpass2.paramdesc = dsplowpass2_param; + dsplowpass2.setparameter = DSPLowPass2::setParameterCallback; + dsplowpass2.getparameter = DSPLowPass2::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsplowpass2.getmemoryused = &DSPLowPass2::getMemoryUsedCallback; +#endif + + dsplowpass2.mType = FMOD_DSP_TYPE_ITLOWPASS; + dsplowpass2.mCategory = FMOD_DSP_CATEGORY_FILTER; + dsplowpass2.mSize = sizeof(DSPLowPass2); +#else + dsplowpass2.read = DSPLowPass2::readCallback; /* We only care about read function on SPU */ +#endif + return &dsplowpass2; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass2::createInternal() +{ + int count; + + init(); + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + resetInternal(); + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mResonance = mResonanceUpdate; + mCutoffHz = mCutoffHzUpdate; + updateCoefficients(mResonance, mCutoffHz); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass2::resetInternal() +{ + int count; + + for (count = 0; count < DSP_MAXLEVELS_MAX; count++) + { + mFilter_Y[count][0] = mFilter_Y[count][1] = 0; + } + + return FMOD_OK; +} + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass2::updateCoefficients(float resonance, float cutoff) +{ + int outputrate; + float fc, fs; + float fg, fb0, fb1; + +#ifdef PLATFORM_PS3_SPU + outputrate = 48000; +#else + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); +#endif + + fc = (float)cutoff; + fs = (float)outputrate; + + fc *= (float)(2.0f * 3.14159265358f / fs); + + float dmpfac = FMOD_POW(10.0f, -((24.0f / 128.0f) * resonance) / 20.0f); + float d = (1.0f - 2.0f * dmpfac) * fc; + + if (d > 2.0f) + { + d = 2.0f; + } + + d = (2.0f * dmpfac - d)/fc; + float e = FMOD_POW(1.0f / fc, 2.0f); + + fg = 1.0f / (1.0f + d + e); + fb0 = (d + e + e) / (1 + d + e); + fb1 = -e / (1 + d + e); + + if (fg < 0.00001f) + { + fg = 0; + } + + if (fb0 > 1.999f && fb1 < -0.999f) + { + fb0 = 2.0f; + fb1 = -1.0f; + } + + mFilter_A0 = fg; + mFilter_B0 = fb0; + mFilter_B1 = fb1; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass2::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + static float dc = (float)1E-20; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (mResonance != mResonanceUpdate || mCutoffHz != mCutoffHzUpdate) + { + mResonance = mResonanceUpdate; + mCutoffHz = mCutoffHzUpdate; + updateCoefficients(mResonance, mCutoffHz); + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No speaker channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + if (mFilter_A0 == 0.0f && mFilter_B0 == 2.0f && mFilter_B1 == -1.0f) + { + int count; + + FMOD_memset(outbuffer, 0, sizeof(float)*length*inchannels); + + for (count = 0; count < inchannels; count++) + { + mFilter_Y[count][0] = 0.0f; + mFilter_Y[count][1] = 0.0f; + } + return FMOD_OK; + } + + if (inchannels == 1 && (speakermask & 1)) + { + float fy1 = mFilter_Y[0][0]; + float fy2 = mFilter_Y[0][1]; + + while (length) + { + outbuffer[0] = (inbuffer[0] + dc) * mFilter_A0 + fy1 * mFilter_B0 + fy2 * mFilter_B1; + fy2 = fy1; + fy1 = outbuffer[0]; + + dc = -dc; + length--; + outbuffer++; + inbuffer++; + } + + mFilter_Y[0][0] = fy1; + mFilter_Y[0][1] = fy2; + } + else if (inchannels == 2 && (speakermask & 0x3) == 0x3) + { + float fyL1 = mFilter_Y[0][0]; + float fyL2 = mFilter_Y[0][1]; + float fyR1 = mFilter_Y[1][0]; + float fyR2 = mFilter_Y[1][1]; + + while (length) + { + float fyL,fyR; + + fyL = (inbuffer[0] + dc) * mFilter_A0 + fyL1 * mFilter_B0 + fyL2 * mFilter_B1; + fyR = (inbuffer[1] + dc) * mFilter_A0 + fyR1 * mFilter_B0 + fyR2 * mFilter_B1; + fyL2 = fyL1; + fyL1 = fyL; + fyR2 = fyR1; + fyR1 = fyR; + + + outbuffer[0] = fyL; + outbuffer[1] = fyR; + + dc = -dc; + length--; + outbuffer+=2; + inbuffer+=2; + } + + mFilter_Y[0][0] = fyL1; + mFilter_Y[0][1] = fyL2; + mFilter_Y[1][0] = fyR1; + mFilter_Y[1][1] = fyR2; + } + else if (inchannels == 6 && (speakermask & 0x3F) == 0x3F ) + { + float fyA1 = mFilter_Y[0][0]; + float fyA2 = mFilter_Y[0][1]; + float fyB1 = mFilter_Y[1][0]; + float fyB2 = mFilter_Y[1][1]; + float fyC1 = mFilter_Y[2][0]; + float fyC2 = mFilter_Y[2][1]; + float fyD1 = mFilter_Y[3][0]; + float fyD2 = mFilter_Y[3][1]; + float fyE1 = mFilter_Y[4][0]; + float fyE2 = mFilter_Y[4][1]; + float fyF1 = mFilter_Y[5][0]; + float fyF2 = mFilter_Y[5][1]; + + while (length) + { + float fyA,fyB,fyC,fyD,fyE,fyF; + + fyA = (inbuffer[0] + dc) * mFilter_A0 + fyA1 * mFilter_B0 + fyA2 * mFilter_B1; + fyB = (inbuffer[1] + dc) * mFilter_A0 + fyB1 * mFilter_B0 + fyB2 * mFilter_B1; + fyC = (inbuffer[2] + dc) * mFilter_A0 + fyC1 * mFilter_B0 + fyC2 * mFilter_B1; + fyD = (inbuffer[3] + dc) * mFilter_A0 + fyD1 * mFilter_B0 + fyD2 * mFilter_B1; + fyE = (inbuffer[4] + dc) * mFilter_A0 + fyE1 * mFilter_B0 + fyE2 * mFilter_B1; + fyF = (inbuffer[5] + dc) * mFilter_A0 + fyF1 * mFilter_B0 + fyF2 * mFilter_B1; + fyA2 = fyA1; + fyA1 = fyA; + fyB2 = fyB1; + fyB1 = fyB; + fyC2 = fyC1; + fyC1 = fyC; + fyD2 = fyD1; + fyD1 = fyD; + fyE2 = fyE1; + fyE1 = fyE; + fyF2 = fyF1; + fyF1 = fyF; + outbuffer[0] = fyA; + outbuffer[1] = fyB; + outbuffer[2] = fyC; + outbuffer[3] = fyD; + outbuffer[4] = fyE; + outbuffer[5] = fyF; + + dc = -dc; + length--; + outbuffer+=6; + inbuffer+=6; + } + + mFilter_Y[0][0] = fyA1; + mFilter_Y[0][1] = fyA2; + mFilter_Y[1][0] = fyB1; + mFilter_Y[1][1] = fyB2; + mFilter_Y[2][0] = fyC1; + mFilter_Y[2][1] = fyC2; + mFilter_Y[3][0] = fyD1; + mFilter_Y[3][1] = fyD2; + mFilter_Y[4][0] = fyE1; + mFilter_Y[4][1] = fyE2; + mFilter_Y[5][0] = fyF1; + mFilter_Y[5][1] = fyF2; + } + else if (inchannels == 8 && (speakermask & 0xFF) == 0xFF ) + { + float fyA1 = mFilter_Y[0][0]; + float fyA2 = mFilter_Y[0][1]; + float fyB1 = mFilter_Y[1][0]; + float fyB2 = mFilter_Y[1][1]; + float fyC1 = mFilter_Y[2][0]; + float fyC2 = mFilter_Y[2][1]; + float fyD1 = mFilter_Y[3][0]; + float fyD2 = mFilter_Y[3][1]; + float fyE1 = mFilter_Y[4][0]; + float fyE2 = mFilter_Y[4][1]; + float fyF1 = mFilter_Y[5][0]; + float fyF2 = mFilter_Y[5][1]; + float fyG1 = mFilter_Y[6][0]; + float fyG2 = mFilter_Y[6][1]; + float fyH1 = mFilter_Y[7][0]; + float fyH2 = mFilter_Y[7][1]; + + while (length) + { + float fyA,fyB,fyC,fyD,fyE,fyF,fyG,fyH; + + fyA = (inbuffer[0] + dc) * mFilter_A0 + fyA1 * mFilter_B0 + fyA2 * mFilter_B1; + fyB = (inbuffer[1] + dc) * mFilter_A0 + fyB1 * mFilter_B0 + fyB2 * mFilter_B1; + fyC = (inbuffer[2] + dc) * mFilter_A0 + fyC1 * mFilter_B0 + fyC2 * mFilter_B1; + fyD = (inbuffer[3] + dc) * mFilter_A0 + fyD1 * mFilter_B0 + fyD2 * mFilter_B1; + fyE = (inbuffer[4] + dc) * mFilter_A0 + fyE1 * mFilter_B0 + fyE2 * mFilter_B1; + fyF = (inbuffer[5] + dc) * mFilter_A0 + fyF1 * mFilter_B0 + fyF2 * mFilter_B1; + fyG = (inbuffer[6] + dc) * mFilter_A0 + fyG1 * mFilter_B0 + fyG2 * mFilter_B1; + fyH = (inbuffer[7] + dc) * mFilter_A0 + fyH1 * mFilter_B0 + fyH2 * mFilter_B1; + fyA2 = fyA1; + fyA1 = fyA; + fyB2 = fyB1; + fyB1 = fyB; + fyC2 = fyC1; + fyC1 = fyC; + fyD2 = fyD1; + fyD1 = fyD; + fyE2 = fyE1; + fyE1 = fyE; + fyF2 = fyF1; + fyF1 = fyF; + fyG2 = fyG1; + fyG1 = fyG; + fyH2 = fyH1; + fyH1 = fyH; + outbuffer[0] = fyA; + outbuffer[1] = fyB; + outbuffer[2] = fyC; + outbuffer[3] = fyD; + outbuffer[4] = fyE; + outbuffer[5] = fyF; + outbuffer[6] = fyG; + outbuffer[7] = fyH; + + dc = -dc; + length--; + outbuffer+=8; + inbuffer+=8; + } + + mFilter_Y[0][0] = fyA1; + mFilter_Y[0][1] = fyA2; + mFilter_Y[1][0] = fyB1; + mFilter_Y[1][1] = fyB2; + mFilter_Y[2][0] = fyC1; + mFilter_Y[2][1] = fyC2; + mFilter_Y[3][0] = fyD1; + mFilter_Y[3][1] = fyD2; + mFilter_Y[4][0] = fyE1; + mFilter_Y[4][1] = fyE2; + mFilter_Y[5][0] = fyF1; + mFilter_Y[5][1] = fyF2; + mFilter_Y[6][0] = fyG1; + mFilter_Y[6][1] = fyG2; + mFilter_Y[7][0] = fyH1; + mFilter_Y[7][1] = fyH2; + } + else + { + int count2; + + for (count2 = 0; count2 < inchannels; count2++) + { + float *in = inbuffer + count2; + float *out = outbuffer + count2; + int len; + + if (!((1<<count2) & speakermask)) //DSP effect is not active on the current speaker so: out = in + { + int inc; + int offset1, offset2, offset3; + + offset1 = inchannels; + offset2 = inchannels * 2; + offset3 = inchannels * 3; + + len = length >> 2; + inc = inchannels << 2; + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + + len--; + in += inc; + out += inc; + } + + len = length & 3; + while (len) + { + out[0] = in[0]; + len--; + in += inchannels; + out += inchannels; + } + continue; + } + else //DSP effect is active on the current speaker so: out = lowpass(in) + { + float fy1 = mFilter_Y[count2][0]; + float fy2 = mFilter_Y[count2][1]; + len = length; + while (len) + { + out[0] = (in[0] + dc) * mFilter_A0 + fy1 * mFilter_B0 + fy2 * mFilter_B1; + fy2 = fy1; + fy1 = out[0]; + + dc = -dc; + len--; + in += inchannels; + out += inchannels; + } + + mFilter_Y[count2][0] = fy1; + mFilter_Y[count2][1] = fy2; + } + } + } + + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass2::setParameterInternal(int index, float value) +{ + switch (index) + { + case FMOD_DSP_LOWPASS_CUTOFF: + { + mCutoffHzUpdate = value; + break; + } + case FMOD_DSP_LOWPASS_RESONANCE: + { + mResonanceUpdate = value; + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPass2::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_LOWPASS_CUTOFF: + { + *value = mCutoffHzUpdate; + sprintf(valuestr, "%.02f", mCutoffHzUpdate); + break; + } + case FMOD_DSP_LOWPASS_RESONANCE: + { + *value = mResonanceUpdate; + sprintf(valuestr, "%.02f", mResonanceUpdate); + break; + } + } + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPLowPass2::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass2::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPLowPass2 *lowpass2 = (DSPLowPass2 *)dsp; + + return lowpass2->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass2::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPLowPass2 *lowpass2 = (DSPLowPass2 *)dsp; + + return lowpass2->resetInternal(); +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass2::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPLowPass2 *lowpass2 = (DSPLowPass2 *)dsp; + + return lowpass2->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass2::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPLowPass2 *lowpass2 = (DSPLowPass2 *)dsp; + + return lowpass2->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass2::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPLowPass2 *lowpass2 = (DSPLowPass2 *)dsp; + + return lowpass2->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPass2::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPLowPass2 *lowpass2 = (DSPLowPass2 *)dsp; + + return lowpass2->DSPLowPass2::getMemoryUsed(tracker); +} +#endif + +#endif //!PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_lowpass2.h b/src/fmod_dsp_lowpass2.h new file mode 100755 index 0000000..73cf0ca --- /dev/null +++ b/src/fmod_dsp_lowpass2.h @@ -0,0 +1,53 @@ +#ifndef _FMOD_DSP_LOWPASS2_H +#define _FMOD_DSP_LOWPASS2_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_LOWPASS2 + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPLowPass2 : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float mResonance; + float mResonanceUpdate; + float mCutoffHz; + float mCutoffHzUpdate; + + float mFilter_Y[DSP_MAXLEVELS_MAX][2]; + float mFilter_A0, mFilter_B0, mFilter_B1; + + FMOD_RESULT createInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + FMOD_RESULT updateCoefficients(float resonance, float cutoff); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_lowpass_simple.cpp b/src/fmod_dsp_lowpass_simple.cpp new file mode 100755 index 0000000..13ea321 --- /dev/null +++ b/src/fmod_dsp_lowpass_simple.cpp @@ -0,0 +1,849 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_LOWPASS_SIMPLE + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_lowpass_simple.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include "fmod_common_spu.h" + #include "fmod_spu_printf.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> +#include <math.h> + +#define CUTOFF_MIN 10.0f +#define CUTOFF_MAX 22000.0f + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_lowpass_simple_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsplowpass_simple; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPLowPassSimple::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU_PIC + +FMOD_DSP_PARAMETERDESC dsplowpass_simple_param[1] = +{ + { CUTOFF_MIN, CUTOFF_MAX, 5000.0f, "Cutoff freq", "hz", "Lowpass cutoff frequency in hz. 1.0 to 22000.0. Default = 5000.0" }, +}; + +#endif // PLATFORM_PS3_SPU_PIC + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPLowPassSimple::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dsplowpass_simple, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsplowpass_simple.name, "FMOD Lowpass Simple"); + dsplowpass_simple.version = 0x00010100; + dsplowpass_simple.create = DSPLowPassSimple::createCallback; + dsplowpass_simple.reset = DSPLowPassSimple::resetCallback; + + #ifdef PLATFORM_PS3 + dsplowpass_simple.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_lowpass_simple_pic_start; /* SPU PIC entry address */ + #else + dsplowpass_simple.read = DSPLowPassSimple::readCallback; + #endif + + dsplowpass_simple.numparameters = sizeof(dsplowpass_simple_param) / sizeof(dsplowpass_simple_param[0]); + dsplowpass_simple.paramdesc = dsplowpass_simple_param; + dsplowpass_simple.setparameter = DSPLowPassSimple::setParameterCallback; + dsplowpass_simple.getparameter = DSPLowPassSimple::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsplowpass_simple.getmemoryused = &DSPLowPassSimple::getMemoryUsedCallback; +#endif + + dsplowpass_simple.mType = FMOD_DSP_TYPE_LOWPASS_SIMPLE; + dsplowpass_simple.mCategory = FMOD_DSP_CATEGORY_FILTER; + dsplowpass_simple.mSize = sizeof(DSPLowPassSimple); +#else + dsplowpass_simple.read = DSPLowPassSimple::readCallback; /* We only care about read function on SPU */ +#endif + return &dsplowpass_simple; +} + + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPassSimple::createInternal() +{ + int count; + + init(); + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + resetInternal(); + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mCutoffHz = mCutoffHzUpdate; + updateCoefficients(mCutoffHz); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPassSimple::resetInternal() +{ + int count; + + for (count = 0; count < DSP_MAXLEVELS_MAX; count++) + { + mFilter_Y[count][0] = mFilter_Y[count][1] = 0; + } + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPassSimple::updateCoefficients(float cutoffhz) +{ + /* + Simple RC based time constant method H(z) = tc / (1 - (1-tc)z^-1) + Single pole {0 -> 0.99..} on real axis + */ + int outputrate; + float RC; + float dt; + float threshold; + +#ifdef PLATFORM_PS3_SPU + outputrate = 48000; +#else + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); +#endif + + dt = 1.0f/outputrate; + threshold = outputrate/3.14159265358979323846f; + + if (cutoffhz >= CUTOFF_MAX) + { + mFilter_tc = 1.0f; + mFilter_oneminus_tc = 0.0f; + } + else if (cutoffhz <= threshold) + { + RC = 1.0f / (2.0f * FMOD_PI * mCutoffHz); + mFilter_tc = dt / (RC + dt); + mFilter_oneminus_tc = 1.0f - mFilter_tc; + } + else + { + mFilter_tc = 0.666666667f + (cutoffhz-threshold)/(3.0f*(CUTOFF_MAX - threshold)); + mFilter_oneminus_tc = 1.0f - mFilter_tc; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPassSimple::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + static float dc = (float)1E-20; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (mCutoffHz != mCutoffHzUpdate) + { + mCutoffHz = mCutoffHzUpdate; + updateCoefficients(mCutoffHz); + } + + if (mFilter_tc == 1.0f || !(speakermask & ((1 << inchannels)-1))) + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + else if (mFilter_tc == 0.0f) + { + int count; + + FMOD_memset(outbuffer, 0, sizeof(float)*length*inchannels); + + for (count = 0; count < inchannels; count++) + { + mFilter_Y[count][0] = mFilter_Y[count][1] = 0; + } + return FMOD_OK; + } + + if (inchannels == 1 && (speakermask & 1)) + { + float fy1 = mFilter_Y[0][0]; + float fy2 = mFilter_Y[0][1]; + float fy; + + while (length) + { + fy = mFilter_tc * (inbuffer[0]+dc) + mFilter_oneminus_tc * fy1; + fy1 = fy; + fy = mFilter_tc * fy + mFilter_oneminus_tc * fy2; + fy2 = fy; + outbuffer[0] = fy; + + dc = -dc; + length--; + outbuffer++; + inbuffer++; + } + + mFilter_Y[0][0] = fy1; + mFilter_Y[0][1] = fy2; + } + else if (inchannels == 2 && ((speakermask & 0x03) == 0x03)) + { + float fyL1 = mFilter_Y[0][0]; + float fyL2 = mFilter_Y[0][1]; + float fyR1 = mFilter_Y[1][0]; + float fyR2 = mFilter_Y[1][1]; + + float fyL, fyR; + while (length) + { + fyL = mFilter_tc * (inbuffer[0]+dc) + mFilter_oneminus_tc * fyL1; + fyR = mFilter_tc * (inbuffer[1]+dc) + mFilter_oneminus_tc * fyR1; + + fyL1 = fyL; + fyR1 = fyR; + + fyL = mFilter_tc * fyL + mFilter_oneminus_tc * fyL2; + fyR = mFilter_tc * fyR + mFilter_oneminus_tc * fyR2; + + fyL2 = fyL; + fyR2 = fyR; + + outbuffer[0] = fyL; + outbuffer[1] = fyR; + + dc = -dc; + length--; + outbuffer+=2; + inbuffer+=2; + } + + mFilter_Y[0][0] = fyL1; + mFilter_Y[0][1] = fyL2; + mFilter_Y[1][0] = fyR1; + mFilter_Y[1][1] = fyR2; + } + else if (inchannels == 6 && ((speakermask & 0x3F) == 0x3F)) + { + float fyA1 = mFilter_Y[0][0]; + float fyA2 = mFilter_Y[0][1]; + float fyB1 = mFilter_Y[1][0]; + float fyB2 = mFilter_Y[1][1]; + float fyC1 = mFilter_Y[2][0]; + float fyC2 = mFilter_Y[2][1]; + float fyD1 = mFilter_Y[3][0]; + float fyD2 = mFilter_Y[3][1]; + float fyE1 = mFilter_Y[4][0]; + float fyE2 = mFilter_Y[4][1]; + float fyF1 = mFilter_Y[5][0]; + float fyF2 = mFilter_Y[5][1]; + + float fyA,fyB,fyC,fyD,fyE,fyF; + while (length) + { + + fyA = mFilter_tc * (inbuffer[0]+dc) + mFilter_oneminus_tc * fyA1; + fyB = mFilter_tc * (inbuffer[1]+dc) + mFilter_oneminus_tc * fyB1; + fyC = mFilter_tc * (inbuffer[2]+dc) + mFilter_oneminus_tc * fyC1; + fyD = mFilter_tc * (inbuffer[3]+dc) + mFilter_oneminus_tc * fyD1; + fyE = mFilter_tc * (inbuffer[4]+dc) + mFilter_oneminus_tc * fyE1; + fyF = mFilter_tc * (inbuffer[5]+dc) + mFilter_oneminus_tc * fyF1; + + fyA1 = fyA; + fyB1 = fyB; + fyC1 = fyC; + fyD1 = fyD; + fyE1 = fyE; + fyF1 = fyF; + + fyA = mFilter_tc * fyA + mFilter_oneminus_tc * fyA2; + fyB = mFilter_tc * fyB + mFilter_oneminus_tc * fyB2; + fyC = mFilter_tc * fyC + mFilter_oneminus_tc * fyC2; + fyD = mFilter_tc * fyD + mFilter_oneminus_tc * fyD2; + fyE = mFilter_tc * fyE + mFilter_oneminus_tc * fyE2; + fyF = mFilter_tc * fyF + mFilter_oneminus_tc * fyF2; + + fyA2 = fyA; + fyB2 = fyB; + fyC2 = fyC; + fyD2 = fyD; + fyE2 = fyE; + fyF2 = fyF; + + outbuffer[0] = fyA; + outbuffer[1] = fyB; + outbuffer[2] = fyC; + outbuffer[3] = fyD; + outbuffer[4] = fyE; + outbuffer[5] = fyF; + + dc = -dc; + length--; + outbuffer+=6; + inbuffer+=6; + } + + mFilter_Y[0][0] = fyA1; + mFilter_Y[0][1] = fyA2; + mFilter_Y[1][0] = fyB1; + mFilter_Y[1][1] = fyB2; + mFilter_Y[2][0] = fyC1; + mFilter_Y[2][1] = fyC2; + mFilter_Y[3][0] = fyD1; + mFilter_Y[3][1] = fyD2; + mFilter_Y[4][0] = fyE1; + mFilter_Y[4][1] = fyE2; + mFilter_Y[5][0] = fyF1; + mFilter_Y[5][1] = fyF2; + } + else if (inchannels == 8 && ((speakermask & 0xFF) == 0xFF)) + { + float fyA1 = mFilter_Y[0][0]; + float fyA2 = mFilter_Y[0][1]; + float fyB1 = mFilter_Y[1][0]; + float fyB2 = mFilter_Y[1][1]; + float fyC1 = mFilter_Y[2][0]; + float fyC2 = mFilter_Y[2][1]; + float fyD1 = mFilter_Y[3][0]; + float fyD2 = mFilter_Y[3][1]; + float fyE1 = mFilter_Y[4][0]; + float fyE2 = mFilter_Y[4][1]; + float fyF1 = mFilter_Y[5][0]; + float fyF2 = mFilter_Y[5][1]; + float fyG1 = mFilter_Y[6][0]; + float fyG2 = mFilter_Y[6][1]; + float fyH1 = mFilter_Y[7][0]; + float fyH2 = mFilter_Y[7][1]; + + float fyA,fyB,fyC,fyD,fyE,fyF,fyG,fyH; + while (length) + { + + fyA = mFilter_tc * (inbuffer[0]+dc) + mFilter_oneminus_tc * fyA1; + fyB = mFilter_tc * (inbuffer[1]+dc) + mFilter_oneminus_tc * fyB1; + fyC = mFilter_tc * (inbuffer[2]+dc) + mFilter_oneminus_tc * fyC1; + fyD = mFilter_tc * (inbuffer[3]+dc) + mFilter_oneminus_tc * fyD1; + fyE = mFilter_tc * (inbuffer[4]+dc) + mFilter_oneminus_tc * fyE1; + fyF = mFilter_tc * (inbuffer[5]+dc) + mFilter_oneminus_tc * fyF1; + fyG = mFilter_tc * (inbuffer[6]+dc) + mFilter_oneminus_tc * fyG1; + fyH = mFilter_tc * (inbuffer[7]+dc) + mFilter_oneminus_tc * fyH1; + + fyA1 = fyA; + fyB1 = fyB; + fyC1 = fyC; + fyD1 = fyD; + fyE1 = fyE; + fyF1 = fyF; + fyG1 = fyG; + fyH1 = fyH; + + fyA = mFilter_tc * fyA + mFilter_oneminus_tc * fyA2; + fyB = mFilter_tc * fyB + mFilter_oneminus_tc * fyB2; + fyC = mFilter_tc * fyC + mFilter_oneminus_tc * fyC2; + fyD = mFilter_tc * fyD + mFilter_oneminus_tc * fyD2; + fyE = mFilter_tc * fyE + mFilter_oneminus_tc * fyE2; + fyF = mFilter_tc * fyF + mFilter_oneminus_tc * fyF2; + fyG = mFilter_tc * fyG + mFilter_oneminus_tc * fyG2; + fyH = mFilter_tc * fyH + mFilter_oneminus_tc * fyH2; + + fyA2 = fyA; + fyB2 = fyB; + fyC2 = fyC; + fyD2 = fyD; + fyE2 = fyE; + fyF2 = fyF; + fyG2 = fyG; + fyH2 = fyH; + + outbuffer[0] = fyA; + outbuffer[1] = fyB; + outbuffer[2] = fyC; + outbuffer[3] = fyD; + outbuffer[4] = fyE; + outbuffer[5] = fyF; + outbuffer[6] = fyG; + outbuffer[7] = fyH; + + dc = -dc; + length--; + outbuffer+=8; + inbuffer+=8; + } + + mFilter_Y[0][0] = fyA1; + mFilter_Y[0][1] = fyA2; + mFilter_Y[1][0] = fyB1; + mFilter_Y[1][1] = fyB2; + mFilter_Y[2][0] = fyC1; + mFilter_Y[2][1] = fyC2; + mFilter_Y[3][0] = fyD1; + mFilter_Y[3][1] = fyD2; + mFilter_Y[4][0] = fyE1; + mFilter_Y[4][1] = fyE2; + mFilter_Y[5][0] = fyF1; + mFilter_Y[5][1] = fyF2; + mFilter_Y[6][0] = fyG1; + mFilter_Y[6][1] = fyG2; + mFilter_Y[7][0] = fyH1; + mFilter_Y[7][1] = fyH2; + } + else + { + int count2; + + for (count2 = 0; count2 < inchannels; count2++) + { + float *in = inbuffer + count2; + float *out = outbuffer + count2; + int len; + + + if (!((1 << count2) & speakermask)) + { + int inc; + int offset1, offset2, offset3; + + offset1 = inchannels; + offset2 = inchannels * 2; + offset3 = inchannels * 3; + + len = length >> 2; + inc = inchannels << 2; + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + + len--; + in += inc; + out += inc; + } + + len = length & 3; + while (len) + { + out[0] = in[0]; + len--; + in += inchannels; + out += inchannels; + } + continue; + } + else + { + float fy1 = mFilter_Y[count2][0]; + float fy2 = mFilter_Y[count2][1]; + float fy; + len = length; + while (len) + { + fy = mFilter_tc * (in[0]+dc) + mFilter_oneminus_tc * fy1; + fy1 = fy; + fy = mFilter_tc * fy + mFilter_oneminus_tc * fy2; + fy2 = fy; + out[0] = fy; + + dc = -dc; + len--; + in += inchannels; + out += inchannels; + } + + mFilter_Y[count2][0] = fy1; + mFilter_Y[count2][1] = fy2; + } + } + } + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPassSimple::setParameterInternal(int index, float value) +{ + switch (index) + { + case FMOD_DSP_LOWPASS_SIMPLE_CUTOFF: + { + mCutoffHzUpdate = value; + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPLowPassSimple::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_LOWPASS_CUTOFF: + { + *value = mCutoffHzUpdate; + sprintf(valuestr, "%.02f", mCutoffHzUpdate); + break; + } + } + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPLowPassSimple::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPassSimple::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPLowPassSimple *lowpass_simple = (DSPLowPassSimple *)dsp; + + return lowpass_simple->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPassSimple::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPLowPassSimple *lowpass_simple = (DSPLowPassSimple *)dsp; + + return lowpass_simple->resetInternal(); +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPassSimple::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPLowPassSimple *lowpass_simple = (DSPLowPassSimple *)dsp; + + return lowpass_simple->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPassSimple::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPLowPassSimple *lowpass_simple = (DSPLowPassSimple *)dsp; + + return lowpass_simple->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPassSimple::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPLowPassSimple *lowpass_simple = (DSPLowPassSimple *)dsp; + + return lowpass_simple->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPLowPassSimple::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPLowPassSimple *lowpasssimple = (DSPLowPassSimple *)dsp; + + return lowpasssimple->DSPLowPassSimple::getMemoryUsed(tracker); +} +#endif + +#endif + +} + +#endif + diff --git a/src/fmod_dsp_lowpass_simple.h b/src/fmod_dsp_lowpass_simple.h new file mode 100755 index 0000000..a4179f7 --- /dev/null +++ b/src/fmod_dsp_lowpass_simple.h @@ -0,0 +1,50 @@ +#ifndef _FMOD_DSP_LOWPASS_SIMPLE_H +#define _FMOD_DSP_LOWPASS_SIMPLE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_LOWPASS_SIMPLE + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPLowPassSimple : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + float mCutoffHz; + float mCutoffHzUpdate; + + float mFilter_Y[DSP_MAXLEVELS_MAX][2]; + float mFilter_tc, mFilter_oneminus_tc; + + FMOD_RESULT createInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + FMOD_RESULT updateCoefficients(float cutoffhz); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_normalize.cpp b/src/fmod_dsp_normalize.cpp new file mode 100755 index 0000000..eef2ae8 --- /dev/null +++ b/src/fmod_dsp_normalize.cpp @@ -0,0 +1,586 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NORMALIZE + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_normalize.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_normalize_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspnormalize; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPNormalize::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dspnormalize_param[3] = +{ + { 0.0f, 20000.0f, 5000.0f, "Fade in time", "seconds", "Time to ramp the silence to full in ms. 0.0 to 20000.0. Default = 5000.0." }, + { 0.0f, 1.0f, 0.1f, "Lowest volume", "", "Lower volume range threshold to ignore. 0.0 to 1.0. Default = 0.1. Raise higher to stop amplification of very quiet signals." }, + { 0.0f, 100000.0f, 20.0f, "Maximum amp", "x", "Maximum amplification allowed. 1.0 to 100000.0. Default = 20.0. 1.0 = no amplifaction, higher values allow more boost." }, +}; + +#endif //PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPNormalize::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dspnormalize, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspnormalize.name, "FMOD Normalize"); + dspnormalize.version = 0x00010100; + dspnormalize.create = DSPNormalize::createCallback; + dspnormalize.release = DSPNormalize::releaseCallback; + dspnormalize.reset = DSPNormalize::resetCallback; + + #ifdef PLATFORM_PS3 + dspnormalize.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_normalize_pic_start; /* SPU PIC entry address */ + #else + dspnormalize.read = DSPNormalize::readCallback; + #endif + + dspnormalize.numparameters = sizeof(dspnormalize_param) / sizeof(dspnormalize_param[0]); + dspnormalize.paramdesc = dspnormalize_param; + dspnormalize.setparameter = DSPNormalize::setParameterCallback; + dspnormalize.getparameter = DSPNormalize::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspnormalize.getmemoryused = &DSPNormalize::getMemoryUsedCallback; +#endif + + dspnormalize.mType = FMOD_DSP_TYPE_NORMALIZE; + dspnormalize.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspnormalize.mSize = sizeof(DSPNormalize); +#else + dspnormalize.read = DSPNormalize::readCallback; /* We only care about read function on SPU */ +#endif + return &dspnormalize; +} + +#ifndef PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPNormalize::createInternal() +{ + FMOD_RESULT result; + int count; + + init(); + + mMaximum = mTargetMaximum = 1.0f; + + result = mSystem->getSoftwareFormat(&mOutputRate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mDescription.numparameters; count++) + { + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPNormalize::releaseInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPNormalize::resetInternal() +{ + mMaximum = mTargetMaximum = 1.0f; + + return FMOD_OK; +} +#endif // !PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPNormalize::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count, count2; + float attackspeed = mAttackSpeed; + float threshold = mThreshold; + float maxamp = mMaxAmp; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No speaker channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + for (count = 0; count < length; count++) + { + float scale; + + mMaximum -= attackspeed; + if (mMaximum < threshold) + { + mMaximum = threshold; + } + + for (count2 = 0; count2 < (unsigned int)inchannels; count2++) + { + if (((1 << count2) & speakermask) && FMOD_FABS(inbuffer[(count * inchannels) + count2]) > mMaximum) + { + mMaximum = FMOD_FABS(inbuffer[(count * inchannels) + count2]); + } + } + + scale = 1.0f / mMaximum; + + if (scale > maxamp) + { + scale = maxamp; + } + + for (count2 = 0; count2 < (unsigned int)inchannels; count2++) + { + if (!((1 << count2) & speakermask)) + { + outbuffer[(count * inchannels) + count2] = inbuffer[(count * inchannels) + count2]; + } + else + { + outbuffer[(count * inchannels) + count2] = inbuffer[(count * inchannels) + count2] * scale; + } + } + } + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPNormalize::setParameterInternal(int index, float value) +{ + switch (index) + { + case FMOD_DSP_NORMALIZE_FADETIME: + { + mFadeTime = value; + break; + } + case FMOD_DSP_NORMALIZE_THRESHHOLD: + { + mThreshold = value; + break; + } + case FMOD_DSP_NORMALIZE_MAXAMP: + { + mMaxAmp = value; + break; + } + } + + if (mFadeTime) + { + mAttackSpeed = 1.0f / (mFadeTime * (float)mOutputRate / 1000.0f); + } + else + { + mAttackSpeed = 1.0f; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPNormalize::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_NORMALIZE_FADETIME: + { + *value = mFadeTime; + sprintf(valuestr, "%.02f", mFadeTime); + break; + } + case FMOD_DSP_NORMALIZE_THRESHHOLD: + { + *value = mThreshold; + sprintf(valuestr, "%.02f", mThreshold); + break; + } + case FMOD_DSP_NORMALIZE_MAXAMP: + { + *value = mMaxAmp; + sprintf(valuestr, "%.02f", mMaxAmp); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPNormalize::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->resetInternal(); +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPNormalize::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPNormalize *normalize = (DSPNormalize *)dsp; + + return normalize->DSPNormalize::getMemoryUsed(tracker); +} +#endif + +#endif // !PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_normalize.h b/src/fmod_dsp_normalize.h new file mode 100755 index 0000000..ca36fbb --- /dev/null +++ b/src/fmod_dsp_normalize.h @@ -0,0 +1,53 @@ +#ifndef _FMOD_DSP_NORMALIZE_H +#define _FMOD_DSP_NORMALIZE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NORMALIZE + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPNormalize : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float FMOD_PPCALIGN16(mThreshold); + float FMOD_PPCALIGN16(mMaxAmp); + float FMOD_PPCALIGN16(mFadeTime); + float FMOD_PPCALIGN16(mAttackSpeed); + float FMOD_PPCALIGN16(mMaximum); + float FMOD_PPCALIGN16(mTargetMaximum); + int FMOD_PPCALIGN16(mOutputRate); + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_oscillator.cpp b/src/fmod_dsp_oscillator.cpp new file mode 100755 index 0000000..ba34cd0 --- /dev/null +++ b/src/fmod_dsp_oscillator.cpp @@ -0,0 +1,561 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OSCILLATOR + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_oscillator.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include <spu_intrinsics.h> +#else + #include "fmod_systemi.h" +#endif + +#include <stdlib.h> +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_oscillator_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsposcillator; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPOscillator::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dsposcillator_param[2] = +{ + { 0.0f, 5.0f, 0.0f, "Oscillator type", "", "Select a waveform type" }, + { 1.0f, 22000.0f, 220.0f, "Frequency", "hz", "Playback frequency of tone, for example music note A above middle C is 440.0." } +}; + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPOscillator::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dsposcillator, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsposcillator.name, "FMOD Oscillator"); + dsposcillator.version = 0x00010100; + dsposcillator.channels = 1; + dsposcillator.create = DSPOscillator::createCallback; + dsposcillator.release = DSPOscillator::releaseCallback; + + #ifdef PLATFORM_PS3 + dsposcillator.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_oscillator_pic_start; /* SPU PIC entry address */ + #else + dsposcillator.read = DSPOscillator::readCallback; + #endif + + dsposcillator.numparameters = sizeof(dsposcillator_param) / sizeof(dsposcillator_param[0]); + dsposcillator.paramdesc = dsposcillator_param; + dsposcillator.setparameter = DSPOscillator::setParameterCallback; + dsposcillator.getparameter = DSPOscillator::getParameterCallback; + + dsposcillator.mType = FMOD_DSP_TYPE_OSCILLATOR; + dsposcillator.mSize = sizeof(DSPOscillator); + dsposcillator.mCategory = FMOD_DSP_CATEGORY_FILTER; + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsposcillator.getmemoryused = &DSPI::getMemoryUsedCallback; +#endif + +#else + dsposcillator.read = DSPOscillator::readCallback; /* We only care about read function on SPU */ +#endif + return &dsposcillator; +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPOscillator::createInternal() +{ + int count; + + init(); + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPOscillator::releaseInternal() +{ + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPOscillator::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + + if (!inbuffer) + { + return FMOD_OK; + } + + switch (mType) + { + case 0: // sine + { + #ifdef PLATFORM_PS3_SPU + + vector float *out = (vector float *)outbuffer; + + for (count = 0; count < length; count+=4) + { + vector float position; + + float position0 = mPosition; + float position1 = position0 + (mRate * (FMOD_PI * 2)); + float position2 = position1 + (mRate * (FMOD_PI * 2)); + float position3 = position2 + (mRate * (FMOD_PI * 2)); + + if (position1 >= (FMOD_PI * 2.0f)) + { + position1 -= (FMOD_PI * 2.0f); + } + if (position2 >= (FMOD_PI * 2.0f)) + { + position2 -= (FMOD_PI * 2.0f); + } + if (position3 >= (FMOD_PI * 2.0f)) + { + position3 -= (FMOD_PI * 2.0f); + } + + position = spu_insert(position0, position, 0); + position = spu_insert(position1, position, 1); + position = spu_insert(position2, position, 2); + position = spu_insert(position3, position, 3); + + *out = sinf4(position); + out++; + + mPosition = position3 + (mRate * (FMOD_PI * 2)); + if (mPosition >= (FMOD_PI * 2.0f)) + { + mPosition -= (FMOD_PI * 2.0f); + } + } + + #else + + for (count = 0; count < length; count++) + { + outbuffer[count] = FMOD_SIN(mPosition); + + mPosition += (mRate * (FMOD_PI * 2)); + if (mPosition >= (FMOD_PI * 2.0f)) + { + mPosition -= (FMOD_PI * 2.0f); + } + } + + #endif + break; + } + case 1: // square + { + for (count = 0; count < length; count++) + { + outbuffer[count] = 1.0f * (float)mDirection; + mPosition += mRate; + if (mPosition >= 1.0f) + { + mPosition -= 1.0f; + mDirection = -mDirection; + } + } + break; + } + case 2: // saw up + { + for (count = 0; count < length; count++) + { + outbuffer[count] = (mPosition * 2.0f) - 1.0f; + mPosition += mRate; + if (mPosition >= 1.0f) + { + mPosition -= 1.0f; + } + } + break; + } + case 3: // saw down + { + for (count = 0; count < length; count++) + { + outbuffer[count] = (mPosition * -2.0f) + 1.0f;; + + mPosition += mRate; + if (mPosition >= 1.0f) + { + mPosition -= 1.0f; + } + } + break; + } + case 4: // triangle + { + for (count = 0; count < length; count++) + { + outbuffer[count] = mPosition; + + mPosition += (mRate * (float)mDirection * 2.0f); + if (mPosition > 1.0f || mPosition < -1.0f) + { + mDirection = -mDirection; + mPosition += (mRate * (float)mDirection); + mPosition += (mRate * (float)mDirection); + } + } + break; + } + case 5: // noise + { + for (count = 0; count < length; count++) + { + outbuffer[count] = ((float)(FMOD_RAND()%32768) / 16384.0f) - 1.0f; + } + break; + } + } + + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPOscillator::setParameterInternal(int index, float value) +{ + FMOD_RESULT result; + int outputrate; + + switch (index) + { + case FMOD_DSP_OSCILLATOR_TYPE: + { + mType = (int)value; + mPosition = 0; + break; + } + case FMOD_DSP_OSCILLATOR_RATE: + { + mRateHz = value; + break; + } + } + + mDirection = 1; + result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0 ); + if (result != FMOD_OK) + { + return result; + } + + mRate = mRateHz / (float)outputrate; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPOscillator::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_OSCILLATOR_TYPE: + { + *value = (float)mType; + switch (mType) + { + case 0: { FMOD_strcpy(valuestr, "sine"); break; } + case 1: { FMOD_strcpy(valuestr, "square"); break; } + case 2: { FMOD_strcpy(valuestr, "saw up"); break; } + case 3: { FMOD_strcpy(valuestr, "saw down"); break; } + case 4: { FMOD_strcpy(valuestr, "triangle"); break; } + case 5: { FMOD_strcpy(valuestr, "noise"); break; } + } + break; + } + case FMOD_DSP_OSCILLATOR_RATE: + { + *value = mRateHz; + sprintf(valuestr, "%.02f", mRateHz); + break; + } + } + + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPOscillator::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPOscillator *osc = (DSPOscillator *)dsp; + + return osc->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPOscillator::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPOscillator *osc = (DSPOscillator *)dsp; + + return osc->releaseInternal(); +} + +#endif //!PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPOscillator::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPOscillator *osc = (DSPOscillator *)dsp; + + return osc->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPOscillator::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPOscillator *osc = (DSPOscillator *)dsp; + + return osc->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPOscillator::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPOscillator *osc = (DSPOscillator *)dsp; + + return osc->getParameterInternal(index, value, valuestr); +} + +#endif //!PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_oscillator.h b/src/fmod_dsp_oscillator.h new file mode 100755 index 0000000..33b9802 --- /dev/null +++ b/src/fmod_dsp_oscillator.h @@ -0,0 +1,44 @@ +#ifndef _FMOD_DSP_OSCILLATOR_H +#define _FMOD_DSP_OSCILLATOR_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OSCILLATOR + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPOscillator : public DSPFilter + { + private: + + float mRate; + int mType; + float mRateHz; + int mDirection; + float mPosition; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_parameq.cpp b/src/fmod_dsp_parameq.cpp new file mode 100755 index 0000000..c0ae5ae --- /dev/null +++ b/src/fmod_dsp_parameq.cpp @@ -0,0 +1,1016 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PARAMEQ + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_parameq.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_parameq_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspparameq; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPParamEq::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#ifndef PLATFORM_PS3_SPU + +FMOD_DSP_PARAMETERDESC dspparameq_param[3] = +{ + { 20.0f, 22000.0f, 8000.0f, "Center freq", "hz", "Frequency center. 20.0 to 22000.0. Default = 8000.0." }, + { 0.2f, 5.0f, 1.0f, "Octave range", "octaves", "Octave range around the center frequency to filter. 0.2 to 5.0. Default = 1.0." }, + { 0.05f, 3.0f, 1.0f, "Frequency gain", "", "Frequency Gain. 0.05 to 3.0. Default = 1.0." }, +}; + +#endif // PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPParamEq::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dspparameq, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspparameq.name, "FMOD ParamEQ"); + dspparameq.version = 0x00010100; + dspparameq.create = DSPParamEq::createCallback; + dspparameq.reset = DSPParamEq::resetCallback; + + #ifdef PLATFORM_PS3 + dspparameq.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_parameq_pic_start; // MRAM ADDRESS OF SPU CODE + #else + dspparameq.read = DSPParamEq::readCallback; + #endif + + dspparameq.numparameters = sizeof(dspparameq_param) / sizeof(dspparameq_param[0]); + dspparameq.paramdesc = dspparameq_param; + dspparameq.setparameter = DSPParamEq::setParameterCallback; + dspparameq.getparameter = DSPParamEq::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspparameq.getmemoryused = &DSPParamEq::getMemoryUsedCallback; +#endif + + dspparameq.mType = FMOD_DSP_TYPE_PARAMEQ; + dspparameq.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspparameq.mSize = sizeof(DSPParamEq); +#else + dspparameq.read = DSPParamEq::readCallback; /* We only care about read function on SPU */ +#endif + + return &dspparameq; +} + +#ifndef PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPParamEq::createInternal() +{ + int count; + + init(); + + mBandwidth = 0.2f; + mGain = 1.0f; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + resetInternal(); + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + mCenter = mCenterUpdate; + mBandwidth = mBandwidthUpdate; + mGain = mGainUpdate; + + updateCoefficients(mCenter, mBandwidth, mGain); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPParamEq::resetInternal() +{ + int count; + + for (count=0; count < DSP_MAXLEVELS_MAX; count++) + { + mFilterIn[count][0] = mFilterIn[count][1] = 0; + mFilterOut[count][0] = mFilterOut[count][1] = 0; + } + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPParamEq::updateCoefficients(float center, float bandwidth, float gain) +{ + int outputrate; + +#ifdef PLATFORM_PS3_SPU + outputrate = 48000; +#else + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); +#endif + + float w0 = 2.0f * FMOD_PI * center / outputrate; + + #if 0 + + // bandpass + float alpha = FMOD_SIN(w0) * FMOD_SINH( FMOD_LOG(2.0f) / 2.0f * bandwidth * w0 / FMOD_SIN(w0) ); // (case: BW) + + mCoefficient_b0 = FMOD_SIN(w0) / 2.0f; // = Q*alpha + mCoefficient_b1 = 0; + mCoefficient_b2 = -FMOD_SIN(w0) / 2.0f; // = -Q*alpha + mCoefficient_a0 = 1.0f + alpha; + mCoefficient_a1 = -2.0f * FMOD_COS(w0); + mCoefficient_a2 = 1.0f - alpha; + #endif + + #if 0 + + // notch + float alpha = FMOD_SIN(w0) * FMOD_SINH( FMOD_LOG(2.0f) / 2.0f * bandwidth * w0 / FMOD_SIN(w0) ); // (case: BW) + + mCoefficient_b0 = 1.0f; + mCoefficient_b1 = -2.0f * FMOD_COS(w0); + mCoefficient_b2 = 1.0f; + mCoefficient_a0 = 1.0f + alpha; + mCoefficient_a1 = -2.0f * FMOD_COS(w0); + mCoefficient_a2 = 1.0f - alpha; + #endif + + #if 1 + + // Peaking EQ + float Q = 1.0f / bandwidth; + float A = gain; + float alpha = FMOD_SIN(w0) / (2.0f * Q); + + mCoefficient_b0 = 1.0f + alpha *A; + mCoefficient_b1 = -2.0f * FMOD_COS(w0); + mCoefficient_b2 = 1.0f - alpha*A; + mCoefficient_a0 = 1.0f + alpha/A; + mCoefficient_a1 = -2.0f * FMOD_COS(w0); + mCoefficient_a2 = 1.0f - alpha/A; + + #endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPParamEq::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int count; + int count2; + static float dc = (float)1E-20; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << inchannels)-1))) /*No channels are active, copy in buffer to out buffer and skip the DSP*/ + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + if (mCenter != mCenterUpdate || mBandwidth != mBandwidthUpdate || mGain != mGainUpdate) + { + mCenter = mCenterUpdate; + mBandwidth = mBandwidthUpdate; + mGain = mGainUpdate; + + updateCoefficients(mCenter, mBandwidth, mGain); + } + + float a0 = 1.0f / mCoefficient_a0, a1 = mCoefficient_a1, a2 = mCoefficient_a2; /* numerator coefficients */ + float b0 = mCoefficient_b0, b1 = mCoefficient_b1, b2 = mCoefficient_b2; /* denominator coefficients */ + + if (inchannels == 1 && (speakermask & 1)) + { + float in1 = mFilterIn[0][0]; + float in2 = mFilterIn[0][1]; + float out1 = mFilterOut[0][0]; + float out2 = mFilterOut[0][1]; + + for (count = 0; count < length; count++) + { + float in0, out0; + + in0 = inbuffer[count] + dc; + out0 = (b0 * in0 + b1 * in1 + b2 * in2 - a1 * out1 - a2 * out2) * a0; + + in2 = in1; + in1 = in0; + out2 = out1; + out1 = out0; + + outbuffer[count] = out0; + + dc = -dc; + } + + mFilterIn[0][0] = in1; + mFilterIn[0][1] = in2; + mFilterOut[0][0] = out1; + mFilterOut[0][1] = out2; + } + else if (inchannels == 2 && (speakermask & 0x3)==0x3) + { + float in1L = mFilterIn[0][0]; + float in1R = mFilterIn[1][0]; + float in2L = mFilterIn[0][1]; + float in2R = mFilterIn[1][1]; + float out1L = mFilterOut[0][0]; + float out1R = mFilterOut[1][0]; + float out2L = mFilterOut[0][1]; + float out2R = mFilterOut[1][1]; + + while (length) + { + float in0L, out0L; + float in0R, out0R; + + in0L = inbuffer[0] + dc; + in0R = inbuffer[1] + dc; + + out0L = (b0 * in0L + b1 * in1L + b2 * in2L - a1 * out1L - a2 * out2L) * a0; + out0R = (b0 * in0R + b1 * in1R + b2 * in2R - a1 * out1R - a2 * out2R) * a0; + + in2L = in1L; + in1L = in0L; + in2R = in1R; + in1R = in0R; + out2L = out1L; + out1L = out0L; + out2R = out1R; + out1R = out0R; + + outbuffer[0] = out0L; + outbuffer[1] = out0R; + + dc = -dc; + outbuffer+=2; + inbuffer+=2; + length--; + } + + mFilterIn[0][0] = in1L; + mFilterIn[1][0] = in1R; + mFilterIn[0][1] = in2L; + mFilterIn[1][1] = in2R; + mFilterOut[0][0] = out1L; + mFilterOut[1][0] = out1R; + mFilterOut[0][1] = out2L; + mFilterOut[1][1] = out2R; + } + else if (inchannels == 6 && ((speakermask & 0x3F) == 0x3F)) + { + float in1A = mFilterIn[0][0]; + float in1B = mFilterIn[1][0]; + float in1C = mFilterIn[2][0]; + float in1D = mFilterIn[3][0]; + float in1E = mFilterIn[4][0]; + float in1F = mFilterIn[5][0]; + float in2A = mFilterIn[0][1]; + float in2B = mFilterIn[1][1]; + float in2C = mFilterIn[2][1]; + float in2D = mFilterIn[3][1]; + float in2E = mFilterIn[4][1]; + float in2F = mFilterIn[5][1]; + float out1A = mFilterOut[0][0]; + float out1B = mFilterOut[1][0]; + float out1C = mFilterOut[2][0]; + float out1D = mFilterOut[3][0]; + float out1E = mFilterOut[4][0]; + float out1F = mFilterOut[5][0]; + float out2A = mFilterOut[0][1]; + float out2B = mFilterOut[1][1]; + float out2C = mFilterOut[2][1]; + float out2D = mFilterOut[3][1]; + float out2E = mFilterOut[4][1]; + float out2F = mFilterOut[5][1]; + + while (length) + { + float in0A, out0A; + float in0B, out0B; + float in0C, out0C; + float in0D, out0D; + float in0E, out0E; + float in0F, out0F; + + in0A = inbuffer[0] + dc; + in0B = inbuffer[1] + dc; + in0C = inbuffer[2] + dc; + in0D = inbuffer[3] + dc; + in0E = inbuffer[4] + dc; + in0F = inbuffer[5] + dc; + + out0A = (b0 * in0A + b1 * in1A + b2 * in2A - a1 * out1A - a2 * out2A) * a0; + out0B = (b0 * in0B + b1 * in1B + b2 * in2B - a1 * out1B - a2 * out2B) * a0; + out0C = (b0 * in0C + b1 * in1C + b2 * in2C - a1 * out1C - a2 * out2C) * a0; + out0D = (b0 * in0D + b1 * in1D + b2 * in2D - a1 * out1D - a2 * out2D) * a0; + out0E = (b0 * in0E + b1 * in1E + b2 * in2E - a1 * out1E - a2 * out2E) * a0; + out0F = (b0 * in0F + b1 * in1F + b2 * in2F - a1 * out1F - a2 * out2F) * a0; + + in2A = in1A; + in2B = in1B; + in2C = in1C; + in2D = in1D; + in2E = in1E; + in2F = in1F; + in1A = in0A; + in1B = in0B; + in1C = in0C; + in1D = in0D; + in1E = in0E; + in1F = in0F; + + out2A = out1A; + out2B = out1B; + out2C = out1C; + out2D = out1D; + out2E = out1E; + out2F = out1F; + out1A = out0A; + out1B = out0B; + out1C = out0C; + out1D = out0D; + out1E = out0E; + out1F = out0F; + + outbuffer[0] = out0A; + outbuffer[1] = out0B; + outbuffer[2] = out0C; + outbuffer[3] = out0D; + outbuffer[4] = out0E; + outbuffer[5] = out0F; + + dc = -dc; + outbuffer+=6; + inbuffer+=6; + length--; + } + mFilterIn[0][0] = in1A; + mFilterIn[1][0] = in1B; + mFilterIn[2][0] = in1C; + mFilterIn[3][0] = in1D; + mFilterIn[4][0] = in1E; + mFilterIn[5][0] = in1F; + mFilterIn[0][1] = in2A; + mFilterIn[1][1] = in2B; + mFilterIn[2][1] = in2C; + mFilterIn[3][1] = in2D; + mFilterIn[4][1] = in2E; + mFilterIn[5][1] = in2F; + mFilterOut[0][0] = out1A; + mFilterOut[1][0] = out1B; + mFilterOut[2][0] = out1C; + mFilterOut[3][0] = out1D; + mFilterOut[4][0] = out1E; + mFilterOut[5][0] = out1F; + mFilterOut[0][1] = out2A; + mFilterOut[1][1] = out2B; + mFilterOut[2][1] = out2C; + mFilterOut[3][1] = out2D; + mFilterOut[4][1] = out2E; + mFilterOut[5][1] = out2F; + } + else if (inchannels == 8 && ((speakermask & 0xFF) == 0xFF)) + { + float in1A = mFilterIn[0][0]; + float in1B = mFilterIn[1][0]; + float in1C = mFilterIn[2][0]; + float in1D = mFilterIn[3][0]; + float in1E = mFilterIn[4][0]; + float in1F = mFilterIn[5][0]; + float in1G = mFilterIn[6][0]; + float in1H = mFilterIn[7][0]; + float in2A = mFilterIn[0][1]; + float in2B = mFilterIn[1][1]; + float in2C = mFilterIn[2][1]; + float in2D = mFilterIn[3][1]; + float in2E = mFilterIn[4][1]; + float in2F = mFilterIn[5][1]; + float in2G = mFilterIn[6][1]; + float in2H = mFilterIn[7][1]; + float out1A = mFilterOut[0][0]; + float out1B = mFilterOut[1][0]; + float out1C = mFilterOut[2][0]; + float out1D = mFilterOut[3][0]; + float out1E = mFilterOut[4][0]; + float out1F = mFilterOut[5][0]; + float out1G = mFilterOut[6][0]; + float out1H = mFilterOut[7][0]; + float out2A = mFilterOut[0][1]; + float out2B = mFilterOut[1][1]; + float out2C = mFilterOut[2][1]; + float out2D = mFilterOut[3][1]; + float out2E = mFilterOut[4][1]; + float out2F = mFilterOut[5][1]; + float out2G = mFilterOut[6][1]; + float out2H = mFilterOut[7][1]; + + while (length) + { + float in0A, out0A; + float in0B, out0B; + float in0C, out0C; + float in0D, out0D; + float in0E, out0E; + float in0F, out0F; + float in0G, out0G; + float in0H, out0H; + + in0A = inbuffer[0] + dc; + in0B = inbuffer[1] + dc; + in0C = inbuffer[2] + dc; + in0D = inbuffer[3] + dc; + in0E = inbuffer[4] + dc; + in0F = inbuffer[5] + dc; + in0G = inbuffer[6] + dc; + in0H = inbuffer[7] + dc; + + out0A = (b0 * in0A + b1 * in1A + b2 * in2A - a1 * out1A - a2 * out2A) * a0; + out0B = (b0 * in0B + b1 * in1B + b2 * in2B - a1 * out1B - a2 * out2B) * a0; + out0C = (b0 * in0C + b1 * in1C + b2 * in2C - a1 * out1C - a2 * out2C) * a0; + out0D = (b0 * in0D + b1 * in1D + b2 * in2D - a1 * out1D - a2 * out2D) * a0; + out0E = (b0 * in0E + b1 * in1E + b2 * in2E - a1 * out1E - a2 * out2E) * a0; + out0F = (b0 * in0F + b1 * in1F + b2 * in2F - a1 * out1F - a2 * out2F) * a0; + out0G = (b0 * in0G + b1 * in1G + b2 * in2G - a1 * out1G - a2 * out2G) * a0; + out0H = (b0 * in0H + b1 * in1H + b2 * in2H - a1 * out1H - a2 * out2H) * a0; + + in2A = in1A; + in2B = in1B; + in2C = in1C; + in2D = in1D; + in2E = in1E; + in2F = in1F; + in2G = in1G; + in2H = in1H; + in1A = in0A; + in1B = in0B; + in1C = in0C; + in1D = in0D; + in1E = in0E; + in1F = in0F; + in1G = in0G; + in1H = in0H; + + out2A = out1A; + out2B = out1B; + out2C = out1C; + out2D = out1D; + out2E = out1E; + out2F = out1F; + out2G = out1G; + out2H = out1H; + out1A = out0A; + out1B = out0B; + out1C = out0C; + out1D = out0D; + out1E = out0E; + out1F = out0F; + out1G = out0G; + out1H = out0H; + + outbuffer[0] = out0A; + outbuffer[1] = out0B; + outbuffer[2] = out0C; + outbuffer[3] = out0D; + outbuffer[4] = out0E; + outbuffer[5] = out0F; + outbuffer[6] = out0G; + outbuffer[7] = out0H; + + dc = -dc; + outbuffer+=8; + inbuffer+=8; + length--; + } + mFilterIn[0][0] = in1A; + mFilterIn[1][0] = in1B; + mFilterIn[2][0] = in1C; + mFilterIn[3][0] = in1D; + mFilterIn[4][0] = in1E; + mFilterIn[5][0] = in1F; + mFilterIn[6][0] = in1G; + mFilterIn[7][0] = in1H; + mFilterIn[0][1] = in2A; + mFilterIn[1][1] = in2B; + mFilterIn[2][1] = in2C; + mFilterIn[3][1] = in2D; + mFilterIn[4][1] = in2E; + mFilterIn[5][1] = in2F; + mFilterIn[6][1] = in2G; + mFilterIn[7][1] = in2H; + mFilterOut[0][0] = out1A; + mFilterOut[1][0] = out1B; + mFilterOut[2][0] = out1C; + mFilterOut[3][0] = out1D; + mFilterOut[4][0] = out1E; + mFilterOut[5][0] = out1F; + mFilterOut[6][0] = out1G; + mFilterOut[7][0] = out1H; + mFilterOut[0][1] = out2A; + mFilterOut[1][1] = out2B; + mFilterOut[2][1] = out2C; + mFilterOut[3][1] = out2D; + mFilterOut[4][1] = out2E; + mFilterOut[5][1] = out2F; + mFilterOut[6][1] = out2G; + mFilterOut[7][1] = out2H; + } + else + { + for (count2 = 0; count2 < inchannels; count2++) + { + unsigned int len; + + float *in = inbuffer + count2; + float *out = outbuffer + count2; + + if (!((1 << count2) & speakermask)) + { + int offset1, offset2, offset3, inc; + + offset1 = inchannels; + offset2 = inchannels * 2; + offset3 = inchannels * 3; + len = length >> 2; + inc = inchannels << 2; + + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + + in += inc; + out += inc; + len--; + } + + len = length & 3; + while (len) + { + out[0] = in[0]; + len--; + in += inchannels; + out += inchannels; + } + continue; + } + else + { + len = length; + float in1 = mFilterIn[count2][0]; + float in2 = mFilterIn[count2][1]; + float out1 = mFilterOut[count2][0]; + float out2 = mFilterOut[count2][1]; + while (len) + { + float in0, out0; + + in0 = in[0] + dc; + out0 = (b0 * in0 + b1 * in1 + b2 * in2 - a1 * out1 - a2 * out2) * a0; + in2 = in1; + in1 = in0; + out2 = out1; + out1 = out0; + out[0] = out0; + + in += inchannels; + out += inchannels; + len--; + dc = -dc; + } + + mFilterIn[count2][0] = in1; + mFilterIn[count2][1] = in2; + mFilterOut[count2][0] = out1; + mFilterOut[count2][1] = out2; + } + } + } + + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPParamEq::setParameterInternal(int index, float value) +{ + FMOD_RESULT result; + int outputrate; + + result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + switch (index) + { + case FMOD_DSP_PARAMEQ_CENTER: + { + if (value >= ((float)outputrate / 2.0f) - 100.0f) + { + value = ((float)outputrate / 2.0f) - 100.0f; + } + mCenterUpdate = value; + break; + } + case FMOD_DSP_PARAMEQ_BANDWIDTH: + { + mBandwidthUpdate = value; + break; + } + case FMOD_DSP_PARAMEQ_GAIN: + { + mGainUpdate = value; + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPParamEq::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_PARAMEQ_CENTER: + { + *value = mCenterUpdate; + sprintf(valuestr, "%.02f", mCenterUpdate); + break; + } + case FMOD_DSP_PARAMEQ_BANDWIDTH: + { + *value = mBandwidthUpdate; + sprintf(valuestr, "%.02f", mBandwidthUpdate); + break; + } + case FMOD_DSP_PARAMEQ_GAIN: + { + *value = mGainUpdate; + sprintf(valuestr, "%.02f", mGainUpdate); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPParamEq::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + +#endif // !PLATFORM_PS3_SPU + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPParamEq::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPParamEq *parameq = (DSPParamEq *)dsp; + + return parameq->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPParamEq::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPParamEq *parameq = (DSPParamEq *)dsp; + + return parameq->resetInternal(); +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPParamEq::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPParamEq *parameq = (DSPParamEq *)dsp; + + return parameq->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPParamEq::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPParamEq *parameq = (DSPParamEq *)dsp; + + return parameq->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPParamEq::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPParamEq *parameq = (DSPParamEq *)dsp; + + return parameq->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPParamEq::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPParamEq *parameq = (DSPParamEq *)dsp; + + return parameq->DSPParamEq::getMemoryUsed(tracker); +} +#endif + +#endif // !PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_parameq.h b/src/fmod_dsp_parameq.h new file mode 100755 index 0000000..5859ba3 --- /dev/null +++ b/src/fmod_dsp_parameq.h @@ -0,0 +1,58 @@ +#ifndef _FMOD_DSP_PARAMEQ_H +#define _FMOD_DSP_PARAMEQ_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PARAMEQ + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + class DSPParamEq : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + float mCenter; + float mBandwidth; + float mGain; + + float mCenterUpdate; + float mBandwidthUpdate; + float mGainUpdate; + + float mFilterIn[DSP_MAXLEVELS_MAX][2]; + float mFilterOut[DSP_MAXLEVELS_MAX][2]; + float mCoefficient_a0, mCoefficient_a1, mCoefficient_a2; /* numerator coefficients */ + float mCoefficient_b0, mCoefficient_b1, mCoefficient_b2; /* denominator coefficients */ + + FMOD_RESULT createInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + FMOD_RESULT updateCoefficients(float center, float bandwidth, float gain); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_pitchshift.cpp b/src/fmod_dsp_pitchshift.cpp new file mode 100755 index 0000000..7a5d9fd --- /dev/null +++ b/src/fmod_dsp_pitchshift.cpp @@ -0,0 +1,2640 @@ +/**************************************************************************** +* +* NAME: smbPitchShift.cpp +* VERSION: 1.1 +* HOME URL: http://www.dspdimension.com +* KNOWN BUGS: none +* +* SYNOPSIS: Routine for doing pitch shifting while maintaining +* duration using the Short Time Fourier Transform. +* +* DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5 +* (one octave down) and 2. (one octave up). A value of exactly 1 does not change +* the pitch. numSampsToProcess tells the routine how many samples in indata[0... +* numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ... +* numSampsToProcess-1]. The two buffers can be identical (ie. it can process the +* data in-place). fftFrameSize defines the FFT frame size used for the +* processing. Typical values are 1024, 2048 and 4096. It may be any value <= +* MAX_FFT_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT +* oversampling factor which also determines the overlap between adjacent STFT +* frames. It should at least be 4 for moderate scaling ratios. A value of 32 is +* recommended for best quality. sampleRate takes the sample rate for the signal +* in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in +* indata[] should be in the range [-1.0, 1.0), which is also the output range +* for the data, make sure you scale the data accordingly (for 16bit signed integers +* you would have to divide (and multiply) by 32768). +* +* COPYRIGHT 1999-2003 Stephan M. Bernsee <smb@dspdimension.com> +* +* The Wide Open License (WOL) +* +* Permission to use, copy, modify, distribute and sell this software and its +* documentation for any purpose is hereby granted without fee, provided that +* the above copyright notice and this license appear in all source copies. +* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF +* ANY KIND. See http://www.dspguru.com/wol.htm for more information. +* +*****************************************************************************/ + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PITCHSHIFT + +#include "fmod.h" +#include "fmod_3d.h" +#include "fmod_dspi.h" +#include "fmod_dsp_pitchshift.h" +#include "fmod_systemi.h" + +#include <stdio.h> + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsppitchshift; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPPitchShift::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +FMOD_DSP_PARAMETERDESC dsppitchshift_param[4] = +{ + { 0.5f, 2.0f, 1.0f, "Pitch", "x", "Pitch value. 0.5 to 2.0. Default = 1.0. 0.5 = one octave down, 2.0 = one octave up. 1.0 does not change the pitch." }, + { 256.0f, 4096.0f, 1024.0f, "FFT size", "", "FFT window size. 256, 512, 1024, 2048, 4096. Default = 1024. Increase this to reduce 'smearing'. This effect is a warbling sound similar to when an mp3 is encoded at very low bitrates." }, + { 1.0f, 32.0f, 4.0f, "Overlap", "", "Window overlap. 1 to 32. Default = 4. Increase this to reduce 'tremolo' effect. Increasing it by a factor of 2 doubles the CPU usage." }, + { 0.0f, 16.0f, 0.0f, "Max channels", "channels", "Maximum channels supported. 0 to 16. 0 = same as fmod's default output polyphony, 1 = mono, 2 = stereo etc. See remarks for more. Default = 0. It is suggested to leave at 0!" } +}; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPPitchShift::getDescriptionEx() +{ + FMOD_memset(&dsppitchshift, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsppitchshift.name, "FMOD Pitch Shifter"); + dsppitchshift.version = 0x00010100; + dsppitchshift.create = DSPPitchShift::createCallback; + dsppitchshift.release = DSPPitchShift::releaseCallback; + dsppitchshift.reset = DSPPitchShift::resetCallback; + dsppitchshift.read = DSPPitchShift::readCallback; + + dsppitchshift.numparameters = sizeof(dsppitchshift_param) / sizeof(dsppitchshift_param[0]); + dsppitchshift.paramdesc = dsppitchshift_param; + dsppitchshift.setparameter = DSPPitchShift::setParameterCallback; + dsppitchshift.getparameter = DSPPitchShift::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsppitchshift.getmemoryused = &DSPPitchShift::getMemoryUsedCallback; +#endif + + dsppitchshift.mType = FMOD_DSP_TYPE_PITCHSHIFT; + dsppitchshift.mCategory = FMOD_DSP_CATEGORY_FILTER; + dsppitchshift.mSize = sizeof(DSPPitchShift); + + return &dsppitchshift; +} + + +float gFFTworksp [MAX_FRAME_LENGTH * 2]; +float gFFTtable [MAX_FRAME_LENGTH / 2]; +int gFFTbitrev [2 + (1<<MAX_FRAME_LENGTH_HALF_BITS)]; +float gSynFreq [MAX_FRAME_LENGTH]; +float gSynMagn [MAX_FRAME_LENGTH]; +float gAnaFreq [MAX_FRAME_LENGTH]; +float gAnaMagn [MAX_FRAME_LENGTH]; + + +void DSPPitchShiftSMB::initFft(int fftSize) +{ + int j, nwh; + float delta, x, y; + + gFFTbitrev[0] = fftSize >> 1; + gFFTbitrev[1] = 1; + + nwh = fftSize >> 2; +#ifdef DSP_PITCHSHIFT_USECOSTAB + delta = 1.0f / (8.0f * nwh); +#else + delta = atan(1.0f) / nwh; +#endif + gFFTtable[0] = 1; + gFFTtable[1] = 0; +#ifdef DSP_PITCHSHIFT_USECOSTAB + gFFTtable[nwh] = cosine(1.0f / 8.0f); //cos(pi/4) +#else + gFFTtable[nwh] = cos(delta * nwh); +#endif + gFFTtable[nwh + 1] = gFFTtable[nwh]; + for (j = 2; j < nwh; j += 2) { +#ifdef DSP_PITCHSHIFT_USECOSTAB + x = cosine(delta * j); + y = sine(delta * j); +#else + x = cos(delta * j); + y = sin(delta * j); +#endif + gFFTtable[j] = x; + gFFTtable[j + 1] = y; + gFFTtable[gFFTbitrev[0] - j] = y; + gFFTtable[gFFTbitrev[0] - j + 1] = x; + } + bitrv2(gFFTtable, gFFTbitrev[0]); +} + +void DSPPitchShiftSMB::bitrv2(float *data, int count) +{ + int j, j1, k, k1, m, m2; + float xr, xi, yr, yi; + int *ip = gFFTbitrev + 2; + + ip[0] = 0; + m = 1; + while ((m << 3) < count) { + count >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + count; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == count) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + } + } else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = data[j1]; + xi = data[j1 + 1]; + yr = data[k1]; + yi = data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + } + } + } +} + +void DSPPitchShiftSMB::bitrv2conj(float *data, int count) +{ + int j, j1, k, k1, m, m2; + float xr, xi, yr, yi; + int *ip = gFFTbitrev + 2; + + ip[0] = 0; + m = 1; + while ((m << 3) < count) { + count >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + count; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == count) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + data[k1 + 1] = -data[k1 + 1]; + j1 = k1 + m2; + k1 = j1 + m2; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + k1 += m2; + data[k1 + 1] = -data[k1 + 1]; + } + } else { + data[1] = -data[1]; + data[m2 + 1] = -data[m2 + 1]; + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = data[j1]; + xi = -data[j1 + 1]; + yr = data[k1]; + yi = -data[k1 + 1]; + data[j1] = yr; + data[j1 + 1] = yi; + data[k1] = xr; + data[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + data[k1 + 1] = -data[k1 + 1]; + data[k1 + m2 + 1] = -data[k1 + m2 + 1]; + } + } +} + +void DSPPitchShiftSMB::cft1st(float *fftBuffer) +{ + int j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = fftBuffer[0] + fftBuffer[2]; + x0i = fftBuffer[1] + fftBuffer[3]; + x1r = fftBuffer[0] - fftBuffer[2]; + x1i = fftBuffer[1] - fftBuffer[3]; + x2r = fftBuffer[4] + fftBuffer[6]; + x2i = fftBuffer[5] + fftBuffer[7]; + x3r = fftBuffer[4] - fftBuffer[6]; + x3i = fftBuffer[5] - fftBuffer[7]; + fftBuffer[0] = x0r + x2r; + fftBuffer[1] = x0i + x2i; + fftBuffer[4] = x0r - x2r; + fftBuffer[5] = x0i - x2i; + fftBuffer[2] = x1r - x3i; + fftBuffer[3] = x1i + x3r; + fftBuffer[6] = x1r + x3i; + fftBuffer[7] = x1i - x3r; + wk1r = gFFTtable[2]; + x0r = fftBuffer[8] + fftBuffer[10]; + x0i = fftBuffer[9] + fftBuffer[11]; + x1r = fftBuffer[8] - fftBuffer[10]; + x1i = fftBuffer[9] - fftBuffer[11]; + x2r = fftBuffer[12] + fftBuffer[14]; + x2i = fftBuffer[13] + fftBuffer[15]; + x3r = fftBuffer[12] - fftBuffer[14]; + x3i = fftBuffer[13] - fftBuffer[15]; + fftBuffer[8] = x0r + x2r; + fftBuffer[9] = x0i + x2i; + fftBuffer[12] = x2i - x0i; + fftBuffer[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + fftBuffer[10] = wk1r * (x0r - x0i); + fftBuffer[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + fftBuffer[14] = wk1r * (x0i - x0r); + fftBuffer[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < (mFFTFrameSize << 1); j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = gFFTtable[k1]; + wk2i = gFFTtable[k1 + 1]; + wk1r = gFFTtable[k2]; + wk1i = gFFTtable[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = fftBuffer[j] + fftBuffer[j + 2]; + x0i = fftBuffer[j + 1] + fftBuffer[j + 3]; + x1r = fftBuffer[j] - fftBuffer[j + 2]; + x1i = fftBuffer[j + 1] - fftBuffer[j + 3]; + x2r = fftBuffer[j + 4] + fftBuffer[j + 6]; + x2i = fftBuffer[j + 5] + fftBuffer[j + 7]; + x3r = fftBuffer[j + 4] - fftBuffer[j + 6]; + x3i = fftBuffer[j + 5] - fftBuffer[j + 7]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + fftBuffer[j + 4] = wk2r * x0r - wk2i * x0i; + fftBuffer[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + fftBuffer[j + 2] = wk1r * x0r - wk1i * x0i; + fftBuffer[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + fftBuffer[j + 6] = wk3r * x0r - wk3i * x0i; + fftBuffer[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = gFFTtable[k2 + 2]; + wk1i = gFFTtable[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = fftBuffer[j + 8] + fftBuffer[j + 10]; + x0i = fftBuffer[j + 9] + fftBuffer[j + 11]; + x1r = fftBuffer[j + 8] - fftBuffer[j + 10]; + x1i = fftBuffer[j + 9] - fftBuffer[j + 11]; + x2r = fftBuffer[j + 12] + fftBuffer[j + 14]; + x2i = fftBuffer[j + 13] + fftBuffer[j + 15]; + x3r = fftBuffer[j + 12] - fftBuffer[j + 14]; + x3i = fftBuffer[j + 13] - fftBuffer[j + 15]; + fftBuffer[j + 8] = x0r + x2r; + fftBuffer[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + fftBuffer[j + 12] = -wk2i * x0r - wk2r * x0i; + fftBuffer[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + fftBuffer[j + 10] = wk1r * x0r - wk1i * x0i; + fftBuffer[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + fftBuffer[j + 14] = wk3r * x0r - wk3i * x0i; + fftBuffer[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + + +void DSPPitchShiftSMB::cftmdl(float *fftBuffer, int count) +{ + int j, j1, j2, j3, k, k1, k2, m, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = count << 2; + for (j = 0; j < count; j += 2) { + j1 = j + count; + j2 = j1 + count; + j3 = j2 + count; + x0r = fftBuffer[j] + fftBuffer[j1]; + x0i = fftBuffer[j + 1] + fftBuffer[j1 + 1]; + x1r = fftBuffer[j] - fftBuffer[j1]; + x1i = fftBuffer[j + 1] - fftBuffer[j1 + 1]; + x2r = fftBuffer[j2] + fftBuffer[j3]; + x2i = fftBuffer[j2 + 1] + fftBuffer[j3 + 1]; + x3r = fftBuffer[j2] - fftBuffer[j3]; + x3i = fftBuffer[j2 + 1] - fftBuffer[j3 + 1]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i + x2i; + fftBuffer[j2] = x0r - x2r; + fftBuffer[j2 + 1] = x0i - x2i; + fftBuffer[j1] = x1r - x3i; + fftBuffer[j1 + 1] = x1i + x3r; + fftBuffer[j3] = x1r + x3i; + fftBuffer[j3 + 1] = x1i - x3r; + } + wk1r = gFFTtable[2]; + for (j = m; j < count + m; j += 2) { + j1 = j + count; + j2 = j1 + count; + j3 = j2 + count; + x0r = fftBuffer[j] + fftBuffer[j1]; + x0i = fftBuffer[j + 1] + fftBuffer[j1 + 1]; + x1r = fftBuffer[j] - fftBuffer[j1]; + x1i = fftBuffer[j + 1] - fftBuffer[j1 + 1]; + x2r = fftBuffer[j2] + fftBuffer[j3]; + x2i = fftBuffer[j2 + 1] + fftBuffer[j3 + 1]; + x3r = fftBuffer[j2] - fftBuffer[j3]; + x3i = fftBuffer[j2 + 1] - fftBuffer[j3 + 1]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i + x2i; + fftBuffer[j2] = x2i - x0i; + fftBuffer[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + fftBuffer[j1] = wk1r * (x0r - x0i); + fftBuffer[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + fftBuffer[j3] = wk1r * (x0i - x0r); + fftBuffer[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < (mFFTFrameSize << 1); k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = gFFTtable[k1]; + wk2i = gFFTtable[k1 + 1]; + wk1r = gFFTtable[k2]; + wk1i = gFFTtable[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < count + k; j += 2) { + j1 = j + count; + j2 = j1 + count; + j3 = j2 + count; + x0r = fftBuffer[j] + fftBuffer[j1]; + x0i = fftBuffer[j + 1] + fftBuffer[j1 + 1]; + x1r = fftBuffer[j] - fftBuffer[j1]; + x1i = fftBuffer[j + 1] - fftBuffer[j1 + 1]; + x2r = fftBuffer[j2] + fftBuffer[j3]; + x2i = fftBuffer[j2 + 1] + fftBuffer[j3 + 1]; + x3r = fftBuffer[j2] - fftBuffer[j3]; + x3i = fftBuffer[j2 + 1] - fftBuffer[j3 + 1]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + fftBuffer[j2] = wk2r * x0r - wk2i * x0i; + fftBuffer[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + fftBuffer[j1] = wk1r * x0r - wk1i * x0i; + fftBuffer[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + fftBuffer[j3] = wk3r * x0r - wk3i * x0i; + fftBuffer[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = gFFTtable[k2 + 2]; + wk1i = gFFTtable[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < count + (k + m); j += 2) { + j1 = j + count; + j2 = j1 + count; + j3 = j2 + count; + x0r = fftBuffer[j] + fftBuffer[j1]; + x0i = fftBuffer[j + 1] + fftBuffer[j1 + 1]; + x1r = fftBuffer[j] - fftBuffer[j1]; + x1i = fftBuffer[j + 1] - fftBuffer[j1 + 1]; + x2r = fftBuffer[j2] + fftBuffer[j3]; + x2i = fftBuffer[j2 + 1] + fftBuffer[j3 + 1]; + x3r = fftBuffer[j2] - fftBuffer[j3]; + x3i = fftBuffer[j2 + 1] - fftBuffer[j3 + 1]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + fftBuffer[j2] = -wk2i * x0r - wk2r * x0i; + fftBuffer[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + fftBuffer[j1] = wk1r * x0r - wk1i * x0i; + fftBuffer[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + fftBuffer[j3] = wk3r * x0r - wk3i * x0i; + fftBuffer[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + +void DSPPitchShiftSMB::cftfsub(float *fftBuffer) +{ + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if ((mFFTFrameSize << 1) > 8) { + cft1st(fftBuffer); + l = 8; + while ((l << 2) < (mFFTFrameSize << 1)) { + cftmdl(fftBuffer, l); + l <<= 2; + } + } + if ((l << 2) == (mFFTFrameSize << 1)) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = fftBuffer[j] + fftBuffer[j1]; + x0i = fftBuffer[j + 1] + fftBuffer[j1 + 1]; + x1r = fftBuffer[j] - fftBuffer[j1]; + x1i = fftBuffer[j + 1] - fftBuffer[j1 + 1]; + x2r = fftBuffer[j2] + fftBuffer[j3]; + x2i = fftBuffer[j2 + 1] + fftBuffer[j3 + 1]; + x3r = fftBuffer[j2] - fftBuffer[j3]; + x3i = fftBuffer[j2 + 1] - fftBuffer[j3 + 1]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i + x2i; + fftBuffer[j2] = x0r - x2r; + fftBuffer[j2 + 1] = x0i - x2i; + fftBuffer[j1] = x1r - x3i; + fftBuffer[j1 + 1] = x1i + x3r; + fftBuffer[j3] = x1r + x3i; + fftBuffer[j3 + 1] = x1i - x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = fftBuffer[j] - fftBuffer[j1]; + x0i = fftBuffer[j + 1] - fftBuffer[j1 + 1]; + fftBuffer[j] += fftBuffer[j1]; + fftBuffer[j + 1] += fftBuffer[j1 + 1]; + fftBuffer[j1] = x0r; + fftBuffer[j1 + 1] = x0i; + } + } +} + +void DSPPitchShiftSMB::cftbsub(float *fftBuffer) +{ + int j, j1, j2, j3, count; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + count = 2; + if ((mFFTFrameSize << 1) > 8) { + cft1st(fftBuffer); + count = 8; + while ((count << 2) < (mFFTFrameSize << 1)) { + cftmdl(fftBuffer, count); + count <<= 2; + } + } + if ((count << 2) == (mFFTFrameSize << 1)) { + for (j = 0; j < count; j += 2) { + j1 = j + count; + j2 = j1 + count; + j3 = j2 + count; + x0r = fftBuffer[j] + fftBuffer[j1]; + x0i = -fftBuffer[j + 1] - fftBuffer[j1 + 1]; + x1r = fftBuffer[j] - fftBuffer[j1]; + x1i = -fftBuffer[j + 1] + fftBuffer[j1 + 1]; + x2r = fftBuffer[j2] + fftBuffer[j3]; + x2i = fftBuffer[j2 + 1] + fftBuffer[j3 + 1]; + x3r = fftBuffer[j2] - fftBuffer[j3]; + x3i = fftBuffer[j2 + 1] - fftBuffer[j3 + 1]; + fftBuffer[j] = x0r + x2r; + fftBuffer[j + 1] = x0i - x2i; + fftBuffer[j2] = x0r - x2r; + fftBuffer[j2 + 1] = x0i + x2i; + fftBuffer[j1] = x1r - x3i; + fftBuffer[j1 + 1] = x1i - x3r; + fftBuffer[j3] = x1r + x3i; + fftBuffer[j3 + 1] = x1i + x3r; + } + } else { + for (j = 0; j < count; j += 2) { + j1 = j + count; + x0r = fftBuffer[j] - fftBuffer[j1]; + x0i = -fftBuffer[j + 1] + fftBuffer[j1 + 1]; + fftBuffer[j] += fftBuffer[j1]; + fftBuffer[j + 1] = -fftBuffer[j + 1] - fftBuffer[j1 + 1]; + fftBuffer[j1] = x0r; + fftBuffer[j1 + 1] = x0i; + } + } +} + +void DSPPitchShiftSMB::fft(float *fftBuffer, int sign) +{ + if (sign >= 0) { + bitrv2(fftBuffer, mFFTFrameSize << 1); + cftfsub(fftBuffer); + } else { + bitrv2conj(fftBuffer, mFFTFrameSize << 1); + cftbsub(fftBuffer); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void DSPPitchShiftSMB::smbInit() +{ + /* initialize our static arrays */ + FMOD_memset(mInFIFO, 0, MAX_FRAME_LENGTH * sizeof(float)); + FMOD_memset(mOutFIFO, 0, MAX_FRAME_LENGTH * sizeof(float)); + FMOD_memset(mLastPhase, 0, (4+(MAX_FRAME_LENGTH/2)) * sizeof(float)); + FMOD_memset(mSumPhase, 0, (4+(MAX_FRAME_LENGTH/2)) * sizeof(float)); + FMOD_memset(mOutputAccum, 0, 2 * MAX_FRAME_LENGTH * sizeof(float)); + + FMOD_memset(gFFTworksp, 0, 2 * MAX_FRAME_LENGTH * sizeof(float)); + FMOD_memset(gAnaFreq, 0, MAX_FRAME_LENGTH * sizeof(float)); + FMOD_memset(gAnaMagn, 0, MAX_FRAME_LENGTH * sizeof(float)); + FMOD_memset(gSynFreq, 0, MAX_FRAME_LENGTH * sizeof(float)); + FMOD_memset(gSynMagn, 0, MAX_FRAME_LENGTH * sizeof(float)); + + mRover = false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPPitchShift::createInternal() +{ + FMOD_RESULT result; + int count; + + init(); + +#ifdef DSP_PITCHSHIFT_USECOSTAB + for (count = 0; count < DSP_PITCHSHIFT_COSTABSIZE; count++) + { + mCosTab[count] = (float)FMOD_COS(FMOD_PI_2 * (float)count / (float)DSP_PITCHSHIFT_COSTABSIZE); + } +#endif + + mOverlap = 4; + mPitchShift = 0; + mPitch = 1.0f; + for (count = 0; count < mDescription.numparameters; count++) + { + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPPitchShift::releaseInternal() +{ + if (mPitchShift) + { + FMOD_Memory_Free(mPitchShift); + mPitchShift = 0; + } + + return FMOD_OK; +} + + +#ifdef DSP_PITCHSHIFT_USECOSTAB + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPPitchShiftSMB::cosine(float x) +{ + int y; + + x *= DSP_PITCHSHIFT_TABLERANGE; + y = (int)(x); + if (y < 0) + { + y = -y; + } + + y &= DSP_PITCHSHIFT_TABLEMASK; + switch (y >> DSP_PITCHSHIFT_COSTABBITS) + { + case 0 : return mCosTab[y]; + case 1 : return -mCosTab[(DSP_PITCHSHIFT_COSTABSIZE - 1) - (y - (DSP_PITCHSHIFT_COSTABSIZE * 1))]; + case 2 : return -mCosTab[ (y - (DSP_PITCHSHIFT_COSTABSIZE * 2))]; + case 3 : return mCosTab[(DSP_PITCHSHIFT_COSTABSIZE - 1) - (y - (DSP_PITCHSHIFT_COSTABSIZE * 3))]; + } + + return 0.0f; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPPitchShiftSMB::sine(float x) +{ + return cosine(x - 0.25f); +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Routine smbPitchShift(). See top of file for explanation + Purpose: doing pitch shifting while maintaining duration using the Short + Time Fourier Transform. + Author: (c)1999-2002 Stephan M. Bernsee <smb@dspdimension.com> + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ + + +#ifdef USEWINDOWTAB + +static float vwin1024[513] = { + 0, + 0.0000094123586994454555565426F, + 0.0000376490804277485047180107F, + 0.0000847091020882984047091213F, + 0.0001505906518978750163739733F, + 0.0002352912494534287191072508F, + 0.000338807705825228122620274F, + 0.0004611361236773192651128284F, + 0.0006022718974137974967675291F, + 0.0007622097133526128942548894F, + 0.0009409435499254104051658487F, + 0.0011384666779041818784889983F, + 0.0013547716606548965145861985F, + 0.0015898503544171660450956551F, + 0.001843693908610999354635851F, + 0.0021162927661700914327980172F, + 0.0024076366639015356341246843F, + 0.0027177146328722923129816991F, + 0.0030465149988219697441138578F, + 0.0033940253826027499961526246F, + 0.0037602327006450164681439219F, + 0.0041451231654502374013304689F, + 0.004548682286109995143164042F, + 0.0049708948688514387193038147F, + 0.0054117450176094927805081625F, + 0.005871216134625267812907623F, + 0.0063492909210707826339614712F, + 0.0068459513777006653079126863F, + 0.0073611788055293891908092974F, + 0.0078949538065354873950241199F, + 0.0084472562843918574948531841F, + 0.0090180654452223785177977788F, + 0.0096073597983847847103788808F, + 0.0102151171572797405673327376F, + 0.0108413146401861726353388349F, + 0.0114859286711228025801290187F, + 0.0121489349807357149835240762F, + 0.0128303086072120708927002397F, + 0.0135300238972199116105343819F, + 0.014248054506874108238179133F, + 0.0149843734027280128806580706F, + 0.0157389528627913111158420634F, + 0.0165117644775739647045043057F, + 0.017302779151155300851883112F, + 0.0181119671022800798887431029F, + 0.0189392978654792099035830688F, + 0.0197847402922171067274348388F, + 0.0206482625520642004701699079F, + 0.0215298321338955878090359874F, + 0.0224294158471146087840963901F, + 0.0233469798229030689462604187F, + 0.0242824895154958309007042772F, + 0.0252359097034816626248243665F, + 0.0262072044911294543823032654F, + 0.027196337309739360144078546F, + 0.0282032709190198072057853551F, + 0.0292279674084895968455555249F, + 0.0302703881989050405110219799F, + 0.0313304940437125201135870611F, + 0.0324082450305261948741986089F, + 0.0335036005826305216537264187F, + 0.0346165194605081438794513815F, + 0.0357469597633922053780963779F, + 0.0368948789308443103607260127F, + 0.0380602337443566307584319475F, + 0.0392429803289789935760722983F, + 0.0404430741549711147975187941F, + 0.0416604700394786475747821441F, + 0.0428951221482346545244013214F, + 0.0441469839972850608411647499F, + 0.0454160084547388098741294016F, + 0.0467021477425423325868791835F, + 0.048005353438278330902022617F, + 0.0493255764769889859522322695F, + 0.0506627671530230916374648586F, + 0.0520168751219073910441181852F, + 0.0533878494022423377707298187F, + 0.0547756383776210609148904496F, + 0.0561801897985730325224551507F, + 0.0576014507845311052314229983F, + 0.0590393678258224752219973652F, + 0.060493886785683237405919499F, + 0.0619649529022966993885290776F, + 0.063452510790854954603190663F, + 0.0649565044456442697295983635F, + 0.0664768772421536757732951628F, + 0.0680135719392065962729532203F, + 0.0695665306811163453026836123F, + 0.0711356949998639409571410397F, + 0.0727210058172997331205067439F, + 0.0743224034473674022294176211F, + 0.0759398275983513837417149261F, + 0.077573217375146441554534249F, + 0.0792225112815507781505175444F, + 0.0808876472225809606264590457F, + 0.0825685625068099948720146131F, + 0.0842651938487273821642986604F, + 0.0859774773711221018679395911F, + 0.087705348607487354506417887F, + 0.0894487425044476758273503947F, + 0.0912075934242081443059646517F, + 0.0929818351470257931090657166F, + 0.0947714008737026158968319578F, + 0.0965762232281003329958934955F, + 0.0983962342596775285663568411F, + 0.1002313654460474934282387949F, + 0.1020815476955582168372416163F, + 0.1039467113498938055649034595F, + 0.1058267861866968306827629931F, + 0.1077217014222123792066554415F, + 0.1096313857139527558892666548F, + 0.111555767163383778850516137F, + 0.1134947733186315033115931783F, + 0.1154483311772101505887633266F, + 0.1174163671887705207019791942F, + 0.1193988072578691106429005231F, + 0.1213955767467577162577185845F, + 0.1234066004781937397893898378F, + 0.1254318027382703149008591481F, + 0.1274711072792669708242385695F, + 0.1295244373225204470578830751F, + 0.1315917155613150479886996891F, + 0.1336728641637935921515634163F, + 0.1357678047758874018136054929F, + 0.1378764585242664986175498143F, + 0.1399987460193091726168290734F, + 0.1421345873580907026578756813F, + 0.1442839021273917832210997858F, + 0.1464466094067262136313445353F, + 0.1486226277713873500374575087F, + 0.150811875295513542205583235F, + 0.1530142695551729992153866533F, + 0.1552297276314665297469730376F, + 0.1574581661136498222930413249F, + 0.1596995011022734334282802138F, + 0.1619536482123419829370902789F, + 0.1642205225764908349539439314F, + 0.1665000388481812643171053878F, + 0.1687921112049141081357106486F, + 0.1710966533514606813248803974F, + 0.1734135785231116222426805962F, + 0.175742799488943723940082009F, + 0.1780842285551042514235575709F, + 0.1804377775681121343076540597F, + 0.182803357918177256102865158F, + 0.1851808805425364523600251232F, + 0.1875702559288067727827353792F, + 0.1899713941183553966851604855F, + 0.1923842047096865903732521019F, + 0.1948085968618452623601911F, + 0.1972444792978372274383502827F, + 0.1996917603080655134739629375F, + 0.202150347753783266036009536F, + 0.2046201490705628622812639605F, + 0.2071010712717805679616844827F, + 0.2095930209521177367548716575F, + 0.2120959042910773306722660436F, + 0.2146096270565163166565980646F, + 0.217134094608193384257077696F, + 0.2196692119013319843823239808F, + 0.2222148834901988556644880646F, + 0.2247710135316975943453599029F, + 0.2273375057889767680840975572F, + 0.2299142636350535173761500118F, + 0.2325011900564513678268951935F, + 0.2350981876568525863469005799F, + 0.2377051586607655808691674792F, + 0.2403220049172052341646121931F, + 0.24294862790338916935795055F, + 0.2455849287284465054526094718F, + 0.2482308081371412122884123619F, + 0.25088616651360906573131615F, + 0.2535509038851079255394438405F, + 0.2562249199257820020392273364F, + 0.2589081139604385572994260656F, + 0.2616003849683388726710120409F, + 0.2643016315870010957134184082F, + 0.2670117521160169093974445786F, + 0.2697306445208800251833736183F, + 0.2724582064368280542865363714F, + 0.2751943351726967024184489219F, + 0.2779389277147853443139524643F, + 0.2806918807307361429792536001F, + 0.2834530905734239936144547301F, + 0.2862224532848589020339602484F, + 0.2889998646001000759397925322F, + 0.2917852199511813404697591068F, + 0.294578414471048044553924683F, + 0.2973793429975050695013294444F, + 0.3001879000771766059507683622F, + 0.303003979969475922828792136F, + 0.3058274766505868491606179305F, + 0.3086582838174550813548080441F, + 0.3114962948917908702739509863F, + 0.3143414030240812007122030991F, + 0.3171935010976130175919251997F, + 0.3200524817325058890560285363F, + 0.3229182372897547725898448334F, + 0.3257906598752827731502179631F, + 0.3286696413440027830787926177F, + 0.3315550733038900022009443092F, + 0.334446847120061785396671894F, + 0.3373448539188684813794338879F, + 0.3402489845919921540584596187F, + 0.3431591298005541856852573801F, + 0.3460751799792325389404368252F, + 0.3489970253403859024032840352F, + 0.35192455587818805007316314F, + 0.3548576613727688622290656895F, + 0.3577962313943641170510545635F, + 0.3607401553074734978920901085F, + 0.3636893222750254839326089495F, + 0.3666436212625507895523924162F, + 0.3696029410423622429959777946F, + 0.3725671701977426586438468803F, + 0.3755361971271398702221233634F, + 0.378509910048367981261208115F, + 0.3814881970028163316044356179F, + 0.3844709458596643480987609109F, + 0.3874580443201036117351065968F, + 0.3904493799215651428369255882F, + 0.3934448400419543467876337672F, + 0.3964443119038907337170485334F, + 0.3994476825789540219702189461F, + 0.4024548389919358482025302237F, + 0.4054656679250968620564776757F, + 0.4084800560224294829758662218F, + 0.4114978897939255420013182629F, + 0.4145190556198493081474509836F, + 0.4175434397550149556721521549F, + 0.4205709283330693049052229071F, + 0.4236014073707782823241529968F, + 0.4266347627723191537896241243F, + 0.4296708803335753668051211207F, + 0.432709645746436888380515029F, + 0.4357509446031033739643589797F, + 0.4387946624003918882905850296F, + 0.4418406845440475683162162568F, + 0.4448888963530583939842699692F, + 0.4479391830639726235219200134F, + 0.45099142983521961491888419F, + 0.4540455217514336450079781571F, + 0.4571013438277800600140210463F, + 0.4601587810142849233052686486F, + 0.4632177182001662729682323061F, + 0.4662780402181679328954544417F, + 0.469339631848895655341635802F, + 0.4724023778251549843254508687F, + 0.4754661628362909508993539021F, + 0.4785308715325295447762243839F, + 0.4815963885293205182236420114F, + 0.4846625984116816887592449348F, + 0.4877293857385438524687515383F, + 0.4907966350470975847031240846F, + 0.4938642308571400407757323592F, + 0.4969320576754227558602394765F, + 0.5F, + 0.5030679423245771886286092922F, + 0.5061357691428599592242676408F, + 0.5092033649529023042745734529F, + 0.5122706142614560365089459992F, + 0.5153374015883182002184526027F, + 0.5184036114706794817763579886F, + 0.5214691284674703997126243848F, + 0.5245338371637090491006460979F, + 0.5275976221748449601633979F, + 0.5306603681511042891472129668F, + 0.5337219597818320115933943271F, + 0.5367822817998336715206164627F, + 0.5398412189857150211835801201F, + 0.5428986561722198844748277224F, + 0.5459544782485662439697193804F, + 0.5490085701647803295699645787F, + 0.552060816936027265455777524F, + 0.5551111036469414949934275683F, + 0.5581593154559523206614812807F, + 0.5612053375996081117094149704F, + 0.5642490553968965150133385578F, + 0.5672903542535630005971825085F, + 0.5703291196664245221725764168F, + 0.5733652372276808462103758757F, + 0.5763985926292216621646957719F, + 0.5794290716669305840724746304F, + 0.5824565602449849333055453826F, + 0.5854809443801506363413977851F, + 0.5885021102060743469763792746F, + 0.591519943977570461512982547F, + 0.5945343320749030269212198618F, + 0.597545161008064096286318545F, + 0.6005523174210458670074785914F, + 0.6035556880961092662829514666F, + 0.6065551599580456532123662328F, + 0.6095506200784348571630744118F, + 0.6125419556798963327537421719F, + 0.6155290541403355408789366265F, + 0.6185118029971836683955643821F, + 0.6214900899516319077164894225F, + 0.6244638028728600742667254053F, + 0.6274328298022572303338506572F, + 0.6303970589576377570040222054F, + 0.6333563787374492104476075838F, + 0.6363106777249744050450885879F, + 0.6392598446925265021079098915F, + 0.6422037686056358829489454365F, + 0.645142338627231026748631848F, + 0.64807544412181194992683686F, + 0.6510029746596139865744135022F, + 0.6539248200207674610595631748F, + 0.6568408701994457032924401574F, + 0.6597510154080078459415403813F, + 0.6626551460811314075982636496F, + 0.6655531528799381035810256435F, + 0.6684449266961099977990556908F, + 0.6713303586559971058989049197F, + 0.6742093401247172268497820369F, + 0.6770817627102452274101551666F, + 0.6799475182674941109439714637F, + 0.6828064989023869824080748003F, + 0.6856585969759187992877969009F, + 0.6885037051082091297260490137F, + 0.6913417161825448076228894934F, + 0.6941725233494131508393820695F, + 0.6969960200305239661489054015F, + 0.6998120999228233385380804066F, + 0.7026206570024948749875193243F, + 0.7054215855289518444237728545F, + 0.7082147800488185485079384307F, + 0.7110001353998998130379050053F, + 0.7137775467151409314325860578F, + 0.7165469094265759508743940387F, + 0.7193081192692636349761414749F, + 0.7220610722852145446637450732F, + 0.7248056648273033530927023094F, + 0.7275417935631718346911611661F, + 0.7302693554791199748166263817F, + 0.7329882478839829795802529588F, + 0.7356983684129988487754303605F, + 0.7383996150316609607955342653F, + 0.7410918860395613316782714719F, + 0.7437750800742178869384702011F, + 0.7464490961148919634382536969F, + 0.7491138334863908232463813874F, + 0.7517691918628587322004364069F, + 0.7544150712715536055696929907F, + 0.7570513720966107751308982188F, + 0.7596779950827947658353878069F, + 0.7622948413392343081085300582F, + 0.7649018123431473581419481889F, + 0.7674988099435484656396511127F, + 0.7700857363649464826238499882F, + 0.7726624942110231764047512115F, + 0.7752289864683024056546400971F, + 0.7777851165098009778020582417F, + 0.780330788098667960106524788F, + 0.7828659053918065602317710727F, + 0.7853903729434835723210994729F, + 0.7879040957089227248388851876F, + 0.7904069790478822632451283425F, + 0.7928989287282194320383155173F, + 0.7953798509294369711852823457F, + 0.7978496522462166229416880014F, + 0.8003082396919343199925833687F, + 0.8027555207021627170504984861F, + 0.8051914031381548486621113625F, + 0.8076157952903133541155966668F, + 0.8100286058816446033148395145F, + 0.8124297440711931717061133895F, + 0.8148191194574635476399748768F, + 0.8171966420818226328748323795F, + 0.8195622224318879212034971715F, + 0.8219157714448956930652911979F, + 0.8242572005110562205487667597F, + 0.8265864214768883222461681726F, + 0.8289033466485393741862708339F, + 0.8312078887950860028865918139F, + 0.8334999611518186801717433809F, + 0.8357794774235092205572072999F, + 0.8380463517876579615517584898F, + 0.840300498897726511060568555F, + 0.8425418338863501777069586751F, + 0.8447702723685335257641781936F, + 0.8469857304448269452734621154F, + 0.8491881247044863467721143024F, + 0.8513773722286125389402400288F, + 0.8535533905932737308575042334F, + 0.8557160978726082722900514455F, + 0.8578654126419092973421243187F, + 0.8600012539806908273831709266F, + 0.8621235414757333348489964919F, + 0.8642321952241125426752432759F, + 0.8663271358362063523372853524F, + 0.8684082844386848965001490797F, + 0.8704755626774793864086632311F, + 0.8725288927207329736646101992F, + 0.8745681972617296295879896206F, + 0.8765933995218062602106101622F, + 0.8786044232532422837422814155F, + 0.8806011927421308893570994769F, + 0.8825836328112295348091720371F, + 0.8845516688227896828777829796F, + 0.8865052266813684411772555904F, + 0.888444232836616221149483863F, + 0.890368614286047188599582114F, + 0.892278298577787509771042096F, + 0.8941732138133031693172370069F, + 0.8960532886501060279016428467F, + 0.8979184523044416721404559212F, + 0.8997686345539526175940636676F, + 0.9016037657403224159224919276F, + 0.9034237767718996670041065045F, + 0.9052285991262973841031680422F, + 0.9070181648529742624020855146F, + 0.9087924065757917446717328858F, + 0.9105512574955523241726496053F, + 0.9122946513925125344712796505F, + 0.9140225226288778426209091776F, + 0.9157348061512726733468525708F, + 0.9174314374931900051279853869F, + 0.9191123527774189838623897231F, + 0.9207774887184492218494824556F, + 0.922426782624853558445465751F, + 0.9240601724016486162582850739F, + 0.9256775965526325977705823789F, + 0.9272789941827002113683420248F, + 0.9288643050001359480205564978F, + 0.9304334693188835991861651564F, + 0.9319864280607933482158955485F, + 0.9335231227578464352490072997F, + 0.935043495554355619248099174F, + 0.9365474892091449898856581058F, + 0.9380350470977032451003196911F, + 0.939506113214316762594080501F, + 0.9409606321741774692668514035F, + 0.942398549215468950279728233F, + 0.9438198102014269119663936181F, + 0.9452243616223789945962607817F, + 0.9466121505977576067181189501F, + 0.9479831248780925534447305836F, + 0.9493372328469769083625351414F, + 0.950674423523010903025465268F, + 0.9519946465617217246091286142F, + 0.9532978522574576674131208165F, + 0.9545839915452611901258705984F, + 0.9558530160027148836476840188F, + 0.9571048778517653454755986786F, + 0.9583395299605212969140666246F, + 0.9595569258450289407136324371F, + 0.9607570196710208954016252392F, + 0.9619397662556433692415680525F, + 0.9631051210691556896392739873F, + 0.9642530402366077391107523908F, + 0.9653834805394918561205486185F, + 0.9664963994173694228351223501F, + 0.9675917549694738051258013911F, + 0.9686695059562874243752617076F, + 0.9697296118010949594889780201F, + 0.9707720325915103476432932439F, + 0.9717967290809801372830634136F, + 0.9728036626902605288336189915F, + 0.9737927955088705456176967346F, + 0.9747640902965183373751756335F, + 0.9757175104845041690992957228F, + 0.9766530201770968755425883501F, + 0.9775705841528853357047523787F, + 0.9784701678661044121909640126F, + 0.9793517374479356885075276296F, + 0.9802152597077828932725651612F, + 0.9810607021345207900964169312F, + 0.9818880328977199756224081284F, + 0.982697220848844699148116888F, + 0.9834882355224260352954956943F, + 0.9842610471372086333730067054F, + 0.9850156265972720426304931607F, + 0.9857519454931258362506696358F, + 0.9864699761027799773671631556F, + 0.987169691392787873596148529F, + 0.9878510650192642295053246926F, + 0.9885140713288771419087197501F, + 0.9891586853598137718535099339F, + 0.9897848828427202594326672624F, + 0.9903926402016152152896211192F, + 0.9909819345547776769933534524F, + 0.9915527437156081980162980472F, + 0.9921050461934645126049758801F, + 0.9926388211944705552980394714F, + 0.9931540486222992791809360824F, + 0.99365070907892927287718976F, + 0.994128783865374732187092377F, + 0.9945882549823905627306430688F, + 0.9950291051311485057695449541F, + 0.9954513177138899493456847267F, + 0.9958548768345497625986695311F, + 0.9962397672993550390430073094F, + 0.9966059746173971944926961442F, + 0.9969534850011780857670373734F, + 0.9972822853671277076870183009F, + 0.9975923633360983533435728532F, + 0.9978837072338299085672019828F, + 0.9981563060913889451342129178F, + 0.9984101496455828339549043449F, + 0.9986452283393451034854138015F, + 0.9988615333220958181215110017F, + 0.99905905645007453408368292F, + 0.9992377902866473871057451106F, + 0.9993977281025862025032324709F, + 0.9995388638763227362460384029F, + 0.9996611922941747163662284947F, + 0.9997647087505465712808927492F, + 0.9998494093481020694724747955F, + 0.9999152908979116460841396474F, + 0.9999623509195723070064332205F, + 0.9999905876413005545444434574F, + 1.0F + }; + +#endif + +#define kINVPI 0.3183098861837F +#define kINVPI2 0.1591549430918F + + +void DSPPitchShiftSMB::smbPitchShift(float pitchShift, int numSampsToProcess, int osamp, float sampleRate, float *indata, float *outdata, int channel, int numchannels) +{ + float window; + float freqPerBin, expct; + int i,k, index, inFifoLatency, stepSize, fftFrameSize2, fadeZoneLen; + float *inptr, sum; + + + inptr = indata + channel; + sum = 0; + for (i = 0; i < numSampsToProcess; i++) + { + sum += FMOD_FABS(inptr[0]); + inptr += numchannels; + } + if (sum < .001f) + { + float *outptr; + /* + Silence anyway, so clear outdata to silence. + */ + outptr = outdata + channel; + + for (i = 0; i < numSampsToProcess; i++) + { + outptr[0] = 0; + outptr += numchannels; + } + return; + } + + + /* + Set up some handy variables + */ + fadeZoneLen = mFFTFrameSize/2; + fftFrameSize2 = mFFTFrameSize/2; + stepSize = mFFTFrameSize/osamp; + freqPerBin = sampleRate/(float)mFFTFrameSize; + expct = FMOD_PI2 * (float)stepSize/(float)mFFTFrameSize; + inFifoLatency = mFFTFrameSize-stepSize; + + + if (mRover == false) + { + mRover = inFifoLatency; + } + + if (mResetPhaseFlag) + { + // + // Best solution so far seems a simple re-init to zero + // + /* PDT - experiment to correct phase data when user changes pitch shift ratio. + for(int sample = 0; sample <= (MAX_FRAME_LENGTH/2); sample++) + { + mPitchShift[count].mLastPhase[sample] = 0.0f; + mPitchShift[count].mSumPhase[sample] = 0.0f; //mPitchShift[count].mLastPhaseWithShift[sample]; + } + */ + FMOD_memset( mLastPhase, 0, (4+(MAX_FRAME_LENGTH/2)) * sizeof(float)); + FMOD_memset( mSumPhase, 0, (4+(MAX_FRAME_LENGTH/2)) * sizeof(float)); + mResetPhaseFlag = false; + } + + /* + main processing loop + */ + for (i = 0; i < numSampsToProcess; i++) + { + + /* As long as we have not yet collected enough data just read in */ + mInFIFO[mRover] = indata[i * numchannels + channel]; + outdata[i * numchannels + channel] = mOutFIFO[mRover-inFifoLatency]; + mRover++; + + /* now we have enough data for processing */ + if (mRover >= mFFTFrameSize) + { + mRover = inFifoLatency; + + // + // Window the input data. Direct lookup is possible + // + for (k = 0; k < mFFTFrameSize; k++) + { + #ifdef USEWINDOWTAB + window = mWindow[k]; + #else + #ifdef DSP_PITCHSHIFT_USECOSTAB + window = -0.5f * cosine((float)k/(float)mFFTFrameSize)+0.5f; + #else + window = -0.5f * cosine(FMOD_PI2 * (float)k/(float)mFFTFrameSize)+0.5f; + #endif + #endif + + gFFTworksp[2*k] = mInFIFO[k] * window; + gFFTworksp[2*k+1] = 0.0f; + } + + /* ***************** ANALYSIS ******************* */ + /* do transform */ +#ifdef FASTER_DFT + fft(gFFTworksp, -1); +#else + smbFft(gFFTworksp, -1); +#endif + + float phase0, phase1,phase2,phase3; + float tmp0, tmp1, tmp2, tmp3; + float real0, real1, real2,real3; + float imag0, imag1, imag2, imag3 ; + int qpd0, qpd1, qpd2, qpd3; + int k0, k1, k2, k3; + float factor = freqPerBin * osamp/FMOD_PI2; + + /* this is the analysis step */ + for (k = 0; k <= fftFrameSize2; k+=4) + { + k0 = k; + k1 = k+1; + k2 = k+2; + k3 = k+3; + + /* de-interlace FFT buffer */ + real0 = gFFTworksp[2*k0]; + imag0 = gFFTworksp[2*k0+1]; + real1 = gFFTworksp[2*k1]; + imag1 = gFFTworksp[2*k1+1]; + real2 = gFFTworksp[2*k2]; + imag2 = gFFTworksp[2*k2+1]; + real3 = gFFTworksp[2*k3]; + imag3 = gFFTworksp[2*k3+1]; + + // Kick phase calculations off early + // + phase0 = smbAtan2(imag0,real0); + phase1 = smbAtan2(imag1,real1); + phase2 = smbAtan2(imag2,real2); + phase3 = smbAtan2(imag3,real3); + + // Meanwhile calculate magnitudes +#if 0 + gAnaMagn[k0] = 2.0f*FMOD_SQRT(real0*real0 + imag0*imag0); + gAnaMagn[k1] = 2.0f*FMOD_SQRT(real1*real1 + imag1*imag1); + gAnaMagn[k2] = 2.0f*FMOD_SQRT(real2*real2 + imag2*imag2); + gAnaMagn[k3] = 2.0f*FMOD_SQRT(real3*real3 + imag3*imag3); +#else + + + FMOD_VECTOR v0 = { real0, imag0, 0}; + gAnaMagn[k0] = 2.0f*FMOD_Vector_GetLengthFast(&v0); + FMOD_VECTOR v1 = { real1, imag1, 0}; + gAnaMagn[k1] = 2.0f*FMOD_Vector_GetLengthFast(&v1); + FMOD_VECTOR v2 = { real2, imag2, 0}; + gAnaMagn[k2] = 2.0f*FMOD_Vector_GetLengthFast(&v2); + FMOD_VECTOR v3 = { real3, imag3, 0}; + gAnaMagn[k3] = 2.0f*FMOD_Vector_GetLengthFast(&v3); + +#endif + + // Back to phase... initialise the temporary variables + + tmp0 = -((float)(k0)*expct + mLastPhase[k0]); + tmp1 = -((float)(k1)*expct + mLastPhase[k1]); + tmp2 = -((float)(k2)*expct + mLastPhase[k2]); + tmp3 = -((float)(k3)*expct + mLastPhase[k3]); + + // Add calculated phase + tmp0 += phase0; + mLastPhase[k0] = phase0; + tmp1 += phase1; + mLastPhase[k1] = phase1; + tmp2 += phase2; + mLastPhase[k2] = phase2; + tmp3 += phase3; + mLastPhase[k3] = phase3; + + /* map delta phase into +/- Pi interval */ + qpd0 = (int)(tmp0 * kINVPI); + if (qpd0 >= 0) qpd0 += qpd0&1; + else qpd0 -= qpd0&1; + tmp0 -= FMOD_PI * (float)qpd0; + + qpd1 = (int)(tmp1 * kINVPI); + if (qpd1 >= 0) qpd1 += qpd1&1; + else qpd1 -= qpd1&1; + tmp1 -= FMOD_PI * (float)qpd1; + + qpd2 = (int)(tmp2 * kINVPI); + if (qpd2 >= 0) qpd2 += qpd2&1; + else qpd2 -= qpd2&1; + tmp2 -= FMOD_PI * (float)qpd2; + + qpd3 = (int)(tmp3 * kINVPI); + if (qpd3 >= 0) qpd3 += qpd3&1; + else qpd3 -= qpd3&1; + tmp3 -= FMOD_PI * (float)qpd3; + + /* compute the k-th partials' true frequency */ + gAnaFreq[k0] = (float)k0*freqPerBin + tmp0*factor; + gAnaFreq[k1] = (float)k1*freqPerBin + tmp1*factor; + gAnaFreq[k2] = (float)k2*freqPerBin + tmp2*factor; + gAnaFreq[k3] = (float)k3*freqPerBin + tmp3*factor; + + } + + /* ***************** PROCESSING ******************* */ + /* this does the actual pitch shifting */ + FMOD_memset(gSynMagn, 0, mFFTFrameSize*sizeof(float)); + FMOD_memset(gSynFreq, 0, mFFTFrameSize*sizeof(float)); + + // Try to maintain the neighbouring frequencies + for (k = 0; k <= fftFrameSize2; k++) + { + index = (int)((k*pitchShift)+0.5f); + if (index <= fftFrameSize2) + { + gSynFreq[index] = gAnaFreq[k]*pitchShift; + gSynMagn[index] += gAnaMagn[k]; + } + } + + float phasemul = FMOD_PI2 /osamp; + + /* ***************** SYNTHESIS ******************* */ + /* this is the synthesis step */ + for (k = 0; k <= fftFrameSize2; k+=4) + { + k0 = k; + k1 = k+1; + k2 = k+2; + k3 = k+3; + + /* accumulate delta phase to get bin phase */ + mSumPhase[k0] += k0*expct + phasemul*((gSynFreq[k0] / freqPerBin) - k0); + mSumPhase[k1] += k1*expct + phasemul*((gSynFreq[k1] / freqPerBin) - k1); + mSumPhase[k2] += k2*expct + phasemul*((gSynFreq[k2] / freqPerBin) - k2); + mSumPhase[k3] += k3*expct + phasemul*((gSynFreq[k3] / freqPerBin) - k3); + + if(mSumPhase[k0] > FMOD_PI) + mSumPhase[k0] -= ((int)(mSumPhase[k0]*kINVPI2)) * FMOD_PI2; + else if(mSumPhase[k0] < -FMOD_PI) + mSumPhase[k0] -= ((int)(mSumPhase[k0]*kINVPI2)) * FMOD_PI2; + + if(mSumPhase[k1] > FMOD_PI) + mSumPhase[k1] -= ((int)(mSumPhase[k1]*kINVPI2)) * FMOD_PI2; + else if(mSumPhase[k1] < -FMOD_PI) + mSumPhase[k1] -= ((int)(mSumPhase[k1]*kINVPI2)) * FMOD_PI2; + + if(mSumPhase[k2] > FMOD_PI) + mSumPhase[k2] -= ((int)(mSumPhase[k2]*kINVPI2)) * FMOD_PI2; + else if(mSumPhase[k2] < -FMOD_PI) + mSumPhase[k2] -= ((int)(mSumPhase[k2]*kINVPI2)) * FMOD_PI2; + + if(mSumPhase[k3] > FMOD_PI) + mSumPhase[k3] -= ((int)(mSumPhase[k3]*kINVPI2)) * FMOD_PI2; + else if(mSumPhase[k3] < -FMOD_PI) + mSumPhase[k3] -= ((int)(mSumPhase[k3]*kINVPI2)) * FMOD_PI2; + +#ifdef DSP_PITCHSHIFT_USECOSTAB + phase0 = mSumPhase[k0] * kINVPI2; + phase1 = mSumPhase[k1] * kINVPI2; + phase2 = mSumPhase[k2] * kINVPI2; + phase3 = mSumPhase[k3] * kINVPI2; +#else + phase0 = mSumPhase[k0]; + phase1 = mSumPhase[k1]; + phase2 = mSumPhase[k2]; + phase3 = mSumPhase[k3]; +#endif + + /* get real and imag part and re-interleave */ + gFFTworksp[2*k0] = gSynMagn[k0] * cosine(phase0); + gFFTworksp[2*k0+1] = gSynMagn[k0] * sine(phase0); + gFFTworksp[2*k1] = gSynMagn[k1] * cosine(phase1); + gFFTworksp[2*k1+1] = gSynMagn[k1] * sine(phase1); + gFFTworksp[2*k2] = gSynMagn[k2] * cosine(phase2); + gFFTworksp[2*k2+1] = gSynMagn[k2] * sine(phase2); + gFFTworksp[2*k3] = gSynMagn[k3] * cosine(phase3); + gFFTworksp[2*k3+1] = gSynMagn[k3] * sine(phase3); + + } + /* zero negative frequencies */ + FMOD_memset(&gFFTworksp[mFFTFrameSize+2], 0, sizeof(float)*(mFFTFrameSize - 2)); + // for (k = mFFTFrameSize+2; k < 2*mFFTFrameSize; k++) gFFTworksp[k] = 0.0f; + + /* do inverse transform */ + +#ifdef FASTER_DFT + fft(gFFTworksp, 1); +#else + smbFft(gFFTworksp, 1); +#endif + +#ifdef USEWINDOWTAB + for(k=0; k < mFFTFrameSize; k++) + { + //mOutputAccum[k] += 2.0f*gWindow[k]*gFFTworksp[2*k]/(fftFrameSize2*osamp); + + // Precalculated window includes the root of 4/(fftsize*osamp) + // (use the root because the window coeffs multiply in both fft and ifft + // + mOutputAccum[k] += mWindow[k]*gFFTworksp[2*k]; + } +#else + for(k=0; k < mFFTFrameSize; k++) + { + #ifdef DSP_PITCHSHIFT_USECOSTAB + window = -0.5f * cosine((float)k/(float)mFFTFrameSize)+0.5f; + #else + window = -0.5f * cosine(FMOD_PI2 * (float)k/(float)mFFTFrameSize)+0.5f; + #endif + + mOutputAccum[k] += window*gFFTworksp[2*k]2.0f/(fftFrameSize2*osamp); + } +#endif + + for (k = 0; k < stepSize; k++) mOutFIFO[k] = mOutputAccum[k]; + + /* shift accumulator */ + // memmove(mOutputAccum, mOutputAccum+stepSize, mFFTFrameSize*sizeof(float)); + // PDT output accumulator init new space at end + memmove(mOutputAccum, mOutputAccum+stepSize, inFifoLatency*sizeof(float)); + FMOD_memset(mOutputAccum+inFifoLatency,0, stepSize * sizeof(float)); + + + /* move input FIFO */ + for (k = 0; k < inFifoLatency; k++) mInFIFO[k] = mInFIFO[k+stepSize]; + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse) + Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the + time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes + and returns the cosine and sine parts in an interleaved manner, ie. + fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize + must be a power of 2. It expects a complex input signal (see footnote 2), + ie. when working with 'common' audio signals our input signal has to be + passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform + of the frequencies of interest is in fftBuffer[0...fftFrameSize]. + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void DSPPitchShiftSMB::smbFft(float *fftBuffer, int sign) +{ + float wr, wi, arg, *p1, *p2, temp; + float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i; + int i, bitm, j, le, le2, k; + + for (i = 2; i < 2 * mFFTFrameSize-2; i += 2) + { + for (bitm = 2, j = 0; bitm < 2*mFFTFrameSize; bitm <<= 1) + { + if (i & bitm) j++; + j <<= 1; + } + if (i < j) + { + p1 = fftBuffer+i; + p2 = fftBuffer+j; + + temp = p1[0]; + p1[0] = p2[0]; + p2[0] = temp; + + temp = p1[1]; + p1[1] = p2[1]; + p2[1] = temp; + + p1++; + p2++; + } + } + + for (k = 0, le = 2; k < mFFTFrameBits; k++) + { + le <<= 1; + le2 = le>>1; + ur = 1.0f; + ui = 0.0f; + +#ifdef DSP_PITCHSHIFT_USECOSTAB + arg = 0.5f / (le2>>1); +#else + arg = FMOD_PI / (le2>>1); +#endif + + wr = cosine(arg); + wi = sign * sine(arg); + + for (j = 0; j < le2; j += 2) + { + p1r = fftBuffer+j; + p1i = p1r+1; + p2r = p1r+le2; + p2i = p2r+1; + + for (i = j; i < 2*mFFTFrameSize; i += le) + { + tr = p2r[0] * ur - p2i[0] * ui; + ti = p2r[0] * ui + p2i[0] * ur; + + p2r[0] = p1r[0] - tr; + p2i[0] = p1i[0] - ti; + + p1r[0] += tr; + p1i[0] += ti; + + p1r += le; + p1i += le; + p2r += le; + p2i += le; + } + tr = ur*wr - ui*wi; + ui = ur*wi + ui*wr; + ur = tr; + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_INLINE const float DSPPitchShiftSMB::smbAtan2(float x, float y) +{ + +#if 1 + float signx; + if (x > 0.0f) signx = 1.0f; + else signx = -1.0f; + + if (x == 0.0f) return 0.0f; + if (y == 0.0f) return signx * FMOD_PI / 2.0f; + + return (float)atan2(x, y); +#else + float coeff_1, coeff_2, abs_x, angle; + + coeff_1 = FMOD_PI/4; + coeff_2 = 3*coeff_1; + abs_x = FMOD_FABS(x) + (float)1e-10; // kludge to prevent 0/0 condition + + if (y >= 0) + { + angle = coeff_1 - coeff_1 * ((y - abs_x) / (y + abs_x)); + } + else + { + angle = coeff_2 - coeff_1 * ((y + abs_x) / (abs_x - y)); + } + if (x < 0) + { + return(-angle); // negate if in quad III or IV + } + else + { + return(angle); + } +#endif +} + +void DSPPitchShiftSMB::setResetPhaseFlag() +{ + mResetPhaseFlag=true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPPitchShift::resetInternal() +{ + if (mPitchShift) + { + int count; + + for (count = 0; count < mChannels; count++) + { + mPitchShift[count].smbInit(); + + #ifdef DSP_PITCHSHIFT_USECOSTAB + mPitchShift[count].mCosTab = mCosTab; + #endif + } + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ + +FMOD_RESULT DSPPitchShift::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + int count; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (inchannels > mChannels || !mPitchShift || !(speakermask & ((1 << inchannels)-1))) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_OK; + } + + for (count = 0; count < inchannels; count++) + { + if (!((1 << count) & speakermask)) + { + int offset1, offset2, offset3; + float *out = outbuffer + count; + float *in = inbuffer + count; + unsigned int len = length >> 2; + int inc = inchannels << 2; + + offset1 = inchannels; + offset2 = inchannels*2; + offset3 = inchannels*3; + + while(len) + { + out[0]=in[0]; + out[offset1]=in[offset1]; + out[offset2]=in[offset2]; + out[offset3]=in[offset3]; + + len--; + in += inc; + out += inc; + } + + len = length & 3; + + while(len) + { + out[0]=in[0]; + len--; + in +=inchannels; + out += inchannels; + } + } + else + { + mPitchShift[count].mFFTFrameSize = mFFTSize; + mPitchShift[count].mFFTFrameBits = mFFTFrameBits; + mPitchShift[count].smbPitchShift(mPitch, length, mOverlap, (float)mOutputRate, inbuffer, outbuffer, count, inchannels); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPPitchShift::setParameterInternal(int index, float value) +{ + FMOD_RESULT result; + int oldchannels, oldfftsize, oldoverlap; + float oldpitch; + bool done_lock = false; + + oldchannels = mChannels; + oldfftsize = mFFTSize; + oldpitch = mPitch; + oldoverlap = mOverlap; + + // + // Get parameters and constrain + // + switch (index) + { + case FMOD_DSP_PITCHSHIFT_PITCH: + { + mPitch = value; + if(mPitch == 0.0f) + { + mPitch = 1.0f; + } + break; + } + case FMOD_DSP_PITCHSHIFT_FFTSIZE: + { + // + // Constrain FFT size to powers of 2 between 256 and 4096, + // rounding up + // + if(value > 4096) + { + mFFTSize = 4096; + } + else + { + int nextfftsize = 256; + while(nextfftsize <= 4096) + { + if(value <= nextfftsize) + { + mFFTSize = nextfftsize; + break; + } + nextfftsize <<= 1; + } + } + if(oldfftsize != mFFTSize) + { + mSystem->lockDSP(); + done_lock = true; + } + break; + } + case FMOD_DSP_PITCHSHIFT_OVERLAP: + { + mOverlap = 4; //(int)value; + if(mOverlap < 0) + { + mOverlap = 0; + } + if(oldoverlap != mOverlap) + { + mSystem->lockDSP(); + done_lock = true; + } + break; + } + case FMOD_DSP_PITCHSHIFT_MAXCHANNELS: + { + mMaxChannels = (int)value; + break; + } + } + + // + // Get output rate and number of channels + // + result = mSystem->getSoftwareFormat(&mOutputRate, 0, &mChannels, 0, 0, 0); + if (result != FMOD_OK) + { + if(done_lock) + { + mSystem->unlockDSP(); + } + return result; + } + if (mMaxChannels) + { + mChannels = mMaxChannels; + if(oldchannels != mChannels) + { + mSystem->lockDSP(); + done_lock = true; + } + } + + if (oldfftsize != mFFTSize) + { + mPitchShift->initFft(mFFTSize); + } + // + // Recalculate window coefficients if FFT size has changed + // + if (oldfftsize != mFFTSize || oldoverlap != mOverlap) + { + #ifdef USEWINDOWTAB + float *wintab = NULL; + int wtabsize = 0; + bool window_parity = false; + + if (!mPitchShift) + { + return FMOD_ERR_INVALID_HANDLE; + } + + switch(mFFTSize) + { + case 256: + wintab = vwin1024; + wtabsize = 513; + window_parity = false; + break; + case 512: + wintab = vwin1024; + wtabsize = 513; + window_parity = false; + break; + case 1024: + wintab = vwin1024; + wtabsize = 513; + window_parity = true; + break; + case 2048: + wintab = vwin1024; + wtabsize = 513; + window_parity = false; + break; + case 4096: + wintab = vwin1024; + wtabsize = 513; + window_parity = false; + break; + } + + int ws2m1 = 2*(wtabsize-1); + float ws2m1ratio = (float)ws2m1/(float)mFFTSize; + float window_factor = FMOD_SQRT((float)(mFFTSize * 0.25f * mOverlap)); + + for(int k=0; k<mFFTSize; k++) + { + if(window_parity) + { + if(k >= wtabsize) + { + mWindow[k] = wintab[ws2m1-k]; + } + else + { + mWindow[k] = wintab[k]; + } + } + else + { + float ktabpos; + ktabpos = k * ws2m1ratio ; + int k_a, k_next; + + if(ktabpos > (float)(wtabsize-1)) + { + ktabpos = ws2m1-ktabpos; + k_next = -1; + } + else + { + k_next = +1; + } + + k_a = (int)ktabpos; + float win_a = wintab[k_a]; + + if(((float)k_a != ktabpos) && (k_a + k_next >= 0)) + { + // + // Linear interpolation between lookups + // + float win_b = wintab[k_a + k_next]; + mWindow[k] = win_a + (ktabpos - (float)k_a) * (win_b - win_a); + } + else + { + // + // Direct lookup + // + mWindow[k] = win_a; + } + } + mWindow[k] /= window_factor; + } + for (int count = 0; count < mChannels; count++) + { + mPitchShift[count].mWindow = mWindow; + } + #endif + reset(); + } + + // + // If the number of channels has changed, reinitialise everything + // + if (mChannels != oldchannels || !mPitchShift) + { + int count; + + if (mPitchShift) + { + FMOD_Memory_Free(mPitchShift); + } + + mPitchShift = (DSPPitchShiftSMB *)FMOD_Memory_Alloc(sizeof(DSPPitchShiftSMB) * mChannels); + if (!mPitchShift) + { + if(done_lock) + { + mSystem->unlockDSP(); + } + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < mChannels; count++) + { + mPitchShift[count].smbInit(); + + #ifdef USEWINDOWTAB + mPitchShift[count].mWindow = mWindow; + #endif + + #ifdef DSP_PITCHSHIFT_USECOSTAB + mPitchShift[count].mCosTab = mCosTab; + #endif + } + } + + // + // If pitch has changed, flag a reset of the phase arrays + // + for (int count = 0; count < mChannels; count++) + { + mPitchShift[count].mPitchRatioFromLast = mPitch/oldpitch; + if (oldpitch != mPitch) + { + mPitchShift[count].setResetPhaseFlag(); + } + } + + // + // Calculate the number of bits in the FFT size + // + { + int bitslength = mFFTSize; + + mFFTFrameBits = 0; + while (bitslength > 1) + { + bitslength >>= 1; + mFFTFrameBits++; + } + } + + if(done_lock) + { + mSystem->unlockDSP(); + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPPitchShift::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_PITCHSHIFT_PITCH: + { + if (value) + { + *value = mPitch; + } + if (valuestr) + { + sprintf(valuestr, "%.02f", mPitch); + } + break; + } + case FMOD_DSP_PITCHSHIFT_FFTSIZE: + { + if (value) + { + *value = (float)mFFTSize; + } + if (valuestr) + { + sprintf(valuestr, "%d", mFFTSize); + } + break; + } + case FMOD_DSP_PITCHSHIFT_OVERLAP: + { + if (value) + { + *value = (float)mOverlap; + } + if (valuestr) + { + sprintf(valuestr, "%d", mOverlap); + } + break; + } + case FMOD_DSP_PITCHSHIFT_MAXCHANNELS: + { + if (value) + { + *value = (float)mMaxChannels; + } + if (valuestr) + { + sprintf(valuestr, "%d", mMaxChannels); + } + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPPitchShift::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + +#if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + if (mPitchShift) + { + tracker->add(false, FMOD_MEMBITS_DSP, sizeof(DSPPitchShiftSMB) * mChannels); + } +#endif + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPPitchShift::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPPitchShift *pitchshift = (DSPPitchShift *)dsp; + + return pitchshift->DSPPitchShift::getMemoryUsed(tracker); +} +#endif + +} + +#endif diff --git a/src/fmod_dsp_pitchshift.h b/src/fmod_dsp_pitchshift.h new file mode 100755 index 0000000..a353c02 --- /dev/null +++ b/src/fmod_dsp_pitchshift.h @@ -0,0 +1,174 @@ +#ifndef _FMOD_DSP_PITCHSHIFT_H +#define _FMOD_DSP_PITCHSHIFT_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PITCHSHIFT + +#include "fmod.h" +#include "fmod_dsp_filter.h" + + +#if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + #define DSP_PITCHSHIFT_USECOSTAB + #define USEWINDOWTAB +#endif + +#ifndef PLATFORM_PS3 +#define FASTER_DFT +#endif + +namespace FMOD +{ + const int DSP_PITCHSHIFT_COSTABBITS = 13; + const int DSP_PITCHSHIFT_COSTABSIZE = (1 << DSP_PITCHSHIFT_COSTABBITS); + const int DSP_PITCHSHIFT_TABLERANGE = (DSP_PITCHSHIFT_COSTABSIZE * 4); + const int DSP_PITCHSHIFT_TABLEMASK = (DSP_PITCHSHIFT_TABLERANGE - 1); + + class DSPPitchShift; + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + const int MAX_FRAME_LENGTH = 2048; + const int MAX_FRAME_LENGTH_HALF_BITS = 6; // 2048 = 2^(5.5 * 2) -> round 5.5 up to 6 + #else + const int MAX_FRAME_LENGTH = 4096; + const int MAX_FRAME_LENGTH_HALF_BITS = 6; // 2048 = 2^(6 * 2) + #endif + + class DSPPitchShiftSMB + { + public: + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + + float *mInFIFO; + float *mOutFIFO; + float *mLastPhase; + float *mSumPhase; + float *mOutputAccum; + float *mInFIFOMemory; + float *mOutFIFOMemory; + float *mLastPhaseMemory; + float *mSumPhaseMemory; + float *mOutputAccumMemory; + + float *gFFTtable; + int *gFFTbitrev; + + DSPPitchShift *mDSPPitchShift; + DSPPitchShift *mDSPPitchShiftMram; +#else + float mInFIFO [MAX_FRAME_LENGTH]; + float mOutFIFO [MAX_FRAME_LENGTH]; + float mLastPhase [MAX_FRAME_LENGTH/2+4]; + float mSumPhase [MAX_FRAME_LENGTH/2+4]; + float mOutputAccum[MAX_FRAME_LENGTH * 2]; +#endif + float *mWindow; + + int mRover; + float mPitchRatioFromLast; + int mFFTFrameSize; + int mFFTFrameBits; + float *mCosTab; + bool mResetPhaseFlag; + + void initFft(int fftSize); + void bitrv2(float *data, int count); + void bitrv2conj(float *data, int count); + void cft1st(float *fftBuffer); + void cftmdl(float *fftBuffer, int count); + void cftfsub(float *fftBuffer); + void cftbsub(float *fftBuffer); + void fft(float *fftBuffer, int sign); + + void smbInit(); + void smbFft(float *fftBuffer, int sign); + void smbPitchShift(float pitchShift, int numSampsToProcess, int osamp, float sampleRate, float *indata, float *outdata, int channel, int numchannels); + void setResetPhaseFlag(); + + FMOD_INLINE const float smbAtan2(float x, float y); + +#ifdef DSP_PITCHSHIFT_USECOSTAB + FMOD_INLINE const float cosine(float x); + FMOD_INLINE const float sine(float x); +#else + FMOD_INLINE const float cosine(float x) { return FMOD_COS(x); } + FMOD_INLINE const float sine(float x) { return FMOD_SIN(x); } +#endif + }; + + class DSPPitchShift : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class DSPPitchShiftSMB; + + private: + +#ifdef DSP_PITCHSHIFT_USECOSTAB + float mCosTab[DSP_PITCHSHIFT_COSTABSIZE]; +#endif +#ifdef USEWINDOWTAB + float mWindow[MAX_FRAME_LENGTH]; +#endif + float mPitch; + int mFFTSize; + int mOverlap; + int mMaxChannels; + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + + FMOD_PPCALIGN128(float mTempLastPhase [MAX_FRAME_LENGTH/2+4 + 4]); // 4K + FMOD_PPCALIGN128(float mTempSumPhase [MAX_FRAME_LENGTH/2+4 + 4]); // 4K + FMOD_PPCALIGN128(float mTempBuffer0 [MAX_FRAME_LENGTH * 2]); // 16k + float *mTempBuffer1; + + float *mSynFreq; + float *mSynMagn; + float *mAnaFreq; + float *mAnaMagn; + float *mSynFreqMemory; + float *mSynMagnMemory; + float *mAnaFreqMemory; + float *mAnaMagnMemory; + + #ifdef FASTER_DFT + float mFFTtable[MAX_FRAME_LENGTH / 2]; + int mFFTbitrev[2 + (1<<MAX_FRAME_LENGTH_HALF_BITS)]; + #endif + + DSPPitchShiftSMB mPitchShift[8]; +#else + DSPPitchShiftSMB *mPitchShift; +#endif + int mOutputRate; + int mChannels; + int mFFTFrameBits; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif diff --git a/src/fmod_dsp_resampler.cpp b/src/fmod_dsp_resampler.cpp new file mode 100755 index 0000000..b695b55 --- /dev/null +++ b/src/fmod_dsp_resampler.cpp @@ -0,0 +1,1349 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_channel_software.h" +#include "fmod_dsp_codec.h" +#include "fmod_dsp_resampler_nointerp.h" +#include "fmod_dsp_resampler_linear.h" +#include "fmod_dsp_resampler_cubic.h" +#include "fmod_dsp_resampler_spline.h" +#include "fmod_dsp_resampler.h" +#include "fmod_sample_software.h" + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) +#include "../ps3/src/fmod_common_spu.h" + +#ifdef PLATFORM_WINDOWS_PS3MODE + float gSourceMixBuffer[8192 * 16]; + float gTargetMixBuffer[8192 * 16]; +#endif + +#endif + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" + + #include <cell/dma.h> + + #define mSystem (&gSystem) +#else + #include "fmod_systemi.h" +#endif + +namespace FMOD +{ + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +DSPResampler::DSPResampler() +{ + mResampleBufferMemory = 0; + mResampleBuffer = 0; + mTargetFrequency = 0; + mSpeed.mValue = 0; + mOverflowLength = FMOD_DSP_RESAMPLER_OVERFLOWLENGTH; + + mResampleBufferPos = 0; + mResampleFinishPos = (unsigned int)-1; + mFill = 2; + mResamplePosition.mValue = 0; + mPosition.mValue = 0; + mNoDMA = 0; + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + mNoDMAMemory = NULL; + #else + FMOD_memset(&mNoDMAMemory, 0, sizeof(DSPResampler_NODMA)); + #endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::release(bool freethis) +{ + DSPI::release(false); + + if (mResampleBufferMemory) + { + FMOD_Memory_Free(mResampleBufferMemory); + mResampleBufferMemory = 0; + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + if (mNoDMAMemory) + { + FMOD_Memory_Free(mNoDMAMemory); + mNoDMAMemory = 0; + mNoDMA = 0; + } + #endif + + if (freethis) + { + #ifdef PLATFORM_PS3 + if (mMemory) + { + FMOD_Memory_Free(mMemory); + } + else + #endif + { + FMOD_Memory_Free(this); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::alloc(FMOD_DSP_DESCRIPTION_EX *description) +{ + FMOD_RESULT result; + int channels; + unsigned int bytespersample = 0; + + result = DSPI::alloc(description); + if (result != FMOD_OK) + { + return result; + } + + result = mSystem->getSoftwareFormat(&mTargetFrequency, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + + FMOD_MEMORY_TYPE type; + + if (description->mCategory == FMOD_DSP_CATEGORY_DSPCODECMPEG || + description->mCategory == FMOD_DSP_CATEGORY_DSPCODECRAW || + description->mCategory == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { + type = FMOD_MEMORY_PERSISTENT; + } + else + { + type = FMOD_MEMORY_NORMAL; + } + + mNoDMAMemory = (DSPResampler_NODMA *)FMOD_Memory_CallocType(sizeof(DSPResampler_NODMA) + 16, type); + mNoDMA = (DSPResampler_NODMA *)(((FMOD_UINT_NATIVE)mNoDMAMemory + 15) & ~15); + if (!mNoDMA) + { + return FMOD_ERR_MEMORY; + } + #else + mNoDMA = &mNoDMAMemory; + #endif + + if (description->mResamplerBlockLength) + { + mResampleBlockLength = description->mResamplerBlockLength; + channels = description->channels; + } + else + { + result = mSystem->getDSPBufferSize(&mResampleBlockLength, 0); + if (result != FMOD_OK) + { + return result; + } + channels = mSystem->mMaxInputChannels; + } + + mResampleBufferLength = mResampleBlockLength * 2; /* *2 = double buffer */ + + if (mDescription.mFormat == FMOD_SOUND_FORMAT_NONE) + { + mDescription.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + } + + SoundI::getBytesFromSamples(1, &bytespersample, channels, mDescription.mFormat); + + if (0) + { + } + #ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + if (mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECMPEG) + { + DSPCodecMPEG *dspcodecmpeg = SAFE_CAST(DSPCodecMPEG, this); + + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(dspcodecmpeg->mResampleBufferMemory, 16); + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + DSPCodecXMA *dspcodecxma = SAFE_CAST(DSPCodecXMA, this); + + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(dspcodecxma->mResampleBufferMemory, 16); + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECCELT) + { + DSPCodecCELT *dspcodeccelt = SAFE_CAST(DSPCodecCELT, this); + + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(dspcodeccelt->mResampleBufferMemory, 16); + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { + DSPCodecADPCM *dspcodecadpcm = SAFE_CAST(DSPCodecADPCM, this); + + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(dspcodecadpcm->mResampleBufferMemory, 16); + } + #endif + #ifdef FMOD_SUPPORT_RAW + else if (mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECRAW) + { + DSPCodecRaw *dspcodecraw = SAFE_CAST(DSPCodecRaw, this); + + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(dspcodecraw->mResampleBufferMemory, 16); + } + #endif + #endif /* FMOD_SUPPORT_DSPCODEC */ + #ifndef PLATFORM_PS3 + else + { + mResampleBufferMemory = FMOD_Memory_Calloc(((mResampleBufferLength + (mOverflowLength * 4)) * bytespersample) + 16); + if (!mResampleBufferMemory) + { + return FMOD_ERR_MEMORY; + } + mResampleBuffer = mResampleBufferMemory; + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(mResampleBuffer, 16); + } + + mResampleBuffer = (char *)mResampleBuffer + (mOverflowLength * bytespersample); + mResampleBuffer = (char *)FMOD_ALIGNPOINTER(mResampleBuffer, 16); + #endif /* !PLATFORM_PS3 */ + + mResampleBufferPos = 0; + mResampleFinishPos = (unsigned int)-1; + mFill = 2; + mPosition.mValue = 0; + mResamplePosition.mValue = 0; + + mDSPTick = 0; + mNoDMA->mDSPFinishTick = 0; + + if (description->mDSPSoundCard) + { + mDSPSoundCard = description->mDSPSoundCard; + } + else + { + mDSPSoundCard = mSystem->mDSPSoundCard; + } + + return FMOD_OK; +} + +#endif + +#if defined(FMOD_SUPPORT_MIXER_NONRECURSIVE) + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + THIS FUNCTION IS PS3 ONLY + + [PLATFORMS] + PlayStation 3 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::update(unsigned int length, int *outchannels, void **outbuffer, unsigned int tick) +{ + FMOD_RESULT result = FMOD_OK; + + mFlags &= ~FMOD_DSP_FLAG_IDLE; + mFlags &= ~FMOD_DSP_FLAG_FINISHED; + + if (mFill) + { + void *resamplebuff; + unsigned int oldresamplebufferpos; + unsigned int bytespersample = 0; + + oldresamplebufferpos = mResampleBufferPos; + + if (mDescription.channels) + { + mResampleBufferChannels = mDescription.channels; + } + else + { + mResampleBufferChannels = *outchannels; + } + + SoundI::getBytesFromSamples(1, &bytespersample, mResampleBufferChannels, mDescription.mFormat); + + resamplebuff = (char *)mResampleBuffer + (mResampleBufferPos * bytespersample); + + if (mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECMPEG || + mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECADPCM || + mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECRAW || + mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + if ((int)mResampleFinishPos == -1) + { + instance = (FMOD_DSP *)this; + + /* + Read from the dsp unit's read function. Bring a chunk of data in to fill the back buffer of the resampler double-buffer. + */ + result = mDescription.read((FMOD_DSP_STATE *)this, 0, (float *)resamplebuff, mResampleBlockLength, mDescription.channels, mDescription.channels); + } + else + { + FMOD_memset(resamplebuff, 0, mResampleBlockLength * bytespersample); + } + + if (mDirection == DSPRESAMPLER_SPEEDDIR_BACKWARDS) + { + unsigned int pos, count; + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, this); + + /* + Reverse the buffer. + */ + if (mDescription.channels == 2) + { + unsigned int *buff = (unsigned int *)resamplebuff; + for (count = 0; count < mResampleBlockLength / 2; count++) /* /2 is for half of a buffer. */ + { + unsigned int tmp = buff[mResampleBlockLength - count - 1]; + buff[mResampleBlockLength - count - 1] = buff[count]; + buff[count] = tmp; + } + } + else + { + signed short *buff = (signed short *)resamplebuff; + for (count = 0; count < mResampleBlockLength / 2; count++) /* /2 is for half of a buffer. */ + { + signed short tmp = buff[mResampleBlockLength - count - 1]; + buff[mResampleBlockLength - count - 1] = buff[count]; + buff[count] = tmp; + } + } + + if (result == FMOD_OK) + { + result = dspcodec->getPositionInternal(&pos); + pos -= (mResampleBlockLength * 2); /* Just read one forward, now jump 2 back. */ + if ((int)pos < (int)mNoDMA->mLoopStart) + { + pos += mNoDMA->mLoopLength; + } + result = dspcodec->setPositionInternal(pos, true); + } + } + + } + else + { + if (*outbuffer != resamplebuff) + { + FMOD_memcpy(resamplebuff, *outbuffer, mResampleBlockLength * bytespersample); + } + } + + mResampleBufferPos += mResampleBlockLength; + if (mResampleBufferPos >= mResampleBufferLength) + { + mResampleBufferPos = 0; + } + + if (mResampleFinishPos == (unsigned int)-1 && result != FMOD_OK) + { + mResampleFinishPos = mResampleBufferPos; + if (!mResampleFinishPos) + { + mResampleFinishPos = mResampleBufferLength; + } + } + + /* + Copy the first samples to the end of the buffer so when interpolation does a read past the end, the data will be valid. + */ + if (!oldresamplebufferpos) + { + unsigned int count; + + for (count=0; count < mOverflowLength * 2 * bytespersample; count++) /* *2 = double, not sizeof(short) */ + { + ((unsigned char *)mResampleBuffer)[(mResampleBufferLength * bytespersample) + count] = ((unsigned char *)mResampleBuffer)[count]; + } + } + + mFill--; + if (mFill) + { + mFlags |= FMOD_DSP_FLAG_FIRSTMIX; + return FMOD_ERR_FILE_EOF; + } + } + + /* + Now that the double buffer is up to date, do some resampling. + */ + { + int inlength = length - mReadPosition; + float *readbuffer; + FMOD_SINT64P speed; + float freq; + + readbuffer = (float *)gTargetMixBuffer; + + freq = mFrequency; + speed = mSpeed; + + if (mNoDMA->mDSPClockEnd.mValue > 0 && + mNoDMA->mDSPClockEnd.mValue < (mSystem->mDSPClock.mValue + mReadPosition) + inlength) + { + unsigned int diff = (unsigned int)((mSystem->mDSPClock.mValue + mReadPosition + inlength) - mNoDMA->mDSPClockEnd.mValue); + if (diff > (unsigned int)inlength) + { + diff = inlength; + } + + FMOD_memset(readbuffer + (mReadPosition + ((inlength - diff) * mDescription.channels)), 0, diff * sizeof(float) * mDescription.channels); + inlength -= diff; + mNoDMA->mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + } + + if (mNoDMA->mDSPClockPause.mValue > 0 && + mNoDMA->mDSPClockPause.mValue < (mSystem->mDSPClock.mValue + mReadPosition) + inlength) + { + unsigned int diff = (unsigned int)((mSystem->mDSPClock.mValue + mReadPosition + inlength) - mNoDMA->mDSPClockPause.mValue); + if (diff > (unsigned int)inlength) + { + diff = inlength; + } + + FMOD_memset(readbuffer + (mReadPosition + ((inlength - diff) * mDescription.channels)), 0, diff * sizeof(float) * mDescription.channels); + inlength -= diff; + + mFlags &= ~FMOD_DSP_FLAG_ACTIVE; + mNoDMA->mDSPClockPause.mValue = 0; + } + + if (mNoDMA->mDSPClockStart.mValue > mSystem->mDSPClock.mValue && + mNoDMA->mDSPClockStart.mValue + inlength > mSystem->mDSPClock.mValue) + { + unsigned int diff = (unsigned int)(mNoDMA->mDSPClockStart.mValue - mSystem->mDSPClock.mValue); + if (diff > (unsigned int)inlength) + { + diff = inlength; + } + + if (diff) + { + FMOD_memset(readbuffer, 0, diff * sizeof(float) * mDescription.channels); + inlength -= diff; + mReadPosition += diff; + } + } + + while (inlength) + { + unsigned int rlength; + FMOD_RESAMPLER_END endflag = FMOD_RESAMPLER_END_MIXBUFFER; + + /* + Work out what is going to end first. The source data or the output? + */ + rlength = inlength; /* Start it off defaulting to the amount requested */ + if (speed.mValue > 0x100) + { + FMOD_UINT64P mixesleft, fracleft; + int nextpos; + bool endofsound = false; + + nextpos = mResamplePosition.mHi - mOverflowLength; + nextpos /= (int)mResampleBlockLength; + nextpos ++; + nextpos *= mResampleBlockLength; + nextpos += mOverflowLength; + + mixesleft.mHi = nextpos; + mixesleft.mLo = 0; + mixesleft.mValue -= mResamplePosition.mValue; + + if (mResampleFinishPos != (unsigned int)-1) + { + FMOD_UINT64P mixeslefttofinish; + + mixeslefttofinish.mHi = mResampleFinishPos; + mixeslefttofinish.mLo = 0; + mixeslefttofinish.mValue -= mResamplePosition.mValue; + + if (mixeslefttofinish.mValue <= mixesleft.mValue) + { + mixesleft.mValue = mixeslefttofinish.mValue; + endofsound = true; + } + } + + fracleft.mValue = mixesleft.mValue % speed.mValue; + mixesleft.mValue /= speed.mValue; + + if (fracleft.mValue) /* round the count up (this could be done better) */ + { + mixesleft.mValue++; + } + + if (mixesleft.mValue <= rlength) + { + if (endofsound) + { + endflag = FMOD_RESAMPLER_END_SOUND; + } + else + { + endflag = FMOD_RESAMPLER_END_RESAMPLEBUFFER; + } + rlength = mixesleft.mLo; + } + } + + /* + Resample the src buffer into the destination. + */ +#ifdef FMOD_SUPPORT_RESAMPLER_NOINTERP + if (speed.mHi == 1 && speed.mLo == 0) + { + FMOD_Resampler_NoInterp(readbuffer + (mReadPosition * mResampleBufferChannels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mResampleBufferChannels); + } + else +#endif + { + switch (mSystem->mResampleMethod) + { + #ifdef FMOD_SUPPORT_RESAMPLER_NOINTERP + case FMOD_DSP_RESAMPLER_NOINTERP: + { + FMOD_Resampler_NoInterp(readbuffer + (mReadPosition * mResampleBufferChannels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } + #endif + #ifdef FMOD_SUPPORT_RESAMPLER_LINEAR + case FMOD_DSP_RESAMPLER_LINEAR: + { + FMOD_Resampler_Linear(readbuffer + (mReadPosition * mResampleBufferChannels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mResampleBufferChannels); + + break; + } + #endif + #ifdef FMOD_SUPPORT_RESAMPLER_CUBIC + case FMOD_DSP_RESAMPLER_CUBIC: + { + FMOD_Resampler_Cubic(readbuffer + (mReadPosition * mResampleBufferChannels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } + #endif + #ifdef FMOD_SUPPORT_RESAMPLER_SPLINE + case FMOD_DSP_RESAMPLER_SPLINE: + { + FMOD_Resampler_Spline(readbuffer + (mReadPosition * mResampleBufferChannels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } + #endif + default: + { + FMOD_Resampler_Linear(readbuffer + (mReadPosition * mResampleBufferChannels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } + } + } + + if (mResamplePosition.mHi >= (mResampleBufferLength + mOverflowLength)) + { + mResamplePosition.mHi -= mResampleBufferLength; + } + + /* + Update mPosition and execute any loop / end logic. + THIS INFO IS FOR DISPLAY ONLY. + */ + if (1) + { + FMOD_UINT64P len, endpoint; + + if (mLength < mNoDMA->mLoopStart + mNoDMA->mLoopLength) + { + mNoDMA->mLoopLength = mLength - mNoDMA->mLoopStart; + } + + if (mDirection == DSPRESAMPLER_SPEEDDIR_BACKWARDS) + { + FMOD_UINT64P len, endpoint; + + if (mNoDMA->mMode & FMOD_LOOP_NORMAL && mLoopCount) + { + endpoint.mHi = mNoDMA->mLoopStart; + endpoint.mLo = 0; + } + else + { + endpoint.mHi = 0; + endpoint.mLo = 0; + } + + len.mValue = speed.mValue * rlength; + + mPosition.mValue -= len.mValue; + + if ((signed int)mPosition.mHi <= (signed int)endpoint.mHi) + { + if ((mNoDMA->mMode & FMOD_LOOP_NORMAL) || mLength == (unsigned int)-1 && mLength) /* 0xFFFFFFFF should mean it is an infinite netstream */ + { + mPosition.mHi += mNoDMA->mLoopLength; + } + else + { + mPosition.mHi = 0; + } + } + } + else + { + if (mNoDMA->mMode & FMOD_LOOP_NORMAL && mLoopCount) + { + endpoint.mHi = mNoDMA->mLoopStart + mNoDMA->mLoopLength -1; + endpoint.mLo = 0xFFFFFFFF; + } + else + { + endpoint.mHi = mLength - 1; + endpoint.mLo = 0xFFFFFFFF; + } + + len.mValue = (speed.mValue * rlength); + + mPosition.mValue += len.mValue; + + if (mPosition.mValue > endpoint.mValue) + { + if ((mNoDMA->mMode & FMOD_LOOP_NORMAL) || mLength == (unsigned int)-1 && mLength) /* 0xFFFFFFFF should mean it is an infinite netstream */ + { + mPosition.mHi -= mNoDMA->mLoopLength; + } + else + { + mPosition.mHi = mLength; + } + } + } + } + + inlength -= rlength; + mReadPosition += rlength; + + if (endflag == FMOD_RESAMPLER_END_SOUND) + { + FMOD_memset(readbuffer + (mReadPosition * mDescription.channels), 0, inlength * mDescription.channels * sizeof(float)); + mNoDMA->mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + inlength = 0; + break; + } + else if (endflag == FMOD_RESAMPLER_END_RESAMPLEBUFFER) + { + mFill++; + break; + } + } + + if (!inlength) + { + *outbuffer = readbuffer; + *outchannels = mResampleBufferChannels; + mReadPosition = 0; + } + else + { + if (mFill) + { + return FMOD_ERR_FILE_EOF; + } + else + { + *outbuffer = readbuffer; + *outchannels = mResampleBufferChannels; + mReadPosition = 0; + } + } + } + + return FMOD_OK; + + /* + This comment is here to avoid a internal compiler error in ProDG gcc compiler! + */ +} + +#else + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int bytespersample; + + if (tick >= mNoDMA->mDSPFinishTick) + { + mFlags |= FMOD_DSP_FLAG_FINISHED; + return FMOD_OK; + } + + mFlags &= ~FMOD_DSP_FLAG_IDLE; + mFlags &= ~FMOD_DSP_FLAG_FINISHED; + + SoundI::getBytesFromSamples(1, &bytespersample, mDescription.channels, mDescription.mFormat); + + if (mDSPTick != tick) + { + int len, inlength = *length; + int readoffset = 0; + FMOD_SINT64P speed; + unsigned int starttime = 0; + unsigned int endtime = 0; + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + speed.mValue = mSpeed.mValue; + + if (mNoDMA) + { + if (mNoDMA->mDSPClockEnd.mValue > 0 && + mNoDMA->mDSPClockEnd.mValue < mSystem->mDSPClock.mValue + inlength) + { + unsigned int diff = (unsigned int)(mSystem->mDSPClock.mValue + inlength - mNoDMA->mDSPClockEnd.mValue); + if (diff > (unsigned int)inlength) + { + diff = inlength; + } + + FMOD_memset(mBuffer + ((inlength - diff) * mDescription.channels), 0, diff * sizeof(float) * mDescription.channels); + inlength -= diff; + + mNoDMA->mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + } + + if (mNoDMA->mDSPClockPause.mValue > 0 && + mNoDMA->mDSPClockPause.mValue < (mSystem->mDSPClock.mValue + mReadPosition) + inlength) + { + unsigned int diff = (unsigned int)((mSystem->mDSPClock.mValue + mReadPosition + inlength) - mNoDMA->mDSPClockPause.mValue); + if (diff > (unsigned int)inlength) + { + diff = inlength; + } + + FMOD_memset(mBuffer + (mReadPosition + ((inlength - diff) * mDescription.channels)), 0, diff * sizeof(float) * mDescription.channels); + inlength -= diff; + + mFlags &= ~FMOD_DSP_FLAG_ACTIVE; + mNoDMA->mDSPClockPause.mValue = 0; + } + + if (mNoDMA->mDSPClockStart.mValue > mSystem->mDSPClock.mValue && + mNoDMA->mDSPClockStart.mValue + inlength > mSystem->mDSPClock.mValue) + { + unsigned int diff = (unsigned int)(mNoDMA->mDSPClockStart.mValue - mSystem->mDSPClock.mValue); + if (diff > (unsigned int)inlength) + { + diff = inlength; + } + + if (diff) + { + FMOD_memset(mBuffer, 0, diff * sizeof(float) * mDescription.channels); + inlength -= diff; + readoffset += diff; + } + } + } + + len = inlength; + while (len) + { + unsigned int rlength; + FMOD_RESAMPLER_END endflag = FMOD_RESAMPLER_END_MIXBUFFER; + + /* + Fill the double buffer if nescessary. + */ + while (mFill) + { + unsigned int readlen, oldresamplebufferpos; + void *resamplebuff; + + oldresamplebufferpos = mResampleBufferPos; + readlen = mResampleBlockLength; + resamplebuff = (char *)mResampleBuffer + (mResampleBufferPos * bytespersample); + + if ((int)mResampleFinishPos == -1) + { + instance = (FMOD_DSP *)this; + + /* + Read from the dsp unit's read function. Bring a chunk of data in to fill the back buffer of the resampler double-buffer. + */ + result = mDescription.read((FMOD_DSP_STATE *)this, 0, (float *)resamplebuff, readlen, mDescription.channels, mDescription.channels); + } + else + { + FMOD_memset(resamplebuff, 0, readlen * mDescription.channels * sizeof(signed short)); + result = FMOD_OK; + } + + mResampleBufferPos += readlen; + if (mResampleBufferPos >= mResampleBufferLength) + { + mResampleBufferPos = 0; + } + + if ((int)mResampleFinishPos == -1 && result != FMOD_OK) + { + mResampleFinishPos = mResampleBufferPos; + if (!mResampleFinishPos) + { + mResampleFinishPos = mResampleBufferLength; + } + } + + + /* + Copy the first samples to the end of the buffer so when interpolation does a read past the end, the data will be valid. + */ + if (!oldresamplebufferpos) + { + unsigned int count; + + for (count=0; count < mOverflowLength * 2 * bytespersample; count++) + { + ((unsigned char *)mResampleBuffer)[(mResampleBufferLength * bytespersample) + count] = ((unsigned char *)mResampleBuffer)[count]; + } + } + + mFill--; + } + + /* + Work out what is going to end first. The source data or the output? + */ + rlength = len; /* Start it off defaulting to the amount requested */ + if (speed.mValue > 0x100) + { + FMOD_UINT64P mixesleft, fracleft; + int nextpos; + bool endofsound = false; + + nextpos = mResamplePosition.mHi - mOverflowLength; + nextpos /= (int)mResampleBlockLength; + nextpos ++; + nextpos *= mResampleBlockLength; + nextpos += mOverflowLength; + + mixesleft.mHi = nextpos; + mixesleft.mLo = 0; + mixesleft.mValue -= mResamplePosition.mValue; + + if (mResampleFinishPos != (unsigned int)-1) + { + FMOD_UINT64P mixeslefttofinish; + + mixeslefttofinish.mHi = mResampleFinishPos; + mixeslefttofinish.mLo = 0; + mixeslefttofinish.mValue -= mResamplePosition.mValue; + + if (mixeslefttofinish.mValue <= mixesleft.mValue) + { + mixesleft.mValue = mixeslefttofinish.mValue; + endofsound = true; + } + } + + fracleft.mValue = mixesleft.mValue % speed.mValue; + mixesleft.mValue /= speed.mValue; + + if (fracleft.mValue) /* round the count up (this could be done better) */ + { + mixesleft.mValue++; + } + + if (mixesleft.mValue <= rlength) + { + if (endofsound) + { + endflag = FMOD_RESAMPLER_END_SOUND; + } + else + { + endflag = FMOD_RESAMPLER_END_RESAMPLEBUFFER; + } + rlength = mixesleft.mLo; + } + } + + /* + Resample the src buffer into the destination. + */ + if (speed.mHi == 1 && speed.mLo == 0) + { + FMOD_Resampler_NoInterp(mBuffer + (readoffset * mDescription.channels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mDescription.channels); + } + else + { + switch (mSystem->mResampleMethod) + { +#ifdef FMOD_SUPPORT_RESAMPLER_NOINTERP + case FMOD_DSP_RESAMPLER_NOINTERP: + { + FMOD_Resampler_NoInterp(mBuffer + (readoffset * mDescription.channels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mDescription.channels); + break; + } +#endif + case FMOD_DSP_RESAMPLER_LINEAR: + { + FMOD_Resampler_Linear(mBuffer + (readoffset * mDescription.channels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mDescription.channels); + break; + } +#ifdef FMOD_SUPPORT_RESAMPLER_CUBIC + case FMOD_DSP_RESAMPLER_CUBIC: + { + FMOD_Resampler_Cubic(mBuffer + (readoffset * mDescription.channels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mDescription.channels); + break; + } +#endif +#ifdef FMOD_SUPPORT_RESAMPLER_SPLINE + case FMOD_DSP_RESAMPLER_SPLINE: + { + FMOD_Resampler_Spline(mBuffer + (readoffset * mDescription.channels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mDescription.channels); + break; + } +#endif + default: + { + FMOD_Resampler_Linear(mBuffer + (readoffset * mDescription.channels), rlength, mResampleBuffer, mDescription.mFormat, &mResamplePosition, &speed, mDescription.channels); + break; + } + } + } + + if (mResamplePosition.mHi >= (mResampleBufferLength + mOverflowLength)) + { + mResamplePosition.mHi -= mResampleBufferLength; + } + + len -= rlength; + readoffset += rlength; + + if (endflag == FMOD_RESAMPLER_END_SOUND) + { + FMOD_memset(mBuffer + (readoffset * mDescription.channels), 0, len * mDescription.channels * sizeof(float)); + mNoDMA->mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + break; + } + else if (endflag == FMOD_RESAMPLER_END_RESAMPLEBUFFER) + { + mFill++; + } + }; + + /* + Update mPosition and execute any loop / end logic. + THIS INFO IS FOR DISPLAY ONLY. + */ + if (mLength < mNoDMA->mLoopStart + mNoDMA->mLoopLength) + { + mNoDMA->mLoopLength = mLength - mNoDMA->mLoopStart; + } + + if (mDirection == DSPRESAMPLER_SPEEDDIR_BACKWARDS) + { + FMOD_UINT64P len, endpoint; + + if (mNoDMA->mMode & FMOD_LOOP_NORMAL && mLoopCount) + { + endpoint.mHi = mNoDMA->mLoopStart; + endpoint.mLo = 0; + } + else + { + endpoint.mHi = 0; + endpoint.mLo = 0; + } + + len.mValue = (-speed.mValue * inlength); + + mPosition.mValue += len.mValue; + + if ((signed int)mPosition.mHi <= (signed int)endpoint.mHi) + { + if ((mNoDMA->mMode & FMOD_LOOP_NORMAL) || mLength == (unsigned int)-1 && mLength) /* 0xFFFFFFFF should mean it is an infinite netstream */ + { + mPosition.mHi += mNoDMA->mLoopLength; + } + else + { + mPosition.mHi = 0; + } + } + } + else + { + FMOD_UINT64P len, endpoint; + + if (mLength < mNoDMA->mLoopStart + mNoDMA->mLoopLength) + { + mNoDMA->mLoopLength = mLength - mNoDMA->mLoopStart; + } + + if (mNoDMA->mMode & FMOD_LOOP_NORMAL && mLoopCount) + { + endpoint.mHi = mNoDMA->mLoopStart + mNoDMA->mLoopLength -1; + endpoint.mLo = 0xFFFFFFFF; + } + else + { + endpoint.mHi = mLength - 1; + endpoint.mLo = 0xFFFFFFFF; + } + + len.mValue = (speed.mValue * inlength); + + mPosition.mValue += len.mValue; + + if (mPosition.mValue > endpoint.mValue) + { + if ((mNoDMA->mMode & FMOD_LOOP_NORMAL) || mLength == (unsigned int)-1 && mLength) /* 0xFFFFFFFF should mean it is an infinite netstream */ + { + mPosition.mHi -= mNoDMA->mLoopLength; + } + else + { + mPosition.mHi = mLength; + } + } + } + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsage = endtime - starttime; + +#if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) && !defined(PLATFORM_PS3) + calculatePeaks(mBuffer, *length, mDescription.channels); +#endif + } + } + + *outbuffer = mBuffer; /* Return the already processed buffer */ + *outchannels = mDescription.channels; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::addInput(DSPI *target) +{ + return FMOD_ERR_DSP_CONNECTION; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::setFrequency(float frequency) +{ + if (frequency < 0) + { + mDirection = DSPRESAMPLER_SPEEDDIR_BACKWARDS; + frequency = -frequency; + } + else + { + mDirection = DSPRESAMPLER_SPEEDDIR_FORWARDS; + } + + mFrequency = frequency; + + mSpeed.mValue = (FMOD_SINT64)(mFrequency / mTargetFrequency * 4294967296.0f); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::getFrequency(float *frequency) +{ + if (!frequency) + { + return FMOD_ERR_INVALID_PARAM; + } + + *frequency = mFrequency; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::setPosition(unsigned int position, bool processinputs) +{ + FMOD_RESULT result; + + result = DSPI::setPosition(position, processinputs); + if (result != FMOD_OK) + { + return result; + } + + mResampleBufferPos = 0; + mResampleFinishPos = (unsigned int)-1; + mFill = 2; + mResamplePosition.mValue = 0; + mPosition.mHi = position; + mPosition.mLo = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::setFinished(bool finished, bool force) +{ + if (!finished) + { + mNoDMA->mDSPFinishTick = 0xFFFFFFFF; + mFlags &= ~FMOD_DSP_FLAG_FINISHED; + return FMOD_OK; + } + + if (force) + { + #ifndef PLATFORM_PS3_SPU + FMOD_OS_CriticalSection_Enter(mSystem->mDSPCrit); /* because it came from virtual voice swap most likely and needs to block/sync. */ + #endif + + mNoDMA->mDSPFinishTick = 0; + + #ifndef PLATFORM_PS3_SPU + FMOD_OS_CriticalSection_Leave(mSystem->mDSPCrit); + #endif + } + else + { + if (mDSPSoundCard) + { + mNoDMA->mDSPFinishTick = mDSPSoundCard->mDSPTick + 1; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResampler::getFinished(bool *finished) +{ + if (!mDSPSoundCard) + { + *finished = true; + return FMOD_OK; + } + + if ((mNoDMA->mDSPFinishTick >= mDSPSoundCard->mDSPTick && !(mFlags & FMOD_DSP_FLAG_FINISHED)) || (mFlags & FMOD_DSP_FLAG_QUEUEDFORDISCONNECT)) + { + *finished = false; /* hasn't really finished yet, still mixing. */ + } + else + { + *finished = true; + } + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPResampler::getMemoryUsedImpl(MemoryTracker *tracker) +{ + + return FMOD_OK; +} + +#endif + + +} + +#endif diff --git a/src/fmod_dsp_resampler.h b/src/fmod_dsp_resampler.h new file mode 100755 index 0000000..4901b01 --- /dev/null +++ b/src/fmod_dsp_resampler.h @@ -0,0 +1,109 @@ +#ifndef _FMOD_DSP_RESAMPLER_H +#define _FMOD_DSP_RESAMPLER_H + +#include "fmod_settings.h" + +#include "fmod_dsp_filter.h" + +namespace FMOD +{ + typedef FMOD_RESULT (F_CALLBACK *FMOD_RESAMPLE_CALLBACK)(FMOD_DSP_STATE *dsp, FMOD_UINT64P numsamples, void *userdata); + + const int FMOD_DSP_RESAMPLER_OVERFLOWLENGTH = 4; /* Enough extra samples for the pcm wraparound */ + + typedef enum + { + FMOD_RESAMPLER_END_MIXBUFFER, + FMOD_RESAMPLER_END_RESAMPLEBUFFER, + FMOD_RESAMPLER_END_SOUND, + FMOD_RESAMPLER_END_SUBSOUND + } FMOD_RESAMPLER_END; + + typedef enum + { + DSPRESAMPLER_SPEEDDIR_FORWARDS, + DSPRESAMPLER_SPEEDDIR_BACKWARDS, + } DSPRESAMPLER_SPEEDDIR; + + struct DSPResampler_NODMA + { + FMOD_UINT64P mDSPClockStart FMOD_PACKED_INTERNAL; // 8 + FMOD_UINT64P mDSPClockEnd FMOD_PACKED_INTERNAL; // 8 + FMOD_UINT64P mDSPClockPause FMOD_PACKED_INTERNAL; // 8 + FMOD_MODE mMode FMOD_PACKED_INTERNAL; // 4 + unsigned int mLoopStart FMOD_PACKED_INTERNAL; // 4 + unsigned int mLoopLength FMOD_PACKED_INTERNAL; // 4 + unsigned int mDSPFinishTick FMOD_PACKED_INTERNAL; // 4 + int mNewLoopCount FMOD_PACKED_INTERNAL; // 4 + unsigned int mLoopCountIncrement FMOD_PACKED_INTERNAL; // 4 + unsigned int mNewPosition FMOD_PACKED_INTERNAL; // 4 + unsigned int mSetPosIncrement FMOD_PACKED_INTERNAL; // 4 + + #ifdef PLATFORM_PS3 + unsigned int mPad[2] FMOD_PACKED_INTERNAL; // 8 + #endif + } FMOD_PACKED; // = 64 /* Keep 16 byte aligned? */ + + class DSPResampler : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + + FMOD_UINT64P mPosition; + FMOD_SINT64P mSpeed; + float mFrequency; + int mTargetFrequency; + FMOD_UINT64P mResamplePosition; + void *mResampleBufferMemory; + FMOD_PPCALIGN16(void *mResampleBuffer); + FMOD_PPCALIGN16(int mResampleBufferChannels); + FMOD_PPCALIGN16(unsigned int mResampleBlockLength); + unsigned int mResampleBufferLength; + unsigned int mResampleBufferPos; + unsigned int mResampleFinishPos; + unsigned int mOverflowLength; + unsigned int mReadPosition; + int mFill; + DSPI *mDSPSoundCard; + DSPRESAMPLER_SPEEDDIR mDirection; + + FMOD_PPCALIGN16(unsigned int mLength); + FMOD_PPCALIGN16(int mLoopCount); + FMOD_PPCALIGN16(DSPResampler_NODMA *mNoDMA); + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + DSPResampler_NODMA *mNoDMAMemory; + #else + DSPResampler_NODMA mNoDMAMemory; + #endif + + DSPResampler(); + + virtual FMOD_RESULT release(bool freethis = true); + FMOD_RESULT alloc(FMOD_DSP_DESCRIPTION_EX *description); + FMOD_RESULT setFrequency(float frequency); + FMOD_RESULT getFrequency(float *frequency); + FMOD_RESULT setPosition(unsigned int position, bool processinputs); + + #if defined(FMOD_SUPPORT_MIXER_NONRECURSIVE) + FMOD_RESULT update(unsigned int length, int *outchannels, void **outbuffer, unsigned int tick); + #else + virtual FMOD_RESULT addInput(DSPI *target); + virtual FMOD_RESULT read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + #endif + + FMOD_RESULT setFinished (bool finished, bool force = false); + FMOD_RESULT getFinished (bool *finished); + }; + + #ifdef PLATFORM_PS3 + class DSPResamplerPS3 : public DSPResampler + { + public: + char mResampleBufferMemory[((512 + (FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * 4)) * sizeof(short) * 8 * 2) + 16]; // *8 = 8 channel max. *2 = double buffer. + }; + #endif +} + +#endif + diff --git a/src/fmod_dsp_resampler_cubic.cpp b/src/fmod_dsp_resampler_cubic.cpp new file mode 100755 index 0000000..fc2d725 --- /dev/null +++ b/src/fmod_dsp_resampler_cubic.cpp @@ -0,0 +1,583 @@ +#include "fmod_settings.h" + +#include "fmod_dsp_resampler_cubic.h" + +#define OO4GIG (1.0f / 4294967296.0f) + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void FMOD_Resampler_Cubic(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels) +{ + float scale = 1.0f; + + switch (srcformat) + { + /* + 8 BIT + */ + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *inptr = (signed char *)src; + + scale /= (float)(1<<7); + + if (channels == 1) + { + float f; + float p0,p1,p2,p3; + float r1,r2,r3,r4; + float a,b,c; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r2 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r3 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r4 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float p0,p1,p2,p3; + float r; + float a,b,c; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + p0 = (float)inptr[((position->mHi - 1) * channels) + count] * scale; + p1 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + p2 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + p3 = (float)inptr[((position->mHi + 2) * channels) + count] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r = (((a * f) + b) * f + c) * f + p1; + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 16 BIT + */ + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *inptr = (signed short *)src; + + scale /= (float)(1<<15); + + if (channels == 1) + { + float f; + float p0,p1,p2,p3; + float r1,r2,r3,r4; + float a,b,c; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r2 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r3 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r4 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float p0,p1,p2,p3; + float r; + float a,b,c; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + p0 = (float)inptr[((position->mHi - 1) * channels) + count] * scale; + p1 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + p2 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + p3 = (float)inptr[((position->mHi + 2) * channels) + count] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r = (((a * f) + b) * f + c) * f + p1; + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 24 BIT + */ + case FMOD_SOUND_FORMAT_PCM24: + { + FMOD_INT24 *inptr = (FMOD_INT24 *)src; + + scale /= (float)(1<<23); + + if (channels == 1) + { + while (outlength) + { + float r; + float a,b,c; + float f = position->mLo * OO4GIG; + FMOD_INT24 *s0 = &inptr[position->mHi - 1]; + FMOD_INT24 *s1 = &inptr[position->mHi + 0]; + FMOD_INT24 *s2 = &inptr[position->mHi + 1]; + FMOD_INT24 *s3 = &inptr[position->mHi + 2]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + float p1 = (float)((int)(((unsigned int)s1->val[0] << 8) | ((unsigned int)s1->val[1] << 16) | ((unsigned int)s1->val[2] << 24)) >> 8) * scale; + float p2 = (float)((int)(((unsigned int)s2->val[0] << 8) | ((unsigned int)s2->val[1] << 16) | ((unsigned int)s2->val[2] << 24)) >> 8) * scale; + float p3 = (float)((int)(((unsigned int)s3->val[0] << 8) | ((unsigned int)s3->val[1] << 16) | ((unsigned int)s3->val[2] << 24)) >> 8) * scale; + + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r = (((a * f) + b) * f + c) * f + p1; + *out++ = r; + + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float r; + float a,b,c; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + FMOD_INT24 *s0 = &inptr[((position->mHi - 1) * channels) + count]; + FMOD_INT24 *s1 = &inptr[((position->mHi + 0) * channels) + count]; + FMOD_INT24 *s2 = &inptr[((position->mHi + 1) * channels) + count]; + FMOD_INT24 *s3 = &inptr[((position->mHi + 2) * channels) + count]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + float p1 = (float)((int)(((unsigned int)s1->val[0] << 8) | ((unsigned int)s1->val[1] << 16) | ((unsigned int)s1->val[2] << 24)) >> 8) * scale; + float p2 = (float)((int)(((unsigned int)s2->val[0] << 8) | ((unsigned int)s2->val[1] << 16) | ((unsigned int)s2->val[2] << 24)) >> 8) * scale; + float p3 = (float)((int)(((unsigned int)s3->val[0] << 8) | ((unsigned int)s3->val[1] << 16) | ((unsigned int)s3->val[2] << 24)) >> 8) * scale; + + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r = (((a * f) + b) * f + c) * f + p1; + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 32 BIT + */ + case FMOD_SOUND_FORMAT_PCM32: + { + signed int *inptr = (signed int *)src; + + scale /= (float)(1U<<31); + + if (channels == 1) + { + float f; + float p0,p1,p2,p3; + float r1,r2,r3,r4; + float a,b,c; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r2 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r3 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r4 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + p0 = (float)inptr[position->mHi - 1] * scale; + p1 = (float)inptr[position->mHi + 0] * scale; + p2 = (float)inptr[position->mHi + 1] * scale; + p3 = (float)inptr[position->mHi + 2] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float p0,p1,p2,p3; + float r; + float a,b,c; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + p0 = (float)inptr[((position->mHi - 1) * channels) + count] * scale; + p1 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + p2 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + p3 = (float)inptr[((position->mHi + 2) * channels) + count] * scale; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r = (((a * f) + b) * f + c) * f + p1; + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + FLOATING POINT. + */ + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + float *inptr = (float *)src; + + if (channels == 1) + { + float f; + float p0,p1,p2,p3; + float r1,r2,r3,r4; + float a,b,c; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + p0 = inptr[position->mHi - 1]; + p1 = inptr[position->mHi + 0]; + p2 = inptr[position->mHi + 1]; + p3 = inptr[position->mHi + 2]; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = inptr[position->mHi - 1]; + p1 = inptr[position->mHi + 0]; + p2 = inptr[position->mHi + 1]; + p3 = inptr[position->mHi + 2]; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r2 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = inptr[position->mHi - 1]; + p1 = inptr[position->mHi + 0]; + p2 = inptr[position->mHi + 1]; + p3 = inptr[position->mHi + 2]; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r3 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + p0 = inptr[position->mHi - 1]; + p1 = inptr[position->mHi + 0]; + p2 = inptr[position->mHi + 1]; + p3 = inptr[position->mHi + 2]; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r4 = (((a * f) + b) * f + c) * f + p1; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + p0 = inptr[position->mHi - 1]; + p1 = inptr[position->mHi + 0]; + p2 = inptr[position->mHi + 1]; + p3 = inptr[position->mHi + 2]; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r1 = (((a * f) + b) * f + c) * f + p1; + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float p0,p1,p2,p3; + float r; + float a,b,c; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + p0 = inptr[((position->mHi - 1) * channels) + count]; + p1 = inptr[((position->mHi + 0) * channels) + count]; + p2 = inptr[((position->mHi + 1) * channels) + count]; + p3 = inptr[((position->mHi + 2) * channels) + count]; + a = (3.0f * (p1-p2) - p0 + p3) / 2.0f; + b = 2.0f * p2 + p0 - (5.0f * p1 + p3) / 2.0f; + c = (p2 - p0) / 2.0f; + r = (((a * f) + b) * f + c) * f + p1; + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + default: + { + break; + } + }; +} + + + diff --git a/src/fmod_dsp_resampler_cubic.h b/src/fmod_dsp_resampler_cubic.h new file mode 100755 index 0000000..a5fc967 --- /dev/null +++ b/src/fmod_dsp_resampler_cubic.h @@ -0,0 +1,21 @@ +#ifndef _FMOD_DSP_RESAMPLER_CUBIC_H +#define _FMOD_DSP_RESAMPLER_CUBIC_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + void FMOD_Resampler_Cubic(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fmod_dsp_resampler_linear.h b/src/fmod_dsp_resampler_linear.h new file mode 100755 index 0000000..f0602b9 --- /dev/null +++ b/src/fmod_dsp_resampler_linear.h @@ -0,0 +1,21 @@ +#ifndef _FMOD_DSP_RESAMPLER_LINEAR_H +#define _FMOD_DSP_RESAMPLER_LINEAR_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + void FMOD_Resampler_Linear(float * FMOD_RESTRICT out, int outlength, void * FMOD_RESTRICT src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fmod_dsp_resampler_multiinput.cpp b/src/fmod_dsp_resampler_multiinput.cpp new file mode 100755 index 0000000..9ec6db5 --- /dev/null +++ b/src/fmod_dsp_resampler_multiinput.cpp @@ -0,0 +1,321 @@ +#include "fmod_settings.h" + +#ifndef PLATFORM_WINDOWS_PS3MODE + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_dsp_resampler_nointerp.h" +#include "fmod_dsp_resampler_linear.h" +#include "fmod_dsp_resampler_cubic.h" +#include "fmod_dsp_resampler_spline.h" +#include "fmod_dsp_resampler_multiinput.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResamplerMultiInput::read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + FMOD_RESULT result = FMOD_OK; + + if (tick >= mNoDMA->mDSPFinishTick) + { + return FMOD_OK; + } + + mFlags |= FMOD_DSP_FLAG_IDLE; + + if (mDSPTick != tick) + { + float *readbuffer = mBuffer; + int inlength = *length; + int readoffset = 0; + unsigned int starttime = 0; + unsigned int endtime = 0; + FMOD_SINT64P speed; + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + speed.mValue = mSpeed.mValue; + + if (!mBuffer) + { + return FMOD_ERR_INTERNAL; + } + + do + { + unsigned int rlength; + FMOD_RESAMPLER_END endflag = FMOD_RESAMPLER_END_MIXBUFFER; + + while (mFill) + { + unsigned int readlen, oldresamplebufferpos; + + /* + Before a fill happens, write the end of the second block to the negative buffer area. + */ + oldresamplebufferpos = mResampleBufferPos; + + /* + Now call the input and convert it to float. + */ + readlen = mResampleBlockLength; + + { + float *resamplebuff = (float *)mResampleBuffer + (mResampleBufferPos * mResampleBufferChannels); + float *outbuff = 0; + int outch; + + updateDSPTick(tick - 1); /* Reset the visited flags so we can execute the children again. */ + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsageTemp += endtime - starttime; + } + + result = DSPFilter::read(&outbuff, &outch, &readlen, speakermode, speakermodechannels, tick); + if (result != FMOD_OK) + { + outbuff = resamplebuff; + FMOD_memset(outbuff, 0, readlen * mResampleBufferChannels * sizeof(float)); + mResampleFinishPos = mResampleBufferPos; + } + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + mResampleBufferChannels = outch; + FMOD_memcpy(resamplebuff, outbuff, readlen * mResampleBufferChannels * sizeof(float)); + + mResampleBufferPos += readlen; + if (mResampleBufferPos >= mResampleBufferLength) + { + mResampleBufferPos = 0; + } + } + + /* + Copy the first samples to the end of the buffer so when interpolation does a read past the end, the data will be valid. + */ + if (!oldresamplebufferpos) + { + unsigned int count; + + for (count=0; count < mResampleBufferChannels * mOverflowLength * 2; count++) + { + ((float *)mResampleBuffer)[(mResampleBufferLength * mResampleBufferChannels) + count] = ((float *)mResampleBuffer)[count]; + } + } + + mFill--; + } + + /* + Work out what is going to end first. The source data or the output? + */ + rlength = inlength; /* Start it off defaulting to the amount requested */ + if (speed.mValue > 0x100) + { + FMOD_UINT64P mixesleft, fracleft; + int nextpos; + bool endofsound = false; + + nextpos = mResamplePosition.mHi - mOverflowLength; + nextpos /= (int)mResampleBlockLength; + nextpos ++; + nextpos *= mResampleBlockLength; + nextpos += mOverflowLength; + + mixesleft.mHi = nextpos; + mixesleft.mLo = 0; + mixesleft.mValue -= mResamplePosition.mValue; +/* + if (connection->mInputUnit->mFinished) + { + FMOD_UINT64P mixeslefttofinish; + + mixeslefttofinish.mHi = mResampleFinishPos; + mixeslefttofinish.mLo = 0; + mixeslefttofinish.mValue -= mPosition.mValue; + + if (mixeslefttofinish.mValue <= mixesleft.mValue) + { + mixesleft.mValue = mixeslefttofinish.mValue; + endofsound = true; + } + } +*/ + + fracleft.mValue = mixesleft.mValue % speed.mValue; + mixesleft.mValue /= speed.mValue; + + if (fracleft.mValue) /* round the count up (this could be done better) */ + { + mixesleft.mValue++; + } + + if (mixesleft.mValue <= rlength) + { + if (endofsound) + { + endflag = FMOD_RESAMPLER_END_SOUND; + } + else + { + endflag = FMOD_RESAMPLER_END_RESAMPLEBUFFER; + } + rlength = mixesleft.mLo; + } + } + + /* + Resample the src buffer into the destination. + */ + if (speed.mHi == 1 && speed.mLo == 0) + { + FMOD_memcpy(readbuffer + (readoffset * mResampleBufferChannels), (float *)mResampleBuffer + (mResamplePosition.mHi * mResampleBufferChannels), rlength * sizeof(float) * mResampleBufferChannels); + + mResamplePosition.mValue += speed.mValue * rlength; + } + else + { + switch (mSystem->mResampleMethod) + { + case FMOD_DSP_RESAMPLER_NOINTERP: + { + FMOD_Resampler_NoInterp(readbuffer + (readoffset * mResampleBufferChannels), rlength, mResampleBuffer, FMOD_SOUND_FORMAT_PCMFLOAT, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } + case FMOD_DSP_RESAMPLER_LINEAR: + { + FMOD_Resampler_Linear(readbuffer + (readoffset * mResampleBufferChannels), rlength, mResampleBuffer, FMOD_SOUND_FORMAT_PCMFLOAT, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } +#ifdef FMOD_SUPPORT_RESAMPLER_CUBIC + case FMOD_DSP_RESAMPLER_CUBIC: + { + FMOD_Resampler_Cubic(readbuffer + (readoffset * mResampleBufferChannels), rlength, mResampleBuffer, FMOD_SOUND_FORMAT_PCMFLOAT, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } +#endif +#ifdef FMOD_SUPPORT_RESAMPLER_SPLINE + case FMOD_DSP_RESAMPLER_SPLINE: + { + FMOD_Resampler_Spline(readbuffer + (readoffset * mResampleBufferChannels), rlength, mResampleBuffer, FMOD_SOUND_FORMAT_PCMFLOAT, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } +#endif + default: + { + FMOD_Resampler_Linear(readbuffer + (readoffset * mResampleBufferChannels), rlength, mResampleBuffer, FMOD_SOUND_FORMAT_PCMFLOAT, &mResamplePosition, &speed, mResampleBufferChannels); + break; + } + } + } + + if (mResamplePosition.mHi >= (mResampleBufferLength + mOverflowLength)) + { + mResamplePosition.mHi -= mResampleBufferLength; + } + + inlength -= rlength; + readoffset += rlength; + + mFlags &= ~FMOD_DSP_FLAG_IDLE; + + if (endflag == FMOD_RESAMPLER_END_SOUND) + { + mNoDMA->mDSPFinishTick = tick; + break; + } + else if (endflag == FMOD_RESAMPLER_END_RESAMPLEBUFFER) + { + mFill++; + } + + } while (inlength > 0); + + *outbuffer = readbuffer; + *outchannels = mResampleBufferChannels; + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsageTemp += endtime - starttime; + + mCPUUsage = mCPUUsageTemp; + mCPUUsageTemp = 0; + } + } + else + { + *outbuffer = mBuffer; /* Return the already processed buffer */ + *outchannels = mResampleBufferChannels; + mFlags &= ~FMOD_DSP_FLAG_IDLE; + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPResamplerMultiInput::addInput(DSPI *target) +{ + FMOD_RESULT result; + + result = DSPI::addInput(target); + if (result != FMOD_OK) + { + return result; + } + + mResampleBufferPos = 0; + mFill = 2; + mPosition.mValue = 0; + mResamplePosition.mValue = 0; + + return FMOD_OK; +} + + +} + +#endif +#endif + diff --git a/src/fmod_dsp_resampler_multiinput.h b/src/fmod_dsp_resampler_multiinput.h new file mode 100755 index 0000000..95fe78b --- /dev/null +++ b/src/fmod_dsp_resampler_multiinput.h @@ -0,0 +1,21 @@ +#ifndef _FMOD_DSP_RESAMPLER_MULTIINPUT_H +#define _FMOD_DSP_RESAMPLER_MULTIINPUT_H + +#include "fmod_settings.h" + +#include "fmod_dsp_filter.h" +#include "fmod_dsp_resampler.h" + +namespace FMOD +{ + class DSPResamplerMultiInput : public DSPResampler + { + public: + + FMOD_RESULT read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + FMOD_RESULT addInput(DSPI *target); + }; +} + +#endif + diff --git a/src/fmod_dsp_resampler_nointerp.cpp b/src/fmod_dsp_resampler_nointerp.cpp new file mode 100755 index 0000000..7af86ff --- /dev/null +++ b/src/fmod_dsp_resampler_nointerp.cpp @@ -0,0 +1,383 @@ +#include "fmod_settings.h" + +#include "fmod_dsp_resampler_nointerp.h" + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void FMOD_Resampler_NoInterp(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels) +{ + float scale = 1.0f; + + switch (srcformat) + { + /* + 8 BIT + */ + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *inptr = (signed char *)src; + + scale /= (float)(1<<7); + + if (channels == 1) + { + float r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + r1 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r2 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r3 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r4 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + *out++ = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + + for (count = 0; count < channels; count++) + { + *out++ = (float)inptr[(position->mHi * channels) + count] * scale; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 16 BIT + */ + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *inptr = (signed short *)src; + + scale /= (float)(1<<15); + + if (channels == 1) + { + float r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + r1 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r2 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r3 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r4 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + *out++ = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + len--; + } + } + else if (channels == 2) + { + float r1l,r2l,r3l,r4l; + float r1r,r2r,r3r,r4r; + int len; + + len = outlength >> 2; + while (len) + { + r1l = (float)inptr[(position->mHi<<1) + 0] * scale; + r1r = (float)inptr[(position->mHi<<1) + 1] * scale; + position->mValue += speed->mValue; + r2l = (float)inptr[(position->mHi<<1) + 0] * scale; + r2r = (float)inptr[(position->mHi<<1) + 1] * scale; + position->mValue += speed->mValue; + r3l = (float)inptr[(position->mHi<<1) + 0] * scale; + r3r = (float)inptr[(position->mHi<<1) + 1] * scale; + position->mValue += speed->mValue; + r4l = (float)inptr[(position->mHi<<1) + 0] * scale; + r4r = (float)inptr[(position->mHi<<1) + 1] * scale; + position->mValue += speed->mValue; + + out[0] = r1l; + out[1] = r1r; + out[2] = r2l; + out[3] = r2r; + out[4] = r3l; + out[5] = r3r; + out[6] = r4l; + out[7] = r4r; + len--; + out+=8; + } + + len = outlength & 3; + while (len) + { + *out++ = (float)inptr[(position->mHi<<1) + 0] * scale; + *out++ = (float)inptr[(position->mHi<<1) + 1] * scale; + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + + for (count = 0; count < channels; count++) + { + *out++ = (float)inptr[(position->mHi * channels) + count] * scale; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 24 BIT + */ + case FMOD_SOUND_FORMAT_PCM24: + { + FMOD_INT24 *inptr = (FMOD_INT24 *)src; + + scale /= (float)(1<<23); + + if (channels == 1) + { + int len; + + len = outlength >> 2; + while (len) + { + FMOD_INT24 *s; + float p0, p1, p2, p3; + + s = &inptr[position->mHi]; + p0 = (float)((int)(((unsigned int)s->val[0] << 8) | ((unsigned int)s->val[1] << 16) | ((unsigned int)s->val[2] << 24)) >> 8) * scale; + position->mValue += speed->mValue; + s = &inptr[position->mHi]; + p1 = (float)((int)(((unsigned int)s->val[0] << 8) | ((unsigned int)s->val[1] << 16) | ((unsigned int)s->val[2] << 24)) >> 8) * scale; + position->mValue += speed->mValue; + s = &inptr[position->mHi]; + p2 = (float)((int)(((unsigned int)s->val[0] << 8) | ((unsigned int)s->val[1] << 16) | ((unsigned int)s->val[2] << 24)) >> 8) * scale; + position->mValue += speed->mValue; + s = &inptr[position->mHi]; + p3 = (float)((int)(((unsigned int)s->val[0] << 8) | ((unsigned int)s->val[1] << 16) | ((unsigned int)s->val[2] << 24)) >> 8) * scale; + position->mValue += speed->mValue; + + out[0] = p0; + out[1] = p1; + out[2] = p2; + out[3] = p3; + out+=4; + len--; + } + + len = outlength & 3; + while (len) + { + FMOD_INT24 *s0 = &inptr[position->mHi]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + + *out++ = p0; + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + + for (count = 0; count < channels; count++) + { + FMOD_INT24 *s0 = &inptr[(position->mHi * channels) + count]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + + *out++ = p0; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 32 BIT + */ + case FMOD_SOUND_FORMAT_PCM32: + { + signed int *inptr = (signed int *)src; + + scale /= (float)(1U<<31); + + if (channels == 1) + { + float r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + r1 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r2 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r3 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + r4 = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + *out++ = (float)inptr[position->mHi] * scale; + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + + for (count = 0; count < channels; count++) + { + *out++ = (float)inptr[(position->mHi * channels) + count] * scale; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + FLOATING POINT. + */ + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + float *inptr = (float *)src; + + if (channels == 1) + { + float r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + r1 = inptr[position->mHi]; + position->mValue += speed->mValue; + r2 = inptr[position->mHi]; + position->mValue += speed->mValue; + r3 = inptr[position->mHi]; + position->mValue += speed->mValue; + r4 = inptr[position->mHi]; + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + *out++ = inptr[position->mHi]; + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + + for (count = 0; count < channels; count++) + { + *out++ = inptr[(position->mHi * channels) + count]; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + default: + { + break; + } + }; +} + + + diff --git a/src/fmod_dsp_resampler_nointerp.h b/src/fmod_dsp_resampler_nointerp.h new file mode 100755 index 0000000..b4c09ba --- /dev/null +++ b/src/fmod_dsp_resampler_nointerp.h @@ -0,0 +1,21 @@ +#ifndef _FMOD_DSP_RESAMPLER_NOINTERP_H +#define _FMOD_DSP_RESAMPLER_NOINTERP_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + void FMOD_Resampler_NoInterp(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fmod_dsp_resampler_spline.cpp b/src/fmod_dsp_resampler_spline.cpp new file mode 100755 index 0000000..c4ecc7c --- /dev/null +++ b/src/fmod_dsp_resampler_spline.cpp @@ -0,0 +1,378 @@ +#include "fmod_settings.h" + +#include "fmod_dsp_resampler_spline.h" + +#define OO4GIG (1.0f / 4294967296.0f) + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void FMOD_Resampler_Spline(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels) +{ + float scale = 1.0f; + + switch (srcformat) + { + /* + 8 BIT + */ + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *inptr = (signed char *)src; + + scale /= (float)(1<<7); + + if (channels == 1) + { + while (outlength) + { + float f = position->mLo * OO4GIG; + float r; + float p0 = (float)inptr[position->mHi - 2] * scale; + float p1 = (float)inptr[position->mHi - 1] * scale; + float p2 = (float)inptr[position->mHi + 0] * scale; + float p3 = (float)inptr[position->mHi + 1] * scale; + float p4 = (float)inptr[position->mHi + 2] * scale; + float p5 = (float)inptr[position->mHi + 3] * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float r; + float p0 = (float)inptr[((position->mHi - 2) * channels) + count] * scale; + float p1 = (float)inptr[((position->mHi - 1) * channels) + count] * scale; + float p2 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + float p3 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + float p4 = (float)inptr[((position->mHi + 2) * channels) + count] * scale; + float p5 = (float)inptr[((position->mHi + 3) * channels) + count] * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 16 BIT + */ + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *inptr = (signed short *)src; + + scale /= (float)(1<<15); + + if (channels == 1) + { + while (outlength) + { + float f = position->mLo * OO4GIG; + float r; + float p0 = (float)inptr[position->mHi - 2] * scale; + float p1 = (float)inptr[position->mHi - 1] * scale; + float p2 = (float)inptr[position->mHi + 0] * scale; + float p3 = (float)inptr[position->mHi + 1] * scale; + float p4 = (float)inptr[position->mHi + 2] * scale; + float p5 = (float)inptr[position->mHi + 3] * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float r; + float p0 = (float)inptr[((position->mHi - 2) * channels) + count] * scale; + float p1 = (float)inptr[((position->mHi - 1) * channels) + count] * scale; + float p2 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + float p3 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + float p4 = (float)inptr[((position->mHi + 2) * channels) + count] * scale; + float p5 = (float)inptr[((position->mHi + 3) * channels) + count] * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 24 BIT + */ + case FMOD_SOUND_FORMAT_PCM24: + { + FMOD_INT24 *inptr = (FMOD_INT24 *)src; + + scale /= (float)(1<<23); + + if (channels == 1) + { + while (outlength) + { + float f = position->mLo * OO4GIG; + float r; + FMOD_INT24 *s0 = &inptr[position->mHi - 2]; + FMOD_INT24 *s1 = &inptr[position->mHi - 1]; + FMOD_INT24 *s2 = &inptr[position->mHi + 0]; + FMOD_INT24 *s3 = &inptr[position->mHi + 1]; + FMOD_INT24 *s4 = &inptr[position->mHi + 2]; + FMOD_INT24 *s5 = &inptr[position->mHi + 3]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + float p1 = (float)((int)(((unsigned int)s1->val[0] << 8) | ((unsigned int)s1->val[1] << 16) | ((unsigned int)s1->val[2] << 24)) >> 8) * scale; + float p2 = (float)((int)(((unsigned int)s2->val[0] << 8) | ((unsigned int)s2->val[1] << 16) | ((unsigned int)s2->val[2] << 24)) >> 8) * scale; + float p3 = (float)((int)(((unsigned int)s3->val[0] << 8) | ((unsigned int)s3->val[1] << 16) | ((unsigned int)s3->val[2] << 24)) >> 8) * scale; + float p4 = (float)((int)(((unsigned int)s4->val[0] << 8) | ((unsigned int)s4->val[1] << 16) | ((unsigned int)s4->val[2] << 24)) >> 8) * scale; + float p5 = (float)((int)(((unsigned int)s5->val[0] << 8) | ((unsigned int)s5->val[1] << 16) | ((unsigned int)s5->val[2] << 24)) >> 8) * scale; + + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float r; + FMOD_INT24 *s0 = &inptr[((position->mHi - 2) * channels) + count]; + FMOD_INT24 *s1 = &inptr[((position->mHi - 1) * channels) + count]; + FMOD_INT24 *s2 = &inptr[((position->mHi + 0) * channels) + count]; + FMOD_INT24 *s3 = &inptr[((position->mHi + 1) * channels) + count]; + FMOD_INT24 *s4 = &inptr[((position->mHi + 2) * channels) + count]; + FMOD_INT24 *s5 = &inptr[((position->mHi + 3) * channels) + count]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + float p1 = (float)((int)(((unsigned int)s1->val[0] << 8) | ((unsigned int)s1->val[1] << 16) | ((unsigned int)s1->val[2] << 24)) >> 8) * scale; + float p2 = (float)((int)(((unsigned int)s2->val[0] << 8) | ((unsigned int)s2->val[1] << 16) | ((unsigned int)s2->val[2] << 24)) >> 8) * scale; + float p3 = (float)((int)(((unsigned int)s3->val[0] << 8) | ((unsigned int)s3->val[1] << 16) | ((unsigned int)s3->val[2] << 24)) >> 8) * scale; + float p4 = (float)((int)(((unsigned int)s4->val[0] << 8) | ((unsigned int)s4->val[1] << 16) | ((unsigned int)s4->val[2] << 24)) >> 8) * scale; + float p5 = (float)((int)(((unsigned int)s5->val[0] << 8) | ((unsigned int)s5->val[1] << 16) | ((unsigned int)s5->val[2] << 24)) >> 8) * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 32 BIT + */ + case FMOD_SOUND_FORMAT_PCM32: + { + signed int *inptr = (signed int *)src; + + scale /= (float)(1U<<31); + + if (channels == 1) + { + while (outlength) + { + float f = position->mLo * OO4GIG; + float r; + float p0 = (float)inptr[position->mHi - 2] * scale; + float p1 = (float)inptr[position->mHi - 1] * scale; + float p2 = (float)inptr[position->mHi + 0] * scale; + float p3 = (float)inptr[position->mHi + 1] * scale; + float p4 = (float)inptr[position->mHi + 2] * scale; + float p5 = (float)inptr[position->mHi + 3] * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float r; + float p0 = (float)inptr[((position->mHi - 2) * channels) + count] * scale; + float p1 = (float)inptr[((position->mHi - 1) * channels) + count] * scale; + float p2 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + float p3 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + float p4 = (float)inptr[((position->mHi + 2) * channels) + count] * scale; + float p5 = (float)inptr[((position->mHi + 3) * channels) + count] * scale; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + FLOATING POINT. + */ + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + float *inptr = (float *)src; + + if (channels == 1) + { + while (outlength) + { + float f = position->mLo * OO4GIG; + float r; + float p0 = inptr[position->mHi - 2]; + float p1 = inptr[position->mHi - 1]; + float p2 = inptr[position->mHi + 0]; + float p3 = inptr[position->mHi + 1]; + float p4 = inptr[position->mHi + 2]; + float p5 = inptr[position->mHi + 3]; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float r; + float p0 = inptr[((position->mHi - 2) * channels) + count]; + float p1 = inptr[((position->mHi - 1) * channels) + count]; + float p2 = inptr[((position->mHi + 0) * channels) + count]; + float p3 = inptr[((position->mHi + 1) * channels) + count]; + float p4 = inptr[((position->mHi + 2) * channels) + count]; + float p5 = inptr[((position->mHi + 3) * channels) + count]; + + r = p2 + 0.04166666666f * f * ((p3-p1) * 16.0f+(p0-p4) * 2.0f + + f * ((p3 + p1) * 16.0f - p0 - p2 * 30.0f - p4 + + f * (p3 * 66.0f - p2 * 70.0f - p4 * 33.0f + p1 * 39.0f + p5 * 7.0f - p0 * 9.0f + + f * (p2 * 126.0f - p3 * 124.0f + p4 * 61.0f - p1 * 64.0f - p5 * 12.0f + p0 * 13.0f + + f * ((p3 - p2) * 50.0f + (p1 - p4) * 25.0f + (p5 - p0) * 5.0f))))); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + default: + { + break; + } + }; +} + + + diff --git a/src/fmod_dsp_resampler_spline.h b/src/fmod_dsp_resampler_spline.h new file mode 100755 index 0000000..7d13716 --- /dev/null +++ b/src/fmod_dsp_resampler_spline.h @@ -0,0 +1,21 @@ +#ifndef _FMOD_DSP_RESAMPLER_SPLINE_H +#define _FMOD_DSP_RESAMPLER_SPLINE_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + void FMOD_Resampler_Spline(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fmod_dsp_reverb.cpp b/src/fmod_dsp_reverb.cpp new file mode 100755 index 0000000..ba2c5f9 --- /dev/null +++ b/src/fmod_dsp_reverb.cpp @@ -0,0 +1,597 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FREEVERB + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_reverb.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspreverb; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPReverb::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +FMOD_DSP_PARAMETERDESC dspreverb_param[6] = +{ + { 0.0f, 1.0f, 0.5f, "Roomsize", "", "Roomsize. 0.0 to 1.0. Default = 0.5" }, + { 0.0f, 1.0f, 0.5f, "Damp", "", "Damp. 0.0 to 1.0. Default = 0.5" }, + { 0.0f, 1.0f, 0.33f, "Wet", "", "Wet mix. 0.0 to 1.0. Default = 0.33" }, + { 0.0f, 1.0f, 0.66f, "Dry", "", "Dry mix. 0.0 to 1.0. Default = 0.66" }, + { 0.0f, 1.0f, 1.0f, "Width", "", "Width. 0.0 to 1.0. Default = 1.0" }, + { 0.0f, 1.0f, 0.0f, "Mode", "", "Mode. 0, 1. Default = 0" } +}; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPReverb::getDescriptionEx() +{ + FMOD_memset(&dspreverb, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspreverb.name, "FMOD Reverb"); + dspreverb.version = 0x00010100; + dspreverb.create = DSPReverb::createCallback; + dspreverb.release = DSPReverb::releaseCallback; + dspreverb.reset = DSPReverb::resetCallback; + dspreverb.read = DSPReverb::readCallback; + + dspreverb.numparameters = 6; + dspreverb.paramdesc = dspreverb_param; + dspreverb.setparameter = DSPReverb::setParameterCallback; + dspreverb.getparameter = DSPReverb::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspreverb.getmemoryused = &DSPReverb::getMemoryUsedCallback; +#endif + + dspreverb.mType = FMOD_DSP_TYPE_REVERB; + dspreverb.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspreverb.mSize = sizeof(DSPReverb); + + return &dspreverb; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPReverb::createInternal() +{ + int count; + + init(); + + new (&mReverb) revmodel; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPReverb::releaseInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPReverb::resetInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPReverb::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + float *inleft = inbuffer; + float *inright = inbuffer + 1; + + float *outleft = outbuffer; + float *outright = outbuffer + 1; + + if (!inbuffer) + { + return FMOD_OK; + } + + if ((speakermask & 0x3) == 0) /* Both speaker channels have been set to inactive */ + { + FMOD_memcpy(outbuffer, inbuffer, inchannels*length*sizeof(float)); + return FMOD_OK; + } + + if (inchannels > 2 || ((speakermask & 0x3) != 0x3)) + { + // Prevent output buffer crosstalk by ensuring every sample is set + FMOD_memcpy(outbuffer, inbuffer, inchannels*length*sizeof(float)); + } + + mReverb.processreplace(inleft, inright, outleft, outright, length, inchannels, speakermask); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPReverb::setParameterInternal(int index, float value) +{ + mSystem->lockDSP(); + + switch (index) + { + case FMOD_DSP_REVERB_ROOMSIZE: + { + if (value > 1.0f) + { + value = 1.0f; + } + else if (value < 0.0f) + { + value = 0.0f; + } + mReverb.setroomsize(value); + break; + } + case FMOD_DSP_REVERB_DAMP: + { + if (value > 1.0f) + { + value = 1.0f; + } + else if (value < 0.0f) + { + value = 0.0f; + } + mReverb.setdamp(value); + break; + } + case FMOD_DSP_REVERB_WETMIX: + { + if (value > 1.0f) + { + value = 1.0f; + } + else if (value < 0.0f) + { + value = 0.0f; + } + mReverb.setwet(value); + break; + } + case FMOD_DSP_REVERB_DRYMIX: + { + if (value > 1.0f) + { + value = 1.0f; + } + else if (value < 0.0f) + { + value = 0.0f; + } + mReverb.setdry(value); + break; + } + case FMOD_DSP_REVERB_WIDTH: + { + if (value > 1.0f) + { + value = 1.0f; + } + else if (value < 0.0f) + { + value = 0.0f; + } + mReverb.setwidth(value); + break; + } + case FMOD_DSP_REVERB_MODE: + { + if (value >= freezemode) + { + value = 1.0f; + } + else + { + value = 0.0f; + } + mReverb.setmode(value); + break; + } + } + + mSystem->unlockDSP(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPReverb::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_REVERB_ROOMSIZE: + { + *value = mReverb.getroomsize(); + sprintf(valuestr, "%0.2f", *value); + break; + } + case FMOD_DSP_REVERB_DAMP: + { + *value = mReverb.getdamp(); + sprintf(valuestr, "%0.2f", *value); + break; + } + case FMOD_DSP_REVERB_WETMIX: + { + *value = mReverb.getwet(); + sprintf(valuestr, "%0.2f", *value); + break; + } + case FMOD_DSP_REVERB_DRYMIX: + { + *value = mReverb.getdry(); + sprintf(valuestr, "%0.2f", *value); + break; + } + case FMOD_DSP_REVERB_WIDTH: + { + *value = mReverb.getwidth(); + sprintf(valuestr, "%0.2f", *value); + break; + } + case FMOD_DSP_REVERB_MODE: + { + *value = mReverb.getmode(); + if (*value >= freezemode) + { + *value = 1.0f; + sprintf(valuestr, "FREEZE"); + } + else + { + *value = 0.0f; + sprintf(valuestr, "NORMAL"); + } + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPReverb::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPReverb::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPReverb *reverb = (DSPReverb *)dsp; + + return reverb->DSPReverb::getMemoryUsed(tracker); +} +#endif + +} + +#endif diff --git a/src/fmod_dsp_reverb.h b/src/fmod_dsp_reverb.h new file mode 100755 index 0000000..808a92d --- /dev/null +++ b/src/fmod_dsp_reverb.h @@ -0,0 +1,49 @@ +#ifndef _FMOD_DSP_REVERB_H +#define _FMOD_DSP_REVERB_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_FREEVERB + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +#include "../lib/freeverb/revmodel.h" + +namespace FMOD +{ + class DSPReverb : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + revmodel mReverb; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_sfxreverb.cpp b/src/fmod_dsp_sfxreverb.cpp new file mode 100755 index 0000000..bbc06c5 --- /dev/null +++ b/src/fmod_dsp_sfxreverb.cpp @@ -0,0 +1,1650 @@ +/**************************************************************************** + +*****************************************************************************/ + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SFXREVERB + +#include "fmod.h" +#include "fmod_3d.h" +#include "fmod_dspi.h" +#include "fmod_dsp_sfxreverb.h" +#include "aSfxDsp.hpp" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> +#include <assert.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_sfxreverb_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspsfxreverb; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPSfxReverb::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +#define FMODLISTENER_MINLFREFERENCE 20.0f +#define FMODLISTENER_MAXLFREFERENCE 1000.0f +#define FMODLISTENER_DEFAULTLFREFERENCE 250.0f + +#define FMODLISTENER_MINROOMLF -10000 +#define FMODLISTENER_MAXROOMLF 0 +#define FMODLISTENER_DEFAULTROOMLF 0 + +#ifndef PLATFORM_PS3_SPU +// +// All parameter ranges and defaults are defined in '3dl2.h' +// +FMOD_DSP_PARAMETERDESC dspsfxreverb_param[] = +{ + {-10000.0f, 0.0f, 0.0f, + "Dry Level", "mB", "Dry Level" }, + + {I3DL2LISTENER_MINROOM, I3DL2LISTENER_MAXROOM, I3DL2LISTENER_DEFAULTROOM, + "Room", "mB", "Room" }, + + {I3DL2LISTENER_MINROOMHF, I3DL2LISTENER_MAXROOMHF, I3DL2LISTENER_DEFAULTROOMHF, + "Room HF", "mB", "Room HF" }, + + {I3DL2LISTENER_MINROOMROLLOFFFACTOR, I3DL2LISTENER_MAXROOMROLLOFFFACTOR, I3DL2LISTENER_DEFAULTROOMROLLOFFFACTOR, + "Room Rolloff", "", "Room Rolloff Factor" }, + + {I3DL2LISTENER_MINDECAYTIME, I3DL2LISTENER_MAXDECAYTIME, I3DL2LISTENER_DEFAULTDECAYTIME, + "Decay Time", "s", "Decay Time" }, + + {I3DL2LISTENER_MINDECAYHFRATIO,I3DL2LISTENER_MAXDECAYHFRATIO,I3DL2LISTENER_DEFAULTDECAYHFRATIO, + "Decay HF Ratio", "", "Decay HF Ratio" }, + + { I3DL2LISTENER_MINREFLECTIONS,I3DL2LISTENER_MAXREFLECTIONS ,I3DL2LISTENER_DEFAULTREFLECTIONS, + "Reflections", "mB", "Reflections" }, + + { I3DL2LISTENER_MINREFLECTIONSDELAY, I3DL2LISTENER_MAXREFLECTIONSDELAY, I3DL2LISTENER_DEFAULTREFLECTIONSDELAY, + "Reflect Delay", "", "Reflections Delay" }, + + { I3DL2LISTENER_MINREVERB, I3DL2LISTENER_MAXREVERB, I3DL2LISTENER_DEFAULTREVERB, + "Reverb", "mB", "Reverb" }, + + { I3DL2LISTENER_MINREVERBDELAY, I3DL2LISTENER_MAXREVERBDELAY, I3DL2LISTENER_DEFAULTREVERBDELAY, + "Reverb Delay", "s", "Reverb Delay" }, + + { I3DL2LISTENER_MINDIFFUSION, I3DL2LISTENER_MAXDIFFUSION, I3DL2LISTENER_DEFAULTDIFFUSION, + "Diffusion", "%", "Diffusion" }, + + { I3DL2LISTENER_MINDENSITY, I3DL2LISTENER_MAXDENSITY, I3DL2LISTENER_DEFAULTDENSITY, + "Density", "%", "Density" }, + + { I3DL2LISTENER_MINHFREFERENCE, I3DL2LISTENER_MAXHFREFERENCE , I3DL2LISTENER_DEFAULTHFREFERENCE, + "HF Reference", "Hz", "HF Reference" }, + + { FMODLISTENER_MINROOMLF, FMODLISTENER_MAXROOMLF , FMODLISTENER_DEFAULTROOMLF, + "Room LF", "mB", "Room LF" }, + + { FMODLISTENER_MINLFREFERENCE, FMODLISTENER_MAXLFREFERENCE , FMODLISTENER_DEFAULTLFREFERENCE, + "LF Reference", "Hz", "LF Reference" } + +}; + +#endif // PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPSfxReverb::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dspsfxreverb, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspsfxreverb.name, "SFX Reverb"); + dspsfxreverb.version = 0x00010100; + dspsfxreverb.create = DSPSfxReverb::createCallback; + dspsfxreverb.release = DSPSfxReverb::releaseCallback; + dspsfxreverb.reset = DSPSfxReverb::resetCallback; + + #ifdef PLATFORM_PS3 + dspsfxreverb.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_sfxreverb_pic_start; /* SPU PIC entry address */ + #else + dspsfxreverb.read = DSPSfxReverb::readCallback; + #endif + + dspsfxreverb.numparameters = sizeof(dspsfxreverb_param) / sizeof(dspsfxreverb_param[0]); + dspsfxreverb.paramdesc = dspsfxreverb_param; + dspsfxreverb.setparameter = DSPSfxReverb::setParameterCallback; + dspsfxreverb.getparameter = DSPSfxReverb::getParameterCallback; + dspsfxreverb.update = DSPSfxReverb::updateCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspsfxreverb.getmemoryused = &DSPSfxReverb::getMemoryUsedCallback; +#endif + + dspsfxreverb.mType = FMOD_DSP_TYPE_SFXREVERB; + dspsfxreverb.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspsfxreverb.mSize = sizeof(DSPSfxReverb); +#else + dspsfxreverb.read = DSPSfxReverb::readCallback; /* We only care about read function on SPU */ +#endif + + return &dspsfxreverb; +} + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +// Room, roomHF, flRoomRolloffFactor, flDecayTime, flDecayHFRatio, lReflections, refDelay, rev, revDelay, diff, dens, flHFReference. + + +#define FIRELIGHT_OFF \ + -10000, -10000, 0.0f, 1.0f, 1.0f, -2602, 0.007f, 200, 0.011f, 0.0f, 0.0f, 5000.0f + +#define FIRELIGHT_SATURATION \ + -600, 0, 0.0f, 20.0f, 0.1f, 1000, 0.3f, 0000, 0.1f, 100.0f, 100.0f, 5000.0f + +#define FIRELIGHT_SATURATION2 \ + -600, 0, 0.0f, 0.1f, 0.1f, 1000, 0.3f, 0000, 0.1f, 100.0f, 100.0f, 5000.0f + +#define I3DL2_ENVIRONMENT_PRESET_10SEC \ + -600, 0, 0.0f, 10.0f, 1.0f, 0, 0.007f, 0000, 0.011f, 100.0f, 100.0f, 5000.0f + +#define I3DL2_ENVIRONMENT_PRESET_20SEC \ + 0, 0, 0.0f, 20.0f, 1.0f, 0, 0.007f, 0000, 0.011f, 100.0f, 100.0f, 5000.0f + +#define I3DL2_ENVIRONMENT_PRESET_20SEC_FILTERED \ + -600, 0, 0.0f, 20.0f, 0.85f, 0, 0.007f, 0000, 0.011f, 100.0f, 100.0f, 5000.0f + +#define I3DL2_ENVIRONMENT_PRESET_60SEC \ + -600, 0, 0.0f, 60.0f, 1.0f, 0, 0.007f, 0000, 0.011f, 100.0f, 100.0f, 5000.0f + +#define I3DL2_ENVIRONMENT_PRESET_1000SEC \ + 0, 0, 0.0f, 1000.0f, 1.0f, 0, 0.007f, 2000, 0.011f, 100.0f, 100.0f, 5000.0f + +#define I3DL2_ENVIRONMENT_PRESET_1000SEC_FILTERED \ + 0, 0, 0.0f, 1000.0f, 0.5f, 0, 0.007f, 1000, 0.011f, 100.0f, 100.0f, 1000.0f + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::createInternal() +{ + FMOD_RESULT result; + I3DL2_LISTENERPROPERTIES props = + { + FIRELIGHT_OFF + }; + int count; + + init(); + mOldSpeakerMask = 0xFFFF; + + result = mSystem->getSoftwareFormat(&mOutputRate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + /* + Initial properties (will get overridden) + */ + mDryLevelLin = 0.0f; + mDryLevelmB = -100000.0f; + +#ifdef PLATFORM_PS3 + mProps = (I3DL2_LISTENERPROPERTIES *)FMOD_Memory_Calloc(sizeof(I3DL2_LISTENERPROPERTIES)); + if (!mProps) + { + return FMOD_ERR_MEMORY; + } + mUpdateProps = (I3DL2_LISTENERPROPERTIES *)FMOD_Memory_Calloc(sizeof(I3DL2_LISTENERPROPERTIES)); + if (!mUpdateProps) + { + return FMOD_ERR_MEMORY; + } + + mLFProps = (SFX_REVERB_LFPROPS *)FMOD_Memory_Calloc(sizeof(SFX_REVERB_LFPROPS)); + if (!mLFProps) + { + return FMOD_ERR_MEMORY; + } + + mUpdateLFProps = (SFX_REVERB_LFPROPS *)FMOD_Memory_Calloc(sizeof(SFX_REVERB_LFPROPS)); + if (!mUpdateLFProps) + { + return FMOD_ERR_MEMORY; + } +#else + mProps = &mPropsMemory; + mUpdateProps = &mUpdatePropsMemory; + mLFProps = &mLFPropsMemory; + mUpdateLFProps = &mUpdateLFPropsMemory; +#endif + + FMOD_memcpy(mProps, &props, sizeof(I3DL2_LISTENERPROPERTIES)); + FMOD_memcpy(mUpdateProps, &props, sizeof(I3DL2_LISTENERPROPERTIES)); + + mLFProps->mRoomLF = mUpdateLFProps->mRoomLF = FMODLISTENER_DEFAULTROOMLF; + mLFProps->mLFReference = mUpdateLFProps->mLFReference = FMODLISTENER_DEFAULTLFREFERENCE; + + /* + Create DSP processing object and initialise + */ + if (mpSfxDsp.init((float)mOutputRate) ) + { + return FMOD_ERR_MEMORY; + } + + mpSfxDsp.mSystem = mSystem; + + if (mpSfxDsp.UpdateBufferSize(mSystem->mDSPBlockSize)) + { + return FMOD_ERR_MEMORY; + } + + mpSfxDsp.mNumLateReverbDelays = kNumLateReverbDelays; + mpSfxDsp.mNumMatrixStages = int(FMOD_LOG((float)mpSfxDsp.mNumLateReverbDelays) / FMOD_LOG(2.0f) + 0.5f);; + mpSfxDsp.ClearBuffers(); + + /* + Initialise reverb parameters + */ + for (count = 0; count < mDescription.numparameters; count++) + { + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + FMOD_memcpy(mProps, mUpdateProps, sizeof(I3DL2_LISTENERPROPERTIES)); + + mLFProps->mRoomLF = mUpdateLFProps->mRoomLF; + mLFProps->mLFReference = mUpdateLFProps->mLFReference; + + SetRoom(mProps); + SetRoomHF(mProps); + SetRoomRolloffFactor(mProps); + SetDecayTime(mProps); + SetDecayHFRatio(mProps); + SetReflectionsLevel(mProps); + SetReflectionsDelay(mProps); + SetReverbLevel(mProps); + SetReverbDelay(mProps); + SetDiffusion(mProps); + SetDensity(mProps); + SetHFReference(mProps); + SetRoomLF(mLFProps); + SetLFReference(mLFProps); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::releaseInternal() +{ + mpSfxDsp.close(); + +#ifdef PLATFORM_PS3 + if (mProps) + { + FMOD_Memory_Free(mProps); + mProps = 0; + } + if (mUpdateProps) + { + FMOD_Memory_Free(mUpdateProps); + mUpdateProps = 0; + } + if (mLFProps) + { + FMOD_Memory_Free(mLFProps); + mLFProps = 0; + } + if (mUpdateLFProps) + { + FMOD_Memory_Free(mUpdateLFProps); + mUpdateLFProps = 0; + } +#endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::resetInternal() +{ + if (mpSfxDsp.UpdateBufferSize(mSystem->mDSPBlockSize)) + { + return FMOD_ERR_MEMORY; + } + + mpSfxDsp.ClearBuffers(); + + return FMOD_OK; +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + #ifndef PLATFORM_PS3 + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << inchannels) - 1))) /* all speaker channels have been disabled, skip the DSP */ + { + FMOD_memcpy(outbuffer, inbuffer, inchannels*length*sizeof(float)); + + if (mOldSpeakerMask & ((1 << inchannels) - 1)) + { + mOldSpeakerMask = speakermask; + return resetInternal(); + } + else + { + return FMOD_OK; + } + } + + mOldSpeakerMask = speakermask; + + #endif + +#if 0 + FMOD_memset(outbuffer, 0, length * inchannels * sizeof(float)); +#else + mpSfxDsp.DoDSPProcessing(inbuffer, outbuffer, (int)inchannels, (unsigned int)length, (float)mOutputRate, mDryLevelLin, speakermask); +#endif + + return FMOD_OK; +} + + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::updateInternal() +{ + if (mProps->lRoom != mUpdateProps->lRoom) + { + mProps->lRoom = mUpdateProps->lRoom; + SetRoom(mProps); + } + + if (mProps->lRoomHF != mUpdateProps->lRoomHF) + { + mProps->lRoomHF = mUpdateProps->lRoomHF; + SetRoomHF(mProps); + } + + if (mProps->flRoomRolloffFactor != mUpdateProps->flRoomRolloffFactor) + { + mProps->flRoomRolloffFactor = mUpdateProps->flRoomRolloffFactor; + SetRoomRolloffFactor(mProps); + } + + if (mProps->flDecayTime != mUpdateProps->flDecayTime) + { + mProps->flDecayTime = mUpdateProps->flDecayTime; + SetDecayTime(mProps); + } + + if (mProps->flDecayHFRatio != mUpdateProps->flDecayHFRatio) + { + mProps->flDecayHFRatio = mUpdateProps->flDecayHFRatio; + SetDecayHFRatio(mProps); + } + + if (mProps->lReflections != mUpdateProps->lReflections) + { + mProps->lReflections = mUpdateProps->lReflections; + SetReflectionsLevel(mProps); + } + + if (mProps->flReflectionsDelay != mUpdateProps->flReflectionsDelay) + { + mProps->flReflectionsDelay = mUpdateProps->flReflectionsDelay; + SetReflectionsDelay(mProps); + } + + if (mProps->lReverb != mUpdateProps->lReverb) + { + mProps->lReverb = mUpdateProps->lReverb; + SetReverbLevel(mProps); + } + + if (mProps->flReverbDelay != mUpdateProps->flReverbDelay) + { + mProps->flReverbDelay = mUpdateProps->flReverbDelay; + SetReverbDelay(mProps); + } + + if (mProps->flDiffusion != mUpdateProps->flDiffusion) + { + mProps->flDiffusion = mUpdateProps->flDiffusion; + SetDiffusion(mProps); + } + + if (mProps->flDensity != mUpdateProps->flDensity) + { + mProps->flDensity = mUpdateProps->flDensity; + SetDensity(mProps); + } + + if (mProps->flHFReference != mUpdateProps->flHFReference) + { + mProps->flHFReference = mUpdateProps->flHFReference; + SetHFReference(mProps); + } + + if (mLFProps->mRoomLF != mUpdateLFProps->mRoomLF) + { + mLFProps->mRoomLF = mUpdateLFProps->mRoomLF; + SetRoomLF(mLFProps); + } + + if (mLFProps->mLFReference != mUpdateLFProps->mLFReference) + { + mLFProps->mLFReference = mUpdateLFProps->mLFReference; + SetLFReference(mLFProps); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::setParameterInternal(int index, float value) +{ + FMOD_RESULT result = FMOD_OK; + + switch (index) + { + case FMOD_DSP_SFXREVERB_DRYLEVEL: + { + mDryLevelmB = value; + mDryLevelLin = (float)FMOD_POW(10.0f, mDryLevelmB / 2000.0f); + break; + } + case FMOD_DSP_SFXREVERB_ROOM: + { + mUpdateProps->lRoom = (int)(value < 0 ? value - 0.5f : value + 0.5f); + break; + } + case FMOD_DSP_SFXREVERB_ROOMHF: + { + mUpdateProps->lRoomHF = (int)(value < 0 ? value - 0.5f : value + 0.5f); + break; + } + case FMOD_DSP_SFXREVERB_ROOMROLLOFFFACTOR: + { + mUpdateProps->flRoomRolloffFactor = value; + break; + } + case FMOD_DSP_SFXREVERB_DECAYTIME: + { + mUpdateProps->flDecayTime = value; + break; + } + case FMOD_DSP_SFXREVERB_DECAYHFRATIO: + { + mUpdateProps->flDecayHFRatio = value; + break; + } + case FMOD_DSP_SFXREVERB_REFLECTIONSLEVEL: + { + mUpdateProps->lReflections = (int)(value < 0 ? value - 0.5f : value + 0.5f); + break; + } + case FMOD_DSP_SFXREVERB_REFLECTIONSDELAY: + { + mUpdateProps->flReflectionsDelay = value; + break; + } + case FMOD_DSP_SFXREVERB_REVERBLEVEL: + { + mUpdateProps->lReverb = (int)(value < 0 ? value - 0.5f : value + 0.5f); + break; + } + case FMOD_DSP_SFXREVERB_REVERBDELAY: + { + mUpdateProps->flReverbDelay = value; + break; + } + case FMOD_DSP_SFXREVERB_DIFFUSION: + { + mUpdateProps->flDiffusion = value; + break; + } + case FMOD_DSP_SFXREVERB_DENSITY: + { + mUpdateProps->flDensity = value; + break; + } + case FMOD_DSP_SFXREVERB_HFREFERENCE: + { + mUpdateProps->flHFReference = value; + break; + } + case FMOD_DSP_SFXREVERB_ROOMLF: + { + mUpdateLFProps->mRoomLF = (int)(value < 0 ? value - 0.5f : value + 0.5f); + break; + } + case FMOD_DSP_SFXREVERB_LFREFERENCE: + { + mUpdateLFProps->mLFReference = value; + break; + } + default: + { + result = FMOD_ERR_INVALID_PARAM; + break; + } + } + + if (result == FMOD_OK) + { + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + { + DSPConnectionRequest *request; + + if (mSystem->mConnectionRequestFreeHead.isEmpty()) + { + mSystem->flushDSPConnectionRequests(); + } + + request = (DSPConnectionRequest *)mSystem->mConnectionRequestFreeHead.getNext(); + + request->removeNode(); + request->addBefore(&mSystem->mConnectionRequestUsedHead); + + request->mThis = this; + request->mRequest = DSPCONNECTION_REQUEST_REVERBUPDATEPARAMETERS; + } + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSfxReverb::getParameterInternal(int index, float *value, char *valuestr) +{ + + switch (index) + { + case FMOD_DSP_SFXREVERB_DRYLEVEL: + { + if (value) + *value = mDryLevelmB; + if (valuestr) + sprintf(valuestr, "%f", mDryLevelmB); + break; + } + case FMOD_DSP_SFXREVERB_ROOM: + { + if (value) + *value = (float)mProps->lRoom; + if (valuestr) + sprintf(valuestr, "%d", (int)mProps->lRoom); + break; + } + case FMOD_DSP_SFXREVERB_ROOMHF: + { + if (value) + *value = (float)mProps->lRoomHF; + if (valuestr) + sprintf(valuestr, "%d", (int)mProps->lRoomHF); + break; + } + case FMOD_DSP_SFXREVERB_ROOMROLLOFFFACTOR: + { + if (value) + *value = (float)mProps->flRoomRolloffFactor; + if (valuestr) + sprintf(valuestr, "%f", mProps->flRoomRolloffFactor); + break; + } + case FMOD_DSP_SFXREVERB_DECAYTIME: + { + if (value) + *value = (float)mProps->flDecayTime; + if (valuestr) + sprintf(valuestr, "%f", mProps->flDecayTime); + break; + } + case FMOD_DSP_SFXREVERB_DECAYHFRATIO: + { + if (value) + *value = (float)mProps->flDecayHFRatio; + if (valuestr) + sprintf(valuestr, "%f", mProps->flDecayHFRatio); + break; + } + case FMOD_DSP_SFXREVERB_REFLECTIONSLEVEL: + { + if (value) + *value = (float)mProps->lReflections; + if (valuestr) + sprintf(valuestr, "%d", (int)mProps->lReflections); + break; + } + case FMOD_DSP_SFXREVERB_REFLECTIONSDELAY: + { + if (value) + *value = (float)mProps->flReflectionsDelay; + if (valuestr) + sprintf(valuestr, "%f", mProps->flReflectionsDelay); + break; + } + case FMOD_DSP_SFXREVERB_REVERBLEVEL: + { + if (value) + *value = (float)mProps->lReverb; + if (valuestr) + sprintf(valuestr, "%d", (int)mProps->lReverb); + break; + } + case FMOD_DSP_SFXREVERB_REVERBDELAY: + { + if (value) + *value = (float)mProps->flReverbDelay; + if (valuestr) + sprintf(valuestr, "%f", mProps->flReverbDelay); + break; + } + case FMOD_DSP_SFXREVERB_DIFFUSION: + { + if (value) + *value = (float)mProps->flDiffusion; + if (valuestr) + sprintf(valuestr, "%f", mProps->flDiffusion); + break; + } + case FMOD_DSP_SFXREVERB_DENSITY: + { + if (value) + *value = (float)mProps->flDensity; + if (valuestr) + sprintf(valuestr, "%f", mProps->flDensity); + break; + } + case FMOD_DSP_SFXREVERB_HFREFERENCE: + { + if (value) + *value = (float)mProps->flHFReference; + if (valuestr) + sprintf(valuestr, "%f", mProps->flHFReference); + break; + } + case FMOD_DSP_SFXREVERB_ROOMLF: + { + if (value) + *value = (float)mLFProps->mRoomLF; + if (valuestr) + sprintf(valuestr, "%d", (int)mLFProps->mRoomLF); + break; + } + case FMOD_DSP_SFXREVERB_LFREFERENCE: + { + if (value) + *value = mLFProps->mLFReference; + if (valuestr) + sprintf(valuestr, "%f", mLFProps->mLFReference); + break; + } + default: + { + return FMOD_ERR_INVALID_PARAM; + break; + } + } + return FMOD_OK; +} + +// Adjust intensity level for the room effect (i.e. both the early reflections and the late reverberation). +bool DSPSfxReverb::SetRoom(I3DL2_LISTENERPROPERTIES *pProps) // [-10000, 0] default: -10000 mB +{ + if (pProps->lRoom < I3DL2LISTENER_MINROOM) + pProps->lRoom = I3DL2LISTENER_MINROOM; + else if (pProps->lRoom > I3DL2LISTENER_MAXROOM) + pProps->lRoom = I3DL2LISTENER_MAXROOM; + + mProps->lRoom = pProps->lRoom; + bool err = SetReflectionsLevel(pProps); + err |= SetReverbLevel(pProps); + + return err; +} + +// Adjust low-pass filter for the room effect (i.e. both the early reflections and the late reverberation). +// Room_HF is defined as the attenuation at high frequencies relative to the intensity at low frequencies. +bool DSPSfxReverb::SetRoomHF(I3DL2_LISTENERPROPERTIES *pProps) // [-10000, 0] default: 0 mB +{ + if (pProps->lRoomHF < I3DL2LISTENER_MINROOMHF) + pProps->lRoomHF = I3DL2LISTENER_MINROOMHF; + else if (pProps->lRoomHF > I3DL2LISTENER_MAXROOMHF) + pProps->lRoomHF = I3DL2LISTENER_MAXROOMHF; + + mProps->lRoomHF = pProps->lRoomHF; + + float gainDB = (float)pProps->lRoomHF * 0.01f / 2.0f; // Convert to dB. + // 2 filters in series, so each has half the attenuation + float gain = (float)FMOD_POW(10.0f, gainDB / 20.0f); // Convert to linear + + float K; + + bool err = Calculate1stOrderLowpassCoeff(gain, pProps->flHFReference, (float)mOutputRate, &K); + mpSfxDsp.mRoomHF = 1.0f - K; + + return err; +} + +bool DSPSfxReverb::Calculate1stOrderLowpassCoeff(float gain, float cutoff, float sampleRate, float *pK) +{ + + // *** For now, limit the gain to 1 *** + if (gain > 1.0f) + { + gain = 1.0f; + } + + // But b24ac becomes NAN... + if (gain == 1.0f) + { + *pK = 0.0f; + } + else + { + assert(sampleRate); + float Gain = gain * gain; // gain squared + + float a = Gain - 1.0f; + float b = 2.0f * (1.0f - Gain * (float)FMOD_COS(2.0f * FMOD_PI * cutoff / sampleRate)); + float c = a; + + float b24ac = b*b - 4.0f*a*c; + + // assert(b24ac >= 0); + if (b24ac < 0) + b24ac = 0; + + *pK = (-b + (float)FMOD_SQRT(b24ac)) / (2.0f * a); + if (*pK < 0.0f) + { + *pK = 0.0f; + } + else if (*pK > 0.99f) + { + *pK = 0.99f; + } + } + + return 0; +} + +// Adjust low-pass filter for the room effect (i.e. both the early reflections and the late reverberation). +// Room_HF is defined as the attenuation at high frequencies relative to the intensity at low frequencies. +bool DSPSfxReverb::SetRoomLF(SFX_REVERB_LFPROPS *pLFProps) // [-10000, 0] default: 0 mB +{ + if (pLFProps->mRoomLF < FMODLISTENER_MINROOMLF) + { + pLFProps->mRoomLF = FMODLISTENER_MINROOMLF; + } + else if (pLFProps->mRoomLF > FMODLISTENER_MAXROOMLF) + { + pLFProps->mRoomLF = FMODLISTENER_MAXROOMLF; + } + + mpSfxDsp.mRoomLF = pLFProps->mRoomLF / 100.0f; + + bool err = CalculateShelfCoeffs(mpSfxDsp.mRoomLF , pLFProps->mLFReference, (float)mOutputRate, + &mpSfxDsp.mRoomLFcoeffs.a0, &mpSfxDsp.mRoomLFcoeffs.a1, &mpSfxDsp.mRoomLFcoeffs.a2, + &mpSfxDsp.mRoomLFcoeffs.b1, &mpSfxDsp.mRoomLFcoeffs.b2); + return err; +} + + +bool DSPSfxReverb::CalculateShelfCoeffs(float gain, float cutoff, float sampleRate, float *a0, float *a1, float *a2, float *b1, float *b2) +{ + float t1, t2, g1, g2; + + float sqrtgainlin = FMOD_EXP(gain * 0.057564627f); + t1 = t2 = FMOD_TAN(3.14159265f * cutoff / sampleRate); + g1 = g2 = 1.414213562f; + t2 /= sqrtgainlin; + + float denom = 1.0f / (1.0f + t2 * (g2 + t2)); + *a0 = (1.0f + t1 * (t1 + g1)) * denom; + *a1 = (t1 * t1 - 1.0f) * denom * 2.0f; + *a2 = (1.0f + t1 * (t1 - g1)) * denom; + *b1 = -2.0f * (t2 * t2 - 1.0f) * denom; + *b2 = -(1.0f + t2 * (t2 - g2)) * denom; + + return 0; +} + +// Controls the rolloff of room effect intensity vs. distance, in the same way that DS3D's Rolloff Factor +// operates on the direct path intensity. Default is 0.0, implying that the reverberation intensity does not +// depend on source-listener distance. A value of 1.0 means that the reverberation decays by 6 dB per doubling +// of distance. +bool DSPSfxReverb::SetRoomRolloffFactor(I3DL2_LISTENERPROPERTIES *pProps) // [0.0, 10.0] default: 0.0 +{ + mProps->flRoomRolloffFactor = pProps->flRoomRolloffFactor; + return 0; +} + +#define NEW_MAX_DECAY_TIME 10000 // Change this to match the I3DL2 spec. + +// Reverberation decay time +bool DSPSfxReverb::SetDecayTime(I3DL2_LISTENERPROPERTIES *pProps) // [0.1, 20.0] default: 1.0 s +{ + float K; + bool err = false; + // err = SetDelayLineLengths(pProps); // *** Put this somewhere else, unless it will change for different decay times. + int delayLine; + + if (pProps->flDecayTime< I3DL2LISTENER_MINDECAYTIME) + { + pProps->flDecayTime = I3DL2LISTENER_MINDECAYTIME; + } + else if (pProps->flDecayTime > NEW_MAX_DECAY_TIME) + { + pProps->flDecayTime = NEW_MAX_DECAY_TIME; + } + + mProps->flDecayTime = pProps->flDecayTime; + + for (delayLine=0; delayLine<mpSfxDsp.mNumLateReverbDelays; delayLine++) + { + if (pProps->flDecayTime) + { + // At low frequencies, each delay line should decay by 60 dB in flDecayTime seconds. + // Attenuation each time through the delay line + float attenuationDB = (-60.f / pProps->flDecayTime) * mpSfxDsp.mLateDelayLenSec[delayLine]; + + // Convert to linear + float feedback = (float)FMOD_POW(10.0f, attenuationDB / 20.0f); + mpSfxDsp.mFeedback[delayLine] = feedback; + + // Also handle HF decay + // At the HF reference frequency, each delay line should decay by 60 dB in flDecayHFRatio * flDecayTime seconds. + float attenuationDB_HF = (-60.0f / (pProps->flDecayHFRatio * pProps->flDecayTime)) * mpSfxDsp.mLateDelayLenSec[delayLine]; + + // Subtract the DC attenuation to get the filter attenuation + attenuationDB_HF -= attenuationDB; + + // Convert to linear + float attenuation_HF = (float)FMOD_POW(10.0f, attenuationDB_HF / 20.0f); + + err |= Calculate1stOrderLowpassCoeff(attenuation_HF, pProps->flHFReference, (float)mOutputRate, &K); + mpSfxDsp.mDecayHF[delayLine] = 1.0f - K; + } + } + // NEW + SetReverbLevel(mProps); // Adjust the reverb gain to correspond to the new decay time, + // to keep the energy constant. + // \NEW + /* + +Calculate1stOrderLowpassCoeff: b=2.000000, b24ac=-0.000000 a=-1.000000 +Calculate1stOrderLowpassCoeff(0.000037, 5000.000000, 48000.000000) +-> attenuationDB = -9.843841 +-> feedback = 0.321965 +-> attenuationDB_HF = -88.594559 +-> K = -1.#IND00 +*/ + return err; +} + +/* +#define LOWEST_DELAY_LINE_LENGTH_SEC (0.061f) +#define DELAY_LINE_RATIO (1.32f) +#define LOWEST_SHORT_DELAY_LINE_LENGTH_SEC (0.0017f) +#define SHORT_DELAY_LINE_RATIO (1.61f) +*/ + +bool DSPSfxReverb::SetDelayLineLengths(I3DL2_LISTENERPROPERTIES *pProps) +{ + /* + mpSfxDsp.SetLateDelays(LOWEST_DELAY_LINE_LENGTH_SEC, DELAY_LINE_RATIO, + LOWEST_DELAY_LINE_LENGTH_B_SEC, DELAY_LINE_RATIO_B, + (float)mOutputRate); + */ + + float density = 0.01f * pProps->flDensity; // 0 ~ 1. + + density *= (density * density); + + // Modal density ~= 4pi*V*f^2 / c^3 (modes per Hz) + // c = 343 m/sec + // If f = 1000, + // If V = 44m x 25m x 17m = 18,700 m^3, MD = 5823. + // If V = 2m x 2m x 2m = 8 m^3, MD = 2.49 + // Ratio ~= 2338 + // If f = 100, + // If V = 18,700 m^3, MD = 58.23 + // If V = 8 m^3, MD = 0.025 + // Ratio ~= 2337. + + // Map flDensity 100 to MD 1.0 + // Map flDensity 0 to MD 0.2 +#define MIN_DENS (0.1f) +#define DENS_SCAL (1.0f - MIN_DENS) + density *= DENS_SCAL; + density += MIN_DENS; + + if (density < MIN_DENS) + { + density = MIN_DENS; + } + else if (density > 1.0f) + { + density = 1.0f; + } + + float lowestDelayLenSec = LOWEST_DELAY_LINE_LENGTH_SEC * density; + + mpSfxDsp.SetLateDelays(lowestDelayLenSec, DELAY_LINE_RATIO, + LOWEST_DELAY_LINE_LENGTH_B_SEC, DELAY_LINE_RATIO_B, + (float)mOutputRate); + + bool err = SetDecayTime(pProps); // Make sure it takes into account the new delay lengths + + return err; +} + +// Ratio of high-frequency decay time relative to low-frequency decay time. Decay_HF_Ratio would typically +// control one or several filters located in the feedback loop of the reverberator, whereas Room_HF should +// control a filter located at the input or output of the reverberator (outside of the feedback loop). +bool DSPSfxReverb::SetDecayHFRatio(I3DL2_LISTENERPROPERTIES *pProps) // [0.1, 2.0] default: 0.5 +{ + if (pProps->flDecayHFRatio < I3DL2LISTENER_MINDECAYHFRATIO) + pProps->flDecayHFRatio = I3DL2LISTENER_MINDECAYHFRATIO; + else if (pProps->flDecayHFRatio > I3DL2LISTENER_MAXDECAYHFRATIO) // Do we support >1? + pProps->flDecayHFRatio = I3DL2LISTENER_MAXDECAYHFRATIO; + + mProps->flDecayHFRatio = pProps->flDecayHFRatio; + + bool err = SetDecayTime(pProps); // This also handles HF decay + + return err; +} + +// Adjusts intensity level of early reflections (relative to Room value) +bool DSPSfxReverb::SetReflectionsLevel(I3DL2_LISTENERPROPERTIES *pProps) // [-10000, 1000] default: -10000 mB +{ + if (pProps->lReflections < I3DL2LISTENER_MINREFLECTIONS) + pProps->lReflections = I3DL2LISTENER_MINREFLECTIONS; + else if (pProps->lReflections > I3DL2LISTENER_MAXREFLECTIONS) + pProps->lReflections = I3DL2LISTENER_MAXREFLECTIONS; + + mProps->lReflections = pProps->lReflections; + + float reflectionsLevelDB = (float)(pProps->lReflections + pProps->lRoom) * 0.01f; // Add Room level, convert to dB + + // Convert to linear + float ampGain = FMOD_POW(10.0f, reflectionsLevelDB / 20.0f); + + // If we have 8 outputs, scale the gain by sqrt(1/8). + mpSfxDsp.mERgain = ampGain * FMOD_SQRT(1.0f/(float)(kNumEarlyDelayTaps + 1.0f)); + + return 0; +} + +// Delay time of the first reflection (relative to the direct path) +bool DSPSfxReverb::SetReflectionsDelay(I3DL2_LISTENERPROPERTIES *pProps) // [0.0, 0.3] default: 0.02 s +{ + if (pProps->flReflectionsDelay < I3DL2LISTENER_MINREFLECTIONSDELAY) + pProps->flReflectionsDelay = I3DL2LISTENER_MINREFLECTIONSDELAY; + else if (pProps->flReflectionsDelay > I3DL2LISTENER_MAXREFLECTIONSDELAY) + pProps->flReflectionsDelay = I3DL2LISTENER_MAXREFLECTIONSDELAY; + + mProps->flReflectionsDelay = pProps->flReflectionsDelay; + mpSfxDsp.mEarlyLateSec[0] = mProps->flReflectionsDelay; + + int refDelaySamples = (int)((float)mOutputRate * mProps->flReflectionsDelay); + + if (refDelaySamples == 0) + { + refDelaySamples++; // Avoid long wrap-around delay + } + + mpSfxDsp.mEarlyLateSamples[0] = refDelaySamples; + + SetReverbDelay(mProps); // This affects the absolute position of the reverb delay. + + return 0; +} + +// Adjusts intensity of late reverberation (relative to Room value) +// Note: Reverb and Decay_time are independent. If the application adjusts Decay_time without changing +// Reverb, the driver must keep the intensity of the late reverberation constant. +bool DSPSfxReverb::SetReverbLevel(I3DL2_LISTENERPROPERTIES *pProps) // [-10000, 2000] default: -10000 mB +{ + if (pProps->lReverb < I3DL2LISTENER_MINREVERB) + pProps->lReverb = I3DL2LISTENER_MINREVERB; + else if (pProps->lReverb > I3DL2LISTENER_MAXREVERB) + pProps->lReverb = I3DL2LISTENER_MAXREVERB; + + mProps->lReverb = pProps->lReverb; + + float reverbLevelDB = (float)(pProps->lReverb + pProps->lRoom) * 0.01f; // Add room level, convert to dB + float reverbGain = (float)FMOD_POW(10.0f, reverbLevelDB / 20.0f); // convert to linear + int delayLine; + float powerGain; + + // Adjust this to compensate for the gain of the late reverb loop. + // First, calculate the average feedback power gain, and the average scatter power gain: + float aveFeedbackPowerGain = 0; + + for (delayLine = 0; delayLine<mpSfxDsp.mNumLateReverbDelays; delayLine++) + { + float feedbackGain = mpSfxDsp.mFeedback[delayLine]; + aveFeedbackPowerGain += (feedbackGain * feedbackGain); + } + + aveFeedbackPowerGain /= mpSfxDsp.mNumLateReverbDelays; + + // Power gain = power gain of first pass + loop gain + // = 1 + Feedback / (1-Feedback) + if (aveFeedbackPowerGain != 1.0f) + { + powerGain = 1.0f + (aveFeedbackPowerGain / (1.0f - aveFeedbackPowerGain)); + } + else + { + powerGain = 1.0f; + } + + float ampGain; + + // Adjust reverbGain by the amplitude gain of the late feedback loop + if (powerGain > 0.0f) + { + ampGain = reverbGain / (float)FMOD_SQRT(powerGain); + } + else + { + ampGain = reverbGain; + } + + // If we have 8 outs, scale gain by sqrt(1/8) + mpSfxDsp.mLRgain = ampGain * FMOD_SQRT(1.0/(float)kNumLateReverbDelays); + + return 0; +} + +// Defines the time limit between the early reflections and the late reverberation (relative to the time +// of the first reflection). +bool DSPSfxReverb::SetReverbDelay(I3DL2_LISTENERPROPERTIES *pProps) // [0.0, 0.1] default: 0.04 s +{ + if (pProps->flReverbDelay < I3DL2LISTENER_MINREVERBDELAY) + pProps->flReverbDelay = I3DL2LISTENER_MINREVERBDELAY; + else if (pProps->flReverbDelay > I3DL2LISTENER_MAXREVERBDELAY) + pProps->flReverbDelay = I3DL2LISTENER_MAXREVERBDELAY; + + mProps->flReverbDelay = pProps->flReverbDelay; + + float refPlusRevDelaySec = mProps->flReflectionsDelay + mProps->flReverbDelay; + + mpSfxDsp.SetLate_EarlyLateDelayTaps(refPlusRevDelaySec, kEarlyLateNextLengthSec, kEarlyLateDelayRatio, (float)mOutputRate); + + return 0; +} + +// Controls the echo density in the late reverberation decay. 0% = minimum, 100% = maximum. +bool DSPSfxReverb::SetDiffusion(I3DL2_LISTENERPROPERTIES *pProps) // [0.0, 100.0] default: 100.0 % +{ + if (pProps->flDiffusion < I3DL2LISTENER_MINDIFFUSION) + pProps->flDiffusion = I3DL2LISTENER_MINDIFFUSION; + else if (pProps->flDiffusion > I3DL2LISTENER_MAXDIFFUSION) + pProps->flDiffusion = I3DL2LISTENER_MAXDIFFUSION; + + mProps->flDiffusion = pProps->flDiffusion; + + // Set the gains inside the matrix + float diffusion = pProps->flDiffusion * 0.01f; + diffusion = diffusion * FMOD_PI / 4.0f; + mpSfxDsp.mHadamard = (float)FMOD_TAN(diffusion); + + // Compensate for the matrix gain + // A ----------cos() ----------- C + // \ / sin() + // \/ + // / \ -sin() + // B --------- cos() ----------- D + + // -> + // A --- cos() ----------- C + // \ / tan + // \/ + // / \ -tan + // B --- cos() ----------- D + + // C = A cos(th) + B sin(th) = A cos(th) + B cos(th)tan(th) + // C = B cos(th) - A sin(th) = B cos(th) - A cos(th)tan(th) + + // If C = A * diffScale + (B * diffScale) * Hadamard, and + // D = B * diffScale - (A * diffScale) * Hadamard, + // If A = B = 1, C^2 + D^2 = 2. Then + // C^2 = diffScale^2 * (1 + 2*Hadamard + Hadamard^2), and + // D^2 = diffScale^2 * (1 - 2*Hadamard + Hadamard^2). Then + // 1 = diffScale^2 * (1 + Hadamard^2) + // For full diffusion, Hadamard = 1; diff = 0.707 = cos(PI/4) (sinusoidal rotation matrix) + // Apply this gain for each stage of the matrix. + float diffusionScale = (float)FMOD_POW(FMOD_COS(diffusion), mpSfxDsp.mNumMatrixStages); + mpSfxDsp.mDiffusionScale = diffusionScale; // * diffusionScale; // 2 matrices + + return 0; +} + +// Controls the modal density in the late reverberation decay. 0% = minimum, 100% = maximum. +bool DSPSfxReverb::SetDensity(I3DL2_LISTENERPROPERTIES *pProps) // [0.0, 100.0] default: 100.0 % +{ + if (pProps->flDensity < I3DL2LISTENER_MINDENSITY) + pProps->flDensity = I3DL2LISTENER_MINDENSITY; + else if (pProps->flDensity > I3DL2LISTENER_MAXDENSITY) + pProps->flDensity = I3DL2LISTENER_MAXDENSITY; + + mProps->flDensity = pProps->flDensity; + + bool err = SetDelayLineLengths(pProps); + + return err; +} + +// Reference high frequency. +bool DSPSfxReverb::SetHFReference(I3DL2_LISTENERPROPERTIES *pProps) // [20.0, 20000.0] default: 5000.0 Hz +{ + if (pProps->flHFReference < I3DL2LISTENER_MINHFREFERENCE) + pProps->flHFReference = I3DL2LISTENER_MINHFREFERENCE; + else if (pProps->flHFReference > I3DL2LISTENER_MAXHFREFERENCE) + pProps->flHFReference = I3DL2LISTENER_MAXHFREFERENCE; + + mProps->flHFReference = pProps->flHFReference; + SetRoomHF(mProps); + SetDecayTime(mProps); + return 0; +} + +// Reference low frequency. +bool DSPSfxReverb::SetLFReference(SFX_REVERB_LFPROPS *pLFProps) // [20.0, 20000.0] default: 5000.0 Hz +{ + if (pLFProps->mLFReference < FMODLISTENER_MINLFREFERENCE) + { + pLFProps->mLFReference = FMODLISTENER_MINLFREFERENCE; + } + else if (pLFProps->mLFReference > FMODLISTENER_MAXLFREFERENCE) + { + pLFProps->mLFReference = FMODLISTENER_MAXLFREFERENCE; + } + + SetRoomLF(pLFProps); + return 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPSfxReverb::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + +#ifdef PLATFORM_PS3 + if (mProps) + { + tracker->add(false, FMOD_MEMBITS_DSP, sizeof(I3DL2_LISTENERPROPERTIES)); + } + + if (mUpdateProps) + { + tracker->add(false, FMOD_MEMBITS_DSP, sizeof(I3DL2_LISTENERPROPERTIES)); + } + + if (mLFProps) + { + tracker->add(false, FMOD_MEMBITS_DSP, sizeof(SFX_REVERB_LFPROPS)); + } + + if (mUpdateLFProps) + { + tracker->add(false, FMOD_MEMBITS_DSP, sizeof(SFX_REVERB_LFPROPS)); + } +#endif + + int delayLine; + for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++) + { + if (mpSfxDsp.mLateDelays[delayLine]) + { + tracker->add(false, FMOD_MEMBITS_DSP, mpSfxDsp.mLateDelaySamplesAllocated[delayLine] * sizeof(float)); + } + } + + if (mpSfxDsp.mEarlyLateDelay) + { + tracker->add(false, FMOD_MEMBITS_DSP, mpSfxDsp.mEarlyLateSamplesAllocated * sizeof(float)); + } + + for (delayLine=0; delayLine<kNumAllpassDelays; delayLine++) + { + if (mpSfxDsp.mAllpassDelays[delayLine]) + { + tracker->add(false, FMOD_MEMBITS_DSP, mpSfxDsp.mAllpassSamplesAllocated[delayLine] * sizeof(float)); + } + } + + if (mpSfxDsp.mEarlyDelay) + { + tracker->add(false, FMOD_MEMBITS_DSP, mpSfxDsp.mEarlyDelaySamplesAllocated * sizeof(float)); + } + + if (mpSfxDsp.mInBuffMemory) + { + tracker->add(false, FMOD_MEMBITS_DSP, (mpSfxDsp.mNumAllocatedInBuffSamples * sizeof(float)) + 16); + } + + return FMOD_OK; +} + +#endif + +#endif + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->resetInternal(); +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->getParameterInternal(index, value, valuestr); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::updateCallback(FMOD_DSP_STATE *dsp) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->updateInternal(); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPSfxReverb::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPSfxReverb *sfxreverb = (DSPSfxReverb *)dsp; + + return sfxreverb->DSPSfxReverb::getMemoryUsed(tracker); +} +#endif + +#endif + +} + +#endif diff --git a/src/fmod_dsp_sfxreverb.h b/src/fmod_dsp_sfxreverb.h new file mode 100755 index 0000000..daa97b7 --- /dev/null +++ b/src/fmod_dsp_sfxreverb.h @@ -0,0 +1,92 @@ +#ifndef _FMOD_DSP_SFXREVERB_H +#define _FMOD_DSP_SFXREVERB_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SFXREVERB + +#include "fmod.h" +#include "fmod_dsp_filter.h" +#include "3dl2.h" +#include "aSfxDsp.hpp" + +namespace FMOD +{ + struct SFX_REVERB_LFPROPS + { + int mRoomLF; + float mLFReference; + }; + + class DSPSfxReverb : public DSPFilter + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + ASfxDsp mpSfxDsp; // SFX DSP module + + I3DL2_LISTENERPROPERTIES *mProps; // Reverb properties + I3DL2_LISTENERPROPERTIES *mUpdateProps; // Update properties + + SFX_REVERB_LFPROPS *mLFProps; + SFX_REVERB_LFPROPS *mUpdateLFProps; + +#ifndef PLATFORM_PS3 + I3DL2_LISTENERPROPERTIES mPropsMemory; + I3DL2_LISTENERPROPERTIES mUpdatePropsMemory; + + SFX_REVERB_LFPROPS mLFPropsMemory; + SFX_REVERB_LFPROPS mUpdateLFPropsMemory; +#endif + float mDryLevelmB; // Dry level (mB) + float mDryLevelLin; // Dry level (linear) + int mOutputRate; // Sample rate + unsigned short mOldSpeakerMask; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + FMOD_RESULT updateInternal(); + + private: + bool SetRoom(I3DL2_LISTENERPROPERTIES *pProps); // [-10000, 0] default: -10000 mB + bool SetRoomHF(I3DL2_LISTENERPROPERTIES *pProps); // [-10000, 0] default: 0 mB + bool SetRoomLF(SFX_REVERB_LFPROPS *pLFProps); // [-10000, 0] default: 0 mB + bool Calculate1stOrderLowpassCoeff(float gain, float cutoff, float sampleRate, float *pK); + bool CalculateShelfCoeffs(float gain, float cutoff, float sampleRate, float *a0, float *a1, float *a2, float *b1, float *b2); + bool SetRoomRolloffFactor(I3DL2_LISTENERPROPERTIES *pProps); // [0.0, 10.0] default: 0.0 + bool SetDecayTime(I3DL2_LISTENERPROPERTIES *pProps); // [0.1, 20.0] default: 1.0 s + bool SetDelayLineLengths(I3DL2_LISTENERPROPERTIES *pProps); + bool SetDecayHFRatio(I3DL2_LISTENERPROPERTIES *pProps); // [0.1, 2.0] default: 0.5 + bool SetReflectionsLevel(I3DL2_LISTENERPROPERTIES *pProps); // [-10000, 1000] default: -10000 mB + bool SetReflectionsDelay(I3DL2_LISTENERPROPERTIES *pProps); // [0.0, 0.3] default: 0.02 s + bool SetReverbLevel(I3DL2_LISTENERPROPERTIES *pProps); // [-10000, 2000] default: -10000 mB + bool SetReverbDelay(I3DL2_LISTENERPROPERTIES *pProps); // [0.0, 0.1] default: 0.04 s + bool SetDiffusion(I3DL2_LISTENERPROPERTIES *pProps); // [0.0, 100.0] default: 100.0 % + bool SetHFReference(I3DL2_LISTENERPROPERTIES *pProps); // [20.0, 20000.0] default: 5000.0 Hz + bool SetLFReference(SFX_REVERB_LFPROPS *pLFProps); // [20.0, 20000.0] default: 5000.0 Hz + bool SetDensity(I3DL2_LISTENERPROPERTIES *pProps); // [0.0, 100.0] default: 100.0 % + + public: + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); + static FMOD_RESULT F_CALLBACK updateCallback(FMOD_DSP_STATE *dsp); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_soundcard.cpp b/src/fmod_dsp_soundcard.cpp new file mode 100755 index 0000000..53d5bfd --- /dev/null +++ b/src/fmod_dsp_soundcard.cpp @@ -0,0 +1,173 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_downmix.h" +#include "fmod_dsp_filter.h" +#include "fmod_dsp_soundcard.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSoundCard::alloc(FMOD_DSP_DESCRIPTION_EX *description) +{ + FMOD_RESULT result; + + result = DSPI::alloc(description); + if (result != FMOD_OK) + { + return result; + } + + updateTreeLevel(0); /* Soundcard units always start at level 0 */ + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPSoundCard::read(void *outbuffer, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + FMOD_RESULT result = FMOD_OK; + float *readbuffer = 0; + int outchannels = 0; + unsigned int starttime = 0; + unsigned int endtime = 0; + + result = DSPFilter::read(&readbuffer, &outchannels, length, speakermode, speakermodechannels, tick); + if (result != FMOD_OK) + { + return result; + } + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + #if !defined(PLATFORM_PS3) + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&outchannels); + } + else + #endif + { + outchannels = speakermodechannels; + } + + if (mFlags & FMOD_DSP_FLAG_IDLE) + { + unsigned int lenbytes; + + SoundI::getBytesFromSamples(*length, &lenbytes, outchannels, mDescription.mFormat); + + memset(outbuffer, 0, lenbytes); + +#if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) && !defined(PLATFORM_PS3) + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + mNumPeakVolumeChans = outchannels; + memset(mPeakVolume, 0, sizeof(mPeakVolume)); + } +#endif + } + else + { + float *src; + bool convertformat = (mDescription.mFormat != FMOD_SOUND_FORMAT_PCMFLOAT); + + #if !defined(PLATFORM_PS3) + if (mSystem->mDownmix && this == mSystem->mDSPSoundCard) + { + src = convertformat ? mSystem->mDSPTempBuff : (float *)outbuffer; + mSystem->mDownmix->encode(readbuffer, src, *length); + } + else + #endif + { + src = readbuffer; + } + +#if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) && !defined(PLATFORM_PS3) + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + calculatePeaks(src, *length, outchannels); + } +#endif + + /* + mBuffer being true means the format is not the same so it needs to read as float first, then convert to the soundcard format. + */ + if (convertformat) + { + result = convert(outbuffer, src, mDescription.mFormat, FMOD_SOUND_FORMAT_PCMFLOAT, *length * outchannels, 1, 1, 1.0f); + if (result != FMOD_OK) + { + return result; + } + } + else if (src != outbuffer) + { + unsigned int lenbytes; + + SoundI::getBytesFromSamples(*length, &lenbytes, outchannels, mDescription.mFormat); + + memcpy(outbuffer, src, lenbytes); + } + } + + mDSPTick = tick; + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsageTemp += endtime - starttime; + + mCPUUsage = mCPUUsageTemp; + mCPUUsageTemp = 0; + } + + return FMOD_OK; +} + +} + +#endif diff --git a/src/fmod_dsp_soundcard.h b/src/fmod_dsp_soundcard.h new file mode 100755 index 0000000..88734d1 --- /dev/null +++ b/src/fmod_dsp_soundcard.h @@ -0,0 +1,21 @@ +#ifndef _FMOD_DSP_SOUNDCARD_H +#define _FMOD_DSP_SOUNDCARD_H + +#include "fmod_settings.h" + +#include "fmod_dsp_filter.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class DSPSoundCard : public DSPFilter + { + FMOD_RESULT alloc(FMOD_DSP_DESCRIPTION_EX *description); + FMOD_RESULT read(void *outbuffer, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + }; +} + +#endif diff --git a/src/fmod_dsp_tremolo.cpp b/src/fmod_dsp_tremolo.cpp new file mode 100755 index 0000000..04f509f --- /dev/null +++ b/src/fmod_dsp_tremolo.cpp @@ -0,0 +1,1120 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_TREMOLO + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_tremolo.h" +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" +#else + #include "fmod_systemi.h" +#endif + +#include <stdio.h> + +#ifdef PLATFORM_PS3 +extern unsigned int _binary_spu_fmod_dsp_tremolo_pic_start[]; +#endif + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dsptremolo_desc; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPTremolo::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +const DSPTremolo::speakerPhaseMap DSPTremolo::phaseMap[4] = +{ + // F/left F/Right Front Woofer B/Left B/Right S/Left S/Right + { 2, { -0.25f, 0.25f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }}, + { 4, { -0.125f, 0.125f,-0.375f, 0.375f, 0.0f, 0.0f, 0.0f, 0.0f }}, + { 6, { -0.2f, 0.2f, 0.0f, 0.0f, -0.4f, 0.4f, 0.0f, 0.0f }}, + { 8, { -0.143f, 0.143f, 0.0f, 0.0f, -0.429f, 0.429f,-0.286f, 0.286f }} +}; + + +FMOD_DSP_PARAMETERDESC dsptremolo_param[8] = +{ + { 0.01f, 20.0f, 5.0f, "Frequency", "Hz", "LFO frequency in Hz. 0.01 to 20. Default = 5." }, + { 0.0f, 1.0f, 1.0f, "Depth", "", "Tremolo depth. 0 to 1. Default = 1." }, + { 0.0f, 1.0f, 0.0f, "Shape", "", "LFO shape morph between triangle and sine. 0 to 1. Default = 0." }, + { -1.0f, 1.0f, 0.0f, "Skew", "", "Time-skewing of LFO cycle. -1 to 1. Default = 0." }, + { 0.0f, 1.0f, 0.5f, "Duty", "", "LFO on-time. 0 to 1. Default = 0.5." }, + { 0.0f, 1.0f, 0.0f, "Square", "", "Flatness of the LFO shape. 0 to 1. Default = 0." }, + { 0.0f, 1.0f, 0.0f, "Phase", "", "Instantaneous LFO phase. 0 to 1. Default = 0." }, + { -1.0f, 1.0f, 0.0f, "Spread", "", "Rotation / auto-pan effect. -1 to 1. Default = 0." }, +}; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPTremolo::getDescriptionEx() +{ +#ifndef PLATFORM_PS3_SPU + FMOD_memset(&dsptremolo_desc, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dsptremolo_desc.name, "FMOD Tremolo"); + dsptremolo_desc.version = 0x00010100; + dsptremolo_desc.create = DSPTremolo::createCallback; + dsptremolo_desc.release = DSPTremolo::releaseCallback; + dsptremolo_desc.reset = DSPTremolo::resetCallback; + + #ifdef PLATFORM_PS3 + dsptremolo_desc.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_tremolo_pic_start; /* SPU PIC entry address */ + #else + dsptremolo_desc.read = DSPTremolo::readCallback; + #endif + + dsptremolo_desc.numparameters = sizeof(dsptremolo_param) / sizeof(dsptremolo_param[0]); + dsptremolo_desc.paramdesc = dsptremolo_param; + dsptremolo_desc.setparameter = DSPTremolo::setParameterCallback; + dsptremolo_desc.getparameter = DSPTremolo::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsptremolo_desc.getmemoryused = &DSPTremolo::getMemoryUsedCallback; +#endif + + dsptremolo_desc.mType = FMOD_DSP_TYPE_TREMOLO; + dsptremolo_desc.mCategory = FMOD_DSP_CATEGORY_FILTER; + dsptremolo_desc.mSize = sizeof(DSPTremolo); +#else + dsptremolo_desc.read = DSPTremolo::readCallback; /* We only care about read function on SPU */ +#endif + return &dsptremolo_desc; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPTremolo::createInternal() +{ + int count; + + init(); + + mChannels = 0; + mOldSpeakerMask = 0xFFFF; + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Do a forced update here to make sure everything is calculated correctly first. + */ + { + mSystem->getSoftwareFormat(&mOutputRate, 0, &mChannels, 0, 0, 0); + + mTransitionRampFactor = 1.0f / (float)TREMOLO_TRANSITION; + mInvTableSize = 1.0f / (float)TREMOLO_TABLE_SIZE; + + mFrequency = mFrequencyUpdate; + mDepth = mDepthUpdate; + mShape = mShapeUpdate; + mSkew = mSkewUpdate; + mDuty = mDutyUpdate; + mSquare = mSquareUpdate; + mPhase = mPhaseUpdate; + mSpread = mSpreadUpdate; + + mLFOMin = 1.0f - mDepth; + + resetInternal(); + + createLFOTable(); + updateWaveform(); + updateTiming(); + applyPhase(); + } + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + +void DSPTremolo::createLFOTable() +{ + float angle = -FMOD_PI_2; + float anglestep = FMOD_PI * mInvTableSize; + float ramp = 0.0f; + float rampstep = 1.0f * mInvTableSize; + + for (int i = 0; i <= TREMOLO_TABLE_SIZE; i++) + { + mLFOTable[i] = mShape * (FMOD_SIN(angle) * 0.5f + 0.5f); + mLFOTable[i] += (1.0f - mShape) * ramp; + + angle += anglestep; + ramp += rampstep; + } +} + + +float DSPTremolo::readLFOTable(int index, bool forward, float *ramp) +{ + if (forward) + { + if (index == TREMOLO_TABLE_SIZE) + { + *ramp = 0.0f; + } + else + { + *ramp = (mLFOTable[index + 1] - mLFOTable[index]) * mUpRampFactor; + } + } + else + { + if (index == 0) + { + *ramp = 0.0f; + } + else + { + *ramp = (mLFOTable[index - 1] - mLFOTable[index]) * mDownRampFactor; + } + } + + return mLFOMin + mDepth * mLFOTable[index]; +} + + +void DSPTremolo::updateWaveform() +{ + float skew = (mSkew + 1.0f) * 0.5f; + float ontime = (1.0f - mDuty) * skew; + float offtime = skew + (1.0f - skew) * mDuty; + float halfuptime = (1.0f - mSquare) * FMOD_MIN(ontime, skew - ontime); + float halfdowntime = (1.0f - mSquare) * FMOD_MIN(offtime - skew, 1.0f - offtime); + + mKUp = ontime - halfuptime; + mKHigh = ontime + halfuptime; + mKDown = offtime - halfdowntime; + mKLow = offtime + halfdowntime; +} + + +void DSPTremolo::updateTiming() +{ + float phase[TREMOLO_MAX_CHANNELS]; + float nextramp[TREMOLO_MAX_CHANNELS]; + int ch; + + for (ch = 0; ch < mChannels; ch++) + { + phase[ch] = (float)mLFOPos[ch] * mInvPeriod; + nextramp[ch] = (float)mLFONextRamp[ch] * mInvPeriod; + } + + mPeriod = (float)mOutputRate / mFrequency; + mInvPeriod = 1.0f / mPeriod; + mPeriodSamples = (int)(floor(mPeriod)); + mPeriodAdjust = mPeriod - floor(mPeriod); + mPeriodOffset = 0.0f; + mPeriodAddSample = 0; + + mTableUpPeriod = mPeriod * (mKHigh - mKUp) * mInvTableSize; + mTableDownPeriod = mPeriod * (mKLow - mKDown) * mInvTableSize; + + if (mTableUpPeriod <= 0.0f) + { + mTableUpPeriod = 0.0f; + mUpRampFactor = 0.0f; + } + else + { + mUpRampFactor = mDepth / mTableUpPeriod; + } + + if (mTableDownPeriod <= 0.0f) + { + mTableDownPeriod = 0.0f; + mDownRampFactor = 0.0f; + } + else + { + mDownRampFactor = mDepth / mTableDownPeriod; + } + + mPosUp = (int)(mPeriod * mKUp); + mPosHigh = (int)(mPeriod * mKHigh); + mPosDown = (int)(mPeriod * mKDown); + mPosLow = (int)(mPeriod * mKLow); + + for (ch = 0; ch < mChannels; ch++) + { + mLFOPos[ch] = (int)(phase[ch] * mPeriod); + if ((float)mLFOPos[ch] >= mPeriod) + { + mLFOPos[ch] = 0; + } + mLFONextRamp[ch] = (int)(nextramp[ch] * mPeriod); + if (mLFONextRamp[ch] >= mPeriodSamples) + { + mLFONextRamp[ch] = 0; + } + } +} + + +void DSPTremolo::applyPhase() +{ + int map = -1; + int i, ch; + + for (i = 0; i < sizeof(phaseMap)/sizeof(phaseMap[0]); i++) + { + if (phaseMap[i].channelcount == mChannels && mChannels <= TREMOLO_MAX_CHANNEL_MAPS) + { + map = i; + } + } + + for (ch = 0; ch < mChannels; ch++) + { + if (map >= 0) + { + mLFOPos[ch] = (int)((mPhase - phaseMap[map].phase[ch] * mSpread) * mPeriod); + } + else + { + mLFOPos[ch] = (int)((mPhase - ((float)i / (float)mChannels * mSpread)) * mPeriod); + } + + if (mLFOPos[ch] >= mPeriodSamples) + { + mLFOPos[ch] -= mPeriodSamples; + } + else if (mLFOPos[ch] < 0) + { + mLFOPos[ch] += mPeriodSamples; + } + } +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPTremolo::releaseInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPTremolo::resetInternal() +{ + for (int i = 0; i < TREMOLO_MAX_CHANNELS; i++) + { + mLFOPos[i] = 0; + mLFOLevel[i] = 0.0f; + mLFORamp[i] = 0.0f; + mLFONextRamp[i] = 0; + } + + mUpdatePhase = false; + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + + +void DSPTremolo::getRampValues(int samplepos, float *p_lfolevel, float *p_lforamp, int *p_nextramppos) +{ + int tableindex; + + if (samplepos >= mPosLow) // LFO bottom + { + *p_lfolevel = 1.0f - mDepth; + *p_lforamp = 0.0f; + + *p_nextramppos = mPosUp; + } + else if (samplepos >= mPosDown) // LFO downward slope + { + tableindex = ((samplepos - mPosDown) * TREMOLO_TABLE_SIZE) / (mPosLow - mPosDown); + + if (tableindex + 1 == TREMOLO_TABLE_SIZE) + { + *p_nextramppos = mPosLow; + } + else + { + *p_nextramppos = mPosDown + (int)(mTableDownPeriod * (float)(tableindex + 1) + 0.5f); + } + + if (samplepos != mPosDown + (int)(mTableDownPeriod * (float)(tableindex) + 0.5f)) + { + *p_lfolevel = readLFOTable(TREMOLO_TABLE_SIZE - tableindex, false, p_lforamp); + *p_lfolevel += *p_lforamp * ((float)samplepos - ((float)mPosDown + tableindex * mTableDownPeriod)); + } + else + { + if (tableindex == 0) + { + *p_lfolevel = readLFOTable(TREMOLO_TABLE_SIZE - tableindex, true, p_lforamp); + } + else + { + readLFOTable(TREMOLO_TABLE_SIZE - tableindex, false, p_lforamp); + } + } + } + else if (samplepos >= mPosHigh) // LFO top + { + *p_lfolevel = 1.0f; + *p_lforamp = 0.0f; + + *p_nextramppos = mPosDown; + } + else if (samplepos >= mPosUp) // LFO upward slope + { + tableindex = ((samplepos - mPosUp) * TREMOLO_TABLE_SIZE) / (mPosHigh - mPosUp); + + if (tableindex + 1 == TREMOLO_TABLE_SIZE) + { + *p_nextramppos = mPosHigh; + } + else + { + *p_nextramppos = mPosUp + (int)(mTableUpPeriod * (float)(tableindex + 1) + 0.5f); + } + + if (samplepos != mPosUp + (int)(mTableUpPeriod * (float)(tableindex) + 0.5f)) + { + *p_lfolevel = readLFOTable(tableindex, true, p_lforamp); + *p_lfolevel += *p_lforamp * ((float)samplepos - ((float)mPosUp + tableindex * mTableUpPeriod)); + } + else + { + if (tableindex == 0) + { + *p_lfolevel = readLFOTable(tableindex, true, p_lforamp); + } + else + { + readLFOTable(tableindex, true, p_lforamp); + } + } + } + else // LFO bottom + { + *p_lfolevel = 1.0f - mDepth; + *p_lforamp = 0.0f; + + *p_nextramppos = mPosUp; + } + + if (*p_nextramppos >= mPeriodSamples) + { + *p_nextramppos = 0; + } +} + +float DSPTremolo::getLFOLevel(int samplepos) +{ + int tableindex; + float level; + float ramp; + + if (samplepos >= mPosLow) // LFO bottom + { + level = 1.0f - mDepth; + } + else if (samplepos >= mPosDown) // LFO downward slope + { + tableindex = ((samplepos - mPosDown) * TREMOLO_TABLE_SIZE) / (mPosLow - mPosDown); + + level = readLFOTable(TREMOLO_TABLE_SIZE - tableindex, false, &ramp); + level += ramp * ((float)samplepos - ((float)mPosDown + tableindex * mTableDownPeriod)); + } + else if (samplepos >= mPosHigh) // LFO top + { + level = 1.0f; + } + else if (samplepos >= mPosUp) // LFO upward slope + { + tableindex = ((samplepos - mPosUp) * TREMOLO_TABLE_SIZE) / (mPosHigh - mPosUp); + + level = readLFOTable(tableindex, true, &ramp); + level += ramp * ((float)samplepos - ((float)mPosUp + tableindex * mTableUpPeriod)); + } + else // LFO bottom + { + level = 1.0f - mDepth; + } + + return level; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPTremolo::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + bool changedepth = false; + bool changetiming = false; + bool changeshape = false; + bool changewaveform = false; + bool changephase = false; + bool transition = false; + + #define OO_32767 (1.0f / 32767.0f) + + if (!inbuffer) + { + return FMOD_OK; + } + + int outputrate = 0; + + #ifdef PLATFORM_PS3 + outputrate = 48000; + #else + mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + #endif + + /* + Update parameters + */ + if (mChannels != inchannels) + { + mChannels = inchannels; + changephase = true; + } + if (mOutputRate != outputrate) + { + mOutputRate = outputrate; + changetiming = true; + } + if (mFrequency != mFrequencyUpdate) + { + mFrequency = mFrequencyUpdate; + changetiming = true; + } + if (mDepth != mDepthUpdate) + { + mDepth = mDepthUpdate; + changedepth = true; + } + if (mShape != mShapeUpdate) + { + mShape = mShapeUpdate; + changeshape = true; + } + if (mSkew != mSkewUpdate) + { + mSkew = mSkewUpdate; + changewaveform = true; + } + if (mDuty != mDutyUpdate) + { + mDuty = mDutyUpdate; + changewaveform = true; + } + if (mSquare != mSquareUpdate) + { + mSquare = mSquareUpdate; + changewaveform = true; + } + if (mUpdatePhase) + { + mUpdatePhase = false; + mPhase = mPhaseUpdate; + changephase = true; + } + if (mSpread != mSpreadUpdate) + { + mSpread = mSpreadUpdate; + changephase = true; + } + + if (changedepth) + { + mLFOMin = 1.0f - mDepth; + + mUpRampFactor = mDepth / mTableUpPeriod; + mDownRampFactor = mDepth / mTableDownPeriod; + transition = true; + } + if (changeshape) + { + createLFOTable(); + transition = true; + } + if (changewaveform) + { + updateWaveform(); + changetiming = true; + transition = true; + } + if (changetiming) + { + updateTiming(); + transition = true; + } + if (changephase) + { + applyPhase(); + transition = true; + } + + if (!(speakermask & ((1 << inchannels)-1)) ) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_OK; + } + + { + float *in = inbuffer; + float *out = outbuffer; + int ch; + + if (length--) + { + for (ch = 0; ch < inchannels; ch++) + { + if (transition) + { + mLFONextRamp[ch] = mLFOPos[ch] + TREMOLO_TRANSITION; + if (mLFONextRamp[ch] >= mPeriodSamples) + { + mLFONextRamp[ch] = 0; + } + mLFORamp[ch] = (getLFOLevel(mLFONextRamp[ch]) - mLFOLevel[ch]) * mTransitionRampFactor; + } + else + { + getRampValues(mLFOPos[ch], &mLFOLevel[ch], &mLFORamp[ch], &mLFONextRamp[ch]); + } + out[ch] = in[ch] * mLFOLevel[ch]; + mLFOLevel[ch] += mLFORamp[ch]; + + if (++mLFOPos[ch] >= mPeriodSamples) + { + if (ch == 0) + { + mPeriodOffset += mPeriodAdjust; + if (mPeriodOffset >= 1.0f) + { + mPeriodOffset -= 1.0f; + mPeriodAddSample = inchannels; + } + } + + if (mPeriodAddSample > 0) + { + mLFOPos[ch] = -1; + mPeriodAddSample--; + } + else + { + mLFOPos[ch] = 0; + } + } + } + + in += inchannels; + out += inchannels; + } + + while (length--) + { + for (ch = 0; ch < inchannels; ch++) + { + if (mLFOPos[ch] >= mLFONextRamp[ch]) + { + getRampValues(mLFOPos[ch], &mLFOLevel[ch], &mLFORamp[ch], &mLFONextRamp[ch]); + } + + out[ch] = in[ch] * mLFOLevel[ch]; + mLFOLevel[ch] += mLFORamp[ch]; + + if (++mLFOPos[ch] >= mPeriodSamples) + { + if (ch == 0) + { + mPeriodOffset += mPeriodAdjust; + if (mPeriodOffset >= 1.0f) + { + mPeriodOffset -= 1.0f; + mPeriodAddSample = inchannels; + } + } + + if (mPeriodAddSample > 0) + { + mLFOPos[ch] = -1; + mPeriodAddSample--; + } + else + { + mLFOPos[ch] = 0; + } + } + } + + in += inchannels; + out += inchannels; + } + } + + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPTremolo::setParameterInternal(int index, float value) +{ + switch (index) + { + case FMOD_DSP_TREMOLO_FREQUENCY: + { + mFrequencyUpdate = value; + break; + } + case FMOD_DSP_TREMOLO_DEPTH: + { + mDepthUpdate = value; + break; + } + case FMOD_DSP_TREMOLO_SHAPE: + { + mShapeUpdate = value; + break; + } + case FMOD_DSP_TREMOLO_SKEW: + { + mSkewUpdate = value; + break; + } + case FMOD_DSP_TREMOLO_DUTY: + { + mDutyUpdate = value; + break; + } + case FMOD_DSP_TREMOLO_SQUARE: + { + mSquareUpdate = value; + break; + } + case FMOD_DSP_TREMOLO_PHASE: + { + mPhaseUpdate = value; + mUpdatePhase = (value > 0.0f); + break; + } + case FMOD_DSP_TREMOLO_SPREAD: + { + mSpreadUpdate = value; + break; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPTremolo::getParameterInternal(int index, float *value, char *valuestr) +{ + switch (index) + { + case FMOD_DSP_TREMOLO_FREQUENCY: + { + *value = mFrequencyUpdate; + sprintf(valuestr, "%.02f", mFrequencyUpdate); + break; + } + case FMOD_DSP_TREMOLO_DEPTH: + { + *value = mDepthUpdate; + sprintf(valuestr, "%.02f", mDepthUpdate); + break; + } + case FMOD_DSP_TREMOLO_SHAPE: + { + *value = mShapeUpdate; + sprintf(valuestr, "%.02f", mShapeUpdate); + break; + } + case FMOD_DSP_TREMOLO_SKEW: + { + *value = mSkewUpdate; + sprintf(valuestr, "%.02f", mSkewUpdate); + break; + } + case FMOD_DSP_TREMOLO_DUTY: + { + *value = mDutyUpdate; + sprintf(valuestr, "%.02f", mDutyUpdate); + break; + } + case FMOD_DSP_TREMOLO_SQUARE: + { + *value = mSquareUpdate; + sprintf(valuestr, "%.02f", mSquareUpdate); + break; + } + case FMOD_DSP_TREMOLO_PHASE: + { + *value = mPhaseUpdate; + sprintf(valuestr, "%.02f", mPhaseUpdate); + break; + } + case FMOD_DSP_TREMOLO_SPREAD: + { + *value = mSpreadUpdate; + sprintf(valuestr, "%.02f", mSpreadUpdate); + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPTremolo::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->resetInternal(); +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPTremolo::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPTremolo *tremolo = (DSPTremolo *)dsp; + + return tremolo->DSPTremolo::getMemoryUsed(tracker); +} +#endif + +#endif // !PLATFORM_PS3_SPU + +} + +#endif diff --git a/src/fmod_dsp_tremolo.h b/src/fmod_dsp_tremolo.h new file mode 100755 index 0000000..f0db64f --- /dev/null +++ b/src/fmod_dsp_tremolo.h @@ -0,0 +1,127 @@ +#ifndef _FMOD_DSP_TREMOLO_H +#define _FMOD_DSP_TREMOLO_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_TREMOLO + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +#define TREMOLO_MAX_CHANNELS 16 +#define TREMOLO_MAX_CHANNEL_MAPS 8 +#define TREMOLO_TABLE_SIZE 16 +#define TREMOLO_TRANSITION 128 + +namespace FMOD +{ + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + class DSPTremolo : public DSPI + #else + class DSPTremolo : public DSPFilter + #endif + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + typedef struct { + int channelcount; + float phase[TREMOLO_MAX_CHANNEL_MAPS]; + } speakerPhaseMap; + + static const speakerPhaseMap phaseMap[]; + + float FMOD_PPCALIGN16(mFrequency); + float FMOD_PPCALIGN16(mFrequencyUpdate); + float FMOD_PPCALIGN16(mDepth); + float FMOD_PPCALIGN16(mDepthUpdate); + float FMOD_PPCALIGN16(mShape); + float FMOD_PPCALIGN16(mShapeUpdate); + float FMOD_PPCALIGN16(mSkew); + float FMOD_PPCALIGN16(mSkewUpdate); + float FMOD_PPCALIGN16(mDuty); + float FMOD_PPCALIGN16(mDutyUpdate); + float FMOD_PPCALIGN16(mSquare); + float FMOD_PPCALIGN16(mSquareUpdate); + float FMOD_PPCALIGN16(mPhase); + float FMOD_PPCALIGN16(mPhaseUpdate); + bool FMOD_PPCALIGN16(mUpdatePhase); + float FMOD_PPCALIGN16(mSpread); + float FMOD_PPCALIGN16(mSpreadUpdate); + + float FMOD_PPCALIGN16(mKUp); + float FMOD_PPCALIGN16(mKHigh); + float FMOD_PPCALIGN16(mKDown); + float FMOD_PPCALIGN16(mKLow); + + int FMOD_PPCALIGN16(mPosUp); + int FMOD_PPCALIGN16(mPosHigh); + int FMOD_PPCALIGN16(mPosDown); + int FMOD_PPCALIGN16(mPosLow); + + float FMOD_PPCALIGN16(mPeriod); + int FMOD_PPCALIGN16(mPeriodSamples); + + float FMOD_PPCALIGN16(mPeriodAdjust); + float FMOD_PPCALIGN16(mPeriodOffset); + int FMOD_PPCALIGN16(mPeriodAddSample); + + float FMOD_PPCALIGN16(mTableUpPeriod); + float FMOD_PPCALIGN16(mTableDownPeriod); + float FMOD_PPCALIGN16(mUpRampFactor); + float FMOD_PPCALIGN16(mDownRampFactor); + float FMOD_PPCALIGN16(mTransitionRampFactor); + float FMOD_PPCALIGN16(mLFOMin); + + float FMOD_PPCALIGN16(mInvPeriod); + float FMOD_PPCALIGN16(mInvTableSize); + + int FMOD_PPCALIGN16(mLFOPos[TREMOLO_MAX_CHANNELS]); + float FMOD_PPCALIGN16(mLFOLevel[TREMOLO_MAX_CHANNELS]); + float FMOD_PPCALIGN16(mLFORamp[TREMOLO_MAX_CHANNELS]); + int FMOD_PPCALIGN16(mLFONextRamp[TREMOLO_MAX_CHANNELS]); + + float FMOD_PPCALIGN16(mLFOTable[TREMOLO_TABLE_SIZE+1]); + + int FMOD_PPCALIGN16(mOutputRate); + int FMOD_PPCALIGN16(mChannels); + + unsigned short mOldSpeakerMask; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + + void createLFOTable(); + void updateWaveform(); + void updateTiming(); + void applyPhase(); + + void getRampValues(int samplepos, float *p_lfolevel, float *p_lforamp, int *p_nextramppos); + float getLFOLevel(int samplepos); + float readLFOTable(int pos, bool forward, float *ramp); + + public: + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker); +#endif + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_vstplugin.cpp b/src/fmod_dsp_vstplugin.cpp new file mode 100755 index 0000000..8ae6748 --- /dev/null +++ b/src/fmod_dsp_vstplugin.cpp @@ -0,0 +1,755 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_VSTPLUGIN + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_vstplugin.h" +#include "fmod_memory.h" +#include "fmod_os_misc.h" +#include "fmod_systemi.h" + + +#define BUFFERSIZE 256 + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspvstplugin; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPVSTPlugin::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPVSTPlugin::getDescriptionEx() +{ + FMOD_memset(&dspvstplugin, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + // name is stamped in later. + dspvstplugin.create = DSPVSTPlugin::createCallback; + dspvstplugin.release = DSPVSTPlugin::releaseCallback; + dspvstplugin.reset = DSPVSTPlugin::resetCallback; + dspvstplugin.read = DSPVSTPlugin::readCallback; + + dspvstplugin.setparameter = DSPVSTPlugin::setParameterCallback; + dspvstplugin.getparameter = DSPVSTPlugin::getParameterCallback; + dspvstplugin.config = DSPVSTPlugin::configCallback; + + #ifdef FMOD_SUPPORT_DLLS + dspvstplugin.configidle = DSPVSTPlugin::configIdleCallback; + #endif + + dspvstplugin.mType = FMOD_DSP_TYPE_VSTPLUGIN; + dspvstplugin.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspvstplugin.mSize = sizeof(DSPVSTPlugin); + + return &dspvstplugin; +} + + +float *DSPVSTPlugin::mDeInterleavedInputBuffer[DSP_MAXLEVELS_MAX] = { 0 }; +float *DSPVSTPlugin::mDeInterleavedOutputBuffer[DSP_MAXLEVELS_MAX] = { 0 }; +int DSPVSTPlugin::mDeInterleavedBufferCount = 0; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +long DSPVSTPlugin::audioMasterCB(AEffect *effect, long opcode, long index, long value, void *ptr, float opt) +{ + switch(opcode) + { + case audioMasterAutomate: + { + return 0; + } + case audioMasterVersion: + { + return 2300; + } + case audioMasterCurrentId: + { + return 0; + } + case audioMasterIdle: + { + return 0; + } + case audioMasterPinConnected: + { + return 0; + } + } + + return 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::createInternal() +{ + unsigned int buffersize; + int samplerate; + + init(); + + /* + Initialise the plugin + */ + mSystem->getDSPBufferSize(&buffersize, 0); + mSystem->getSoftwareFormat(&samplerate, 0, 0, 0, 0, 0); + + mEffect->dispatcher(mEffect, effOpen, 0, 0, 0, 0); + mEffect->dispatcher(mEffect, effSetSampleRate, 0, 0, 0, (float)samplerate); + mEffect->dispatcher(mEffect, effSetBlockSize, 0, buffersize, 0, 0); + mEffect->dispatcher(mEffect, effMainsChanged, 0, 1, 0, 0); // This calls Resume() in the plugin + + /* + Allocate memory for the deinterleaved buffers + */ + if (mDeInterleavedBufferCount == 0) + { + int count; + + for (count = 0; count < DSP_MAXLEVELS_MAX; count ++) + { + mDeInterleavedInputBuffer[count] = (float *)FMOD_Memory_Alloc(sizeof(float) * BUFFERSIZE); + if (!mDeInterleavedInputBuffer[count]) + { + return FMOD_ERR_MEMORY; + } + } + + for (count = 0; count < DSP_MAXLEVELS_MAX; count ++) + { + mDeInterleavedOutputBuffer[count] = (float *)FMOD_Memory_Alloc(sizeof(float) * BUFFERSIZE); + if (!mDeInterleavedOutputBuffer[count]) + { + return FMOD_ERR_MEMORY; + } + } + } + + mDeInterleavedBufferCount++; + + mCallIdle = false; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::releaseInternal() +{ + mEffect->dispatcher(mEffect, effMainsChanged, 0, 0, 0, 0); + + mDeInterleavedBufferCount--; + + if (mDeInterleavedBufferCount == 0) + { + int i; + + for (i = 0; i < DSP_MAXLEVELS_MAX ; i++) + { + if (mDeInterleavedInputBuffer[i]) + { + FMOD_Memory_Free(mDeInterleavedInputBuffer[i]); + mDeInterleavedInputBuffer[i] = 0; + } + if (mDeInterleavedOutputBuffer[i]) + { + FMOD_Memory_Free(mDeInterleavedOutputBuffer[i]); + mDeInterleavedOutputBuffer[i] = 0; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::resetInternal() +{ + mEffect->dispatcher(mEffect, effMainsChanged, 0, 0, 0, 0); // suspend + mEffect->dispatcher(mEffect, effMainsChanged, 0, 1, 0, 0); // resume + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + unsigned int remaining = length; + int i, j; + bool downmix = false; + + float *in = inbuffer; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << outchannels)-1))) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_OK; + } + + /* + Downmix to mono if thats what the vst plugin wants + */ + if (inchannels > 1 && mEffect->numInputs == 1) + { + downmix = true; + } + else if (inchannels != mEffect->numInputs) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_ERR_FORMAT; + } + else if (outchannels != mEffect->numOutputs) + { + FMOD_memcpy(outbuffer, inbuffer, length * outchannels * sizeof(float)); + return FMOD_ERR_FORMAT; + } + + /* + Clear the output buffer + */ + for (i = 0; i < outchannels; i++) + { + FMOD_memset(mDeInterleavedOutputBuffer[i], 0, sizeof(float) * BUFFERSIZE); + } + + while(remaining > 0) + { + int size = remaining; + + if (size > BUFFERSIZE) + { + size = BUFFERSIZE; + } + + /* + If the plugin expects mono, downmix inbuffer to mono + */ + if (downmix) + { + for (i = 0; i < size; i++) + { + float data = 0.0f; + + for (j = 0; j < inchannels; j++) + { + data += *inbuffer++; + } + + mDeInterleavedInputBuffer[0][i] = data / inchannels; + } + } + else + { + /* + DeInterleave + */ + for (i = 0; i < size; i++) + { + for (j = 0; j < inchannels; j++) + { + mDeInterleavedInputBuffer[j][i] = *inbuffer++; + } + } + } + + /* + Process data + */ + if (mEffect) + { + mEffect->dispatcher(mEffect, effStartProcess, 0, 0, 0, 0); + + if (mEffect->processReplacing) + { + mEffect->processReplacing(mEffect, mDeInterleavedInputBuffer, mDeInterleavedOutputBuffer, size); + } + else if (mEffect->process) + { + mEffect->process(mEffect, mDeInterleavedInputBuffer, mDeInterleavedOutputBuffer, size); + } + + mEffect->dispatcher(mEffect, effStopProcess, 0, 0, 0, 0); + } + + /* + Interleave back into buffer + */ + if (downmix) + { + /* + If the input was downmixed, duplicate the resulting mono buffer for each output channel + */ + for (i = 0; i < size; i++) + { + for (j = 0; j < outchannels; j++) + { + if (!((1 << j) & speakermask)) + { + *outbuffer++ = mDeInterleavedInputBuffer[0][i]; + } + else + { + *outbuffer++ = mDeInterleavedOutputBuffer[0][i]; + } + } + } + } + else + { + for (i = 0; i < size; i++) + { + for (j = 0; j < outchannels; j++) + { + if (!((1 << j) & speakermask)) + { + *outbuffer++ = *in; + } + else + { + *outbuffer++ = mDeInterleavedOutputBuffer[j][i]; + } + in++; + } + } + } + + remaining -= size; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::setParameterInternal(int index, float value) +{ + mSystem->lockDSP(); + { + mEffect->setParameter(mEffect, index, value); + } + mSystem->unlockDSP(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::getParameterInternal(int index, float *value, char *valuestr) +{ + *value = mEffect->getParameter(mEffect, index); + + mEffect->dispatcher(mEffect, effGetParamDisplay, index, 0, valuestr, 0); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::showConfigDialogInternal(void *hwnd, int show) +{ + if (mEffect->flags & effFlagsHasEditor) + { + mEffect->dispatcher(mEffect, (show ? effEditOpen : effEditClose), 0, 0, hwnd, 0); + + mCallIdle = show ? true : false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPVSTPlugin::configIdleInternal() +{ + if (mCallIdle) + { + mEffect->dispatcher(mEffect, effEditIdle, 0, 0, 0, 0); + } + + return FMOD_OK; +} + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->getParameterInternal(index, value, valuestr); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::configCallback(FMOD_DSP_STATE *dsp, void *hwnd, int show) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->showConfigDialogInternal(hwnd, show); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPVSTPlugin::configIdleCallback(FMOD_DSP_STATE *dsp) +{ + DSPVSTPlugin *vst = (DSPVSTPlugin *)dsp; + + return vst->configIdleInternal(); +} +} + +#endif diff --git a/src/fmod_dsp_vstplugin.h b/src/fmod_dsp_vstplugin.h new file mode 100755 index 0000000..b9a2541 --- /dev/null +++ b/src/fmod_dsp_vstplugin.h @@ -0,0 +1,59 @@ +#ifndef _FMOD_DSP_VSTPLUGIN_H +#define _FMOD_DSP_VSTPLUGIN_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_VSTPLUGIN + +#include "fmod.h" +#include "fmod_dsp_filter.h" +#include "fmod_os_misc.h" + +#include "../lib/vst/AEffect.h" +#include "../lib/vst/aeffectx.h" + + +namespace FMOD +{ + class DSPVSTPlugin : public DSPFilter + { + private: + + static float *mDeInterleavedInputBuffer[DSP_MAXLEVELS_MAX]; + static float *mDeInterleavedOutputBuffer[DSP_MAXLEVELS_MAX]; + static int mDeInterleavedBufferCount; + + bool mCallIdle; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + FMOD_RESULT showConfigDialogInternal(void *hwnd, int show); + FMOD_RESULT configIdleInternal(); + + public: + + AEffect *mEffect; + + static long VSTCALLBACK audioMasterCB(AEffect *effect, long opcode, long index, long value, void *ptr, float opt); + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); + static FMOD_RESULT F_CALLBACK configCallback(FMOD_DSP_STATE *dsp, void *hwnd, int show); + static FMOD_RESULT F_CALLBACK configIdleCallback(FMOD_DSP_STATE *dsp); + }; +} + +#endif + +#endif + diff --git a/src/fmod_dsp_wavetable.cpp b/src/fmod_dsp_wavetable.cpp new file mode 100755 index 0000000..f3e344a --- /dev/null +++ b/src/fmod_dsp_wavetable.cpp @@ -0,0 +1,862 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_channel_software.h" +#include "fmod_dsp_wavetable.h" +#include "fmod_dsp_resampler.h" +#include "fmod_dsp_resampler_nointerp.h" +#include "fmod_dsp_resampler_linear.h" +#include "fmod_dsp_resampler_cubic.h" +#include "fmod_dsp_resampler_spline.h" +#include "fmod_sample_software.h" + +#include "fmod_systemi.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::alloc(FMOD_DSP_DESCRIPTION_EX *description) +{ + FMOD_RESULT result; + + result = DSPI::alloc(description); + if (result != FMOD_OK) + { + return result; + } + + mFrequency = 0; + result = mSystem->getSoftwareFormat(&mTargetFrequency, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mDirection = DSPWAVETABLE_SPEEDDIR_FORWARDS; + mNewPosition = 0xFFFFFFFF; + mDSPFinishTick = 0xFFFFFFFF; + mDSPTick = 0xFFFFFFFF; + + if (description->mDSPSoundCard) + { + mDSPSoundCard = description->mDSPSoundCard; + } + else + { + mDSPSoundCard = mSystem->mDSPSoundCard; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::addInput(DSPI *target) +{ + FMOD_RESULT result; + + result = DSPI::addInput(target); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + FMOD_RESULT result = FMOD_OK; + + mFlags &= ~FMOD_DSP_FLAG_IDLE; + mFlags &= ~FMOD_DSP_FLAG_FINISHED; + + if (mDSPTick != tick) + { + void *readbuffer; + SampleSoftware *samplesoftware; + unsigned int outlength = *length; + unsigned int readoffset = 0; + unsigned int soundoffset = 0; + unsigned int soundlength = 0; + FMOD_SINT64P origspeed; + unsigned int starttime = 0; + unsigned int endtime = 0; + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&starttime); + } + + if (mNewPosition != 0xFFFFFFFF) + { + mPosition.mHi = mNewPosition; + mPosition.mLo = 0; + mNewPosition = 0xFFFFFFFF; + } + +#ifdef FMOD_SUPPORT_SENTENCING + if (mSound->mSubSoundList) /* Pick the right sound pointer to mix from, set the offset. */ + { + int count; + + soundoffset = 0; + count = 0; + samplesoftware = SAFE_CAST(SampleSoftware, mSound->mSubSound[mSound->mSubSoundList[count].mIndex]); + + while (count < mChannel->mSubSoundListCurrent) + { + if (samplesoftware) + { + soundoffset += samplesoftware->mLength; + } + count++; + samplesoftware = SAFE_CAST(SampleSoftware, mSound->mSubSound[mSound->mSubSoundList[count].mIndex]); + } + + if (!samplesoftware) + { + return FMOD_ERR_INVALID_PARAM; + } + } + else +#endif + { + samplesoftware = (SampleSoftware *)mSound; + if (!samplesoftware) + { + return FMOD_ERR_INVALID_PARAM; + } + } + + soundlength = samplesoftware->mLength; + + readbuffer = samplesoftware->mBuffer; + if (!readbuffer) + { + FMOD_memset(mBuffer, 0, outlength * sizeof(float) * mSound->mChannels); + mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + *outchannels = mSound->mChannels; + return FMOD_OK; + } + origspeed = mSpeed; + + if (mDSPClockEnd.mValue > 0 && + mDSPClockEnd.mValue < mSystem->mDSPClock.mValue + outlength) + { + unsigned int diff = (unsigned int)(mSystem->mDSPClock.mValue + outlength - mDSPClockEnd.mValue); + if (diff > outlength) + { + diff = outlength; + } + + FMOD_memset(mBuffer + ((outlength - diff) * mSound->mChannels), 0, diff * sizeof(float) * mSound->mChannels); + outlength -= diff; + + mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + } + if (mDSPClockPause.mValue > 0 && + mDSPClockPause.mValue < mSystem->mDSPClock.mValue + outlength) + { + unsigned int diff = (unsigned int)(mSystem->mDSPClock.mValue + outlength - mDSPClockPause.mValue); + if (diff > outlength) + { + diff = outlength; + } + + FMOD_memset(mBuffer + ((outlength - diff) * mSound->mChannels), 0, diff * sizeof(float) * mSound->mChannels); + outlength -= diff; + + mFlags &= ~FMOD_DSP_FLAG_ACTIVE; + mDSPClockPause.mValue = 0; + } + + if (mDSPClockStart.mValue > mSystem->mDSPClock.mValue && + mDSPClockStart.mValue + outlength > mSystem->mDSPClock.mValue) + { + unsigned int diff = (unsigned int)(mDSPClockStart.mValue - mSystem->mDSPClock.mValue); + if (diff > outlength) + { + diff = outlength; + } + + if (diff) + { + FMOD_memset(mBuffer, 0, diff * sizeof(float) * mSound->mChannels); + outlength -= diff; + readoffset += diff; + } + } + + while (outlength > 0) + { + unsigned int rlength; + FMOD_SINT64P speed; + FMOD_UINT64P pos; + FMOD_RESAMPLER_END endflag = FMOD_RESAMPLER_END_MIXBUFFER; + + /* + Work out what is going to end first. The source data or the output? + */ + pos = mPosition; + pos.mHi -= soundoffset; /* Take it out of 'sentence space' into 'sample space'. */ + speed = origspeed; + if (mDirection == DSPWAVETABLE_SPEEDDIR_BACKWARDS) + { + speed.mValue = -speed.mValue; + } + + rlength = outlength; /* Start it off defaulting to the amount requested */ + if (origspeed.mValue > 0x100) + { + FMOD_UINT64P mixesleft, fracleft; + bool eos = false; + + if (mDirection == DSPWAVETABLE_SPEEDDIR_BACKWARDS) + { + if (mChannel->mMode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI) && mChannel->mLoopCount && pos.mHi >= mChannel->mLoopStart) + { + mixesleft.mHi = pos.mHi - mChannel->mLoopStart; + } + else + { + mixesleft.mHi = pos.mHi; + } + mixesleft.mLo = 0; + + if (mixesleft.mHi > soundlength) + { + mixesleft.mValue = pos.mValue; + eos = true; + } + } + else + { + if (mChannel->mMode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI) && mChannel->mLoopCount) + { + mixesleft.mHi = mChannel->mLoopStart + mChannel->mLoopLength; + } + else + { + mixesleft.mHi = mSound->mLength; + } + mixesleft.mLo = 0; + + if (mixesleft.mValue > pos.mValue) + { + mixesleft.mValue -= pos.mValue; + } + else + { + mixesleft.mValue = 0; + } + + if (pos.mHi + mixesleft.mHi > soundlength) /* Sentence subsound length clamp (or just safety in case somehow one sound mixleft > length */ + { + mixesleft.mHi = soundlength - pos.mHi; + eos = true; + } + } + + fracleft.mValue = mixesleft.mValue % origspeed.mValue; + mixesleft.mValue /= origspeed.mValue; + if (fracleft.mValue) /* round the count up (this could be done better) */ + { + mixesleft.mValue++; + } + + if (mixesleft.mLo <= rlength) + { + endflag = FMOD_RESAMPLER_END_SOUND; + rlength = mixesleft.mLo; + +#ifdef FMOD_SUPPORT_SENTENCING + if (eos && mSound->mSubSoundList) + { + endflag = FMOD_RESAMPLER_END_SUBSOUND; + } +#endif + } + } + + /* + Resample the src buffer into the destination. + */ + if (origspeed.mHi == 1 && speed.mLo == 0) + { + FMOD_Resampler_NoInterp(mBuffer + (readoffset * mSound->mChannels), rlength, readbuffer, mSound->mFormat, &pos, &speed, mSound->mChannels); + } + else + { + switch (mSystem->mResampleMethod) + { + case FMOD_DSP_RESAMPLER_NOINTERP: + { + FMOD_Resampler_NoInterp(mBuffer + (readoffset * mSound->mChannels), rlength, readbuffer, mSound->mFormat, &pos, &speed, mSound->mChannels); + break; + } + case FMOD_DSP_RESAMPLER_LINEAR: + { + FMOD_Resampler_Linear(mBuffer + (readoffset * mSound->mChannels), rlength, readbuffer, mSound->mFormat, &pos, &speed, mSound->mChannels); + break; + } +#ifdef FMOD_SUPPORT_RESAMPLER_CUBIC + case FMOD_DSP_RESAMPLER_CUBIC: + { + FMOD_Resampler_Cubic(mBuffer + (readoffset * mSound->mChannels), rlength, readbuffer, mSound->mFormat, &pos, &speed, mSound->mChannels); + break; + } +#endif +#ifdef FMOD_SUPPORT_RESAMPLER_SPLINE + case FMOD_DSP_RESAMPLER_SPLINE: + { + FMOD_Resampler_Spline(mBuffer + (readoffset * mSound->mChannels), rlength, readbuffer, mSound->mFormat, &pos, &speed, mSound->mChannels); + break; + } +#endif + default: + { + FMOD_Resampler_Linear(mBuffer + (readoffset * mSound->mChannels), rlength, readbuffer, mSound->mFormat, &pos, &speed, mSound->mChannels); + break; + } + } + } + + pos.mHi += soundoffset; /* Put it back into 'sentence space' */ + mPosition = pos; + + outlength -= rlength; + readoffset += rlength; + + if (endflag == FMOD_RESAMPLER_END_SOUND) /* If a sentence, this will skip to FMOD_RESAMPLER_END_SUBSOUND instead. */ + { + if (mChannel->mMode & FMOD_LOOP_BIDI && mChannel->mLoopCount) + { + if ((int)mPosition.mHi < 0) + { + mPosition.mHi = 0; + } + + if (mDirection == DSPWAVETABLE_SPEEDDIR_FORWARDS) + { + mDirection = DSPWAVETABLE_SPEEDDIR_BACKWARDS; + } + else + { + mDirection = DSPWAVETABLE_SPEEDDIR_FORWARDS; + } + } + else if (mChannel->mMode & FMOD_LOOP_NORMAL && mChannel->mLoopCount) + { + if (mDirection == DSPWAVETABLE_SPEEDDIR_BACKWARDS) + { + mPosition.mHi += mChannel->mLoopLength; + + /* + Safety check. Sometimes the speed is so high it might go past double the length of the loop. + */ + while (mPosition.mHi < mChannel->mLoopStart) + { + mPosition.mHi += mChannel->mLoopLength; + } + + if (mChannel->mLoopCount > 0) + { + mChannel->mLoopCount --; + } + } + else + { + if (mPosition.mHi >= mChannel->mLoopLength) + { + mPosition.mHi -= mChannel->mLoopLength; + } + else + { + mPosition.mHi = 0; + } + + /* + Safety check. Sometimes the speed is so high it might go past double the length of the loop. + */ + while (mPosition.mHi >= mChannel->mLoopStart + mChannel->mLoopLength) + { + mPosition.mHi -= mChannel->mLoopLength; + } + + if (mChannel->mLoopCount > 0) + { + mChannel->mLoopCount --; + } + } + } + else + { + mPosition.mHi = samplesoftware->mLength; + mPosition.mLo = 0; + + FMOD_memset(mBuffer + (readoffset * mSound->mChannels), 0, outlength * mSound->mChannels * sizeof(float)); + + mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + break; + } + } +#ifdef FMOD_SUPPORT_SENTENCING + else if (endflag == FMOD_RESAMPLER_END_SUBSOUND) + { + if (mDirection == DSPWAVETABLE_SPEEDDIR_FORWARDS) + { + mChannel->mSubSoundListCurrent++; + if (mChannel->mSubSoundListCurrent >= mSound->mSubSoundListNum) + { + if (!(mSound->mMode & FMOD_LOOP_NORMAL && mChannel->mLoopCount)) + { + mPosition.mHi = samplesoftware->mLength; + mPosition.mLo = 0; + + FMOD_memset(mBuffer + (readoffset * mSound->mChannels), 0, outlength * mSound->mChannels * sizeof(float)); + + mDSPFinishTick = tick; + mFlags |= FMOD_DSP_FLAG_FINISHED; + break; + } + else + { + mChannel->mSubSoundListCurrent = 0; + mPosition.mHi -= mChannel->mLoopLength; + } + } + + /* Re-select the right soundoffset. */ + { + int count; + + soundoffset = 0; + count = 0; + samplesoftware = SAFE_CAST(SampleSoftware, mSound->mSubSound[mSound->mSubSoundList[count].mIndex]); + + while (count < mChannel->mSubSoundListCurrent) + { + if (samplesoftware) + { + soundoffset += samplesoftware->mLength; + } + count++; + samplesoftware = SAFE_CAST(SampleSoftware, mSound->mSubSound[mSound->mSubSoundList[count].mIndex]); + } + } + } + else + { + mChannel->mSubSoundListCurrent--; + } + + samplesoftware = SAFE_CAST(SampleSoftware, mSound->mSubSound[mSound->mSubSoundList[mChannel->mSubSoundListCurrent].mIndex]); + readbuffer = samplesoftware->mBuffer; + soundlength = samplesoftware->mLength; + + if (mDirection == DSPWAVETABLE_SPEEDDIR_BACKWARDS) + { + soundoffset -= samplesoftware->mLength; + mPosition.mHi--; /* Position will be '0' of the previous sample, wind it back one more to make it length - 1. */ + } + } +#endif + } + + if (mSystem->mFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_OS_Time_GetNs(&endtime); + mCPUUsage = endtime - starttime; + +#if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) && !defined(PLATFORM_PS3) + calculatePeaks(mBuffer, *length, mSound->mChannels); +#endif + } + } + + *outbuffer = mBuffer; + *outchannels = mSound->mChannels; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::setPositionInternal(unsigned int position) +{ + if (!mSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (position >= mSound->mLength) + { + position = mSound->mLength; + } + + mNewPosition = position; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::setParameterInternal(int index, float value) +{ + return FMOD_ERR_INVALID_PARAM; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::getParameterInternal(int index, float *value, char *valuestr) +{ + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::setFrequency(float frequency) +{ + if (frequency < 0) + { + mDirection = DSPWAVETABLE_SPEEDDIR_BACKWARDS; + frequency = -frequency; + } + else + { + SampleSoftware *samplesoftware = (SampleSoftware *)mSound; + + if (samplesoftware && !(mChannel->mMode & FMOD_LOOP_BIDI)) + { + mDirection = DSPWAVETABLE_SPEEDDIR_FORWARDS; + } + } + + mFrequency = frequency; + +#ifdef PLATFORM_PSP + /* + Avoid casting to 64 bit, it is software emulated + and very slow on PSP + */ + float value = mFrequency / mTargetFrequency; + + mSpeed.mHi = (signed int)value; + mSpeed.mLo = (unsigned int)((value - (signed int)value) * 4294967296.0f); +#else + mSpeed.mValue = (FMOD_SINT64)(mFrequency / mTargetFrequency * 4294967296.0f); +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::getFrequency(float *frequency) +{ + if (!frequency) + { + return FMOD_ERR_INVALID_PARAM; + } + + *frequency = mFrequency; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::setFinished(bool finished, bool force) +{ + if (!finished) + { + mDSPFinishTick = 0xFFFFFFFF; + mFlags &= ~FMOD_DSP_FLAG_FINISHED; + return FMOD_OK; + } + + if (force) + { + #ifndef PLATFORM_PS3_SPU + FMOD_OS_CriticalSection_Enter(mSystem->mDSPCrit); /* because it came from virtual voice swap most likely and needs to block/sync. */ + #endif + + mDSPFinishTick = 0; + + #ifndef PLATFORM_PS3_SPU + FMOD_OS_CriticalSection_Leave(mSystem->mDSPCrit); + #endif + } + + if (mDSPSoundCard) + { + mDSPFinishTick = mDSPSoundCard->mDSPTick + 1; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWaveTable::getFinished(bool *finished) +{ + if (!mDSPSoundCard) + { + *finished = true; + return FMOD_OK; + } + + if (mDSPFinishTick >= mDSPSoundCard->mDSPTick && !(mFlags &FMOD_DSP_FLAG_FINISHED)) + { + *finished = false; /* hasn't really finished yet, still mixing. */ + } + else + { + *finished = true; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWaveTable::setPositionCallback(FMOD_DSP_STATE *dsp, unsigned int position) +{ + DSPWaveTable *wavetable = (DSPWaveTable *)dsp; + + return wavetable->setPositionInternal(position); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWaveTable::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPWaveTable *wavetable = (DSPWaveTable *)dsp; + + return wavetable->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWaveTable::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPWaveTable *wavetable = (DSPWaveTable *)dsp; + + return wavetable->getParameterInternal(index, value, valuestr); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWaveTable::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPWaveTable *wavetable = (DSPWaveTable *)dsp; + + return wavetable->resetInternal(); +} + +} + +#endif diff --git a/src/fmod_dsp_wavetable.h b/src/fmod_dsp_wavetable.h new file mode 100755 index 0000000..ec01009 --- /dev/null +++ b/src/fmod_dsp_wavetable.h @@ -0,0 +1,63 @@ +#ifndef _FMOD_DSP_WAVETABLE_H +#define _FMOD_DSP_WAVETABLE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_dspi.h" +#include "fmod_dsp_resampler.h" + +namespace FMOD +{ + class SoundI; + + typedef enum + { + DSPWAVETABLE_SPEEDDIR_FORWARDS, + DSPWAVETABLE_SPEEDDIR_BACKWARDS, + } DSPWAVETABLE_SPEEDDIR; + + class DSPWaveTable : public DSPI + { + public: + + FMOD_UINT64P mPosition; + unsigned int mNewPosition; + FMOD_SINT64P mSpeed; + DSPWAVETABLE_SPEEDDIR mDirection; + float mFrequency; + ChannelReal *mChannel; + SoundI *mSound; + FMOD_UINT64P mDSPClockStart; + FMOD_UINT64P mDSPClockEnd; + FMOD_UINT64P mDSPClockPause; + unsigned int mDSPFinishTick; + DSPI *mDSPSoundCard; + + public: + + FMOD_RESULT alloc(FMOD_DSP_DESCRIPTION_EX *description); + FMOD_RESULT addInput(DSPI *target); + FMOD_RESULT read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + FMOD_RESULT setPositionInternal(unsigned int position); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + FMOD_RESULT resetInternal() { mNewPosition = 0xFFFFFFFF; return FMOD_OK; } + + FMOD_RESULT setFrequency(float frequency); + FMOD_RESULT getFrequency(float *frequency); + FMOD_RESULT setFinished(bool finished, bool force = false); + FMOD_RESULT getFinished(bool *finished); + + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_DSP_STATE *dsp, unsigned int position); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + + }; +} + +#endif + +#endif diff --git a/src/fmod_dspi.cpp b/src/fmod_dspi.cpp new file mode 100755 index 0000000..3ee1860 --- /dev/null +++ b/src/fmod_dspi.cpp @@ -0,0 +1,4794 @@ +#include "fmod_settings.h" + +#include "fmod_dspi.h" +#include "fmod_dsp_filter.h" +#include "fmod_dsp_resampler.h" +#include "fmod_dsp_soundcard.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_soundi.h" +#include "fmod_string.h" +#include "fmod_3d.h" + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) +#include "../ps3/src/fmod_common_spu.h" +#include "fmod_dsp_sfxreverb.h" +#endif + +#ifdef PLATFORM_PS3_SPU + #include <cell/dma.h> + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" +#else + #include "fmod_systemi.h" +#endif + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::doesUnitExist(DSPI *target, bool protect) +{ +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + int count; + int numinputs; + + if (this == target) + { + return FMOD_OK; + } + + result = getNumInputs(&numinputs, protect); + if (result != FMOD_OK) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count=0; count < numinputs; count++) + { + DSPConnectionI *connection; + + result = getInput(count, 0, &connection, protect); + if (result != FMOD_OK) + { + return result; + } + + result = connection->mInputUnit->doesUnitExist(target, protect); + if (result == FMOD_OK) + { + return result; + } + } +#endif + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::alloc(FMOD_DSP_DESCRIPTION_EX *description) +{ +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + int inputchannels,rate; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + if (description->channels < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mSystem->getSoftwareFormat(&rate, 0, 0, &inputchannels, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + /* + Do various error checking based on the type of unit being created. + */ + switch (description->mCategory) + { + case FMOD_DSP_CATEGORY_FILTER: + { + if (description->channels > inputchannels) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + break; + } + case FMOD_DSP_CATEGORY_DSPCODECMPEG: + case FMOD_DSP_CATEGORY_DSPCODECRAW: + case FMOD_DSP_CATEGORY_DSPCODECXMA: + case FMOD_DSP_CATEGORY_DSPCODECCELT: + case FMOD_DSP_CATEGORY_DSPCODECADPCM: + { + if (!description->channels) + { + return FMOD_ERR_INVALID_PARAM; + } + break; + } + case FMOD_DSP_CATEGORY_RESAMPLER: + { + if (description->channels) + { + return FMOD_ERR_INVALID_PARAM; + } + break; + } + case FMOD_DSP_CATEGORY_WAVETABLE: + { + break; + } + case FMOD_DSP_CATEGORY_SOUNDCARD: + { + break; + } + default: + { + return FMOD_ERR_INVALID_PARAM; + } + } + + FMOD_memcpy(&mDescription, description, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + mDSPTick = 0; + + mFlags &= ~FMOD_DSP_FLAG_ACTIVE; + mFlags &= ~FMOD_DSP_FLAG_USEDADDDSP; + speakermask = 0xFFFF; + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + mFlagsMramAddress = (unsigned int)&mFlags; + #endif + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getInput(int index, DSPI **inputdsp, DSPConnectionI **input, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + LocalCriticalSection crit(mSystem->mDSPConnectionCrit); + + if (protect) + { + mSystem->flushDSPConnectionRequests(); + + crit.enter(); + } + + int count; + LinkedListNode *current; + DSPConnectionI *connection; + + if (index >= mNumInputs) + { + return FMOD_ERR_INVALID_PARAM; + } + + current = mInputHead.getNext(); + if (current == &mInputHead) + { + return FMOD_ERR_INTERNAL; + } + + count = 0; + while (count < index) + { + current = current->getNext(); + count++; + } + connection = (DSPConnectionI *)current->getData(); + + if (input) + { + *input = connection; + } + if (inputdsp) + { + *inputdsp = connection->mInputUnit; + } + + if (protect) + { + crit.leave(); + } +#endif + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getOutput(int index, DSPI **outputdsp, DSPConnectionI **output, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + LocalCriticalSection crit(mSystem->mDSPConnectionCrit); + + if (protect) + { + mSystem->flushDSPConnectionRequests(); + + crit.enter(); + } + + int count; + LinkedListNode *current; + DSPConnectionI *connection; + + if (index >= mNumOutputs) + { + return FMOD_ERR_INVALID_PARAM; + } + + current = mOutputHead.getNext(); + if (current == &mOutputHead) + { + return FMOD_ERR_INTERNAL; + } + + count = 0; + while (count < index) + { + current = current->getNext(); + count++; + } + connection = (DSPConnectionI *)current->getData(); + + if (output) + { + *output = connection; + } + if (outputdsp) + { + *outputdsp = connection->mOutputUnit; + } + + if (protect) + { + crit.leave(); + } +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +#ifndef FMOD_SUPPORT_MIXER_NONRECURSIVE + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::read(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + return FMOD_ERR_INTERNAL; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::read(void *outbuffer, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ + return read(0, 0, length, speakermode, speakermodechannels, tick); +} + + +#ifdef FMOD_SUPPORT_MIXER_NONRECURSIVE + +#ifdef PLATFORM_WINDOWS_PS3MODE + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::stepBack(LinkedListNode *¤t, DSPConnectionI *&connection, DSPI *&t, LinkedListNode *&next, bool dspionly) +{ + current = t->mPrevious; // Reading from LS, making current point to mainRAM address. + connection = (DSPConnectionI *)current->getData(); // Point to LS + t = connection->mOutputUnit; // Point to LS + next = connection->mInputNode.getNext(); // Next points to MainRAM + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::stepForwards(LinkedListNode *¤t, DSPConnectionI *&connection, DSPConnectionI *&prevconnection, DSPI *&t, LinkedListNode *&next) +{ + connection = (DSPConnectionI *)current->getData(); // Point to LS + t = connection->mInputUnit; // Point to LS + next = connection->mInputNode.getNext(); // next points to MainRAM + t->mPrevious = current; // LS variable pointing to MainRAM address. + prevconnection = connection; + current = t->mInputHead.getNext(); // Point to MainRAM + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::updateHistory(DSPI *t, float *buffer, int length, int channels) +{ + float *srcptr, *destptr; + int len; + + destptr = t->mHistoryBuffer; + srcptr = (float *)buffer; + + len = length; + while (len) + { + int size = len; + if (t->mHistoryPosition + size > FMOD_HISTORYBUFFERLEN) + { + size = FMOD_HISTORYBUFFERLEN - t->mHistoryPosition; + } + + FMOD_memcpy(destptr + (t->mHistoryPosition * channels), srcptr, size * channels * sizeof(float)); + + len -= size; + srcptr += (size * channels); + + t->mHistoryPosition += size; + if (t->mHistoryPosition >= FMOD_HISTORYBUFFERLEN) + { + t->mHistoryPosition = 0; + } + } + + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::run(float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result = FMOD_OK; + DSPI *t = this; + LinkedListNode *current = mInputHead.getNext(); + DSPConnectionI *connection = 0, *prevconnection = 0; + +#ifdef PLATFORM_PS3_SPU + unsigned int starttime, endtime; + + FMOD_memcpy((void *)gDMAMemoryDSP, (void *)gDMAMemorySoundCard, t->mDescription.mSize); + t = (DSPI *)gDMAMemoryDSP; + + *outbuffer = (float *)gTargetMixBuffer; +#else + if (mBuffer) + { + *outbuffer = mBuffer; + } +#endif + + t->mFlags |= FMOD_DSP_FLAG_FIRSTMIX; + + while (current != mInputHeadAddress) + { + /* + ============================================================================================== + Stepping forwards through DSP tree + ============================================================================================== + */ +#ifdef PLATFORM_PS3_SPU + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_start(&starttime); + } +#endif + + if (current != t->mInputHeadAddress) + { + bool goback = false; + LinkedListNode *next; + + if (t->mDSPTick == tick) + { + goback = true; + } + else if (t->mDescription.mCategory == FMOD_DSP_CATEGORY_RESAMPLER || + t->mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECMPEG || + t->mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECRAW || + t->mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { +#if defined(PLATFORM_PS3_SPU) + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_LAZYDMA1]); + + cellDmaLargeGet((void *)(gDMAMemoryDSP + gDSPISize), t->mMramAddress + gDSPISize, t->mDescription.mSize - gDSPISize, TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + FMOD_PS3_SPU_DSPPointersToLS(t, false); + + t = (DSPI *)gDMAMemoryDSP; + + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_LAZYDMA1]); +#endif + DSPResampler *resampler = SAFE_CAST(DSPResampler, t); + if (!resampler->mFill) + { + goback = true; + } + } + + if (goback) + { + prevconnection = 0; + current = t->mInputHeadAddress; + t->mLastChannels = *outchannels; + continue; + } + +#ifdef PLATFORM_PS3_SPU + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_end(starttime, &t->mCPUUsageTemp); + } +#endif + + t->mBufferChannels = speakermodechannels; + + stepForwards(current, connection, prevconnection, t, next); + + t->mFlags |= FMOD_DSP_FLAG_IDLE; + + if (mDSPTick != tick) + { + /* + Reset CPU usage of dsp node if it is the first time + it has been stepped into. + */ + t->mCPUUsage = t->mCPUUsageTemp; + t->mCPUUsageTemp = 0; + } + + connection = 0; + + if (!(t->mFlags & FMOD_DSP_FLAG_ACTIVE) || t->mFlags & (FMOD_DSP_FLAG_FINISHED | FMOD_DSP_FLAG_QUEUEDFORDISCONNECT)) + { + t->mDSPTick = tick; + +#ifdef PLATFORM_PS3_SPU + cellDmaPutUint32(t->mFlags, t->mFlagsMramAddress, TAG1, TID, RID); +#endif + + stepBack(current, connection, t, next, true); + + if (t->mLastChannels > 0) + { + *outchannels = t->mLastChannels; + } + else + { + *outchannels = speakermodechannels; + } + + /* + Clear the buffer if first input was inactive + */ + if (t->mBuffer && (t->mFlags & FMOD_DSP_FLAG_FIRSTMIX)) + { + t->mFlags &= ~FMOD_DSP_FLAG_FIRSTMIX; +#ifdef PLATFORM_PS3_SPU + *outbuffer = (float *)gTargetMixBuffer; + + FMOD_memset(*outbuffer, 0, *length * *outchannels * sizeof(float)); + + cellDmaPut((void *)*outbuffer, (uint64_t)t->mBuffer, *length * *outchannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); +#else + FMOD_memset(t->mBuffer, 0, *length * *outchannels * sizeof(float)); + *outbuffer = t->mBuffer; +#endif + } + current = next; + continue; + } + + t->mFlags |= FMOD_DSP_FLAG_FIRSTMIX; + +#ifdef PLATFORM_PS3_SPU + cellDmaPutUint32(t->mFlags, t->mFlagsMramAddress, TAG1, TID, RID); +#endif + } + else + { + /* + ============================================================================================== + Stepping backwards through DSP tree + ============================================================================================== + */ + LinkedListNode *next; + bool wholedspdma = false; + +#ifdef PLATFORM_PS3_SPU + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_start(&starttime); + } +#endif + if (t->mDSPTick == tick) + { +#ifdef PLATFORM_PS3_SPU + if (t->mBuffer) + { + cellDmaGet((void *)gTargetMixBuffer, (uint64_t)t->mBuffer, MIXBUFFERSIZE, TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + } + + *outbuffer = (float *)gTargetMixBuffer; +#else + if (t->mBuffer) + { + *outbuffer = t->mBuffer; + } +#endif + *outchannels = t->mBufferChannels; + t->mFlags &= ~FMOD_DSP_FLAG_IDLE; + } + else + { + if (t->mDescription.mCategory == FMOD_DSP_CATEGORY_RESAMPLER || + t->mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECMPEG || + t->mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECRAW || + t->mDescription.mCategory == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { +#if defined(PLATFORM_PS3_SPU) + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_LAZYDMA2]); + + /* + Need to DMA in the whole unit + */ + cellDmaLargeGet((void *)(gDMAMemoryDSP + gDSPISize), t->mMramAddress + gDSPISize, t->mDescription.mSize - gDSPISize, TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + + FMOD_PS3_SPU_DSPPointersToLS(t, false); + + t = (DSPI *)gDMAMemoryDSP; + + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_LAZYDMA2]); + + wholedspdma = true; +#endif + DSPResampler *resampler = SAFE_CAST(DSPResampler, t); + + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_RESAMPLE]); + + result = resampler->update(*length, outchannels, (void **)outbuffer, tick); + + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_RESAMPLE]); + + t->mBufferChannels = *outchannels; + + if (result == FMOD_ERR_FILE_EOF) + { +#if defined(PLATFORM_PS3_SPU) + FMOD_PS3_SPU_DSPPointersToMRAM(t, false); + cellDmaLargePut((void *)gDMAMemoryDSP, t->mMramAddress, t->mDescription.mSize, TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); +#endif + current = t->mInputHead.getNext(); /* Make it start again on the subtree */ + +#ifdef PLATFORM_PS3_SPU + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_end(starttime, &t->mCPUUsageTemp); + } +#endif + continue; + } + else + { + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + #if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) + if (t->mDescription.mCategory != FMOD_DSP_CATEGORY_SOUNDCARD) + { + calculatePeaks(*outbuffer, *length, *outchannels, t); + } + #endif + } + } + +#ifdef PLATFORM_PS3_SPU + if (t->mBuffer) + { + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_DMA3]); + + cellDmaPut((void *)*outbuffer, (uint64_t)t->mBuffer, *length * *outchannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_DMA3]); + } +#endif + } + + /* + Call the plugin callback to actually process or generate the data for this unit. + + mSystem->mDSPTempBuff actually just points to gSourceMixBuffer + */ + if (t->mDescription.read && !(t->mFlags & FMOD_DSP_FLAG_BYPASS) && t->mDescription.mCategory != FMOD_DSP_CATEGORY_DSPCODECMPEG && t->mDescription.mCategory != FMOD_DSP_CATEGORY_DSPCODECADPCM && t->mDescription.mCategory != FMOD_DSP_CATEGORY_DSPCODECRAW) + { + if (t->mDescription.channels) + { + *outchannels = t->mDescription.channels; + FMOD_memset(mSystem->mDSPTempBuff, 0, *length * t->mDescription.channels * sizeof(float)); + } + else if (!t->mNumInputs) + { + *outbuffer = mSystem->mDSPTempBuff; /* mSystem->mDSPTempBuff actually just points to gSourceMixBuffer */ + FMOD_memset(*outbuffer, 0, *length * *outchannels * sizeof(float)); + } + +#if defined(PLATFORM_PS3_SPU) + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_LAZYDMA3]); + cellDmaLargeGet((void *)(gDMAMemoryDSP + gDSPISize), t->mMramAddress + gDSPISize, t->mDescription.mSize - gDSPISize, TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_LAZYDMA3]); + + t = (DSPI *)gDMAMemoryDSP; + FMOD_PS3_SPU_DSPPointersToLS(t, false); + wholedspdma = true; +#endif + t->instance = (FMOD_DSP *)t; + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_DSPREAD]); + t->mDescription.read((FMOD_DSP_STATE *)t, (float *)*outbuffer, (float *)mSystem->mDSPTempBuff, *length, *outchannels, *outchannels); + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_DSPREAD]); + + *outbuffer = mSystem->mDSPTempBuff; + t->mFlags &= ~FMOD_DSP_FLAG_IDLE; + } + + t->mDSPTick = tick; + + t->mLastChannels = *outchannels; + +#ifdef PLATFORM_PS3_SPU + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_end(starttime, &t->mCPUUsageTemp); + } +#endif + } + + if (t->mFlags & FMOD_DSP_FLAG_IDLE) + { + t->mDSPTick = tick; + + stepBack(current, connection, t, next, !wholedspdma); + + if (t->mLastChannels) + { + *outchannels = t->mLastChannels; + } + else + { + *outchannels = speakermodechannels; + } + + if (t->mFlags & FMOD_DSP_FLAG_FIRSTMIX) + { +#ifdef PLATFORM_PS3_SPU + FMOD_memset((void *)gSourceMixBuffer, 0, *length * *outchannels * sizeof(float)); + + cellDmaPut((void *)gSourceMixBuffer, (uint64_t)t->mBuffer, *length * *outchannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + *outbuffer = (float *)gSourceMixBuffer; +#else + FMOD_memset((void *)t->mBuffer, 0, *length * *outchannels * sizeof(float)); +#endif + t->mFlags &= ~FMOD_DSP_FLAG_FIRSTMIX; + } + else + { + /* + Re-get the source buffer from this level, otherwise we're just returning the idle buffer. + */ +#ifdef PLATFORM_PS3_SPU + cellDmaGet((void *)gSourceMixBuffer, (uint64_t)t->mBuffer, *length * *outchannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + *outbuffer = (float *)gSourceMixBuffer; +#else + *outbuffer = t->mBuffer; +#endif + } + } + else + { + t->mDSPTick = tick; + + stepBack(current, connection, t, next, !wholedspdma); + + t->mFlags &= ~FMOD_DSP_FLAG_IDLE; + +#ifdef PLATFORM_PS3_SPU + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_start(&starttime); + } + /* + Bring in the connection levels + */ + cellDmaGet((void *)gDMAMemoryConnectionLevels, (uint64_t)connection->mMramAddressLevels, connection->mMaxOutputLevels * connection->mMaxInputLevels * 3 * sizeof(DSP_LEVEL_TYPE), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + FMOD_PS3_SPU_ConnectionLevelsToLS(connection); +#endif + + /* + Now that we know how many channels are coming in from the input, does the pan need updating? + */ + bool passthru = false; + if (t->mNumInputs <= 1 && connection->mVolume == 1.0f && + ((!connection->mSetLevelsUsed && t->mDescription.mCategory != FMOD_DSP_CATEGORY_SOUNDCARD) || connection->checkUnity(*outchannels, speakermodechannels) == FMOD_OK)) + { + passthru = true; + } + + if (t->mDescription.read && t->mDescription.channels && t->mDescription.channels != *outchannels) + { + passthru = false; + } + + /* + Now either do a 'pass through' if the data is 1:1 and unscaled by volume, or do a mix. + */ + if (passthru) + { + if (t->mNumOutputs > 1 && t->mDescription.mCategory != FMOD_DSP_CATEGORY_RESAMPLER) /* A multiinput resampler doesnt need its mbuffer stomped on. */ + { +#ifdef PLATFORM_PS3_SPU + if (*outbuffer) + { + if (*outbuffer != gTargetMixBuffer) + { + FMOD_memcpy((void *)gTargetMixBuffer, *outbuffer, *length * *outchannels * sizeof(float)); + } + } + else + { + FMOD_memset((void *)gTargetMixBuffer, 0, *length * *outchannels * sizeof(float)); + } + *outbuffer = (float *)gTargetMixBuffer; + + cellDmaPut((void *)*outbuffer, (uint64_t)t->mBuffer, *length * *outchannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); +#else + if (*outbuffer) + { + FMOD_memcpy(t->mBuffer, (float *)*outbuffer, *length * *outchannels * sizeof(float)); + } + else + { + FMOD_memset(t->mBuffer, 0, *length * *outchannels * sizeof(float)); + } + + *outbuffer = t->mBuffer; /* Return the already processed buffer */ +#endif + t->mBufferChannels = *outchannels; + } + if (connection->mRampCount) + { + int count, count2; + + for (count = 0; count < connection->mMaxOutputLevels; count++) + { + for (count2 = 0; count2 < connection->mMaxInputLevels; count2++) + { + connection->mLevelCurrent[count][count2] = connection->mLevel[count][count2]; + connection->mLevelDelta [count][count2] = 0; + } + } + connection->mRampCount = 0; + +#ifdef PLATFORM_PS3_SPU + cellDmaPut((void *)gDMAMemoryConnectionLevels, (uint64_t)connection->mMramAddressLevels, connection->mMaxOutputLevels * connection->mMaxInputLevels * 3 * sizeof(DSP_LEVEL_TYPE), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); +#endif + } + } + else + { +#ifdef PLATFORM_PS3_SPU + float *mixbuffer = (float *)gTargetMixBuffer; + + if (*outbuffer == gTargetMixBuffer) + { + FMOD_memcpy((void *)gSourceMixBuffer, (void *)gTargetMixBuffer, *length * *outchannels * sizeof(float)); + + *outbuffer = (float *)gSourceMixBuffer; + } + if (t->mBuffer) + { + cellDmaGet((void *)gTargetMixBuffer, (uint64_t)t->mBuffer, *length * speakermodechannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + } +#else + float *mixbuffer = t->mBuffer; + + if (!mixbuffer) + { + mixbuffer = mSystem->mDSPTempBuff; + } +#endif + + /* + If a new connection happened and no pan has been set, set it here. + */ + if (!connection->mSetLevelsUsed) + { + if (speakermodechannels == *outchannels && connection->mVolume == 1.0f) + { + connection->setUnity(); + } + else + { + connection->setPan(0.0f, speakermodechannels, *outchannels, speakermode); + } + connection->mSetLevelsUsed = true; + } + + /* + If it is the first input, give it a blank buffer to mix to. + */ + if (t->mFlags & FMOD_DSP_FLAG_FIRSTMIX) + { + FMOD_memset(mixbuffer, 0, *length * speakermodechannels * sizeof(float)); + t->mFlags &= ~FMOD_DSP_FLAG_FIRSTMIX; + } + + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_MIX]); + // out // in + connection->mix(mixbuffer, (float *)*outbuffer, speakermodechannels, *outchannels, *length); + + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_MIX]); + + *outbuffer = mixbuffer; + *outchannels = speakermodechannels; +#ifdef PLATFORM_PS3_SPU + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_DMA4]); + unsigned int waitmask = 0; + if (t->mBuffer) + { + cellDmaPut((void *)*outbuffer, (uint64_t)t->mBuffer, *length * *outchannels * sizeof(float), TAG1, TID, RID); + cellDmaWaitTagStatusAll(MASK1); + waitmask |= MASK1; + } + cellDmaPut((void *)gDMAMemoryConnectionLevels, (uint64_t)connection->mMramAddressLevels, connection->mMaxOutputLevels * connection->mMaxInputLevels * 3 * sizeof(DSP_LEVEL_TYPE), TAG2, TID, RID); + waitmask |= MASK2; + cellDmaWaitTagStatusAll(waitmask); + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_DMA4]); + #endif + } + + /* + DMA connection and DSP unit back to MRAM + */ +#ifdef PLATFORM_PS3_SPU + unsigned int waitmask = 0; + FMOD_SPU_PROFILE_START(gProfileResults[FMOD_SPU_PROFILE_DMAUNITSBACK]); + + FMOD_PS3_SPU_ConnectionLevelsToMRAM(connection); + + if (connection->mMramAddress) + { + cellDmaPut((void *)gDMAMemoryConnection, (uint64_t)connection->mMramAddress, gDSPConnectionSize, TAG1, TID, RID); + waitmask |= MASK1; + } + FMOD_PS3_SPU_DSPPointersToMRAM(t, true); + { + cellDmaLargePut((void *)gDMAMemoryDSP, (uint64_t)t->mMramAddress, gDSPISize, TAG2, TID, RID); + waitmask |= MASK2; + cellDmaWaitTagStatusAll(waitmask); + } + FMOD_PS3_SPU_DSPPointersToLS(t, true); + + FMOD_SPU_PROFILE_STOP(gProfileResults[FMOD_SPU_PROFILE_DMAUNITSBACK]); + + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + FMOD_PS3_SPU_DSP_timer_end(starttime, &t->mCPUUsageTemp); + + #if defined(FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS) + if (t->mDescription.mCategory != FMOD_DSP_CATEGORY_SOUNDCARD) + { + calculatePeaks(*outbuffer, *length, *outchannels, t); + } + #endif + } +#endif + } + + /* + If the history buffer option is set, buffer off this data into a separate ring buffer. + */ + if (t->mHistoryBuffer) + { + updateHistory(t, *outbuffer, *length, *outchannels); + } + + current = next; + } + }; + + /* + THIS IS FOR DSP SOUNDCARD UNIT, since its not part of the above loop + + If the history buffer option is set, buffer off this data into a separate ring buffer. + */ + if (mHistoryBuffer) + { + updateHistory(this, *outbuffer, *length, *outchannels); + } + + if (gSystemFlags & FMOD_INIT_ENABLE_PROFILE) + { + calculatePeaks(*outbuffer, *length, *outchannels); + + mCPUUsage = mCPUUsageTemp; + mCPUUsageTemp = 0; + + FMOD_PS3_SPU_DSPPointersToMRAM(this, true); + + cellDmaPut((void *)gDMAMemorySoundCard, (uint64_t)gDSPSoundCardMram, gDSPSoundCardSize, TAG1, TID, RID); + cellDmaWaitTagStatusAll(1<<TAG1); + } + + mDSPTick = tick; + +#ifdef PLATFORM_PS3_SPU + cellDmaPutUint32(mDSPTick, gDSPSoundCardDSPTickMram, TAG1, TID, RID); +#endif + + return result; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::validate(DSP *dsp, DSPI **dspi) +{ + if (!dspi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!dsp) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *dspi = (DSPI *)dsp; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::setPosition(unsigned int position, bool processinputs) +{ + FMOD_RESULT result; + + if (processinputs) + { + int count, numinputs = 0; + + result = getNumInputs(&numinputs); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < numinputs; count++) + { + DSPI *dsp = 0; + + getInput(count, &dsp); + + dsp->setPosition(position, true); + } + } + + if (!mDescription.setposition) + { + return FMOD_OK; + } + + instance = (FMOD_DSP *)this; + + return mDescription.setposition((FMOD_DSP_STATE *)this, position); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::calculateSpeakerLevels(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright, FMOD_SPEAKERMODE speakermode, int channels, FMOD_SPEAKERMAPTYPE speakermap, float *outlevels, int *numinputlevels) +{ + *numinputlevels = 0; + + switch (speakermode) + { + case FMOD_SPEAKERMODE_RAW: + { + switch (channels) + { + case 1: + { + break; + } + case 2: + { + break; + } + case 4: + { + break; + } + case 6: + { + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_MONO: + { + switch (channels) + { + case 1: + { + float levels[1][1] = + { + { FMOD_SQRT(frontleft*frontleft + center*center*0.5f + backleft*backleft * 0.25f) }, + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[1][2] = + { + { + FMOD_SQRT(frontleft*frontleft + center*center*0.5f + backleft*backleft * 0.25f), FMOD_SQRT(frontright*frontright + center*center*0.5f + backleft*backleft * 0.25f), + }, + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[1][4] = + { + { frontleft, 0, backleft*0.5f, 0 }, // FL + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + float levels[1][6] = + { + { frontleft, frontright, center, lfe, backleft, backright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 6; + break; + } + case 8: + { + float levels[1][8] = + { + { frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 8; + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_STEREO: + case FMOD_SPEAKERMODE_STEREO_LINEAR: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + int count; + + FMOD_memset(outlevels, 0, 2 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = center * .707f; + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = center * .707f; + } + + *numinputlevels = channels; + break; + } + else if (speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO || channels > 8) + { + int count; + + FMOD_memset(outlevels, 0, 2 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + if (count & 1) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = frontright; + } + else + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = frontleft; + } + } + + *numinputlevels = channels; + break; + } + + switch (channels) + { + case 1: + { + float levels[2][1] = + { + { FMOD_SQRT(frontleft*frontleft + center*center*0.5f + backleft*backleft * 0.25f) }, + { FMOD_SQRT(frontright*frontright + center*center*0.5f + backright*backright * 0.25f) } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[2][2] = + { + { + FMOD_SQRT(frontleft*frontleft + center*center*0.5f + backleft*backleft * 0.25f), + 0 + }, + { + 0, + FMOD_SQRT(frontright*frontright + center*center*0.5f + backright*backright * 0.25f) + } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[2][4] = + { + { frontleft, 0, backleft*0.5f, 0 }, // FL + { 0, frontright, 0, backright*0.5f }, // FR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + float levels[2][6] = + { /* FL C FR BL BR LFE */ + { frontleft, center * 0.5f, 0, backleft*0.5f, 0, lfe * 0.5f }, // FL + { 0, center * 0.5f, frontright, 0, backright*0.5f, lfe * 0.5f }, // FR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + else + { + float levels[2][6] = + { /* FL FR C LFE BL BR */ + { frontleft, 0, center * 0.5f, lfe * 0.5f, backleft*0.5f, 0 }, // FL + { 0, frontright, center * 0.5f, lfe * 0.5f, 0, backright*0.5f }, // FR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + *numinputlevels = 6; + break; + } + case 8: + { + float levels[2][8] = + { /* FL FR C LFE BL BR SL SR */ + { frontleft, 0, center * 0.5f, lfe * 0.5f, backleft*0.5f, 0, sideleft * 0.75f, 0 }, // FL + { 0, frontright, center * 0.5f, lfe * 0.5f, 0, backright*0.5f, 0, sideright * 0.75f }, // FR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 8; + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_QUAD: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + int count; + + FMOD_memset(outlevels, 0, 4 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = center * .707f; + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = center * .707f; + } + + *numinputlevels = channels; + break; + } + else if (speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO || channels > 8) + { + int count; + + FMOD_memset(outlevels, 0, 4 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + if (count & 1) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = frontright; + } + else + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = frontleft; + } + } + + *numinputlevels = channels; + break; + } + + switch (channels) + { + case 1: + { + float levels[4][1] = + { + { frontleft + center }, /* FIX THIS - CLIPPING */ + { frontright + center }, /* FIX THIS - CLIPPING */ + { backleft }, + { backright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[4][2] = + { /* left right */ + { frontleft, 0 }, + { 0, frontright }, + { backleft, 0 }, + { 0, backright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[4][4] = + { + { frontleft, 0, 0, 0 }, // FL + { 0, frontright, 0, 0 }, // FR + { 0, 0, backleft*0.5f, 0 }, // RL + { 0, 0, 0, backright*0.5f } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + float levels[4][6] = + { /* FL C FR BL BR LFE */ + { frontleft, center * 0.5f, 0, 0, 0, lfe * 0.25f }, // FL + { 0, center * 0.5f, frontright, 0, 0, lfe * 0.25f }, // FR + { 0, 0, 0, backleft, 0, lfe * 0.25f }, // RL + { 0, 0, 0, 0, backright, lfe * 0.25f }, // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + else + { + float levels[4][6] = + { /* FL FR C LFE BL BR */ + { frontleft, 0, center * 0.5f, lfe * 0.25f, 0, 0 }, // FL + { 0, frontright, center * 0.5f, lfe * 0.25f, 0, 0 }, // FR + { 0, 0, 0, lfe * 0.25f, backleft, 0 }, // RL + { 0, 0, 0, lfe * 0.25f, 0, backright}, // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + *numinputlevels = 6; + break; + } + case 8: + { + float levels[4][8] = + { /* FL FR C LFE BL BR SL SR */ + { frontleft, 0, center * 0.5f, lfe * 0.25f, 0, 0, sideleft * 0.5f, 0 }, // FL + { 0, frontright, center * 0.5f, lfe * 0.25f, 0, 0, 0, sideright * 0.5f}, // FR + { 0, 0, 0, lfe * 0.25f, backleft, 0, sideleft * 0.5f, 0 }, // RL + { 0, 0, 0, lfe * 0.25f, 0, backright, 0, sideright * 0.5f}, // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 8; + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_SURROUND: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + int count; + + FMOD_memset(outlevels, 0, 5 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_CENTER) + count] = center; + } + + *numinputlevels = channels; + break; + } + else if (speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO || channels > 8) + { + int count; + + FMOD_memset(outlevels, 0, 5 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + if (count & 1) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = frontright; + } + else + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = frontleft; + } + } + + *numinputlevels = channels; + break; + } + + switch (channels) + { + case 1: + { + float levels[5][1] = + { + { frontleft }, + { frontright }, + { center }, + { backleft }, + { backright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[5][2] = + { /* left right */ + { frontleft, 0 }, + { 0, frontright }, + { center * 0.5f, center * 0.5f }, /* This is a mono channel with both left and right going into it, so keep the volume consistent by multiplying by 0.5f. */ + { backleft, 0 }, + { 0, backright }, + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[5][4] = + { // L R C S + { frontleft, 0, 0, 0 }, // FL + { 0, frontright, 0, 0 }, // FR + { center * 0.5f, center * 0.5f, 0, 0 }, // C + { 0, 0, backleft, 0 }, // RL + { 0, 0, 0, backright } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + float levels[5][6] = + { /* FL C FR BL BR LFE */ + { frontleft, 0, 0, 0, 0, 0 }, // FL + { 0, 0, frontright, 0, 0, 0 }, // FR + { 0, center, 0, 0, 0, 0 }, // C + { 0, 0, 0, backleft, 0, 0 }, // RL + { 0, 0, 0, 0, backright, 0 } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + else + { + float levels[5][6] = + { /* FL FR C LFE BL BR */ + { frontleft, 0, 0, 0, 0, 0 }, // FL + { 0, frontright, 0, 0, 0, 0 }, // FR + { 0, 0, center, 0, 0, 0 }, // C + { 0, 0, 0, 0, backleft, 0 }, // RL + { 0, 0, 0, 0, 0, backright } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + *numinputlevels = 6; + break; + } + case 8: + { + float levels[5][8] = + { /* FL FR C LFE BL BR SL SR */ + { frontleft, 0, 0, 0, 0, 0, sideleft * 0.5f, 0 }, // FL + { 0, frontright, 0, 0, 0, 0, 0, sideright * 0.5f}, // FR + { 0, 0, center, 0, 0, 0, 0, 0 }, // C + { 0, 0, 0, 0, backleft, 0, sideleft * 0.5f, 0 }, // RL + { 0, 0, 0, 0, 0, backright, 0, sideright * 0.5f}, // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 8; + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_5POINT1: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + int count; + + FMOD_memset(outlevels, 0, 6 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_CENTER) + count] = center; + } + + *numinputlevels = channels; + break; + } + else if (speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO || channels > 8) + { + int count; + + FMOD_memset(outlevels, 0, 6 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + if (count & 1) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = frontright; + } + else + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = frontleft; + } + } + + *numinputlevels = channels; + break; + } + + switch (channels) + { + case 1: + { + float levels[6][1] = + { + { frontleft }, + { frontright }, + { center }, + { lfe }, + { backleft }, + { backright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[6][2] = + { /* left right */ + { frontleft, 0 }, + { 0, frontright }, + { center * 0.5f, center * 0.5f }, /* This is a mono channel with both left and right going into it, so keep the volume consistent by multiplying by 0.5f. */ + { lfe * 0.5f, lfe * 0.5f }, /* This is a mono channel with both left and right going into it, so keep the volume consistent by multiplying by 0.5f. */ + { backleft, 0 }, + { 0, backright }, + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[6][4] = + { + { frontleft, 0, 0, 0 }, // FL + { 0, frontright, 0, 0 }, // FR + { center * 0.5f, center * 0.5f, 0, 0 }, // C + { lfe * 0.5f, lfe * 0.5f, 0, 0 }, // LFE + { 0, 0, backleft, 0 }, // RL + { 0, 0, 0, backright } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + float levels[6][6] = + { /* FL C FR BL BR LFE */ + { frontleft, 0, 0, 0, 0, 0 }, // FL + { 0, 0, frontright, 0, 0, 0 }, // FR + { 0, center, 0, 0, 0, 0 }, // C + { 0, 0, 0, 0, 0, lfe }, // LFE + { 0, 0, 0, backleft, 0, 0 }, // RL + { 0, 0, 0, 0, backright, 0 } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + else + { + float levels[6][6] = + { /* FL FR C LFE BL BR */ + { frontleft, 0, 0, 0, 0, 0 }, // FL + { 0, frontright, 0, 0, 0, 0 }, // FR + { 0, 0, center, 0, 0, 0 }, // C + { 0, 0, 0, lfe, 0, 0 }, // LFE + { 0, 0, 0, 0, backleft, 0 }, // RL + { 0, 0, 0, 0, 0, backright } // RR + }; + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + *numinputlevels = 6; + break; + } + case 8: + { + float levels[6][8] = + { /* FL FR C LFE BL BR SL SR */ + { frontleft, 0, 0, 0, 0, 0, sideleft * 0.5f, 0 }, // FL + { 0, frontright, 0, 0, 0, 0, 0, sideright * 0.5f}, // FR + { 0, 0, center, 0, 0, 0, 0, 0 }, // C + { 0, 0, 0, lfe, 0, 0, 0, 0 }, // LFE + { 0, 0, 0, 0, backleft, 0, sideleft * 0.5f, 0 }, // RL + { 0, 0, 0, 0, 0, backright, 0, sideright * 0.5f}, // RR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 8; + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_7POINT1: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + int count; + + FMOD_memset(outlevels, 0, 8 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_CENTER) + count] = center; + } + + *numinputlevels = channels; + break; + } + else if (speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO || channels > 8) + { + int count; + + FMOD_memset(outlevels, 0, 8 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + if (count & 1) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = frontright; + } + else + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = frontleft; + } + } + + *numinputlevels = channels; + break; + } + + switch (channels) + { + case 1: + { + float levels[8][1] = + { + { frontleft }, + { frontright }, + { center }, + { lfe }, + { backleft }, + { backright }, + { sideleft }, + { sideright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[8][2] = + { /* left right */ + { frontleft, 0 }, + { 0, frontright }, + { center * 0.5f, center * 0.5f }, /* This is a mono channel with both left and right going into it, so keep the volume consistent by multiplying by 0.5f. */ + { lfe * 0.5f, lfe * 0.5f }, /* This is a mono channel with both left and right going into it, so keep the volume consistent by multiplying by 0.5f. */ + { backleft, 0 }, + { 0, backright }, + { sideleft, 0 }, + { 0, sideright } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[8][4] = + { // FL FR RL RR + { frontleft, 0, 0, 0 }, // FL + { 0, frontright, 0, 0 }, // FR + { center * 0.5f, center * 0.5f, 0, 0 }, // C + { lfe * 0.5f, lfe * 0.5f, 0, 0 }, // LFE + { 0, 0, backleft*0.5f, 0 }, // RL + { 0, 0, 0, backright*0.5f }, // RR + { 0, 0, sideleft*0.5f, 0 }, // SL + { 0, 0, 0, sideright*0.5f }, // SR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + float levels[8][6] = + { /* FL C FR BL BR LFE */ + { frontleft, 0, 0, 0, 0, 0 }, // FL + { 0, 0, frontright, 0, 0, 0 }, // FR + { 0, center, 0, 0, 0, 0 }, // C + { 0, 0, 0, 0, 0, lfe }, // LFE + { 0, 0, 0, backleft, 0, 0 }, // RL + { 0, 0, 0, 0, backright, 0 }, // RR + { sideleft * 0.5f, 0, 0, sideleft * 0.5f, 0, 0 }, // SL + { 0, 0, sideright * 0.5f, 0, sideleft * 0.5f, 0 }, // SR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + else + { + float levels[8][6] = + { /* FL FR C LFE BL BR */ + { frontleft, 0, 0, 0, 0, 0 }, // FL + { 0, frontright, 0, 0, 0, 0 }, // FR + { 0, 0, center, 0, 0, 0 }, // C + { 0, 0, 0, lfe, 0, 0 }, // LFE + { 0, 0, 0, 0, backleft, 0 }, // RL + { 0, 0, 0, 0, 0, backright }, // RR + { sideleft * 0.5f, 0, 0, 0, sideleft * 0.5f, 0 }, // SL + { 0, sideright * 0.5f, 0, 0, 0, sideleft * 0.5f }, // SR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + *numinputlevels = 6; + break; + } + case 8: + { + float levels[8][8] = + { /* FL FR C LFE BL BR SL SR */ + { frontleft, 0, 0, 0, 0, 0, 0, 0 }, // FL + { 0, frontright, 0, 0, 0, 0, 0, 0 }, // FR + { 0, 0, center, 0, 0, 0, 0, 0 }, // C + { 0, 0, 0, lfe, 0, 0, 0, 0 }, // LFE + { 0, 0, 0, 0, backleft, 0, 0, 0 }, // RL + { 0, 0, 0, 0, 0, backright, 0, 0 }, // RR + { 0, 0, 0, 0, 0, 0, sideleft, 0 }, // SL + { 0, 0, 0, 0, 0, 0, 0, sideright }, // SR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 8; + break; + } + default: + { + break; + } + } + break; + } + case FMOD_SPEAKERMODE_PROLOGIC: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + int count; + + FMOD_memset(outlevels, 0, 2 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = center * .707f; + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = center * .707f; + } + + *numinputlevels = channels; + break; + } + else if (speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO || channels > 8) + { + int count; + + FMOD_memset(outlevels, 0, 2 * channels * sizeof(float)); + + for (count = 0; count < channels; count++) + { + if (count & 1) + { + outlevels[(channels * FMOD_SPEAKER_FRONT_RIGHT) + count] = frontright; + } + else + { + outlevels[(channels * FMOD_SPEAKER_FRONT_LEFT) + count] = frontleft; + } + } + + *numinputlevels = channels; + break; + } + + /* + Square the input levels to conform with Pro Logic linear gain + */ + float fl2 = frontleft * frontleft; + float fr2 = frontright * frontright; + float c2 = center * center; + float bl2 = backleft * backleft; + float br2 = backright * backright; + + switch (channels) + { + case 1: + { + float levels[2] = + { + fl2 + (c2 *0.707f) + (-0.872f * bl2) + (-0.49f * br2), + fr2 + (c2 *0.707f) + (0.49f * bl2) + (0.872f * br2) + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 1; + break; + } + case 2: + { + float levels[2][2] = + { + { fl2 + (c2 *0.707f) + (-0.872f * bl2), 0 + (c2 *0.707f) + (-0.49f * br2) }, + { 0 + (c2 *0.707f) + (0.49f * bl2), fr2 + (c2 *0.707f) + (0.872f * br2) } + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 2; + break; + } + case 4: + { + float levels[2][4] = + { + { fl2, 0, 0.5f * bl2, 0 }, // FL + { 0, fr2, 0 , 0.5f * br2 } // FR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + *numinputlevels = 4; + break; + } + case 6: + { + if (speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + float levels[2][6] = + { /* FL C FR BL BR LFE */ + { fl2, c2 * 0.5f, 0, bl2, 0, lfe * 0.5f }, // FL + { 0, c2 * 0.5f, fr2, 0, br2, lfe * 0.5f }, // FR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + else + { + float levels[2][6] = + { /* FL FR C LFE BL BR */ + { fl2, 0, c2 * 0.5f, lfe * 0.5f, bl2, 0}, // FL + { 0, fr2, c2 * 0.5f, lfe * 0.5f, 0, br2}, // FR + }; + + FMOD_memcpy(outlevels, levels, sizeof(levels)); + } + *numinputlevels = 6; + break; + } + default: + { + break; + } + } + break; + } + default: + { + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +DSPI::DSPI() +{ + mBuffer = 0; + mOutputBuffer = 0; + mFlags = 0; + + mNumInputs = 0; + mNumOutputs = 0; + mTreeLevel = -1; +#if defined(FMOD_SUPPORT_MIXER_NONRECURSIVE) || defined(PLATFORM_PS3) + mLastChannels = 0; +#endif + + mDefaultVolume = 1.0f; + mDefaultFrequency = DEFAULT_FREQUENCY; + mDefaultPan = 0.0f; + mDefaultPriority = FMOD_CHANNEL_DEFAULTPRIORITY; + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + mMemory = 0; + mInputHeadAddress = &mInputHead; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::release(bool freethis) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFORM_PS3_SPU + + FMOD_RESULT result; + + if (mSystem) + { + result = mSystem->stopDSP(this); + if (result != FMOD_OK) + { + return result; + } + } + + mSystem->flushDSPConnectionRequests(); /* If we're about to free DSP memory, dont leave any in queue! */ + + result = removeInternal(true); + if (result != FMOD_OK) + { + result = disconnectFromInternal(0, 0, true); + if (result != FMOD_OK) + { + return result; + } + } + + if (mOutputBuffer) + { + FMOD_Memory_Free(mOutputBuffer); + mOutputBuffer = 0; + } + + removeNode(); + + if (mDescription.release) + { + instance = (FMOD_DSP *)this; + + mDescription.release((FMOD_DSP_STATE *)this); + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + stopBuffering(); + #endif + + #ifdef FMOD_SUPPORT_VSTPLUGIN + if (mDescription.mType == FMOD_DSP_TYPE_VSTPLUGIN) + { + if (!mSystem->mVSTPluginsListHead.isEmpty()) + { + LinkedListNode *dspvstnode = mSystem->mVSTPluginsListHead.getNext(); + while (dspvstnode != &mSystem->mVSTPluginsListHead) + { + DSPI *dspvst = (DSPI *)dspvstnode->getData(); + + /* + Check if this is the node associated with this dsp unit. + If so, then remove it and free it. + */ + if (dspvst == this) + { + dspvstnode->removeNode(); + FMOD_Memory_Free(dspvstnode); + break; + } + + dspvstnode = dspvstnode->getNext(); + } + } + } + #endif + + if (freethis) + { + #ifdef PLATFORM_PS3 + if (mMemory) + { + FMOD_Memory_Free(mMemory); + } + else + #endif + { + FMOD_Memory_Free(this); + } + } + + #endif + + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getSystemObject(System **system) +{ + if (!system) + { + return FMOD_ERR_INVALID_PARAM; + } + + *system = (System *)mSystem; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::updateTreeLevel(int level) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + LinkedListNode *current; + + if (mNumOutputs > 1 && level < mTreeLevel) /* Don't make it move backwards if it is already connected to something else that is deeper. */ + { + return FMOD_OK; + } + + if (level >= FMOD_DSP_MAXTREEDEPTH) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DSPI::addInputInteral", "ERROR. Exceeded maximum DSP tree depth. Either added too many DSP effects by accident or encountered a recursive add.\n")); + + if (mSystem->mCallback) + { + mSystem->mCallback((FMOD_SYSTEM *)mSystem, FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL, (FMOD_DSP *)this, 0); + } + + return FMOD_ERR_DSP_CONNECTION; + } + + mTreeLevel = level; + + if (!mOutputBuffer || (FMOD_UINT_NATIVE)mBuffer != FMOD_ALIGNPOINTER(mOutputBuffer, 16)) + { + if (!mSystem->mDSPMixBuff[level]) + { + mSystem->mDSPMixBuff[level] = (float *)FMOD_Memory_CallocType((mSystem->mDSPBlockSize * (mSystem->mMaxOutputChannels < mSystem->mMaxInputChannels ? mSystem->mMaxInputChannels : mSystem->mMaxOutputChannels) * sizeof(float)) + 16, FMOD_MEMORY_PERSISTENT); + if (!mSystem->mDSPMixBuff[level]) + { + return FMOD_ERR_MEMORY; + } + } + } + + current = mInputHead.getNext(); + while (current != &mInputHead) + { + DSPConnectionI *connection = (DSPConnectionI *)current->getData(); + + connection->mInputUnit->updateTreeLevel(mTreeLevel + 1); + + current = current->getNext(); + } + + if (!mOutputBuffer || (FMOD_UINT_NATIVE)mBuffer != FMOD_ALIGNPOINTER(mOutputBuffer, 16)) + { + mBuffer = (float *)FMOD_ALIGNPOINTER(mSystem->mDSPMixBuff[mTreeLevel], 16); + } + +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::updateDSPTick(unsigned int tick) +{ + LinkedListNode *current; + + mDSPTick = tick; + + current = mInputHead.getNext(); + while (current != &mInputHead) + { + DSPConnectionI *connection = (DSPConnectionI *)current->getData(); + + connection->mInputUnit->updateDSPTick(tick); + + current = current->getNext(); + } + + return FMOD_OK; +} + + + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::createHistoryBuffer(float **ptr, int numchannels) +{ + return mSystem->mHistoryBufferPool.alloc(ptr, numchannels); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::releaseHistoryBuffer(float *ptr) +{ + return mSystem->mHistoryBufferPool.free(ptr); +} +#endif + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT DSPI::addInputInternal(DSPI *target, bool checkcircular, DSPConnectionI *connection, DSPConnectionI **connection_out, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + LocalCriticalSection crit_dsp(mSystem->mDSPCrit); + LocalCriticalSection crit_dspconnect(mSystem->mDSPConnectionCrit); + bool reset = false; + + if (!target) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Resamplers cant connect to something that already has an output. + */ + if (mDescription.mCategory == FMOD_DSP_CATEGORY_RESAMPLER && target->mNumOutputs) + { + return FMOD_ERR_DSP_CONNECTION; + } + + /* + Generators dont have inputs. + */ +// if (mDescription.mCategory == FMOD_DSP_CATEGORY_GENERATOR) +// { +// return FMOD_ERR_DSP_CONNECTION; +// } + + /* + Soundcards cant be connected to. + */ +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + if (target->mTreeLevel == 0) + { + return FMOD_ERR_DSP_CONNECTION; + } +#else + if (target->mDescription.mCategory == FMOD_DSP_CATEGORY_SOUNDCARD) + { + return FMOD_ERR_DSP_CONNECTION; + } +#endif + + /* + Dont allow a connection to this target if it is a child of a resampler. + */ + + /* + Dont allow connections to be circular! + */ + if (checkcircular) + { + result = target->doesUnitExist(this, protect); + if (result == FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "DSPI::addInputInteral", "ERROR. Tried to make a circular connection!.\n")); + + if (mSystem->mCallback) + { + mSystem->mCallback((FMOD_SYSTEM *)mSystem, FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION, (FMOD_DSP *)this, (FMOD_DSP *)target); + } + + return FMOD_ERR_DSP_CONNECTION; + } + } + + if (protect) + { + crit_dsp.enter(); + crit_dspconnect.enter(); + } + { + if (!connection) + { + /* + Create a new connection for this unit. + */ + result = mSystem->mDSPConnectionPool.alloc(&connection); + if (result != FMOD_OK) + { + return result; + } + + reset = true; + } + + connection->mInputNode.addBefore(&mInputHead); + mNumInputs++; + connection->mOutputNode.addBefore(&target->mOutputHead); + target->mNumOutputs++; + + connection->mInputUnit = target; + connection->mOutputUnit = this; + #ifdef PLATFORM_PS3 + connection->mInputUnitSize = target->mDescription.mSize; + connection->mOutputUnitSize = mDescription.mSize; + #endif + + if (reset) + { + connection->reset(); + } + + if (mTreeLevel >= 0) + { + target->updateTreeLevel(mTreeLevel + 1); + } + if (!mOutputBuffer) + { + mBuffer = (float *)FMOD_ALIGNPOINTER(mSystem->mDSPMixBuff[mTreeLevel], 16); + } + + /* + Allocate mBuffer for the target if it now has more than 1 input or output + */ + if (target->mNumOutputs > 1) + { + if (!target->mOutputBuffer) + { + target->mOutputBuffer = (float *)FMOD_Memory_Calloc((mSystem->mDSPBlockSize * (mSystem->mMaxOutputChannels < mSystem->mMaxInputChannels ? mSystem->mMaxInputChannels : mSystem->mMaxOutputChannels) * sizeof(float)) + 16); + if (!target->mOutputBuffer) + { + return FMOD_ERR_MEMORY; + } + } + + target->mBuffer = (float *)FMOD_ALIGNPOINTER(target->mOutputBuffer, 16); + } + } + if (protect) + { + crit_dsp.leave(); + crit_dspconnect.leave(); + } + + if (connection_out) + { + *connection_out = connection; + } + +#endif + return FMOD_OK; + +#else + + return FMOD_ERR_NEEDSSOFTWARE; + +#endif + +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::addInput(DSPI *target, DSPConnectionI **connection) +{ + if (!mSystem) + { + return FMOD_ERR_UNINITIALIZED; + } + +#ifndef PLATFORM_PS3_SPU + return addInputQueued(target, true, 0, connection); +#else + return FMOD_OK; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::addInputQueued(DSPI *target, bool checkcircular, DSPConnectionI *connection_old, DSPConnectionI **connection_out) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + DSPConnectionI *connection; + + if (!target) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Resamplers cant connect to something that already has an output. + */ + if (mDescription.mCategory == FMOD_DSP_CATEGORY_RESAMPLER && target->mNumOutputs) + { + return FMOD_ERR_DSP_CONNECTION; + } + + /* + Generators dont have inputs. + */ +// if (mDescription.mCategory == FMOD_DSP_CATEGORY_GENERATOR) +// { +// return FMOD_ERR_DSP_CONNECTION; +// } + + /* + Soundcards cant be connected to. + */ +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + if (target->mTreeLevel == 0) + { + return FMOD_ERR_DSP_CONNECTION; + } +#else + if (target->mDescription.mCategory == FMOD_DSP_CATEGORY_SOUNDCARD) + { + return FMOD_ERR_DSP_CONNECTION; + } +#endif + + /* + Create a new connection for this unit. + */ + result = mSystem->mDSPConnectionPool.alloc(&connection); + if (result != FMOD_OK) + { + return result; + } + + if (connection_old) + { + connection->copy(connection_old); + } + else + { + connection->reset(); + } + + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + { + DSPConnectionRequest *request; + + if (mSystem->mConnectionRequestFreeHead.isEmpty()) + { + mSystem->flushDSPConnectionRequests(); + } + + request = (DSPConnectionRequest *)mSystem->mConnectionRequestFreeHead.getNext(); + + request->removeNode(); + request->addBefore(&mSystem->mConnectionRequestUsedHead); + + request->mThis = this; + request->mTarget = target; + request->mConnection = connection; + if (checkcircular) + { + request->mRequest = DSPCONNECTION_REQUEST_ADDINPUT_ERRCHECK; + } + else + { + request->mRequest = DSPCONNECTION_REQUEST_ADDINPUT; + } + } + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + + if (connection_out) + { + *connection_out = connection; + } +#endif + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::disconnectFromInternal(DSPI *target, DSPConnectionI *connection, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + LocalCriticalSection crit_dsp(mSystem->mDSPCrit); + LocalCriticalSection crit_dspconnect(mSystem->mDSPConnectionCrit); + + if (!target) + { + int numinputs = 0, numoutputs = 0; + + /* + Disconnect all inputs connecting to this unit. + */ + getNumInputs(&numinputs, protect); + while (numinputs) + { + DSPI *input; + DSPConnectionI *inputconnection; + + result = getInput(0, &input, &inputconnection, protect); + if (result != FMOD_OK) + { + return result; + } + disconnectFromInternal(input, inputconnection, protect); + + getNumInputs(&numinputs, protect); + } + + /* + Disconnect outputs connecting to this unit. + */ + getNumOutputs(&numoutputs, protect); + while (numoutputs) + { + DSPI *output; + DSPConnectionI *outputconnection; + + result = getOutput(0, &output, &outputconnection, protect); + if (result != FMOD_OK) + { + return result; + } + output->disconnectFromInternal(this, outputconnection, protect); + + getNumOutputs(&numoutputs, protect); + } + + return FMOD_OK; + } + + if (protect) + { + crit_dspconnect.enter(); + crit_dsp.enter(); + } + + if (!connection) + { + int count; + DSPConnectionI *inputconnection; + + for (count=0; count < mNumInputs; count++) + { + result = getInput(count, 0, &inputconnection, protect); + if (result != FMOD_OK) + { + return result; + } + + if (inputconnection->mInputUnit == target) + { + connection = inputconnection; + break; + } + } + } + + if (!connection) + { + return FMOD_ERR_DSP_NOTFOUND; + } + + if (!connection->mInputUnit && !connection->mOutputUnit) + { + return FMOD_OK; /* Already been disconnected, must have been a double disconnect. */ + } + + connection->mInputNode.removeNode(); + mNumInputs--; + if (mOutputBuffer && mNumOutputs <= 1) + { + FMOD_Memory_Free(mOutputBuffer); + + if ((FMOD_UINT_NATIVE)mBuffer == FMOD_ALIGNPOINTER(mOutputBuffer, 16)) + { + mBuffer = (float *)FMOD_ALIGNPOINTER(mSystem->mDSPMixBuff[mTreeLevel], 16); + } + + mOutputBuffer = 0; + } + + connection->mOutputNode.removeNode(); + target->mNumOutputs--; + + result = mSystem->mDSPConnectionPool.free(connection, protect); + if (result != FMOD_OK) + { + return result; + } + + if (protect) + { + crit_dsp.leave(); + crit_dspconnect.leave(); + } + +#endif + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::disconnectFrom(DSPI *target, DSPConnectionI *connection) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + { + DSPConnectionRequest *request; + + if (mSystem->mConnectionRequestFreeHead.isEmpty()) + { + mSystem->flushDSPConnectionRequests(); + } + + request = (DSPConnectionRequest *)mSystem->mConnectionRequestFreeHead.getNext(); + + request->removeNode(); + request->addBefore(&mSystem->mConnectionRequestUsedHead); + + request->mThis = this; + request->mTarget = target; + request->mConnection = connection; + request->mRequest = DSPCONNECTION_REQUEST_DISCONNECTFROM; + + if (target) + { + request->mTarget->mFlags |= FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + } + else + { + request->mThis->mFlags |= FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + } + } + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::disconnectAllInternal(bool inputs, bool outputs, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + int count, numinputs, numoutputs; + + if (inputs) + { + result = getNumInputs(&numinputs, protect); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < numinputs; count++) + { + DSPI *input; + DSPConnectionI *inputconnection; + + result = getInput(0, &input, &inputconnection, protect); // keep getting the first one. + if (result != FMOD_OK) + { + return result; + } + + result = disconnectFromInternal(input, inputconnection, protect); + if (result != FMOD_OK) + { + return result; + } + } + } + + if (outputs) + { + result = getNumOutputs(&numoutputs, protect); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < numoutputs; count++) + { + DSPI *output; + DSPConnectionI *outputconnection; + + result = getOutput(0, &output, &outputconnection, protect); // keep getting the first one. + if (result != FMOD_OK) + { + return result; + } + + result = output->disconnectFromInternal(this, outputconnection, protect); + if (result != FMOD_OK) + { + return result; + } + } + } +#endif + + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::disconnectAll(bool inputs, bool outputs) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + + if (!inputs && !outputs) + { + return FMOD_OK; + } + + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + { + DSPConnectionRequest *request; + + if (mSystem->mConnectionRequestFreeHead.isEmpty()) + { + mSystem->flushDSPConnectionRequests(); + } + + request = (DSPConnectionRequest *)mSystem->mConnectionRequestFreeHead.getNext(); + + request->removeNode(); + request->addBefore(&mSystem->mConnectionRequestUsedHead); + + request->mThis = this; + request->mTarget = 0; + if (inputs && outputs) + { + request->mRequest = DSPCONNECTION_REQUEST_DISCONNECTALL; + request->mThis->mFlags |= FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + } + else if (inputs) + { + request->mRequest = DSPCONNECTION_REQUEST_DISCONNECTALLINPUTS; + } + else + { + request->mRequest = DSPCONNECTION_REQUEST_DISCONNECTALLOUTPUTS; + request->mThis->mFlags |= FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + } + } + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::insertInputBetween(DSPI *dsptoinsert, int inputindex, bool search, DSPConnectionI **connection_out) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + DSPConnectionI *connection; + FMOD_RESULT result; + + /* + Create a new connection for this unit. + */ + result = mSystem->mDSPConnectionPool.alloc(&connection); + if (result != FMOD_OK) + { + return result; + } + + connection->reset(); + + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + { + DSPConnectionRequest *request; + + if (mSystem->mConnectionRequestFreeHead.isEmpty()) + { + mSystem->flushDSPConnectionRequests(); + } + + request = (DSPConnectionRequest *)mSystem->mConnectionRequestFreeHead.getNext(); + + request->removeNode(); + request->addBefore(&mSystem->mConnectionRequestUsedHead); + + request->mThis = this; + request->mTarget = dsptoinsert; + request->mConnection = connection; + request->mInputIndex = inputindex; + request->mRequest = search ? DSPCONNECTION_REQUEST_INSERTINBETWEEN_SEARCH : DSPCONNECTION_REQUEST_INSERTINBETWEEN; + + dsptoinsert->mFlags |= FMOD_DSP_FLAG_USEDADDDSP; + } + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + + if (connection_out) + { + *connection_out = connection; + } + +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::insertInputBetweenInternal(DSPI *dsp, int inputindex, bool search, DSPConnectionI *connection, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + DSPI *target = 0; + DSPConnectionI *targetconnection; + + /* + Disconnect it from wherever it used to be. + */ + if (dsp->mFlags & FMOD_DSP_FLAG_USEDADDDSP) + { + DSPI *current = dsp; + + current->disconnectAllInternal(false, true, protect); /* Disconnect outputs from start of chain. */ + + while (1) + { + DSPI *next; + + result = current->getInput(0, &next, 0, protect); + if (result != FMOD_OK) + { + break; + } + + if (!(next->mFlags & FMOD_DSP_FLAG_USEDADDDSP)) + { + break; + } + + current = next; + } + + current->disconnectAllInternal(true, false, protect); /* Disconnect outputs from end of chain. */ + } + else + { + result = dsp->disconnectFromInternal(0, 0, protect); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Find the next unit to insert between. If not found, we'll just add it to the end. + */ + getInput(inputindex, &target, &targetconnection, protect); + + /* + Disconnect the head from its first input. + */ + if (target) + { + result = disconnectFromInternal(target, targetconnection, protect); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Add the new input to the head. + */ + result = addInputInternal(dsp, false, connection, 0, protect); + if (result != FMOD_OK) + { + return result; + } + + /* + If more than 1 unit, move the pointer forward to the end so we can reconnect the 'target' onto the end of this subchain. + */ + if (search) + { + while (1) + { + DSPI *next; + + result = dsp->getInput(0, &next, 0, protect); + if (result != FMOD_OK) + { + break; + } + + if (!(next->mFlags & FMOD_DSP_FLAG_USEDADDDSP)) + { + break; + } + + dsp = next; + } + } + + /* + Add the old first input to this new unit. + */ + if (target) + { + result = dsp->addInputInternal(target, false, 0, 0, protect); + if (result != FMOD_OK) + { + return result; + } + } + +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::remove() +{ + return removeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::removeInternal(bool protect) +{ +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + DSPI *output, *input; + int numinputs, numoutputs; + + if (!(mFlags & FMOD_DSP_FLAG_USEDADDDSP)) + { + return disconnectFromInternal(0, 0, protect); + } + + result = getNumInputs(&numinputs, protect); + if (result != FMOD_OK) + { + return result; + } + result = getNumOutputs(&numoutputs, protect); + if (result != FMOD_OK) + { + return result; + } + + if (!numinputs && !numoutputs) + { + return FMOD_OK; + } + + if (numinputs != 1 || numoutputs != 1) + { + mFlags &= ~FMOD_DSP_FLAG_USEDADDDSP; + return disconnectFromInternal(0, 0, protect); + } + + result = setActive(false); + if (result != FMOD_OK) + { + return result; + } + + result = getInput(0, &input, 0, protect); + if (result != FMOD_OK) + { + return result; + } + + result = getOutput(0, &output, 0, protect); + if (result != FMOD_OK) + { + return result; + } + + result = disconnectFromInternal(0, 0, protect); + if (result != FMOD_OK) + { + return result; + } + + result = output->addInputInternal(input, false, 0, 0, protect); + if (result != FMOD_OK) + { + return result; + } + + mFlags &= ~FMOD_DSP_FLAG_USEDADDDSP; +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getNumInputs(int *numinputs, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFORM_PS3_SPU + + if (!numinputs) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (protect) + { + mSystem->flushDSPConnectionRequests(); + + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + } + + *numinputs = mNumInputs; + + if (protect) + { + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + } + + #endif + + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT DSPI::getNumOutputs(int *numoutputs, bool protect) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFORM_PS3_SPU + + if (!numoutputs) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (protect) + { + mSystem->flushDSPConnectionRequests(); + + FMOD_OS_CriticalSection_Enter(mSystem->mDSPConnectionCrit); + } + + *numoutputs = mNumOutputs; + + if (protect) + { + FMOD_OS_CriticalSection_Leave(mSystem->mDSPConnectionCrit); + } + + #endif + + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::reset() +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + if (!mDescription.reset) + { + return FMOD_ERR_UNSUPPORTED; + } + + instance = (FMOD_DSP *)this; + + return mDescription.reset((FMOD_DSP_STATE *)this); +#else + return FMOD_ERR_INTERNAL; +#endif +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::setParameter(int index, float value) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + + if (!mDescription.setparameter) + { + return FMOD_ERR_UNSUPPORTED; + } + + if (index < 0 || index > mDescription.numparameters) + { + return FMOD_ERR_INVALID_PARAM; + } + + #ifdef FMOD_DEBUG + + result = FMOD_CHECKFLOAT(value); + if (result != FMOD_OK) + { + return result; + } + + #endif + + if (value < mDescription.paramdesc[index].min) + { + value = mDescription.paramdesc[index].min; + } + if (value > mDescription.paramdesc[index].max) + { + value = mDescription.paramdesc[index].max; + } + + instance = (FMOD_DSP *)this; + + result = mDescription.setparameter((FMOD_DSP_STATE *)this, index, value); + if (result != FMOD_OK) + { + return result; + } +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getParameter(int index, float *value, char *valuestr, int valuestrlen) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT result; + float v; + char s[32]; + + if (!mDescription.getparameter) + { + return FMOD_ERR_UNSUPPORTED; + } + + if (index < 0 || index > mDescription.numparameters) + { + return FMOD_ERR_INVALID_PARAM; + } + + instance = (FMOD_DSP *)this; + + result = mDescription.getparameter((FMOD_DSP_STATE *)this, index, &v, s); + if (result != FMOD_OK) + { + return result; + } + + if (value) + { + *value = v; + } + + if (valuestr) + { + FMOD_strncpy(valuestr, s, valuestrlen > 16 ? 16 : valuestrlen); + } +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getNumParameters(int *numparams) +{ +#ifndef PLATFORM_PS3_SPU + if (!numparams) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numparams = mDescription.numparameters; +#endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getParameterInfo(int index, char *name, char *label, char *description, int descriptionlen, float *min, float *max) +{ +#ifdef FMOD_SUPPORT_SOFTWARE +#ifndef PLATFORM_PS3_SPU + if (index < 0 || index >= mDescription.numparameters) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name) + { + FMOD_strcpy(name, mDescription.paramdesc[index].name); + } + if (description && descriptionlen) + { + if (mDescription.paramdesc[index].description) + { + FMOD_strncpy(description, mDescription.paramdesc[index].description, descriptionlen); + } + else + { + *description = 0; + } + } + if (label) + { + FMOD_strcpy(label, mDescription.paramdesc[index].label); + } + if (min) + { + *min = mDescription.paramdesc[index].min; + } + if (max) + { + *max = mDescription.paramdesc[index].max; + } +#endif + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::showConfigDialog(void *hwnd, bool show) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFROM_PS3_SPU + + if (!mDescription.config) + { + return FMOD_ERR_UNSUPPORTED; + } + + instance = (FMOD_DSP *)this; + + return mDescription.config((FMOD_DSP_STATE *)this, hwnd, show); + + #else + + return FMOD_ERR_INTERNAL; + + #endif +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getInfo(char *name, unsigned int *version, int *channels, int *configwidth, int *configheight) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFORM_PS3_SPU + + if (name) + { + FMOD_strncpy(name, mDescription.name, 32); + } + + if (version) + { + *version = mDescription.version; + } + + if (channels) + { + *channels = mDescription.channels; + } + + if (configwidth) + { + *configwidth = mDescription.configwidth; + } + + if (configheight) + { + *configheight = mDescription.configheight; + } + + #endif + + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getType(FMOD_DSP_TYPE *type) +{ + if (type) + { + *type = mDescription.mType; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::setDefaults(float frequency, float volume, float pan, int priority) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFORM_PS3_SPU + + if (volume > 1) + { + volume = 1; + } + if (volume < 0) + { + volume = 0; + } + if (pan < -1) + { + pan = -1; + } + if (pan > 1) + { + pan = 1; + } + if (priority < 0) + { + priority = 0; + } + if (priority > 256) + { + priority = 256; + } + + mDefaultFrequency = frequency; + mDefaultVolume = volume; + mDefaultPan = pan; + mDefaultPriority = priority; + + #endif + + return FMOD_OK; + +#else + + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getDefaults(float *frequency, float *volume, float *pan, int *priority) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + + #ifndef PLATFORM_PS3_SPU + + if (frequency) + { + *frequency = mDefaultFrequency; + } + if (volume) + { + *volume = mDefaultVolume; + } + if (pan) + { + *pan = mDefaultPan; + } + if (priority) + { + *priority = mDefaultPriority; + } + + #endif + + return FMOD_OK; + +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::setUserData(void *userdata) +{ +#ifndef PLATFORM_PS3_SPU + mDescription.userdata = userdata; +#endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getUserData(void **userdata) +{ +#ifndef PLATFORM_PS3_SPU + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mDescription.userdata; +#endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::setTargetFrequency(int frequency) +{ +#ifndef PLATFORM_PS3_SPU + mTargetFrequency = frequency; +#endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getTargetFrequency(int *frequency) +{ +#ifndef PLATFORM_PS3_SPU + if (!frequency) + { + return FMOD_ERR_INVALID_PARAM; + } + + *frequency = mTargetFrequency; +#endif + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::calculatePeaks(const float *buffer, unsigned int length, unsigned int numchannels, DSPI *t) +{ + float peakvolume[16] = { 0 }; + unsigned int sampleoffset = 0; + unsigned int channeloffset = 0; + + if (!t) + { + t = this; + } + + FMOD_memset(t->mPeakVolume, 0, sizeof(t->mPeakVolume)); + + if (buffer == NULL) + { + t->mNumPeakVolumeChans = 0; + return FMOD_OK; + } + + while (sampleoffset < length) + { + float absValue = buffer[sampleoffset] < 0 ? -buffer[sampleoffset] : buffer[sampleoffset]; + + if (absValue > peakvolume[channeloffset]) + { + peakvolume[channeloffset] = absValue; + } + + sampleoffset++; + channeloffset++; + + if (channeloffset >= numchannels) + { + channeloffset = 0; + } + } + + for (channeloffset = 0; channeloffset < DSP_MAXLEVELS_MAX; channeloffset++) + { + unsigned short peakvalue = 0; + + /* Channel is really silent (not mearly very quiet) */ + if (peakvolume[channeloffset] <= DSP_LEVEL_SMALLVAL) + { + peakvalue = 0; + } + /* Channel volume has clipped our maximum */ + else if (peakvolume[channeloffset] > 1.0f) + { + peakvalue = 31; + } + /* Convert volume to dB (1 to 30 represents -2 to -60 dB) */ + else + { + float dB = FMOD_LOG10(peakvolume[channeloffset]) * 20.0f; + + peakvalue = (short)(-dB + 0.5f); + peakvalue = peakvalue / 2; + peakvalue = (peakvalue > 30) ? 30 : (peakvalue < 1) ? 1 : peakvalue; + } + + /* Pack result value, 5-bits per value, stored big endian */ + { + unsigned char bitoffset = channeloffset * 5; + unsigned char byteoffset = bitoffset >> 3; + unsigned char excessbits = bitoffset & 7; + unsigned char upperbound = sizeof(t->mPeakVolume) - sizeof(short); + unsigned short *peakdata = NULL; + + /* Our sliding window is type short, so don't slide off the end of the array */ + excessbits = (byteoffset > upperbound) ? excessbits + 8 : excessbits; + byteoffset = (byteoffset > upperbound) ? upperbound : byteoffset; + + peakvalue <<= excessbits; + + #ifdef PLATFORM_ENDIAN_LITTLE + peakvalue = FMOD_SWAPENDIAN_WORD(peakvalue); + #endif + + peakdata = (unsigned short *)&t->mPeakVolume[upperbound - byteoffset]; + peakvalue |= *peakdata; + + /* + This should be: *peakdata = peakvalue; + Cannot do above because PS3 SPU seems to treat *peakdata as a char instead of + a short, so we will manually assign the two bytes of the short. + This is because the short needs to be 2 byte aligned on the SPU. + */ + { + unsigned char *p = (unsigned char *)peakdata; + unsigned char *v = (unsigned char *)&peakvalue; + + p[0] = v[0]; + p[1] = v[1]; + } + } + } + + t->mNumPeakVolumeChans = numchannels; + return FMOD_OK; +} +#endif + +#if !defined(PLATFORM_PS3_SPU) && (defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::startBuffering() +{ + FMOD_RESULT result; + int channels; + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + + if (mHistoryBuffer) + { + return FMOD_OK; + } + + criticalsection.enter(); + + result = mSystem->getSoftwareFormat(0, 0, &channels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (mHistoryBufferMemory) + { + result = releaseHistoryBuffer(mHistoryBufferMemory); + if (result != FMOD_OK) + { + return result; + } + } + + result = createHistoryBuffer(&mHistoryBufferMemory, channels); + if (result != FMOD_OK) + { + return result; + } + mHistoryBuffer = (float *)FMOD_ALIGNPOINTER(mHistoryBufferMemory, 16); + + mHistoryPosition = 0; + + criticalsection.leave(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getHistoryBuffer(float **buffer, unsigned int *position, unsigned int *length) +{ + if (buffer) + { + *buffer = mHistoryBuffer; + } + + if (position) + { + *position = mHistoryPosition; + } + + if (length) + { + *length = FMOD_HISTORYBUFFERLEN; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::stopBuffering() +{ + if (mHistoryBufferMemory) + { + FMOD_RESULT result; + LocalCriticalSection criticalsection(mSystem->mDSPCrit, true); + + result = releaseHistoryBuffer(mHistoryBufferMemory); + if (result != FMOD_OK) + { + return result; + } + mHistoryBuffer = mHistoryBufferMemory = 0; + } + + return FMOD_OK; +} + +#else + + +FMOD_RESULT DSPI::stopBuffering() +{ + return FMOD_OK; +} + +#endif + + +/* +#if defined(FMOD_SUPPORT_MEMORYTRACKER) && !defined(PLATFORM_PS3_SPU) + +FMOD_RESULT DSPI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(MEMTYPE_DSPI, sizeof(*this)); + +// LinkedListNode mInputHead; +// LinkedListNode mOutputHead; +// int mNumInputs; +// int mNumOutputs; + +// float *mOutputBuffer; + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) +// LinkedListNode *mPrevious; + #endif + +// static FMOD_OS_CRITICALSECTION *gCrit; + +// FMOD_DSP_DESCRIPTION_EX mDescription; +// float *mBuffer; +// int mBufferChannels; +// char *mWantsToFinish; + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) +// DSPI *mMemory; +// LinkedListNode *mInputHeadAddress; + +// float *mHistoryBuffer; +// float *mHistoryBufferMemory; +#endif + + return FMOD_OK; +} + +#endif + +*/ +/* +[API] +[ + [DESCRIPTION] + This callback is called once when a user creates a DSP unit of this type. It is used to allocate memory, initialize variables and the like. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + System::createDSP<br> + System::createDSPByType<br> + System::createDSPByPlugin<br> + Sometimes a user will re-use a DSP unit instead of releasing it and creating a new one, so it may be useful to implement FMOD_DSP_RESETCALLBACK to reset any variables or buffers when the user calls it.<br> + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + System::createDSP + System::createDSPByType + System::createDSPByPlugin + FMOD_DSP_RESETCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_CREATECALLBACK(FMOD_DSP_STATE *dsp_state); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + This callback is called when the user releases the DSP unit. It is used to free any resources allocated during the course of the lifetime of the DSP or perform any shut down code needed to clean up the DSP unit. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + DSP::release<br> + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + DSP::release +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_RELEASECALLBACK(FMOD_DSP_STATE *dsp_state); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + This callback function is called by DSP::reset to allow the effect to reset itself to a default state.<br> + This is useful if an effect is for example still holding audio data for a sound that has stopped, and the unit wants to be relocated to a new sound. Resetting the unit would clear any buffers, put the effect back to its initial state, and get it ready for new sound data. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + DSP::reset<br> + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + DSP::reset +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_RESETCALLBACK(FMOD_DSP_STATE *dsp_state); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + This callback is called back regularly when the unit has been created, inserted to the DSP network, and set to active by the user.<br> + This callback requires the user to fill the output pointer with data. Incoming data is provided and may be filtered on its way to the output pointer.<br> + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + 'inbuffer' Pointer to incoming floating point -1.0 to +1.0 ranged data. + 'outbuffer' Pointer to outgoing floating point -1.0 to +1.0 ranged data. The dsp writer must write to this pointer else there will be silence. + 'length' The length of the incoming and outgoing buffer in samples. To get the length of the buffer in bytes, the user must multiply this number by the number of channels coming in (and out, they may be different) and then multiply by 4 for 1 float = 4 bytes. + 'inchannels' The number of channels of interleaved PCM data in the inbuffer parameter. A mono signal coming in would be 1. A stereo signal coming in would be 2. + 'outchannels' The number of channels of interleaved PCM data in the outbuffer parameter. A mono signal going out would be 1. A stereo signal going out would be 2. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + <i>None</i>.<br> + This callback is called automatically and periodically when the DSP engine updates.<br> + For a read update to be called it would have to be enabled, and this is done with DSP::setActive. + <br> + The range of -1 to 1 is a soft limit. In the case of the inbuffer it is not guaranteed to be in that range, and in the case of the outbuffer FMOD will accept values outside that range. However all values will be clamped to the range of -1 to 1 in the final mix.<br> + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + DSP::setActive +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_READCALLBACK(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Callback that is called when the user sets the position of a channel with Channel::setPosition. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + 'position' Position in channel stream to set to. Units are PCM samples (ie FMOD_TIMEUNIT_PCM). + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + Channel::setPosition.<br> + If a DSP unit is attached to a channel and the user calls Channel::setPosition then this funciton will be called. + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + Channel::setPosition +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_SETPOSITIONCALLBACK(FMOD_DSP_STATE *dsp_state, unsigned int position); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + This callback is called when the user wants the plugin to display a configuration dialog box. This is not always nescessary, so this can be left blank if wanted. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + 'hwnd' This is the target hwnd to display the dialog in. It must not pop up on this hwnd, it must actually be drawn within it. + 'show' 1 = show the dialog, 0 = hide/remove the dialog. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + DSP::showConfigDialog.<br> + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + DSP::showConfigDialog +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_DIALOGCALLBACK(FMOD_DSP_STATE *dsp_state, void *hwnd, int show); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + This callback is called when the user wants to set a parameter for a DSP unit. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + 'index' The index into the parameter list for the parameter the user wants to set. + 'value' The value passed in by the user to set for the selected parameter. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + DSP::setParameter.<br> + <br> + Range checking is not needed. FMOD will clamp the incoming value to the specified min/max. + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + DSP::setParameter + FMOD_DSP_GETPARAMCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_SETPARAMCALLBACK(FMOD_DSP_STATE *dsp_state, int index, float value); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + This callback is called when the user wants to get an indexed parameter from a DSP unit. + + [PARAMETERS] + 'dsp_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. Do not cast this to FMOD_DSP! The handle to the user created DSP handle is stored within the FMOD_DSP_STATE structure. + 'index' The index into the parameter list for the parameter the user wants to get. + 'value' Pointer to a floating point variable to receive the selected parameter value. + 'valuestr' A pointer to a string to receive the value of the selected parameter, but in text form. This might be useful to display words instead of numbers. For example "ON" or "OFF" instead of 1.0 and 0.0. The length of the buffer being passed in is always 16 bytes, so do not exceed this. + + [RETURN_VALUE] + + [REMARKS] + Functions that the user would have to call for this callback to be called.<br> + DSP::getParameter.<br> + FMOD_DSP_GETPARAMCALLBACK.<br> + <br> + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_DSP_STATE + DSP::getParameter + FMOD_DSP_SETPARAMCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_DSP_GETPARAMCALLBACK(FMOD_DSP_STATE *dsp_state, int index, float *value, char *valuestr) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ + +#if defined(FMOD_SUPPORT_MEMORYTRACKER) && !defined(PLATFORM_PS3_SPU) + +FMOD_RESULT DSPI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_DSP, mDescription.mSize); + + if (mOutputBuffer) + { + tracker->add(false, FMOD_MEMBITS_DSP, (mSystem->mDSPBlockSize * (mSystem->mMaxOutputChannels < mSystem->mMaxInputChannels ? mSystem->mMaxInputChannels : mSystem->mMaxOutputChannels) * sizeof(float)) + 16); + } + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + + if (mHistoryBufferMemory) + { + int channels; + CHECK_RESULT(mSystem->getSoftwareFormat(0, 0, &channels, 0, 0, 0)); + tracker->add(false, FMOD_MEMBITS_DSP, FMOD_HISTORYBUFFERLEN * channels * sizeof(float) + 16); + } +#endif + + if (mDescription.getmemoryused) + { + CHECK_RESULT(mDescription.getmemoryused(this, tracker)); + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_dspi.h b/src/fmod_dspi.h new file mode 100755 index 0000000..1a6e858 --- /dev/null +++ b/src/fmod_dspi.h @@ -0,0 +1,283 @@ +#ifndef _FMOD_DSPI_H +#define _FMOD_DSPI_H + +#include "fmod_settings.h" + +#include "fmod_plugin.h" +#include "fmod.hpp" +#include "fmod_dsp_connectioni.h" +#include "fmod_linkedlist.h" +#include "fmod_os_misc.h" + +namespace FMOD +{ + class ChannelSoftware; + class DSPFilter; + class PluginFactory; + class MemoryTracker; + + typedef enum + { + FMOD_DSP_CATEGORY_FILTER, /* This is a unit type that processes incoming data. */ + FMOD_DSP_CATEGORY_DSPCODECMPEG, /* This is a unit type that resamples data from a codec. (MPEG) */ + FMOD_DSP_CATEGORY_DSPCODECADPCM, /* This is a unit type that resamples data from a codec. (ADPCM) */ + FMOD_DSP_CATEGORY_DSPCODECXMA, /* This is a unit type that resamples data from a codec. (XMA) */ + FMOD_DSP_CATEGORY_DSPCODECCELT, /* This is a unit type that resamples data from a codec. (CELT) */ + FMOD_DSP_CATEGORY_DSPCODECRAW, /* This is a unit type that resamples data from a codec. (RAW) */ + FMOD_DSP_CATEGORY_SOUNDCARD, /* This is a unit type that only receives data. (Not used on PS3 non-recursive SPU mixer) */ + FMOD_DSP_CATEGORY_WAVETABLE, /* This is a unit type that only reads wave data. */ + FMOD_DSP_CATEGORY_RESAMPLER + } FMOD_DSP_CATEGORY; + + #define FMOD_DSP_MAXTREEDEPTH 128 + + const int FMOD_DSP_TYPE_CODECREADER = 1000; + + #ifdef FMOD_SUPPORT_DLLS + typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_DIALOGIDLECALLBACK) (FMOD_DSP_STATE *dsp_state); + #endif + typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_GETMEMORYUSED) (FMOD_DSP_STATE *dsp_state, MemoryTracker *tracker); + typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_UPDATE) (FMOD_DSP_STATE *dsp_state); + + struct FMOD_DSP_DESCRIPTION_EX : public FMOD_DSP_DESCRIPTION, public LinkedListNode + { + FMOD_SOUND_FORMAT mFormat; + FMOD_DSP_TYPE mType; + int mSize; + FMOD_DSP_CATEGORY mCategory; +#ifdef FMOD_SUPPORT_DLLS + FMOD_OS_LIBRARY *mModule; + void *mAEffect; +#endif + int mResamplerBlockLength; + unsigned int mHandle; + DSPI *mDSPSoundCard; /* what to base dsp tick on. */ + +#ifdef FMOD_SUPPORT_DLLS + FMOD_DSP_DIALOGIDLECALLBACK configidle; +#endif + FMOD_DSP_GETMEMORYUSED getmemoryused; + FMOD_DSP_UPDATE update; + }; + + #define FMOD_DSP_CONNECTION_REQUEST_MAX 512 + + typedef enum + { + DSPCONNECTION_REQUEST_ADDINPUT, + DSPCONNECTION_REQUEST_ADDINPUT_ERRCHECK, + DSPCONNECTION_REQUEST_DISCONNECTFROM, + DSPCONNECTION_REQUEST_DISCONNECTALLINPUTS, + DSPCONNECTION_REQUEST_DISCONNECTALLOUTPUTS, + DSPCONNECTION_REQUEST_DISCONNECTALL, + DSPCONNECTION_REQUEST_INSERTINBETWEEN, + DSPCONNECTION_REQUEST_INSERTINBETWEEN_SEARCH, + DSPCONNECTION_REQUEST_REVERBUPDATEPARAMETERS + } FMOD_DSPCONNECTIONREQUEST_CMD; + + #define FMOD_DSP_FLAG_ACTIVE 0x00000002 + #define FMOD_DSP_FLAG_BYPASS 0x00000004 + #define FMOD_DSP_FLAG_FINISHED 0x00000008 + #define FMOD_DSP_FLAG_IDLE 0x00000010 + #define FMOD_DSP_FLAG_USEDADDDSP 0x00000020 + #define FMOD_DSP_FLAG_FIRSTMIX 0x00000040 + #define FMOD_DSP_FLAG_QUEUEDFORDISCONNECT 0x00000100 + #define FMOD_DSP_FLAG_PARAMETERUPDATE 0x00001000 + + class DSPConnectionRequest : public LinkedListNode + { + public: + + DSPI *mThis; + DSPI *mTarget; + DSPConnectionI *mConnection; + FMOD_DSPCONNECTIONREQUEST_CMD mRequest; + int mInputIndex; + }; + + class DSPI : public Plugin, public FMOD_DSP_STATE + { + DECLARE_MEMORYTRACKER_NONVIRTUAL_EXPORT + + friend class DSPFilter; + #if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP) + friend class ProfileDsp; + #endif + + private: + + /* + This stuff is timing sensitive. It must not be referenced directly except through accessors. + */ + LinkedListNode mInputHead; + LinkedListNode mOutputHead; + int mNumInputs; + int mNumOutputs; + + protected: + + float *mOutputBuffer; + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + LinkedListNode *mPrevious; + #endif + int mTargetFrequency; + short mTreeLevel; + + #if defined(FMOD_SUPPORT_MIXER_NONRECURSIVE) || defined(PLATFORM_PS3) + short mLastChannels; + #endif + + unsigned short mCPUUsage; + unsigned short mCPUUsageTemp; +#ifdef FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS + unsigned char mPeakVolume[10]; /* 16 values, 5-bit packed */ + unsigned short mNumPeakVolumeChans; +#endif + + FMOD_RESULT doesUnitExist(DSPI *target, bool protect); + + public: + + static FMOD_OS_CRITICALSECTION *gCrit; + static bool gActive; + + static FMOD_RESULT convert (void *outbuffer, void *inbuffer, FMOD_SOUND_FORMAT outformat, FMOD_SOUND_FORMAT informat, unsigned int length, int destchannelstep, int srcchannelstep, float wetmix); + static FMOD_RESULT validate (DSP *dsp, DSPI **dspi); + + virtual FMOD_RESULT read (float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + virtual FMOD_RESULT read (void *outbuffer, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + virtual FMOD_RESULT alloc (FMOD_DSP_DESCRIPTION_EX *description); + + #ifdef FMOD_SUPPORT_MIXER_NONRECURSIVE + FMOD_RESULT updateHistory (DSPI *t, float *buffer, int length, int channels); + static FMOD_RESULT stepBack (LinkedListNode *¤t, DSPConnectionI *&connection, DSPI *&t, LinkedListNode *&next, bool dspionly); + static FMOD_RESULT stepForwards (LinkedListNode *¤t, DSPConnectionI *&connection, DSPConnectionI *&prevconnection, DSPI *&t, LinkedListNode *&next); + #endif + FMOD_RESULT run (float **outbuffer, int *outchannels, unsigned int *length, FMOD_SPEAKERMODE speakermode, int speakermodechannels, unsigned int tick); + + virtual FMOD_RESULT setPosition (unsigned int position, bool processinputs); + static FMOD_RESULT calculateSpeakerLevels(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright, FMOD_SPEAKERMODE speakermode, int channels, FMOD_SPEAKERMAPTYPE speakermap, float *outlevels, int *numinputlevels); + FMOD_RESULT updateTreeLevel (int level); + FMOD_RESULT updateDSPTick (unsigned int tick); +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT createHistoryBuffer(float **buffer, int numchannels); + FMOD_RESULT releaseHistoryBuffer(float *buffer); +#endif + + FMOD_DSP_DESCRIPTION_EX mDescription; + float mDefaultVolume; + float mDefaultFrequency; + float mDefaultPan; + int mDefaultPriority; + float *mBuffer; + int mBufferChannels; + unsigned int mDSPTick; + FMOD_PPCALIGN16(unsigned int mFlags); + +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + unsigned int mFlagsMramAddress; + + FMOD_PPCALIGN16(unsigned int mMramAddress); + DSPI *mMemory; + LinkedListNode *mInputHeadAddress; + + float *mHistoryBuffer; + float *mHistoryBufferMemory; + unsigned int mHistoryPosition; + unsigned int mHistoryLength; + + unsigned int mCodeMramAddress; + unsigned int mCodeEntryAddress; + unsigned int mCodeSize; + unsigned int mCodeLSOffset; + + unsigned int mCodeFillLocation; + unsigned int mCodeFillSize; + unsigned int mCodeFillValue; +#else + char mWantsToFinishMem; +#endif + + DSPI(); + + virtual FMOD_RESULT release (bool freethis = true); + virtual FMOD_RESULT getSystemObject (System **system); + + // Connection / disconnection / input and output enumeration. + FMOD_RESULT addInputInternal (DSPI *target, bool checkcircular, DSPConnectionI *connection = 0, DSPConnectionI **connection_out = 0, bool protect = true); + virtual FMOD_RESULT addInput (DSPI *target, DSPConnectionI **connection = 0); + FMOD_RESULT addInputQueued (DSPI *target, bool checkcircular, DSPConnectionI *connection_old, DSPConnectionI **connection_out); + + FMOD_RESULT disconnectFromInternal (DSPI *target, DSPConnectionI *connection, bool protect = true); + FMOD_RESULT disconnectFrom (DSPI *target, DSPConnectionI *connection = 0); + + FMOD_RESULT disconnectAllInternal (bool inputs, bool outputs, bool protect = true); + FMOD_RESULT disconnectAll (bool inputs, bool outputs); + + FMOD_RESULT insertInputBetween (DSPI *target, int inputindex, bool search, DSPConnectionI **connection_out); + FMOD_RESULT insertInputBetweenInternal (DSPI *target, int inputindex, bool search, DSPConnectionI *connection, bool protect = true); + + virtual FMOD_RESULT remove (); + FMOD_RESULT removeInternal (bool protect = true); + + FMOD_RESULT getNumInputs (int *numinputs, bool protect = true); + FMOD_RESULT getNumOutputs (int *numoutputs, bool protect = true); + FMOD_RESULT getInput (int index, DSPI **input, DSPConnectionI **inputconnection = 0, bool protect = true); + FMOD_RESULT getOutput (int index, DSPI **output, DSPConnectionI **outputconnection = 0, bool protect = true); + + // DSP unit control + FMOD_INLINE FMOD_RESULT setActive (bool active) { active ? mFlags |= FMOD_DSP_FLAG_ACTIVE : mFlags &= ~FMOD_DSP_FLAG_ACTIVE; return FMOD_OK; } + FMOD_INLINE FMOD_RESULT getActive (bool *active) { mFlags & FMOD_DSP_FLAG_ACTIVE ? *active = true : *active = false; return FMOD_OK; } + FMOD_INLINE FMOD_RESULT setBypass (bool bypass) { bypass ? mFlags |= FMOD_DSP_FLAG_BYPASS : mFlags &= ~FMOD_DSP_FLAG_BYPASS; return FMOD_OK; } + FMOD_INLINE FMOD_RESULT getBypass (bool *bypass) { mFlags & FMOD_DSP_FLAG_BYPASS ? *bypass = true : *bypass = false; return FMOD_OK; } + FMOD_INLINE FMOD_RESULT setSpeakerActive (FMOD_SPEAKER speaker, bool active) { active ? speakermask |= (1 << speaker) : speakermask &= ~(1 << speaker); return FMOD_OK; } + FMOD_INLINE FMOD_RESULT getSpeakerActive (FMOD_SPEAKER speaker, bool *active) { speakermask & (1 << speaker) ? *active = true : *active = false; return FMOD_OK; } + + virtual FMOD_RESULT reset (); + + // DSP parameter control + virtual FMOD_RESULT setParameter (int index, float value); + virtual FMOD_RESULT getParameter (int index, float *value, char *valuestr, int valuestrlen); + virtual FMOD_RESULT getNumParameters (int *numparams); + virtual FMOD_RESULT getParameterInfo (int index, char *name, char *label, char *description, int descriptionlen, float *min, float *max); + virtual FMOD_RESULT showConfigDialog (void *hwnd, bool show); + + // DSP attributes. + virtual FMOD_RESULT getInfo (char *name, unsigned int *version, int *channels, int *configwidth, int *configheight); + virtual FMOD_RESULT getType (FMOD_DSP_TYPE *type); + virtual FMOD_RESULT setDefaults (float frequency, float volume, float pan, int priority); + virtual FMOD_RESULT getDefaults (float *frequency, float *volume, float *pan, int *priority); + virtual FMOD_RESULT stopBuffering (); + + // Userdata set/get. + FMOD_RESULT setUserData (void *userdata); + FMOD_RESULT getUserData (void **userdata); + +#ifdef FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS + FMOD_RESULT calculatePeaks (const float *buffer, unsigned int length, unsigned int numchannels, DSPI *t = 0); +#endif + FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + + virtual FMOD_RESULT setTargetFrequency (int frequency); + virtual FMOD_RESULT getTargetFrequency (int *frequency); + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) + { + tracker->add(false, FMOD_MEMBITS_DSP, ((DSPI *)dsp)->mDescription.mSize); + return FMOD_OK; + } +#endif + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + + FMOD_RESULT startBuffering(); + FMOD_RESULT getHistoryBuffer(float **buffer, unsigned int *position, unsigned int *length); + + #endif + }; +} + +#endif + + diff --git a/src/fmod_file.cpp b/src/fmod_file.cpp new file mode 100755 index 0000000..bf4e8a1 --- /dev/null +++ b/src/fmod_file.cpp @@ -0,0 +1,2274 @@ +#include "fmod_settings.h" + +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_string.h" +#include "fmod_stringw.h" +#include "fmod_thread.h" +#include "fmod_time.h" +#include "fmod_types.h" + +#ifdef PLATFORM_PS3_SPU + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" + #include "fmod_file_dma.h" +#else + #include "fmod_systemi.h" +#endif + +#ifdef PLATFORM_PS2_EE + #include "fmod_cmd.h" +#endif + +#ifdef FMOD_SUPPORT_NET + #include "fmod_file_net.h" +#endif + +#ifdef FMOD_SUPPORT_CDDA + #include "fmod_file_cdda.h" + #include "fmod_os_cdda.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#ifndef PLATFORM_PS3_SPU +extern "C" +{ + +/* +[API] +[ + [DESCRIPTION] + Mutex function to synchronize user file reads with FMOD's file reads. This function tells fmod that you are using the disk so that it will + block until you are finished with it.<br> + This function also blocks if FMOD is already using the disk, so that you cannot do a read at the same time FMOD is reading. + + [PARAMETERS] + 'busy' 1 = you are about to perform a disk access. 0 = you are finished with the disk. + + [RETURN_VALUE] + + [REMARKS] + Use this function as a wrapper around your own file reading functions if you want to do simulatenous file reading while FMOD is also reading. + ie + <PRE> + FMOD_File_SetDiskBusy(1); + myfread(...); + FMOD_File_SetDiskBusy(0); + </PRE> + Warning! This is a critical section internally. If you do not match your busy = true with a busy = false your program may hang!<br> + If you forget to set diskbusy to false it will stop FMOD from reading from the disk. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + File_GetDiskBusy +] +*/ +FMOD_RESULT F_API FMOD_File_SetDiskBusy(int busy) +{ + #ifdef PLATFORM_PS2_EE + { + FMOD_RESULT result; + struct + { + int busy; + } cmd = { busy }; + + result = FMOD_Command_Send(2, FMOD::FMOD_CMD_SETDISKBUSY, &cmd, sizeof(cmd), 0, 0, 0, true); + if (result != FMOD_OK) + { + return result; + } + } + #endif + + if (busy) + { + FMOD_OS_CriticalSection_Enter(FMOD::gGlobal->gFileCrit); + FMOD::gGlobal->gFileBusy = true; + } + else + { + FMOD::gGlobal->gFileBusy = false; + FMOD_OS_CriticalSection_Leave(FMOD::gGlobal->gFileCrit); + } + + return FMOD_OK; +} + + +/* +[API] +[ + [DESCRIPTION] + Information function to retreive the state of fmod's disk access. + + [PARAMETERS] + 'busy' Address of an integer to receive the busy state of the disk at the current time. + + [RETURN_VALUE] + + [REMARKS] + Do not use this function to syncrhonize your own reads with, as due to timing, you might call this function and it says false = it is not busy, + but the split second after call this function, internally FMOD might set it to busy. Use File_SetDiskBusy for proper mutual exclusion as it uses semaphores. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + File_SetDiskBusy +] +*/ +FMOD_RESULT F_API FMOD_File_GetDiskBusy(int *busy) +{ + if (!busy) + { + return FMOD_ERR_INVALID_PARAM; + } + + *busy = FMOD::gGlobal->gFileBusy; + + return FMOD_OK; +} + +} +#endif + +namespace FMOD +{ + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void fileThreadFunc(void *data) +{ + FileThread *fileThread = (FileThread *)data; + + fileThread->threadFunc(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FileThread::threadFunc() +{ + if (mThreadActive) + { + File *file; + + FMOD_OS_CriticalSection_Enter(mFileListCrit); + + mFileListCurrent = mFileListHead.getNext(); + + while (mFileListCurrent != &mFileListHead) + { + mFileListNext = mFileListCurrent->getNext(); + + file = SAFE_CAST(File, mFileListCurrent); + if (file->mFlags & FMOD_FILE_FLIPPING) + { + FMOD_OS_CriticalSection_Leave(mFileListCrit); + + file->flip(false); + + FMOD_OS_CriticalSection_Enter(mFileListCrit); + } + + mFileListCurrent = mFileListNext; + } + + FMOD_OS_CriticalSection_Leave(mFileListCrit); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FileThread::FileThread() +{ + mFileListCrit = 0; + mFileListCurrent = 0; + mFileListNext = 0; + mThreadActive = false; + mDeviceType = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FileThread::init(int devicetype, bool owned, SystemI *system) +{ + FMOD_RESULT result; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "FileThread::init", "created thread for %p\n", this)); + FLOG_INDENT(4); + + mDeviceType = devicetype; + mOwned = owned; + + result = FMOD_OS_CriticalSection_Create(&mFileListCrit); + if (result != FMOD_OK) + { + FLOG_INDENT(-4); + return result; + } + + result = mThread.initThread("FMOD file thread", fileThreadFunc, this, FILE_THREADPRIORITY, 0, FILE_STACKSIZE, true, 0, system); + if (result != FMOD_OK) + { + FLOG_INDENT(-4); + FMOD_OS_CriticalSection_Free(mFileListCrit); + return result; + } + + mThreadActive = true; + + addAfter(&FMOD::gGlobal->gFileThreadHead); + + FLOG_INDENT(-4); + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "FileThread::init", "done\n", this)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FileThread::release() +{ + removeNode(); + + mThreadActive = false; + + mThread.closeThread(); + + if (mFileListCrit) + { + FMOD_OS_CriticalSection_Free(mFileListCrit); + } + + FMOD_Memory_Free(this); + + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FileThread::release", "released thread for %p\n", this)); + + return FMOD_OK; +} + +#endif //!PLATFORM_PS3_SPU + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getFileThread() +{ + FMOD_RESULT result; + LinkedListNode *current; + int devicetype; + bool found, owned; + FileThread *filethread = 0; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::getFileThread", "creating file thread\n")); + + devicetype = DEVICE_DISK; + owned = false; + +#ifdef FMOD_SUPPORT_NET + if (!FMOD_strnicmp("http://", mName, 7)) + { + devicetype = DEVICE_NET; + owned = true; + } +#endif +#ifdef FMOD_SUPPORT_CDDA + if (FMOD_OS_CDDA_IsDeviceName(mName)) + { + devicetype = DEVICE_CDDA; + owned = true; + } +#endif + + found = false; + if (devicetype == DEVICE_DISK) + { + current = FMOD::gGlobal->gFileThreadHead.getNext(); + while (current != &FMOD::gGlobal->gFileThreadHead) + { + filethread = SAFE_CAST(FileThread, current); + + if (filethread->mDeviceType == DEVICE_DISK) + { + found = true; + break; + } + + current = current->getNext(); + } + } + + if (!found) + { + filethread = FMOD_Object_Alloc(FileThread); + if (!filethread) + { + return FMOD_ERR_MEMORY; + } + + result = filethread->init(devicetype, owned, mSystem); + if (result != FMOD_OK) + { + FMOD_Memory_Free(filethread); + return result; + } + } + + mFileThread = filethread; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::getFileThread", "done\n")); + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +File::File() +{ + mFlags = FMOD_FILE_BUFFERISSTRING | FMOD_FILE_SEEKABLE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::init(SystemI *system, unsigned int filesize, int blocksize) +{ + mBlockSize = blocksize; + mLength = 0; + mFileThread = 0; + mRiderHandle = 0; + mRiderUserData = 0; + mSystem = system; + mStartOffset = 0; + mBuffer = 0; + mBufferMemory = 0; +#ifdef PLATFORM_PS3 + mBufferSize = mBlockSize; +#else + mBufferSize = 0; +#endif + mCurrentPosition = 0; + mNextPosition = 0; + mNextPositionDisplay = 0; + mBlockOffset = 0; + mBufferPos = 0; + mBufferSkip = 0; + mAsyncError = FMOD_OK; + mEncryptionKeyLength = 0; + mEncryptionKeyIndex = 0; + mSema = 0; + mPercentBuffered = 0; + + mLengthOriginal = filesize; + mLength = filesize; + mFileSize = filesize; + + FMOD_memset(mName, 0, FMOD_STRING_MAXNAMELEN); + + return FMOD_OK; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::shutDown() +{ + LinkedListNode *current, *next; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::shutDown","\n")); + + current = FMOD::gGlobal->gFileThreadHead.getNext(); + while (current != &FMOD::gGlobal->gFileThreadHead) + { + FileThread *filethread; + + next = current->getNext(); + + filethread = SAFE_CAST(FileThread, current); + filethread->release(); + + current = next; + } + +#ifdef FMOD_SUPPORT_NET + NetFile::shutDown(); +#endif + +#ifdef FMOD_SUPPORT_CDDA + CddaFile::shutDown(); +#endif + + if (FMOD::gGlobal->gFileCrit) + { + FMOD_OS_CriticalSection_Free(FMOD::gGlobal->gFileCrit); + FMOD::gGlobal->gFileCrit = 0; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::shutDown","done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::open(const char *name_or_data, unsigned int length, bool unicode, const char *encryptionkey) +{ + FMOD_RESULT result; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::open", "name %s : length %d : unicode %s\n", name_or_data, length, unicode ? "YES" : "NO")); + + mFlags &= ~FMOD_FILE_BIGENDIAN; + mStartOffset = 0; + mCurrentPosition = 0; + mNextPosition = 0; + mNextPositionDisplay = 0; + mBlockOffset = 0; + mBufferPos = 0; + mBufferSkip = 0; + mAsyncError = FMOD_OK; + mFlags &= ~FMOD_FILE_EXIT; + mFlags &= ~FMOD_FILE_STARVING; + mEncryptionKeyLength = 0; + mEncryptionKeyIndex = 0; + + mLength = length; + mFileSize = length; + + unicode ? mFlags |= FMOD_FILE_UNICODE : mFlags &= ~FMOD_FILE_UNICODE; + + if (encryptionkey) + { + mEncryptionKeyLength = FMOD_strlen(encryptionkey); + if (mEncryptionKeyLength > 32) + { + mEncryptionKeyLength = 32; + } + + FMOD_memset(mEncryptionKey, 0, 32); + FMOD_strncpy(mEncryptionKey, encryptionkey, mEncryptionKeyLength); + } + + mBufferSize = mBlockSize; /* Blocking single buffer */ + if (mBufferSize && !mBuffer) + { + mBufferMemory = FMOD_Memory_Calloc(mBufferSize + FMOD_FILE_ALIGN); + if (!mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + + #if (FMOD_FILE_ALIGN > 0) + mBuffer = (void *)FMOD_ALIGNPOINTER(mBufferMemory, FMOD_FILE_ALIGN); + #else + mBuffer = mBufferMemory; + #endif + } + + result = reallyOpen(name_or_data, &mFileSize); + if (result != FMOD_OK) + { + if (mBufferMemory) + { + FMOD_Memory_Free(mBufferMemory); + mBuffer = mBufferMemory = 0; + } + return result; + } + + if (name_or_data && mFlags & FMOD_FILE_BUFFERISSTRING) + { + if (mFlags & FMOD_FILE_UNICODE) + { + FMOD_strncpyW((short*)mName, (short*)name_or_data, FMOD_STRING_MAXNAMELEN / 2); + FMOD_wtoa((short*)mName); + } + else + { + FMOD_strncpy(mName, name_or_data, FMOD_STRING_MAXNAMELEN); + } + mName[FMOD_STRING_MAXNAMELEN - 1] = 0; //null terminator + } + + #ifndef PLATFORM_PS3_SPU + if (mSystem && mSystem->mOpenRiderCallback) + { + mSystem->mOpenRiderCallback(name_or_data, unicode, &mFileSize, &mRiderHandle, &mRiderUserData); + } + #endif + + if (!mLength) + { + mLength = mFileSize; + } + mLengthOriginal = mLength; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::open", "done. handle %p. File size = %d\n", this, mLength)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::close() +{ + FMOD_RESULT result; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::close", "handle %p\n", this)); + + /* + Cancel any pending file requests. + This is needed for Wii / GC and to cancel net reads. + */ + cancel(); + + #ifndef PLATFORM_PS3_SPU + if (mSema && mFlags & FMOD_FILE_BUSY) + { + FMOD_OS_Semaphore_Wait(mSema); + FMOD_OS_Semaphore_Signal(mSema); + } + #endif + + if (mFileThread) + { + FMOD_OS_CriticalSection_Enter(mFileThread->mFileListCrit); + if (this == mFileThread->mFileListNext) + { + mFileThread->mFileListNext = getNext(); + } + removeNode(); + FMOD_OS_CriticalSection_Leave(mFileThread->mFileListCrit); + + if (mFileThread->mOwned) + { + mFileThread->release(); + } + + mFileThread = 0; + } + + result = reallyClose(); + + if (mSema) + { + FMOD_OS_Semaphore_Free(mSema); + } + + #ifndef PLATFORM_PS3_SPU + if (mSystem && mSystem->mCloseRiderCallback) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::close", "calling rider callback\n")); + + mSystem->mCloseRiderCallback(mRiderHandle, mRiderUserData); + } + #endif + + if (mBufferMemory) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::close", "free mBuffer (mBuffer = %p, mBufferMemory = %p)\n", mBuffer, mBufferMemory)); + FMOD_Memory_Free(mBufferMemory); + mBuffer = mBufferMemory = 0; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::close", "handle %p done\n", this)); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::cancel() +{ + mFlags |= FMOD_FILE_EXIT; + + return reallyCancel(); +} + +#endif // !PLATFORM_PS3_SPU + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::flip(bool frommainthread) +{ + FMOD_RESULT result = FMOD_OK; + + if (frommainthread && mSema) + { + FMOD_OS_Semaphore_Wait(mSema); + } + { + unsigned int bytestoread = mBlockSize - mBufferSkip; + unsigned int bytestoreadorig; + unsigned int bytesread = 0; + char *destptr = (char *)mBuffer + mBlockOffset + mBufferSkip; + + mFlags |= FMOD_FILE_BUSY; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::flip", "%p **** fill to %d with %d bytes. mBlockOffset %d mBufferSkip %d\n", this, mBlockOffset + mBufferSkip, bytestoread, mBlockOffset, mBufferSkip)); + + bytestoreadorig = bytestoread; + + while (bytestoread) + { + unsigned int read; + unsigned int r = 0; + + read = bytestoread; + + #ifdef PLATFORM_PS3_SPU + result = FMOD_DmaFile_ReadCallback(this, destptr, read, &r); + #else + result = reallyRead(destptr, read, &r); + #endif + if (result != FMOD_OK) + { + bytestoread = r; + + if ((int)mLength == -1 && result == FMOD_ERR_FILE_EOF) + { + if (mNextPositionDisplay) + { + mLength = mFileSize = mLengthOriginal = mNextPositionDisplay; + } + } + } + + #ifndef PLATFORM_PS3_SPU + if (mSystem && mSystem->mReadRiderCallback) + { + mSystem->mReadRiderCallback(mRiderHandle, destptr, r, 0, mRiderUserData); + } + #endif + + if (!r || r > bytestoread) + { + if (r > bytestoread) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "File::flip", " BAD LENGTH RETURNED FROM FILE READ FUNCTION, TERMINATING.\n")); + } + break; + } + + bytestoread -= r; + destptr += r; + bytesread += r; + mNextPositionDisplay += r; + + mPercentBuffered = (int)(((float)mNextPositionDisplay - (float)mCurrentPosition) / (float)mBufferSize * 100.0f); + if (mPercentBuffered < 0) + { + mPercentBuffered = 0; + } + + if (mFlags & FMOD_FILE_EXIT) + { + mFlags &= ~FMOD_FILE_EXIT; + break; + } + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::flip", "%p **** filled to %8d got %8d bytes\n", this, mBlockOffset + mBufferSkip, bytesread)); + + if (!mBlockOffset) + { + mFlags |= FMOD_FILE_FRONTBUFFERFULL; + } + else + { + mFlags |= FMOD_FILE_BACKBUFFERFULL; + } + + mBlockOffset += mBlockSize; + if (mBlockOffset >= mBufferSize) + { + mBlockOffset = 0; + } + + mBufferSkip = 0; + mAsyncError = result; + mFlags &= ~FMOD_FILE_FLIPPING; + } + + mFlags &= ~FMOD_FILE_BUSY; + + if (mSema) + { + FMOD_OS_Semaphore_Signal(mSema); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::seekAndReset() +{ + FMOD_RESULT result; + unsigned int alignedpos; + + #ifndef PLATFORM_PS3_SPU + if (mSema && mFlags & FMOD_FILE_BUSY) + { + FMOD_OS_Semaphore_Wait(mSema); + FMOD_OS_Semaphore_Signal(mSema); + } + #endif + + alignedpos = mCurrentPosition; + alignedpos /= mBufferSize; /* We are seeking resetting to the start of the WHOLE buffer */ + alignedpos *= mBufferSize; + + mBufferPos = mCurrentPosition - alignedpos; + mBlockOffset = 0; + mNextPosition = alignedpos; /* This will immediately be incremeneted by the flip */ + mNextPositionDisplay = mNextPosition; + mBufferSkip = 0; + + mFlags &= ~FMOD_FILE_FRONTBUFFERFULL; /* Reset state of buffers. */ + mFlags &= ~FMOD_FILE_BACKBUFFERFULL; + + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::seekAndReset", "%p seek to %d\n", this, alignedpos)); + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::seekAndReset", "%p reset mBufferPos to %d\n", this, mBufferPos)); + + #ifdef PLATFORM_PS3_SPU + + result = FMOD_DmaFile_SeekCallback(this, alignedpos); + + #else + + result = reallySeek(alignedpos); + + #endif + + #ifndef PLATFORM_PS3_SPU + if (mSystem && mSystem->mSeekRiderCallback) + { + mSystem->mSeekRiderCallback(mRiderHandle, alignedpos, mRiderUserData); + } + #endif + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::checkBufferedStatus() +{ + FMOD_RESULT result; + int diff; + + #ifndef PLATFORM_PS3_SPU + + if (mAsyncError != FMOD_OK && mAsyncError != FMOD_ERR_FILE_EOF && mAsyncError != FMOD_ERR_FILE_DISKEJECTED) + { + return mAsyncError; + } + + #endif + + if (mNextPosition < mCurrentPosition || /* If the position after the buffer */ + (mBufferSize > mBlockSize && !(mFlags & (FMOD_FILE_BACKBUFFERFULL | FMOD_FILE_FLIPPING)) && mCurrentPosition < mNextPosition - mBlockSize)) /* or behind the buffer. */ + { + diff = -1; + } + else + { + mPercentBuffered = (int)(((float)mNextPositionDisplay - (float)mCurrentPosition) / (float)mBufferSize * 100.0f); + if (mPercentBuffered < 0 || mBufferSkip) + { + mPercentBuffered = 0; + } + + diff = mNextPosition - mCurrentPosition; + diff += (mBlockSize - 1); + diff /= mBlockSize; + } + + if (mBufferSkip) + { + if (diff > 2) + { + mBufferSkip = 0; + } + else + { + diff = -1; /* We want to do a blocking read here. Not a background flip. -1 stops it from going into first 2 if statements. */ + mNextPositionDisplay = mBufferSkip; + mNextPosition = mCurrentPosition - mBufferPos; + } + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::checkBufferedStatus", "%p mCurrentPosition %d mNextPosition %d nextpos diffbytes %d diff %d\n", this, mCurrentPosition, mNextPosition, mNextPosition - mCurrentPosition, diff)); + + if (diff != 2) + { + #ifndef PLATFORM_PS3_SPU + if (mFlags & FMOD_FILE_BUSY) + { + mFlags |= FMOD_FILE_STARVING; + + FMOD_OS_Semaphore_Wait(mSema); + FMOD_OS_Semaphore_Signal(mSema); + + mFlags &= ~FMOD_FILE_STARVING; + } + #endif + } + + /* + If diff = 1, then we are reading from the front buffer, so non blocking fill the back buffer. + */ + if(0) + { + } +#ifndef PLATFORM_PS3_SPU + else if (diff == 1 && mBufferSize > mBlockSize) /* This case is only for double buffered (buffsize > blocksize) */ + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::checkBufferedStatus", "%p issue non blocking flip\n", this)); + + mFlags |= FMOD_FILE_BUSY; + mFlags |= FMOD_FILE_FLIPPING; + + FMOD_OS_Semaphore_Wait(mSema); /* ENTER */ + + mFileThread->mThread.wakeupThread(); + + mNextPositionDisplay = mNextPosition; + mNextPosition += mBlockSize; + + return FMOD_OK; + } +#endif + /* + If diff = 2, then we are reading from the front buffer and the fill is more than 1 ahead, so there is no current danger of undderun. + This means we can just exit. + */ + else if ((mBufferSize > mBlockSize && diff == 2) || (mBufferSize == mBlockSize && diff == 1)) /* This is the idle case. If it is doublebuffered the diff must be 2, or if it is single buffered the diff must be 1 */ + { + return FMOD_OK; + } + /* + If diff = 0, or diff = something large (ie there was a seek), do a blocking read. + */ + else + { + if (diff && mFlags & FMOD_FILE_SEEKABLE) /* was there a seek */ + { + result = seekAndReset(); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Fill the front buffer. + */ + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::checkBufferedStatus", "%p FORCIBLY FILL FRONTBUFFER\n", this)); + + result = flip(true); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + if (result == FMOD_ERR_FILE_DISKEJECTED) + { + mFlags |= FMOD_FILE_STARVING; + } + return result; + } + mFlags &= ~FMOD_FILE_STARVING; + + if (mBufferSize == mBlockSize && result == FMOD_ERR_FILE_EOF && (int)mLength == -1) + { + return result; + } + + mNextPositionDisplay = mNextPosition; + mNextPosition += mBlockSize; + + /* + Fill the back buffer. + */ + if (mBufferPos >= mBlockSize) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::checkBufferedStatus", "%p FORCIBLY FILL BACKBUFFER\n", this)); + + result = flip(true); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + if (result == FMOD_ERR_FILE_DISKEJECTED) + { + mFlags |= FMOD_FILE_STARVING; + } + return result; + } + mFlags &= ~FMOD_FILE_STARVING; + + mNextPositionDisplay = mNextPosition; + mNextPosition += mBlockSize; + } + + return result; + } + + return FMOD_ERR_INTERNAL; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::read(void *buffer, unsigned int size, unsigned int count, unsigned int *rd) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int oldsize = size; + unsigned int bytesleft; + unsigned int r = 0; + bool eof = false; + + if (!buffer) + { + return FMOD_ERR_INVALID_PARAM; + } + + if ((int)(size * count) < 0) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_FILE | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "File::read", "Tried to read %d bytes\n", size * count)); + return FMOD_ERR_INVALID_PARAM; + } + + mFlags &= ~FMOD_FILE_EXIT; + + size *= count; + + if ((mCurrentPosition + size) > mStartOffset + mLength) + { + if (mCurrentPosition <= mStartOffset + mLength) + { + size = (mStartOffset + mLength) - mCurrentPosition; + } + else + { + size = 0; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "File::read", "(mCurrentPosition + size) > mStartOffset + mLength\n")); + return FMOD_ERR_FILE_BAD; + } + eof = true; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p----> want to read %d\n", this, size)); + + bytesleft = size; + + while (bytesleft) + { + size = bytesleft; + + #ifndef PLATFORM_PS3_SPU + /* + Direct read - single buffered only. + */ + if ((mBlockSize == mBufferSize && size > mBlockSize && !mBufferPos && mFlags & FMOD_FILE_SEEKABLE) + #if defined(PLATFORM_GC) || defined(PLATFORM_WII) + && + ((mDeviceType == DEVICE_DISK || mDeviceType == DEVICE_USER) ? (((FMOD_UINT_NATIVE)((char *)buffer + r) & (FMOD_FILE_ALIGN - 1)) ? false : true) : true) /* Dest must be 32 byte aligned (if it is a disk file) */ + #endif + ) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p mCurrentPosition %d mNextPosition %d nextpos diffbytes %d\n", this, mCurrentPosition, mNextPosition, mNextPosition - mCurrentPosition)); + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p DIRECT READ want %d bytes\n", this, size)); + + if (mBlockSize) + { + if (mCurrentPosition != mNextPosition) + { + result = seekAndReset(); + if (result != FMOD_OK) + { + return result; + } + } + + size /= mBlockSize; + size *= mBlockSize; + } + + #ifdef PLATFORM_PS3_SPU + result = FMOD_DmaFile_ReadCallback(this, (char *)buffer + r, size, &size); + #else + result = reallyRead((char *)buffer + r, size, &size); + #endif + + mFlags &= ~FMOD_FILE_BUSY; + + #ifndef PLATFORM_PS3_SPU + if (mSystem && mSystem->mReadRiderCallback) + { + mSystem->mReadRiderCallback(mRiderHandle, (char *)buffer + r, size, 0, mRiderUserData); + } + #endif + + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p DIRECT READ got %d bytes\n", this, size)); + + mNextPositionDisplay = mNextPosition; + mNextPosition += size; + + if (!size) + { + result = FMOD_ERR_FILE_EOF; + } + + if (result == FMOD_ERR_FILE_EOF) + { + break; + } + } + + /* + Buffered read - single or double buffered. + */ + else + #endif + { + result = checkBufferedStatus(); + + if (result == FMOD_ERR_FILE_EOF && !(mBlockSize == mBufferSize && (int)mLength == -1)) + { + result = FMOD_OK; /* We dont care about the EOF for the flip status, we determine EOF ourselves. */ + } + if (result != FMOD_OK) + { + break; + } + + if (size > (mBlockSize - (mBufferPos % mBlockSize))) + { + size = mBlockSize - (mBufferPos % mBlockSize); + } + + FMOD_memcpy((char *)buffer + r, (char *)mBuffer + mBufferPos, size); + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p copied %d bytes from mBufferPos = %d\n", this, size, mBufferPos)); + + mBufferPos += size; + if (mBufferPos >= mBufferSize) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p buffer wrap\n", this)); + mBufferPos = 0; + } + } + + mCurrentPosition += size; + bytesleft -= size; + r += size; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::read", "%p<---- done\n", this)); + + if (oldsize == 2) + { + unsigned short *wptr = (unsigned short *)buffer; + unsigned int i; + + #ifdef PLATFORM_ENDIAN_LITTLE + if (mFlags & FMOD_FILE_BIGENDIAN) + #else + if (!(mFlags & FMOD_FILE_BIGENDIAN)) + #endif + { + for (i=0; i < (r / oldsize); i++) + { + wptr[i] = FMOD_SWAPENDIAN_WORD(wptr[i]); + } + } + } + else if (oldsize == 4) + { + unsigned int *dptr = (unsigned int *)buffer; + unsigned int i; + + #ifdef PLATFORM_ENDIAN_LITTLE + if (mFlags & FMOD_FILE_BIGENDIAN) + #else + if (!(mFlags & FMOD_FILE_BIGENDIAN)) + #endif + { + for (i=0; i < (r / oldsize); i++) + { + dptr[i] = FMOD_SWAPENDIAN_DWORD(dptr[i]); + } + } + } + + r /= oldsize; + + if (mEncryptionKeyLength) + { + unsigned char *val = (unsigned char *)buffer; + unsigned int count; + + if (mFlags & FMOD_FILE_USEOLDDECRYPT) + { + for (count = 0; count < r; count++) + { + FMOD_DECRYPT_OLD(val[count], mEncryptionKey[mEncryptionKeyIndex]); + + mEncryptionKeyIndex++; + if (mEncryptionKeyIndex >= mEncryptionKeyLength) + { + mEncryptionKeyIndex = 0; + } + } + } + else + { + for (count = 0; count < r; count++) + { + FMOD_DECRYPT(val[count], mEncryptionKey[mEncryptionKeyIndex]); + + mEncryptionKeyIndex++; + if (mEncryptionKeyIndex >= mEncryptionKeyLength) + { + mEncryptionKeyIndex = 0; + } + } + } + } + + if (rd) + { + *rd = r; + } + + if (result == FMOD_OK && eof) + { + result = FMOD_ERR_FILE_EOF; + } + + return result; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getByte(unsigned char *val) +{ + FMOD_RESULT result; + unsigned int rd; + unsigned char temp; + + result = read(&temp, 1, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getByte(unsigned short *val) +{ + FMOD_RESULT result; + unsigned int rd; + unsigned char temp; + + result = read(&temp, 1, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getByte(unsigned int *val) +{ + FMOD_RESULT result; + unsigned int rd; + unsigned char temp; + + result = read(&temp, 1, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getByte(signed char *val) +{ + FMOD_RESULT result; + unsigned int rd; + signed char temp; + + result = read(&temp, 1, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getByte(signed short *val) +{ + FMOD_RESULT result; + unsigned int rd; + signed char temp; + + result = read(&temp, 1, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getByte(signed int *val) +{ + FMOD_RESULT result; + unsigned int rd; + signed char temp; + + result = read(&temp, 1, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getWord(unsigned short *val) +{ + FMOD_RESULT result; + unsigned int rd; + unsigned short temp; + + result = read(&temp, 2, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getWord(unsigned int *val) +{ + FMOD_RESULT result; + unsigned int rd; + unsigned short temp; + + result = read(&temp, 2, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getWord(signed short *val) +{ + FMOD_RESULT result; + unsigned int rd; + signed short temp; + + result = read(&temp, 2, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getWord(signed int *val) +{ + FMOD_RESULT result; + unsigned int rd; + signed short temp; + + result = read(&temp, 2, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getDword(unsigned int *val) +{ + FMOD_RESULT result; + unsigned int rd; + unsigned int temp; + + result = read(&temp, 4, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getDword(signed int *val) +{ + FMOD_RESULT result; + unsigned int rd; + signed int temp; + + result = read(&temp, 4, 1, &rd); + + if (val) + { + *val = temp; + } + + return result; +} + +#endif // !PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::seek(int pos, int mode) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int newpos = 0; + int delta; + + if (mode != SEEK_SET && mode != SEEK_CUR && mode != SEEK_END) + { + return FMOD_ERR_INVALID_PARAM; + } + + mFlags &= ~FMOD_FILE_EXIT; + + if (mode == SEEK_SET) + { + newpos = mStartOffset + pos; + } + else if (mode == SEEK_CUR) + { + newpos = mCurrentPosition + pos; + } + else if (mode == SEEK_END) + { + newpos = mStartOffset + mLength + pos; + } + + if (newpos > mStartOffset + mLength) + { + if (pos >= 0) + { + newpos = mStartOffset + mLength; + } + else + { + newpos = 0; + } + } + + /* + If no read has been done and not seekable and we are trying to seek beyond the first (future) read size, return an error. + */ + if (!mNextPosition && !(mFlags & FMOD_FILE_SEEKABLE) && newpos >= mBlockSize) + { + return FMOD_ERR_FILE_COULDNOTSEEK; + } + + /* + Single buffered, not seekable, dont allow seeks outside the current buffer + */ + if ((mBlockSize == mBufferSize) && !(mFlags & FMOD_FILE_SEEKABLE) && (mNextPosition >= mBlockSize)) + { + if (mNextPosition && newpos < mNextPosition - mBlockSize) + { + return FMOD_ERR_FILE_COULDNOTSEEK; + } + if (newpos >= mNextPosition + mBlockSize) + { + return FMOD_ERR_FILE_COULDNOTSEEK; + } + } + + delta = newpos - mCurrentPosition; + + mCurrentPosition = newpos; + + if (mEncryptionKeyLength) + { + mEncryptionKeyIndex = mCurrentPosition % mEncryptionKeyLength; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::seek", "%p seek %d bytes to %d\n",this, delta, newpos)); + + if (mBufferSize) + { + mBufferPos = mCurrentPosition % mBufferSize; /* We are seeking resetting to the start of the WHOLE buffer */ + } + else + { + #ifdef PLATFORM_PS3_SPU + + result = FMOD_DmaFile_SeekCallback(this, newpos); + + #else + + result = reallySeek(newpos); + + if (mSystem && mSystem->mSeekRiderCallback) + { + mSystem->mSeekRiderCallback(mRiderHandle, newpos, mRiderUserData); + } + + #endif + } + + return result; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::tell(unsigned int *pos) +{ + FMOD_RESULT result = FMOD_OK; + + if (!pos) + { + return FMOD_ERR_INVALID_PARAM; + } + + mFlags &= ~FMOD_FILE_EXIT; + + *pos = mCurrentPosition; + *pos -= mStartOffset; + + return result; +} + +#ifndef PLATFORM_PS3_SPU + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::setStartOffset(unsigned int offset) +{ + mStartOffset = offset; + + mLength = mLengthOriginal; + + if (mStartOffset + mLength > mFileSize) + { + mLength = mFileSize - mStartOffset; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getStartOffset(unsigned int *offset) +{ + if (!offset) + { + return FMOD_ERR_INVALID_PARAM; + } + + *offset = mStartOffset; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::enableDoubleBuffer(unsigned int sizebytes, void *oldbuffer) +{ + FMOD_RESULT result; + unsigned int oldblocksize, oldbuffersize; + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::enableDoubleBuffer", "%p buffersize = %d bytes\n", this, sizebytes)); + + if (!mBlockSize) + { + return FMOD_OK; + } + + #ifndef PLATFORM_PS3_SPU + result = FMOD_OS_Semaphore_Create(&mSema); + if (result != FMOD_OK) + { + return result; + } + FMOD_OS_Semaphore_Signal(mSema); /* Put it up to 1 */ + #endif + + if (sizebytes < FILE_SECTORSIZE) + { + sizebytes = FILE_SECTORSIZE; + } + if (sizebytes < mBlockSize) + { + sizebytes = mBlockSize; /* Can't go smaller, we can only go bigger */ + } + + oldblocksize = mBlockSize; + oldbuffersize = mBufferSize; + + mBlockSize = sizebytes; + mBlockSize /= oldblocksize; + mBlockSize *= oldblocksize; + + mBufferSkip = oldblocksize; /* This is for the next file flip, it will skip what we already have in the older buffer */ + mBlockOffset = 0; + mNextPosition = mBlockSize; + mNextPositionDisplay = mNextPosition; + + mBufferSize = mBlockSize; + mBufferSize *= 2; /* Make it a double buffer */ + + if (oldbuffer) + { + mBufferMemory = FMOD_Memory_CallocType(mBufferSize + FMOD_FILE_ALIGN, FMOD_MEMORY_STREAM_FILE); + if (!mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + FMOD_memcpy(mBufferMemory, oldbuffer, oldbuffersize); + } + else + { + mBufferMemory = FMOD_Memory_ReAllocType(mBufferMemory, mBufferSize + FMOD_FILE_ALIGN, FMOD_MEMORY_STREAM_FILE); + if (!mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + } + + #if (FMOD_FILE_ALIGN > 0) + mBuffer = (void *)FMOD_ALIGNPOINTER(mBufferMemory, FMOD_FILE_ALIGN); + #else + mBuffer = mBufferMemory; + #endif + + + result = getFileThread(); + if (result != FMOD_OK) + { + return result; + } + + FMOD_OS_CriticalSection_Enter(mFileThread->mFileListCrit); + addAfter(&mFileThread->mFileListHead); + FMOD_OS_CriticalSection_Leave(mFileThread->mFileListCrit); + + #if 0 + { + FILE *fp; + unsigned int offset = 0; + + printf("-------------------------------------------------------\n"); + + fp = fopen(mName, "rb"); + + srand(1); + + do + { + unsigned char buff1[4096], buff2[4096]; + unsigned int size; + + offset = 0; //FMOD_RAND() % mLength; + size = 1; //FMOD_RAND() % 4096; + + fseek(fp, offset, SEEK_SET); + fread(buff1, size, 1, fp); + + seek(offset, SEEK_SET); + read(buff2, size, 1, 0); + + if (memcmp(buff1, buff2, size)) + { + size = size; + } + + offset++; + } while (offset < mStartOffset + mLength); + } + #endif + + result = checkBufferedStatus(); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "File::enableDoubleBuffer", "%p done\n", this)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::getName(char **name) +{ + if (name) + { + *name = mName; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT File::setName(char *name) +{ + if (!name) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_strncpy(mName, name, FMOD_STRING_MAXNAMELEN); + mName[FMOD_STRING_MAXNAMELEN - 1] = 0; //null terminator + + return FMOD_OK; +} + +#endif // !PLATFORM_PS3_SPU + + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT File::getMemoryUsedImpl(MemoryTracker *tracker) +{ + #ifndef PLATFORM_PS3_SPU + if (mSema) + { + tracker->add(false, FMOD_MEMBITS_FILE, gSizeofSemaphore); + } + + tracker->add(false, FMOD_MEMBITS_FILE, mBufferSize + FMOD_FILE_ALIGN); /* mSyncPointHead & mSyncPointTail */ + #endif + + return FMOD_OK; +} +#endif + + +/* +[API] +[ + [DESCRIPTION] + Callback for opening a file. + + [PARAMETERS] + 'name' This is the filename passed in by the user. You may treat this as you like. + 'unicode' Tells the callback if the string being passed in is a double byte unicode string or not. You may have to support this unless you know the target application will not support unicode. + 'filesize' The size of the file to be passed back to fmod, in bytes. + 'handle' This is to store a handle generated by the user. This will be the handle that gets passed into the other callbacks. Optional but may be needed. + 'userdata' This is to store userdata to be passed into the other callbacks. Optional. + + [RETURN_VALUE] + + [REMARKS] + Return the appropriate error code such as FMOD_ERR_FILE_NOTFOUND if the file fails to open. + If the callback is from System::attachFileSystem, then the return value is ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setFileSystem + System::attachFileSystem + FMOD_FILE_CLOSECALLBACK + FMOD_FILE_READCALLBACK + FMOD_FILE_SEEKCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_FILE_OPENCALLBACK(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Calback for closing a file. + + [PARAMETERS] + 'handle' This is the handle returned from the open callback to use for your own file routines. + 'userdata' Userdata initialized in the FMOD_FILE_OPENCALLBACK. + + [RETURN_VALUE] + + [REMARKS] + Close any user created file handle and perform any cleanup nescessary for the file here. + If the callback is from System::attachFileSystem, then the return value is ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setFileSystem + System::attachFileSystem + FMOD_FILE_OPENCALLBACK + FMOD_FILE_READCALLBACK + FMOD_FILE_SEEKCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_FILE_CLOSECALLBACK(void *handle, void *userdata); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Callback for reading from a file. + + [PARAMETERS] + 'handle' This is the handle you returned from the open callback to use for your own file routines. + 'buffer' The buffer to read your data into. + 'sizebytes' The number of bytes to read. + 'bytesread' The number of bytes successfully read. + 'userdata' Userdata initialized in the FMOD_FILE_OPENCALLBACK. + + [RETURN_VALUE] + + [REMARKS] + If the callback is from System::attachFileSystem, then the return value is ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setFileSystem + System::attachFileSystem + FMOD_FILE_OPENCALLBACK + FMOD_FILE_CLOSECALLBACK + FMOD_FILE_SEEKCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_FILE_READCALLBACK(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Callback for seeking within a file. + + [PARAMETERS] + 'handle' This is the handle returned from the open callback to use for your own file routines. + 'pos' This is the position or offset to seek to in the file in bytes. + 'userdata' Data initialized in the FMOD_FILE_OPENCALLBACK. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::setFileSystem + FMOD_FILE_OPENCALLBACK + FMOD_FILE_CLOSECALLBACK + FMOD_FILE_READCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_FILE_SEEKCALLBACK(void *handle, unsigned int pos, void *userdata); +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +} diff --git a/src/fmod_file.h b/src/fmod_file.h new file mode 100755 index 0000000..5b4bd5a --- /dev/null +++ b/src/fmod_file.h @@ -0,0 +1,212 @@ +#ifndef _FMOD_FILE_H +#define _FMOD_FILE_H + +#include "fmod_settings.h" + +#include "fmod_linkedlist.h" +#include "fmod_memory.h" +#include "fmod_metadata.h" +#include "fmod_os_misc.h" +#include "fmod_time.h" + +//#ifndef PLATFORM_PS3_SPU + #include "fmod_thread.h" +//#endif + +#define FMOD_FILE_QUEUEREAD 0 +#define FMOD_FILE_QUEUESEEK 1 +#define FMOD_FILE_QUEUEMAX 64 /* Must be a power of 2 */ + +#ifdef PLATFORM_PS2 + #ifndef SEEK_SET + #define SEEK_SET (0) + #endif + #ifndef SEEK_CUR + #define SEEK_CUR (1) + #endif + #ifndef SEEK_END + #define SEEK_END (2) + #endif +#endif + +#define PS3_DMAFILE_BLOCKSIZE 256 + +namespace FMOD +{ + class File; + + const unsigned int FILE_SECTORSIZE = (2*1024); /* default. CD sector size */ + + typedef struct + { + int mType; + File *mHandle; + void *mReadBuff; + int mReadSize; + int mReadCount; + int mSeekPos; + signed char mSeekMode; + } FileQueue; + + enum + { + DEVICE_USER = 0, + DEVICE_MEMORY, + DEVICE_RECORD, + DEVICE_NET, + DEVICE_CDDA, + DEVICE_DISK + }; + + #define FMOD_FILE_SEEKABLE 0x00000001 + #define FMOD_FILE_BUFFERISSTRING 0x00000002 + #define FMOD_FILE_UNICODE 0x00000004 + #define FMOD_FILE_BIGENDIAN 0x00000008 + #define FMOD_FILE_BUSY 0x00000010 + #define FMOD_FILE_STARVING 0x00000020 + #define FMOD_FILE_EXIT 0x00000040 + #define FMOD_FILE_FLIPPING 0x00000080 + #define FMOD_FILE_FRONTBUFFERFULL 0x00000100 + #define FMOD_FILE_BACKBUFFERFULL 0x00000200 + #define FMOD_FILE_USEOLDDECRYPT 0x00000400 + #define FMOD_FILE_STREAMING 0x00000800 /* PS3 - If using FIOS, we can set streaming file reads to higher priority */ + + #if defined(PLATFORM_WII) || defined(PLATFORM_GC) + #define FMOD_FILE_ALIGN 32 + #else + #define FMOD_FILE_ALIGN 0 + #endif + + class FileThread : public LinkedListNode /* This linked list node entry is for File::gFileThreadHead */ + { +#ifndef PLATFORM_PS3_SPU + public : + + Thread mThread; + bool mThreadActive; + LinkedListNode mFileListHead; + LinkedListNode *mFileListCurrent; + LinkedListNode *mFileListNext; + FMOD_OS_CRITICALSECTION *mFileListCrit; + int mDeviceType; + bool mOwned; + + FMOD_RESULT threadFunc(); + + FileThread(); + + FMOD_RESULT init(int devicetype, bool owned, SystemI *system); + FMOD_RESULT release(); +#endif + }; + + class File : public LinkedListNode /* This linked list node entry is for FileThread::mHead */ + { + DECLARE_MEMORYTRACKER + + friend class FileThread; + + protected: + + unsigned int mLength; + unsigned int mLengthOriginal; + unsigned int mFileSize; + void *mRiderUserData; + void *mRiderHandle; + int mDeviceType; + + private: + + FMOD_PPCALIGN16(char mName[FMOD_STRING_MAXNAMELEN]); + FMOD_PPCALIGN16(char mEncryptionKey[32]); + FMOD_PPCALIGN16(int mEncryptionKeyLength); + FMOD_PPCALIGN16(int mEncryptionKeyIndex); + FMOD_PPCALIGN16(unsigned int mBufferPos); + FMOD_PPCALIGN16(unsigned int mBufferSize); + FMOD_PPCALIGN16(unsigned int mBufferSkip); + FMOD_PPCALIGN16(unsigned int mBlockSize); + FMOD_PPCALIGN16(unsigned int mBlockOffset); + FMOD_PPCALIGN16(unsigned int mCurrentPosition); + FMOD_PPCALIGN16(unsigned int mNextPosition); + FMOD_PPCALIGN16(unsigned int mNextPositionDisplay); + FMOD_PPCALIGN16(unsigned int mStartOffset); + FMOD_PPCALIGN16(int mPercentBuffered); + FMOD_PPCALIGN16(FMOD_RESULT mAsyncError); + FMOD_PPCALIGN16(FileThread *mFileThread); + FMOD_PPCALIGN16(FMOD_OS_SEMAPHORE *mSema); + + FMOD_RESULT flip(bool frommainthread); + FMOD_RESULT checkBufferedStatus(); + FMOD_RESULT seekAndReset(); +#ifndef PLATFORM_PS3_SPU + FMOD_RESULT getFileThread(); +#endif + + public: + + unsigned int mFlags; + SystemI *mSystem; + + #ifdef PLATFORM_PS3 + char mBufferMemoryPS3[PS3_DMAFILE_BLOCKSIZE] __attribute__((aligned(16))); + #endif + + void *mBuffer; + void *mBufferMemory; + + File(); + + FMOD_RESULT init(SystemI *system, unsigned int size, int blocksize); + + FMOD_RESULT getByte(unsigned char *byte = 0); + FMOD_RESULT getByte(unsigned short *byte); + FMOD_RESULT getByte(unsigned int *byte); + FMOD_RESULT getByte(signed char *byte); + FMOD_RESULT getByte(signed short *byte); + FMOD_RESULT getByte(signed int *byte); + FMOD_RESULT getWord(unsigned short *word = 0); + FMOD_RESULT getWord(unsigned int *word); + FMOD_RESULT getWord(signed short *word); + FMOD_RESULT getWord(signed int *word); + FMOD_RESULT getDword(unsigned int *dword = 0); + FMOD_RESULT getDword(signed int *dword); + + FMOD_RESULT setStartOffset(unsigned int offset); + FMOD_RESULT getStartOffset(unsigned int *offset); + FMOD_RESULT setBuffer(void *buffer) { mBuffer = buffer; return FMOD_OK; } + FMOD_RESULT enableDoubleBuffer(unsigned int sizebytes, void *oldbuffer = 0); + + FMOD_RESULT getName(char **name); + FMOD_RESULT setName(char *name); + FMOD_RESULT setBigEndian(bool bigendian) { bigendian ? mFlags |= FMOD_FILE_BIGENDIAN : mFlags &= ~FMOD_FILE_BIGENDIAN; return FMOD_OK; } + FMOD_RESULT isBusy(bool *isbusy, unsigned int *percent) { if (isbusy) *isbusy = (mFlags & FMOD_FILE_BUSY ? true : false); if (percent) *percent = mPercentBuffered; return FMOD_OK; } + FMOD_RESULT isStarving(bool *starving) { if (!starving) return FMOD_ERR_INVALID_PARAM; *starving = (mFlags & FMOD_FILE_STARVING ? true : false); return FMOD_OK; } + + virtual FMOD_RESULT getMetadata(Metadata **metadata) { return FMOD_ERR_TAGNOTFOUND; } + virtual FMOD_RESULT getSize(unsigned int *size) { *size = mLength; return FMOD_OK; } + virtual FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize) = 0; + virtual FMOD_RESULT reallyClose() = 0; + virtual FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read) = 0; + virtual FMOD_RESULT reallySeek(unsigned int pos) = 0; + virtual FMOD_RESULT reallyCancel() { return FMOD_OK; } + + FMOD_RESULT open(const char *name_or_data, unsigned int length, bool unicode = false, const char *encryptionkey = 0); + FMOD_RESULT close(); + FMOD_RESULT cancel(); + FMOD_RESULT read(void *buffer, unsigned int size, unsigned int count, unsigned int *read = 0); + FMOD_RESULT seek(int pos, int mode); + FMOD_RESULT tell(unsigned int *pos); + + protected: + + friend class SystemI; + + #ifndef PLATFORM_PS3_SPU + static FMOD_RESULT shutDown(); + #endif + }; +} + +#endif + + diff --git a/src/fmod_file_cdda.h b/src/fmod_file_cdda.h new file mode 100755 index 0000000..46b25ea --- /dev/null +++ b/src/fmod_file_cdda.h @@ -0,0 +1,71 @@ +#ifndef _FMOD_FILE_CDDA_H +#define _FMOD_FILE_CDDA_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CDDA + +#include "fmod_file.h" +#include "fmod_linkedlist.h" + +namespace FMOD +{ + typedef struct FMOD_CDDA_DEVICE FMOD_CDDA_DEVICE; + + class CddaFile : public File + { + DECLARE_MEMORYTRACKER + + private: + + FMOD_CDDA_DEVICE *mDevice; + char *mReadBuf; + char *mReadPtr; + unsigned int mReadSizebytes; + unsigned int mStartSector; + unsigned int mCurrentSector; + unsigned int mSectorsInChunk; + unsigned int mSectorsLeft; + char *mJitterBuf; + unsigned int mJitterBufSectors; + bool mJitterBufEmpty; + bool mJitterCorrection; + unsigned int mLastTimeAccessed; + bool mGotUserToc; + Metadata mMetadata; + + int mCurrentTrack; + char **mTrackFiles; + void *mTrackHandle; + + public: + + CddaFile(); + + FMOD_RESULT init(bool force_aspi, bool jitter_correction); + + FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize); + FMOD_RESULT reallyClose(); + FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT reallySeek(unsigned int pos); + + FMOD_RESULT getNumTracks(int *numtracks); + FMOD_RESULT getTrackLength(unsigned int track, unsigned int *tracklength); + FMOD_RESULT openTrack(unsigned int track); + FMOD_RESULT doJitterCorrection(unsigned int sectors_to_read); + + FMOD_RESULT getMetadata(Metadata **metadata); + + protected: + + friend class File; + + static FMOD_RESULT shutDown(); + }; +} + +#endif + +#endif + + diff --git a/src/fmod_file_disk.cpp b/src/fmod_file_disk.cpp new file mode 100755 index 0000000..e3fc3b8 --- /dev/null +++ b/src/fmod_file_disk.cpp @@ -0,0 +1,227 @@ +#include "fmod_settings.h" + +#include "fmod_debug.h" +#include "fmod_file_disk.h" +#include "fmod_os_misc.h" +#include "fmod_stringw.h" +#include "fmod_systemi.h" + +#include <stdio.h> +#include <stdlib.h> + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DiskFile::reallyOpen(const char *name_or_data, unsigned int *filesize) +{ + FMOD_RESULT result; + char path[2048]; + + if (mFlags & FMOD_FILE_UNICODE) + { + if (!FMOD_strlenW((short *)name_or_data)) + { + return FMOD_ERR_FILE_NOTFOUND; + } + } + else + { + if (!FMOD_strlen(name_or_data)) + { + return FMOD_ERR_FILE_NOTFOUND; + } + } + + /* + Expand relative path to absolute path so we can work out what drive this file is on later on + */ + +#ifdef PLATFORM_WINDOWS //!! _fullpath() not supported on xbox + if (!_fullpath(path, name_or_data, 2047)) + { + return FMOD_ERR_INVALID_PARAM; + } +#else + FMOD_strcpy(path, name_or_data); +#endif + + result = setName(path); + if (result != FMOD_OK) + { + return result; + } + +#ifdef PLATFORM_PS3 + if (mFlags & FMOD_FILE_STREAMING) + { + result = FMOD_OS_File_Open((const char *)name_or_data, (char *)"s", mFlags & FMOD_FILE_UNICODE ? 1 : 0, filesize, &mHandle); + } + else +#endif + { + result = FMOD_OS_File_Open((const char *)name_or_data, (char *)"rb", mFlags & FMOD_FILE_UNICODE ? 1 : 0, filesize, &mHandle); + } + + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "DiskFile::reallyOpen", "Call to open failed\n")); + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DiskFile::reallyClose() +{ + FMOD_RESULT result = FMOD_OK; + + if (mHandle) + { + result = FMOD_OS_File_Close(mHandle); + mHandle = 0; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DiskFile::reallyRead(void *buffer, unsigned int size, unsigned int *rd) +{ + FMOD_RESULT result; + unsigned int read; + FMOD_UINT_NATIVE threadid = 0; + + FMOD_OS_Thread_GetCurrentID(&threadid); + + if (mSystem && threadid != mSystem->mMainThreadID) /* Only stall fmod reads in other threads. */ + { + FMOD_File_SetDiskBusy(true); + } + + result = FMOD_OS_File_Read(mHandle, buffer, size, &read); + + if (mSystem && threadid != mSystem->mMainThreadID) + { + FMOD_File_SetDiskBusy(false); + } + + if (rd) + { + *rd = read; + } + + if (result == FMOD_OK && size != read) + { + return FMOD_ERR_FILE_EOF; + } + + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DiskFile::reallySeek(unsigned int pos) +{ + return FMOD_OS_File_Seek(mHandle, pos); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT DiskFile::reallyCancel() +{ + return FMOD_OS_File_Cancel(mHandle); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT DiskFile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(DiskFile)); + + return File::getMemoryUsedImpl(tracker); +} +#endif + + +} + + diff --git a/src/fmod_file_disk.h b/src/fmod_file_disk.h new file mode 100755 index 0000000..88a1fd7 --- /dev/null +++ b/src/fmod_file_disk.h @@ -0,0 +1,31 @@ +#ifndef _FMOD_FILE_DISK_H +#define _FMOD_FILE_DISK_H + +#include "fmod_settings.h" +#include "fmod_file.h" + +namespace FMOD +{ + class DiskFile : public File + { + DECLARE_MEMORYTRACKER + + private: + + void *mHandle; + + public: + + DiskFile() { mDeviceType = DEVICE_DISK; } + + FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize); + FMOD_RESULT reallyClose(); + FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT reallySeek(unsigned int pos); + FMOD_RESULT reallyCancel(); + }; +} + +#endif + + diff --git a/src/fmod_file_memory.cpp b/src/fmod_file_memory.cpp new file mode 100755 index 0000000..417c1c2 --- /dev/null +++ b/src/fmod_file_memory.cpp @@ -0,0 +1,140 @@ +#include "fmod_settings.h" + +#include "fmod_file_memory.h" + +#include <stdio.h> +#include <string.h> + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MemoryFile::reallyOpen(const char *name_or_data, unsigned int *filesize) +{ + mMem = (signed char *)name_or_data; + mPosition = 0; + mFlags &= ~FMOD_FILE_BUFFERISSTRING; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MemoryFile::reallyClose() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MemoryFile::reallyRead(void *buffer, unsigned int size, unsigned int *read) +{ + FMOD_RESULT result = FMOD_OK; + + if (mPosition + size > mFileSize) + { + size = mFileSize - mPosition; + + result = FMOD_ERR_FILE_EOF; + } + + FMOD_memcpy(buffer, (char *)mMem + mPosition, size); + + *read = size; + + mPosition += size; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MemoryFile::reallySeek(unsigned int pos) +{ + mPosition = pos; + + if (mPosition > mFileSize) + { + mPosition = mFileSize; + } + if (mPosition < 0) + { + mPosition = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT MemoryFile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(MemoryFile)); + + return File::getMemoryUsedImpl(tracker); +} +#endif + +} + + diff --git a/src/fmod_file_memory.h b/src/fmod_file_memory.h new file mode 100755 index 0000000..7dd4d0b --- /dev/null +++ b/src/fmod_file_memory.h @@ -0,0 +1,29 @@ +#ifndef _FMOD_FILE_MEMORY_H +#define _FMOD_FILE_MEMORY_H + +#include "fmod_settings.h" +#include "fmod_file.h" + +namespace FMOD +{ + class MemoryFile : public File + { + DECLARE_MEMORYTRACKER + + public: + + unsigned int mPosition; + void *mMem; + + MemoryFile() { mDeviceType = DEVICE_MEMORY; } + + FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize); + FMOD_RESULT reallyClose(); + FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT reallySeek(unsigned int pos); + }; +} + +#endif + + diff --git a/src/fmod_file_net.cpp b/src/fmod_file_net.cpp new file mode 100755 index 0000000..8d16fc6 --- /dev/null +++ b/src/fmod_file_net.cpp @@ -0,0 +1,1266 @@ +#include "fmod_settings.h" + +#include "fmod_debug.h" +#include "fmod_file_net.h" +#include "fmod_metadata.h" +#include "fmod_net.h" +#include "fmod_os_net.h" +#include "fmod_string.h" +#include "fmod_stringw.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +namespace FMOD +{ + +static const unsigned int FMOD_FILE_NET_SEEK_BLOCKSIZE = 16384; +static const unsigned int SIZEOF_METABUF = (255 * 16) + 1; + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +NetFile::NetFile() +{ + mHandle = (void *)-1; + mProtocol = -1; + mAbsolutePos = 0; + mHttpStatus = 0; + mMetaint = 0; + mBytesBeforeMeta = 0; + mMetabuf = 0; + mMetaFormat = FMOD_TAGTYPE_UNKNOWN; + mFlags &= ~FMOD_FILE_SEEKABLE; + mChunked = false; + mBytesLeftInChunk = 0; + mDeviceType = DEVICE_NET; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::init() +{ + return FMOD_OS_Net_Init(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::shutDown() +{ + return FMOD_OS_Net_Shutdown(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::parseUrl(char *url, char *host, int hostlen, char *auth, int authlen, unsigned short *port, char *file, const int filelen, bool *mms) +{ + char *p; + bool isipaddr = true; + bool hasuserpass = false; + char userpass[4096]; + + if (mms) + { + *mms = false; + } + + if (!FMOD_strnicmp("http://", url, 7) || !FMOD_strnicmp("http:\\\\", url, 7)) + { + url += 7; + } + else if (!FMOD_strnicmp("https://", url, 8) || !FMOD_strnicmp("https:\\\\", url, 8)) + { + url += 8; + } + else if (!FMOD_strnicmp("mms://", url, 6) || !FMOD_strnicmp("mms:\\\\", url, 6)) + { + if (mms) + { + *mms = true; + } + url += 6; + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + + p = (char *)url; + + /* + Could be username:password@url:port or just url:port + */ + + /* + Get username:password + */ + while (*p && *p != '/') + { + if (*p == '@') + { + hasuserpass = true; + break; + } + + p++; + } + + if (hasuserpass) + { + FMOD_strcpy(userpass, url); + userpass[p - url] = 0; + p++; + url = p; + } + else + { + p = (char *)url; + } + + /* + Get host + */ + while (*p && *p != ':' && *p != '/') + { + if ((*p < '0' || *p > '9') && *p != '.') + { + isipaddr = false; + } + p++; + } + + if (FMOD_strlen(url) >= hostlen) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_strncpy(host, url, hostlen); + host[p - url] = 0; + + if (!*p || *p == '/') + { + *port = 80; + } + else + { + int i; + char tmp[FMOD_STRING_MAXPATHLEN]; + p++; + for (i=0;(i < FMOD_STRING_MAXPATHLEN) && *p && (*p >= '0') && (*p <= '9');i++, p++) + { + tmp[i] = *p; + } + tmp[i] = 0; + *port = atoi(tmp); + } + + if (*p && !FMOD_isspace(*p)) + { + if (FMOD_strlen(p) >= filelen) + { + return FMOD_ERR_INVALID_PARAM; + } + else + { + char *t = p + FMOD_strlen(p) - 1; + for (;(t > p) && FMOD_isspace(*t);t--) ; + FMOD_strncpy(file, p, (int)(t - p + 1)); + file[t - p + 1] = 0; + } + } + else + { + FMOD_strcpy(file, "/"); + } + + if (hasuserpass) + { + if (auth) + { + FMOD_RESULT result = FMOD_Net_EncodeBase64(userpass, auth, authlen); + if (result != FMOD_OK) + { + return result; + } + } + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::openAsHTTP(const char *name_or_data, char *host, char *file, char *auth, unsigned short port, unsigned int *filesize) +{ + FMOD_RESULT result; + char buf[8192], http_request[8192], agent[32]; + int version, statuscode, redirect_count = 0; + bool fakeagent = false; + + sprintf(agent, "FMODEx/%x.%02x.%02x", (FMOD_VERSION >> 16) & 0xff, (FMOD_VERSION >> 8) & 0xff, FMOD_VERSION & 0xff); + +CONNECT: + + /* + Connect to the remote host + */ + if (FMOD_Net_ProxyHostname) + { + result = FMOD_OS_Net_Connect(FMOD_Net_ProxyHostname, FMOD_Net_ProxyPort, &mHandle); + } + else + { + result = FMOD_OS_Net_Connect(host, port, &mHandle); + } + if (result != FMOD_OK) + { + return result; + } + + /* + Construct a http request + */ + http_request[0] = 0; + sprintf(buf, "GET %s HTTP/1.1\r\n", FMOD_Net_ProxyHostname ? name_or_data : file); + FMOD_strcat(http_request, buf); + sprintf(buf, "Host: %s\r\n", host); + FMOD_strcat(http_request, buf); + sprintf(buf, "User-Agent: %s\r\n", agent); + FMOD_strcat(http_request, buf); + sprintf(buf, "Icy-MetaData:1\r\n"); + FMOD_strcat(http_request, buf); + if (FMOD_Net_ProxyAuth) + { + sprintf(buf, "Proxy-Authorization: Basic %s\r\n", FMOD_Net_ProxyAuth); + FMOD_strcat(http_request, buf); + } + if (auth[0]) + { + sprintf(buf, "Authorization: Basic %s\r\n", auth); + FMOD_strcat(http_request, buf); + } + sprintf(buf, "Connection: close\r\n"); + FMOD_strcat(http_request, buf); + FMOD_strcat(http_request, "\r\n"); + + /* + Send the http request + */ + unsigned int byteswritten = 0; + result = FMOD_OS_Net_Write(mHandle, http_request, FMOD_strlen(http_request), &byteswritten); + if (result != FMOD_OK) + { + return result; + } + + /* + Read and parse the remote host's response + */ + result = FMOD_OS_Net_ReadLine(mHandle, buf, 8191); + if (result == FMOD_OK) + { + result = FMOD_Net_ParseHTTPStatus(buf, FMOD_strlen(buf), &version, &statuscode); + if (result == FMOD_OK) + { + mHttpStatus = statuscode; + + if (statuscode == 200) + { + /* + Request was successful + */ + do + { + result = FMOD_OS_Net_ReadLine(mHandle, buf, 8191); + if (result == FMOD_OK) + { + char *tmp; + + /* + HTTP headers + */ + if (!FMOD_strncmp("Content-Length:", buf, 15)) + { + *filesize = atoi(buf + 15); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_HTTP; + } + } + else if (!FMOD_strncmp("Transfer-Encoding: chunked", buf, 26)) + { + mChunked = true; + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_HTTP; + } + } + /* + SHOUTcast headers + */ + else if (!FMOD_strncmp("icy-metaint:", buf, 12)) + { + mMetaint = atoi(buf + 12); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-br:", buf, 7)) + { + tmp = FMOD_eatwhite(buf + 7); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-br", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-pub:", buf, 8)) + { + tmp = FMOD_eatwhite(buf + 8); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-pub", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-notice1:", buf, 12)) + { + tmp = FMOD_eatwhite(buf + 12); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-notice1", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-notice2:", buf, 12)) + { + tmp = FMOD_eatwhite(buf + 12); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-notice2", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-name:", buf, 9)) + { + tmp = FMOD_eatwhite(buf + 9); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-name", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-genre:", buf, 10)) + { + tmp = FMOD_eatwhite(buf + 10); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-genre", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-url:", buf, 8)) + { + tmp = FMOD_eatwhite(buf + 8); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-url", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-irc:", buf, 8)) + { + tmp = FMOD_eatwhite(buf + 8); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-irc", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-icq:", buf, 8)) + { + tmp = FMOD_eatwhite(buf + 8); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-icq", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + else if (!FMOD_strncmp("icy-aim:", buf, 8)) + { + tmp = FMOD_eatwhite(buf + 8); + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "icy-aim", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_SHOUTCAST; + } + } + /* + Icecast headers + */ + if (!FMOD_strncmp("ice-bitrate: ", buf, 13)) + { + tmp = FMOD_eatwhite(buf + 13); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-bitrate", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-description: ", buf, 17)) + { + tmp = FMOD_eatwhite(buf + 17); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-description", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-public: ", buf, 12)) + { + tmp = FMOD_eatwhite(buf + 12); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-public", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-name: ", buf, 10)) + { + tmp = FMOD_eatwhite(buf + 10); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-name", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-genre: ", buf, 11)) + { + tmp = FMOD_eatwhite(buf + 11); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-genre", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-url: ", buf, 9)) + { + tmp = FMOD_eatwhite(buf + 9); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-url", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-irc: ", buf, 9)) + { + tmp = FMOD_eatwhite(buf + 9); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-irc", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-icq: ", buf, 9)) + { + tmp = FMOD_eatwhite(buf + 9); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-icq", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-aim: ", buf, 9)) + { + tmp = FMOD_eatwhite(buf + 9); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-aim", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-audio-info: ", buf, 16)) + { + tmp = FMOD_eatwhite(buf + 16); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-audio-info", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + else if (!FMOD_strncmp("ice-private: ", buf, 13)) + { + tmp = FMOD_eatwhite(buf + 13); + mMetadata.addTag(FMOD_TAGTYPE_ICECAST, "ice-private", tmp, FMOD_strlen(tmp) + 1, FMOD_TAGDATATYPE_STRING, false); + if (mProtocol == -1) + { + mProtocol = FMOD_PROTOCOL_ICECAST; + } + } + } + else + { + break; + } + + } while (FMOD_strlen(buf)); + } + else if ((statuscode == 301) || (statuscode == 302) || (statuscode == 303) || + (statuscode == 305) || (statuscode == 307)) + { + /* + HTTP redirect + */ + if (redirect_count < 16) + { + do + { + result = FMOD_OS_Net_ReadLine(mHandle, buf, 8191); + if (result == FMOD_OK) + { + if (!FMOD_strncmp("Location: ", buf, 10)) + { + /* + Got the new location - parse it and then go back and connect again + */ + char new_host[FMOD_STRING_MAXPATHLEN], new_file[FMOD_STRING_MAXPATHLEN]; + unsigned short new_port; + + new_host[0] = new_file[0] = 0; + new_port = 0; + + if (FMOD_strnicmp(buf + 10, "http://", 7) && + FMOD_strnicmp(buf + 10, "http:\\\\", 7)) + { + FMOD_strncpy(buf + 3, "http://", 7); + parseUrl(buf + 3, new_host, FMOD_STRING_MAXNAMELEN-1, 0, 0, &new_port, new_file, FMOD_STRING_MAXNAMELEN-1, 0); + } + else + { + parseUrl(buf + 10, new_host, FMOD_STRING_MAXNAMELEN-1, 0,0, &new_port, new_file, FMOD_STRING_MAXNAMELEN-1, 0); + } + + if (FMOD_strlen(new_host)) + { + FMOD_strcpy(host, new_host); + } + + if (FMOD_strlen(new_file)) + { + FMOD_strcpy(file, new_file); + } + + if (new_port) + { + port = new_port; + } + + redirect_count++; + FMOD_OS_Net_Close(mHandle); + mHandle = (void *)-1; + + goto CONNECT; + } + } + else + { + break; + } + + } while (FMOD_strlen(buf)); + } + + return FMOD_ERR_HTTP; + } + else if (statuscode == 403 && fakeagent == false) + { + /* + Some shoutcast servers deny unknown user-agents, so lets + pretend to be winamp and try again + */ + sprintf(agent, "Winamp"); + fakeagent = true; + + goto CONNECT; + } + else + { + /* + HTTP Error + */ + switch (mHttpStatus) + { + case 401 : + case 403 : + return FMOD_ERR_HTTP_ACCESS; + + case 404 : + return FMOD_ERR_FILE_NOTFOUND; + + case 407 : + return FMOD_ERR_HTTP_PROXY_AUTH; + + case 408 : + return FMOD_ERR_HTTP_TIMEOUT; + + case 500 : + case 501 : + case 502 : + case 503 : + case 504 : + case 505 : + return FMOD_ERR_HTTP_SERVER_ERROR; + + default : + return FMOD_ERR_HTTP; + } + } + } + else + { + return result; + } + } + else + { + return result; + } + + if ((mProtocol == FMOD_PROTOCOL_ICECAST) || (mProtocol == FMOD_PROTOCOL_HTTP)) + { + /* + These protocols can support any number of formats - try to detect ones we want to treat specially + */ + if (FMOD_strlen(name_or_data) > 4) + { + name_or_data = name_or_data + FMOD_strlen(name_or_data) - 4; + + if (!FMOD_stricmp(name_or_data, ".ogg")) + { + mMetaFormat = FMOD_TAGTYPE_VORBISCOMMENT; + } + else if (!FMOD_stricmp(name_or_data, ".mp3")) + { + mMetaFormat = FMOD_TAGTYPE_SHOUTCAST; + } + + // Add other format checks here... + } + } + else + { + /* + SHOUTcast only supports mp3 + */ + mMetaFormat = FMOD_TAGTYPE_SHOUTCAST; + } + + if (mMetaint) + { + mBytesBeforeMeta = mMetaint; + mMetabuf = (char *)FMOD_Memory_Alloc(SIZEOF_METABUF); + if (!mMetabuf) + { + reallyClose(); + return FMOD_ERR_MEMORY; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::openAsMMS(const char *name_or_data, char *host, char *file, char *auth, unsigned short port, unsigned int *filesize) +{ + return FMOD_ERR_UNSUPPORTED; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT NetFile::reallyOpen(const char *name_or_data, unsigned int *filesize) +{ + FMOD_RESULT result; + char host[FMOD_STRING_MAXPATHLEN], file[FMOD_STRING_MAXPATHLEN], url[FMOD_STRING_MAXPATHLEN]; + char auth[4096]; + unsigned short port; + bool mms = false; + + init(); + + *filesize = (unsigned int)-1; + + FMOD_memset(auth, 0, 4096); + + if (mFlags & FMOD_FILE_UNICODE) + { + FMOD_strncpyW((short*)url, (const short*)name_or_data, FMOD_STRING_MAXPATHLEN / 2); + FMOD_wtoa((short*)url); + } + else + { + FMOD_strncpy(url, name_or_data, FMOD_STRING_MAXPATHLEN); + } + + result = parseUrl(url, host, FMOD_STRING_MAXNAMELEN-1, auth, 4096, &port, file, FMOD_STRING_MAXNAMELEN-1, &mms); + if (result != FMOD_OK) + { + return result; + } + + if (mms) + { + return openAsMMS(url, host, file, auth, port, filesize); + } + else + { + return openAsHTTP(url, host, file, auth, port, filesize); + } + + return FMOD_ERR_INTERNAL; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::reallyClose() +{ + FMOD_RESULT result; + + if (mHandle != (void *)-1) + { + result = FMOD_OS_Net_Close(mHandle); + mHandle = (void *)-1; + if (result != FMOD_OK) + { + return result; + } + } + + if (mMetabuf) + { + FMOD_Memory_Free(mMetabuf); + mMetabuf = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::reallyRead(void *buffer, unsigned int size, unsigned int *rd) +{ + FMOD_RESULT result; + +AGAIN: + + if (mChunked) + { + if (!mBytesLeftInChunk) + { + const int MAX_CHUNKSIZESTR_LEN = 256; + char chunksizestr[MAX_CHUNKSIZESTR_LEN]; + + FMOD_memset(chunksizestr, 0, MAX_CHUNKSIZESTR_LEN); + chunksizestr[0] = '0'; + chunksizestr[1] = 'x'; + + result = FMOD_OS_Net_ReadLine(mHandle, chunksizestr+2, MAX_CHUNKSIZESTR_LEN); + if (result != FMOD_OK) + { + return result; + } + + sscanf(chunksizestr, "%x", &mBytesLeftInChunk); + if (!mBytesLeftInChunk) + { + return FMOD_ERR_FILE_EOF; + } + } + + if (!size) + { + return FMOD_ERR_FILE_EOF; + } + + if (size > mBytesLeftInChunk) + { + size = mBytesLeftInChunk; + } + } + + if (mMetaint) + { + if (!mBytesBeforeMeta) + { + if (mMetaFormat == FMOD_TAGTYPE_SHOUTCAST) + { + char tmp, *p, *name, *value, *artist, *title, *strend; + unsigned int metaread, metabytestoread; + + /* + Read metadata length + */ + result = FMOD_OS_Net_Read(mHandle, (char *)&tmp, 1, &metaread); + if (result != FMOD_OK) + { + return result; + } + + if (metaread != 1) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + /* + Read metadata + */ + metabytestoread = (int)tmp * 16; + p = mMetabuf; + FMOD_memset(mMetabuf, 0, SIZEOF_METABUF); + while (metabytestoread) + { + result = FMOD_OS_Net_Read(mHandle, p, metabytestoread, &metaread); + if (result != FMOD_OK) + { + return result; + } + + metabytestoread -= metaread; + p += metaread; + } + + /* + Parse metadata + */ + p = FMOD_strstr(mMetabuf, "StreamTitle='"); + if (p) + { + name = p; + name[11] = 0; + + value = &name[13]; + strend = value; + for (;*strend && *strend != ';';strend++); + strend--; // skip back to last "'" + *strend = 0; + + artist = value; + title = FMOD_strstr(artist, " - "); + if (title) + { + *title = 0; + title += 3; + } + + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "ARTIST", artist, FMOD_strlen(artist) + 1, FMOD_TAGDATATYPE_STRING, true); + if (title) + { + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, "TITLE", title, FMOD_strlen(title) + 1, FMOD_TAGDATATYPE_STRING, true); + } + + name[11] = ' '; + *strend = ' '; + if (title) + { + title -= 3; + *title = ' '; + } + } + + p = FMOD_strstr(mMetabuf, "StreamUrl='"); + if (p) + { + name = p; + name[9] = 0; + + value = &name[11]; + strend = value; + for (;*strend && *strend != ';';strend++); + strend--; // skip back to last "'" + *strend = 0; + + mMetadata.addTag(FMOD_TAGTYPE_SHOUTCAST, name, value, FMOD_strlen(value) + 1, FMOD_TAGDATATYPE_STRING, true); + } + + mBytesBeforeMeta = mMetaint; + + goto AGAIN; + } + else if (mMetaFormat == FMOD_TAGTYPE_VORBISCOMMENT) + { + mBytesBeforeMeta = mMetaint; + + goto AGAIN; + } + else if (mMetaFormat == FMOD_TAGTYPE_UNKNOWN) + { + mBytesBeforeMeta = mMetaint; + + goto AGAIN; + } + else + { + /* + Unknown metadata format + */ + return FMOD_ERR_INVALID_PARAM; + } + } + else + { + if (mBytesBeforeMeta < size) + { + size = mBytesBeforeMeta; + } + } + } + + if (mAbsolutePos == mLength) + { + return FMOD_ERR_FILE_EOF; + } + + if ((mAbsolutePos + size) > mLength) + { + size = mLength - mAbsolutePos; + } + + if (size > 4096) + { + size = 4096; + } + + result = FMOD_OS_Net_Read(mHandle, (char *)buffer, size, rd); + if (result != FMOD_OK) + { + return result; + } + + mAbsolutePos += *rd; + + if (mMetaint) + { + mBytesBeforeMeta -= *rd; + } + if (mChunked) + { + mBytesLeftInChunk -= *rd; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::reallySeek(unsigned int pos) +{ + FMOD_RESULT result = FMOD_OK; + + if (pos >= mAbsolutePos) + { + unsigned int read, bytestoread = pos - mAbsolutePos; + + if (bytestoread) + { + char *tmpbuf; + unsigned int blocksize = bytestoread > FMOD_FILE_NET_SEEK_BLOCKSIZE ? FMOD_FILE_NET_SEEK_BLOCKSIZE : bytestoread; + + tmpbuf = (char *)FMOD_Memory_Alloc(blocksize); + if (!tmpbuf) + { + return FMOD_ERR_MEMORY; + } + + while (bytestoread) + { + unsigned int readlen = bytestoread > blocksize ? blocksize : bytestoread; + + if (mMetaint) + { + if (!mBytesBeforeMeta) + { + if (mMetaFormat == FMOD_TAGTYPE_SHOUTCAST) + { + char tmp, *p; + unsigned int metaread, metabytestoread; + + result = FMOD_OS_Net_Read(mHandle, (char *)&tmp, 1, &metaread); + if (result != FMOD_OK) + { + break; + } + + if (metaread != 1) + { + result = FMOD_ERR_NET_SOCKET_ERROR; + break; + } + + metabytestoread = (int)tmp * 16; + p = mMetabuf; + FMOD_memset(mMetabuf, 0, SIZEOF_METABUF); + while (metabytestoread) + { + result = FMOD_OS_Net_Read(mHandle, p, metabytestoread, &metaread); + if (result != FMOD_OK) + { + break; + } + + if (metaread == 0) + { + result = FMOD_ERR_FILE_COULDNOTSEEK; + break; + } + + metabytestoread -= metaread; + p += metaread; + } + + if (result != FMOD_OK) + { + break; + } + } + + mBytesBeforeMeta = mMetaint; + } + + if (readlen > mBytesBeforeMeta) + { + readlen = mBytesBeforeMeta; + } + } + + result = FMOD_OS_Net_Read(mHandle, tmpbuf, readlen, &read); + if (result != FMOD_OK) + { + result = FMOD_ERR_FILE_COULDNOTSEEK; + break; + } + + if (read == 0) + { + result = FMOD_ERR_FILE_COULDNOTSEEK; + break; + } + + bytestoread -= read; + mAbsolutePos += read; + if (mMetaint) + { + mBytesBeforeMeta -= read; + } + } + + FMOD_Memory_Free(tmpbuf); + } + } + else + { + result = FMOD_ERR_FILE_COULDNOTSEEK; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::reallyCancel() +{ + FMOD_OS_Net_Close(mHandle); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NetFile::getMetadata(Metadata **metadata) +{ + if (!metadata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *metadata = &mMetadata; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT NetFile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(NetFile)); + + return File::getMemoryUsedImpl(tracker); +} +#endif + + +} + + + diff --git a/src/fmod_file_net.h b/src/fmod_file_net.h new file mode 100755 index 0000000..da32194 --- /dev/null +++ b/src/fmod_file_net.h @@ -0,0 +1,66 @@ +#ifndef _FMOD_FILE_NET_H +#define _FMOD_FILE_NET_H + +#include "fmod_settings.h" + +#include "fmod_file.h" +#include "fmod_linkedlist.h" +#include "fmod_metadata.h" + +namespace FMOD +{ + class NetFile : public File + { + DECLARE_MEMORYTRACKER + + private: + + void *mHandle; + int mProtocol; + unsigned int mAbsolutePos; + int mHttpStatus; + int mMetaint; + unsigned int mBytesBeforeMeta; + char *mMetabuf; + int mMetaFormat; + Metadata mMetadata; + unsigned short mPort; + char mHost[FMOD_STRING_MAXPATHLEN]; + FMOD_RESULT mConnectStatus; + bool mChunked; + unsigned int mBytesLeftInChunk; + + FMOD_RESULT parseUrl(char *url, char *host, int hostlen, char *auth, int authlen, unsigned short *port, char *file, const int filelen, bool *mms); + FMOD_RESULT openAsHTTP(const char *name_or_data, char *host, char *name, char *auth, unsigned short port, unsigned int *filesize); + FMOD_RESULT openAsMMS(const char *name_or_data, char *host, char *name, char *auth, unsigned short port, unsigned int *filesize); + + static void asyncConnectCallback(void *userdata); + + public: + + NetFile(); + + FMOD_RESULT getMetadata(Metadata **metadata); + + FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize); + FMOD_RESULT reallyClose(); + FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT reallySeek(unsigned int pos); + FMOD_RESULT reallyCancel(); + + FMOD_RESULT getSeekable(bool *seekable) { *seekable = false; return FMOD_OK; } + + void asyncConnect(); + + protected: + + friend class File; + + static FMOD_RESULT init(); + static FMOD_RESULT shutDown(); + }; +} + +#endif + + diff --git a/src/fmod_file_null.cpp b/src/fmod_file_null.cpp new file mode 100755 index 0000000..f6ec7bd --- /dev/null +++ b/src/fmod_file_null.cpp @@ -0,0 +1,138 @@ +#include "fmod_settings.h" + +#include "fmod_file_null.h" + +#include <stdio.h> + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NullFile::reallyOpen(const char *name_or_data, unsigned int *filesize) +{ + mPosition = 0; + + *filesize = 0; /* This will be set by the user after this call? */ + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NullFile::reallyClose() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NullFile::reallyRead(void *buffer, unsigned int size, unsigned int *read) +{ + FMOD_RESULT result = FMOD_OK; + + if (mPosition + size > mLength) + { + size = mLength - mPosition; + + result = FMOD_ERR_INVALID_PARAM; + } + + *read = size; + + mPosition += size; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT NullFile::reallySeek(unsigned int pos) +{ + mPosition = pos; + + if (mPosition > mLength) + { + mPosition = mLength; + } + if (mPosition < 0) + { + mPosition = 0; + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT NullFile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(NullFile)); + + return File::getMemoryUsedImpl(tracker); +} +#endif + +} + diff --git a/src/fmod_file_null.h b/src/fmod_file_null.h new file mode 100755 index 0000000..d174a7d --- /dev/null +++ b/src/fmod_file_null.h @@ -0,0 +1,28 @@ +#ifndef _FMOD_FILE_NULL_H +#define _FMOD_FILE_NULL_H + +#include "fmod_settings.h" +#include "fmod_file.h" + +namespace FMOD +{ + class NullFile : public File + { + DECLARE_MEMORYTRACKER + + private: + + unsigned int mPosition; + + public: + + FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize); + FMOD_RESULT reallyClose(); + FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT reallySeek(unsigned int pos); + }; +} + +#endif + + diff --git a/src/fmod_file_user.cpp b/src/fmod_file_user.cpp new file mode 100755 index 0000000..4cf6e92 --- /dev/null +++ b/src/fmod_file_user.cpp @@ -0,0 +1,191 @@ +#include "fmod_settings.h" + +#include "fmod_debug.h" +#include "fmod_file_user.h" +#include "fmod_systemi.h" + +#include <stdio.h> + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT UserFile::reallyOpen(const char *name_or_data, unsigned int *filesize) +{ + FMOD_RESULT result = FMOD_OK; + + if (mOpenCallback) + { + result = mOpenCallback(name_or_data, mFlags & FMOD_FILE_UNICODE ? 1 : 0, filesize, &mHandle, &mUserData); + } + else if (mSystem->mOpenCallback) + { + result = mSystem->mOpenCallback(name_or_data, mFlags & FMOD_FILE_UNICODE ? 1 : 0, filesize, &mHandle, &mUserData); + } + + if (result == FMOD_OK) + { + result = reallySeek(0); + if (result == FMOD_ERR_FILE_COULDNOTSEEK) + { + mFlags &= ~FMOD_FILE_SEEKABLE; + result = FMOD_OK; + } + } + + if (!mHandle) + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "UserFile::reallyOpen", "FAILED\n")); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT UserFile::reallyClose() +{ + if (mCloseCallback) + { + mCloseCallback(mHandle, mUserData); + } + else if (mSystem->mCloseCallback) + { + mSystem->mCloseCallback(mHandle, mUserData); + } + else + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "UserFile::reallyClose", "FAILED\n")); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT UserFile::reallyRead(void *buffer, unsigned int size, unsigned int *rd) +{ + FMOD_RESULT result = FMOD_OK; + + if (mReadCallback) + { + result = mReadCallback(mHandle, buffer, size, rd, mUserData); + } + else if (mSystem->mReadCallback) + { + result = mSystem->mReadCallback(mHandle, buffer, size, rd, mUserData); + } + else + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "UserFile::reallyRead", "FAILED\n")); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT UserFile::reallySeek(unsigned int pos) +{ + if (mSeekCallback) + { + FMOD_RESULT result; + + result = mSeekCallback(mHandle, pos, mUserData); + if (result != FMOD_OK) + { + return result; + } + } + else if (mSystem->mSeekCallback) + { + FMOD_RESULT result; + + result = mSystem->mSeekCallback(mHandle, pos, mUserData); + if (result != FMOD_OK) + { + return result; + } + } + else + { + FLOG((FMOD_DEBUG_TYPE_FILE, __FILE__, __LINE__, "UserFile::reallyRead", "FAILED\n")); + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT UserFile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(UserFile)); + + return File::getMemoryUsedImpl(tracker); +} +#endif + +} + diff --git a/src/fmod_file_user.h b/src/fmod_file_user.h new file mode 100755 index 0000000..97720c4 --- /dev/null +++ b/src/fmod_file_user.h @@ -0,0 +1,63 @@ +#ifndef _FMOD_FILE_USER_H +#define _FMOD_FILE_USER_H + +#include "fmod_settings.h" +#include "fmod_file.h" + +namespace FMOD +{ + class UserFile : public File + { + DECLARE_MEMORYTRACKER + + private: + + FMOD_FILE_OPENCALLBACK mOpenCallback; + FMOD_FILE_CLOSECALLBACK mCloseCallback; + FMOD_FILE_READCALLBACK mReadCallback; + FMOD_FILE_SEEKCALLBACK mSeekCallback; + + void *mHandle; + void *mUserData; + + public: + + UserFile() + { + mHandle = 0; + mUserData = 0; + mOpenCallback = 0; + mCloseCallback = 0; + mReadCallback = 0; + mSeekCallback = 0; + mDeviceType = DEVICE_USER; + } + + FMOD_RESULT reallyOpen(const char *name_or_data, unsigned int *filesize); + FMOD_RESULT reallyClose(); + FMOD_RESULT reallyRead(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT reallySeek(unsigned int pos); + + FMOD_RESULT setUserCallbacks(FMOD_FILE_OPENCALLBACK open, FMOD_FILE_CLOSECALLBACK close, FMOD_FILE_READCALLBACK read, FMOD_FILE_SEEKCALLBACK seek) + { + if (!open || !close || !read || !seek) + { + open = 0; + close = 0; + read = 0; + seek = 0; + } + + mOpenCallback = open; + mCloseCallback = close; + mReadCallback = read; + mSeekCallback = seek; + + return FMOD_OK; + } + }; +} + +#endif + + diff --git a/src/fmod_geometry.cpp b/src/fmod_geometry.cpp new file mode 100755 index 0000000..39a71a7 --- /dev/null +++ b/src/fmod_geometry.cpp @@ -0,0 +1,377 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +#include "fmod_geometryi.h" +#include "fmod.hpp" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT Geometry::release() +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->release(); + } +} + + +FMOD_RESULT Geometry::addPolygon(float directocclusion, float reverbocclusion, bool doublesided, int numvertices, const FMOD_VECTOR *vertices, int *polygonindex) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->addPolygon(directocclusion, reverbocclusion, doublesided, numvertices, vertices, polygonindex); + } +} + + +FMOD_RESULT Geometry::getNumPolygons(int *numpolygons) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getNumPolygons(numpolygons); + } +} + + +FMOD_RESULT Geometry::getMaxPolygons(int *maxpolygons, int *maxvertices) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getMaxPolygons(maxpolygons, maxvertices); + } +} + + +FMOD_RESULT Geometry::getPolygonNumVertices(int index, int *numvertices) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getPolygonNumVertices(index, numvertices); + } +} + + +FMOD_RESULT Geometry::setPolygonVertex(int index, int vertexindex, const FMOD_VECTOR *vertex) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setPolygonVertex(index, vertexindex, vertex); + } +} + + +FMOD_RESULT Geometry::getPolygonVertex(int index, int vertexindex, FMOD_VECTOR *vertex) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getPolygonVertex(index, vertexindex, vertex); + } +} + + +FMOD_RESULT Geometry::setPolygonAttributes(int index, float directocclusion, float reverbocclusion, bool doublesided) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setPolygonAttributes(index, directocclusion, reverbocclusion, doublesided); + } +} + + +FMOD_RESULT Geometry::getPolygonAttributes(int index, float *directocclusion, float *reverbocclusion, bool *doublesided) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getPolygonAttributes(index, directocclusion, reverbocclusion, doublesided); + } +} + + +FMOD_RESULT Geometry::setActive(bool active) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setActive(active); + } +} + + +FMOD_RESULT Geometry::getActive(bool *active) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getActive(active); + } +} + + +FMOD_RESULT Geometry::setRotation(const FMOD_VECTOR *forward, const FMOD_VECTOR *up) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setRotation(forward, up); + } +} + + +FMOD_RESULT Geometry::getRotation(FMOD_VECTOR *forward, FMOD_VECTOR *up) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getRotation(forward, up); + } +} + + +FMOD_RESULT Geometry::setPosition(const FMOD_VECTOR *position) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setPosition(position); + } +} + + +FMOD_RESULT Geometry::getPosition(FMOD_VECTOR *position) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getPosition(position); + } +} + + +FMOD_RESULT Geometry::setScale(const FMOD_VECTOR *scale) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setScale(scale); + } +} + + +FMOD_RESULT Geometry::getScale(FMOD_VECTOR *scale) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getScale(scale); + } +} + + +FMOD_RESULT Geometry::save(void *data, int *datasize) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->save(data, datasize); + } +} + + +FMOD_RESULT Geometry::setUserData(void *_userdata) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->setUserData(_userdata); + } +} + + +FMOD_RESULT Geometry::getUserData(void **_userdata) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getUserData(_userdata); + } +} + + +FMOD_RESULT Geometry::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + GeometryI *geometryi; + + result = GeometryI::validate(this, &geometryi); + if (result != FMOD_OK) + { + return result; + } + else + { + return geometryi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} + +#endif // FMOD_SUPPORT_GEOMETRY + +/*$ preserve end $*/ diff --git a/src/fmod_geometry_mgr.cpp b/src/fmod_geometry_mgr.cpp new file mode 100755 index 0000000..f22faac --- /dev/null +++ b/src/fmod_geometry_mgr.cpp @@ -0,0 +1,751 @@ +#include "fmod_settings.h" +#include "fmod_geometry_mgr.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_localcriticalsection.h" +#include "fmod_3d.h" + +#if defined(FMOD_GEOMETRY_DEBUGGING) + +// exported functions for debugging +void F_DECLSPEC F_DLLEXPORT geometryRenderTest(void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)) +{ + if (FMOD::GeometryMgr::sThis) + FMOD::GeometryMgr::sThis->renderTree(renderBox); +} + +void F_DECLSPEC F_DLLEXPORT geometryLineTestTest(FMOD_VECTOR* start, FMOD_VECTOR* end, float* intensity) +{ + if (FMOD::GeometryMgr::sThis) + { + float directOcclusion = 0; + float reverbOcclusion = 0; + FMOD::GeometryMgr::sThis->lineTestAll(start, end, &directOcclusion, &reverbOcclusion); + *intensity = 1.0f - directOcclusion; + } +} + +void F_DECLSPEC F_DLLEXPORT testLineTestForEachPolygon() +{ + // does a line test for ever polygon and make sure that it + // intersects with it. + // hasn't been made to work with transformations. + if (FMOD::GeometryMgr::sThis) + { + FMOD::GeometryMgr::sThis->testLineTestForEachPolygon(); + } +} +#endif + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ + +#if defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING) +#define ASSERT(x) do { if (!(x)) __asm { __emit 0xF1 } } while (false) +#else +#define ASSERT(x) +#endif + +#if defined(FMOD_GEOMETRY_DEBUGGING) +GeometryMgr* GeometryMgr::sThis = 0; +#endif + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +OcclusionThread::OcclusionThread() : Thread() +{ + mQueueCrit = 0; + mTasks = 0; + mGeometryMgr = 0; + mInitialized = false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void OcclusionThread::init() +{ + ASSERT(mGeometryMgr); + ASSERT(mGeometryMgr->mSystem); + ASSERT(!mTasks); + mInitialized = true; + + unsigned int numchans = mGeometryMgr->mSystem->mNumChannels; + FMOD_OS_CriticalSection_Create(&mQueueCrit); + LocalCriticalSection crit(mQueueCrit, true); + + mTasks = (OCCLUSION_TASK *)FMOD_Memory_Calloc(numchans * sizeof(OCCLUSION_TASK)); + + for (unsigned int count = 0; count < numchans; count++) + { + mTasks[count].state = OCCLUSION_STATE_READY; + mTasks[count].initNode(); + } + + mGeometryMgr->initCritalSection(); + initThread("FMOD geometry thread", NULL, NULL, GEOMETRY_THREADPRIORITY, 0, GEOMETRY_STACKSIZE, false, 0, mGeometryMgr->mSystem); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OcclusionThread::release() +{ + FMOD_RESULT result = FMOD_OK; + + mInitialized = false; + if (mQueueCrit) + { + result = closeThread(); + ASSERT(result == FMOD_OK); + if (result != FMOD_OK) + { + return result; + } + + ASSERT(mTasks); + FMOD_Memory_Free(mTasks); + mTasks = 0; + result = FMOD_OS_CriticalSection_Free(mQueueCrit); + mQueueCrit = 0; + } + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OcclusionThread::threadFunc() +{ + mGeometryMgr->mSystem->mGeometryTimeStamp.stampIn(); + OCCLUSION_TASK *task = dequeue(); + if (task && task->state == OCCLUSION_STATE_READY) + { + /* test occlusion from channel to listener */ + mGeometryMgr->lineTestAll(&mGeometryMgr->mSystem->mListener[0].mPosition, &task->position, &task->directocclusion, &task->reverbocclusion); + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* test occlusion from main 3D reverb to channel (this replaces ChannelI::calculate3DReverbGain)*/ + float direct_o, reverb_o; + FMOD_VECTOR reverbpos; + mGeometryMgr->mSystem->mReverb3D.get3DAttributes(&reverbpos, 0, 0); + mGeometryMgr->lineTestAll(&task->position, &reverbpos, &direct_o, &reverb_o); + task->reverbgain = 1.0f - reverb_o; +#endif + /* tell channeli->update to trigger callback */ + task->state = OCCLUSION_STATE_UPDATED; + mGeometryMgr->mSystem->mGeometryTimeStamp.stampOut(95); + } + else + { + mGeometryMgr->mSystem->mGeometryTimeStamp.stampOut(95); + FMOD_OS_Time_Sleep(10); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void OcclusionThread::enqueue(unsigned int index, unsigned int currenthandle, FMOD_VECTOR *worldposition) +{ + if (!mInitialized) + { + init(); + } + + LocalCriticalSection crit(mQueueCrit, true); + OCCLUSION_TASK *task = &mTasks[index]; + + if (task->state != OCCLUSION_STATE_UPDATED) /* don't enqueue if it's most recent update hasn't be checked by main thread */ + { + task->state = OCCLUSION_STATE_READY; + task->currenthandle = currenthandle; + FMOD_Vector_Copy(worldposition, &task->position); + if (task->getNext() == task && task->getPrev() == task) /* make sure it is not already queued */ + { + task->addBefore(&mQueueRoot); + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +OCCLUSION_TASK* OcclusionThread::dequeue() +{ + OCCLUSION_TASK *task = 0; + LocalCriticalSection crit(mQueueCrit, true); + + if (!mQueueRoot.isEmpty()) + { + task = (OCCLUSION_TASK*)mQueueRoot.getNext(); + task->removeNode(); + } + + return task; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +OCCLUSION_TASK* OcclusionThread::retrieveOcclusion(unsigned int index) +{ + ASSERT(index > 0); + + if (!mTasks) + { + return NULL; + } + + OCCLUSION_TASK *task = &mTasks[index]; + if (task->state == OCCLUSION_STATE_UPDATED) + { + task->state = OCCLUSION_STATE_CHECKED; + return task; + } + + return NULL; +} + +#endif //FMOD_SUPPORT_GEOMETRY_THREADED + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +GeometryMgr::GeometryMgr() +{ + mSystem = 0; + mMainOctree = 0; + mRefCount = 0; + mFirstUpdateItem = 0; + mWorldSize = 1000.0f; /* Arbitrary value, could be anything */ + mMoved = true; +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + mOcclusionThread.mGeometryMgr = this; +#endif //FMOD_SUPPORT_GEOMETRY_THREADED +#if defined(FMOD_GEOMETRY_DEBUGGING) + sThis = this; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +GeometryMgr::~GeometryMgr() +{ +#if defined(FMOD_GEOMETRY_DEBUGGING) + sThis = 0; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::aquireMainOctree() +{ + mRefCount++; + if (!mMainOctree) + { + mMainOctree = (Octree *)FMOD_Memory_Alloc(sizeof (Octree)); + if (!mMainOctree) + return FMOD_ERR_MEMORY; + new (mMainOctree) Octree(mWorldSize); + } + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void GeometryMgr::releaseMainOctree() +{ + mRefCount--; + if (mRefCount <= 0) + { + mRefCount = 0; + if (mMainOctree) + { + mMainOctree->~Octree(); + FMOD_Memory_Free(mMainOctree); + mMainOctree = 0; + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +bool GeometryMgr::mainOctreeLineTestCallback(OctreeNode* item, void* data) +{ + GeometryI::SpatialData* spatialdata = (GeometryI::SpatialData*)item; + ASSERT(spatialdata->geometry); + GeometryI::LineTestData* lineTestData = (GeometryI::LineTestData*)data; + lineTestData->geometryI = spatialdata->geometry; + + return spatialdata->geometry->lineTest(lineTestData); +} + +#ifdef _DEBUG +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +Octree *GeometryMgr::mainOctree() +{ + ASSERT(mMainOctree); + return mMainOctree; +} +#endif // _DEBUG + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::setWorldSize(float worldSize) +{ + if (mWorldSize == worldSize) + { + return FMOD_OK; + } + + mWorldSize = worldSize; + + if (mMainOctree) + { + mMainOctree->setMaxSize(worldSize); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::lineTestAll(const FMOD_VECTOR* start, const FMOD_VECTOR* end, float* directOcclusion, float* reverbOcclusion) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryCrit, true); +#endif + flushAll(); + + GeometryI::LineTestData lineTestData; + lineTestData.start = *start; + lineTestData.end = *end; + lineTestData.directTransmission = 1.0f; + lineTestData.reverbTransmission = 1.0f; + lineTestData.geometryI = 0; + + if (mMainOctree) + { + mainOctree()->testLine(mainOctreeLineTestCallback, &lineTestData, *start, *end); + } + + *directOcclusion = 1.0f - lineTestData.directTransmission; + *reverbOcclusion = 1.0f - lineTestData.reverbTransmission; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::flushAll() +{ + GeometryI* geometryI = mFirstUpdateItem; + mFirstUpdateItem = 0; + + while (geometryI) + { + GeometryI* next = geometryI->mNextUpdateItem; + + geometryI->mNextUpdateItem = 0; + ASSERT(geometryI->mToBeUpdated); + geometryI->mToBeUpdated = false; + + geometryI->flush(); + geometryI = next; + } + + return FMOD_OK; +} + +#if defined(FMOD_GEOMETRY_DEBUGGING) + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::renderTree(void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)) +{ + mainOctree()->renderTree(renderBox); + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::testLineTestForEachPolygon() +{ + GeometryI* current = mSystem->mGeometryList; + do + { + current->testLineTestForEachPolygon(); + current = SAFE_CAST(GeometryI, current->getNext()); + } + while (current != mSystem->mGeometryList); + + return FMOD_OK; +} +#endif + + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::initCritalSection() +{ + return FMOD_OS_CriticalSection_Create(&mGeometryCrit); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryMgr::releaseOcclusionThread() +{ + FMOD_RESULT result; + mOcclusionThread.release(); + result = FMOD_OS_CriticalSection_Free(mGeometryCrit); + mGeometryCrit = 0; + return result; +} + +#endif //FMOD_SUPPORT_GEOMETRY_THREADED + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT GeometryMgr::getMemoryUsedImpl(MemoryTracker *tracker) +{ + //AJS incomplete + tracker->add(false, FMOD_MEMBITS_GEOMETRY, sizeof(*this)); + + return FMOD_OK; +} + +#endif + + +} + +#endif // FMOD_SUPPORT_GEOMETRY diff --git a/src/fmod_geometry_mgr.h b/src/fmod_geometry_mgr.h new file mode 100755 index 0000000..9809ff7 --- /dev/null +++ b/src/fmod_geometry_mgr.h @@ -0,0 +1,125 @@ +#ifndef _FMOD_GEOMETRYMGR_H +#define _FMOD_GEOMETRYMGR_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +#include "fmod_octree.h" +#include "fmod_geometryi.h" +#include "fmod_thread.h" +#include "fmod_channeli.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class GeometryI; + class SystemI; + class GeometryMgr; + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + typedef enum + { + OCCLUSION_STATE_READY, + OCCLUSION_STATE_UPDATED, + OCCLUSION_STATE_CHECKED + } OCCLUSION_STATE; + + struct OCCLUSION_TASK : LinkedListNode + { + OCCLUSION_STATE state; + unsigned int currenthandle; + FMOD_VECTOR position; + float directocclusion; + float reverbocclusion; +#ifdef FMOD_SUPPORT_MULTIREVERB + float reverbgain; +#endif + }; + + class OcclusionThread : public Thread + { + public: + friend class GeometryMgr; + + OcclusionThread(); + + FMOD_RESULT threadFunc(); + void init(); + FMOD_RESULT release(); + void enqueue(unsigned int index, unsigned int currentHandle, FMOD_VECTOR *worldposition); + OCCLUSION_TASK* retrieveOcclusion(unsigned int index); + + private: + bool mInitialized; + OCCLUSION_TASK* dequeue(); + + OCCLUSION_TASK *mTasks; + LinkedListNode mQueueRoot; + FMOD_OS_CRITICALSECTION *mQueueCrit; + GeometryMgr *mGeometryMgr; + }; +#endif //FMOD_SUPPORT_GEOMETRY_THREADED + + class GeometryMgr + { + DECLARE_MEMORYTRACKER + + public: + friend class GeometryI; + friend class ChannelI; + + SystemI *mSystem; + bool mMoved; +#if defined(FMOD_GEOMETRY_DEBUGGING) + static GeometryMgr *sThis; +#endif + + GeometryMgr(); + ~GeometryMgr(); + + FMOD_RESULT aquireMainOctree (); + void releaseMainOctree (); +#ifdef _DEBUG + Octree *mainOctree (); +#else + Octree *mainOctree () { return mMainOctree; } +#endif + FMOD_RESULT setWorldSize (float worldSize); + float getWorldSize () { return mWorldSize; } + FMOD_RESULT lineTestAll (const FMOD_VECTOR* start, const FMOD_VECTOR* end, float* directOcclusion, float* reverbOcclusion); + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + friend class OcclusionThread; + + FMOD_RESULT initCritalSection (); + FMOD_RESULT releaseOcclusionThread (); +#endif +#if defined(FMOD_GEOMETRY_DEBUGGING) + FMOD_RESULT renderTree (void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)); + FMOD_RESULT testLineTestForEachPolygon(); +#endif + + static bool mainOctreeLineTestCallback (OctreeNode* item, void* data); + + private: + FMOD_RESULT flushAll (); + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + OcclusionThread mOcclusionThread; + FMOD_OS_CRITICALSECTION *mGeometryCrit; +#endif + Octree *mMainOctree; + int mRefCount; + GeometryI *mFirstUpdateItem; + float mWorldSize; + }; +} + +#endif + +#endif + diff --git a/src/fmod_geometryi.cpp b/src/fmod_geometryi.cpp new file mode 100755 index 0000000..da0e833 --- /dev/null +++ b/src/fmod_geometryi.cpp @@ -0,0 +1,1983 @@ +#include "fmod_geometryi.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +#include "fmod_3d.h" +#include "fmod_memory.h" +#include "fmod_geometry_mgr.h" +#include "fmod_systemi.h" +#include "fmod_localcriticalsection.h" + +#include <string.h> + +namespace FMOD +{ + +#if defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING) +#define ASSERT(x) do { if (!(x)) __asm { int 3 } } while (false) +#else +#define ASSERT(x) +#endif + +// if the sound transsmision drop this low then +// don't bother doing any more ray testing +const float MIN_TRANSMISSION = 0.05f; + +static inline float MAX(float x, float y) { return x > y ? x : y; } +static inline float MIN(float x, float y) { return x < y ? x : y; } + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +static inline void cross(const FMOD_VECTOR& a, const FMOD_VECTOR& b, FMOD_VECTOR& result) +{ + result.x = a.y * b.z - a.z * b.y; + result.y = a.z * b.x - a.x * b.z; + result.z = a.x * b.y - a.y * b.x; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +static void matrixMult(const float matrix[3][4], const FMOD_VECTOR *src, FMOD_VECTOR *dst) +{ + dst->x = matrix[0][0] * src->x + matrix[0][1] * src->y + matrix[0][2] * src->z; + dst->y = matrix[1][0] * src->x + matrix[1][1] * src->y + matrix[1][2] * src->z; + dst->z = matrix[2][0] * src->x + matrix[2][1] * src->y + matrix[2][2] * src->z; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void GeometryI::calculateMatrix() +{ + mMatrix[1][0] = mUp.x * mScale.y; + mMatrix[1][1] = mUp.y * mScale.y; + mMatrix[1][2] = mUp.z * mScale.y; + mMatrix[2][0] = mForward.x * mScale.z; + mMatrix[2][1] = mForward.y * mScale.z; + mMatrix[2][2] = mForward.z * mScale.z; + mMatrix[0][0] = (mUp.y * mForward.z - mUp.z * mForward.y) * mScale.x; + mMatrix[0][1] = (mUp.z * mForward.x - mUp.x * mForward.z) * mScale.x; + mMatrix[0][2] = (mUp.x * mForward.y - mUp.y * mForward.x) * mScale.x; + + mInvMatrix[0][1] = mUp.x / mScale.y; + mInvMatrix[1][1] = mUp.y / mScale.y; + mInvMatrix[2][1] = mUp.z / mScale.y; + mInvMatrix[0][2] = mForward.x / mScale.z; + mInvMatrix[1][2] = mForward.y / mScale.z; + mInvMatrix[2][2] = mForward.z / mScale.z; + mInvMatrix[0][0] = (mUp.y * mForward.z - mUp.z * mForward.y) / mScale.x; + mInvMatrix[1][0] = (mUp.z * mForward.x - mUp.x * mForward.z) / mScale.x; + mInvMatrix[2][0] = (mUp.x * mForward.y - mUp.y * mForward.x) / mScale.x; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void GeometryI::updateSpatialData() +{ + // find world aabb + FMOD_VECTOR center; + center.x = (mAABB.xMax + mAABB.xMin) * 0.5f; + center.y = (mAABB.yMax + mAABB.yMin) * 0.5f; + center.z = (mAABB.zMax + mAABB.zMin) * 0.5f; + FMOD_VECTOR newCenter; + matrixMult(mMatrix, ¢er, &newCenter); + newCenter.x += mPosition.x; + newCenter.y += mPosition.y; + newCenter.z += mPosition.z; + + + float xExtent = (mAABB.xMax - mAABB.xMin) * 0.5f; + float yExtent = (mAABB.yMax - mAABB.yMin) * 0.5f; + float zExtent = (mAABB.zMax - mAABB.zMin) * 0.5f; + + float xNewExtent = (float)fabs(mMatrix[0][0]) * xExtent + (float)fabs(mMatrix[1][0]) * yExtent + (float)fabs(mMatrix[2][0]) * zExtent; + float yNewExtent = (float)fabs(mMatrix[0][1]) * xExtent + (float)fabs(mMatrix[1][1]) * yExtent + (float)fabs(mMatrix[2][1]) * zExtent; + float zNewExtent = (float)fabs(mMatrix[0][2]) * xExtent + (float)fabs(mMatrix[1][2]) * yExtent + (float)fabs(mMatrix[2][2]) * zExtent; + + + ASSERT(mSpatialData); + mSpatialData->octreeNode.aabb.xMax = newCenter.x + xNewExtent; + mSpatialData->octreeNode.aabb.xMin = newCenter.x - xNewExtent; + mSpatialData->octreeNode.aabb.yMax = newCenter.y + yNewExtent; + mSpatialData->octreeNode.aabb.yMin = newCenter.y - yNewExtent; + mSpatialData->octreeNode.aabb.zMax = newCenter.z + zNewExtent; + mSpatialData->octreeNode.aabb.zMin = newCenter.z - zNewExtent; + + if (mActive) + { + mGeometryMgr->mainOctree()->updateItem(&mSpatialData->octreeNode); + } + else + { + mGeometryMgr->mainOctree()->deleteItem(&mSpatialData->octreeNode); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void GeometryI::setToBeUpdated() +{ + mGeometryMgr->mMoved = true; + + if (!mToBeUpdated) + { + mToBeUpdated = true; + mNextUpdateItem = mGeometryMgr->mFirstUpdateItem; + mGeometryMgr->mFirstUpdateItem = this; + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +bool GeometryI::octreeLineTestCallback(OctreeNode* item, void* data) +{ + LineTestData *lineTestData = (LineTestData*)data; + + ASSERT(lineTestData); + ASSERT(lineTestData->geometryI); + + const FMOD_VECTOR& a = lineTestData->start; + const FMOD_VECTOR& b = lineTestData->end; + + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)item; + + float dotA = FMOD_Vector_DotProduct(&polygon.normal, &a) - polygon.distance; + float dotB = FMOD_Vector_DotProduct(&polygon.normal, &b) - polygon.distance; + + if (dotA >= 0.0f && dotB >= 0.0f || dotA <= 0.0f && dotB <= 0.0f) + { + return true; // no collision + } + + if (dotA > 0.0f && (polygon.flags & FMOD_POLYGON_FLAG_DOUBLE_SIDED) == 0) + { + return true; // no collision + } + + // find point on plane + float time = dotA / (dotA - dotB); + + FMOD_VECTOR collisionPosition; + + collisionPosition.x = a.x + (b.x - a.x) * time; + collisionPosition.y = a.y + (b.y - a.y) * time; + collisionPosition.z = a.z + (b.z - a.z) * time; + + FMOD_VECTOR* vertices = &polygon.vertices; + + // find if point lies inside all edges of polygon + int side; + for (side = 0; side < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); side++) + { + FMOD_VECTOR diff; + FMOD_VECTOR edgeVector; + int next = side + 1; + + if (next >= (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK)) + { + next = 0; + } + + diff.x = vertices[next].x - vertices[side].x; + diff.y = vertices[next].y - vertices[side].y; + diff.z = vertices[next].z - vertices[side].z; + + cross(diff, polygon.normal, edgeVector); + + float dot = + edgeVector.x * (collisionPosition.x - vertices[side].x) + + edgeVector.y * (collisionPosition.y - vertices[side].y) + + edgeVector.z * (collisionPosition.z - vertices[side].z); + + if (dot > 0.0f) + { + break; // point is outside polygon so no interestection occured + } + } + if (side == (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK)) + { + if (lineTestData->geometryI->mGeometryMgr->mSystem->mFlags & FMOD_INIT_GEOMETRY_USECLOSEST) + { + float newDirectTransmission = (1.0f - polygon.directOcclusion); + float newReverbTransmission = (1.0f - polygon.reverbOcclusion); + + if (newDirectTransmission < lineTestData->directTransmission || (newDirectTransmission == lineTestData->directTransmission && newReverbTransmission < lineTestData->reverbTransmission)) + { + lineTestData->directTransmission = newDirectTransmission; + lineTestData->reverbTransmission = newReverbTransmission; + } + } + else + { + lineTestData->directTransmission *= (1.0f - polygon.directOcclusion); + lineTestData->reverbTransmission *= (1.0f - polygon.reverbOcclusion); + } + +#if defined(FMOD_GEOMETRY_DEBUGGING) + // for function testLineTestForEachPolygon to see if this + // polygon has actually been intersected with + polygon.flags &= ~0x80000000; +#else + if (lineTestData->directTransmission < MIN_TRANSMISSION && + lineTestData->reverbTransmission < MIN_TRANSMISSION) + { + // sounds level has dropped too low so stop doing line testing + return false; + } +#endif + } + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +GeometryI::GeometryI(GeometryMgr* geometryMgr) : LinkedListNode(), mOctree(geometryMgr->getWorldSize()) +{ + mGeometryMgr = geometryMgr; + mMaxNumVertices = 0; + mNumVertices = 0; + mMaxNumPolygons = 0; + mNumPolygons = 0; + mPolygonOffsets = 0; + mPolygonDataPos = 0; + mPolygonData = 0; + + mForward.x = 0.0f; + mForward.y = 0.0f; + mForward.z = 1.0f; + mUp.x = 0.0f; + mUp.y = 1.0f; + mUp.z = 0.0f; + mPosition.x = 0.0f; + mPosition.y = 0.0f; + mPosition.z = 0.0f; + mScale.x = 1.0f; + mScale.y = 1.0f; + mScale.z = 1.0f; + calculateMatrix(); + + mPolygonUpdateList = 0; + mNextUpdateItem = 0; + mToBeUpdated = false; + mActive = true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::release() +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + // mGeometryMgr->flushAll(); + + // find out if we are in the "to be updated" list and remove us. + // Other wise we will try to update a deleted object + GeometryI* geometryI = mGeometryMgr->mFirstUpdateItem; + GeometryI* prevGeometryI = 0; + while (geometryI) + { + if (geometryI == this) + { + if (prevGeometryI) + { + prevGeometryI->mNextUpdateItem = geometryI->mNextUpdateItem; + } + else + { + mGeometryMgr->mFirstUpdateItem = geometryI->mNextUpdateItem; + } + break; + } + prevGeometryI = geometryI; + geometryI = geometryI->mNextUpdateItem; + } + + + if (mSpatialData) + { + mGeometryMgr->mainOctree()->deleteItem(&mSpatialData->octreeNode); + mGeometryMgr->mainOctree()->removeInternalNode(&mSpatialData->octreeInternalNode); + mGeometryMgr->releaseMainOctree(); + FMOD_Memory_Free(mSpatialData); + mSpatialData = 0; + } + if (mPolygonData) + { + FMOD_Memory_Free(mPolygonData); + mPolygonData = 0; + } + if (mPolygonOffsets) + { + FMOD_Memory_Free(mPolygonOffsets); + mPolygonOffsets = 0; + } + + mGeometryMgr->mMoved = true; // Make it reset the voices back to unoccluded. + mGeometryMgr->mSystem->update(); + + if (mGeometryMgr->mSystem->mGeometryList == this) + { + mGeometryMgr->mSystem->mGeometryList = (GeometryI*)getNext(); + if (mGeometryMgr->mSystem->mGeometryList == this) + { + mGeometryMgr->mSystem->mGeometryList = 0; + } + } + removeNode(); + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::alloc(int maxNumPolygons, int maxNumVertices) +{ + FMOD_RESULT result; +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + + if (mPolygonData || mPolygonOffsets ) + { + return FMOD_ERR_INTERNAL; + } + + mMaxNumVertices = maxNumVertices; + mNumVertices = 0; + mMaxNumPolygons = maxNumPolygons; + mNumPolygons = 0; + mPolygonOffsets = (int*)FMOD_Memory_Alloc(mMaxNumVertices * sizeof (int)); + if (!mPolygonOffsets) + { + return FMOD_ERR_MEMORY; + } + mPolygonDataPos = 0; + int polygonDataSize = (sizeof(FMOD_POLYGON) - sizeof (FMOD_VECTOR)) * maxNumPolygons; + polygonDataSize += sizeof (FMOD_VECTOR) * maxNumVertices; + + mPolygonData = (unsigned char*)FMOD_Memory_Alloc(polygonDataSize); + if (!mPolygonData) + { + return FMOD_ERR_MEMORY; + } + + result = mGeometryMgr->aquireMainOctree(); + if (result != FMOD_OK) + { + return result; + } + + mSpatialData = (SpatialData *)FMOD_Memory_Alloc(sizeof (SpatialData)); + if (!mSpatialData) + { + return FMOD_ERR_MEMORY; + } + + FMOD_memset(mSpatialData, 0, sizeof (SpatialData)); + mSpatialData->geometry = this; + + mGeometryMgr->mainOctree()->addInternalNode(&mSpatialData->octreeInternalNode); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::addPolygon(float directOcclusion, float reverbOcclusion, bool doubleSided, int numVertices, const FMOD_VECTOR *vertices, int *polygonIndex) +{ + ASSERT(vertices); + ASSERT(numVertices >= 3); + ASSERT(mNumPolygons < mMaxNumPolygons); + ASSERT(mNumVertices + numVertices <= mMaxNumVertices); +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + + if (vertices == 0) + { + return FMOD_ERR_INVALID_PARAM; + } + if (numVertices < 3) + { + return FMOD_ERR_INVALID_PARAM; + } + if (mNumPolygons >= mMaxNumPolygons) + { + return FMOD_ERR_INVALID_PARAM; + } + if (mNumVertices + numVertices > mMaxNumVertices) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (polygonIndex) + { + *polygonIndex = mNumPolygons; + } + + mNumVertices += numVertices; + mPolygonOffsets[mNumPolygons] = mPolygonDataPos; + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonDataPos]; + mPolygonDataPos += sizeof(FMOD_POLYGON) + (numVertices - 1) * sizeof (FMOD_VECTOR); + mNumPolygons++; + + FMOD_memset(&polygon.node, 0, sizeof (OctreeNode)); + FMOD_memset(&polygon.nodeInternal, 0, sizeof (OctreeNode)); + polygon.directOcclusion = directOcclusion; + polygon.reverbOcclusion = reverbOcclusion; + polygon.flags = numVertices; + if (doubleSided) + polygon.flags |= FMOD_POLYGON_FLAG_DOUBLE_SIDED; + + int vertex; + for (vertex = 0; vertex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); vertex++) + (&polygon.vertices)[vertex] = vertices[vertex]; + + // add to update list + polygon.node.nextItem = mPolygonUpdateList; + mPolygonUpdateList = &polygon.node; + + setToBeUpdated(); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getNumPolygons(int *numPolygons) +{ + if (!numPolygons) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numPolygons = mNumPolygons; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getMaxPolygons(int *maxNumPolygons, int *maxVertices) +{ + if (maxNumPolygons) + { + *maxNumPolygons = mMaxNumPolygons; + } + if (maxVertices) + { + *maxVertices = mMaxNumVertices; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getPolygonNumVertices (int polygonIndex, int *numVertices) +{ + ASSERT(polygonIndex >= 0); + ASSERT(polygonIndex < mNumPolygons); + ASSERT(mPolygonOffsets[polygonIndex] >= 0); + ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos); + + if (polygonIndex < 0 || polygonIndex >= mNumPolygons) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]]; + + if (numVertices) + { + *numVertices = (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setPolygonVertex(int polygonIndex, int vertexIndex, const FMOD_VECTOR *vertex) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + ASSERT(polygonIndex >= 0); + ASSERT(polygonIndex < mNumPolygons); + ASSERT(mPolygonOffsets[polygonIndex] >= 0); + ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos); + + if (polygonIndex < 0 || polygonIndex >= mNumPolygons) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]]; + ASSERT(vertexIndex >= 0); + ASSERT(vertexIndex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK)); + ASSERT(vertex); + + if (vertexIndex < 0 || vertexIndex >= (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK)) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (vertex == 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if ((&polygon.vertices)[vertexIndex].x == vertex->x && + (&polygon.vertices)[vertexIndex].y == vertex->y && + (&polygon.vertices)[vertexIndex].z == vertex->z) + { + return FMOD_OK; + } + + (&polygon.vertices)[vertexIndex] = *vertex; + + // if we are in the octreee then remove and add to the update list + if (polygon.node.flags & OCTREE_FLAG_INSERTED) + { + mOctree.deleteItem(&polygon.node); + polygon.node.nextItem = mPolygonUpdateList; + mPolygonUpdateList = &polygon.node; + } + + setToBeUpdated(); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getPolygonVertex(int polygonIndex, int vertexIndex, FMOD_VECTOR *vertex) +{ + ASSERT(polygonIndex >= 0); + ASSERT(polygonIndex < mNumPolygons); + ASSERT(mPolygonOffsets[polygonIndex] >= 0); + ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos); + + if (polygonIndex < 0 || polygonIndex >= mNumPolygons || !vertex) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]]; + ASSERT(vertexIndex >= 0); + ASSERT(vertexIndex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK)); + + if (vertexIndex < 0 || vertexIndex >= (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK)) + { + return FMOD_ERR_INVALID_PARAM; + } + + *vertex = (&polygon.vertices)[vertexIndex]; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setPolygonAttributes(int polygonIndex, float directOcclusion, float reverbOcclusion, bool doubleSided) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + ASSERT(polygonIndex >= 0); + ASSERT(polygonIndex < mNumPolygons); + ASSERT(mPolygonOffsets[polygonIndex] >= 0); + ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos); + if (polygonIndex < 0 || polygonIndex >= mNumPolygons) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]]; + polygon.directOcclusion = directOcclusion; + polygon.reverbOcclusion = reverbOcclusion; + if (doubleSided) + { + polygon.flags |= FMOD_POLYGON_FLAG_DOUBLE_SIDED; + } + else + { + polygon.flags &= ~FMOD_POLYGON_FLAG_DOUBLE_SIDED; + } + + setToBeUpdated(); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getPolygonAttributes(int polygonIndex, float *directOcclusion, float *reverbOcclusion, bool *doubleSided) +{ + ASSERT(polygonIndex >= 0); + ASSERT(polygonIndex < mNumPolygons); + ASSERT(mPolygonOffsets[polygonIndex] >= 0); + ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos); + + if (polygonIndex < 0 || polygonIndex >= mNumPolygons) + { + return FMOD_ERR_INVALID_PARAM; + } + + { +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + FMOD_POLYGON &polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]]; + + if (directOcclusion) + { + *directOcclusion = polygon.directOcclusion; + } + if (reverbOcclusion) + { + *reverbOcclusion = polygon.reverbOcclusion; + } + if (doubleSided) + { + *doubleSided = (polygon.flags & FMOD_POLYGON_FLAG_DOUBLE_SIDED) != 0; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::flush() +{ + /* +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + */ + int vertex; + + // iterate through polygons in the update list, calucate normals, aabb and add to octree. + OctreeNode* node = mPolygonUpdateList; + mPolygonUpdateList = 0; + while (node) + { + OctreeNode* next = node->nextItem; + node->nextItem = 0; + + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)node; + FMOD_VECTOR* vertices =& polygon.vertices; + + // calculate polygon normal + float xN = 0.0f; + float yN = 0.0f; + float zN = 0.0f; + // todo: return an error if a polygon has less then 3 vertices. + for (vertex = 0; vertex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK) - 2; vertex++) + { + float xA = vertices[vertex + 1].x -vertices[0].x; + float yA = vertices[vertex + 1].y -vertices[0].y; + float zA = vertices[vertex + 1].z -vertices[0].z; + float xB = vertices[vertex + 2].x -vertices[0].x; + float yB = vertices[vertex + 2].y -vertices[0].y; + float zB = vertices[vertex + 2].z -vertices[0].z; + // cross product + xN += yA * zB - zA * yB; + yN += zA * xB - xA * zB; + zN += xA * yB - yA * xB; + } + float fMagnidued = (float)sqrt(xN * xN + yN * yN + zN * zN); + if (fMagnidued > 0.0f) // a tollerance here might be called for + { + polygon.flags &= ~FMOD_POLYGON_FLAG_INVALID; + xN /= fMagnidued; + yN /= fMagnidued; + zN /= fMagnidued; + } + else + { + polygon.flags |= FMOD_POLYGON_FLAG_INVALID; + } + polygon.normal.x = xN; + polygon.normal.y = yN; + polygon.normal.z = zN; + polygon.distance = FMOD_Vector_DotProduct(&vertices[0], &polygon.normal); + + // add to octree + mOctree.addInternalNode(&polygon.nodeInternal); + FMOD_AABB& aabb = polygon.node.aabb; + aabb.xMax = aabb.xMin = vertices[0].x; + aabb.yMax = aabb.yMin = vertices[0].y; + aabb.zMax = aabb.zMin = vertices[0].z; + for (vertex = 1; vertex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); vertex++) + { + aabb.xMax = MAX(aabb.xMax, vertices[vertex].x); + aabb.xMin = MIN(aabb.xMin, vertices[vertex].x); + aabb.yMax = MAX(aabb.yMax, vertices[vertex].y); + aabb.yMin = MIN(aabb.yMin, vertices[vertex].y); + aabb.zMax = MAX(aabb.zMax, vertices[vertex].z); + aabb.zMin = MIN(aabb.zMin, vertices[vertex].z); + } + // we need to grow the bounding box a little to cope with the + // case of an axis alligned polygon that might be right on the edge of a + // bounding box. + // Becaouse the line is clamped to each box in the tree before testing against + // the polygon, we need to make the boxes a little bit bigger then the + // polygons to make sure the line still passes through them. + float fTollerance = aabb.xMax - aabb.xMin; + fTollerance = MAX(fTollerance, aabb.yMax - aabb.yMin); + fTollerance = MAX(fTollerance, aabb.zMax - aabb.zMin); + fTollerance *= 0.01f; + aabb.xMin -= fTollerance; + aabb.xMax += fTollerance; + aabb.yMin -= fTollerance; + aabb.yMax += fTollerance; + aabb.zMin -= fTollerance; + aabb.zMax += fTollerance; + + if ((polygon.flags & FMOD_POLYGON_FLAG_INVALID) == 0) + { + mOctree.insertItem(&polygon.node); + } + node = next; + } + + // update spacial data + //mOctree.calculateAverageNodeElements(); // just for testing + mOctree.getAABB(&mAABB); + updateSpatialData(); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setActive(bool active) +{ + setToBeUpdated(); + + mActive = active; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getActive(bool *active) +{ + if (!active) + { + return FMOD_ERR_INVALID_PARAM; + } + + *active = mActive; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setRotation(const FMOD_VECTOR* forward, const FMOD_VECTOR* up) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + + if (!forward) + { + return FMOD_ERR_INVALID_PARAM; + } + if (!up) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mForward.x == forward->x && + mForward.y == forward->y && + mForward.z == forward->z && + mUp.x == up->x && + mUp.y == up->y && + mUp.z == up->z) + { + return FMOD_OK; + } + + mForward = *forward; + mUp = *up; + calculateMatrix(); + setToBeUpdated(); + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getRotation(FMOD_VECTOR* forward, FMOD_VECTOR* up) +{ + if (forward) + { + *forward = mForward; + } + + if (up) + { + *up = mUp; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setPosition(const FMOD_VECTOR* position) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mPosition.x == position->x && mPosition.y == position->y && mPosition.z == position->z) + { + return FMOD_OK; + } + + mPosition = *position; + + setToBeUpdated(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getPosition(FMOD_VECTOR* position) +{ + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + *position = mPosition; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setScale(const FMOD_VECTOR* scale) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + + if (!scale) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (scale->x == 0.0f || scale->y == 0.0f || scale->z == 0.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mScale.x == scale->x && + mScale.y == scale->y && + mScale.z == scale->z) + { + return FMOD_OK; + } + mScale = *scale; + calculateMatrix(); + setToBeUpdated(); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getScale(FMOD_VECTOR *scale) +{ + if (!scale) + { + return FMOD_ERR_INVALID_PARAM; + } + + *scale = mScale; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::save(void *data, int *dataSize) +{ + FMOD_RESULT result = FMOD_OK; + + if (!dataSize) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (data) + { + int tmpDataSize = *dataSize; + result = serialiser(data, &tmpDataSize, true, false, saveData); + } + else + { + result = serialiser(data, dataSize, false, false, countData); + } + + return result; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::load(const void *data, int dataSize) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + + if (!data) + { + return FMOD_ERR_INVALID_PARAM; + } + + return serialiser((void*)data, &dataSize, false, true, loadData); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::saveData(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize) +{ + if (*fileDataIndex + liveDataSize > dataSize) + return FMOD_ERR_INVALID_PARAM; + +#ifdef PLATFORM_ENDIAN_BIG + if (liveDataSize == 4) + { + *(unsigned int*)&((unsigned char*)fileData)[*fileDataIndex] = *(unsigned int*)liveData; + } + else + { + FMOD_memcpy(&((unsigned char*)fileData)[*fileDataIndex], liveData, liveDataSize); + } +#else + FMOD_memcpy(&((unsigned char*)fileData)[*fileDataIndex], liveData, liveDataSize); +#endif + + *fileDataIndex += liveDataSize; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::loadData(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize) +{ + if (*fileDataIndex + liveDataSize > dataSize) + return FMOD_ERR_INVALID_PARAM; + +#ifdef PLATFORM_ENDIAN_BIG + if (liveDataSize == 4) + { + *(unsigned int*)liveData = *(unsigned int*)&((unsigned char*)fileData)[*fileDataIndex]; + } + else + { + FMOD_memcpy(liveData, &((unsigned char*)fileData)[*fileDataIndex], liveDataSize); + } +#else + FMOD_memcpy(liveData, &((unsigned char*)fileData)[*fileDataIndex], liveDataSize); +#endif + + *fileDataIndex += liveDataSize; + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::countData(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize) +{ + *fileDataIndex += liveDataSize; + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::serialiser( + void *data, + int *dataSize, + bool bWrite, + bool bRead, + FMOD_RESULT (*serialiseData)(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize)) +{ + bool bCount = !bWrite && !bRead; + + #define _CHECK_RESULT(x) \ + { \ + FMOD_RESULT result = (x); \ + if (result != FMOD_OK) \ + { \ + if (vertexArray) \ + FMOD_Memory_Free(vertexArray); \ + return result; \ + } \ + } + + FMOD_VECTOR* vertexArray = 0; + int index = 0; + + unsigned int header; + const unsigned int realHeader = 'F' + ('M' << 8) + ('O' << 16) + ('D' << 24); + header = realHeader; + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &header, sizeof (int))) + if (header != realHeader) + { + return FMOD_ERR_INVALID_PARAM; + } + + int dataSizeCheck = *dataSize; + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &dataSizeCheck, sizeof (int))) + if (bRead) + { + if (*dataSize != dataSizeCheck) + { + return FMOD_ERR_INVALID_PARAM; + } + } + + int numPolygons; + if (bWrite || bCount) + { + _CHECK_RESULT(getNumPolygons(&numPolygons)) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &numPolygons, sizeof (int))) + + int maxPolygons; + int maxVertices; + if (bWrite || bCount) + { + _CHECK_RESULT(getMaxPolygons(&maxPolygons, &maxVertices)) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &maxPolygons, sizeof (int))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &maxVertices, sizeof (int))) + + int maxNumVerticesInOnPolygon = 64; // not very likely that this will be broken + vertexArray = (FMOD_VECTOR*)FMOD_Memory_Alloc(maxNumVerticesInOnPolygon * sizeof (FMOD_VECTOR)); + if (!vertexArray) + return FMOD_ERR_MEMORY; + + if (bRead) + { + _CHECK_RESULT(alloc(maxPolygons, maxVertices)) + } + for (int polygonIndex = 0; polygonIndex < numPolygons; polygonIndex++) + { + int numVertices; + if (bWrite || bCount) + { + _CHECK_RESULT(getPolygonNumVertices(polygonIndex, &numVertices)) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &numVertices, sizeof (int))) + + if (maxNumVerticesInOnPolygon < numVertices) + { + // in the unlikly event we have more then 64 vertices in one polygon we need to + // reallocate the array + FMOD_Memory_Free(vertexArray); + vertexArray = (FMOD_VECTOR*)FMOD_Memory_Alloc(maxNumVerticesInOnPolygon * sizeof (FMOD_VECTOR)); + if (!vertexArray) + return FMOD_ERR_MEMORY; + } + + for (int vertexIndex = 0; vertexIndex < numVertices; vertexIndex++) + { + // FMOD_VECTOR vertex = FMOD_Memory_Alloc(mMaxNumVertices * sizeof (int)); + if (bWrite) + { + _CHECK_RESULT(getPolygonVertex(polygonIndex, vertexIndex, &vertexArray[vertexIndex])) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &vertexArray[vertexIndex].x, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &vertexArray[vertexIndex].y, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &vertexArray[vertexIndex].z, sizeof (float))) + } + + float directOcclusion; + float reverbOcclusion; + bool bDoubleSided = false;; + if (bWrite || bCount) + { + _CHECK_RESULT(getPolygonAttributes(polygonIndex, &directOcclusion, &reverbOcclusion, &bDoubleSided)) + } + int doubleSided = bDoubleSided ? 1 : 0; + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &directOcclusion, sizeof (int))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &reverbOcclusion, sizeof (int))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &doubleSided, sizeof (int))) + bDoubleSided = doubleSided != 0; + + if (bRead) + { + _CHECK_RESULT(addPolygon(directOcclusion, reverbOcclusion, bDoubleSided, numVertices, vertexArray, 0)) + } + } + + FMOD_Memory_Free(vertexArray); + vertexArray = 0; + + FMOD_VECTOR forward; + FMOD_VECTOR up; + if (bWrite || bCount) + { + _CHECK_RESULT(getRotation(&forward, &up)) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &forward.x, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &forward.y, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &forward.z, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &up.x, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &up.y, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &up.z, sizeof (float))) + if (bRead) + { + _CHECK_RESULT(setRotation(&forward, &up)) + } + + FMOD_VECTOR position; + if (bWrite || bCount) + { + _CHECK_RESULT(getPosition(&position)) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &position.x, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &position.y, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &position.z, sizeof (float))) + if (bRead) + { + _CHECK_RESULT(setPosition(&position)) + } + + FMOD_VECTOR scale; + if (bWrite || bCount) + { + _CHECK_RESULT(getScale(&scale)) + } + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &scale.x, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &scale.y, sizeof (float))) + _CHECK_RESULT(serialiseData(data, *dataSize, &index, &scale.z, sizeof (float))) + if (bRead) + { + _CHECK_RESULT(setScale(&scale)) + } + +#undef _CHECK_RESULT + + if (bRead || bWrite) + { + if (*dataSize != index) + { + return FMOD_ERR_INVALID_PARAM; + } + } + else + { + *dataSize = index; + } + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +bool GeometryI::lineTest(LineTestData* lineTestData) +{ + FMOD_VECTOR startBck = lineTestData->start; + FMOD_VECTOR endBck = lineTestData->end; + + FMOD_VECTOR startTmp = lineTestData->start; + startTmp.x -= mPosition.x; + startTmp.y -= mPosition.y; + startTmp.z -= mPosition.z; + FMOD_VECTOR endTmp = lineTestData->end; + endTmp.x -= mPosition.x; + endTmp.y -= mPosition.y; + endTmp.z -= mPosition.z; + + matrixMult(mInvMatrix, &startTmp, &lineTestData->start); + matrixMult(mInvMatrix, &endTmp, &lineTestData->end); + + bool bResult = mOctree.testLine(octreeLineTestCallback, lineTestData, lineTestData->start, lineTestData->end); + + lineTestData->start = startBck; + lineTestData->end = endBck; + + lineTestData->geometryI = 0; + return bResult; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::setWorldSize(float worldSize) +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + mOctree.setMaxSize(worldSize); + int poly; + for (poly = 0; poly < mNumPolygons; poly++) + { + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[poly]]; + mOctree.deleteItem(&polygon.node); + } + for (poly = 0; poly < mNumPolygons; poly++) + { + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[poly]]; + polygon.node.nextItem = mPolygonUpdateList; + mPolygonUpdateList = &polygon.node; + } + setToBeUpdated(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void GeometryI::removeFromTree() +{ +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true); +#endif + mGeometryMgr->mainOctree()->deleteItem(&mSpatialData->octreeNode); +} + + +#if defined(FMOD_GEOMETRY_DEBUGGING) +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void GeometryI::testLineTestForEachPolygon() +{ + for (int poly = 0; poly < mNumPolygons; poly++) + { + FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[poly]]; + if (polygon.flags & FMOD_POLYGON_FLAG_INVALID) + continue; + FMOD_VECTOR center = { 0.0f, 0.0f, 0.0f }; + int numVertices = (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); + ASSERT(numVertices >= 3); + for (int i = 0; i < numVertices; i++) + { + center.x += (&polygon.vertices)[i].x; + center.y += (&polygon.vertices)[i].y; + center.z += (&polygon.vertices)[i].z; + } + center.x /= numVertices; + center.y /= numVertices; + center.z /= numVertices; + FMOD_VECTOR start; + start.x = center.x - polygon.normal.x * 100.0f; + start.y = center.y - polygon.normal.y * 100.0f; + start.z = center.z - polygon.normal.z * 100.0f; + FMOD_VECTOR end; + end.x = center.x + polygon.normal.x * 100.0f; + end.y = center.y + polygon.normal.y * 100.0f; + end.z = center.z + polygon.normal.z * 100.0f; + polygon.flags |= 0x80000000; + float a, b; + mGeometryMgr->lineTestAll(&start, &end, &a, &b); + ASSERT((polygon.flags & 0x80000000) == 0); + polygon.flags &= ~0x80000000; + } +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::validate(Geometry *geometry, GeometryI **geometryi) +{ + if (!geometryi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!geometry) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *geometryi = (GeometryI *)geometry; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT GeometryI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT GeometryI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + //AJS incomplete + tracker->add(false, FMOD_MEMBITS_GEOMETRY, sizeof(*this)); + + return FMOD_OK; +} + +#endif + + +} + +#endif // FMOD_SUPPORT_GEOMETRY diff --git a/src/fmod_geometryi.h b/src/fmod_geometryi.h new file mode 100755 index 0000000..6735fec --- /dev/null +++ b/src/fmod_geometryi.h @@ -0,0 +1,186 @@ +#ifndef _FMOD_GEOMETRYI_H +#define _FMOD_GEOMETRYI_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +#include "fmod_linkedlist.h" +#include "fmod_octree.h" + +#include "fmod.hpp" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class GeometryMgr; + enum + { + FMOD_POLYGON_NUM_VERTICES_MASK = 0xffff, + FMOD_POLYGON_FLAG_DOUBLE_SIDED = 0x10000, + FMOD_POLYGON_FLAG_INVALID = 0x20000, + }; + struct FMOD_POLYGON + { + OctreeNode node; + OctreeNode nodeInternal; + float distance; + FMOD_VECTOR normal; + float directOcclusion; + float reverbOcclusion; + int flags; + FMOD_VECTOR vertices; + }; + + class GeometryI : public LinkedListNode + { + DECLARE_MEMORYTRACKER + + private: + struct SpatialData + { + OctreeNode octreeNode; + OctreeNode octreeInternalNode; + GeometryI *geometry; + }; + struct LineTestData + { + FMOD_VECTOR start; + FMOD_VECTOR end; + float directTransmission; // 1.0f - directOcclusion + float reverbTransmission; // 1.0f - reverbOcclusion + GeometryI *geometryI; + }; + + friend class GeometryMgr; + + GeometryMgr *mGeometryMgr; + int mMaxNumVertices; + int mNumVertices; + int mMaxNumPolygons; + int mNumPolygons; + int *mPolygonOffsets; + int mPolygonDataPos; + unsigned char *mPolygonData; + void *mUserData; + OctreeNode *mPolygonUpdateList; + FMOD_AABB mAABB; + + bool mActive; + FMOD_VECTOR mForward; + FMOD_VECTOR mUp; + FMOD_VECTOR mPosition; + FMOD_VECTOR mScale; + float mMatrix[3][4]; + float mInvMatrix[3][4]; + + SpatialData *mSpatialData; + Octree mOctree; + GeometryI *mNextUpdateItem; + bool mToBeUpdated; + + void calculateMatrix(); + void updateSpatialData(); + void setToBeUpdated(); + + public: + + GeometryI(GeometryMgr* geometryMgr); + + FMOD_RESULT release (); + FMOD_RESULT alloc (int maxNumPolygons, int maxNumVertices); + + FMOD_RESULT addPolygon (float directOcclusion, float reverbOcclusion, bool doubleSided, int numVertices, const FMOD_VECTOR *vertices, int *polygonIndex); + + FMOD_RESULT getNumPolygons (int *numPolygons); + FMOD_RESULT getMaxPolygons (int *maxPolygons, int *maxVertices); + FMOD_RESULT getPolygonNumVertices (int polygonIndex, int *numVertices); + FMOD_RESULT setPolygonVertex (int polygonIndex, int vertexIndex, const FMOD_VECTOR *vertex); + FMOD_RESULT getPolygonVertex (int polygonIndex, int vertexIndex, FMOD_VECTOR *vertex); + FMOD_RESULT setPolygonAttributes (int polygonIndex, float directOcclusion, float reverbOcclusion, bool doubleSided); + FMOD_RESULT getPolygonAttributes (int polygonIndex, float *directOcclusion, float *reverbOcclusion, bool *doubleSided); + FMOD_RESULT flush (); + + FMOD_RESULT setActive (bool active); + FMOD_RESULT getActive (bool *active); + FMOD_RESULT setRotation (const FMOD_VECTOR *forward, const FMOD_VECTOR *up); + FMOD_RESULT getRotation (FMOD_VECTOR *forward, FMOD_VECTOR *up); + FMOD_RESULT setPosition (const FMOD_VECTOR *position); + FMOD_RESULT getPosition (FMOD_VECTOR *position); + FMOD_RESULT setScale (const FMOD_VECTOR *scale); + FMOD_RESULT getScale (FMOD_VECTOR *scale); + + FMOD_RESULT save (void *data, int *dataSize); + FMOD_RESULT load (const void *data, int dataSize); + static FMOD_RESULT saveData (void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize); + static FMOD_RESULT loadData (void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize); + static FMOD_RESULT countData (void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize); + FMOD_RESULT serialiser (void *data, int *dataSize, bool bWrite, bool bRead, FMOD_RESULT (*serialiseData)(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize)); + + // Userdata set/get. + FMOD_RESULT F_API setUserData (void *userdata); + FMOD_RESULT F_API getUserData (void **userdata); + + FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + + + bool lineTest (LineTestData* lineTestData); + + FMOD_RESULT setWorldSize (float worldSize); + void removeFromTree (); + +#if defined(FMOD_GEOMETRY_DEBUGGING) + void testLineTestForEachPolygon(); +#endif + + static FMOD_RESULT validate(Geometry *geometry, GeometryI **geometryi); + static bool octreeLineTestCallback(OctreeNode* item, void* data); + }; +} + + +#else + +#include "fmod.hpp" + +namespace FMOD +{ + class GeometryI + { + public: + + GeometryI() {} + ~GeometryI() {} + + FMOD_RESULT F_API addPolygon (float directOcclusion, float reverbOcclusion, bool doubleSided, int numVertices, const FMOD_VECTOR *vertices, int *polygonIndex) { return FMOD_ERR_UNIMPLEMENTED; } + + FMOD_RESULT F_API getNumPolygons (int *numPolygons) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getMaxNumPolygons (int *maxNumPolygons) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getPolygonNumVertices (int polygonIndex, int *numVertices) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API setPolygonVertex (int polygonIndex, int vertexIndex, const FMOD_VECTOR *vertex) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getPolygonVertex (int polygonIndex, int vertexIndex, FMOD_VECTOR *vertex) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API setPolygonAttributes (int polygonIndex, float directOcclusion, float reverbOcclusion, bool doubleSided) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getPolygonAttributes (int polygonIndex, float *directOcclusion, float *reverbOcclusion, bool *doubleSided) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API flush () { return FMOD_ERR_UNIMPLEMENTED; } + + FMOD_RESULT F_API setRotation (const FMOD_VECTOR *forward, const FMOD_VECTOR *up) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getRotation (FMOD_VECTOR *forward, FMOD_VECTOR *up) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API setPosition (const FMOD_VECTOR *position) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getPosition (FMOD_VECTOR *position) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API setScale (const FMOD_VECTOR *scale) { return FMOD_ERR_UNIMPLEMENTED; } + FMOD_RESULT F_API getScale (FMOD_VECTOR *scale) { return FMOD_ERR_UNIMPLEMENTED; } + + + static FMOD_RESULT validate(Geometry *geometry, GeometryI **geometryi) { return FMOD_ERR_UNIMPLEMENTED; } + + }; +} + +#endif + + +#endif + diff --git a/src/fmod_globals.cpp b/src/fmod_globals.cpp new file mode 100755 index 0000000..c2b7d01 --- /dev/null +++ b/src/fmod_globals.cpp @@ -0,0 +1,94 @@ +#include "fmod_globals.h" + +#include "fmod_memory.h" +#include "fmod_string.h" + +#ifndef PLATFORM_PS2_IOP + #include "fmod_systemi.h" +#endif + +namespace FMOD +{ + +#if defined(FMOD_STATICFORPLUGINS) || defined(FMOD_USE_GETGLOBALS) + +#ifndef PLATFORM_SOLARIS // Solaris merges the gGlobals, get rid of this one, keeping the valid non fmod_event one +Global *gGlobal = 0; +#endif + +#else + +static Global gGlobalMem; + +#ifndef PLATFORM_PS3_SPU +#ifndef PLATFORM_PS2_IOP +static SystemI gSystemHeadMem; +#endif +static MemPool gSystemPoolMem; +#endif + +Global *gGlobal = &gGlobalMem; + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +void Global::init() +{ +#ifndef PLATFORM_PS3_SPU +#ifndef PLATFORM_PS2_IOP + gSystemHead = &gSystemHeadMem; +#endif + gSystemPool = &gSystemPoolMem; +#endif + +#ifdef FMOD_DEBUG + gDebugLevel = FMOD_DEBUG_LEVEL_LOG | FMOD_DEBUG_LEVEL_WARNING | FMOD_DEBUG_LEVEL_ERROR; + #ifdef FMOD_DEBUG_TOFILE + gDebugMode = DEBUG_FILE; + #else + gDebugMode = DEBUG_STDOUT; + #endif + + #ifdef PLATFORM_PS2 + FMOD_strcpy(FMOD::gGlobal->gDebugFilename, "host0:fmod.log"); + #else + FMOD_strcpy(FMOD::gGlobal->gDebugFilename, "fmod.log"); + #endif + +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_SOLARIS) || defined(PLATFORM_IPHONE) + gStartTimeSeconds = 0; +#endif + + gDSPClock.mHi = 0; + gDSPClock.mLo = 0; + gDSPClockTimeStamp = 0; + gSystemInitCount = 0; + gFileBusy = 0; + gSystemCallback = 0; + gMemoryTypeFlags = 0xFFFFFFFF; + gFileCrit = 0; + +#ifdef FMOD_SUPPORT_PROFILE + gProfile = 0; + gProfileCodec = 0; + gProfileChannel = 0; + gProfileCpu = 0; + gProfileDsp = 0; +#endif + +} + +#endif + + +} diff --git a/src/fmod_globals.h b/src/fmod_globals.h new file mode 100755 index 0000000..710cc47 --- /dev/null +++ b/src/fmod_globals.h @@ -0,0 +1,82 @@ +#ifndef _FMOD_GLOBALS_H +#define _FMOD_GLOBALS_H + +#include "fmod_settings.h" + +#include "fmod_debug.h" +#include "fmod_types.h" +#include "fmod.h" +#include "fmod_linkedlist.h" +#include "fmod_os_misc.h" + +#ifdef FMOD_SUPPORT_DLMALLOC + #include "../lib/dlmalloc/dlmalloc.h" +#endif + +namespace FMOD +{ + class SystemI; + class MemPool; +#ifdef FMOD_SUPPORT_PROFILE + class Profile; + class ProfileCodec; + class ProfileChannel; + class ProfileCpu; + class ProfileDsp; +#endif + + class Global + { + public : + + void init(); + Global() { init(); } + +#ifndef PLATFORM_PS3_SPU +#ifndef PLATFORM_PS2_IOP + SystemI *gSystemHead; +#endif + MemPool *gSystemPool; +#endif + int gSystemInitCount; + +#ifdef FMOD_DEBUG + FMOD_DEBUGLEVEL gDebugLevel; + FMOD_DEBUGMODE gDebugMode; + char gDebugFilename[256]; +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_SOLARIS) || defined(PLATFORM_IPHONE) + unsigned int gStartTimeSeconds; // Common time offset that can be used by event system and low level os time functions +#endif + + FMOD_UINT64P gDSPClock; // The number of times Output::mix() has been called + unsigned int gDSPClockTimeStamp; // System time in ms of when the last call to Output::mix() completed + int gFileBusy; + FMOD_SYSTEM_CALLBACK gSystemCallback; + FMOD_MEMORY_TYPE gMemoryTypeFlags; + + LinkedListNode gFileThreadHead; + FMOD_OS_CRITICALSECTION *gFileCrit; + + FMOD_OS_CRITICALSECTION *gAsyncCrit; + +#ifdef FMOD_SUPPORT_DLMALLOC + struct malloc_params gDLMalloc_mparams; +#endif + +#ifdef FMOD_SUPPORT_PROFILE + Profile *gProfile; + ProfileCodec *gProfileCodec; + ProfileChannel *gProfileChannel; + ProfileCpu *gProfileCpu; + ProfileDsp *gProfileDsp; +#endif + + int gRandomValue; + }; + + extern Global *gGlobal; +} + +#endif diff --git a/src/fmod_historybuffer_pool.cpp b/src/fmod_historybuffer_pool.cpp new file mode 100755 index 0000000..075fa18 --- /dev/null +++ b/src/fmod_historybuffer_pool.cpp @@ -0,0 +1,278 @@ +#include "fmod_settings.h" + +#include "fmod_historybuffer_pool.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +HistoryBufferPool::HistoryBufferPool() +{ + mBufferSize = 0; + mMaxHistoryBuffers = 0; + mBufferPool = 0; + mMemoryBlock = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT HistoryBufferPool::init(int multichannelbuffercount, int channelsperbuffer) +{ + mBufferSize = FMOD_HISTORYBUFFERLEN * sizeof(float); + +#if !defined(PLATFORM_PS3_SPU) && (defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)) + mBufferSize += 16; /* for alignment */ +#endif + + mMaxHistoryBuffers = multichannelbuffercount * channelsperbuffer; + if (mMaxHistoryBuffers == 0) + { + return FMOD_OK; + } + + /* allocate memory block */ + int totalsize = mMaxHistoryBuffers * mBufferSize; + mMemoryBlock = FMOD_Memory_Alloc(totalsize); + if (!mMemoryBlock) + { + release(); + return FMOD_ERR_MEMORY; + } + + /* allocate array of BufferInfos to keep track of data */ + mBufferPool = (BufferInfo*)FMOD_Memory_Alloc(mMaxHistoryBuffers * sizeof(BufferInfo)); + if (!mBufferPool) + { + release(); + return FMOD_ERR_MEMORY; + } + + int i; + for (i = 0; i < mMaxHistoryBuffers; ++i) + { + mBufferPool[i].numchannels = 0; + mBufferPool[i].buffermemory = (float *)((FMOD_UINT_NATIVE)mMemoryBlock + (mBufferSize * i)); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT HistoryBufferPool::alloc(float **historybuffer, int numchannels) +{ + if (!historybuffer || !numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *historybuffer = 0; + + /* find free space for this buffer */ + int bufferindex; + for (bufferindex = 0; bufferindex < mMaxHistoryBuffers; ++bufferindex) + { + int count; + for (count = bufferindex; (count < (bufferindex + numchannels)); count++) + { + if (mBufferPool[count].numchannels != 0 || + mMaxHistoryBuffers <= count) + { + bufferindex = count; + break; + } + } + if ((count - bufferindex) == numchannels) + { + break; + } + } + + if (bufferindex < mMaxHistoryBuffers) + { + int count; + for (count = bufferindex; count < (bufferindex + numchannels); count++) + { + if (mMaxHistoryBuffers < count) + { + return FMOD_ERR_INTERNAL; //PAS + } + mBufferPool[count].numchannels = numchannels; + } + + *historybuffer = mBufferPool[bufferindex].buffermemory; + /* intialize requested memory block to zero */ + FMOD_memset(*historybuffer, 0, mBufferSize * numchannels); + } + else + { + /* not in pool */ + *historybuffer = (float*)FMOD_Memory_Calloc(mBufferSize * numchannels); + } + + if (!*historybuffer) + { + return FMOD_ERR_MEMORY; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT HistoryBufferPool::free(float *historybuffer) +{ + if (mMaxHistoryBuffers != 0) + { + float *poolstart = mBufferPool[0].buffermemory; + float *poolend = mBufferPool[mMaxHistoryBuffers - 1].buffermemory; + + /* check history buffer is in pool*/ + if (poolstart <= historybuffer && historybuffer <= poolend) + { + /* determine the bufferindex of this block */ + int bufferindex = ((FMOD_UINT_NATIVE)historybuffer - (FMOD_UINT_NATIVE)poolstart) / mBufferSize; + int numchannels = mBufferPool[bufferindex].numchannels; + + int count; + for (count = bufferindex; count < (bufferindex + numchannels); count++) + { + if (mMaxHistoryBuffers < count || + mBufferPool[count].numchannels != numchannels || + poolend < mBufferPool[count].buffermemory ) + { + return FMOD_ERR_INTERNAL; //PAS + } + mBufferPool[count].numchannels = 0; + } + return FMOD_OK; + } + } + + /* not in pool */ + FMOD_Memory_Free(historybuffer); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT HistoryBufferPool::release() +{ + if (mBufferPool) + { + FMOD_Memory_Free(mBufferPool); + mBufferPool = 0; + } + if (mMemoryBlock) + { + FMOD_Memory_Free(mMemoryBlock); + mMemoryBlock = 0; + } + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT HistoryBufferPool::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mMemoryBlock) + { + //mMemoryBlock + tracker->add(false, FMOD_MEMBITS_CHANNEL, mMaxHistoryBuffers * mBufferSize); + //mBufferPool + tracker->add(false, FMOD_MEMBITS_CHANNEL, mMaxHistoryBuffers * sizeof(BufferInfo)); + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_historybuffer_pool.h b/src/fmod_historybuffer_pool.h new file mode 100755 index 0000000..e4b9064 --- /dev/null +++ b/src/fmod_historybuffer_pool.h @@ -0,0 +1,44 @@ +#ifndef _FMOD_HISTORYBUFFER_POOL_H +#define _FMOD_HISTORYBUFFER_POOL_H + +#include "fmod_settings.h" + +#include "fmod_memory.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + const int FMOD_HISTORYBUFFERLEN = 16 * 1024; + + typedef struct BufferInfo + { + int numchannels; + float *buffermemory; + } BufferInfo; + + class HistoryBufferPool + { + DECLARE_MEMORYTRACKER + + private: + int mBufferSize; + int mMaxHistoryBuffers; + + BufferInfo *mBufferPool; + void *mMemoryBlock; + + public: + HistoryBufferPool(); + + FMOD_RESULT init(int multichannelbuffercount, int channelsperbuffer); + FMOD_RESULT alloc(float **historybuffer, int numchannels); + FMOD_RESULT free (float *historybuffer); + FMOD_RESULT release(); + }; +} + +#endif + diff --git a/src/fmod_linkedlist.h b/src/fmod_linkedlist.h new file mode 100755 index 0000000..2ce5ec9 --- /dev/null +++ b/src/fmod_linkedlist.h @@ -0,0 +1,195 @@ +#ifndef _FMOD_LINKEDLIST_H +#define _FMOD_LINKEDLIST_H + +#include "fmod_types.h" + +namespace FMOD +{ + class LinkedListNode + { + friend class SortedLinkedListNode; + + private: + + LinkedListNode *mNodeNext; + LinkedListNode *mNodePrev; + void *mNodeData; + + public: + LinkedListNode() { initNode(); } +#ifdef FMOD_SUPPORT_RTTI + virtual ~LinkedListNode() { } +#endif + + inline LinkedListNode *getNext() const + { + return mNodeNext; + } + inline LinkedListNode *getPrev() const + { + return mNodePrev; + } + inline void setData(void *data) + { + mNodeData = data; + } + inline void *getData() const + { + return mNodeData; + } + inline void initNode() + { + mNodePrev = mNodeNext = this; + mNodeData = 0; + } + inline void removeNode() + { + mNodePrev->mNodeNext = mNodeNext; + mNodeNext->mNodePrev = mNodePrev; + mNodeNext = mNodePrev = this; + mNodeData = 0; + } + void addAfter(LinkedListNode *node) + { + mNodeNext = node->mNodeNext; + mNodePrev = node; + mNodeNext->mNodePrev = this; + mNodePrev->mNodeNext = this; + } + void addBefore(LinkedListNode *node) + { + mNodePrev = node->mNodePrev; + mNodeNext = node; + mNodeNext->mNodePrev = this; + mNodePrev->mNodeNext = this; + } + bool exists(LinkedListNode *node) + { + LinkedListNode *current = this->mNodeNext; + + do + { + if (current == node) + { + return true; + } + current = current->mNodeNext; + } while (current != this); + + return false; + } + inline bool isEmpty() const + { + if (mNodeNext == this && mNodePrev == this) + { + return true; + } + + return false; + } + int count() const + { + int i = 0; + LinkedListNode *current = this->mNodeNext; + + while (current != this) + { + i++; + current = current->mNodeNext; + } + + return i; + } + LinkedListNode *getNodeByIndex(int i) const + { + if (i < 0) + { + return 0; + } + + LinkedListNode *current = this->mNodeNext; + + if (current == this) + { + return 0; + } + + for (;i > 0;i--) + { + current = current->mNodeNext; + + if (current == this) + { + return 0; + } + } + + return current; + } + int getNodeIndex(LinkedListNode *node) const + { + LinkedListNode *current = this->mNodeNext; + + for (int i=0; current != this; i++, current = current->mNodeNext) + { + if (current == node) + { + return i; + } + } + + return -1; + } + }; + + class SortedLinkedListNode : public LinkedListNode + { + private: + + unsigned int mNodePriority; + + public: + SortedLinkedListNode() { initNode(); } + + inline void initNode() + { + mNodePriority = (unsigned int)-1; + mNodePrev = mNodeNext = this; + mNodeData = 0; + } + inline void removeNode() + { + mNodePrev->mNodeNext = mNodeNext; + mNodeNext->mNodePrev = mNodePrev; + mNodeNext = mNodePrev = this; + mNodePriority = (unsigned int)-1; + mNodeData = 0; + } + + void addAt(SortedLinkedListNode *head, SortedLinkedListNode *tail, unsigned int priority) + { + SortedLinkedListNode *current = SAFE_CAST(SortedLinkedListNode, head->mNodeNext); + + do + { + if (priority < current->mNodePriority) + { + mNodePriority = priority; + addBefore(current); + return; + } + current = SAFE_CAST(SortedLinkedListNode, current->mNodeNext); + } while (current->mNodePrev != tail); + } + }; + +} + + + + +#endif + + + + diff --git a/src/fmod_listener.cpp b/src/fmod_listener.cpp new file mode 100755 index 0000000..5011a06 --- /dev/null +++ b/src/fmod_listener.cpp @@ -0,0 +1,57 @@ +#include "fmod_settings.h" + +#include "fmod_listener.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +Listener::Listener() +{ + mPosition.x = 0; + mPosition.y = 0; + mPosition.z = 0; + + mLastPosition.x = 0; + mLastPosition.y = 0; + mLastPosition.z = 0; + + mVelocity.x = 0; + mVelocity.y = 0; + mVelocity.z = 0; + + mUp.x = 0; + mUp.y = 1.0f; + mUp.z = 0; + + mFront.x = 0; + mFront.y = 0; + mFront.z = 1.0f; + + mRight.x = 1.0f; + mRight.y = 0; + mRight.z = 0; + +} + + +} + + diff --git a/src/fmod_listener.h b/src/fmod_listener.h new file mode 100755 index 0000000..e45be7f --- /dev/null +++ b/src/fmod_listener.h @@ -0,0 +1,37 @@ +#ifndef _FMOD_LISTENER_H +#define _FMOD_LISTENER_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +namespace FMOD +{ + const int LISTENER_MAX = 4; + + class Listener + { + public: + + FMOD_VECTOR mPosition; + FMOD_VECTOR mLastPosition; + FMOD_VECTOR mVelocity; + FMOD_VECTOR mLastVelocity; + FMOD_VECTOR mUp; + FMOD_VECTOR mLastUp; + FMOD_VECTOR mFront; + FMOD_VECTOR mLastFront; + FMOD_VECTOR mRight; + bool mMoved; + bool mRotated; + + public: + + Listener(); + + }; +} + +#endif + diff --git a/src/fmod_localcriticalsection.h b/src/fmod_localcriticalsection.h new file mode 100755 index 0000000..1cae41d --- /dev/null +++ b/src/fmod_localcriticalsection.h @@ -0,0 +1,55 @@ +#ifndef _FMOD_LOCALCRITICALSECTION_H +#define _FMOD_LOCALCRITICALSECTION_H + +#include "fmod_settings.h" +#include "fmod_os_misc.h" + +namespace FMOD +{ + class LocalCriticalSection + { + private: + + FMOD_OS_CRITICALSECTION *mCrit; + bool mEntered; + + public: + + LocalCriticalSection() + { + mEntered = false; + } + LocalCriticalSection(FMOD_OS_CRITICALSECTION *crit, bool enternow = false) + { + mEntered = false; + mCrit = crit; + + if (enternow) + { + enter(); + } + } + ~LocalCriticalSection() + { + if (mEntered) + { + leave(); + } + } + + void enter() + { + FMOD_OS_CriticalSection_Enter(mCrit); + mEntered = true; + } + void leave() + { + FMOD_OS_CriticalSection_Leave(mCrit); + mEntered = false; + } + }; +} + +#endif + + diff --git a/src/fmod_memory.cpp b/src/fmod_memory.cpp new file mode 100755 index 0000000..20b715d --- /dev/null +++ b/src/fmod_memory.cpp @@ -0,0 +1,1236 @@ +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_debug.h" +#include "fmod_memory.h" +#include "fmod_os_misc.h" +#include "fmod_string.h" +#include "fmod_types.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef DEBUG + #include <assert.h> +#endif + +//#define FMOD_MEMORY_ALLOC_FAIL_RANDOM 30 +//#define FMOD_MEMORY_ALLOC_FAIL_AFTER 822 +#ifdef FMOD_MEMORY_ALLOC_FAIL_AFTER + static int FMOD_Memory_FailCount = 0; +#endif + +#ifdef FMOD_MEMORY_ALLOC_FAIL_RANDOM + #include <time.h> +#endif + +#define FMOD_MEMORY_BYTESTOBLOCKS(_pool, _bytes) (((_bytes) + (mBlockSize - 1)) / mBlockSize) + + +extern "C" +{ + void *FMOD_Memory_allocC(int len, const char *file, const int line) + { + return FMOD::gGlobal->gSystemPool->alloc(len, file, line); + } + void *FMOD_Memory_callocC(int len, const char *file, const int line) + { + return FMOD::gGlobal->gSystemPool->calloc(len, file, line); + } + + void *FMOD_Memory_reallocC(void *ptr, int len, const char *file, const int line) + { + return FMOD::gGlobal->gSystemPool->realloc(ptr, len, file, line); + } + + void FMOD_Memory_freeC(void *ptr, const char *file, const int line) + { + FMOD::gGlobal->gSystemPool->free(ptr, file, line); + } +} + + +namespace FMOD +{ + + +void *MemSingleton::alloc(int len, const char *file, const int line) +{ + if (!mRefCount) + { + mBuffer = FMOD::gGlobal->gSystemPool->alloc(len, file, line); + FLOG((FMOD_DEBUG_TYPE_MEMORY, __FILE__, __LINE__, "MemSingleton::alloc", "Allocated a singleton memory buffer %d bytes\n", len)); + } + mRefCount++; + + return mBuffer; +} + +void MemSingleton::free(const char *file, const int line) +{ + if (mRefCount) + { + mRefCount--; + } + if (!mRefCount) + { + if (mBuffer) + { + FMOD::gGlobal->gSystemPool->free(mBuffer, file, line); + mBuffer = 0; + FLOG((FMOD_DEBUG_TYPE_MEMORY, __FILE__, __LINE__, "MemSingleton::free", "Freed singleton memory buffer\n")); + } + } +} + + +/* + The wrapper is needed to handle stdcall callbacks when malloc/free is normally cdecl +*/ +void * F_CALLBACK Memory_DefaultMalloc(unsigned int size, FMOD_MEMORY_TYPE type) +{ + return FMOD_OS_Memory_Alloc(size, type); +} +void * F_CALLBACK Memory_DefaultRealloc(void *data, unsigned int size, FMOD_MEMORY_TYPE type) +{ + return FMOD_OS_Memory_Realloc(data, size, type); +} +void F_CALLBACK Memory_DefaultFree(void *ptr, FMOD_MEMORY_TYPE type) +{ + FMOD_OS_Memory_Free(ptr, type); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + void + + [REMARKS] +] +*/ +MemPool::MemPool() +{ + int count; + + mAlloc = Memory_DefaultMalloc; + mRealloc = Memory_DefaultRealloc; + mFree = Memory_DefaultFree; +#ifdef FMOD_MEMORY_THREADSAFE + mCrit = 0; +#endif + for (count = 0; count < FMOD_MEMORY_MAXTHREADS; count++) + { + mCurrentAllocated[count] = 0; + mCurrentAllocatedThreadID[count] = 0; + } + + mCurrentAllocatedSecondary = 0; + mMaxAllocatedSecondary = 0; + +#ifdef FMOD_SUPPORT_DLMALLOC + mDLMallocSpace = 0; +#endif +#ifdef FMOD_DEBUG + mAllocCount = 0; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + void + + [REMARKS] +] +*/ +MemPool::~MemPool() +{ + close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + void + + [REMARKS] +] +*/ +FMOD_RESULT MemPool::init(void *poolmem, int poolsize, int blocksize) +{ + FMOD_RESULT result; + void *oldpoolmem = poolmem; + int count; + + if (!poolmem || !poolsize) + { + return FMOD_ERR_MEMORY; + } + + close(); + + #define ALIGNMENT 256 + + /* + Align the pool memory and reduce the pool size by the number of bytes we had to align by. + */ + poolmem = (unsigned char *)FMOD_ALIGNPOINTER(poolmem, ALIGNMENT); + poolsize -= (int)((FMOD_SINT_NATIVE)poolmem - (FMOD_SINT_NATIVE)oldpoolmem); + poolsize &= ~((unsigned int)blocksize-1); + +#ifdef FMOD_SUPPORT_DLMALLOC + mDLMallocSpace = create_mspace_with_base(poolmem, poolsize, 0); + if (!mDLMallocSpace) + { + return FMOD_ERR_MEMORY; + } +#else + int bitmapsize; + + mBlockSize = blocksize; + #ifdef FMOD_MEMORY_USEBITS + bitmapsize = FMOD_MEMORY_BYTESTOBLOCKS(pool, (poolsize + 7) / 8); + #else + bitmapsize = FMOD_MEMORY_BYTESTOBLOCKS(pool, poolsize); + #endif + bitmapsize += (blocksize-1); + bitmapsize &= ~(blocksize-1); + + mSizeBlocks = FMOD_MEMORY_BYTESTOBLOCKS(pool, poolsize - bitmapsize); + mSizeBytes = mSizeBlocks * mBlockSize; + mBitmap = (unsigned char *)poolmem; + mData = mBitmap + bitmapsize; + + set(0, 0, mSizeBlocks); + + FMOD_memset(mData, 0, mSizeBytes); +#endif + + mNumBlocks = 0; + mMaxBlocks = 0; + mMaxAllocated = 0; + mMaxAllocatedSecondary = 0; + mActualMaxBytes = 0; + mWastage = 0; + mFirstFreeBlock = 0; + mCurrentAllocatedSecondary = 0; + for (count = 0; count < FMOD_MEMORY_MAXTHREADS; count++) + { + mCurrentAllocated[count] = 0; + mCurrentAllocatedThreadID[count] = 0; + } + +#ifdef FMOD_MEMORY_THREADSAFE + result = FMOD_OS_CriticalSection_Create(&mCrit, true); + if (result != FMOD_OK) + { + return result; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + void + + [REMARKS] +] +*/ +FMOD_RESULT MemPool::initCustom(void *poolmem, int poolsize, int blocksize) +{ + FMOD_RESULT result; + int bitmapsize, count; + + if (!poolsize) + { + return FMOD_ERR_MEMORY; + } + + close(); + + mBlockSize = blocksize; + mSizeBlocks = FMOD_MEMORY_BYTESTOBLOCKS(pool, poolsize); + mSizeBlocks = mSizeBlocks & 0xFFFFFFFC; + mSizeBytes = mSizeBlocks * mBlockSize; + +#ifdef FMOD_MEMORY_USEBITS + bitmapsize = (mSizeBlocks + 7) / 8; +#else + bitmapsize = mSizeBlocks; +#endif + + mBitmap = (unsigned char *)FMOD_Memory_Alloc(bitmapsize); + if (!mBitmap) + { + return FMOD_ERR_MEMORY; + } + mData = (unsigned char *)poolmem; + + set(0, 0, mSizeBlocks); + + mNumBlocks = 0; + mMaxBlocks = 0; + mMaxAllocated = 0; + mActualMaxBytes = 0; + mWastage = 0; + mCustomPool = true; + mFirstFreeBlock = 0; + + for (count = 0; count < FMOD_MEMORY_MAXTHREADS; count++) + { + mCurrentAllocated[count] = 0; + mCurrentAllocatedThreadID[count] = 0; + } + + mAlloc = 0; + mRealloc = 0; + mFree = 0; + +#ifdef FMOD_MEMORY_THREADSAFE + result = FMOD_OS_CriticalSection_Create(&mCrit); + if (result != FMOD_OK) + { + return result; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + void + + [REMARKS] +] +*/ +void MemPool::close() +{ + int count; + + if (mCustomPool && mBitmap) + { + FMOD_Memory_Free(mBitmap); + } + + mBitmap = 0; + mData = 0; + mSizeBytes = 0; + mSizeBlocks = 0; + mNumBlocks = 0; + mMaxBlocks = 0; + mMaxAllocated = 0; + + for (count = 0; count < FMOD_MEMORY_MAXTHREADS; count++) + { + mCurrentAllocated[count] = 0; + mCurrentAllocatedThreadID[count] = 0; + } + + mAlloc = Memory_DefaultMalloc; + mRealloc = Memory_DefaultRealloc; + mFree = Memory_DefaultFree; + mCustomPool = false; + +#ifdef FMOD_MEMORY_THREADSAFE + if (mCrit) + { + FMOD_OS_CriticalSection_Free(mCrit, true); + mCrit = 0; + } +#endif +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +void MemPool::set(int blockoffset, int value, int numblocks) +{ + #ifdef FMOD_MEMORY_USEBITS + int byteoffset; + int bitoffset; + int count; + int blocksleft; + + blocksleft = numblocks; + byteoffset = blockoffset / 8; + bitoffset = blockoffset & 7; + + /* + First align the blocks to a 32 block boundary, so we can then do a dword at a time. + */ + count = 0; + if (blockoffset & 31) + { + count = 32 - (blockoffset & 31); + if (count > numblocks) + { + count = numblocks; + } + } + + while (count) + { + if (value) + { + mBitmap[byteoffset] |= (1<<bitoffset); + } + else + { + mBitmap[byteoffset] &= ~(1<<bitoffset); + } + + bitoffset++; + if (bitoffset >= 8) + { + bitoffset = 0; + byteoffset++; + } + count--; + blocksleft--; + } + + count = blocksleft / 8; + if (count) + { + FMOD_memset(&mBitmap[byteoffset], value ? 0xFF : 0, count); + + byteoffset += count; + blocksleft -= count * 8; + } + + count = blocksleft & 31; + while (count) + { + if (value) + { + mBitmap[byteoffset] |= (1<<bitoffset); + } + else + { + mBitmap[byteoffset] &= ~(1<<bitoffset); + } + + bitoffset++; + if (bitoffset >= 8) + { + bitoffset = 0; + byteoffset++; + } + count--; + } + #else + FMOD_memset(&mBitmap[blockoffset], value, numblocks); + #endif + + if (value) + { + if (blockoffset == mFirstFreeBlock) /* Only move firstfree forward if it is the firstfree block that is being used. */ + { + mFirstFreeBlock = blockoffset + numblocks; + } + + /* + If mFirstFreeBlock is now pointing to a used block because we just filled in a hole. Scan it forward. + */ + #ifdef FMOD_MEMORY_USEBITS + byteoffset = mFirstFreeBlock / 8; + bitoffset = mFirstFreeBlock & 7; + if (mBitmap[byteoffset] & (1 << bitoffset)) + #else + if (mBitmap[mFirstFreeBlock]) + #endif + { + mFirstFreeBlock = findFreeBlocks(mFirstFreeBlock, mSizeBlocks, 1); + } + } + else + { + if (blockoffset < mFirstFreeBlock) + { + mFirstFreeBlock = blockoffset; + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +int MemPool::findFreeBlocks(int offset, int to, int numblocks) +{ + int found = 0; + #ifdef FMOD_MEMORY_USEBITS + int one_ls_bitoffset, byteoffset; + + byteoffset = offset >> 3; + one_ls_bitoffset = (1 << (offset & 7)); + + while (found < numblocks && offset < to && offset < mSizeBlocks) + { + if (mBitmap[byteoffset] & one_ls_bitoffset || (!(offset & 31) && *((unsigned int *)&mBitmap[byteoffset]) == 0xFFFFFFFF)) + { + found = 0; + } + else + { + found++; + } + + if (!(offset & 31) && *((unsigned int *)&mBitmap[byteoffset]) == 0xFFFFFFFF) + { + byteoffset += 4; + offset += 32; + } + else + { + offset++; + + one_ls_bitoffset *= 2; + if (!(offset & 7)) + { + one_ls_bitoffset = 1; + byteoffset++; + } + } + } + + #else + + while (found < numblocks && offset < to && offset < mSizeBlocks) + { + if (mBitmap[offset]) + { + found = 0; + } + else + { + found++; + } + + offset++; + } + + #endif + + if (found == numblocks) + { + offset -= numblocks; + } + else + { + offset = -1; + } + + return offset; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +void *MemPool::alloc(int len, const char *file, const int line, FMOD_MEMORY_TYPE type, bool clear) +{ +#ifdef FMOD_DEBUG + ++mAllocCount; +#endif + MemBlockHeader *block = 0; + int numblocks = 0, reallen = len; + + { + #ifdef FMOD_MEMORY_ALLOC_FAIL_RANDOM + srand(clock()); + if ( + #ifdef FMOD_MEMORY_ALLOC_FAIL_AFTER + FMOD_Memory_FailCount >= FMOD_MEMORY_ALLOC_FAIL_AFTER && + #endif + !(FMOD_RAND() % FMOD_MEMORY_ALLOC_FAIL_RANDOM)) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_MEMORY | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "MemPool::alloc", "Failed allocating %-45s line %5d. Wanted %d bytes, current %d/%d (rounding wastage = %d bytes)\n", file, line, len, mCurrentAllocated[0], mSizeBytes, mWastage)); + + if (FMOD::gGlobal->gSystemCallback) + { + char fileLine[256]; + sprintf(fileLine, "%s (%d)", file, line); + + FMOD::gGlobal->gSystemCallback(NULL, FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED, (void*)fileLine, (void*)len); + } + + return 0; + } + #elif defined(FMOD_MEMORY_ALLOC_FAIL_AFTER) + if (FMOD_Memory_FailCount >= FMOD_MEMORY_ALLOC_FAIL_AFTER) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_MEMORY | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "MemPool::alloc", "Failed allocating %-45s line %5d. Wanted %d bytes, current %d/%d (rounding wastage = %d bytes)\n", file, line, len, mCurrentAllocated[0], mSizeBytes, mWastage)); + + if (FMOD::gGlobal->gSystemCallback) + { + char fileLine[256]; + sprintf(fileLine, "%s (%d)", file, line); + + FMOD::gGlobal->gSystemCallback(NULL, FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED, (void*)fileLine, (void*)len); + } + + FMOD_Memory_FailCount = 0; + return 0; + } + #endif + #ifdef FMOD_MEMORY_ALLOC_FAIL_AFTER + FMOD_Memory_FailCount++; + #endif + } + +#ifdef FMOD_MEMORY_THREADSAFE + if (!mCrit) + { + FMOD_RESULT result = FMOD_OS_CriticalSection_Create(&mCrit, true); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "MemPool::alloc", "Error creating critical section!\n")); + + return 0; + } + } + FMOD_OS_CriticalSection_Enter(mCrit); +#endif + + if (!mCustomPool) + { + reallen += sizeof(MemBlockHeader); + } + + if (mAlloc) + { + /* + Only want the flags that have been specified. + */ + type &= FMOD::gGlobal->gMemoryTypeFlags; + + block = (MemBlockHeader *)mAlloc(reallen, type); + } + else + { +#ifdef FMOD_SUPPORT_DLMALLOC + if (!mCustomPool) + { + block = (MemBlockHeader *)FMOD::mspace_malloc(mDLMallocSpace, reallen); + } + else +#endif + { + numblocks = FMOD_MEMORY_BYTESTOBLOCKS(pool, reallen); + + /* + Simple search for a free block in our memory pool. First fit. + */ + { + int offset; + + offset = findFreeBlocks(mFirstFreeBlock, mSizeBlocks, numblocks); + if (offset >= 0) + { + set(offset, 1, numblocks); + + if (mCustomPool) + { + block = (MemBlockHeader *)FMOD_Memory_Alloc(sizeof(MemBlockHeader)); + } + else + { + block = (MemBlockHeader *)(mData + (offset * mBlockSize)); + } + block->mBlockOffset = offset; + } + } + } + } + + if (!block) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_MEMORY | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "MemPool::alloc", "Failed allocating %-45s line %5d. Wanted %d bytes, current %d/%d (rounding wastage = %d bytes)\n", file, line, len, mCurrentAllocated[0], mSizeBytes, mWastage)); + +#ifdef FMOD_MEMORY_THREADSAFE + FMOD_OS_CriticalSection_Leave(mCrit); +#endif + + if (FMOD::gGlobal->gSystemCallback) + { + char fileLine[256]; + sprintf(fileLine, "%s (%d)", file, line); + + FMOD::gGlobal->gSystemCallback(NULL, FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED, (void*)fileLine, (void*)len); + } + + return 0; + } + + block->mSize = len; + block->mNumBlocks = numblocks; + block->mThreadID = getCurrentThreadID(); + + if (type & FMOD_MEMORY_SECONDARY) + { + mCurrentAllocatedSecondary += block->mSize; + + if (mCurrentAllocatedSecondary > mMaxAllocatedSecondary) + { + mMaxAllocatedSecondary = mCurrentAllocatedSecondary; + } + } + else + { + mCurrentAllocated[0] += block->mSize; /* All threads. */ + mCurrentAllocated[block->mThreadID] += block->mSize; + + if (mCurrentAllocated[0] > mMaxAllocated) + { + mMaxAllocated = mCurrentAllocated[0]; + } + } + + mNumBlocks += block->mNumBlocks; + if (mNumBlocks > mMaxBlocks) + { + mMaxBlocks = mNumBlocks; + mActualMaxBytes = mMaxBlocks * mBlockSize; + mWastage = mActualMaxBytes - mMaxAllocated; + } + + if (!mCustomPool) + { + block++; /* return pointer to actual data, not our memory block header */ + + if (clear && block) + { + FMOD_memset(block, 0, len); + } + } + +#ifdef FMOD_MEMORY_THREADSAFE + FMOD_OS_CriticalSection_Leave(mCrit); +#endif + + FLOG((FMOD_DEBUG_TYPE_MEMORY, file, line, "MemPool::alloc", "%6d bytes (%p) (alloc %d)\n", len, block, mAllocCount)); + + return block; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +void MemPool::free(void *ptr, const char *file, const int line, FMOD_MEMORY_TYPE type) +{ + MemBlockHeader *block = (MemBlockHeader *)ptr; + +#ifdef FMOD_MEMORY_THREADSAFE + if (!mCrit) + { + FMOD_RESULT result = FMOD_OS_CriticalSection_Create(&mCrit, true); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "MemPool::free", "Error creating critical section!\n")); + + return; + } + } + FMOD_OS_CriticalSection_Enter(mCrit); +#endif + + if (!mCustomPool) + { + block--; /* Find the actual block header */ + } + + FLOG((FMOD_DEBUG_TYPE_MEMORY, file, line, "MemPool::free", "%6d bytes (%p)\n", block->mSize, ptr)); + + #ifdef DEBUG + assert(block->mThreadID); /* If this is 0, it must have already been freed? */ + #endif + + if (type & FMOD_MEMORY_SECONDARY) + { + mCurrentAllocatedSecondary -= block->mSize; + } + else + { + mCurrentAllocated[0] -= block->mSize; + mCurrentAllocated[block->mThreadID] -= block->mSize; + } + + mNumBlocks -= block->mNumBlocks; + block->mThreadID = 0; + + if (mFree) + { + /* + Only want the flags that have been specified. + */ + type &= FMOD::gGlobal->gMemoryTypeFlags; + + mFree(block, type); + } + else + { +#ifdef FMOD_SUPPORT_DLMALLOC + if (!mCustomPool) + { + mspace_free(mDLMallocSpace, block); + } + else +#endif + { + set(block->mBlockOffset, 0, block->mNumBlocks); + } + } + +#ifdef FMOD_MEMORY_THREADSAFE + FMOD_OS_CriticalSection_Leave(mCrit); +#endif + + if (mCustomPool) + { + FMOD_Memory_Free(block); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +void *MemPool::calloc(int len, const char *file, const int line, FMOD_MEMORY_TYPE type) +{ + void *ptr; + + ptr = alloc(len, file, line, type, true); + + return ptr; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +void *MemPool::realloc(void *ptr, int len, const char *file, const int line, FMOD_MEMORY_TYPE type) +{ + MemBlockHeader *block = (MemBlockHeader *)ptr; + int reallen = len, numblocks = 0; + + if (!ptr) + { + return MemPool::alloc(len, file, line); + } + + { + #ifdef FMOD_MEMORY_ALLOC_FAIL_RANDOM + srand(clock()); + if ( + #ifdef FMOD_MEMORY_ALLOC_FAIL_AFTER + FMOD_Memory_FailCount >= FMOD_MEMORY_ALLOC_FAIL_AFTER && + #endif + !(FMOD_RAND() % FMOD_MEMORY_ALLOC_FAIL_RANDOM)) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_MEMORY | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "MemPool::realloc", "Failed allocating %-45s line %5d. Wanted %d bytes, current %d/%d (rounding wastage = %d bytes)\n", file, line, len, mCurrentAllocated[0], mSizeBytes, mWastage)); + + if (FMOD::gGlobal->gSystemCallback) + { + char fileLine[256]; + sprintf(fileLine, "%s (%d)", file, line); + + FMOD::gGlobal->gSystemCallback(NULL, FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED, (void*)fileLine, (void*)len); + } + + return 0; + } + #elif defined(FMOD_MEMORY_ALLOC_FAIL_AFTER) + if (FMOD_Memory_FailCount >= FMOD_MEMORY_ALLOC_FAIL_AFTER) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_MEMORY | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "MemPool::realloc", "Failed allocating %-45s line %5d. Wanted %d bytes, current %d/%d (rounding wastage = %d bytes)\n", file, line, len, mCurrentAllocated[0], mSizeBytes, mWastage)); + + if (FMOD::gGlobal->gSystemCallback) + { + char fileLine[256]; + sprintf(fileLine, "%s (%d)", file, line); + + FMOD::gGlobal->gSystemCallback(NULL, FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED, (void*)fileLine, (void*)len); + } + + FMOD_Memory_FailCount = 0; + return 0; + } + #endif + #ifdef FMOD_MEMORY_ALLOC_FAIL_AFTER + FMOD_Memory_FailCount++; + #endif + } + +#ifdef FMOD_MEMORY_THREADSAFE + if (!mCrit) + { + FMOD_RESULT result = FMOD_OS_CriticalSection_Create(&mCrit, true); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "MemPool::realloc", "Error creating critical section!\n")); + + return 0; + } + } + FMOD_OS_CriticalSection_Enter(mCrit); +#endif + + if (!mCustomPool) + { + reallen += sizeof(MemBlockHeader); + block--; + } + + FLOG((FMOD_DEBUG_TYPE_MEMORY, file, line, "MemPool::realloc", "from %6d to %6d bytes\n", block->mSize, len)); + + mCurrentAllocated[0] -= block->mSize; /* All threads. */ + mCurrentAllocated[block->mThreadID] -= block->mSize; + mNumBlocks -= block->mNumBlocks; + + if (mRealloc) + { + /* + Only want the flags that have been specified. + */ + type &= FMOD::gGlobal->gMemoryTypeFlags; + + block = (MemBlockHeader *)mRealloc(block, reallen, type); + } + else + { +#ifdef FMOD_SUPPORT_DLMALLOC + if (!mCustomPool) + { + block = (MemBlockHeader *)mspace_realloc(mDLMallocSpace, block, reallen); + } + else +#endif + { + int offset; + + numblocks = FMOD_MEMORY_BYTESTOBLOCKS(pool, reallen); + + set(block->mBlockOffset, 0, block->mNumBlocks); + + offset = findFreeBlocks(block->mBlockOffset, block->mBlockOffset + numblocks, numblocks); + + if (offset >= 0) + { + /* + We are able to simply extend the block + */ + set(offset, 1, numblocks); + block = (MemBlockHeader *)(mData + (offset * mBlockSize)); + block->mBlockOffset = offset; + } + else + { + /* + We have to find a new place and move the data + */ + offset = findFreeBlocks(mFirstFreeBlock, mSizeBlocks, numblocks); + if (offset >= 0) + { + MemBlockHeader *newblock; + + set(offset, 1, numblocks); + + if (mCustomPool) + { + newblock = block; + } + else + { + newblock = (MemBlockHeader *)(mData + (offset * mBlockSize)); + } + + newblock->mBlockOffset = offset; + + if (!mCustomPool) + { + FMOD_memmove(newblock + 1, block + 1, block->mSize); + } + + block = newblock; + + } + else + { + block = 0; + } + } + } + } + + if (!block) + { + FLOG(((FMOD_DEBUGLEVEL)(FMOD_DEBUG_TYPE_MEMORY | FMOD_DEBUG_LEVEL_ERROR), __FILE__, __LINE__, "MemPool::realloc", "Failed allocating %-45s line %5d. Wanted %d bytes, current %d/%d (rounding wastage = %d bytes)\n", file, line, len, mCurrentAllocated[0], mSizeBytes, mWastage)); + +#ifdef FMOD_MEMORY_THREADSAFE + FMOD_OS_CriticalSection_Leave(mCrit); +#endif + + if (FMOD::gGlobal->gSystemCallback) + { + char fileLine[256]; + sprintf(fileLine, "%s (%d)", file, line); + + FMOD::gGlobal->gSystemCallback(NULL, FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED, (void*)fileLine, (void*)len); + } + + return 0; + } + + block->mSize = len; + block->mNumBlocks = numblocks; + block->mThreadID = getCurrentThreadID(); + + mCurrentAllocated[0] += block->mSize; /* All threads. */ + mCurrentAllocated[block->mThreadID] += block->mSize; + + if (mCurrentAllocated[0] > mMaxAllocated) + { + mMaxAllocated = mCurrentAllocated[0]; + } + + mNumBlocks += block->mNumBlocks; + if (mNumBlocks > mMaxBlocks) + { + mMaxBlocks = mNumBlocks; + mActualMaxBytes = mMaxBlocks * mBlockSize; + mWastage = mActualMaxBytes - mMaxAllocated; + } + + if (!mCustomPool) + { + block++; /* return pointer to actual data, not our memory block header */ + } + +#ifdef FMOD_MEMORY_THREADSAFE + FMOD_OS_CriticalSection_Leave(mCrit); +#endif + + return block; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] +] +*/ +int MemPool::getSize(void *ptr, const char *file, const int line) +{ + MemBlockHeader *block = (MemBlockHeader *)ptr; + + if (!mCustomPool) + { + block--; /* Find the actual block header */ + } + + return block->mSize; +} + +} + +/* +[API] +[ + [DESCRIPTION] + Callback to allocate a block of memory. + + [PARAMETERS] + 'size' Size in bytes of the memory block to be allocated and returned. + 'type' Type of memory allocation. + + [RETURN_VALUE] + On success, a pointer to the newly allocated block of memory is returned. + On failure, NULL is returned. + + [REMARKS] + Returning an aligned pointer, of 16 byte alignment is recommended for speed purposes. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Memory_Initialize + Memory_GetStats + FMOD_MEMORY_REALLOCCALLBACK + FMOD_MEMORY_FREECALLBACK + FMOD_MEMORY_TYPE +] +*/ +/* +void * F_CALLBACK FMOD_MEMORY_ALLOCCALLBACK(unsigned int size, FMOD_MEMORY_TYPE type) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Callback to re-allocate a block of memory to a different size. + + [PARAMETERS] + 'ptr' Pointer to a block of memory to be resized. If this is NULL then a new block of memory is simply allocated. + 'size' Size of the memory to be reallocated. The original memory must be preserved. + 'type' Type of memory allocation. + + [RETURN_VALUE] + On success, a pointer to the newly re-allocated block of memory is returned. + On failure, NULL is returned. + + [REMARKS] + Returning an aligned pointer, of 16 byte alignment is recommended for speed purposes. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Memory_Initialize + Memory_GetStats + FMOD_MEMORY_ALLOCCALLBACK + FMOD_MEMORY_FREECALLBACK + FMOD_MEMORY_TYPE +] +*/ +/* +void * F_CALLBACK FMOD_MEMORY_REALLOCCALLBACK(void *ptr, unsigned int size, FMOD_MEMORY_TYPE type) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Callback to free a block of memory. + + [PARAMETERS] + 'ptr' Pointer to a pre-existing block of memory to be freed. + 'type' Type of memory to be freed. + + [RETURN_VALUE] + void + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Memory_Initialize + Memory_GetStats + FMOD_MEMORY_ALLOCCALLBACK + FMOD_MEMORY_REALLOCCALLBACK + FMOD_MEMORY_TYPE +] +*/ +/* +void F_CALLBACK FMOD_MEMORY_FREECALLBACK(void *ptr, FMOD_MEMORY_TYPE type) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + + diff --git a/src/fmod_memory.h b/src/fmod_memory.h new file mode 100755 index 0000000..84683e5 --- /dev/null +++ b/src/fmod_memory.h @@ -0,0 +1,258 @@ +#ifndef _FMOD_MEMORY_H +#define _FMOD_MEMORY_H + +#include "fmod_settings.h" + +#ifdef __cplusplus + +#ifndef PLATFORM_PS3 +extern "C++" /* in case it included inside an extern "C" */ +{ +#endif + +#include "fmod.h" + +#include <new> +#include "fmod_os_misc.h" +#include "fmod_debug.h" +#include "fmod_globals.h" + +#define FMOD_MEMORY_THREADSAFE +#define FMOD_MEMORY_USEBITS + +#ifdef FMOD_SUPPORT_DLMALLOC + #include "../lib/dlmalloc/dlmalloc.h" + #define FMOD_MEMORY_DEFAULTBLOCKSIZE 256 +#else + #if defined(FMOD_MEMORY_USEBITS) + #ifdef PLATFORM_PS3 + #define FMOD_MEMORY_DEFAULTBLOCKSIZE 32 /* 16 is incredibly slow on the PS3 for some reason */ + #else + #define FMOD_MEMORY_DEFAULTBLOCKSIZE 16 + #endif + #else + #define FMOD_MEMORY_DEFAULTBLOCKSIZE 256 + #endif +#endif + +#define FMOD_MEMORY_MAXTHREADS 32 + +namespace FMOD +{ + typedef struct + { + int mSize; + int mNumBlocks; + int mBlockOffset; + unsigned int mThreadID; + } MemBlockHeader; + + /* + MemoryPool class. Does all the memory management work. + */ + class MemPool + { + #if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY) + friend class ProfileMemory; + #endif + + private: + unsigned char *mBitmap; + unsigned char *mData; + + bool mCustomPool; + int mSizeBytes; + int mSizeBlocks; + int mNumBlocks; + int mMaxBlocks; + unsigned int mCurrentAllocated[FMOD_MEMORY_MAXTHREADS + 1]; + unsigned int mCurrentAllocatedThreadID[FMOD_MEMORY_MAXTHREADS]; /* id 0 means all threads. */ + unsigned int mMaxAllocated; + unsigned int mMaxAllocatedSecondary; + unsigned int mCurrentAllocatedSecondary; + int mActualMaxBytes; + int mWastage; + int mFirstFreeBlock; + FMOD_MEMORY_ALLOCCALLBACK mAlloc; + FMOD_MEMORY_REALLOCCALLBACK mRealloc; + FMOD_MEMORY_FREECALLBACK mFree; + +#ifdef FMOD_SUPPORT_DLMALLOC + mspace mDLMallocSpace; +#endif + +#ifdef FMOD_MEMORY_THREADSAFE + FMOD_OS_CRITICALSECTION *mCrit; +#endif +#ifdef FMOD_DEBUG + int mAllocCount; +#endif + inline int findFreeBlocks(int offset, int to, int numblocks); + + public: + + void set(int blockoffset, int value, int numblocks); + int mBlockSize; + + MemPool(); + ~MemPool(); + + FMOD_RESULT init (void *poolmem, int poolsize, int blocksize); + FMOD_RESULT initCustom(void *poolmem, int poolsize, int blocksize); + void close(); + + void *alloc (int len, const char *file = "", const int line = 0, FMOD_MEMORY_TYPE type = FMOD_MEMORY_NORMAL, bool clear = false); + void *calloc (int len, const char *file = "", const int line = 0, FMOD_MEMORY_TYPE type = FMOD_MEMORY_NORMAL); + void *realloc(void *ptr, int len, const char *file = "", const int line = 0, FMOD_MEMORY_TYPE type = FMOD_MEMORY_NORMAL); + void free (void *ptr, const char *file = "", const int line = 0, FMOD_MEMORY_TYPE type = FMOD_MEMORY_NORMAL); + int getSize(void *ptr, const char *file = "", const int line = 0); + + inline FMOD_RESULT setCallbacks(FMOD_MEMORY_ALLOCCALLBACK useralloc, FMOD_MEMORY_REALLOCCALLBACK userrealloc, FMOD_MEMORY_FREECALLBACK userfree) + { + mAlloc = useralloc; + mRealloc = userrealloc; + mFree = userfree; + + return FMOD_OK; + } + + inline unsigned int getCurrentAllocated(unsigned int threadid = 0) + { + return mCurrentAllocated[threadid]; + } + inline unsigned int getCurrentAllocatedSecondary() + { + return mCurrentAllocatedSecondary; + } + inline unsigned int getCurrentThreadID(FMOD_UINT_NATIVE threadid = 0) + { + unsigned int count; + FMOD_UINT_NATIVE id = threadid; + + if (!id) + { + FMOD_OS_Thread_GetCurrentID(&id); + } + + for (count = 1; count < FMOD_MEMORY_MAXTHREADS; count++) + { + if (id == mCurrentAllocatedThreadID[count]) + { + break; + } + else if (!mCurrentAllocatedThreadID[count]) + { + mCurrentAllocatedThreadID[count] = id; + mCurrentAllocated[count] = 0; + break; + } + } + + return count; + } + inline void clearThreadID(FMOD_UINT_NATIVE id) + { + int count; + + for (count = 1; count < FMOD_MEMORY_MAXTHREADS; count++) + { + if (id == mCurrentAllocatedThreadID[count]) + { + mCurrentAllocated[count] = 0; + mCurrentAllocatedThreadID[count] = 0; + break; + } + } + } + + inline unsigned int getMaxAllocated() { return mMaxAllocated; } + inline unsigned int getMaxAllocatedSecondary() { return mMaxAllocatedSecondary; } + inline unsigned char *getData() { return mData; } + inline int getSizeBytes() { return mSizeBytes; } + }; + + /* + Base class for Memory overriding to access the global memory pool. + */ + class MemSingleton + { + void *mBuffer; + int mRefCount; + + public: + + MemSingleton() : mBuffer(0), mRefCount(0) {} + ~MemSingleton() { free(); } + + void *alloc(int len, const char *file = "", const int line = 0); + void free(const char *file = "", const int line = 0); + void *getData() { return mBuffer; } + }; + + void * F_CALLBACK Memory_DefaultMalloc(unsigned int size, FMOD_MEMORY_TYPE type); + void * F_CALLBACK Memory_DefaultRealloc(void *data, unsigned int size, FMOD_MEMORY_TYPE type); + void F_CALLBACK Memory_DefaultFree(void *ptr, FMOD_MEMORY_TYPE type); + + /* + Standard alloc functions. + */ +#ifndef PLATFORM_PS3_SPU + + #define FMOD_Memory_Alloc(_len) FMOD::gGlobal->gSystemPool->alloc((_len), __FILE__, __LINE__, false) + #define FMOD_Memory_Calloc(_len) FMOD::gGlobal->gSystemPool->calloc((_len), __FILE__, __LINE__) + #define FMOD_Memory_ReAlloc(_ptr, _len) FMOD::gGlobal->gSystemPool->realloc((_ptr), _len, __FILE__, __LINE__) + #define FMOD_Memory_Free(_ptr) FMOD::gGlobal->gSystemPool->free((_ptr), __FILE__, __LINE__) + + #define FMOD_Memory_AllocType(_len, _type) FMOD::gGlobal->gSystemPool->alloc((_len), __FILE__, __LINE__, _type) + #define FMOD_Memory_CallocType(_len, _type) FMOD::gGlobal->gSystemPool->calloc((_len), __FILE__, __LINE__, _type) + #define FMOD_Memory_ReAllocType(_ptr, _len, _type) FMOD::gGlobal->gSystemPool->realloc((_ptr), _len, __FILE__, __LINE__, _type) + #define FMOD_Memory_FreeType(_ptr, _type) FMOD::gGlobal->gSystemPool->free((_ptr), __FILE__, __LINE__, _type) + + #define FMOD_Object_Alloc(_type) new (FMOD::gGlobal->gSystemPool->alloc(sizeof(_type), __FILE__, __LINE__)) (_type) + #define FMOD_Object_Calloc(_type) new (FMOD::gGlobal->gSystemPool->calloc(sizeof(_type), __FILE__, __LINE__)) (_type) + #define FMOD_Object_AllocSize(_type, _len) new (FMOD::gGlobal->gSystemPool->alloc((_len) < sizeof(_type) ? sizeof(_type) : (_len), __FILE__, __LINE__)) (_type) + #define FMOD_Object_CallocSize(_type, _len) new (FMOD::gGlobal->gSystemPool->calloc((_len) < sizeof(_type) ? sizeof(_type) : (_len), __FILE__, __LINE__)) (_type) +// #define FMOD_Object_AllocArray(_type, _num) new (FMOD::gGlobal->gSystemPool->calloc(sizeof(_type) * (_num), __FILE__, __LINE__)) (_type[_num]); + +#ifndef PLATFORM_PS3 +} /* extern "C++" */ +#endif + +#else + + #define FMOD_Memory_Alloc(_len) (0) + #define FMOD_Memory_Calloc(_len) (0) + #define FMOD_Memory_ReAlloc(_ptr, _len) (0) + #define FMOD_Memory_Free(_ptr) (0) + #define FMOD_Memory_GetSize(_ptr) (0) + #define FMOD_Memory_AddCounter(_ptr) (0) + #define FMOD_Memory_RemoveCounter(_ptr) (0) + #define FMOD_Memory_PauseCounter(_ptr, _p) (0) + + #define FMOD_Object_Alloc(_type) (0) + #define FMOD_Object_Calloc(_type) (0) + #define FMOD_Object_AllocSize(_type, _len) (0) + #define FMOD_Object_CallocSize(_type, _len) (0) +// #define FMOD_Object_AllocArray(_type, _num) + +#endif + + +} + +#else /* __cplusplus */ + + void *FMOD_Memory_allocC (int len, const char *file, const int line); + void *FMOD_Memory_callocC (int len, const char *file, const int line); + void *FMOD_Memory_reallocC(void *ptr, int len, const char *file, const int line); + void FMOD_Memory_freeC (void *ptr, const char *file, const int line); + + #define FMOD_Memory_Alloc(_len) FMOD_Memory_allocC((_len), __FILE__, __LINE__) + #define FMOD_Memory_Calloc(_len) FMOD_Memory_callocC((_len), __FILE__, __LINE__) + #define FMOD_Memory_ReAlloc(_ptr, _len) FMOD_Memory_reallocC((_ptr), _len, __FILE__, __LINE__) + #define FMOD_Memory_Free(_ptr) FMOD_Memory_freeC((_ptr), __FILE__, __LINE__) + +#endif /* __cplusplus */ + + +#endif diff --git a/src/fmod_memorytracker.cpp b/src/fmod_memorytracker.cpp new file mode 100755 index 0000000..600ad0e --- /dev/null +++ b/src/fmod_memorytracker.cpp @@ -0,0 +1,616 @@ +#include "fmod_settings.h" +#include "fmod_types.h" +#include <string.h> + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +//#define FMOD_MEMORYTRACKER_TEST + +#ifdef FMOD_MEMORYTRACKER_TEST +#include "crtdbg.h" +#endif + +namespace FMOD +{ + +MemoryTracker::MemoryTracker() +{ +#ifdef FMOD_MEMORYTRACKER_TEST + test(); +#endif + + clear(); +} + +void MemoryTracker::clear() +{ + FMOD_memset(&mMemUsed, 0, sizeof(FMOD_MEMORY_USAGE_DETAILS)); + mTotalMemUsed = 0; +} + +void MemoryTracker::add(bool eventobject, int bits, unsigned int numbytes) +{ + if (this) + { + if (eventobject) + { + switch(bits) + { + case FMOD_EVENT_MEMBITS_EVENTSYSTEM: + mMemUsed.eventsystem += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_MUSICSYSTEM: + mMemUsed.musicsystem += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_FEV: + mMemUsed.fev += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_MEMORYFSB: + mMemUsed.memoryfsb += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTPROJECT: + mMemUsed.eventproject += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTGROUPI: + mMemUsed.eventgroupi += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_SOUNDBANKCLASS: + mMemUsed.soundbankclass += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_SOUNDBANKLIST: + mMemUsed.soundbanklist += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_STREAMINSTANCE: + mMemUsed.streaminstance += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_SOUNDDEFCLASS: + mMemUsed.sounddefclass += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_SOUNDDEFDEFCLASS: + mMemUsed.sounddefdefclass += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_SOUNDDEFPOOL: + mMemUsed.sounddefpool += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_REVERBDEF: + mMemUsed.reverbdef += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTREVERB: + mMemUsed.eventreverb += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_USERPROPERTY: + mMemUsed.userproperty += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTINSTANCE: + mMemUsed.eventinstance += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTINSTANCE_COMPLEX: + mMemUsed.eventinstance_complex += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTINSTANCE_SIMPLE: + mMemUsed.eventinstance_simple += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTINSTANCE_LAYER: + mMemUsed.eventinstance_layer += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTINSTANCE_SOUND: + mMemUsed.eventinstance_sound += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTENVELOPE: + mMemUsed.eventenvelope += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTENVELOPEDEF: + mMemUsed.eventenvelopedef += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTPARAMETER: + mMemUsed.eventparameter += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTCATEGORY: + mMemUsed.eventcategory += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTENVELOPEPOINT: + mMemUsed.eventenvelopepoint += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_EVENT_MEMBITS_EVENTINSTANCEPOOL: + mMemUsed.eventinstancepool += numbytes; + mTotalMemUsed += numbytes; + break; + } + } + else + { + switch(bits) + { + case FMOD_MEMBITS_OTHER: + mMemUsed.other += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_STRING: + mMemUsed.string += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_SYSTEM: + mMemUsed.system += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_PLUGINS: + mMemUsed.plugins += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_OUTPUT: + mMemUsed.output += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_CHANNEL: + mMemUsed.channel += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_CHANNELGROUP: + mMemUsed.channelgroup += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_CODEC: + mMemUsed.codec += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_FILE: + mMemUsed.file += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_SOUND: + mMemUsed.sound += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_SOUND_SECONDARYRAM: + mMemUsed.secondaryram += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_SOUNDGROUP: + mMemUsed.soundgroup += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_STREAMBUFFER: + mMemUsed.streambuffer += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_DSPCONNECTION: + mMemUsed.dspconnection += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_DSP: + mMemUsed.dsp += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_DSPCODEC: + mMemUsed.dspcodec += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_PROFILE: + mMemUsed.profile += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_RECORDBUFFER: + mMemUsed.recordbuffer += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_REVERB: + mMemUsed.reverb += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_REVERBCHANNELPROPS: + mMemUsed.reverbchannelprops += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_GEOMETRY: + mMemUsed.geometry += numbytes; + mTotalMemUsed += numbytes; + break; + case FMOD_MEMBITS_SYNCPOINT: + mMemUsed.syncpoint += numbytes; + mTotalMemUsed += numbytes; + break; + } + } + } +} + +unsigned int MemoryTracker::getTotal() +{ + return mTotalMemUsed; +} + +unsigned int MemoryTracker::getMemUsedFromBits(unsigned int memorybits, unsigned int event_memorybits) +{ + unsigned int used = 0; + + if (memorybits & FMOD_MEMBITS_OTHER) + { + used += mMemUsed.other; + } + if (memorybits & FMOD_MEMBITS_STRING) + { + used += mMemUsed.string; + } + if (memorybits & FMOD_MEMBITS_SYSTEM) + { + used += mMemUsed.system; + } + if (memorybits & FMOD_MEMBITS_PLUGINS) + { + used += mMemUsed.plugins; + } + if (memorybits & FMOD_MEMBITS_OUTPUT) + { + used += mMemUsed.output; + } + if (memorybits & FMOD_MEMBITS_CHANNEL) + { + used += mMemUsed.channel; + } + if (memorybits & FMOD_MEMBITS_CHANNELGROUP) + { + used += mMemUsed.channelgroup; + } + if (memorybits & FMOD_MEMBITS_CODEC) + { + used += mMemUsed.codec; + } + if (memorybits & FMOD_MEMBITS_FILE) + { + used += mMemUsed.file; + } + if (memorybits & FMOD_MEMBITS_SOUND) + { + used += mMemUsed.sound; + } + if (memorybits & FMOD_MEMBITS_SOUND_SECONDARYRAM) + { + used += mMemUsed.secondaryram; + } + if (memorybits & FMOD_MEMBITS_SOUNDGROUP) + { + used += mMemUsed.soundgroup; + } + if (memorybits & FMOD_MEMBITS_STREAMBUFFER) + { + used += mMemUsed.streambuffer; + } + if (memorybits & FMOD_MEMBITS_DSPCONNECTION) + { + used += mMemUsed.dspconnection; + } + if (memorybits & FMOD_MEMBITS_DSP) + { + used += mMemUsed.dsp; + } + if (memorybits & FMOD_MEMBITS_DSPCODEC) + { + used += mMemUsed.dspcodec; + } + if (memorybits & FMOD_MEMBITS_PROFILE) + { + used += mMemUsed.profile; + } + if (memorybits & FMOD_MEMBITS_RECORDBUFFER) + { + used += mMemUsed.recordbuffer; + } + if (memorybits & FMOD_MEMBITS_REVERB) + { + used += mMemUsed.reverb; + } + if (memorybits & FMOD_MEMBITS_REVERBCHANNELPROPS) + { + used += mMemUsed.reverbchannelprops; + } + if (memorybits & FMOD_MEMBITS_GEOMETRY) + { + used += mMemUsed.geometry; + } + if (memorybits & FMOD_MEMBITS_SYNCPOINT) + { + used += mMemUsed.syncpoint; + } + + // event usage + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTSYSTEM) + { + used += mMemUsed.eventsystem; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_MUSICSYSTEM) + { + used += mMemUsed.musicsystem; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_FEV) + { + used += mMemUsed.fev; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_MEMORYFSB) + { + used += mMemUsed.memoryfsb; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTPROJECT) + { + used += mMemUsed.eventproject; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTGROUPI) + { + used += mMemUsed.eventgroupi; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_SOUNDBANKCLASS) + { + used += mMemUsed.soundbankclass; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_SOUNDBANKLIST) + { + used += mMemUsed.soundbanklist; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_STREAMINSTANCE) + { + used += mMemUsed.streaminstance; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_SOUNDDEFCLASS) + { + used += mMemUsed.sounddefclass; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_SOUNDDEFDEFCLASS) + { + used += mMemUsed.sounddefdefclass; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_SOUNDDEFPOOL) + { + used += mMemUsed.sounddefpool; + } + if (event_memorybits &FMOD_EVENT_MEMBITS_REVERBDEF ) + { + used += mMemUsed.reverbdef; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTREVERB) + { + used += mMemUsed.eventreverb; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_USERPROPERTY) + { + used += mMemUsed.userproperty; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTINSTANCE) + { + used += mMemUsed.eventinstance; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTINSTANCE_COMPLEX) + { + used += mMemUsed.eventinstance_complex; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTINSTANCE_SIMPLE) + { + used += mMemUsed.eventinstance_simple; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTINSTANCE_LAYER) + { + used += mMemUsed.eventinstance_layer; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTINSTANCE_SOUND) + { + used += mMemUsed.eventinstance_sound; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTENVELOPE) + { + used += mMemUsed.eventenvelope; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTENVELOPEDEF) + { + used += mMemUsed.eventenvelopedef; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTPARAMETER) + { + used += mMemUsed.eventparameter; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTCATEGORY) + { + used += mMemUsed.eventcategory; + } + if (event_memorybits & FMOD_EVENT_MEMBITS_EVENTENVELOPEPOINT) + { + used += mMemUsed.eventenvelopepoint; + } + + return used; +} + +void MemoryTracker::test() +{ +#ifdef FMOD_MEMORYTRACKER_TEST + + clear(); + + unsigned int i; + + for (i=1; i <= FMOD_MEMBITS_SYNCPOINT; i <<= 1) + { + add(false, i, i); + } + + for (i=1; i <= FMOD_EVENT_MEMBITS_EVENTINSTANCEPOOL; i <<= 1) + { + add(true, i, i); + } + + _ASSERTE(mMemUsed.other == 0x00000001); + _ASSERTE(getMemUsedFromBits(0x00000001, 0) == 0x00000001); + + _ASSERTE(mMemUsed.string == 0x00000002); + _ASSERTE(getMemUsedFromBits(0x00000002, 0) == 0x00000002); + + _ASSERTE(mMemUsed.system == 0x00000004); + _ASSERTE(getMemUsedFromBits(0x00000004, 0) == 0x00000004); + + _ASSERTE(mMemUsed.plugins == 0x00000008); + _ASSERTE(getMemUsedFromBits(0x00000008, 0) == 0x00000008); + + _ASSERTE(mMemUsed.output == 0x00000010); + _ASSERTE(getMemUsedFromBits(0x00000010, 0) == 0x00000010); + + _ASSERTE(mMemUsed.channel == 0x00000020); + _ASSERTE(getMemUsedFromBits(0x00000020, 0) == 0x00000020); + + _ASSERTE(mMemUsed.channelgroup == 0x00000040); + _ASSERTE(getMemUsedFromBits(0x00000040, 0) == 0x00000040); + + _ASSERTE(mMemUsed.codec == 0x00000080); + _ASSERTE(getMemUsedFromBits(0x00000080, 0) == 0x00000080); + + _ASSERTE(mMemUsed.file == 0x00000100); + _ASSERTE(getMemUsedFromBits(0x00000100, 0) == 0x00000100); + + _ASSERTE(mMemUsed.sound == 0x00000200); + _ASSERTE(getMemUsedFromBits(0x00000200, 0) == 0x00000200); + + _ASSERTE(mMemUsed.secondaryram == 0x00000400); + _ASSERTE(getMemUsedFromBits(0x00000400, 0) == 0x00000400); + + _ASSERTE(mMemUsed.soundgroup == 0x00000800); + _ASSERTE(getMemUsedFromBits(0x00000800, 0) == 0x00000800); + + _ASSERTE(mMemUsed.streambuffer == 0x00001000); + _ASSERTE(getMemUsedFromBits(0x00001000, 0) == 0x00001000); + + _ASSERTE(mMemUsed.dspconnection == 0x00002000); + _ASSERTE(getMemUsedFromBits(0x00002000, 0) == 0x00002000); + + _ASSERTE(mMemUsed.dsp == 0x00004000); + _ASSERTE(getMemUsedFromBits(0x00004000, 0) == 0x00004000); + + _ASSERTE(mMemUsed.dspcodec == 0x00008000); + _ASSERTE(getMemUsedFromBits(0x00008000, 0) == 0x00008000); + + _ASSERTE(mMemUsed.profile == 0x00010000); + _ASSERTE(getMemUsedFromBits(0x00010000, 0) == 0x00010000); + + _ASSERTE(mMemUsed.recordbuffer == 0x00020000); + _ASSERTE(getMemUsedFromBits(0x00020000, 0) == 0x00020000); + + _ASSERTE(mMemUsed.reverb == 0x00040000); + _ASSERTE(getMemUsedFromBits(0x00040000, 0) == 0x00040000); + + _ASSERTE(mMemUsed.reverbchannelprops == 0x00080000); + _ASSERTE(getMemUsedFromBits(0x00080000, 0) == 0x00080000); + + _ASSERTE(mMemUsed.geometry == 0x00100000); + _ASSERTE(getMemUsedFromBits(0x00100000, 0) == 0x00100000); + + _ASSERTE(mMemUsed.syncpoint == 0x00200000); + _ASSERTE(getMemUsedFromBits(0x00200000, 0) == 0x00200000); + + _ASSERTE(mMemUsed.eventsystem == 0x00000001); + _ASSERTE(getMemUsedFromBits(0, 0x00000001) == 0x00000001); + + _ASSERTE(mMemUsed.musicsystem == 0x00000002); + _ASSERTE(getMemUsedFromBits(0, 0x00000002) == 0x00000002); + + _ASSERTE(mMemUsed.fev == 0x00000004); + _ASSERTE(getMemUsedFromBits(0, 0x00000004) == 0x00000004); + + _ASSERTE(mMemUsed.memoryfsb == 0x00000008); + _ASSERTE(getMemUsedFromBits(0, 0x00000008) == 0x00000008); + + _ASSERTE(mMemUsed.eventproject == 0x00000010); + _ASSERTE(getMemUsedFromBits(0, 0x00000010) == 0x00000010); + + _ASSERTE(mMemUsed.eventgroupi == 0x00000020); + _ASSERTE(getMemUsedFromBits(0, 0x00000020) == 0x00000020); + + _ASSERTE(mMemUsed.soundbankclass == 0x00000040); + _ASSERTE(getMemUsedFromBits(0, 0x00000040) == 0x00000040); + + _ASSERTE(mMemUsed.soundbanklist == 0x00000080); + _ASSERTE(getMemUsedFromBits(0, 0x00000080) == 0x00000080); + + _ASSERTE(mMemUsed.streaminstance == 0x00000100); + _ASSERTE(getMemUsedFromBits(0, 0x00000100) == 0x00000100); + + _ASSERTE(mMemUsed.sounddefclass == 0x00000200); + _ASSERTE(getMemUsedFromBits(0, 0x00000200) == 0x00000200); + + _ASSERTE(mMemUsed.sounddefdefclass == 0x00000400); + _ASSERTE(getMemUsedFromBits(0, 0x00000400) == 0x00000400); + + _ASSERTE(mMemUsed.sounddefpool == 0x00000800); + _ASSERTE(getMemUsedFromBits(0, 0x00000800) == 0x00000800); + + _ASSERTE(mMemUsed.reverbdef == 0x00001000); + _ASSERTE(getMemUsedFromBits(0, 0x00001000) == 0x00001000); + + _ASSERTE(mMemUsed.eventreverb == 0x00002000); + _ASSERTE(getMemUsedFromBits(0, 0x00002000) == 0x00002000); + + _ASSERTE(mMemUsed.userproperty == 0x00004000); + _ASSERTE(getMemUsedFromBits(0, 0x00004000) == 0x00004000); + + _ASSERTE(mMemUsed.eventinstance == 0x00008000); + _ASSERTE(getMemUsedFromBits(0, 0x00008000) == 0x00008000); + + _ASSERTE(mMemUsed.eventinstance_complex == 0x00010000); + _ASSERTE(getMemUsedFromBits(0, 0x00010000) == 0x00010000); + + _ASSERTE(mMemUsed.eventinstance_simple == 0x00020000); + _ASSERTE(getMemUsedFromBits(0, 0x00020000) == 0x00020000); + + _ASSERTE(mMemUsed.eventinstance_layer == 0x00040000); + _ASSERTE(getMemUsedFromBits(0, 0x00040000) == 0x00040000); + + _ASSERTE(mMemUsed.eventinstance_sound == 0x00080000); + _ASSERTE(getMemUsedFromBits(0, 0x00080000) == 0x00080000); + + _ASSERTE(mMemUsed.eventenvelope == 0x00100000); + _ASSERTE(getMemUsedFromBits(0, 0x00100000) == 0x00100000); + + _ASSERTE(mMemUsed.eventenvelopedef == 0x00200000); + _ASSERTE(getMemUsedFromBits(0, 0x00200000) == 0x00200000); + + _ASSERTE(mMemUsed.eventparameter == 0x00400000); + _ASSERTE(getMemUsedFromBits(0, 0x00400000) == 0x00400000); + + _ASSERTE(mMemUsed.eventcategory == 0x00800000); + _ASSERTE(getMemUsedFromBits(0, 0x00800000) == 0x00800000); + + _ASSERTE(mMemUsed.eventenvelopepoint == 0x01000000); + _ASSERTE(getMemUsedFromBits(0, 0x01000000) == 0x01000000); + + //_ASSERTE(mMemUsed.eventinstancepool == 0x02000000); + //_ASSERTE(getMemUsedFromBits(0, 0x02000000) == 0x02000000); + +#endif +} + +} + +#endif diff --git a/src/fmod_memorytracker.h b/src/fmod_memorytracker.h new file mode 100755 index 0000000..559deae --- /dev/null +++ b/src/fmod_memorytracker.h @@ -0,0 +1,192 @@ +#ifndef _FMOD_MEMORYTRACKER_H +#define _FMOD_MEMORYTRACKER_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +#ifndef _FMOD_MEMORYINFO_H +#include "fmod_memoryinfo.h" +#endif + +#define DECLARE_MEMORYTRACKER_DEFAULT(i, x) \ + public : \ + bool __mMemoryTrackerVisited; \ + virtual FMOD_RESULT getMemoryUsed(MemoryTracker *tracker) \ + { \ + if (tracker) \ + { \ + if (!__mMemoryTrackerVisited) \ + { \ + tracker->add((i), (x), sizeof(*this)); \ + __mMemoryTrackerVisited = true; \ + } \ + } \ + else \ + { \ + __mMemoryTrackerVisited = false; \ + } \ + return FMOD_OK; \ + } \ + private : + +#define DECLARE_MEMORYTRACKER \ + public : \ + bool __mMemoryTrackerVisited; \ + virtual FMOD_RESULT getMemoryUsedImpl(MemoryTracker *tracker); \ + virtual FMOD_RESULT getMemoryUsed(MemoryTracker *tracker) \ + { \ + if (tracker) \ + { \ + if (!__mMemoryTrackerVisited) \ + { \ + FMOD_RESULT result = getMemoryUsedImpl(tracker); \ + if (result != FMOD_OK) \ + { \ + return result; \ + } \ + __mMemoryTrackerVisited = true; \ + } \ + } \ + else \ + { \ + FMOD_RESULT result = getMemoryUsedImpl(tracker); \ + if (result != FMOD_OK) \ + { \ + return result; \ + } \ + __mMemoryTrackerVisited = false; \ + } \ + return FMOD_OK; \ + } \ + private : + +#define DECLARE_MEMORYTRACKER_NONVIRTUAL \ + public : \ + bool __mMemoryTrackerVisited; \ + FMOD_RESULT getMemoryUsedImpl(MemoryTracker *tracker); \ + FMOD_RESULT getMemoryUsed(MemoryTracker *tracker) \ + { \ + if (tracker) \ + { \ + if (!__mMemoryTrackerVisited) \ + { \ + FMOD_RESULT result = getMemoryUsedImpl(tracker); \ + if (result != FMOD_OK) \ + { \ + return result; \ + } \ + __mMemoryTrackerVisited = true; \ + } \ + } \ + else \ + { \ + FMOD_RESULT result = getMemoryUsedImpl(tracker); \ + if (result != FMOD_OK) \ + { \ + return result; \ + } \ + __mMemoryTrackerVisited = false; \ + } \ + return FMOD_OK; \ + } \ + private : + +#define DECLARE_MEMORYTRACKER_NONVIRTUAL_EXPORT \ + public : \ + bool __mMemoryTrackerVisited; \ + FMOD_RESULT F_API getMemoryUsedImpl(MemoryTracker *tracker); \ + FMOD_RESULT getMemoryUsed(MemoryTracker *tracker) \ + { \ + if (tracker) \ + { \ + if (!__mMemoryTrackerVisited) \ + { \ + FMOD_RESULT result = getMemoryUsedImpl(tracker); \ + if (result != FMOD_OK) \ + { \ + return result; \ + } \ + __mMemoryTrackerVisited = true; \ + } \ + } \ + else \ + { \ + FMOD_RESULT result = getMemoryUsedImpl(tracker); \ + if (result != FMOD_OK) \ + { \ + return result; \ + } \ + __mMemoryTrackerVisited = false; \ + } \ + return FMOD_OK; \ + } \ + private : + +#define DECLARE_MEMORYTRACKER_ONLY \ + public : \ + virtual FMOD_RESULT getMemoryUsed(MemoryTracker *tracker) { return FMOD_ERR_INTERNAL; } \ + private : + +#define GETMEMORYINFO_IMPL \ + \ + if (memoryused) \ + { \ + *memoryused = 0; \ + } \ + \ + MemoryTracker tracker; \ + CHECK_RESULT(getMemoryUsed(0)); \ + CHECK_RESULT(getMemoryUsed(&tracker)); \ + \ + if (memoryused_details) \ + { \ + *memoryused_details = tracker.getMemUsedDetails(); \ + } \ + \ + if (memoryused) \ + { \ + *memoryused = tracker.getMemUsedFromBits(memorybits, event_memorybits); \ + } \ + \ + return FMOD_OK; + + +namespace FMOD +{ + class EventSystemI; + + class MemoryTracker + { + private : + + FMOD_MEMORY_USAGE_DETAILS mMemUsed; + unsigned int mTotalMemUsed; + + + public : + + MemoryTracker(); + + void clear(); + void add(bool eventobject, int bits, unsigned int numbytes); + unsigned int getTotal(); + FMOD_MEMORY_USAGE_DETAILS getMemUsedDetails() { return mMemUsed; } + unsigned int getMemUsedFromBits(unsigned int memorybits, unsigned int event_memorybits); + void test(); + }; +} + +#else + +#define DECLARE_MEMORYTRACKER_DEFAULT(x) +#define DECLARE_MEMORYTRACKER +#define DECLARE_MEMORYTRACKER_NONVIRTUAL +#define DECLARE_MEMORYTRACKER_NONVIRTUAL_EXPORT +#define DECLARE_MEMORYTRACKER_ONLY + +#endif + +#endif diff --git a/src/fmod_metadata.cpp b/src/fmod_metadata.cpp new file mode 100755 index 0000000..f7629c9 --- /dev/null +++ b/src/fmod_metadata.cpp @@ -0,0 +1,516 @@ +#include "fmod_settings.h" + +#include "fmod_memory.h" +#include "fmod_metadata.h" +#include "fmod_string.h" + +#include <string.h> + + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT TagNode::init(FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype) +{ + int memsize; + + mName = FMOD_strdup(name); + if (!mName) + { + return FMOD_ERR_MEMORY; + } + + memsize = datalen; + if (datatype == FMOD_TAGDATATYPE_STRING) + { + memsize += 1; + } + else if (datatype == FMOD_TAGDATATYPE_STRING_UTF16 || datatype == FMOD_TAGDATATYPE_STRING_UTF16BE) + { + memsize += 2; + } + + //AJS double-buffered stuff!!! + + mData[0] = FMOD_Memory_Calloc(memsize); + if (!mData[0]) + { + return FMOD_ERR_MEMORY; + } + FMOD_memcpy(mData[0], data, datalen); + + mDataLen = memsize; + mType = type; + mDataType = datatype; + mUpdated = true; + mUnique = false; + mCurrentBuffer = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT TagNode::release() +{ + if (mName) + { + FMOD_Memory_Free(mName); + mName = 0; + } + + //AJS double-buffered!!! + + if (mData[0]) + { + FMOD_Memory_Free(mData[0]); + mData[0] = 0; + } + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT TagNode::update(void *data, unsigned int datalen) +{ + //AJS double-buffered stuff!!!!!!!!!!1 + + if (mDataLen == datalen) + { + if (!memcmp(mData[0], data, datalen)) + { + mUpdated = true; + + return FMOD_OK; + } + } + + if (mData[0]) + { + FMOD_Memory_Free(mData[0]); + mData[0] = 0; + } + + mData[0] = FMOD_Memory_Alloc(datalen); + if (!mData[0]) + { + return FMOD_ERR_MEMORY; + } + FMOD_memcpy(mData[0], data, datalen); + mDataLen = datalen; + + mUpdated = true; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT Metadata::release() +{ + TagNode *node = (TagNode *)mList.getNext(); + + while (node != &mList) + { + TagNode *next = (TagNode *)node->getNext(); + node->removeNode(); + node->release(); + node = next; + } + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT Metadata::getNumTags(int *numtags, int *numtagsupdated) +{ + TagNode *node; + LinkedListNode *current; + int num = 0, numupdated = 0; + + current = mList.getNext(); + while (current != &mList) + { + num++; + node = (TagNode *)current; + if (node->mUpdated) + { + numupdated++; + } + current = current->getNext(); + } + + if (numtags) + { + *numtags = num; + } + if (numtagsupdated) + { + *numtagsupdated = numupdated; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT Metadata::getTag(const char *name, int index, FMOD_TAG *tag) +{ + int i; + TagNode *node; + LinkedListNode *current; + + /* + index = 0-n, name = 0 Get the 0-n tag of the whole tag list + index = 0-n, name = "" Get the 0-n instance of the named tag + index = -1, name = 0 Get the first updated tag + index = -1, name = "" Get the first updated instance of the named tag + */ + + if (index >= 0) + { + if (!name) + { + node = (TagNode *)mList.getNodeByIndex(index); + if (!node) + { + return FMOD_ERR_TAGNOTFOUND; + } + } + else + { + i = 0; + current = mList.getNext(); + for (;;) + { + if (current == &mList) + { + return FMOD_ERR_TAGNOTFOUND; + } + node = (TagNode *)current; + if (!FMOD_strcmp(node->mName, name)) + { + if (i == index) + { + break; + } + i++; + } + current = current->getNext(); + } + } + } + else + { + if (!name) + { + current = mList.getNext(); + for (;;) + { + if (current == &mList) + { + return FMOD_ERR_TAGNOTFOUND; + } + node = (TagNode *)current; + if (node->mUpdated) + { + break; + } + current = current->getNext(); + } + } + else + { + current = mList.getNext(); + for (;;) + { + if (current == &mList) + { + return FMOD_ERR_TAGNOTFOUND; + } + node = (TagNode *)current; + if (node->mUpdated && !FMOD_strcmp(node->mName, name)) + { + break; + } + current = current->getNext(); + } + } + } + + tag->type = node->mType; + tag->datatype = node->mDataType; + tag->name = node->mName; + tag->data = node->mData[0]; + tag->datalen = node->mDataLen; + tag->updated = node->mUpdated; + + if (node->mUpdated) + { + //AJS flip the buffer stuff... + node->mUpdated = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT Metadata::add(Metadata *metadata) +{ + TagNode *node = (TagNode *)metadata->mList.getNext(); + + while (node != &metadata->mList) + { + TagNode *next = (TagNode *)node->getNext(); + node->removeNode(); + + if (node->mUnique) + { + LinkedListNode *current = mList.getNext(); + TagNode *existingnode = 0; + + for (;;) + { + if (current == &mList) + { + existingnode = 0; + break; + } + existingnode = (TagNode *)current; + if (!FMOD_strcmp(existingnode->mName, node->mName)) + { + break; + } + current = current->getNext(); + } + + if (existingnode) + { + //AJS double-buffered!!!!!1 + existingnode->update(node->mData[0], node->mDataLen); + node->release(); + } + else + { + addTag(node); + } + } + else + { + addTag(node); + } + + node = next; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT Metadata::addTag(TagNode *node) +{ + node->addBefore(&mList); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT Metadata::addTag(FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, bool unique) +{ + FMOD_RESULT result; + TagNode *node = 0; + LinkedListNode *current; + + if (unique) + { + current = mList.getNext(); + for (;;) + { + if (current == &mList) + { + node = 0; + break; + } + node = (TagNode *)current; + if (!FMOD_strcmp(node->mName, name) && (node->mType == type)) + { + break; + } + current = current->getNext(); + } + } + + if (node && unique) + { + result = node->update(data, datalen); + } + else + { + node = FMOD_Object_Alloc(TagNode); + if (!node) + { + return FMOD_ERR_MEMORY; + } + + node->init(type, name, data, datalen, datatype); + result = addTag(node); + } + + if (unique) + { + node->mUnique = true; + } + + return result; +} + + +} + + diff --git a/src/fmod_metadata.h b/src/fmod_metadata.h new file mode 100755 index 0000000..a138b06 --- /dev/null +++ b/src/fmod_metadata.h @@ -0,0 +1,51 @@ +#ifndef _FMOD_METADATA_H +#define _FMOD_METADATA_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_linkedlist.h" + +namespace FMOD +{ + class TagNode : public LinkedListNode + { + public: + + FMOD_TAGTYPE mType; + FMOD_TAGDATATYPE mDataType; + char *mName; + void *mData[2]; + unsigned int mDataLen; + bool mUpdated; + bool mUnique; + int mCurrentBuffer; + + public: + + TagNode() { mType = FMOD_TAGTYPE_UNKNOWN; mDataType = FMOD_TAGDATATYPE_BINARY, mName = 0; mData[0] = mData[1] = 0; mDataLen = 0; mUpdated = true; mUnique = false; mCurrentBuffer = 0; } + + FMOD_RESULT init (FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype); + FMOD_RESULT release (); + FMOD_RESULT update (void *data, unsigned int datalen); + }; + + class Metadata + { + public: + + TagNode mList; + + FMOD_RESULT release(); + + FMOD_RESULT getNumTags (int *numtags, int *numtagsupdated); + FMOD_RESULT getTag (const char *name, int index, FMOD_TAG *node); + FMOD_RESULT add (Metadata *metadata); + FMOD_RESULT addTag (TagNode *node); + FMOD_RESULT addTag (FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, bool unique); + }; +} + +#endif + + diff --git a/src/fmod_music.cpp b/src/fmod_music.cpp new file mode 100755 index 0000000..4d0da72 --- /dev/null +++ b/src/fmod_music.cpp @@ -0,0 +1,1311 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_MOD) || defined(FMOD_SUPPORT_S3M) || defined(FMOD_SUPPORT_XM) || defined(FMOD_SUPPORT_IT) + +#include "fmod_debug.h" +#include "fmod_music.h" +#include "fmod_channeli.h" +#include "fmod_channel_real.h" +#include "fmod_dspi.h" +#include "fmod_outputi.h" +#include "fmod_systemi.h" +#if defined(PLATFORM_PS2) + #include "fmodps2.h" +#endif + +#include <string.h> + +namespace FMOD +{ + +MusicSample gDummySample; +MusicVirtualChannel gDummyVirtualChannel; +MusicChannel gDummyChannel; +MusicInstrument gDummyInstrument; + +// SINE TABLE FOR TREMOLO AND VIBRATO (from protracker so 100% compatible) +unsigned char gSineTable[32] = +{ + 0, 24, 49, 74, 97,120,141,161, + 180,197,212,224,235,244,250,253, + 255,253,250,244,235,224,212,197, + 180,161,141,120, 97, 74, 49, 24 +}; + + +signed char gFineSineTable[256] = +{ + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2, +}; + +// AMIGA TYPE PERIOD TABLE, FOR 11 OCTAVES (octave 9 and 10 are only for +// .MOD's that use these sort of periods when loading, i.e dope.mod) +unsigned int gPeriodTable[134] = +{ + 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14496,//0 + 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248,//1 + 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624,//2 + 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812,//3 + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906,//4 + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,//5 + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,//6 + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,//7 + 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56,//8 + 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28,//9 + 26, 25, 23, 22, 21, 20, 18, 17, 16, 15, 15, 14,//10 + 0, 0 // <- these last 2 are for no note (132) and keyoff (133) ? +}; + +int gITLogPeriodTable[] = +{ + 54784,51712,48768,46080,43392,40960,38656,36480,34432,32512,30720,28992,//0 + 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14496,//1 + 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248,//2 + 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624,//3 + 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812,//4 + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906,//5 + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,//6 + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,//7 + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,//8 + 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56,//9 + 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28,//10 + 26, 25, 23, 22, 21, 20, 18, 17, 16, 15, 15, 14,//11 +}; + +#ifdef FMOD_NO_FPU + +unsigned int gLinearTable[768] = +{ + 535232,534749,534266,533784,533303,532822,532341,531861, + 531381,530902,530423,529944,529466,528988,528511,528034, + 527558,527082,526607,526131,525657,525183,524709,524236, + 523763,523290,522818,522346,521875,521404,520934,520464, + 519994,519525,519057,518588,518121,517653,517186,516720, + 516253,515788,515322,514858,514393,513929,513465,513002, + 512539,512077,511615,511154,510692,510232,509771,509312, + 508852,508393,507934,507476,507018,506561,506104,505647, + 505191,504735,504280,503825,503371,502917,502463,502010, + 501557,501104,500652,500201,499749,499298,498848,498398, + 497948,497499,497050,496602,496154,495706,495259,494812, + 494366,493920,493474,493029,492585,492140,491696,491253, + 490809,490367,489924,489482,489041,488600,488159,487718, + 487278,486839,486400,485961,485522,485084,484647,484210, + 483773,483336,482900,482465,482029,481595,481160,480726, + 480292,479859,479426,478994,478562,478130,477699,477268, + 476837,476407,475977,475548,475119,474690,474262,473834, + 473407,472979,472553,472126,471701,471275,470850,470425, + 470001,469577,469153,468730,468307,467884,467462,467041, + 466619,466198,465778,465358,464938,464518,464099,463681, + 463262,462844,462427,462010,461593,461177,460760,460345, + 459930,459515,459100,458686,458272,457859,457446,457033, + 456621,456209,455797,455386,454975,454565,454155,453745, + 453336,452927,452518,452110,451702,451294,450887,450481, + 450074,449668,449262,448857,448452,448048,447644,447240, + 446836,446433,446030,445628,445226,444824,444423,444022, + 443622,443221,442821,442422,442023,441624,441226,440828, + 440430,440033,439636,439239,438843,438447,438051,437656, + 437261,436867,436473,436079,435686,435293,434900,434508, + 434116,433724,433333,432942,432551,432161,431771,431382, + 430992,430604,430215,429827,429439,429052,428665,428278, + 427892,427506,427120,426735,426350,425965,425581,425197, + 424813,424430,424047,423665,423283,422901,422519,422138, + 421757,421377,420997,420617,420237,419858,419479,419101, + 418723,418345,417968,417591,417214,416838,416462,416086, + 415711,415336,414961,414586,414212,413839,413465,413092, + 412720,412347,411975,411604,411232,410862,410491,410121, + 409751,409381,409012,408643,408274,407906,407538,407170, + 406803,406436,406069,405703,405337,404971,404606,404241, + 403876,403512,403148,402784,402421,402058,401695,401333, + 400970,400609,400247,399886,399525,399165,398805,398445, + 398086,397727,397368,397009,396651,396293,395936,395579, + 395222,394865,394509,394153,393798,393442,393087,392733, + 392378,392024,391671,391317,390964,390612,390259,389907, + 389556,389204,388853,388502,388152,387802,387452,387102, + 386753,386404,386056,385707,385359,385012,384664,384317, + 383971,383624,383278,382932,382587,382242,381897,381552, + 381208,380864,380521,380177,379834,379492,379149,378807, + 378466,378124,377783,377442,377102,376762,376422,376082, + 375743,375404,375065,374727,374389,374051,373714,373377, + 373040,372703,372367,372031,371695,371360,371025,370690, + 370356,370022,369688,369355,369021,368688,368356,368023, + 367691,367360,367028,366697,366366,366036,365706,365376, + 365046,364717,364388,364059,363731,363403,363075,362747, + 362420,362093,361766,361440,361114,360788,360463,360137, + 359813,359488,359164,358840,358516,358193,357869,357547, + 357224,356902,356580,356258,355937,355616,355295,354974, + 354654,354334,354014,353695,353376,353057,352739,352420, + 352103,351785,351468,351150,350834,350517,350201,349885, + 349569,349254,348939,348624,348310,347995,347682,347368, + 347055,346741,346429,346116,345804,345492,345180,344869, + 344558,344247,343936,343626,343316,343006,342697,342388, + 342079,341770,341462,341154,340846,340539,340231,339924, + 339618,339311,339005,338700,338394,338089,337784,337479, + 337175,336870,336566,336263,335959,335656,335354,335051, + 334749,334447,334145,333844,333542,333242,332941,332641, + 332341,332041,331741,331442,331143,330844,330546,330247, + 329950,329652,329355,329057,328761,328464,328168,327872, + 327576,327280,326985,326690,326395,326101,325807,325513, + 325219,324926,324633,324340,324047,323755,323463,323171, + 322879,322588,322297,322006,321716,321426,321136,320846, + 320557,320267,319978,319690,319401,319113,318825,318538, + 318250,317963,317676,317390,317103,316817,316532,316246, + 315961,315676,315391,315106,314822,314538,314254,313971, + 313688,313405,313122,312839,312557,312275,311994,311712, + 311431,311150,310869,310589,310309,310029,309749,309470, + 309190,308911,308633,308354,308076,307798,307521,307243, + 306966,306689,306412,306136,305860,305584,305308,305033, + 304758,304483,304208,303934,303659,303385,303112,302838, + 302565,302292,302019,301747,301475,301203,300931,300660, + 300388,300117,299847,299576,299306,299036,298766,298497, + 298227,297958,297689,297421,297153,296884,296617,296349, + 296082,295815,295548,295281,295015,294749,294483,294217, + 293952,293686,293421,293157,292892,292628,292364,292100, + 291837,291574,291311,291048,290785,290523,290261,289999, + 289737,289476,289215,288954,288693,288433,288173,287913, + 287653,287393,287134,286875,286616,286358,286099,285841, + 285583,285326,285068,284811,284554,284298,284041,283785, + 283529,283273,283017,282762,282507,282252,281998,281743, + 281489,281235,280981,280728,280475,280222,279969,279716, + 279464,279212,278960,278708,278457,278206,277955,277704, + 277453,277203,276953,276703,276453,276204,275955,275706, + 275457,275209,274960,274712,274465,274217,273970,273722, + 273476,273229,272982,272736,272490,272244,271999,271753, + 271508,271263,271018,270774,270530,270286,270042,269798, + 269555,269312,269069,268826,268583,268341,268099,267857 +}; + + +#ifdef FMOD_SUPPORT_IT + +/* + // To generate following table + void main() + { + int count; + for (count=0; count < TABSIZE; count++) + { + float scalar = (float)FMUSIC_ITFINETUNETABLEMAX / (float)FMUSIC_ITFINETUNETABLESIZE * (float)count; + float logs; + + logs = (float)log(scalar); + logs /= 0.693147f; + logs *= 768.0f; + + printf("%4d, ", (int)logs); + if (!((count+1) % 11)) printf("\n"); + } + } +*/ + +signed short gITFineTuneTable[FMUSIC_ITFINETUNETABLESIZE] = +{ + 0, -4926, -4158, -3709, -3390, -3143, -2941, -2770, -2622, -2492, -2375, + -2269, -2173, -2084, -2002, -1926, -1854, -1787, -1724, -1664, -1607, -1553, + -1501, -1452, -1405, -1360, -1316, -1274, -1234, -1195, -1158, -1121, -1086, + -1052, -1019, -987, -956, -925, -896, -867, -839, -812, -785, -759, + -733, -709, -684, -660, -637, -614, -592, -570, -548, -527, -506, + -486, -466, -447, -427, -408, -390, -371, -353, -336, -318, -301, + -284, -267, -251, -235, -219, -203, -188, -172, -157, -143, -128, + -113, -99, -85, -71, -57, -44, -30, -17, -4, 8, 21, + 34, 46, 58, 71, 83, 95, 107, 118, 130, 141, 153, + 164, 175, 186, 197, 208, 219, 229, 240, 250, 261, 271, + 281, 291, 301, 311, 320, 330, 340, 349, 359, 368, 377, + 386, 396, 405, 414, 422, 431, 440, 449, 457, 466, 474, + 483, 491, 500, 508, 516, 524, 532, 540, 548, 556, 564, + 572, 579, 587, 595, 602, 610, 617, 624, 632, 639, 646, + 654, 661, 668, 675, 682, 689, 696, 703, 710, 717, 723, + 730, 737, 743, 750, 757, 763, 770, 776, 783, 789, 795, + 802, 808, 814, 820, 826, 833, 839, 845, 851, 857, 863, + 869, 875, 881, 886, 892, 898, 904, 909, 915, 921, 926, + 932, 938, 943, 949, 954, 960, 965, 971, 976, 981, 987, + 992, 997, 1003, 1008, 1013, 1018, 1023, 1029, 1034, 1039, 1044, + 1049, 1054, 1059, 1064, 1069, 1074, 1079, 1084, 1088, 1093, 1098, + 1103, 1108, 1112, 1117, 1122, 1127, 1131, 1136, 1141, 1145, 1150, + 1154, 1159, 1164, 1168, 1173, 1177, 1182, 1186, 1190, 1195, 1199, + 1204, 1208, 1212, 1217, 1221, 1225, 1230, 1234, 1238, 1242, 1247, + 1251, 1255, 1259, 1263, 1268, 1272, 1276, 1280, 1284, 1288, 1292, + 1296, 1300, 1304, 1308, 1312, 1316, 1320, 1324, 1328, 1332, 1336, + 1340, 1343, 1347, 1351, 1355, 1359, 1363, 1366, 1370, 1374, 1378, + 1381, 1385, 1389, 1392, 1396, 1400, 1404, 1407, 1411, 1414, 1418, + 1422, 1425, 1429, 1432, 1436, 1439, 1443, 1447, 1450, 1454, 1457, + 1461, 1464, 1467, 1471, 1474, 1478, 1481, 1485, 1488, 1491, 1495, + 1498, 1501, 1505, 1508, 1511, 1515, 1518, 1521, 1525, 1528, 1531, + 1534, 1538, 1541, 1544, 1547, 1551, 1554, 1557, 1560, 1563, 1566, + 1570, 1573, 1576, 1579, 1582, 1585, 1588, 1591, 1594, 1598, 1601, + 1604, 1607, 1610, 1613, 1616, 1619, 1622, 1625, 1628, 1631, 1634, + 1637, 1640, 1643, 1646, 1649, 1651, 1654, 1657, 1660, 1663, 1666, + 1669, 1672, 1675, 1677, 1680, 1683, 1686, 1689, 1692, 1694, 1697, + 1700, 1703, 1706, 1708, 1711, 1714, 1717, 1720, 1722, 1725, 1728, + 1730, 1733, 1736, 1739, 1741, 1744, 1747, 1749, 1752, 1755, 1757, + 1760, 1763, 1765, 1768, 1771, 1773, 1776, 1778, 1781, 1784, 1786, + 1789, 1791, 1794, 1797, 1799, 1802, 1804, 1807, 1809, 1812, 1814, + 1817, 1819, 1822, 1824, 1827, 1829, 1832, 1834, 1837, 1839, 1842, + 1844, 1847, 1849, 1852, 1854, 1856, 1859, 1861, 1864, 1866, 1868, + 1871, 1873, 1876, 1878, 1880, 1883, 1885, 1888, 1890, 1892, 1895, + 1897, 1899, 1902, 1904, 1906, 1909, 1911, 1913, 1916, 1918, 1920, + 1922, 1925, 1927, 1929, 1932, 1934, 1936, 1938, 1941, 1943, 1945, + 1947, 1950, 1952, 1954, 1956, 1958, 1961, 1963, 1965, 1967, 1969, + 1972, 1974, 1976, 1978, 1980, 1983, 1985, 1987, 1989, 1991, 1993, + 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2013, 2015, 2017, + 2019, 2021, 2023, 2025, 2027, 2029, 2031, 2033, 2036, 2038, 2040, + 2042, 2044, 2046, 2048, 2050, 2052, 2054, 2056, 2058, 2060, 2062, + 2064, 2066, 2068, 2070, 2072, 2074, 2076, 2078, 2080, 2082, 2084, + 2086, 2088, 2090, 2092, 2094, 2096, 2098, 2100, 2102, 2104, 2106, + 2108, 2109, 2111, 2113, 2115, 2117, 2119, 2121, 2123, 2125, 2127, + 2129, 2131, 2132, 2134, 2136, 2138, 2140, 2142, 2144, 2146, 2147, + 2149, 2151, 2153, 2155, 2157, 2159, 2160, 2162, 2164, 2166, 2168, + 2170, 2172, 2173, 2175, 2177, 2179, 2181, 2182, 2184, 2186, 2188, + 2190, 2191, 2193, 2195, 2197, 2199, 2200, 2202, 2204, 2206, 2207, + 2209, 2211, 2213, 2215, 2216, 2218, 2220, 2222, 2223, 2225, 2227, + 2229, 2230, 2232, 2234, 2235, 2237, 2239, 2241, 2242, 2244, 2246, + 2247, 2249, 2251, 2253, 2254, 2256, 2258, 2259, 2261, 2263, 2264, + 2266, 2268, 2269, 2271, 2273, 2274, 2276, 2278, 2279, 2281, 2283, + 2284, 2286, 2288, 2289, 2291, 2293, 2294, 2296, 2298, 2299, 2301, + 2302, 2304, 2306, 2307, 2309, 2311, 2312, 2314, 2315, 2317, 2319, + 2320, 2322, 2323, 2325, 2327, 2328, 2330, 2331, 2333, 2334, 2336, + 2338, 2339, 2341, 2342, 2344, 2345, 2347, 2349, 2350, 2352, 2353, + 2355, 2356, 2358, 2359, 2361, 2362, 2364, 2366, 2367, 2369, 2370, + 2372, 2373, 2375, 2376, 2378, 2379, 2381, 2382, 2384, 2385, 2387, + 2388, 2390, 2391, 2393, 2394, 2396, 2397, 2399, 2400, 2402, 2403, + 2405, 2406, 2408, 2409, 2411, 2412, 2414, 2415, 2417, 2418, 2419, + 2421, 2422, 2424, 2425, 2427, 2428, 2430, 2431, 2433, 2434, 2435, + 2437, 2438, 2440, 2441, 2443, 2444, 2445, 2447, 2448, 2450, 2451, + 2453, 2454, 2455, 2457, 2458, 2460, 2461, 2462, 2464, 2465, 2467, + 2468, 2469, 2471, 2472, 2474, 2475, 2476, 2478, 2479, 2481, 2482, + 2483, 2485, 2486, 2488, 2489, 2490, 2492, 2493, 2494, 2496, 2497, + 2498, 2500, 2501, 2503, 2504, 2505, 2507, 2508, 2509, 2511, 2512, + 2513, 2515, 2516, 2517, 2519, 2520, 2521, 2523, 2524, 2525, 2527, + 2528, 2529, 2531, 2532, 2533, 2535, 2536, 2537, 2539, 2540, 2541, + 2542, 2544, 2545, 2546, 2548, 2549, 2550, 2552, 2553, 2554, 2555, + 2557, 2558, 2559, 2561, 2562, 2563, 2565, 2566, 2567, 2568, 2570, + 2571, 2572, 2573, 2575, 2576, 2577, 2579, 2580, 2581, 2582, 2584, + 2585, 2586, 2587, 2589, 2590, 2591, 2592, 2594, 2595, 2596, 2597, + 2599, 2600, 2601, 2602, 2604, 2605, 2606, 2607, 2609, 2610, 2611, + 2612, 2613, 2615, 2616, 2617, 2618, 2620, 2621, 2622, 2623, 2624, + 2626, 2627, 2628, 2629, 2630, 2632, 2633, 2634, 2635, 2636, 2638, + 2639, 2640, 2641, 2642, 2644, 2645, 2646, 2647, 2648, 2650, 2651, + 2652, 2653, 2654, 2656, 2657, 2658, 2659, 2660, 2661, 2663, 2664, + 2665, 2666, 2667, 2668, 2670, 2671, 2672, 2673, 2674, 2675, 2677, + 2678, 2679, 2680, 2681, 2682, 2684, 2685, 2686, 2687, 2688, 2689, + 2690, 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2700, 2701, 2702, + 2703, 2704, 2705, 2706, 2707, 2709, 2710, 2711, 2712, 2713, 2714, + 2715, 2716, 2718, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, + 2728, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2739, + 2740, 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2750, 2751 +}; + +#endif /* FMOD_SUPPORT_IT */ + +#endif /* FMOD_NO_FPU */ + + + +#if defined(FMOD_SUPPORT_MOD) || defined(FMOD_SUPPORT_S3M) || defined(FMOD_SUPPORT_XM) || defined(FMOD_SUPPORT_IT) + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::play(bool fromopen) +{ + FMOD_RESULT result; + int count; + + result = stop(); + if (result != FMOD_OK) + { + return result; + } + + mGlobalVolume = mDefaultGlobalVolume; + mSpeed = mDefaultSpeed; + mRow = 0; + mOrder = 0; + mNextOrder = 0; /* make it 'jump' to order 0, mainly for callback triggers */ + mNextRow = 0; /* make it 'jump' to row 0, mainly for callback triggers */ + mMixerSamplesLeft = 0; + mTick = 0; + mPatternDelay = 0; + mPatternDelayTicks = 0; + mPCMOffset = 0; + mFinished = false; + if (fromopen) + { + mDSPTick = 1; + } + + /* + Need to explicitly construct the ChannelGroupI here because MusicSong doesn't have a constructor + */ + new (&mChannelGroup) ChannelGroupI(); + mChannelGroup.mDSPHead = mDSPHead; + mChannelGroup.mDSPMixTarget = mDSPHead; + mChannelGroup.mVolume = 1.0f; + + if (mVisited) + { + FMOD_memset(mVisited, 0, sizeof(bool) * mNumOrders * FMUSIC_MAXROWS); + } + + setBPM(mDefaultBPM); + + if (mNumChannels && mMusicChannel) + { + MusicChannel *cptr; + + for (count = 0; count < mNumChannels; count++) + { + float oldmastervolume; + + cptr = mMusicChannel[count]; + + oldmastervolume = cptr->mMasterVolume; + + FMOD_memset(cptr, 0, sizeof(MusicChannel)); + + cptr->mVirtualChannelHead.initNode(); + + /* + Music system channel initialization + */ + cptr->mGlobalVolume = mDefaultVolume[count]; + cptr->mPan = mDefaultPan[count]; + + if (fromopen) + { + cptr->mMasterVolume = 1.0f; + } + else + { + cptr->mMasterVolume = oldmastervolume; + } + } + } + if (mNumVirtualChannels) + { + MusicVirtualChannel *vcptr; + + for (count = 0; count < mNumVirtualChannels; count++) + { + vcptr = &mVirtualChannel[count]; + + FMOD_memset(vcptr, 0, sizeof(MusicVirtualChannel)); + vcptr->mChannel.init(); + vcptr->mChannel.mFlags |= CHANNELI_FLAG_MUSICOWNED; + vcptr->mIndex = count; + vcptr->mChannel.mIndex = count; + vcptr->mSong = this; + + #ifdef FMOD_SUPPORT_HARDWAREXM + if (!fromopen) + { + #ifdef FMOD_SUPPORT_SOFTWARE + if (!mChannelSoftware) + #endif + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "MusicSong::play", "RESERVING HW VOICE %d / %d\n", count, mNumVirtualChannels)); + + mSystem->mOutput->reserveVoice(count, true); + } + } + #endif + } + } + + mPlaying = true; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::spawnNewVirtualChannel(MusicChannel *cptr, MusicSample *sptr, MusicVirtualChannel **newvcptr) +{ + MusicVirtualChannel *vcptr = 0; + int count; + + for (count = 0; count < mNumVirtualChannels; count++) + { + if (!mVirtualChannel[count].mAllocated) + { + vcptr = &mVirtualChannel[count]; + vcptr->mAllocated = true; + break; + } + } + if (!vcptr) + { + return FMOD_ERR_INTERNAL; + } + + /* + Insert the virtual channel as the first node in the fmusic_channel's vchannel list + */ + vcptr->addAfter(&cptr->mVirtualChannelHead); + + /* + Initialize some stuff + */ + vcptr->mBackground = false; + + vcptr->mEnvVolume.mTick = 0; + vcptr->mEnvVolume.mPosition = 0; + vcptr->mEnvVolume.mValue = 64; + vcptr->mEnvVolume.mFraction = 64 << 16; + vcptr->mEnvVolume.mDelta = 0; + vcptr->mEnvVolume.mStopped = false; + + vcptr->mEnvPan.mTick = 0; + vcptr->mEnvPan.mPosition = 0; + vcptr->mEnvPan.mValue = 128; + vcptr->mEnvPan.mFraction = 128 << 16; + vcptr->mEnvPan.mDelta = 0; + vcptr->mEnvPan.mStopped = false; + + vcptr->mEnvPitchTick = 0; + vcptr->mEnvPitchPos = 0; + vcptr->mEnvPitchFrac = 0; + vcptr->mEnvPitch = 0; + vcptr->mEnvPitchDelta = 0; + vcptr->mEnvPitchStopped = false; + + vcptr->mFadeOutVolume = 1024; + + if (newvcptr) + { + *newvcptr = vcptr; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicVirtualChannel::cleanUp() +{ + bool playing = false; + + mChannel.isPlaying(&playing); + + if (!playing) + { + if (mSong->mLowPass) + { + mSong->mLowPass[mChannel.mIndex]->remove(); + } + + mNoteControl = 0; + removeNode(); /* unlink channel from mod's mixlist */ + mAllocated = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::setBPM(int bpm) +{ + float hz; + + if (bpm < 1) + { + bpm = 1; + } + + mBPM = bpm; + + hz = (float)bpm * 2.0f / 5.0f; + hz *= mMasterSpeed; + + /* number of samples */ + if (hz >= 0.01f) + { + mMixerSamplesPerTick = (int)((float)waveformat[0].frequency / hz); + } + + #ifdef FMOD_SUPPORT_HARDWAREXM + #ifdef FMOD_SUPPORT_SOFTWARE + if (!mChannelSoftware) + #endif + { + mSystem->mStreamThread.setPeriod(1000 / hz); + } + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::stop() +{ + int count; + + mPlaying = false; + mFinished = true; + + if (mMusicChannel) + { + /* + Stop everything playing + */ + for (count = 0; count < mNumChannels; count++) + { + MusicChannel *cptr; + + cptr = mMusicChannel[count]; + + if (cptr && cptr->mVirtualChannelHead.getNext()) + { + while (!cptr->mVirtualChannelHead.isEmpty()) + { + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); /* stop this channel */ + vcptr->mChannel.mRealChannel[0] = 0; + if (mLowPass) + { + mLowPass[vcptr->mChannel.mIndex]->remove(); + } + vcptr->cleanUp(); + } + } + } + + for (count = 0; count < mNumVirtualChannels; count++) + { + #ifdef FMOD_SUPPORT_HARDWAREXM + #ifdef FMOD_SUPPORT_SOFTWARE + if (!mChannelSoftware) + #endif + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "MusicSong::stop", "UNRESERVING HW VOICE %d / %d\n", count, mNumVirtualChannels)); + + mSystem->mOutput->reserveVoice(count, false); + } + #endif + } + } + + #ifdef FMOD_SUPPORT_HARDWAREXM + #ifdef FMOD_SUPPORT_SOFTWARE + if (!mChannelSoftware) + #endif + { + mSystem->mStreamThread.setPeriod(10); + } + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::playSound(MusicSample *sample, MusicVirtualChannel *vcptr, bool addfilter, SNDMIXPLUGIN *plugin) +{ + FMOD_RESULT result; + ChannelI *channel = &vcptr->mChannel; + ChannelReal *realchannel = channel->mRealChannel[0]; + +#ifdef PLATFORM_PSP + if (!mChannelSoftware) + { + result = channel->stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + } +#endif + + result = mChannelPool->allocateChannel(&realchannel, vcptr->mFlip ? vcptr->mIndex + mNumVirtualChannels : vcptr->mIndex, 1, 0); + if (result != FMOD_OK) + { + return result; + } + + //FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "MusicSong::playSound", "use vcptr->mIndex %d : flip = %d.\n", vcptr->mIndex, vcptr->mFlip)); + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mChannelSoftware) + { + vcptr->mFlip = !vcptr->mFlip; + } +#endif + + if (channel->mRealChannel[0]) + { + vcptr->mChannel.setVolume(0); + } + channel->mRealChannel[0] = realchannel; + + /* + If this channel is using an FX send, send it to the plugin DSP, otherwise send it to the normal mixing group. + */ + if (plugin) + { + channel->mChannelGroup = &plugin->mChannelGroup; + } + else + { + channel->mChannelGroup = &mChannelGroup; + } + channel->mSpeakerMode = FMOD_SPEAKERMODE_STEREO_LINEAR; + + result = channel->play(sample->mSound, true, true, false); + if (result != FMOD_OK) + { + channel->stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS); + return result; + } + if (vcptr->mSampleOffset > 0) + { + channel->setPosition(vcptr->mSampleOffset, FMOD_TIMEUNIT_PCM); + vcptr->mSampleOffset = 0; + } + + if (mLowPass) + { + mLowPass[channel->mIndex]->remove(); + if (addfilter) + { + channel->addDSP(mLowPass[channel->mIndex], 0); + } + } + + channel->setPaused(false); + + /* + It would be nice if this only did the ones that were connected in the mod player instead of flushing everything. + If we dont do this the connects/disconnects dont happen until the main sfx mixer feels like it. + */ +#ifdef FMOD_SUPPORT_SOFTWARE + if (mChannelSoftware) + { + mSystem->flushDSPConnectionRequests(); + } +#endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::fineTune2Hz(unsigned char ft, unsigned int *hz) +{ + if (!hz) + { + return FMOD_ERR_INVALID_PARAM; + } + + switch(ft) + { + case 0 : *hz = 8363; break; + case 1 : *hz = 8413; break; + case 2 : *hz = 8463; break; + case 3 : *hz = 8529; break; + case 4 : *hz = 8581; break; + case 5 : *hz = 8651; break; + case 6 : *hz = 8723; break; + case 7 : *hz = 8757; break; + case 8 : *hz = 7895; break; + case 9 : *hz = 7941; break; + case 10 : *hz = 7985; break; + case 11 : *hz = 8046; break; + case 12 : *hz = 8107; break; + case 13 : *hz = 8169; break; + case 14 : *hz = 8232; break; + case 15 : *hz = 8280; break; + default : *hz = 8363; break; + }; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::getLengthInternal(unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ + if (lengthtype == FMOD_TIMEUNIT_MODORDER) + { + *length = mNumOrders; + + return FMOD_OK; + } + else if (lengthtype == FMOD_TIMEUNIT_MODPATTERN) + { + *length = mNumPatterns; + + return FMOD_OK; + } + else if (lengthtype == FMOD_TIMEUNIT_MODROW) + { + *length = mPattern[mOrderList[mOrder]].mRows; + + return FMOD_OK; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::getPositionInternal(unsigned int *position, FMOD_TIMEUNIT postype) +{ + if (postype == FMOD_TIMEUNIT_MODORDER) + { + *position = mOrder; + + return FMOD_OK; + } + else if (postype == FMOD_TIMEUNIT_MODPATTERN) + { + *position = mOrderList[mOrder]; + + return FMOD_OK; + } + else if (postype == FMOD_TIMEUNIT_MODROW) + { + *position = mRow; + + return FMOD_OK; + } + + return FMOD_OK; +} + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::getMusicNumChannelsInternal(int *numchannels) +{ + if (!numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numchannels = mNumChannels; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::setMusicChannelVolumeInternal(int channel, float volume) +{ + if (channel < 0 || channel >= mNumChannels || volume < 0.0f || volume > 1.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + mMusicChannel[channel]->mMasterVolume = volume; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::getMusicChannelVolumeInternal(int channel, float *volume) +{ + if (channel < 0 || channel >= mNumChannels || !volume) + { + return FMOD_ERR_INVALID_PARAM; + } + + *volume = mMusicChannel[channel]->mMasterVolume; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT MusicSong::getHardwareMusicChannel(ChannelReal **realchannel) +{ + if (!realchannel) + { + return FMOD_ERR_INVALID_PARAM; + } + + new (&mHardwareMusicChannel) ChannelMusic(); + + *realchannel = &mHardwareMusicChannel; + + mHardwareMusicChannel.mFlags |= CHANNELREAL_FLAG_ALLOCATED; + mHardwareMusicChannel.mMusic = this; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelMusic::updateStream() +{ + bool paused; + + mMusic->mChannelGroup.getPaused(&paused); + + if (!paused) + { + mMusic->mDescription.update(mMusic); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelMusic::stop() +{ + return mMusic->stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelMusic::start() +{ + return mMusic->play(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelMusic::setPaused(bool paused) +{ + if (mMusic->mMusicChannel) + { + mMusic->mChannelGroup.setPaused(paused); + int count; + + /* + Stop everything playing + */ + for (count = 0; count < mMusic->mNumChannels; count++) + { + MusicChannel *cptr; + + cptr = mMusic->mMusicChannel[count]; + + if (cptr && cptr->mVirtualChannelHead.getNext()) + { + MusicVirtualChannel *vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext(); + vcptr->mChannel.setPaused(paused); + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelMusic::setVolume(float volume) +{ + if (mMusic->mMusicChannel) + { + mMusic->mChannelGroup.setVolume(volume); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK MusicSong::getLengthCallback(FMOD_CODEC_STATE *codec, unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ + MusicSong *cmusic = (MusicSong *)codec; + + return cmusic->getLengthInternal(length, lengthtype); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK MusicSong::getPositionCallback(FMOD_CODEC_STATE *codec, unsigned int *position, FMOD_TIMEUNIT postype) +{ + MusicSong *cmusic = (MusicSong *)codec; + + return cmusic->getPositionInternal(position, postype); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK MusicSong::getMusicNumChannelsCallback(FMOD_CODEC_STATE *codec, int *numchannels) +{ + MusicSong *cmusic = (MusicSong *)codec; + + return cmusic->getMusicNumChannelsInternal(numchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK MusicSong::setMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float volume) +{ + MusicSong *cmusic = (MusicSong *)codec; + + return cmusic->setMusicChannelVolumeInternal(channel, volume); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK MusicSong::getMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float *volume) +{ + MusicSong *cmusic = (MusicSong *)codec; + + return cmusic->getMusicChannelVolumeInternal(channel, volume); +} +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK MusicSong::getHardwareMusicChannelCallback(FMOD_CODEC_STATE *codec, ChannelReal **realchannel) +{ + MusicSong *cmusic = (MusicSong *)codec; + + return cmusic->getHardwareMusicChannel(realchannel); +} + + +#endif + +} + +#endif diff --git a/src/fmod_music.h b/src/fmod_music.h new file mode 100755 index 0000000..658119a --- /dev/null +++ b/src/fmod_music.h @@ -0,0 +1,495 @@ +#ifndef _FMOD_MUSIC_H +#define _FMOD_MUSIC_H + +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_MOD) || defined(FMOD_SUPPORT_S3M) || defined(FMOD_SUPPORT_XM) || defined(FMOD_SUPPORT_IT) + +#include "fmod.h" +#include "fmod_codeci.h" +#include "fmod_channel_real.h" +#include "fmod_channeli.h" +#include "fmod_channelgroupi.h" +#include "fmod_channelpool.h" +#include "fmod_linkedlist.h" +#include "fmod_string.h" +#include "fmod_types.h" + +namespace FMOD +{ + class MusicSong; + class SoundI; + class ChannelSoftware; + + const int FMUSIC_FLAGS_NORMALVOLUMESLIDES = 0; + const int FMUSIC_FLAGS_FASTVOLUMESLIDES = 1; + /*const int FMUSIC_FLAGS_LINEARFREQUENCY = 2; */ + /*const int FMUSIC_FLAGS_AMIGAFREQUENCY = 4; */ + + const int FMUSIC_MAXORDERS = 256; + const int FMUSIC_MAXROWS = 256; + + const int FMUSIC_KEYCUT = 254; + const int FMUSIC_KEYOFF = 255; + + const int FMUSIC_FREQ = 0x1; + const int FMUSIC_VOLUME = 0x2; + const int FMUSIC_PAN = 0x4; + const int FMUSIC_TRIGGER = 0x8; + const int FMUSIC_SURROUND = 0x10; + const int FMUSIC_STOP = 0x20; + const int FMUSIC_SURROUNDOFF = 0x40; + const int FMUSIC_SETPAN = 0x80; + + const int FMUSIC_ENVELOPE_OFF = 0x0; + const int FMUSIC_ENVELOPE_ON = 0x1; + const int FMUSIC_ENVELOPE_SUSTAIN = 0x2; + const int FMUSIC_ENVELOPE_LOOP = 0x4; + const int FMUSIC_ENVELOPE_CARRY = 0x8; + const int FMUSIC_ENVELOPE_FILTER = 0x10; + + const int FMUSIC_CUT = 0; + const int FMUSIC_CONTINUE = 1; + const int FMUSIC_OFF = 2; + const int FMUSIC_FADEOUT = 3; + + const int FMUSIC_FILTER_PRECISION = 8192; + + + #ifndef PLATFORM_GC + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + #endif + + typedef struct MusicEnvelopeNode + { + signed char mValue FMOD_PACKED_INTERNAL; + unsigned short mTick FMOD_PACKED_INTERNAL; + } FMOD_PACKED MusicEnvelopeNode; + + #ifndef PLATFORM_GC + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + #endif + + typedef struct _SNDMIXPLUGININFO + { + unsigned int dwPluginId1; + unsigned int dwPluginId2; + unsigned int dwInputRouting; // MIXPLUG_INPUTF_XXXX + unsigned int dwOutputRouting; // 0=mix 0x80+=fx + unsigned int dwReserved[4]; // Reserved for routing info + char szName[32]; + char szLibraryName[64]; // original DLL name + } SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 + + typedef struct _SNDMIXPLUGINDATA_ECHO + { + float unknown; + float mWetDryMix; + float mFeedback; + float mLeftDelay; + float mRightDelay; + float mPanDelay; + } SNDMIXPLUGINDATA_ECHO; + + typedef struct _SNDMIXPLUGIN + { + ChannelGroupI mChannelGroup; + void *pMixPlugin; + SNDMIXPLUGININFO Info; + } SNDMIXPLUGIN; + + class MusicSample + { + public: + + MusicSample() : mSound(0), mMiddleC(8363), mDefaultVolume(255), mLoopStart(0), mLoopLength(0), mRelative(0) { } + + SoundI *mSound; + unsigned int mMiddleC; + unsigned char mDefaultVolume; + unsigned char mDefaultPan; + unsigned int mLoopStart; + unsigned int mLoopLength; + int mRelative; + int mFineTune; + unsigned int mRawLength; + unsigned char mFlags; + unsigned char mGlobalVolume; /* sample global volume (scalar) */ + unsigned int mSusLoopBegin; /* envelope sustain loop start */ + unsigned int mSusLoopEnd; /* envelope sustain loop length */ + unsigned char mVibSpeed; /* vibrato speed 0-64 */ + unsigned char mVibDepth; /* vibrato depth 0-64 */ + unsigned char mVibType; /* vibrato type 0=sine, 1=rampdown, 2=square, 3=random */ + unsigned char mVibRate; /* vibrato rate 0-64 (like sweep?) */ + FMOD_SOUND_FORMAT mOriginalFormat; + }; + + class MusicEnvelopeState + { + public: + int mTick; /* tick counter for envelope position */ + int mPosition; /* envelope position */ + int mFraction; /* fractional interpolated envelope volume */ + int mValue; /* final interpolated envelope volume */ + int mDelta; /* delta step between points */ + bool mStopped; /* flag to say whether envelope has finished or not */ + }; + + /* + Channel type - contains information on a virtual child channel + */ + class MusicVirtualChannel : public LinkedListNode + { + public: + + int mIndex; + bool mAllocated; + bool mFlip; + ChannelI mChannel; /* pointer to FSOUND system mixing channel */ + MusicSample *mSample; /* pointer to MusicSample object. */ + MusicSong *mSong; /* Pointer to MusicSong object. */ + + unsigned char mLastInstrument; /* last instrument set in channel */ + unsigned char mLastNote; /* last note set in channel */ + unsigned char mLastSample; /* last sample set in channel */ + + bool mBackground; /* flag to say whether channel is a background NNA type channel or not */ + unsigned char mNoteControl; /* flags for FSOUND */ + + unsigned char mNNA; /* nna type for this channel */ + unsigned char mVolType; /* Type of envelope,bit 0:On */ + + int mFrequency; /* current mod frequency period for this channel */ + int mVolume; /* current mod volume for this channel */ + int mPan; /* current mod pan for this channel */ + int mVolumeDelta; /* delta for volume commands.. tremolo/tremor etc */ + int mFrequencyDelta; /* delta for frequency commands.. vibrato/arpeggio etc */ + int mPanDelta; /* delta for pan commands.. panbrello */ + unsigned int mSampleOffset; /* sample offset for this channel in SAMPLES */ + int mDirection; + + int mSampGlobalVol; /* this current sample's global volume */ + + MusicEnvelopeState mEnvVolume; + MusicEnvelopeState mEnvPan; + int mEnvPitchTick; /* tick counter for envelope position */ + int mEnvPitchPos; /* envelope position */ + int mEnvPitchFrac; /* fractional interpolated envelope pitch */ + int mEnvPitch; /* final interpolated envelope pitch */ + int mEnvPitchDelta; /* delta step between points */ + bool mEnvPitchStopped; /* flag to say whether envelope has finished or not */ + + bool mFade; /* flag whether to start fade or not */ + int mFadeOutVolume; /* volume fade out */ + int mIVibPos; /* instrument vibrato position */ + int mIVibSweepPos; /* instrument vibrato sweep position */ + + bool mKeyOff; /* flag whether keyoff has been hit or not) */ + bool mRamping; /* ramping out instead of stopping */ + int mTicksToDie; + + FMOD_RESULT cleanUp(); + }; + + + /* + Multi sample extended instrument + */ + class MusicInstrument + { + public: + signed char mName[28]; /* instrument name */ + int mNumSamples; /* number of samples in this instrument */ + MusicSample mSample[16]; /* 16 samples per instrument */ + unsigned char mKeyMap[96]; /* sample keymap assignments */ + + unsigned char mVolumeType; /* Type of envelope,bit 0:On 1:Sustain 2:Loop */ + unsigned char mVolumeNumPoints; /* Number of volume envelope points */ + unsigned short mVolumePoints[40]; /* Volume envelope points (80 bytes - enough for 25 3 byte IT envelopes) */ + unsigned char mVolumeSustain; /* Volume sustain point */ + unsigned char mVolumeLoopStart; /* Volume envelope loop start */ + unsigned char mVolumeLoopEnd; /* Volume envelope loop end */ + unsigned char mVolumeSustainLoopStart;/* Volume envelope sustain loop start */ + unsigned char mVolumeSustainLoopEnd; /* Volume envelope sustain loop end */ + + unsigned char mPanType; /* Type of envelope,bit 0:On 1:Sustain 2:Loop */ + unsigned char mPanNumPoints; /* Number of panning envelope points */ + unsigned short mPanPoints[40]; /* Panning envelope points */ + unsigned char mPanSustain; /* Panning sustain point */ + unsigned char mPanLoopStart; /* Panning envelope loop start */ + unsigned char mPanLoopEnd; /* Panning envelope loop end */ + unsigned char mPanSustainLoopStart; /* Panning envelope sustain loop start */ + unsigned char mPanSustainLoopEnd; /* Panning envelope sustain loop end */ + + unsigned char mPitchType; /* Type of envelope,bit 0:On 1:Sustain 2:Loop */ + unsigned char mPitchNumpoints; /* Number of pitch envelope points */ + unsigned short mPitchPoints[40]; /* Pitch envelope points */ + unsigned char mPitchSustain; /* Pitch sustain point */ + unsigned char mPitchLoopStart; /* Pitch envelope loop start */ + unsigned char mPitchLoopEnd; /* Pitch envelope loop end */ + unsigned char mPitchSustainLoopStart; /* Pitch envelope sustain loop start */ + unsigned char mPitchSustainLoopEnd; /* Pitch envelope sustain loop end */ + + unsigned char mVibratoType; /* Instrument Vibrato type */ + unsigned char mVibratoSweep; /* Time it takes for vibrato to fully kick in */ + unsigned char mVibratoDepth; /* depth of vibrato */ + unsigned char mVibratoRate; /* rate of vibrato */ + + unsigned short mVolumeFade; /* fade out value */ + + unsigned char mGlobalVolume; /* global volume (scalar) */ + unsigned char mDefaultPan; /* default pan */ + unsigned char mNNA; /* New Note Action type */ + unsigned char mDupCheckType; /* Duplicate Check type */ + unsigned char mDupCheckAction; /* Duplicate Check action */ + unsigned char mPitchPanSep; /* pitch pan seperation */ + unsigned char mPitchPanCenter; /* pitch pan center */ + unsigned char mVolumeVariation; /* random volume variation */ + unsigned char mPanVariation; /* random panning variation */ + unsigned char mNoteTable[240]; /* keymap / notetable */ + + unsigned int mFilterCutOff; + unsigned int mFilterResonance; + + unsigned char mMIDIOutput; /* MIDI Channel / FX Send. */ + }; + + + class MusicChannel + { + public: + MusicVirtualChannel mVirtualChannelHead; /* list of child channels for NNA's - mods/s3ms/xms have 1 child */ + unsigned char mInstrument; /* last instrument set in channel */ + unsigned char mNote; /* last note set in channel */ + unsigned char mSample; /* last sample set in channel */ + unsigned char mRealNote; /* last realnote set in channel */ + int mPeriod; /* last period set in channel */ + unsigned char mRecentEffect; /* previous row's effect.. used to correct tremolo volume */ + + int mVolume; /* current mod volume for this channel */ + int mPan; /* current mod pan for this channel */ + int mVolumeDelta; /* delta for volume commands.. tremolo/tremor etc */ + + unsigned int mSampleOffset; /* sample offset for this channel in SAMPLES */ + + int mGlobalVolume; /* global volume for this channel */ + float mMasterVolume; /* Master volume for this channel */ + unsigned char mPortaUpDown; /* last porta up or down value (S3M / IT) */ + unsigned char mPortaDown; /* last porta down value (XM) */ + unsigned char mPortaUp; /* last porta up value (XM) */ + unsigned char mXtraPortaDown; /* last porta down value (XM) */ + unsigned char mXtraPortaUp; /* last porta up value (XM) */ + unsigned char mVolumeSlide; /* last volume slide value (XM + S3M) */ + unsigned char mPanSlide; /* pan slide parameter (XM + IT MODPLUG) */ + unsigned char mRetrigX; /* last retrig volume slide used (XM + S3M) */ + unsigned char mRetrigY; /* last retrig tick count used (XM + S3M) */ + unsigned char mRetrigCount; /* retrig timer (IT) */ + + int mPortaTarget; /* note to porta to */ + unsigned char mPortaSpeed; /* porta speed */ + unsigned char mPortaReached; /* flag for IT to say portamento has been reached */ + + signed char mVibPos; /* vibrato position */ + unsigned char mVibSpeed; /* vibrato speed */ + unsigned char mVibDepth; /* vibrato depth */ + unsigned char mVibType; /* vibrato type (IT) */ + + signed char mTremoloPosition; /* tremolo position */ + unsigned char mTremoloSpeed; /* tremolo speed */ + unsigned char mTremoloDepth; /* tremolo depth */ + + int mPanbrelloPos; /* panbrello position */ + unsigned char mPanbrelloSpeed; /* panbrello speed */ + unsigned char mPanbrelloDepth; /* panbrello depth */ + + unsigned char mTremorPosition; /* tremor position (XM + S3M) */ + unsigned char mTremorOn; /* remembered parameters for tremor (XM + S3M) */ + unsigned char mTremorOff; /* remembered parameters for tremor (XM + S3M) */ + unsigned char mArpeggio; /* remembered parameters for arpeggio (S3M) */ + int mPatternLoopRow; + int mPatternLoopNumber; /* pattern loop variables for effect E6x */ + unsigned char mChannelVolumeSlide; /* global volume slide parameters */ + + unsigned char mSpecialParam; /* remembered parameter for Sxy */ + unsigned char mWaveControl; /* waveform type for vibrato and tremolo (4bits each) */ + unsigned char mWaveControlVibrato; /* waveform type for vibrato (IT) */ + unsigned char mWaveControlTremolo; /* waveform type for tremolo (IT) */ + unsigned char mWaveControlPan; /* waveform type for panbrello (IT) */ + + unsigned char mFineVolumeSlideDown; /* parameter for fine volume slide down */ + unsigned char mFineVolumeSlideUp; /* parameter for fine volume slide up */ + unsigned char mFinePortaUp; /* parameter for fine porta slide up */ + unsigned char mFinePortaDown; /* parameter for fine porta slide down */ + unsigned char mHighOffset; /* high part of sample offset - ie the 'y' part of yxx00 (IT) */ + unsigned char mVolumeColumnVolumeSlide; /* volume column parameter remembered for volume sliding (IT) */ + }; + + class ChannelMusic : public ChannelReal + { + public: + MusicSong *mMusic; + + FMOD_RESULT updateStream(); + FMOD_RESULT stop(); + FMOD_RESULT start(); + FMOD_RESULT setPaused(bool paused); + FMOD_RESULT setVolume(float volume); + }; + + + /* + Single note type - contains info on 1 note in a pattern + */ + class MusicNote + { + public: + unsigned char mNote; /* note to play at (0-133) 132=none,133=keyoff */ + unsigned char mNumber; /* sample being played (0-99) */ + unsigned char mVolume; /* volume column value (0-64) 255=no volume */ + unsigned char mEffect; /* effect number (0-1Ah) */ + unsigned char mEffectParam; /* effect parameter (0-255) */ + }; + + /* + Pattern data type + */ + class MusicPattern + { + public: + int mRows; + MusicNote *mData; + }; + + const int MUSIC_MAXORDERS = 256; + const int MUSIC_MAXCHANNELS = 64; + + /* + Song type - contains info on song + */ + class MusicSong : public Codec + { + private: + FMOD_RESULT getLengthInternal(unsigned int *length, FMOD_TIMEUNIT lengthtype); + FMOD_RESULT getPositionInternal(unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT getMusicNumChannelsInternal(int *numchannels); + FMOD_RESULT setMusicChannelVolumeInternal(int channel, float volume); + FMOD_RESULT getMusicChannelVolumeInternal(int channel, float *volume); + + public: + char mSongName[FMOD_STRING_MAXNAMELEN]; + MusicPattern *mPattern; /* patterns array for this song */ + DSPI *mDSPHead; /* DSP unit channels usually mix to by default */ + bool *mVisited; /* 256 = maximum rows per pattern */ + unsigned char mOrderList[MUSIC_MAXORDERS]; /* pattern playing order list */ + + /* + Channel stuff. + */ + int mNumChannels; /* number of channels */ + MusicChannel *mMusicChannel[MUSIC_MAXCHANNELS]; /* Array of music channels. One for each track in a mod */ + int mNumVirtualChannels; + MusicVirtualChannel *mVirtualChannel; /* Array of virtual music channels. One for each track in a mod/s3m/xm, or possibly multiple for each track in a .IT file due to NNA */ + ChannelPool *mChannelPool; /* ChannelPool */ + ChannelSoftware *mChannelSoftware; /* Array of FMOD Ex low level real channels. Each one of these lives in a ChannelI channel. */ + DSPI **mLowPass; /* Array of lowpass filters to be used on .IT files */ + ChannelGroupI mChannelGroup; + ChannelMusic mHardwareMusicChannel; + + /* + Mixing stuff + */ + int mMixerSamplesLeft; + int mMixerSamplesPerTick; + unsigned int mPCMOffset; + unsigned int mDSPTick; + + /* + Defaults + */ + int mDefaultSpeed; + unsigned int mDefaultBPM; + unsigned char mDefaultPan[64]; + unsigned char mDefaultVolume[64]; + unsigned char mDefaultGlobalVolume; + + int mNumOrders; /* number of orders (song length) */ + int mNumPatterns; /* number of physical patterns */ + int mNumPatternsMem; /* number of allocated patterns */ + int mNumInstruments; /* number of instruments */ + int mNumSamples; /* number of samples */ + + MusicInstrument *mInstrument; + signed char *mPatternPtr; /* current offset into compressed pattern data */ + unsigned char mLastNote[64]; + unsigned char mLastNumber[64]; + unsigned char mLastVolume[64]; + unsigned char mLastEffect[64]; + unsigned char mLastEffectParam[64]; + unsigned char mPreviousMaskVariable[64]; + MusicNote mNote[64]; + + int mRestart; + float mMasterSpeed; + float mPanSeparation; /* master pan seperation */ + int mMasterVolume; + int mGlobalVolume; /* global mod volume */ + unsigned char mGlobalVolumeSlide; /* global mod volume */ + unsigned short mMusicFlags; /* flags such as linear frequency, format specific quirks etc */ + bool mPlaying; /* song is playing flag */ + bool mFinished; /* song has reached the end */ + bool mLooping; /* flag to say whether song should loop or not */ + int mTick; /* current mod tick */ + int mSpeed; /* speed of song in ticks per row */ + int mBPM; /* speed of song in bpm */ + int mRow; /* current row in pattern */ + int mOrder; /* current song order position */ + int mPatternDelay; /* pattern delay counter */ + int mPatternDelayTicks; /* pattern delay ticks counter */ + int mNextRow; /* current row in pattern */ + int mNextOrder; /* current song order position */ + + inline int period2HZ(int _per) { return (14317056 / (_per)); } + + FMOD_RESULT cleanUpVirtualChannel(MusicVirtualChannel *vcptr, MusicVirtualChannel **newvcptr); + FMOD_RESULT spawnNewVirtualChannel(MusicChannel *cptr, MusicSample *sptr, MusicVirtualChannel **newvcptr); + + FMOD_RESULT setBPM(int bpm); + FMOD_RESULT play(bool fromopen = false); + FMOD_RESULT stop(); + FMOD_RESULT playSound(MusicSample *sample, MusicVirtualChannel *vcptr, bool addfilter, SNDMIXPLUGIN *plugin = 0); + FMOD_RESULT fineTune2Hz(unsigned char ft, unsigned int *hz); + FMOD_RESULT getHardwareMusicChannel(ChannelReal **realchannel); + + static FMOD_RESULT F_CALLBACK getLengthCallback(FMOD_CODEC_STATE *codec, unsigned int *length, FMOD_TIMEUNIT lengthtype); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK getPositionCallback(FMOD_CODEC_STATE *codec, unsigned int *position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK getMusicNumChannelsCallback(FMOD_CODEC_STATE *codec, int *numchannels); + static FMOD_RESULT F_CALLBACK setMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float volume); + static FMOD_RESULT F_CALLBACK getMusicChannelVolumeCallback(FMOD_CODEC_STATE *codec, int channel, float *volume); + static FMOD_RESULT F_CALLBACK getHardwareMusicChannelCallback(FMOD_CODEC_STATE *codec, ChannelReal **realchannel); + }; + + extern unsigned char gSineTable[]; + extern signed char gFineSineTable[]; + extern unsigned int gPeriodTable[]; + extern int gITLogPeriodTable[]; +#if defined(FMOD_NO_FPU) && defined(FMOD_SUPPORT_IT) + extern signed short gITFineTuneTable[]; +#endif + extern MusicSample gDummySample; + extern MusicVirtualChannel gDummyVirtualChannel; + extern MusicInstrument gDummyInstrument; +} + +#endif + +#endif + diff --git a/src/fmod_net.cpp b/src/fmod_net.cpp new file mode 100755 index 0000000..3e9f661 --- /dev/null +++ b/src/fmod_net.cpp @@ -0,0 +1,358 @@ +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_net.h" +#include "fmod_string.h" +#include "fmod_memory.h" + +#include <stdlib.h> + + +enum +{ + FMOD_NET_VERSION_HTTP_1_0 = 0, + FMOD_NET_VERSION_HTTP_1_1, + FMOD_NET_VERSION_ICY, + CT_FMOD_NET_VERSION +}; + +static const char *FMOD_Net_VersionString[CT_FMOD_NET_VERSION] = +{ + "HTTP/1.0", + "HTTP/1.1", + "ICY", +}; + +static const char base64_code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char *FMOD_Net_ProxyString = 0; +char *FMOD_Net_ProxyHostname = 0; +int FMOD_Net_ProxyPort = 0; +char *FMOD_Net_ProxyAuth = 0; // Base64 encoded, user:pass pair +int FMOD_Net_Timeout = 5000; + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_Net_ParseHTTPStatus(char *buf, int buflen, int *version, int *statuscode) +{ + int i, j, k; + + for (i=0;(i < buflen) && (buf[i] != 0) && (buf[i] != ' ');i++); + + if (i < buflen) + { + buf[i] = 0; + + for (j=0;j < CT_FMOD_NET_VERSION;j++) + { + if (!FMOD_strcmp(buf, FMOD_Net_VersionString[j])) + { + *version = j; + break; + } + } + + if (j < CT_FMOD_NET_VERSION) + { + i++; + k = i; + for (;(i < buflen) && (buf[i] != 0) && (buf[i] != ' ');i++); + + if (i < buflen) + { + buf[i] = 0; + *statuscode = atoi(&buf[k]); + return FMOD_OK; + } + } + } + + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#define CHECK_LENGTH if (out_cnt == outbuflen) { return FMOD_ERR_INVALID_PARAM; } + +FMOD_RESULT FMOD_Net_EncodeBase64(char *inbuf, char *outbuf, int outbuflen) +{ + int bits = 0, char_count = 0, out_cnt = 0, c; + + if (!inbuf || !outbuf) + { + return FMOD_ERR_INVALID_PARAM; + } + + while ((c = (unsigned char)*inbuf++)) + { + bits += c; + char_count++; + if (char_count == 3) + { + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[bits >> 18]; + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[(bits >> 12) & 0x3f]; + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[(bits >> 6) & 0x3f]; + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[bits & 0x3f]; + bits = 0; + char_count = 0; + } + else + { + bits <<= 8; + } + } + + if (char_count != 0) + { + bits <<= 16 - (8 * char_count); + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[bits >> 18]; + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[(bits >> 12) & 0x3f]; + if (char_count == 1) + { + CHECK_LENGTH + outbuf[out_cnt++] = '='; + CHECK_LENGTH + outbuf[out_cnt++] = '='; + } + else + { + CHECK_LENGTH + outbuf[out_cnt++] = base64_code[(bits >> 6) & 0x3f]; + CHECK_LENGTH + outbuf[out_cnt++] = '='; + } + } + + CHECK_LENGTH + outbuf[out_cnt] = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_Net_SetProxy(const char *proxy) +{ + char *hostname, *username, *tmp, *p; + FMOD_RESULT result; + int port; + + if (FMOD_Net_ProxyString) + { + FMOD_Memory_Free(FMOD_Net_ProxyString); + FMOD_Net_ProxyString = 0; + } + + if (FMOD_Net_ProxyHostname) + { + FMOD_Memory_Free(FMOD_Net_ProxyHostname); + FMOD_Net_ProxyHostname = 0; + } + + if (FMOD_Net_ProxyAuth) + { + FMOD_Memory_Free(FMOD_Net_ProxyAuth); + FMOD_Net_ProxyAuth = 0; + } + + FMOD_Net_ProxyPort = 0; + + if (FMOD_strlen(proxy)) + { + p = FMOD_strdup(proxy); + if (!p) + { + return FMOD_ERR_MEMORY; + } + } + else + { + /* + Setting proxy server to null here + */ + return FMOD_OK; + } + + FMOD_Net_ProxyString = FMOD_strdup(proxy); + if (!FMOD_Net_ProxyString) + { + return FMOD_ERR_MEMORY; + } + + hostname = FMOD_strstr(p, "@"); + if (hostname) + { + *hostname = 0; + hostname++; + username = p; + } + else + { + hostname = p; + username = 0; + } + + if (username) + { + char buf[4096]; + + result = FMOD_Net_EncodeBase64(username, buf, 4095); + if (result != FMOD_OK) + { + FMOD_Memory_Free(p); + return result; + } + + FMOD_Net_ProxyAuth = FMOD_strdup(buf); + if (!FMOD_Net_ProxyAuth) + { + return FMOD_ERR_MEMORY; + } + } + + tmp = FMOD_strstr(hostname, ":"); + if (tmp) + { + *tmp = 0; + tmp++; + port = atoi(tmp); + } + else + { + port = 80; + } + + FMOD_Net_ProxyHostname = FMOD_strdup(hostname); + if (!FMOD_Net_ProxyHostname) + { + return FMOD_ERR_MEMORY; + } + + FMOD_Net_ProxyPort = port; + + FMOD_Memory_Free(p); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_Net_GetProxy(char *proxy, int proxylen) +{ + if (proxy && proxylen) + { + if (FMOD_Net_ProxyString) + { + FMOD_strncpy(proxy, FMOD_Net_ProxyString, proxylen); + proxy[proxylen - 1] = 0; + } + else + { + proxy[0] = 0; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_Net_SetTimeout(int timeout) +{ + FMOD_Net_Timeout = timeout; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_Net_GetTimeout(int *timeout) +{ + if (!timeout) + { + return FMOD_ERR_INVALID_PARAM; + } + + *timeout = FMOD_Net_Timeout; + + return FMOD_OK; +} diff --git a/src/fmod_net.h b/src/fmod_net.h new file mode 100755 index 0000000..6229dd8 --- /dev/null +++ b/src/fmod_net.h @@ -0,0 +1,29 @@ +#ifndef _FMOD_NET_H +#define _FMOD_NET_H + + +#include "fmod.h" + +enum +{ + FMOD_PROTOCOL_HTTP = 1, + FMOD_PROTOCOL_SHOUTCAST, + FMOD_PROTOCOL_ICECAST +}; + + +FMOD_RESULT FMOD_Net_ParseHTTPStatus(char *buf, int buflen, int *version, int *statuscode); +FMOD_RESULT FMOD_Net_EncodeBase64(char *inbuf, char *outbuf, int outbuflen); +FMOD_RESULT FMOD_Net_SetProxy(const char *proxy); +FMOD_RESULT FMOD_Net_GetProxy(char *proxy, int proxylen); +FMOD_RESULT FMOD_Net_SetTimeout(int timeout); +FMOD_RESULT FMOD_Net_GetTimeout(int *timeout); + +extern char *FMOD_Net_ProxyHostname; +extern int FMOD_Net_ProxyPort; +extern char *FMOD_Net_ProxyAuth; +extern int FMOD_Net_Timeout; + +#endif + + diff --git a/src/fmod_octree.cpp b/src/fmod_octree.cpp new file mode 100755 index 0000000..e1dae6a --- /dev/null +++ b/src/fmod_octree.cpp @@ -0,0 +1,1660 @@ +#include "fmod_octree.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +// Tree algorithim general description: +// +// The aim of this tree is for a more flexible and +// memory predictable octree. This is done by attemting +// to represent and octree where not all levels in the +// tree are explicitly stored. Buy only storing the +// necessary splits, we can garantee that the number +// of internal nodes will not be greater then the +// number of leaf nodes. +// +// The space is seperated by a series of seperating planes. +// Space is spit exactly in half along the z-y plane, then each +// subspace is split in half along the x-z plane, then each +// new subspace is exactlysplit in half along the x-y plane. +// Then this is repeated upto 31 times fitting into 32 bit +// precission. (Similar to a classical oct tree) +// +// Items have a bounding box with gives them a maximum extent. +// This limits the depth that they can be placed in to the +// tree. +// +// Splitting planes are only actually created and stored in +// memory if there are actually to groups of items that are +// seperated by the splitting plane. +// +// Each internal node is given an AABB that encloses its +// children so that the tree can be easily traversed. +// +// +// Problems: +// If a lot of leaf nodes are in the same position they will +// end up in a list at the one node being very inefficient. + + +namespace FMOD +{ + +#if defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING) +#define ASSERT(x) do { if (!(x)) __asm { int 3 } } while (false) +#else +#define ASSERT(x) +#endif + +#if !(defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING)) +#define checkTree(x) {} +#define checkNodeIsContained(x) {} +#endif + +// used to have a splitValue variable. This macro was a safe way to replace it. +#define SPLIT_VALUE(x) (x)->pos[(x)->flags & OCTREE_FLAG_SPLIT_MASK] +//#define SPLIT_VALUE(x) (x)->splitValue + + +//template <class Type> static inline SWAP(Type& x, Type& y) { Type tmp = x; x = y; y = tmp; } + +template <class Type> +class Swap +{ + Type tmp; + + Swap(Type& x, Type& y) + { + tmp = x; + x = y; + y = tmp; + } +}; + +#define SWAP(x, y) { Swap swap(x, y); } + + +static inline float MAX(float x, float y) { return x > y ? x : y; } +static inline float MIN(float x, float y) { return x < y ? x : y; } + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +static unsigned int HighestBit(unsigned int value) +{ + // clear all but the highest bit. + // will a be nice acembler instructions to help with this on some machines. + unsigned int v = value >> 1; + // propergate the highest bit down so all bits are filled + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return value & ~v; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void aabbAdd(FMOD_AABB& a, FMOD_AABB& b, FMOD_AABB& dst) +{ + dst.xMin = MIN(a.xMin, b.xMin); + dst.xMax = MAX(a.xMax, b.xMax); + + dst.yMin = MIN(a.yMin, b.yMin); + dst.yMax = MAX(a.yMax, b.yMax); + + dst.zMin = MIN(a.zMin, b.zMin); + dst.zMax = MAX(a.zMax, b.zMax); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +Octree::Octree(float worldSize) +{ + ASSERT(worldSize != 0); + + mScale = 1.0f / worldSize; + mCenter.x = 0.0f; + mCenter.y = 0.0f; + mCenter.z = 0.0f; + + mRoot = 0; + mFreeList = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +Octree::~Octree() +{ +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void Octree::insertItem(OctreeNode* item) +{ +// checkTree(mRoot); + // Insert a new item into the octree + if (item->flags & OCTREE_FLAG_INSERTED) + return; + ASSERT(!item->parent); + ASSERT(!item->nextItem); + ASSERT(!item->hi); + ASSERT(!item->lo); + + item->flags |= OCTREE_FLAG_LEAF | OCTREE_FLAG_INSERTED; + FMOD_VECTOR extent; + extent.x = item->aabb.xMax - item->aabb.xMin; + extent.y = item->aabb.yMax - item->aabb.yMin; + extent.z = item->aabb.zMax - item->aabb.zMin; + float fMaxExtent; + fMaxExtent = MAX(extent.x, extent.y); + fMaxExtent = MAX(fMaxExtent, extent.z); + fMaxExtent *= mScale * (float)(1 << 30); + unsigned int maxExtent = ftoint(fMaxExtent); + maxExtent = HighestBit(maxExtent); + item->splitLevel = maxExtent; + item->pos[0] = xGetCenter(item); + item->pos[1] = yGetCenter(item); + item->pos[2] = zGetCenter(item); + if (!mRoot) + { + mRoot = item; + checkTree(mRoot); + return; + } + + insertInternal(mRoot, item); +// checkTree(mRoot); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void Octree::deleteItem(OctreeNode* item) +{ + checkTree(mRoot); + if ((item->flags & OCTREE_FLAG_INSERTED) == 0) + return; + + ASSERT(item->flags & OCTREE_FLAG_LEAF); + if (!item->parent) + { + // Item is the root + ASSERT(mRoot == item); + mRoot = item->nextItem; + if (mRoot) + { + mRoot->parent = 0; + mRoot->flags &= ~OCTREE_FLAG_EXTRA_NODE; + } + item->nextItem = 0; + item->flags &= ~(OCTREE_FLAG_LEAF | OCTREE_FLAG_EXTRA_NODE | OCTREE_FLAG_INSERTED | OCTREE_FLAG_SPLIT_MASK | OCTREE_FLAG_CALCULATED_AABB); + } + else + { + ASSERT(item); + ASSERT(item->parent); + OctreeNode* other = 0; + if (item->parent->nextItem == item) + { + item->parent->nextItem = item->nextItem; + if (item->nextItem) + { + item->nextItem->parent = item->parent; + } + } + else + if (item->parent->hi == item) + { + ASSERT((item->parent->flags & OCTREE_FLAG_LEAF) == 0); + item->parent->hi = item->nextItem; + if (item->nextItem) + { + item->nextItem->flags &= ~OCTREE_FLAG_EXTRA_NODE; + item->nextItem->parent = item->parent; + } + else + { + ASSERT(item->parent->hi == 0); + other = item->parent->lo; + ASSERT(other); + } + } + else + { + ASSERT((item->parent->flags & OCTREE_FLAG_LEAF) == 0); + ASSERT(item->parent->lo == item); + item->parent->lo = item->nextItem; + if (item->nextItem) + { + item->nextItem->flags &= ~OCTREE_FLAG_EXTRA_NODE; + item->nextItem->parent = item->parent; + } + else + { + ASSERT(item->parent->lo == 0); + other = item->parent->hi; + ASSERT(other); + } + } + if (other) + { + // the items parent only has one child + // the parent needs to be deleted + ASSERT(!item->nextItem); + ASSERT(item->parent); + OctreeNode* parent = item->parent; + OctreeNode* nextItem = parent->nextItem; + OctreeNode* reinsertAt; + ASSERT((parent->flags & OCTREE_FLAG_LEAF) == 0); + if (parent->parent == 0) + { + mRoot = other; + other->parent = 0; + reinsertAt = mRoot; + } + else + { + ASSERT((parent->parent->flags & OCTREE_FLAG_LEAF) == 0); + ASSERT(parent->parent->hi == parent || parent->parent->lo == parent); + if (parent->parent->hi == parent) + parent->parent->hi = other; + else + parent->parent->lo = other; + other->parent = parent->parent; + reinsertAt = parent->parent; + } + // items stored at the deleted parent need to be delt with. + // we will reinsert them + // actually, we might be able to just add then to the grand parent + // which would be faster but we will do this to make sure + while (nextItem) + { + OctreeNode* next = nextItem->nextItem; + nextItem->nextItem = 0; + nextItem->parent = 0; + nextItem->flags &= ~OCTREE_FLAG_EXTRA_NODE; + insertInternal(reinsertAt, nextItem); + nextItem = next; + } + if (parent->parent) + { + ASSERT(parent->parent->lo->parent == parent->parent); + ASSERT(parent->parent->hi->parent == parent->parent); + adjustAABBs(parent->parent); + } + + parent->parent = 0; + parent->hi = 0; + parent->lo = 0; + parent->nextItem = 0; + parent->flags &= ~(OCTREE_FLAG_SPLIT_MASK | OCTREE_FLAG_CALCULATED_AABB); + addToFreeList(parent); + checkTree(mRoot); + } + else + { + if ((item->parent->flags & OCTREE_FLAG_LEAF) == 0) + { + ASSERT(item->parent->lo->parent == item->parent); + ASSERT(item->parent->hi->parent == item->parent); + adjustAABBs(item->parent); + } + } + item->parent = 0; + item->nextItem = 0; + item->flags &= ~(OCTREE_FLAG_LEAF | OCTREE_FLAG_EXTRA_NODE | OCTREE_FLAG_INSERTED | OCTREE_FLAG_SPLIT_MASK | OCTREE_FLAG_CALCULATED_AABB); + } + checkTree(mRoot); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, XBox360, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void Octree::updateItem(OctreeNode* item) +{ + // this function must be called if an items aabb has been changed. + // if the aabb has changed enough, the item must be deleted and + // inserted, otherwise the aabbs are just ajusted up the tree. + if (item->flags & OCTREE_FLAG_INSERTED) + { + // check if the node acctually needs to change place in the tree. + FMOD_VECTOR extent; + extent.x = item->aabb.xMax - item->aabb.xMin; + extent.y = item->aabb.yMax - item->aabb.yMin; + extent.z = item->aabb.zMax - item->aabb.zMin; + float fMaxExtent; + fMaxExtent = MAX(extent.x, extent.y); + fMaxExtent = MAX(fMaxExtent, extent.z); + fMaxExtent *= mScale * (float)(1 << 30); + unsigned int maxExtent = ftoint(fMaxExtent); + maxExtent = HighestBit(maxExtent); + unsigned int pos[3]; + pos[0] = xGetCenter(item); + pos[1] = yGetCenter(item); + pos[2] = zGetCenter(item); + unsigned int mask = ~(item->splitLevel + 0xffffffff); + // might give better results to check against the parent + // here but we will try to keep things simple + if (maxExtent == item->splitLevel && + (pos[0] & mask) == (item->pos[0] & mask) && + (pos[1] & mask) == (item->pos[1] & mask) && + (pos[2] & mask) == (item->pos[2] & mask)) + { + // The item hasn't changed enough to need to be reinserted. + // We still might need to addjust the aabbs + adjustAABBs(item); + return; + } + // we need to delete the item and reinsert + deleteItem(item); + } + insertItem(item); +} + +void Octree::getAABB(FMOD_AABB* aabb) +{ + if (mRoot) + { + *aabb = mRoot->aabb; + // this will be very slow in the pathological case. + for (OctreeNode* next = mRoot->nextItem; next; next = next->nextItem) + aabbAdd(next->aabb, *aabb, *aabb); + } + else + { + aabb->xMin = 0.0f; + aabb->xMax = 0.0f; + aabb->yMin = 0.0f; + aabb->yMax = 0.0f; + aabb->zMin = 0.0f; + aabb->zMax = 0.0f; + } +} + +void Octree::addInternalNode(OctreeNode* item) +{ + ASSERT(item); + if (item->flags & OCTREE_FLAG_INTERNAL_NODE) + return; + + item->flags |= OCTREE_FLAG_INTERNAL_NODE; + addToFreeList(item); +} + +void Octree::removeInternalNode(OctreeNode* item) +{ + checkTree(mRoot); + ASSERT(item); + if ((item->flags & OCTREE_FLAG_INTERNAL_NODE) == 0) + return; + item->flags &= ~OCTREE_FLAG_INTERNAL_NODE; + ASSERT((item->flags & OCTREE_FLAG_LEAF) == 0); + // we need to free the memory used by this node + if (item->flags & OCTREE_FLAG_FREE) + { + // just remove from the free list + if (item->parent) + { + item->parent->nextItem = item->nextItem; + if (item->nextItem) + { + item->nextItem->parent = item->parent; + } + } + else + { + ASSERT(mFreeList == item); + mFreeList = item->nextItem; + if (mFreeList) + mFreeList->parent = 0; + } + checkTree(mRoot); + } + else + { + // replace with a new node + OctreeNode* replacement = getFreeNode(); + + *replacement = *item; + replacement->flags &= ~OCTREE_FLAG_INTERNAL_NODE; + replacement->flags |= OCTREE_FLAG_INTERNAL_NODE; + if (replacement->parent) + { + if (replacement->parent->nextItem == item) + { + replacement->parent->nextItem = replacement; + } + else + if (replacement->parent->hi == item) + { + replacement->parent->hi = replacement; + } + else + { + ASSERT(replacement->parent->lo == item); + replacement->parent->lo = replacement; + } + } + else + { + mRoot = replacement; + } + if (replacement->nextItem) + replacement->nextItem->parent = replacement; + if (replacement->hi) + replacement->hi->parent = replacement; + if (replacement->lo) + replacement->lo->parent = replacement; + checkTree(mRoot); + } +} + +#if defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING) +void Octree::checkTree(OctreeNode* node) +{ + if (!node) + return; + ASSERT((node->flags & OCTREE_FLAG_FREE) == 0); + if (node->parent) + { + if ((node->flags & OCTREE_FLAG_LEAF) == 0) + { + ASSERT(node->parent->splitLevel >= node->splitLevel); + + if (node->parent->splitLevel == node->splitLevel) + { + ASSERT((node->parent->flags & OCTREE_FLAG_SPLIT_MASK) < (node->flags & OCTREE_FLAG_SPLIT_MASK)); + } + } + if ((node->parent->flags & OCTREE_FLAG_LEAF) == 0) + { + ASSERT((node->flags & OCTREE_FLAG_EXTRA_NODE) == 0); + } + if ((node->flags & OCTREE_FLAG_EXTRA_NODE) == 0) + { + ASSERT((node->parent->flags & OCTREE_FLAG_LEAF) == 0); + } + ASSERT( + node->parent->lo == node || + node->parent->hi == node || + node->parent->nextItem == node); + ASSERT((node->parent->flags & OCTREE_FLAG_FREE) == 0); + } + if (node->flags & OCTREE_FLAG_LEAF) + { + ASSERT(node->lo == 0); + ASSERT(node->hi == 0); + checkNodeIsContained(node); + for (OctreeNode* next = node->nextItem; next; next = next->nextItem) + { + ASSERT(next->flags & OCTREE_FLAG_EXTRA_NODE); + ASSERT(next->flags & OCTREE_FLAG_LEAF); + ASSERT(next->splitLevel >= next->parent->splitLevel); + if (node->parent) + { + ASSERT( + node->parent->lo == node || + node->parent->hi == node || + node->parent->nextItem == node); + } + checkNodeIsContained(next); + } + } + else + { + ASSERT(node->lo); + ASSERT(node->lo->parent == node); + checkTree(node->lo); + + ASSERT(node->hi); + ASSERT(node->lo->parent == node); + checkTree(node->hi); + checkNodeIsContained(node); + } + +} + +void Octree::checkNodeIsContained(OctreeNode* node) +{ + //return; // skip this because it is often too slow even for debug + + if (!node) + return; + + unsigned int xCenter = node->pos[0]; + unsigned int yCenter = node->pos[1]; + unsigned int zCenter = node->pos[2]; + + OctreeNode* bottom = node; + OctreeNode* first = 0; + + while (node->parent) + { + if ((node->parent->flags & OCTREE_FLAG_LEAF) == 0 && + node->parent->nextItem != node) + { + if (first == 0) + first = node->parent; + ASSERT(node->parent->lo == node || node->parent->hi == node); + + switch (node->parent->flags & OCTREE_FLAG_SPLIT_MASK) + { + case OCTREE_FLAG_X_SPLIT: + { + if (node->parent->hi == node) + { + ASSERT(xCenter >= SPLIT_VALUE(node->parent)); + } + else + { + ASSERT(xCenter < SPLIT_VALUE(node->parent)); + } + if (bottom->splitLevel <= node->parent->splitLevel) + { + ASSERT(((bottom->pos[0] ^ node->parent->pos[0]) & ~((node->parent->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((bottom->pos[1] ^ node->parent->pos[1]) & ~((node->parent->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((bottom->pos[2] ^ node->parent->pos[2]) & ~((node->parent->splitLevel << 1) + 0xffffffff)) == 0); + } + else + { + ASSERT(first == node->parent); + } + } + break; + case OCTREE_FLAG_Y_SPLIT: + { + if (node->parent->hi == node) + { + ASSERT(yCenter >= SPLIT_VALUE(node->parent)); + } + else + { + ASSERT(yCenter < SPLIT_VALUE(node->parent)); + } + if (bottom->splitLevel <= node->parent->splitLevel) + { + ASSERT(((bottom->pos[0] ^ node->parent->pos[0]) & ~((node->parent->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((bottom->pos[1] ^ node->parent->pos[1]) & ~((node->parent->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((bottom->pos[2] ^ node->parent->pos[2]) & ~((node->parent->splitLevel << 1) + 0xffffffff)) == 0); + } + else + { + ASSERT(first == node->parent); + } + } + break; + + case OCTREE_FLAG_Z_SPLIT: + { + if (node->parent->hi == node) + { + ASSERT(zCenter >= SPLIT_VALUE(node->parent)); + } + else + { + ASSERT(zCenter < SPLIT_VALUE(node->parent)); + } + if (bottom->splitLevel <= node->parent->splitLevel) + { + ASSERT(((bottom->pos[0] ^ node->parent->pos[0]) & ~((node->parent->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((bottom->pos[1] ^ node->parent->pos[1]) & ~((node->parent->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((bottom->pos[2] ^ node->parent->pos[2]) & ~((node->parent->splitLevel << 1) + 0xffffffff)) == 0); + } + else + { + ASSERT(first == node->parent); + } + } + break; + } + + + + } + node = node->parent; + } +} + +static int nonEmptyNodeCount = 0; +static int totalNodeSize = 0; +static int maxNodeSize = 0; +void Octree::calculateAverageNodeElements() +{ + nonEmptyNodeCount = 0; + totalNodeSize = 0; + maxNodeSize = 0; + calculateAverageNodeElements(mRoot); + + int total = totalNodeSize; + int count = nonEmptyNodeCount; + int max = maxNodeSize; + + int test = 0; +} + +void Octree::calculateAverageNodeElements(OctreeNode* node) +{ + OctreeNode* next; + if (node->flags & OCTREE_FLAG_LEAF) + next = node; + else + next = node->nextItem; + + if (next) + { + nonEmptyNodeCount++; + int count = 0; + for (; next; next = next->nextItem) + { + totalNodeSize++; + count++; + } + if (maxNodeSize < count) + maxNodeSize = count; + } + + if (node->hi) + calculateAverageNodeElements(node->hi); + if (node->lo) + calculateAverageNodeElements(node->lo); + +} +#endif // _DEBUG + +void Octree::setMaxSize(float maxSize) +{ + ASSERT(maxSize != 0); + mScale = 1.0f / maxSize; +} + +bool Octree::testLine(bool (*octreeLineTestCallback)(OctreeNode* item, void* data), void* data, const FMOD_VECTOR& a, const FMOD_VECTOR& b) +{ + ASSERT(octreeLineTestCallback); + if (mRoot) + { + RecursionData recursionData; + recursionData.octreeLineTestCallback = octreeLineTestCallback; + recursionData.data = data; + recursionData.exit = false; + testLine(mRoot, a, b, &recursionData); + return (!recursionData.exit); + } + return true; +} + +#if defined(FMOD_GEOMETRY_DEBUGGING) +void Octree::renderTree(void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)) +{ + renderTreeInternal(mRoot, renderBox); +} + +void Octree::renderTreeInternal(OctreeNode* node, void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)) +{ + if (!node) + return; + renderBox(node->aabb.xMin, node->aabb.xMax, node->aabb.yMin, node->aabb.yMax, node->aabb.zMin, node->aabb.zMax); + + for (OctreeNode* next = node->nextItem; next; next = next->nextItem) + { + renderBox(next->aabb.xMin, next->aabb.xMax, next->aabb.yMin, next->aabb.yMax, next->aabb.zMin, next->aabb.zMax); + } + + renderTreeInternal(node->hi, renderBox); + renderTreeInternal(node->lo, renderBox); +} +#endif + + +#if defined(FMOD_23TREE) +void Octree::insert23Tree(OctreeNode* parent, OctreeNode** pParent, OctreeNode* item) +{ + item->flags |= OCTREE_FLAG_23LEAF; + if (*pParent == 0) + { + // add item as root + *pParent = item; + item->parent = parent; + item->flags |= OCTREE_FLAG_23ROOT; + } + else + if ((*pParent)->flags & OCTREE_FLAG_23LEAF) + { + OctreeNode* other = *pParent; + // make new root + ASSERT(other->parent == parent); + ASSERT(other->flags & OCTREE_FLAG_23ROOT); + other->flags &= ~OCTREE_FLAG_23ROOT; + OctreeNode* internalNode = getFreeNode(); + internalNode->flags |= OCTREE_FLAG_23NODE | OCTREE_FLAG_23ROOT; + + *pParent = internalNode; + internalNode->parent = parent; + + if (other->splitLevel < item->splitLevel) + { + internalNode->lo = other; + internalNode->hi = item; + } + else + { + internalNode->lo = other; + internalNode->hi = item; + } + internalNode->pos[0] = internalNode->hi->splitLevel; + internalNode->pos[2] = internalNode->lo->splitLevel; // store smallest in subtree here + ASSERT(internalNode->nextItem == 0); + internalNode->lo->parent = internalNode; + internalNode->hi->parent = internalNode; + } + else + { + // find where to do the insertion + OctreeNode* node = *pParent; + while (node) + { + ASSERT(node->lo); + ASSERT(node->hi); + if (node->lo->flags & OCTREE_FLAG_23LEAF) + { + if (node->nextItem == 0) + { + ASSERT(node->pos[0] == node->hi->splitLevel); + // we have room for a new item at this leaf + if (item->splitLevel < node->lo->splitLevel) + { + node->pos[1] = node->pos[0]; + node->nextItem = node->hi; + node->hi = node->lo; + node->pos[0] = node->hi->splitLevel; + node->lo = item; + } + else + if (item->splitLevel < node->hi->splitLevel) + { + node->nextItem = node->hi; + node->hi = item; + node->pos[0] = node->hi->splitLevel; + node->pos[1] = node->nextItem->splitLevel; + } + else + { + node->pos[1] = item->splitLevel; + node->nextItem = item; + } + item->parent = node; + ASSERT(node->pos[0] == node->hi->splitLevel); + ASSERT(node->pos[1] == node->nextItem->splitLevel); + ASSERT(node->lo->splitLevel <= node->hi->splitLevel); + ASSERT(node->hi->splitLevel <= node->nextItem->splitLevel); + ASSERT(node->pos[1] >= node->pos[0]); + break; + } + else + { + split23Tree(node, item); + break; + } + } + else + { + if (item->splitLevel < node->pos[0]) + { + node = node->lo; + } + else + if (!node->nextItem) + { + node = node->hi; + } + else + if (item->splitLevel < node->pos[1]) + { + node = node->hi; + } + else + { + node = node->nextItem; + } + } + } + } +} + +void Octree::split23Tree(OctreeNode* node, OctreeNode* item) +{ + ASSERT(node->lo); + ASSERT(node->hi); + ASSERT(node->nextItem); + ASSERT(node->lo->flags & OCTREE_FLAG_23LEAF); + ASSERT(node->hi->flags & OCTREE_FLAG_23LEAF); + ASSERT(node->nextItem->flags & OCTREE_FLAG_23LEAF); + ASSERT(item->flags & OCTREE_FLAG_23LEAF); + if (node->flags & OCTREE_FLAG_23ROOT) + { + + if (item->splitLevel < node->nextItem->splitLevel) + SWAP(item, node->nextItem); + if (node->nextItem->splitLevel < node->hi->splitLevel) + SWAP(node->nextItem, node->hi); + if (node->hi->splitLevel < node->lo->splitLevel) + SWAP(node->hi, node->lo); + + OctreeNode* internalNodeA = getFreeNode(); + internalNodeA->flags |= OCTREE_FLAG_23NODE; + internalNodeA->lo = node->lo; + internalNodeA->hi = node->hi; + internalNodeA->lo->parent = internalNodeA; + internalNodeA->hi->parent = internalNodeA; + internalNodeA->pos[0] = internalNodeA->hi->splitLevel; + + OctreeNode* internalNodeB = getFreeNode(); + internalNodeB->flags |= OCTREE_FLAG_23NODE; + internalNodeB->lo = node->nextItem; + internalNodeB->hi = item; + internalNodeB->lo->parent = internalNodeB; + internalNodeB->hi->parent = internalNodeB; + internalNodeB->pos[0] = internalNodeB->hi->splitLevel; + + node->nextItem = 0; + node->lo = internalNodeA; + node->hi = internalNodeB; + node->lo->parent = node; + node->hi->parent = node; + node->pos[0] = node->hi->lo->splitLevel; + return; + } + + if (item->splitLevel < node->nextItem->splitLevel) + SWAP(item, node->nextItem); + if (node->nextItem->splitLevel < node->hi->splitLevel) + SWAP(node->nextItem, node->hi); + if (node->hi->splitLevel < node->lo->splitLevel) + SWAP(node->hi, node->lo); + + OctreeNode* internalNode = getFreeNode(); + internalNode->flags |= OCTREE_FLAG_23NODE; + internalNode->lo = node->nextItem; + internalNode->hi = item; + internalNode->lo->parent = internalNode; + internalNode->hi->parent = internalNode; + internalNode->pos[0] = internalNode->hi->splitLevel; + unsigned int newSplit = internalNode->lo->splitLevel; + + ASSERT(node->lo->splitLevel <= node->hi->splitLevel); + ASSERT(node->hi->splitLevel <= internalNode->lo->splitLevel); + ASSERT(internalNode->lo->splitLevel <= internalNode->hi->splitLevel); + + node->nextItem = 0; + node->lo->parent = node; + node->hi->parent = node; + node->pos[0] = node->hi->splitLevel; + item = internalNode; + + for (;;) + { + ASSERT(node->flags & OCTREE_FLAG_23NODE); + if (node->flags & OCTREE_FLAG_23ROOT) + { + OctreeNode* internalNode = getFreeNode(); + *internalNode = *node; + ASSERT(internalNode->flags & OCTREE_FLAG_23NODE); + internalNode->flags &= ~OCTREE_FLAG_23ROOT; + internalNode->lo->parent = internalNode; + internalNode->hi->parent = internalNode; + node->pos[0] = newSplit; + node->lo = internalNode; + node->hi = item; + node->lo->parent = node; + node->hi->parent = node; + break; + } + else + { + // try to insert the new internal node at the parent. + ASSERT(node->parent); + ASSERT(node->parent->flags & OCTREE_FLAG_23NODE); + OctreeNode* parent = node->parent; + if (parent->lo == node) + { + if (parent->nextItem) + { + // we need to split the parent + OctreeNode* internalNode = getFreeNode(); + internalNode->flags |= OCTREE_FLAG_23NODE; + internalNode->lo = parent->hi; + internalNode->hi = parent->nextItem; + internalNode->pos[0] = parent->pos[1]; + internalNode->lo->parent = internalNode; + internalNode->hi->parent = internalNode; + + parent->hi = item; + parent->hi->parent = parent; + SWAP(parent->pos[0], newSplit); + //parent->pos[0] = newSplit; + parent->nextItem = 0; + + item = internalNode; + node = parent; + } + else + { + // add item here + parent->nextItem = parent->hi; + parent->pos[1] = parent->pos[0]; + parent->pos[0] = newSplit; + parent->hi = item; + parent->hi->parent = parent; + break; + } + } + else + if (parent->hi == node) + { + if (parent->nextItem) + { + // we need to split the parent + OctreeNode* internalNode = getFreeNode(); + internalNode->flags |= OCTREE_FLAG_23NODE; + internalNode->lo = item; + internalNode->hi = parent->nextItem; + internalNode->pos[0] = parent->pos[1]; + internalNode->lo->parent = internalNode; + internalNode->hi->parent = internalNode; + + parent->nextItem = 0; + + item = internalNode; + node = parent; + } + else + { + // add item here + parent->nextItem = item; + parent->nextItem->parent = parent; + parent->pos[1] = newSplit; + break; + } + } + else + { + ASSERT(parent->nextItem == node); + // we need to split the parent + OctreeNode* internalNode = getFreeNode(); + internalNode->flags |= OCTREE_FLAG_23NODE; + internalNode->lo = parent->nextItem; + internalNode->hi = item; + internalNode->pos[0] = newSplit; + internalNode->lo->parent = internalNode; + internalNode->hi->parent = internalNode; + newSplit = parent->pos[1]; + + parent->nextItem = 0; + + item = internalNode; + node = parent; + } + } + } +} + +void Octree::remove23Tree(OctreeNode* item) +{ +} + +static unsigned int checkTreeValue = 0; +void Octree::check23Tree(OctreeNode* parent, OctreeNode** pParent) +{ + checkTreeValue = 0; + check23TreeRecursive(*pParent); +} + +void Octree::check23TreeRecursive(OctreeNode* node) +{ + if (!node) + return; + if (node->flags & OCTREE_FLAG_23LEAF) + { + ASSERT(node->splitLevel >= checkTreeValue); + checkTreeValue = node->splitLevel; + return; + } + ASSERT(node->flags & OCTREE_FLAG_23NODE); + ASSERT(node->lo); + ASSERT(node->hi); + + ASSERT(node->lo->parent == node); + check23TreeRecursive(node->lo); + + ASSERT(node->hi->parent == node); + check23TreeRecursive(node->hi); + ASSERT(findLowest(node->hi) == node->pos[0]); + if (node->nextItem) + { + ASSERT(node->nextItem->parent == node); + check23TreeRecursive(node->nextItem); + ASSERT(findLowest(node->nextItem) == node->pos[1]); + } +} + +int unsigned Octree::findLowest(OctreeNode* node) +{ + for (; (node->flags & OCTREE_FLAG_23LEAF) == 0; node = node->lo) + ; + return node->splitLevel; +} + +/* +// testing the 2-3-tree insert +#include <memory.h> +#include <stdlib.h> +class TestClass +{ +public: + TestClass() + { + srand(0); + const int numNodes = 5000; + Octree octree; + OctreeNode octreeNodes[numNodes]; + OctreeNode octreeInternalNodes[numNodes]; + FMOD_memset(octreeNodes, 0, sizeof (octreeNodes)); + FMOD_memset(octreeInternalNodes, 0, sizeof (octreeInternalNodes)); + int node; + for (node = 0; node < numNodes; node++) + { + octree.addInternalNode(&octreeInternalNodes[node]); + } + + OctreeNode* root = 0; + for (node = 0; node < numNodes; node++) + { + octreeNodes[node].splitLevel = FMOD_RAND(); + octree.insert23Tree(0, &root, &octreeNodes[node]); + // octree.check23Tree(0, &root); + } + octree.check23Tree(0, &root); + } +}; +TestClass testClass;*/ +#endif + +void Octree::insertInternal(OctreeNode* node, OctreeNode* item) +{ + for (;;) + { + { + // find the highest split with node. + int splitAxis = 0; + unsigned int highestSplit = 0; + if (node->flags & OCTREE_FLAG_LEAF) + { + for (int i = 0; i < 3; i++) + { + unsigned int tmp = (node->pos[i] ^ item->pos[i]); + tmp = HighestBit(tmp); + if (tmp > highestSplit && + tmp > item->splitLevel && + tmp > node->splitLevel) + { + highestSplit = tmp; + splitAxis = i; + } + } + } + else + { + for (int i = 0; i < 3; i++) + { + unsigned int tmp = (node->pos[i] ^ item->pos[i]) & ~(node->splitLevel + 0xffffffff); + tmp = HighestBit(tmp); + if (highestSplit < tmp && + tmp > item->splitLevel && + (tmp > node->splitLevel || (tmp == node->splitLevel && i < (node->flags & OCTREE_FLAG_SPLIT_MASK)))) + { + highestSplit = tmp; + splitAxis = i; + } + } + } + + + if (highestSplit) + { + OctreeNode* newNode = getFreeNode(); + + checkTree(mRoot); + + newNode->flags |= (newNode->flags & ~OCTREE_FLAG_SPLIT_MASK) | splitAxis; + // newNode->splitValue = item->pos[splitAxis] & (~(0xffffffff + highestSplit)) | highestSplit; + newNode->splitLevel = highestSplit; + + + ASSERT((item->pos[splitAxis] & newNode->splitLevel) != (node->pos[splitAxis] & newNode->splitLevel)); + if (item->pos[splitAxis] & highestSplit) + { + newNode->lo = node; + newNode->hi = item; + } + else + { + newNode->hi = node; + newNode->lo = item; + } + + newNode->parent = node->parent; + newNode->hi->parent = newNode; + newNode->lo->parent = newNode; + ASSERT(newNode->splitLevel >= newNode->hi->splitLevel); + ASSERT(newNode->splitLevel >= newNode->lo->splitLevel); + if (newNode->parent) + { + ASSERT(newNode->parent->splitLevel >= newNode->splitLevel); + ASSERT(newNode->parent->hi == node || newNode->parent->lo == node); + if (newNode->parent->lo == node) + newNode->parent->lo = newNode; + else + newNode->parent->hi = newNode; + } + else + { + mRoot = newNode; + } + + if (splitAxis == 0) + { + newNode->pos[0] = item->pos[0] & (~(0xffffffff + (newNode->splitLevel >> 0))) | newNode->splitLevel; + newNode->pos[1] = item->pos[1] & (~(0xffffffff + (newNode->splitLevel >> 0))) | newNode->splitLevel; + newNode->pos[2] = item->pos[2] & (~(0xffffffff + (newNode->splitLevel >> 0))) | newNode->splitLevel; + + ASSERT(((newNode->lo->pos[0] ^ newNode->pos[0]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((newNode->lo->pos[1] ^ newNode->pos[1]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((newNode->lo->pos[2] ^ newNode->pos[2]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + + ASSERT(((newNode->hi->pos[0] ^ newNode->pos[0]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((newNode->hi->pos[1] ^ newNode->pos[1]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((newNode->hi->pos[2] ^ newNode->pos[2]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + } + else + if (splitAxis == 1) + { + newNode->pos[0] = item->pos[0] & (~(0xffffffff + (newNode->splitLevel >> 1))) | (newNode->splitLevel >> 1); + newNode->pos[1] = item->pos[1] & (~(0xffffffff + (newNode->splitLevel >> 0))) | newNode->splitLevel; + newNode->pos[2] = item->pos[2] & (~(0xffffffff + (newNode->splitLevel >> 0))) | newNode->splitLevel; + + ASSERT(((newNode->lo->pos[0] ^ newNode->pos[0]) & ~((newNode->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((newNode->lo->pos[1] ^ newNode->pos[1]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((newNode->lo->pos[2] ^ newNode->pos[2]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + + ASSERT(((newNode->hi->pos[0] ^ newNode->pos[0]) & ~((newNode->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((newNode->hi->pos[1] ^ newNode->pos[1]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + ASSERT(((newNode->hi->pos[2] ^ newNode->pos[2]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + + } + else + { + newNode->pos[0] = item->pos[0] & (~(0xffffffff + (newNode->splitLevel >> 1))) | (newNode->splitLevel >> 1); + newNode->pos[1] = item->pos[1] & (~(0xffffffff + (newNode->splitLevel >> 1))) | (newNode->splitLevel >> 1); + newNode->pos[2] = item->pos[2] & (~(0xffffffff + (newNode->splitLevel >> 0))) | newNode->splitLevel; + + ASSERT(((newNode->lo->pos[0] ^ newNode->pos[0]) & ~((newNode->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((newNode->lo->pos[1] ^ newNode->pos[1]) & ~((newNode->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((newNode->lo->pos[2] ^ newNode->pos[2]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + + ASSERT(((newNode->hi->pos[0] ^ newNode->pos[0]) & ~((newNode->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((newNode->hi->pos[1] ^ newNode->pos[1]) & ~((newNode->splitLevel << 0) + 0xffffffff)) == 0); + ASSERT(((newNode->hi->pos[2] ^ newNode->pos[2]) & ~((newNode->splitLevel << 1) + 0xffffffff)) == 0); + } + ASSERT(newNode->hi->pos[splitAxis] >= SPLIT_VALUE(newNode)); + ASSERT(newNode->lo->pos[splitAxis] < SPLIT_VALUE(newNode)); + OctreeNode* next = node->nextItem; + node->nextItem = 0; + adjustAABBs(newNode); + while (next) + { + OctreeNode* nextNext = next->nextItem; + next->parent = 0; + next->nextItem = 0; + next->flags &= ~OCTREE_FLAG_EXTRA_NODE; + if (newNode->parent) + insertInternal(newNode->parent, next); + else + insertInternal(mRoot, next); + //insertInternal(mRoot, next); + next = nextNext; + } + + + + checkNodeIsContained(newNode->hi); + if (newNode->hi->nextItem) + checkNodeIsContained(newNode->hi->nextItem); + checkNodeIsContained(newNode->lo); + if (newNode->lo->nextItem) + checkNodeIsContained(newNode->lo->nextItem); + checkTree(newNode); + checkTree(mRoot); + break; + } + else + if (item->splitLevel >= node->splitLevel) + { + // the item is too big to go past this node so just add here. + addListItem(node, item); + adjustAABBs(node); + + // error checking + checkNodeIsContained(item); + checkNodeIsContained(node); + checkTree(node); + checkTree(mRoot); + break; + } + else + if (node->flags & OCTREE_FLAG_LEAF) + { + // the item is too big to go past this node so just add here. + OctreeNode* parent = node->parent; + addListItem(node, item); + adjustAABBs(parent); + + // error checking + checkNodeIsContained(item); + checkNodeIsContained(node); + checkTree(parent); + checkTree(mRoot); + break; + } + else + { + + switch (item->pos[node->flags & OCTREE_FLAG_SPLIT_MASK]) + { + case 0: + ASSERT(((item->pos[0] ^ node->pos[0]) & ~((node->splitLevel >> 1) + 0xffffffff)) == 0); + ASSERT(((item->pos[1] ^ node->pos[1]) & ~((node->splitLevel >> 1) + 0xffffffff)) == 0); + ASSERT(((item->pos[2] ^ node->pos[2]) & ~((node->splitLevel >> 1) + 0xffffffff)) == 0); + break; + case 1: + ASSERT(((item->pos[0] ^ node->pos[0]) & ~((node->splitLevel >> 0) + 0xffffffff)) == 0); + ASSERT(((item->pos[1] ^ node->pos[1]) & ~((node->splitLevel >> 1) + 0xffffffff)) == 0); + ASSERT(((item->pos[2] ^ node->pos[2]) & ~((node->splitLevel >> 1) + 0xffffffff)) == 0); + break; + case 2: + ASSERT(((item->pos[0] ^ node->pos[0]) & ~((node->splitLevel >> 0) + 0xffffffff)) == 0); + ASSERT(((item->pos[1] ^ node->pos[1]) & ~((node->splitLevel >> 0) + 0xffffffff)) == 0); + ASSERT(((item->pos[2] ^ node->pos[2]) & ~((node->splitLevel >> 1) + 0xffffffff)) == 0); + break; + } + + // ASSERT((item->pos[0] ^ node->pos[0]) < (node->splitLevel << 0)); + // ASSERT((item->pos[1] ^ node->pos[1]) < (node->splitLevel << 0)); + // ASSERT((item->pos[2] ^ node->pos[2]) < (node->splitLevel << 0)); + node = (item->pos[node->flags & OCTREE_FLAG_SPLIT_MASK] >= SPLIT_VALUE(node)) ? node->hi : node->lo; + ASSERT((node->parent->flags & OCTREE_FLAG_LEAF) == 0); + } + } + } +} + +void Octree::addToFreeList(OctreeNode* item) +{ + item->nextItem = mFreeList; + mFreeList = item; + if (item->nextItem) + item->nextItem->parent = item; + item->parent = 0; + ASSERT((item->flags & OCTREE_FLAG_FREE) == 0); + item->flags |= OCTREE_FLAG_FREE; +} + +OctreeNode* Octree::getFreeNode() +{ + ASSERT(mFreeList); + OctreeNode* node = mFreeList; + mFreeList = mFreeList->nextItem; + if (mFreeList) + mFreeList->parent = 0; + node->nextItem = 0; + ASSERT(node->flags & OCTREE_FLAG_FREE); + node->flags &= ~OCTREE_FLAG_FREE; + return node; +} + +void Octree::adjustAABBs(OctreeNode* node) +{ + while (node) + { + if ((node->flags & OCTREE_FLAG_LEAF) == 0) + { + ASSERT(node->hi); + ASSERT(node->lo); + aabbAdd(node->hi->aabb, node->lo->aabb, node->aabb); + + // todo: Adding all the items on this node to the aabb can be slow. + // this could be made faster using a 2-3-tree for this list. + // An easier way to speed this up would be to only add the new aabb + // being added to the tree rather then recalculating everything at + // each node as we go up the tree. + for (OctreeNode* next = node->nextItem; next; next = next->nextItem) + { + aabbAdd(next->aabb, node->aabb, node->aabb); + } + node->flags |= OCTREE_FLAG_CALCULATED_AABB; + // if a child is a leaf, we have to add any attacted items to the aabb of the parent. + if (node->hi->flags & OCTREE_FLAG_LEAF) + { + for (OctreeNode* next = node->hi->nextItem; next; next = next->nextItem) + { + aabbAdd(next->aabb, node->aabb, node->aabb); + } + } + if (node->lo->flags & OCTREE_FLAG_LEAF) + { + for (OctreeNode* next = node->lo->nextItem; next; next = next->nextItem) + { + aabbAdd(next->aabb, node->aabb, node->aabb); + } + } + } + node = node->parent; + } +} + + +void Octree::removeListItem(OctreeNode* node) +{ + ASSERT(node); + ASSERT(node->parent); + if (node->parent->nextItem == node) + { + node->parent->nextItem = node->nextItem; + } + else + if (node->parent->hi == node) + { + node->parent->hi = node->nextItem; + if (node->nextItem) + node->nextItem->flags &= ~OCTREE_FLAG_EXTRA_NODE; + } + else + { + ASSERT(node->parent->lo == node); + node->parent->lo = node->nextItem; + if (node->nextItem) + node->nextItem->flags &= ~OCTREE_FLAG_EXTRA_NODE; + } + if (node->nextItem) + node->nextItem->parent = node->parent; + node->parent = 0; + node->nextItem = 0; + node->flags &= ~OCTREE_FLAG_EXTRA_NODE; +} + +void Octree::addListItem(OctreeNode* list, OctreeNode* node) +{ + // add to list, sorted by extent + ASSERT(list); + ASSERT(node->nextItem == 0); + ASSERT(node->parent == 0); + if ((list->flags & OCTREE_FLAG_LEAF) == 0) + { + // don't insert before the first item if it isn't a leaf node + if (list->nextItem) + { + list = list->nextItem; + } + else + { + // add after + ASSERT(node->nextItem == 0); + list->nextItem = node; + node->parent = list; + node->flags |= OCTREE_FLAG_EXTRA_NODE; + return; + } + } + + while (node->splitLevel > list->splitLevel && list->nextItem) + list = list->nextItem; + + if (!list->nextItem && node->splitLevel > list->splitLevel) + { + // add after + ASSERT(node->nextItem == 0); + list->nextItem = node; + node->parent = list; + node->flags |= OCTREE_FLAG_EXTRA_NODE; + } + else + { + // add before + if (!list->parent) + { + ASSERT(mRoot == list); + mRoot = node; + } + else + if (list->parent->nextItem == list) + { + list->parent->nextItem = node; + if (list->parent->flags & list->flags & OCTREE_FLAG_LEAF) + node->flags |= OCTREE_FLAG_EXTRA_NODE; + } + else + if (list->parent->hi == list) + { + list->parent->hi = node; + } + else + { + ASSERT(list->parent->lo == list); + list->parent->lo = node; + } + node->parent = list->parent; + node->nextItem = list; + list->parent = node; + list->flags |= OCTREE_FLAG_EXTRA_NODE; + } + ASSERT(list->nextItem != list); + ASSERT(node->nextItem != node); +} + +void Octree::testLine(OctreeNode* node, FMOD_VECTOR a, FMOD_VECTOR b, RecursionData* recursionData) +{ + float t; + float da; + float db; + + for (OctreeNode* next = node->nextItem; next; next = next->nextItem) + { + ASSERT(next->flags & OCTREE_FLAG_LEAF); + // todo: probably best to test this bounding box also. + if (!recursionData->octreeLineTestCallback(next, recursionData->data)) + { + recursionData->exit = true; + return; + } + } + // clip line to box + // if the line does not intersect the box then return. + +#define CLAMP_LINE() \ + if (da < 0.0f && db > 0.0f) \ + { \ + t = da / (da - db); \ + a.x = a.x + t * (b.x - a.x); \ + a.y = a.y + t * (b.y - a.y); \ + a.z = a.z + t * (b.z - a.z); \ + } \ + else \ + if (da > 0.0f && db < 0.0f) \ + { \ + t = db / (db - da); \ + b.x = b.x + t * (a.x - b.x); \ + b.y = b.y + t * (a.y - b.y); \ + b.z = b.z + t * (a.z - b.z); \ + } + +#define CLAMP_MIN() CLAMP_LINE() else if (da < 0.0f && db < 0.0f) return; +#define CLAMP_MAX() CLAMP_LINE() else if (da < 0.0f && db < 0.0f) return; + + + da = a.x - node->aabb.xMin; + db = b.x - node->aabb.xMin; + CLAMP_MIN() + da = node->aabb.xMax - a.x; + db = node->aabb.xMax - b.x; + CLAMP_MAX() + + da = a.y - node->aabb.yMin; + db = b.y - node->aabb.yMin; + CLAMP_MIN() + da = node->aabb.yMax - a.y; + db = node->aabb.yMax - b.y; + CLAMP_MAX() + + da = a.z - node->aabb.zMin; + db = b.z - node->aabb.zMin; + CLAMP_MIN() + da = node->aabb.zMax - a.z; + db = node->aabb.zMax - b.z; + CLAMP_MAX() + + if (node->flags & OCTREE_FLAG_LEAF) + { + if (!recursionData->octreeLineTestCallback(node, recursionData->data)) + { + recursionData->exit = true; + return; + } + } + else + { + if (node->hi) + { + testLine(node->hi, a, b, recursionData); + if (recursionData->exit) + return; + } + if (node->lo) + { + testLine(node->lo, a, b, recursionData); + if (recursionData->exit) + return; + } + } +} + +} + +#endif // FMOD_SUPPORT_GEOMETRY + diff --git a/src/fmod_octree.h b/src/fmod_octree.h new file mode 100755 index 0000000..f161eeb --- /dev/null +++ b/src/fmod_octree.h @@ -0,0 +1,192 @@ +#ifndef _FMOD_OCTREE_H +#define _FMOD_OCTREE_H + +// This is a compramise between an octree and an axis alligned bounding box tree. +// Basicaly an octree which splits space exactly in 8 equal quadrents each time, +// except that only the splits that are needed to seperate the objects are actually stored. + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_GEOMETRY + +//#define FMOD_GEOMETRY_DEBUGGING +//#define FMOD_23TREE + +#include "fmod.hpp" + +namespace FMOD +{ + +struct FMOD_AABB +{ + float xMin; + float xMax; + float yMin; + float yMax; + float zMin; + float zMax; +}; +enum OctreeFlags +{ + OCTREE_FLAG_X_SPLIT = 0x000, + OCTREE_FLAG_Y_SPLIT = 0x001, + OCTREE_FLAG_Z_SPLIT = 0x002, + OCTREE_FLAG_SPLIT_MASK = 0x003, + OCTREE_FLAG_LEAF = 0x004, + OCTREE_FLAG_CALCULATED_AABB = 0x008, + OCTREE_FLAG_EXTRA_NODE = 0x010, + OCTREE_FLAG_FREE = 0x020, + OCTREE_FLAG_INSERTED = 0x040, + OCTREE_FLAG_23LEAF = 0x080, + OCTREE_FLAG_23ROOT = 0x100, + OCTREE_FLAG_23NODE = 0x200, + OCTREE_FLAG_INTERNAL_NODE = 0x400, +}; + +struct OctreeNode +{ + FMOD_AABB aabb; + int flags; + unsigned int splitLevel; + unsigned int pos[3]; + + OctreeNode *parent; + OctreeNode *hi; + OctreeNode *lo; + OctreeNode *nextItem; +}; + +class Octree +{ + public: + // data stucture for the recursive testLine to save on extra + // parameters on the stack. + struct RecursionData + { + bool (*octreeLineTestCallback)(OctreeNode* item, void* data); + void *data; + bool exit; + }; + + // see setMaxSize for description of maxSize + Octree(float maxSize); + ~Octree(); + + OctreeNode* mRoot; + FMOD_VECTOR mCenter; + float mScale; + OctreeNode* mFreeList; + + // Inserting / deleting and updating items + void insertItem(OctreeNode* item); + void deleteItem(OctreeNode* item); + void updateItem(OctreeNode* item); + + // For update optimzation + bool needsReinsert(OctreeNode* item); + void updateItemAABB(OctreeNode* item) { adjustAABBs(item); } + + // Get the Axis Aligned Bounding Box that bounds the entire tree + void getAABB(FMOD_AABB* aabb); + + // Internal nodes. + // A memory pool for internal tree nodes. + // The user is responsible for providing enough internal nodes. + // The maximum number of internal nodes necessery is equal to the number of + // leaf nodes added with insertItem. + void addInternalNode(OctreeNode* item); + void removeInternalNode(OctreeNode* item); + + // Debugging functions +#if defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING) + void checkTree(OctreeNode* node); + void calculateAverageNodeElements(); + void checkNodeIsContained(OctreeNode* node); + void calculateAverageNodeElements(OctreeNode* node); +#endif + + // same maxSize as passed as a parameter to the constructor. + // sets the maximum range in x,y and z for the tree. + // If the range is too large the tree will loose precision and + // if the range is too small, dimensions may wrap and items + // may end up in the top node. In both cases, the tree + // will still return all desired intersecting nodes, but may + // be very slow. + void setMaxSize(float maxSize); + + // test a line for collision with the tree + bool testLine(bool (*octreeLineTestCallback)(OctreeNode* item, void* data), void* data, const FMOD_VECTOR& a, const FMOD_VECTOR& b); + +#if defined(FMOD_GEOMETRY_DEBUGGING) + void renderTree(void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)); + void renderTreeInternal(OctreeNode* node, void (*renderBox)(float xMin, float xMax, float yMin, float yMax, float zMin, float zMax)); +#endif + +#if defined(FMOD_23TREE) + // Use a 2-3-tree for to keep a sorted list of elements in each node. + // This could be used to speed up the pathalogical case where there + // are many AABBs in the one node. However, it may be slower in the + // average case. + // remove23Tree not implemented yet. + void insert23Tree(OctreeNode* parent, OctreeNode** pParent, OctreeNode* item); + void split23Tree(OctreeNode* node, OctreeNode* item); + void remove23Tree(OctreeNode* item); + void check23Tree(OctreeNode* parent, OctreeNode** pParent); + void check23TreeRecursive(OctreeNode* node); + int unsigned findLowest(OctreeNode* node); +#endif + + + static unsigned int ftoint(float f) + { + unsigned int i; + + #if (defined(PLATFORM_WINDOWS) && !defined(__MINGW32__) || defined(PLATFORM_XBOX)) && defined(PLATFORM_32BIT) + + __asm fld f; + __asm fistp i; + + #else + + i = (int)f; + + #endif + + return i; + } + + // transformation functions + unsigned int xGetCenter(OctreeNode* node) + { + return ftoint((((node->aabb.xMin + node->aabb.xMax) * 0.5f - mCenter.x) * mScale * (float)(1 << 30)) + (float)(1 << 30)); + } + unsigned int yGetCenter(OctreeNode* node) + { + return ftoint((((node->aabb.yMin + node->aabb.yMax) * 0.5f - mCenter.y) * mScale * (float)(1 << 30)) + (float)(1 << 30)); + } + unsigned int zGetCenter(OctreeNode* node) + { + return ftoint((((node->aabb.zMin + node->aabb.zMax) * 0.5f - mCenter.z) * mScale * (float)(1 << 30)) + (float)(1 << 30)); + } + + private: + void insertInternal(OctreeNode* node, OctreeNode* item); + void addToFreeList(OctreeNode* item); + OctreeNode* getFreeNode(); + void adjustAABBs(OctreeNode* node); + + // each node of the tree potentially has a list of leaf nodes that are too close to be split. + // these functions make adding and removing for this list easier. + void removeListItem(OctreeNode* next); + void addListItem(OctreeNode* list, OctreeNode* node); + + static void testLine(OctreeNode* node, FMOD_VECTOR a, FMOD_VECTOR b, RecursionData* recursionData); + + +}; + +} + +#endif // FMOD_SUPPORT_GEOMETRY + +#endif // _FMOD_OCTREE_H diff --git a/src/fmod_os_cdda.h b/src/fmod_os_cdda.h new file mode 100755 index 0000000..46f38a7 --- /dev/null +++ b/src/fmod_os_cdda.h @@ -0,0 +1,249 @@ +#ifndef _FMOD_OS_CDDA +#define _FMOD_OS_CDDA + +#include "fmod.h" +#include "fmod_linkedlist.h" + + +namespace FMOD +{ + +//#define FMOD_OS_CDDA_DEBUG +//#define FMOD_OS_CDDA_DUMPTOC + +#ifdef FMOD_OS_CDDA_DEBUG + #define FMOD_OS_CDDA_LOG_FILENAME "fmodcdda.log" + #define FMOD_OS_CDDA_DEBUGPRINT FMOD_OS_CDDA_DebugPrint + #define FMOD_OS_CDDA_DEBUGINIT FMOD_OS_CDDA_DebugInit() + #ifndef FMOD_OS_CDDA_DUMPTOC + #define FMOD_OS_CDDA_DUMPTOC + #endif + void FMOD_OS_CDDA_DebugPrint(char *format, ...); + void FMOD_OS_CDDA_AddProfileData(int elapsedtime, unsigned int bytesread); +#else + #define FMOD_OS_CDDA_DEBUGPRINT + #define FMOD_OS_CDDA_DEBUGINIT +#endif + + +#define FMOD_CDDA_MAX_DEVICES 8 // Maximum number of devices we can handle +#define FMOD_CDDA_MAX_TRACKS 100 // Maximum number of tracks on a single device +#define FMOD_CDDA_SPINUP_TIME 1 // How many seconds to wait for a device to spin up to speed +#define FMOD_CDDA_SPINDOWN_TIME 5 // How many seconds before a device spins down after no activity +#define SIZEOF_CDDA_SECTOR 2352 + +typedef struct SRB SRB; +typedef unsigned long (*FUNC_SENDASPI32COMMAND)(SRB *srb); +typedef unsigned long (*FUNC_GETASPI32SUPPORTINFO)(); + +typedef struct +{ + unsigned int value; + char *string; + +} FMOD_CDDA_StringList; + +typedef struct +{ + unsigned char SK; + unsigned char ASC; + unsigned char ASCQ; + +} FMOD_CDDA_SenseKey; + +/* + Standard INQUIRY data +*/ +typedef struct +{ + unsigned char peripheral_device_type : 5; + unsigned char peripheral_qualifier : 3; + + unsigned char device_type_modifier : 7; + unsigned char removable_medium : 1; + + unsigned char ansi_version : 3; + unsigned char ecma_version : 3; + unsigned char iso_version : 2; + + unsigned char response_data_format : 4; + unsigned char res00 : 2; + unsigned char term_io_process : 1; + unsigned char async_event_notification : 1; + + unsigned char additional_length; + + unsigned char res01; + + unsigned char res02; + + unsigned char soft_reset : 1; + unsigned char cmd_queue : 1; + unsigned char res03 : 1; + unsigned char linked : 1; + unsigned char sync : 1; + unsigned char wide_bus_16 : 1; + unsigned char wide_bus_32 : 1; + unsigned char relative_addressing : 1; + + char vendor_id[8]; // Not null-terminated - padded with spaces + char product_id[16]; + char product_revision[4]; + +} FMOD_CDDA_InquiryData; + +/* + CD capabilities and mechanism status page (mode page 0x2a) +*/ +typedef struct +{ + unsigned char page_code : 6; + unsigned char res00 : 1; + unsigned char ps : 1; + + unsigned char page_length; + + unsigned char cdr_read : 1; + unsigned char cdrw_read : 1; + unsigned char method2 : 1; + unsigned char dvdrom_read : 1; + unsigned char dvdr_read : 1; + unsigned char dvdram_read : 1; + unsigned char res01 : 2; + + unsigned char cdr_write : 1; + unsigned char cdrw_write : 1; + unsigned char test_write : 1; + unsigned char res02 : 1; + unsigned char dvdr_write : 1; + unsigned char dvdram_write : 1; + unsigned char res022 : 2; + + unsigned char audio_play : 1; + unsigned char composite : 1; + unsigned char digital_port1 : 1; + unsigned char digital_port2 : 1; + unsigned char mode2_form1 : 1; + unsigned char mode2_form2 : 1; + unsigned char multi_session : 1; + unsigned char buf : 1; + + unsigned char cdda_support : 1; + unsigned char cdda_accurate : 1; + unsigned char rw_support : 1; + unsigned char rw_deinterleaved_corrected : 1; + unsigned char c2pointer_support : 1; + unsigned char isrc : 1; + unsigned char upc : 1; + unsigned char read_barcode : 1; + + unsigned char lock : 1; + unsigned char lock_state : 1; + unsigned char prevent_jumper : 1; + unsigned char eject : 1; + unsigned char res04 : 1; + unsigned char loading_mech_type : 3; + + unsigned char seperate_vol_per_channel : 1; + unsigned char seperate_channel_mute : 1; + unsigned char disc_present_reporting : 1; + unsigned char sw_slot_selection : 1; + unsigned char side_change_capable : 1; + unsigned char rw_in_leadin : 1; + unsigned char res05 : 2; + + unsigned char max_read_speed_hi; // Obsolete? + unsigned char max_read_speed_lo; // Obsolete? + + unsigned char num_volume_levels_hi; + unsigned char num_volume_levels_lo; + + unsigned char buffer_size_hi; + unsigned char buffer_size_lo; + + unsigned char current_read_speed_hi; // Obsolete? + unsigned char current_read_speed_lo; // Obsolete? + + unsigned char res06; + + unsigned char res07 : 1; + unsigned char bck : 1; + unsigned char rck : 1; + unsigned char lsbf : 1; + unsigned char length : 2; + unsigned char res08 : 2; + + unsigned char max_write_speed_hi; // Obsolete? + unsigned char max_write_speed_lo; // Obsolete? + + unsigned char current_write_speed_hi; // Obsolete? + unsigned char current_write_speed_lo; // Obsolete? + + unsigned char copy_mgmt_revision_hi; + unsigned char copy_mgmt_revision_lo; + + unsigned char res09; + unsigned char res10; + +} FMOD_CDDA_Caps; + +typedef struct +{ + unsigned int num_tracks; + + struct + { + unsigned char pre_emphasis : 1; // TRUE = audio w/pre-emphasis, FALSE = audio w/out pre-emphasis + unsigned char digital_copy_permitted : 1; // TRUE = copy permitted, FALSE = copy prohibited + unsigned char data_track : 1; // TRUE = data track, FALSE = audio track + unsigned char four_channel : 1; // TRUE = four channel audio, FALSE = two channel audio + unsigned char adr : 4; // ADR sub-channel Q field + + } flags[FMOD_CDDA_MAX_TRACKS]; + + unsigned char track_number[FMOD_CDDA_MAX_TRACKS]; + unsigned int start_sector[FMOD_CDDA_MAX_TRACKS]; + unsigned int num_sectors[FMOD_CDDA_MAX_TRACKS]; + +} FMOD_CDDA_TOC; + +struct FMOD_CDDA_DEVICE +{ + char *name; // NULL-terminated string describing this device i.e. drive letter + char *scsiaddr; // ASCII string representing the scsi addr of this device + char *devicename; // String containing the vendor id and product id etc. of this device + int letter; // Drive letter for this drive + int target_id; // SCSI address + int adapter_id; + int lun_id; + int device_open; // TRUE if device has been opened + int speed; // Desired speed e.g. 4x, 32x + FMOD_CDDA_SenseKey sense_key; + FMOD_CDDA_TOC toc; // Table of contents + FMOD_CDTOC usertoc; // Table of contents for user consumption + FMOD_CDDA_InquiryData inq_data; // Standard INQUIRY data + FMOD_CDDA_Caps *caps; // Mode page 0x2A capabilities info + FUNC_SENDASPI32COMMAND SendASPI32Command; +}; + + +FMOD_RESULT FMOD_OS_CDDA_Init(bool force_aspi); +FMOD_RESULT FMOD_OS_CDDA_Shutdown(); +bool FMOD_OS_CDDA_IsDeviceName(char *name); +FMOD_RESULT FMOD_OS_CDDA_GetNumDevices(int *num); +FMOD_RESULT FMOD_OS_CDDA_GetDeviceName(int devicenum, char *name, int namelen, char *scsiaddr, int scsiaddrlen, char *devicename, int devicenamelen); +FMOD_RESULT FMOD_OS_CDDA_OpenDevice(char *name, FMOD_CDDA_DEVICE **device); +FMOD_RESULT FMOD_OS_CDDA_CloseDevice(FMOD_CDDA_DEVICE *device); + +FMOD_RESULT FMOD_OS_CDDA_ReadSectors(FMOD_CDDA_DEVICE *device, char *buf, unsigned int start_sector, unsigned int num_sectors); +FMOD_RESULT FMOD_OS_CDDA_ReadToc(FMOD_CDDA_DEVICE *device, FMOD_CDDA_TOC *toc); +FMOD_RESULT FMOD_OS_CDDA_ReadTocRaw(FMOD_CDDA_DEVICE *device, FMOD_CDDA_TOC *toc); +FMOD_RESULT FMOD_OS_CDDA_SetSpeed(FMOD_CDDA_DEVICE *cdrom, int speed); +bool FMOD_OS_CDDA_TestUnitReady(FMOD_CDDA_DEVICE *cdrom); + + +} + +#endif + + diff --git a/src/fmod_os_misc.h b/src/fmod_os_misc.h new file mode 100755 index 0000000..6b48974 --- /dev/null +++ b/src/fmod_os_misc.h @@ -0,0 +1,145 @@ +#ifndef _FMOD_OS_MISC +#define _FMOD_OS_MISC + +#include "fmod.h" +#include "fmod_types.h" + +/* + ============================================================================== + These functions must be filled in to enable porting to any platform. + ============================================================================== +*/ + + +/* + Memory allocation functions +*/ +void *FMOD_OS_Memory_Alloc(int size, FMOD_MEMORY_TYPE type); +void *FMOD_OS_Memory_Realloc(void *ptr, int size, FMOD_MEMORY_TYPE type); +void FMOD_OS_Memory_Free(void *ptr, FMOD_MEMORY_TYPE type); + +int FMOD_OS_GetMemoryUsed(); + +/* + File functions +*/ +FMOD_RESULT FMOD_OS_File_Open(const char *name, char *mode, int unicode, unsigned int *filesize, void **handle); +FMOD_RESULT FMOD_OS_File_Close(void *handle); +FMOD_RESULT FMOD_OS_File_Read(void *handle, void *buf, unsigned int count, unsigned int *read); +FMOD_RESULT FMOD_OS_File_Seek(void *handle, unsigned int offset); +FMOD_RESULT FMOD_OS_File_Cancel(void *handle); +FMOD_RESULT FMOD_OS_File_DriveStatus(); + +/* + Debugging functions +*/ +FMOD_RESULT FMOD_OS_Debug_OutputStr(const char *s); + +/* + Time functions +*/ +FMOD_RESULT FMOD_OS_Time_GetNs(unsigned int *ns); +FMOD_RESULT FMOD_OS_Time_GetMs(unsigned int *ms); +FMOD_RESULT FMOD_OS_Time_Sleep(unsigned int ms); + +/* + Thread functions +*/ + +#if defined(PLATFORM_WINDOWS) + #include <process.h> + typedef unsigned int THREAD_RETURNTYPE; + #define THREAD_RETURN _endthreadex( 0 ); return 0; + #define THREAD_CALLCONV __stdcall +#elif defined(PLATFORM_PS2) + typedef void THREAD_RETURNTYPE; + #define THREAD_RETURN + #define THREAD_CALLCONV +#elif defined(PLATFORM_PS3_PPU) + #include <sys/ppu_thread.h> + typedef void THREAD_RETURNTYPE; + #define THREAD_RETURN sys_ppu_thread_exit(0); + #define THREAD_CALLCONV +#elif defined(PLATFORM_PSP) + #include <kernel.h> + typedef int THREAD_RETURNTYPE; + #define THREAD_RETURN sceKernelExitDeleteThread(0); return 0; + #define THREAD_CALLCONV +#else + typedef unsigned int THREAD_RETURNTYPE; + #define THREAD_RETURN return 0; + #define THREAD_CALLCONV +#endif + +#if defined(FMOD_SUPPORT_SIMD) + + #if defined (PLATFORM_WINDOWS) || defined (PLATFORM_LINUX) + #ifdef __cplusplus + extern "C" { + #endif + int FMOD_OS_SupportsSSE(); + #ifdef __cplusplus + } + #endif + + #define FMOD_OS_SupportsSIMD() (FMOD_OS_SupportsSSE() ? true : false); + #else + #define FMOD_OS_SupportsSIMD() true; + #endif + +#else + #define FMOD_OS_SupportsSIMD() false; +#endif + +typedef enum +{ + FMOD_THREAD_PRIORITY_VERYLOW = -2, + FMOD_THREAD_PRIORITY_LOW = -1, + FMOD_THREAD_PRIORITY_NORMAL = 0, + FMOD_THREAD_PRIORITY_HIGH = 1, + FMOD_THREAD_PRIORITY_VERYHIGH = 2, + FMOD_THREAD_PRIORITY_CRITICAL = 3 +} FMOD_THREAD_PRIORITY; + +FMOD_RESULT FMOD_OS_Thread_GetCurrentID(FMOD_UINT_NATIVE *id); +FMOD_RESULT FMOD_OS_Thread_Create(const char *name, THREAD_RETURNTYPE (THREAD_CALLCONV *callback)(void *param), void *param, FMOD_THREAD_PRIORITY priority, void *stack, int stacksize, void **handle); +FMOD_RESULT FMOD_OS_Thread_Destroy(void *handle); + +/* + CriticalSection functions +*/ +typedef struct FMOD_OS_CRITICALSECTION FMOD_OS_CRITICALSECTION; + +FMOD_RESULT FMOD_OS_CriticalSection_Create(FMOD_OS_CRITICALSECTION **crit, bool memorycrit = false); +FMOD_RESULT FMOD_OS_CriticalSection_Free(FMOD_OS_CRITICALSECTION *crit, bool memorycrit = false); +FMOD_RESULT FMOD_OS_CriticalSection_Enter(FMOD_OS_CRITICALSECTION *crit); +FMOD_RESULT FMOD_OS_CriticalSection_Leave(FMOD_OS_CRITICALSECTION *crit); + +/* + Semaphore functions +*/ +typedef struct FMOD_OS_SEMAPHORE FMOD_OS_SEMAPHORE; + +FMOD_RESULT FMOD_OS_Semaphore_Create(FMOD_OS_SEMAPHORE **sema); +FMOD_RESULT FMOD_OS_Semaphore_Free(FMOD_OS_SEMAPHORE *sema); +FMOD_RESULT FMOD_OS_Semaphore_Wait(FMOD_OS_SEMAPHORE *sema); +FMOD_RESULT FMOD_OS_Semaphore_Signal(FMOD_OS_SEMAPHORE *sema, bool interrupt = false); + +/* + Dynamic code loading functions. Optional. You only need to support these if the platform allows it. +*/ +typedef struct FMOD_OS_LIBRARY FMOD_OS_LIBRARY; + +FMOD_RESULT FMOD_OS_Library_Load(const char *dllname, FMOD_OS_LIBRARY **handle); +FMOD_RESULT FMOD_OS_Library_GetProcAddress(FMOD_OS_LIBRARY *handle, const char *procname, void **address); +FMOD_RESULT FMOD_OS_Library_Free(FMOD_OS_LIBRARY *handle); + +/* + Generic driver functionaliy. Optional. You only need to support these if the platform supports multiple sound devices. +*/ +FMOD_RESULT FMOD_OS_CheckDriverList(bool *devicelistchanged); + +extern int gSizeofCriticalSection; +extern int gSizeofSemaphore; + +#endif diff --git a/src/fmod_os_net.h b/src/fmod_os_net.h new file mode 100755 index 0000000..9e5d2e3 --- /dev/null +++ b/src/fmod_os_net.h @@ -0,0 +1,24 @@ +#ifndef _FMOD_OS_NET +#define _FMOD_OS_NET + +#include "fmod.h" + +/* + ============================================================================== + These functions must be filled in to enable porting to any platform. + ============================================================================== +*/ + +FMOD_RESULT FMOD_OS_Net_Init(); +FMOD_RESULT FMOD_OS_Net_Shutdown(); +FMOD_RESULT FMOD_OS_Net_Connect(const char *host, const unsigned short port, void **handle); +FMOD_RESULT FMOD_OS_Net_Listen(const unsigned short port, void **listenhandle); +FMOD_RESULT FMOD_OS_Net_Accept(const void *listenhandle, void **clienthandle); +FMOD_RESULT FMOD_OS_Net_Close(const void *handle); +FMOD_RESULT FMOD_OS_Net_Write(const void *handle, const char *buf, const unsigned int len, unsigned int *byteswritten); +FMOD_RESULT FMOD_OS_Net_Read(const void *handle, char *buf, const unsigned int len, unsigned int *bytesread); +FMOD_RESULT FMOD_OS_Net_ReadLine(const void *handle, char *buf, const unsigned int len); + + +#endif + diff --git a/src/fmod_os_output.h b/src/fmod_os_output.h new file mode 100755 index 0000000..8fcd520 --- /dev/null +++ b/src/fmod_os_output.h @@ -0,0 +1,9 @@ +#ifndef _FMOD_OS_OUTPUT_H +#define _FMOD_OS_OUTPUT_H + +FMOD_RESULT FMOD_OS_Output_Register(FMOD::PluginFactory *pluginfactory); + +FMOD_RESULT FMOD_OS_Output_GetDefault(FMOD_OUTPUTTYPE *outputtype); + +#endif + diff --git a/src/fmod_output.cpp b/src/fmod_output.cpp new file mode 100755 index 0000000..ccc838b --- /dev/null +++ b/src/fmod_output.cpp @@ -0,0 +1,1340 @@ +#include "fmod_settings.h" + +#include "fmod_channel_real.h" +#include "fmod_channel_software.h" +#include "fmod_channelpool.h" +#include "fmod_downmix.h" +#include "fmod_dspi.h" +#include "fmod_dsp_resampler_linear.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_output_software.h" +#include "fmod_outputi.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#ifdef PLATFORM_PS3_SPU + #include <cell/dma.h> + #include "fmod_systemi_spu.h" + #include "fmod_spu_printf.h" + #include "fmod_common_spu.h" +#else + #include "fmod_systemi.h" +#endif + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +Output::Output() +{ + mEnumerated = false; + mPolling = false; + mSystem = 0; + mChannelPool3D = 0; + mMixAheadBlocks = 0; + mMusicChannelGroup = 0; + mNum2DChannelsFromCaps = 0; + mNum3DChannelsFromCaps = 0; + mTotalChannelsFromCaps = 0; + mDSPTick = 1; + +#ifdef FMOD_SUPPORT_RECORDING + mRecordEnumerated = false; + mRecordNumActive = 0; + mRecordInfoForResampler = 0; + + FMOD_OS_CriticalSection_Create(&mRecordInfoCrit); + mRecordInfoHead.initNode(); +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::release() +{ + if (mDescription.close) + { +#ifdef FMOD_SUPPORT_SOFTWARE + readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + readfrommixer = 0; +#endif + + mDescription.close(this); + } + +#ifdef FMOD_SUPPORT_RECORDING + FMOD_OS_CriticalSection_Free(mRecordInfoCrit); +#endif + + FMOD_Memory_Free(this); + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_RECORDING + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::recordRead(FMOD_RECORDING_INFO *recordinfo, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + FMOD_RESULT result = FMOD_OK; + char *rptr1 = NULL; + char *rptr2 = NULL; + float *destptr = outbuffer; + unsigned int rlen1 = 0; + unsigned int rlen2 = 0; + unsigned int roffsetbytes = 0; + unsigned int rlenbytes = 0; + unsigned int rblockalign = 0; + + SoundI::getBytesFromSamples(recordinfo->mRecordLastCursorPos, &roffsetbytes, outchannels, recordinfo->mRecordFormat); + SoundI::getBytesFromSamples(length, &rlenbytes, outchannels, recordinfo->mRecordFormat); + SoundI::getBytesFromSamples(1, &rblockalign, outchannels, recordinfo->mRecordFormat); + + result = mDescription.record_lock((FMOD_OUTPUT_STATE *)this, recordinfo, roffsetbytes, rlenbytes, (void **)&rptr1, (void **)&rptr2, &rlen1, &rlen2); + if (result != FMOD_OK) + { + return result; + } + + /* + PCM8 is recorded in unsigned 8-bit, so convert it to signed 8-bit for FMOD + */ + if (recordinfo->mRecordFormat == FMOD_SOUND_FORMAT_PCM8) + { + unsigned char *rptr = NULL; + + if (rptr1 && rlen1) + { + rptr = (unsigned char *)rptr1; + + for (unsigned int count = 0; count < rlen1; count++) + { + *rptr++ ^= 128; + } + } + if (rptr2 && rlen2) + { + rptr = (unsigned char *)rptr2; + + for (unsigned int count = 0; count < rlen2; count++) + { + *rptr++ ^= 128; + } + } + } + + /* + Copy data from hardware and convert to float. + */ + if (rptr1 && rlen1) + { + DSPI::convert(destptr, rptr1, FMOD_SOUND_FORMAT_PCMFLOAT, recordinfo->mRecordFormat, (rlen1 / rblockalign) * outchannels, 1, 1, 1.0f); + destptr += ((rlen1 / rblockalign) * outchannels); + } + if (rptr2 && rlen2) + { + DSPI::convert(destptr, rptr2, FMOD_SOUND_FORMAT_PCMFLOAT, recordinfo->mRecordFormat, (rlen2 / rblockalign) * outchannels, 1, 1, 1.0f); + } + + if (mDescription.record_unlock) + { + result = mDescription.record_unlock((FMOD_OUTPUT_STATE *)this, recordinfo, rptr1, rptr2, rlen1, rlen2); + if (result != FMOD_OK) + { + return result; + } + } + + recordinfo->mRecordLastCursorPos += length; + if (recordinfo->mRecordLastCursorPos >= recordinfo->mRecordBufferLength) + { + recordinfo->mRecordLastCursorPos -= recordinfo->mRecordBufferLength; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK Output::recordResamplerReadCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPResampler *resampler = (DSPResampler *)dsp_state; + Output *output = NULL; + + resampler->getUserData((void **)&output); + + return output->recordRead(output->mRecordInfoForResampler, inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::recordFill(FMOD_RECORDING_INFO *recordinfo, unsigned int length) +{ + FMOD_RESULT result; + int channels; + char *pptr1 = 0, *pptr2 = 0; + unsigned int plen1 = 0, plen2 = 0, poffsetbytes, soundlength, plength, plengthbytes, pblockalign, totalplength; + FMOD_SOUND_FORMAT pformat; + + result = recordinfo->mRecordSound->getFormat(0, &pformat, &channels, 0); + if (result != FMOD_OK) + { + return result; + } + result = recordinfo->mRecordSound->getLength(&soundlength, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + result = SoundI::getBytesFromSamples(1, &pblockalign, channels, pformat); + if (result != FMOD_OK) + { + return result; + } + + totalplength = (unsigned int)((length * recordinfo->mRecordSound->mDefaultFrequency) / recordinfo->mRecordRate); + while (totalplength) + { + /* + Work in blocks so we dont overflow our record resample buffer. + */ + plength = totalplength; + if (plength > recordinfo->mRecordTempBufferLength) + { + plength = recordinfo->mRecordTempBufferLength; + } + + SoundI::getBytesFromSamples(recordinfo->mRecordOffset, &poffsetbytes, channels, pformat); + SoundI::getBytesFromSamples(plength, &plengthbytes, channels, pformat); + + /* + Fill one block into the temporary float buffer. + */ + if (recordinfo->mRecordResamplerDSP) + { + mRecordInfoForResampler = recordinfo; + result = recordinfo->mRecordResamplerDSP->read(&recordinfo->mRecordTempBuffer, &channels, &plength, (FMOD_SPEAKERMODE)0, channels, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + mDSPTick++; + } + else + { + result = recordRead(recordinfo, recordinfo->mRecordTempBuffer, recordinfo->mRecordTempBuffer, plength, channels, channels); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Now lock the destination sample and write to it. + */ + result = recordinfo->mRecordSound->lock(poffsetbytes, plengthbytes, (void **)&pptr1, (void **)&pptr2, &plen1, &plen2); + if (result != FMOD_OK) + { + return result; + } + + /* + Convert from float to the destination sound format, and handle wrapping. + */ + { + float *srcptr = recordinfo->mRecordTempBuffer; + + if (pptr1 && plen1) + { + DSPI::convert(pptr1, srcptr, pformat, FMOD_SOUND_FORMAT_PCMFLOAT, (plen1 / pblockalign) * channels, 1, 1, 1.0f); + srcptr += ((plen1 / pblockalign) * channels); + } + if (pptr2 && plen2) + { + DSPI::convert(pptr2, srcptr, pformat, FMOD_SOUND_FORMAT_PCMFLOAT, (plen2 / pblockalign) * channels, 1, 1, 1.0f); + } + } + + result = recordinfo->mRecordSound->unlock(pptr1, pptr2, plen1, plen2); + if (result != FMOD_OK) + { + return result; + } + + /* + Now that the data has been copied, update the destination sound's cursor. + */ + recordinfo->mRecordOffset += plength; + + /* + Stop the recording if it reaches the end of the sample. + */ + if (plen2 || recordinfo->mRecordOffset >= (int)soundlength) + { + if (recordinfo->mRecordLoop) + { + recordinfo->mRecordOffset -= soundlength; + if (recordinfo->mRecordOffset < 0) + { + recordinfo->mRecordOffset = 0; + } + } + else + { + /* + Mark this recording device for stopping in system update + */ + recordinfo->mRecordFinished = true; + break; + } + } + + totalplength -= plength; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::recordUpdate() +{ + FMOD_RESULT result = FMOD_OK; + FMOD_RECORDING_INFO *current = NULL; + LocalCriticalSection recordInfoCrit(mRecordInfoCrit, true); + + if (!mDescription.record_getposition) + { + return FMOD_OK; + } + + current = SAFE_CAST(FMOD_RECORDING_INFO, mRecordInfoHead.getNext()); + while (current != &mRecordInfoHead) + { + unsigned int cursorpos = 0; + int length = 0; + FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, current->getNext()); + + if (!current->mRecordBufferLength || current->mRecordFinished) + { + current = next; + continue; + } + + result = mDescription.record_getposition((FMOD_OUTPUT_STATE *)this, current, &cursorpos); + if (result != FMOD_OK) + { + return result; + } + + length = cursorpos - current->mRecordLastCursorPos; + if (length < 0) + { + length += current->mRecordBufferLength; + } + if (length < 0 || length > (int)current->mRecordBufferLength) /* Handle bad values coming back from getposition. */ + { + length = 0; + } + + if (current->mRecordResamplerDSP) + { + int blocklength = current->mRecordResamplerDSP->mDescription.mResamplerBlockLength; + + /* + We need at least 3 blocks of data. The resampler double buffer + 1 due to + the read request at end of first resample buffer. + */ + if (length < blocklength * 3) + { + current = next; + continue; + } + + length /= blocklength; /* Round length to record block size. */ + length *= blocklength; + + length -= blocklength; /* Fill output record sample with n - 1 blocks, otherwise resampler will pull more data that we have. */ + } + else + { + /* + We need at least some data to record + */ + if (length <= 0) + { + current = next; + continue; + } + } + + result = recordFill(current, length); + if (result != FMOD_OK) + { + return result; + } + + current = next; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::recordGetInfo(int id, FMOD_RECORDING_INFO **info) +{ + FMOD_RESULT result = FMOD_OK; + FMOD_RECORDING_INFO *current = NULL; + FMOD_GUID guid = {0}; + + if (!info) + { + return FMOD_ERR_INVALID_PARAM; + } + + *info = 0; + + result = mSystem->getRecordDriverInfo(id, NULL, 0, &guid); + CHECK_RESULT(result); + + current = SAFE_CAST(FMOD_RECORDING_INFO, mRecordInfoHead.getNext()); + while (current != &mRecordInfoHead) + { + FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, current->getNext()); + + if (FMOD_memcmp(&guid, ¤t->mRecordGUID, sizeof(FMOD_GUID)) == 0) + { + *info = current; + break; + } + current = next; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::recordStopAll(bool finishedonly) +{ + FMOD_RECORDING_INFO *currentrecordinfo = NULL; + + currentrecordinfo = SAFE_CAST(FMOD_RECORDING_INFO, mRecordInfoHead.getNext()); + while (currentrecordinfo != &mRecordInfoHead) + { + FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, currentrecordinfo->getNext()); + + if (!finishedonly || currentrecordinfo->mRecordFinished) + { + recordStop(currentrecordinfo); + } + + currentrecordinfo = next; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::recordStop(FMOD_RECORDING_INFO *recordinfo) +{ + FMOD_RESULT result = FMOD_OK; + + if (!recordinfo) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Output::recordStop", "\n")); + + /* + Make the record device "dead" by removing it from the list + */ + FMOD_OS_CriticalSection_Enter(mRecordInfoCrit); + { + recordinfo->removeNode(); + mRecordNumActive--; + } + FMOD_OS_CriticalSection_Leave(mRecordInfoCrit); + + if (mDescription.record_stop) + { +#ifdef FMOD_SUPPORT_SOFTWARE + readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + readfrommixer = 0; +#endif + + result = mDescription.record_stop(this, recordinfo); + CHECK_RESULT(result); + } + + if (recordinfo->mRecordTempBuffer) + { + FMOD_Memory_Free(recordinfo->mRecordTempBuffer); + recordinfo->mRecordTempBuffer = NULL; + recordinfo->mRecordTempBufferLength = 0; + } + + if (recordinfo->mRecordResamplerDSP) + { + if (recordinfo->mRecordResamplerDSP->mResampleBufferMemory) + { + FMOD_Memory_Free(recordinfo->mRecordResamplerDSP->mResampleBufferMemory); + recordinfo->mRecordResamplerDSP->mResampleBufferMemory = NULL; + } + + FMOD_Memory_Free(recordinfo->mRecordResamplerDSP); + recordinfo->mRecordResamplerDSP = NULL; + } + + FMOD_Memory_Free(recordinfo); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Output::recordStop", "done\n")); + + return FMOD_OK; +} + +#endif /* FMOD_SUPPORT_RECORDING */ + +#ifdef FMOD_SUPPORT_SOFTWARE +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::mix(void *buffer, unsigned int numsamples) +{ + FMOD_RESULT result; + unsigned int blockalign = 0; + unsigned int offset = 0; + #ifndef PLATFORM_PS3_SPU + LocalCriticalSection criticalsection(mSystem->mDSPCrit); + LocalCriticalSection lockdspcriticalsection(mSystem->mDSPLockCrit); + #endif + FMOD_SOUND_FORMAT outputformat; + int outputchannels; + int outputchannelsmixer; + DSPI *dsphead; + + if (!buffer || !numsamples) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef PLATFORM_PS3_SPU + mSystem = &gSystem; + + outputformat = FMOD_SOUND_FORMAT_PCMFLOAT; + outputchannelsmixer = 8; +#else + result = mSystem->getSoftwareFormat(0, &outputformat, &outputchannelsmixer, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } +#endif + + outputchannels = outputchannelsmixer; + +#if !defined(PLATFORM_PS3) + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&outputchannels); + } +#endif + +#if defined(PLATFORM_PS2) + blockalign = 8; +#elif defined(PLATFORM_PS3_SPU) + blockalign = 32; +#else + result = SoundI::getBytesFromSamples(1, &blockalign, outputchannels, outputformat); + if (result != FMOD_OK) + { + return result; + } +#endif + + +#ifdef PLATFORM_PS3_SPU + cellDmaGet((void *)gDMAMemorySoundCard, (uint64_t)gDSPSoundCardMram, gDSPSoundCardSize, TAG1, TID, RID); + cellDmaWaitTagStatusAll(1<<TAG1); + + dsphead = (DSPI *)gDMAMemorySoundCard; + + FMOD_PS3_SPU_DSPPointersToLS(dsphead, false); +#else + + dsphead = mSystem->mDSPSoundCard; + if (!dsphead) + { + return FMOD_ERR_INVALID_PARAM; + } +#endif + +#if !defined(PLATFORM_PS3) + mSystem->flushDSPConnectionRequests(false); +#endif + +#if !defined(PLATFORM_PS2) && !defined(PLATFORM_PS3_SPU) + lockdspcriticalsection.enter(); + criticalsection.enter(); +#endif + +#ifdef FMOD_SUPPORT_RECORDING + if (mRecordNumActive) + { + recordUpdate(); + } +#endif + + /* + ================================================================================================ + Update Mixer + ================================================================================================ + */ + do + { + void *outbuffer = (char *)buffer + (offset * blockalign); + unsigned int len = numsamples; + + mSystem->mDSPActive = true; + { + #ifdef FMOD_SUPPORT_MIXER_NONRECURSIVE /* Can't use virtual function on a class DMA'd in from PPU */ + result = dsphead->run((float **)&outbuffer, &outputchannelsmixer, &len, mSystem->mSpeakerMode, outputchannelsmixer, mDSPTick); + + if (outbuffer != buffer) + { + FMOD_memcpy(buffer, outbuffer, sizeof(float) * len * outputchannelsmixer); + } + #else + result = dsphead->read(outbuffer, &len, mSystem->mSpeakerMode, outputchannelsmixer, mDSPTick); + #endif + + mDSPTick++; + } + mSystem->mDSPActive = false; + + numsamples -= len; + offset += len; + } + while (numsamples > 0); + +#if !defined(PLATFORM_PS2) && !defined(PLATFORM_PS3_SPU) + if (mDescription.postmixcallback) + { + mDescription.postmixcallback(this); + } + + criticalsection.leave(); + lockdspcriticalsection.leave(); +#endif + +#if !defined(PLATFORM_PS3) + mSystem->mDSPClock.mValue += offset; +#endif + +#ifndef PLATFORM_PS3_SPU + float delta = (float)mSystem->mDSPBlockSize / (float)mSystem->mOutputRate * 1000.0f; // ms + + FMOD::gGlobal->gDSPClock.mValue += (FMOD_UINT64)(delta * 4294967296.0f); + FMOD_OS_Time_GetMs(&FMOD::gGlobal->gDSPClockTimeStamp); +#endif + + return FMOD_OK; +} +#endif + +#ifndef PLATFORM_PS3_SPU +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::getFreeChannel(FMOD_MODE mode, ChannelReal **realchannel, int numchannels, int numsoundchannels, int *found, bool ignorereserved) +{ + FMOD_RESULT result; + + if (!realchannel) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mode & FMOD_3D) + { + if (!mChannelPool3D) + { + return FMOD_ERR_CHANNEL_ALLOC; + } + + result = mChannelPool3D->allocateChannel(realchannel, FMOD_CHANNEL_FREE, numchannels, found, ignorereserved); + if (result != FMOD_OK) + { + return result; + } + } + else + { + if (!mChannelPool) + { + return FMOD_ERR_CHANNEL_ALLOC; + } + + result = mChannelPool->allocateChannel(realchannel, FMOD_CHANNEL_FREE, numchannels, found, ignorereserved); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} +#endif + +#ifdef FMOD_SUPPORT_HARDWAREXM +/* +[API] +[ + [DESCRIPTION] + Reserves a physical SPU2 hardware voice so that it is not used by FMOD. + + [PARAMETERS] + 'voice' Hardware voice number. Values accepted are in the range 0 to 47. + 'reserved' 1 = reserve voice. 0 = unreserve voice. + + [RETURN_VALUE] + FMOD_RESULT + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Output::reserveVoice(int voice, bool reserved) +{ + FMOD_RESULT result; + FMOD::ChannelReal *channel; + + result = mChannelPool->getChannel(voice, (FMOD::ChannelReal**)&channel); + if (result != FMOD_OK) + { + return result; + } + + return channel->setReserved(reserved); +} +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK Output::mixCallback(FMOD_OUTPUT_STATE *output, void *buffer, unsigned int length) +{ + Output *out = (Output *)output; + + return out->mix(buffer, length); +} +#endif + + +/* +[API] +[ + [DESCRIPTION] + Called when the user calls System::getNumDrivers. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'numdrivers' Address of a variable to receive the number of output drivers in your plugin. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT.<br> + Optional. FMOD will assume 0 if this is not specified. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getNumDrivers + System::getDriverInfo + FMOD_OUTPUT_GETDRIVERNAMECALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_GETNUMDRIVERSCALLBACK(FMOD_OUTPUT_STATE *output_state, int *numdrivers) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[API] +[ + [DESCRIPTION] + Called when the user calls System::getDriverInfo. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'id' Index into the total number of outputs possible, provided by the FMOD_OUTPUT_GETNUMDRIVERSCALLBACK callback. + 'name' Address of a variable to receive the driver name relevant to the index passed in. Fill this in. + 'namelen' Length of name buffer being passed in by the user. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getDriverInfo + System::getNumDrivers + FMOD_OUTPUT_GETNUMDRIVERSCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_GETDRIVERNAMECALLBACK(FMOD_OUTPUT_STATE *output_state, int id, char *name, int namelen) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Called when the user calls System::getDriverCaps. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'id' Index into the total number of outputs possible, provided by the FMOD_OUTPUT_GETNUMDRIVERSCALLBACK callback. + 'caps' Address of a variable to receive the caps available by this output device. See FMOD_CAPS. Fill this in. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::getDriverCaps + System::getDriverInfo + System::getNumDrivers + FMOD_OUTPUT_GETNUMDRIVERSCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_GETDRIVERCAPSCALLBACK(FMOD_OUTPUT_STATE *output_state, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Initialization callback which is called when the user calls System::init. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'selecteddriver' This is the selected driver id that the user chose from calling System::setDriver. + 'flags' Initialization flags passed in by the user. + 'outputrate' Output rate selected by the user. If not possible, change the rate to the closest match. + 'outputchannels' Output channel count selected by the user. For example 1 = mono output. 2 = stereo output. + 'outputformat' Output format specified by the user. If not possible to support, return FMOD_ERR_FORMAT. + 'dspbufferlength' Size of the buffer fmod will mix to in one mix update. This value is in PCM samples. + 'dspnumbuffers' Number of buffers fmod will mix to in a circular fashion. Multiply this by dspbufferlength to get the total size of the output sound buffer to allocate. + 'extradriverdata' Data passed in by the user specific to this driver. May be used for any purpose. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_RESULT + System::init + System::setDriver +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_INITCALLBACK(FMOD_OUTPUT_STATE *output_state, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Shut down callback which is called when the user calls System::close or System::release. (System::release calls System::close internally) + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::release + System::close +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_CLOSECALLBACK(FMOD_OUTPUT_STATE *output_state) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Called when the user calls System::update. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_UPDATECALLBACK(FMOD_OUTPUT_STATE *output_state) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Called when the user calls System::getOutputHandle. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'handle' Address of a variable to receieve the current plugin's output 'handle'. This is only if the plugin writer wants to allow the user access to the main handle behind the plugin (for example the file handle in a file writer plugin). The pointer type must be published to the user somehow, as is done in fmod.h. + + [RETURN_VALUE] + + [REMARKS] + Remember to return FMOD_OK at the bottom of the function, or an appropriate error code from FMOD_RESULT. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_GETHANDLECALLBACK(FMOD_OUTPUT_STATE *output_state, void **handle) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Returns the current PCM offset or playback position for the output stream.<br> + Called from the mixer thread, only when the 'polling' member of FMOD_OUTPUT_DESCRIPTION is set to <b>true</b>.<br> + The internal FMOD output thread calls this function periodically to determine if it should ask for a block of audio data or not. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'position' Address of a variable that receives the position of the output stream. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OUTPUT_DESCRIPTION + FMOD_OUTPUT_LOCKCALLBACK + FMOD_OUTPUT_UNLOCKCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_GETPOSITIONCALLBACK(FMOD_OUTPUT_STATE *output_state, unsigned int *pcm) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Called from the mixer thread, only when the 'polling' member of FMOD_OUTPUT_DESCRIPTION is set to true. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'offset' Offset in <i>bytes</i> to the position the caller wants to lock in the sample buffer. + 'length' Number of <i>bytes</i> the caller want to lock in the sample buffer. + 'ptr1' Address of a pointer that will point to the first part of the locked data. + 'ptr2' Address of a pointer that will point to the second part of the locked data. This will be null if the data locked hasn't wrapped at the end of the buffer. + 'len1' Length of data in <i>bytes</i> that was locked for ptr1 + 'len2' Length of data in <i>bytes</i> that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OUTPUT_DESCRIPTION + FMOD_OUTPUT_UNLOCKCALLBACK + FMOD_OUTPUT_GETPOSITIONCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_LOCKCALLBACK(FMOD_OUTPUT_STATE *output_state, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Called from the mixer thread, only when the 'polling' member of FMOD_OUTPUT_DESCRIPTION is set to true. + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'ptr1' Pointer to the 1st locked portion of sample data, from Sound::lock. + 'ptr2' Pointer to the 2nd locked portion of sample data, from Sound::lock. + 'len1' Length of data in <i>bytes</i> that was locked for ptr1 + 'len2' Length of data in <i>bytes</i> that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer. + + [RETURN_VALUE] + + [REMARKS] + This function is normally called after data has been read/written to from Sound::lock. This function will do any post processing nescessary and if needed, send it to sound ram. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_OUTPUT_DESCRIPTION + FMOD_OUTPUT_LOCKCALLBACK + FMOD_OUTPUT_GETPOSITIONCALLBACK +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_UNLOCKCALLBACK(FMOD_OUTPUT_STATE *output_state, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Called by the plugin, when the 'polling' member of FMOD_OUTPUT_DESCRIPTION is set to false.<br> + Use this function from your own driver irq/timer to read some data from FMOD's DSP engine. All of the resulting output caused by playing sounds and specifying effects by the user will be mixed here and written to the memory provided by the plugin writer.<br> + + [PARAMETERS] + 'output_state' Pointer to the plugin state. The user can use this variable to access runtime plugin specific variables and plugin writer user data. + 'buffer' Plugin-writer provided memory for the FMOD Ex mixer to write to. + 'length' Length of the buffer in samples. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_OUTPUT_READFROMMIXER(FMOD_OUTPUT_STATE *output_state, void *buffer, unsigned int length) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT Output::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mChannelPool) + { + CHECK_RESULT(mChannelPool->getMemoryUsed(tracker)); + } + + if (mChannelPool3D && (mChannelPool3D != mChannelPool)) + { + CHECK_RESULT(mChannelPool3D->getMemoryUsed(tracker)); + } + +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RECORDING_INFO *current = SAFE_CAST(FMOD_RECORDING_INFO, mRecordInfoHead.getNext()); + while (current != &mRecordInfoHead) + { + FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, current->getNext()); + + if (current->mRecordSound) + { + CHECK_RESULT(current->mRecordSound->getMemoryUsed(tracker)); + + if (current->mRecordTempBuffer) + { + unsigned int bufferlengthbytes = 0; + SoundI::getBytesFromSamples(FMOD_RECORD_TEMPBUFFERSIZE, &bufferlengthbytes, current->mRecordSound->mChannels, FMOD_SOUND_FORMAT_PCMFLOAT); + tracker->add(false, FMOD_MEMBITS_RECORDBUFFER, bufferlengthbytes); + } + } + + current = next; + } +#endif + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_output_emulated.cpp b/src/fmod_output_emulated.cpp new file mode 100755 index 0000000..127586a --- /dev/null +++ b/src/fmod_output_emulated.cpp @@ -0,0 +1,196 @@ +#include "fmod_settings.h" + +#include "fmod_channel_emulated.h" +#include "fmod_channelpool.h" +#include "fmod_memory.h" +#include "fmod_output_emulated.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +OutputEmulated::OutputEmulated() +{ + FMOD_memset(&mDescription, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + mChannel = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputEmulated::init(int maxchannels) +{ + FMOD_RESULT result; + + if (!mSystem) + { + return FMOD_ERR_UNINITIALIZED; + } + + /* + Create Emulated channels + */ + if (maxchannels) + { + int count; + + mChannelPool = mChannelPool3D = FMOD_Object_Alloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, this, maxchannels); + if (result != FMOD_OK) + { + return result; + } + + mChannel = (ChannelEmulated *)FMOD_Memory_Calloc(sizeof(ChannelEmulated) * maxchannels); + if (!mChannel) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < maxchannels; count++) + { + new (&mChannel[count]) ChannelEmulated; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannel[count])); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputEmulated::release() +{ + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + if (mChannel) + { + FMOD_Memory_Free(mChannel); + mChannel = 0; + } + + return Output::release(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputEmulated::update() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT OutputEmulated::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_OUTPUT, sizeof(*this)); + + if (mChannel) + { + int numchannels = 0; + + if (mChannelPool) + { + CHECK_RESULT(mChannelPool->getNumChannels(&numchannels)); + } + + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelEmulated) * numchannels); + } + + return Output::getMemoryUsedImpl(tracker); +} + +#endif + +} diff --git a/src/fmod_output_emulated.h b/src/fmod_output_emulated.h new file mode 100755 index 0000000..9a0b094 --- /dev/null +++ b/src/fmod_output_emulated.h @@ -0,0 +1,36 @@ +#ifndef _FMOD_OUTPUT_EMULATED_H +#define _FMOD_OUTPUT_EMULATED_H + +#include "fmod_settings.h" + +#include "fmod_outputi.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class ChannelEmulated; + + class OutputEmulated : public Output + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class SystemI; + + private: + + ChannelEmulated *mChannel; + + public: + + OutputEmulated(); + + FMOD_RESULT init(int maxchannels); + FMOD_RESULT release(); + FMOD_RESULT update(); + }; +} + +#endif diff --git a/src/fmod_output_nosound.cpp b/src/fmod_output_nosound.cpp new file mode 100755 index 0000000..e83f465 --- /dev/null +++ b/src/fmod_output_nosound.cpp @@ -0,0 +1,498 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NOSOUND + +#include "fmod_memory.h" +#include "fmod_output_nosound.h" +#include "fmod_soundi.h" +#include "fmod_string.h" +#include "fmod_systemi.h" +#include "fmod_time.h" + +namespace FMOD +{ + +FMOD_OUTPUT_DESCRIPTION_EX nosoundoutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputNoSound::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputNoSound::getDescriptionEx() +{ + FMOD_memset(&nosoundoutput, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + nosoundoutput.name = "FMOD NoSound Output"; + nosoundoutput.version = 0x00010100; + nosoundoutput.polling = true; + nosoundoutput.getnumdrivers = &OutputNoSound::getNumDriversCallback; + nosoundoutput.getdrivername = &OutputNoSound::getDriverNameCallback; + nosoundoutput.getdrivercaps = &OutputNoSound::getDriverCapsCallback; + nosoundoutput.init = &OutputNoSound::initCallback; + nosoundoutput.close = &OutputNoSound::closeCallback; + nosoundoutput.getposition = &OutputNoSound::getPositionCallback; + nosoundoutput.lock = &OutputNoSound::lockCallback; + + /* + Private members + */ + nosoundoutput.mType = FMOD_OUTPUTTYPE_NOSOUND; + nosoundoutput.mSize = sizeof(OutputNoSound); + + return &nosoundoutput; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::getNumDrivers(int *numdrivers) +{ + *numdrivers = 1; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::getDriverName(int driver, char *name, int namelen) +{ + FMOD_strncpy(name, "NoSound Driver", namelen); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::getDriverCaps(int id, FMOD_CAPS *caps) +{ + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_MULTICHANNEL); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM8); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM16); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM24); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM32); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputNoSound::init", "Initializing.\n")); + + SoundI::getBytesFromSamples(dspbufferlength * dspnumbuffers, &mBlockLengthBytes, outputchannels, *outputformat); + + mBuffer = (char *)FMOD_Memory_Calloc(mBlockLengthBytes); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputNoSound::init", "Done.\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::close() +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + } + mBuffer = NULL; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::getPosition(unsigned int *pcm) +{ + FMOD_RESULT result; + unsigned int pos = 0; + int outputrate; + + /* + Get the values FMOD had set for rate, format, channels. + */ + result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + FMOD_OS_Time_GetMs(&pos); + + pos *= outputrate; + pos /= 1000; + + *pcm = pos; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + offset %= mBlockLengthBytes; + + if (offset + length > mBlockLengthBytes) + { + *ptr1 = (char *)mBuffer + offset; + *ptr2 = (char *)mBuffer; + *len1 = mBlockLengthBytes - offset; + *len2 = length - (mBlockLengthBytes - offset); + } + else + { + *ptr1 = (char *)mBuffer + offset; + *ptr2 = NULL; + *len1 = length; + *len2 = 0; + } + + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->getDriverName(id, name, namelen); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound::getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->getDriverCaps(id, caps); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound::getPositionCallback(FMOD_OUTPUT_STATE *output, unsigned int *pcm) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->getPosition(pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound::lockCallback(FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputNoSound *nosound = (OutputNoSound *)output; + + return nosound->lock(offset, length, ptr1, ptr2, len1, len2); +} + + +} + +#endif diff --git a/src/fmod_output_nosound.h b/src/fmod_output_nosound.h new file mode 100755 index 0000000..c89b421 --- /dev/null +++ b/src/fmod_output_nosound.h @@ -0,0 +1,45 @@ +#ifndef _FMOD_OUTPUT_NOSOUND_H +#define _FMOD_OUTPUT_NOSOUND_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NOSOUND + +#include "fmod_output_polled.h" + +namespace FMOD +{ + class OutputNoSound : public OutputPolled + { + private: + + unsigned int mBlockLengthBytes; + char *mBuffer; + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT enumerate(); + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverName(int driver, char *name, int namelen); + FMOD_RESULT getDriverCaps(int id, FMOD_CAPS *caps); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT getPosition(unsigned int *pcm); + FMOD_RESULT lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + + static FMOD_RESULT F_CALLBACK getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen); + static FMOD_RESULT F_CALLBACK getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getPositionCallback (FMOD_OUTPUT_STATE *output, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK lockCallback (FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + }; +} + +#endif /* #ifdef FMOD_SUPPORT_NOSOUND */ + +#endif + diff --git a/src/fmod_output_nosound_nrt.cpp b/src/fmod_output_nosound_nrt.cpp new file mode 100755 index 0000000..3630dc3 --- /dev/null +++ b/src/fmod_output_nosound_nrt.cpp @@ -0,0 +1,437 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NOSOUND + +#include "fmod_memory.h" +#include "fmod_output_nosound_nrt.h" +#include "fmod_soundi.h" +#include "fmod_string.h" +#include "fmod_systemi.h" +#include "fmod_time.h" + +namespace FMOD +{ + +FMOD_OUTPUT_DESCRIPTION_EX nosoundoutput_nrt; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputNoSound_NRT::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputNoSound_NRT::getDescriptionEx() +{ + FMOD_memset(&nosoundoutput_nrt, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + nosoundoutput_nrt.name = "FMOD NoSound Output - Non real-time"; + nosoundoutput_nrt.version = 0x00010100; + nosoundoutput_nrt.polling = false; + nosoundoutput_nrt.getnumdrivers = &OutputNoSound_NRT::getNumDriversCallback; + nosoundoutput_nrt.getdrivername = &OutputNoSound_NRT::getDriverNameCallback; + nosoundoutput_nrt.getdrivercaps = &OutputNoSound_NRT::getDriverCapsCallback; + nosoundoutput_nrt.init = &OutputNoSound_NRT::initCallback; + nosoundoutput_nrt.close = &OutputNoSound_NRT::closeCallback; + nosoundoutput_nrt.update = &OutputNoSound_NRT::updateCallback; + + /* + Private members + */ + nosoundoutput_nrt.mType = FMOD_OUTPUTTYPE_NOSOUND_NRT; + nosoundoutput_nrt.mSize = sizeof(OutputNoSound_NRT); + + return &nosoundoutput_nrt; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::getNumDrivers(int *numdrivers) +{ + *numdrivers = 1; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::getDriverName(int driver, char *name, int namelen) +{ + FMOD_strncpy(name, "NoSound Driver", namelen); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::getDriverCaps(int id, FMOD_CAPS *caps) +{ + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_MULTICHANNEL); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM8); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM16); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM24); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM32); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + FMOD_RESULT result; + FMOD_SOUND_FORMAT format; + unsigned int bufferlengthbytes = 0; + int channels; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputNoSound_NRT::init", "Initializing.\n")); + + result = mSystem->getSoftwareFormat(0, &format, &channels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + mBufferLength = dspbufferlength; + + result = SoundI::getBytesFromSamples(mBufferLength, &bufferlengthbytes, channels, format); + if (result != FMOD_OK) + { + return result; + } + + mBuffer = FMOD_Memory_Calloc(bufferlengthbytes); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputNoSound_NRT::init", "Done.\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::close() +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + mBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::update() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result; + + result = mix(mBuffer, mBufferLength); + if (result != FMOD_OK) + { + return FMOD_OK; + } +#endif + + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound_NRT::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputNoSound_NRT *nosound = (OutputNoSound_NRT *)output; + + return nosound->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound_NRT::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen) +{ + OutputNoSound_NRT *nosound = (OutputNoSound_NRT *)output; + + return nosound->getDriverName(id, name, namelen); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputNoSound_NRT::getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps) +{ + OutputNoSound_NRT *nosound = (OutputNoSound_NRT *)output; + + return nosound->getDriverCaps(id, caps); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound_NRT::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputNoSound_NRT *nosound = (OutputNoSound_NRT *)output; + + return nosound->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound_NRT::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputNoSound_NRT *nosound = (OutputNoSound_NRT *)output; + + return nosound->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputNoSound_NRT::updateCallback(FMOD_OUTPUT_STATE *output) +{ + OutputNoSound_NRT *nosound = (OutputNoSound_NRT *)output; + + return nosound->update(); +} + + + +} + +#endif diff --git a/src/fmod_output_nosound_nrt.h b/src/fmod_output_nosound_nrt.h new file mode 100755 index 0000000..67287d6 --- /dev/null +++ b/src/fmod_output_nosound_nrt.h @@ -0,0 +1,43 @@ +#ifndef _FMOD_OUTPUT_NOSOUND_NRT_H +#define _FMOD_OUTPUT_NOSOUND_NRT_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_NOSOUND_NRT + +#include "fmod_output_polled.h" + +namespace FMOD +{ + class OutputNoSound_NRT : public Output + { + private: + + unsigned int mBufferLength; + void *mBuffer; + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT enumerate(); + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverName(int driver, char *name, int namelen); + FMOD_RESULT getDriverCaps(int id, FMOD_CAPS *caps); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT update(); + + static FMOD_RESULT F_CALLBACK getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen); + static FMOD_RESULT F_CALLBACK getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK updateCallback (FMOD_OUTPUT_STATE *output); + }; +} + +#endif /* #ifdef FMOD_SUPPORT_NOSOUND_NRT */ + +#endif + diff --git a/src/fmod_output_polled.cpp b/src/fmod_output_polled.cpp new file mode 100755 index 0000000..f0811d8 --- /dev/null +++ b/src/fmod_output_polled.cpp @@ -0,0 +1,363 @@ +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_debug.h" +#include "fmod_downmix.h" +#include "fmod_output_polled.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_thread.h" + +// #define DUMPMIXERTODISK + +#ifdef DUMPMIXERTODISK + +#include <stdio.h> +static FILE *fp = 0; + +#endif + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +OutputPolled::OutputPolled() +{ + mCursorBlock = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputPolled::threadFunc() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result; + unsigned int pcm; + unsigned int blocksize; + int numblocks; + FMOD_SOUND_FORMAT outputformat; + int outputchannels; + + result = mSystem->getDSPBufferSize(&blocksize, &numblocks); + if (result != FMOD_OK) + { + return result; + } + + numblocks += mMixAheadBlocks; + + result = mSystem->getSoftwareFormat(0, &outputformat, &outputchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (mDescription.getposition) + { + result = mDescription.getposition(this, &pcm); + if (result != FMOD_OK) + { + return result; + } + } + + mSystem->mDSPTimeStamp.stampIn(); /* Dont time the above getposition, directsound falsely stalls, probably on a criticalsection */ + + pcm /= blocksize; + pcm %= numblocks; + + FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "OutputPolled::updateThread", "PCM %8d fillblock %8d\n", pcm, mCursorBlock)); + + while (mCursorBlock != (int)pcm) + { + void *ptr1 = 0, *ptr2 = 0; + unsigned int len1 = 0, len2 = 0; + unsigned int offbytes = 0, lenbytes = 0, numsamples; + int block = mCursorBlock; + + block -= mMixAheadBlocks; + if (block < 0) + { + block += numblocks; + } + +#if !defined(PLATFORM_PS3) + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&outputchannels); + } +#endif + + result = SoundI::getBytesFromSamples(blocksize, &lenbytes, outputchannels, outputformat); + if (result != FMOD_OK) + { + return result; + } + result = SoundI::getBytesFromSamples(block * blocksize, &offbytes, outputchannels, outputformat); + if (result != FMOD_OK) + { + return result; + } + + if (mDescription.lock) + { + result = mDescription.lock(this, offbytes, lenbytes, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + } + + result = SoundI::getSamplesFromBytes(len1, &numsamples, outputchannels, outputformat); + if (result != FMOD_OK) + { + return result; + } + + result = mix(ptr1, numsamples); + if (result != FMOD_OK) + { + return result; + } + + #ifdef DUMPMIXERTODISK + if (fp) + { + fwrite(ptr1, len1, 1, fp); + } + #endif + + /* + ptr2 and len2 should never be non 0. All updates are block aligned. + */ + if (mDescription.unlock) + { + result = mDescription.unlock(this, ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + return result; + } + } + + mCursorBlock++; + if (mCursorBlock >= numblocks) + { + mCursorBlock = 0; + } + } +#endif + + if (mFinishedSema) + { + FMOD_OS_Semaphore_Signal(mFinishedSema, false); + } + +#ifdef FMOD_SUPPORT_SOFTWARE + mSystem->mDSPTimeStamp.stampOut(95); +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputPolled::start() +{ + FMOD_RESULT result; + + if (mSystem->mFlags & FMOD_INIT_SYNCMIXERWITHUPDATE) + { + mPolledFromMainThread = true; + } + + if (mPolledFromMainThread) + { + FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "OutputPolled::start", "Starting thread to be triggered from System::update\n")); + + result = initThread("FMOD mixer thread", 0, 0, MIXER_THREADPRIORITY, 0, MIXER_STACKSIZE, true, 0, mSystem); + if (result != FMOD_OK) + { + return result; + } + + result = FMOD_OS_Semaphore_Create(&mFinishedSema); + if (result != FMOD_OK) + { + return result; + } + } + else + { + unsigned int blocksize; + int rate; + float ms; + + result = mSystem->getDSPBufferSize(&blocksize, 0); + if (result != FMOD_OK) + { + return result; + } + + result = mSystem->getSoftwareFormat(&rate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + ms = (float)blocksize * 1000.0f / (float)rate; + if (ms < 20) + { + ms /= 3; + if (ms < 1) + { + ms = 1; + } + } + else + { + ms = 10; + } + + FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "OutputPolled::start", "Starting thread that automatically wakes up every %d ms\n", (int)ms)); + + result = initThread("FMOD mixer thread", 0, 0, MIXER_THREADPRIORITY, 0, MIXER_STACKSIZE, false, (int)ms, mSystem); + if (result != FMOD_OK) + { + return result; + } + } + +#ifdef DUMPMIXERTODISK + fp = fopen("C:\\media\\fmod4output.raw", "wb"); +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputPolled::stop() +{ + FMOD_RESULT result; + + result = closeThread(); + if (result != FMOD_OK) + { + return result; + } + + if (mFinishedSema) + { + result = FMOD_OS_Semaphore_Free(mFinishedSema); + if (result != FMOD_OK) + { + return result; + } + } + + +#ifdef DUMPMIXERTODISK + if (fp) + { + fclose(fp); + fp = 0; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT OutputPolled::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mFinishedSema) + { + tracker->add(false, FMOD_MEMBITS_OUTPUT, gSizeofSemaphore); + } + + return Output::getMemoryUsedImpl(tracker); +} + +#endif + +} diff --git a/src/fmod_output_polled.h b/src/fmod_output_polled.h new file mode 100755 index 0000000..fe7e397 --- /dev/null +++ b/src/fmod_output_polled.h @@ -0,0 +1,43 @@ +#ifndef _FMOD_OUTPUT_POLLED_H +#define _FMOD_OUTPUT_POLLED_H + +#include "fmod_settings.h" + +#include "fmod_outputi.h" +#include "fmod_thread.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class Thread; + class DSP; + + class OutputPolled : public Output, public Thread + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + int mCursorBlock; + + FMOD_RESULT threadFunc(); + + protected: + + bool mPolledFromMainThread; + FMOD_OS_SEMAPHORE *mFinishedSema; + + public: + + OutputPolled(); + + FMOD_RESULT start(); + FMOD_RESULT stop(); + }; +} + +#endif + diff --git a/src/fmod_output_software.cpp b/src/fmod_output_software.cpp new file mode 100755 index 0000000..4331f16 --- /dev/null +++ b/src/fmod_output_software.cpp @@ -0,0 +1,457 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_autocleanup.h" +#include "fmod_channel_software.h" +#include "fmod_channelpool.h" +#include "fmod_dsp_resampler.h" +#include "fmod_memory.h" +#include "fmod_output_software.h" +#include "fmod_sample_software.h" +#include "fmod_string.h" +#include "fmod_systemi.h" + +#ifdef PLATFORM_PS3 +#include "fmod_output_ps3.h" +#endif + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +OutputSoftware::OutputSoftware() +{ + FMOD_memset(&mDescription, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + mDescription.name = "FMOD Software Output"; + mDescription.version = 0x00010100; + mDescription.polling = false; + + /* + Private members + */ + mDescription.getsamplemaxchannels = &OutputSoftware::getSampleMaxChannelsCallback; + mDescription.mType = FMOD_OUTPUTTYPE_SOFTWARE; + mDescription.mSize = sizeof(OutputSoftware); + + mChannel = 0; + mChannelPool = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputSoftware::init(int maxchannels) +{ + FMOD_RESULT result; + + if (!mSystem) + { + return FMOD_ERR_UNINITIALIZED; + } + + /* + Create Software channels + */ + if (maxchannels) + { + int count; + + mChannelPool = mChannelPool3D = FMOD_Object_Alloc(ChannelPool); + if (!mChannelPool) + { + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, this, maxchannels); + if (result != FMOD_OK) + { + return result; + } + + mChannel = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * maxchannels); + if (!mChannel) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < maxchannels; count++) + { + new (&mChannel[count]) ChannelSoftware; + + CHECK_RESULT(mChannelPool->setChannel(count, &mChannel[count], mSystem->mChannelGroup->mDSPHead)); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputSoftware::release() +{ + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + if (mChannel) + { + FMOD_Memory_Free(mChannel); + mChannel = 0; + } + + return Output::release(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputSoftware::createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample) +{ + FMOD_RESULT result; + int bits = 0; + SampleSoftware *newsample; + unsigned int overflowbytes = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputSoftware::createSample", "lengthpcm %d, lengthbytes %d, channels %d, format %d, mode %08x\n", waveformat ? waveformat->lengthpcm : 0, waveformat ? waveformat->lengthbytes : 0, waveformat ? waveformat->channels : 0, waveformat ? waveformat->format : FMOD_SOUND_FORMAT_NONE, mode)); + + if (!sample) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (waveformat) + { + result = SoundI::getBitsFromFormat(waveformat->format, &bits); + if (result != FMOD_OK) + { + return result; + } + + if (!bits && waveformat->format != FMOD_SOUND_FORMAT_NONE + #ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_IMAADPCM + && waveformat->format != FMOD_SOUND_FORMAT_IMAADPCM + #endif + #ifdef FMOD_SUPPORT_XMA + && waveformat->format != FMOD_SOUND_FORMAT_XMA + #endif + #ifdef FMOD_SUPPORT_MPEG + && waveformat->format != FMOD_SOUND_FORMAT_MPEG + #endif + #ifdef FMOD_SUPPORT_CELT + && waveformat->format != FMOD_SOUND_FORMAT_CELT + #endif + #endif + ) + { + #if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + if (waveformat->format == FMOD_SOUND_FORMAT_VAG) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputSoftware::createSample", "Tried to create an FMOD_SOFTWARE based sound with FMOD_SOUND_FORMAT_VAG.\n")); + return FMOD_ERR_NEEDSHARDWARE; + } + #endif + #if defined(PLATFORM_WII) || defined(PLATFORM_GC) + if (waveformat->format == FMOD_SOUND_FORMAT_GCADPCM) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputSoftware::createSample", "Tried to create an FMOD_SOFTWARE based sound with FMOD_SOUND_FORMAT_GCADPCM.\n")); + return FMOD_ERR_NEEDSHARDWARE; + } + #endif + + return FMOD_ERR_FORMAT; + } + } + AutoRelease<SampleSoftware> newsample_cleanup; + if (*sample == 0) + { + newsample = FMOD_Object_Calloc(SampleSoftware); + if (!newsample) + { + return FMOD_ERR_MEMORY; + } + newsample_cleanup = newsample; + } + else + { + newsample = SAFE_CAST(SampleSoftware, *sample); + } + + if (!waveformat) + { + *sample = newsample; + newsample_cleanup.releasePtr(); + return FMOD_OK; + } + + newsample->mFormat = waveformat->format; + + if (waveformat->format == FMOD_SOUND_FORMAT_IMAADPCM || waveformat->format == FMOD_SOUND_FORMAT_XMA || waveformat->format == FMOD_SOUND_FORMAT_MPEG || waveformat->format == FMOD_SOUND_FORMAT_CELT) + { + newsample->mLengthBytes = waveformat->lengthbytes; + newsample->mLoopPointDataEnd = 0; + overflowbytes = 0; + } + else + { + result = SoundI::getBytesFromSamples(waveformat->lengthpcm, &newsample->mLengthBytes, waveformat->channels, waveformat->format); + if (result != FMOD_OK) + { + return result; + } + + result = SoundI::getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes, waveformat->channels, waveformat->format); + if (result != FMOD_OK) + { + return result; + } + + if (overflowbytes <= 8) + { + newsample->mLoopPointDataEnd = newsample->mLoopPointDataEndMemory; + } + else + { + newsample->mLoopPointDataEnd = (char *)FMOD_Memory_Calloc(overflowbytes); + if (!newsample->mLoopPointDataEnd) + { + return FMOD_ERR_MEMORY; + } + } + } + + if (mode & FMOD_OPENMEMORY_POINT) + { + newsample->mBufferMemory = 0; + newsample->mBuffer = 0; + } + else + { + if (0) + {} + #ifdef PLATFORM_PS3 + else if ((mode & FMOD_LOADSECONDARYRAM) && OutputPS3::mRSXPoolInitialised) + { + newsample->mBufferMemory = OutputPS3::mRSXPool.calloc(newsample->mLengthBytes + (overflowbytes * 2) + 16, __FILE__, __LINE__); + if (!newsample->mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + if (OutputPS3::mRSXPoolManagerInMRAM) + { + MemBlockHeader *block; + FMOD_UINT_NATIVE offset; + + block = (MemBlockHeader *)newsample->mBufferMemory; + offset = OutputPS3::mRSXPoolBase + (block->mBlockOffset * OutputPS3::mRSXPool.mBlockSize); + + newsample->mBuffer = (char *)FMOD_ALIGNPOINTER(offset + overflowbytes, 16); + } + else + { + newsample->mBuffer = (char *)FMOD_ALIGNPOINTER((FMOD_UINT_NATIVE)newsample->mBufferMemory + overflowbytes, 16); + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputSoftware::createSample", "Allocated sample data in RSX memory pool at address %08x.\n", newsample->mBuffer)); + } + #endif + else if ((mode & FMOD_LOADSECONDARYRAM) && (FMOD::gGlobal->gMemoryTypeFlags & FMOD_MEMORY_SECONDARY)) + { + newsample->mBufferMemory = FMOD_Memory_CallocType(newsample->mLengthBytes + (overflowbytes * 2) + 16, FMOD_MEMORY_SECONDARY | (mode & FMOD_CREATESTREAM ? FMOD_MEMORY_STREAM_DECODE : 0)); + if (!newsample->mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + newsample->mBuffer = (char *)FMOD_ALIGNPOINTER((FMOD_UINT_NATIVE)newsample->mBufferMemory + overflowbytes, 16); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputSoftware::createSample", "Allocated sample data in RSX memory at address %08x.\n", newsample->mBuffer)); + } + else + { + newsample->mBufferMemory = FMOD_Memory_CallocType(newsample->mLengthBytes + (overflowbytes * 2) + 16, mode & FMOD_CREATESTREAM ? FMOD_MEMORY_STREAM_DECODE : 0); + if (!newsample->mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + newsample->mBuffer = (char *)FMOD_ALIGNPOINTER((FMOD_UINT_NATIVE)newsample->mBufferMemory + overflowbytes, 16); + } + } + + newsample->mFormat = waveformat->format; + newsample->mLength = waveformat->lengthpcm; + + *sample = newsample; + newsample_cleanup.releasePtr(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputSoftware::createSample", "done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +int OutputSoftware::getSampleMaxChannels(FMOD_MODE mode, FMOD_SOUND_FORMAT format) +{ + return 16; /* 2D and 3D voices can be panned without needing to split them up. Allow up to 16 channel wide. */ +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +int F_CALLBACK OutputSoftware::getSampleMaxChannelsCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format) +{ + OutputSoftware *outputsoftware = (OutputSoftware *)output; + + return outputsoftware->getSampleMaxChannels(mode, format); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT OutputSoftware::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_OUTPUT, sizeof(*this)); + + if (mChannel) + { + int count, numchannels = 0; + + if (mChannelPool) + { + CHECK_RESULT(mChannelPool->getNumChannels(&numchannels)); + + for (count = 0; count < numchannels; count++) + { + FMOD_RESULT result; + + ChannelSoftware *channelsoftware; + + result = mChannelPool->getChannel(count, (ChannelReal **)&channelsoftware); + if (result == FMOD_OK) + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelSoftware)); + + if (channelsoftware->mDSPLowPass) + { + if (channelsoftware->mDSPLowPass->mDescription.getmemoryused) + { + channelsoftware->mDSPLowPass->mDescription.getmemoryused(channelsoftware->mDSPLowPass, tracker); /* Access through plugin. */ + } + } + if (channelsoftware->mDSPResampler) + { + channelsoftware->mDSPResampler->getMemoryUsed(tracker); /* No plugin stuff. */ + } + } + } + } + } + + return Output::getMemoryUsedImpl(tracker); +} + +#endif + +} + +#endif diff --git a/src/fmod_output_software.h b/src/fmod_output_software.h new file mode 100755 index 0000000..bde38aa --- /dev/null +++ b/src/fmod_output_software.h @@ -0,0 +1,49 @@ +#ifndef _FMOD_OUTPUT_SOFTWARE_H +#define _FMOD_OUTPUT_SOFTWARE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_outputi.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class ChannelSoftware; + + class OutputSoftware : public Output + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class SystemI; + friend class Output; + + #ifdef PLATFORM_PS3 + friend class OutputPS3; + #endif + + private: + + ChannelSoftware *mChannel; + + int getSampleMaxChannels(FMOD_MODE mode, FMOD_SOUND_FORMAT format); + + public: + + OutputSoftware(); + + virtual FMOD_RESULT init(int maxsoftwarechannels); + virtual FMOD_RESULT release(); + FMOD_RESULT createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + + static int F_CALLBACK getSampleMaxChannelsCallback (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format); + }; +} + +#endif + +#endif diff --git a/src/fmod_output_wavwriter.cpp b/src/fmod_output_wavwriter.cpp new file mode 100755 index 0000000..891a41e --- /dev/null +++ b/src/fmod_output_wavwriter.cpp @@ -0,0 +1,814 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WAVWRITER + +#include "fmod_memory.h" +#include "fmod_output_wavwriter.h" +#include "fmod_string.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + + +FMOD_OUTPUT_DESCRIPTION_EX wavwriteroutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputWavWriter::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 } }; +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 } }; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputWavWriter::getDescriptionEx() +{ + FMOD_memset(&wavwriteroutput, 0, sizeof(FMOD_OUTPUT_DESCRIPTION)); + + wavwriteroutput.name = "FMOD WavWriter Output"; + wavwriteroutput.version = 0x00010100; + wavwriteroutput.polling = true; + wavwriteroutput.getnumdrivers = &OutputWavWriter::getNumDriversCallback; + wavwriteroutput.getdrivername = &OutputWavWriter::getDriverNameCallback; + wavwriteroutput.getdrivercaps = &OutputWavWriter::getDriverCapsCallback; + wavwriteroutput.init = &OutputWavWriter::initCallback; + wavwriteroutput.close = &OutputWavWriter::closeCallback; + wavwriteroutput.getposition = &OutputWavWriter::getPositionCallback; + wavwriteroutput.lock = &OutputWavWriter::lockCallback; + wavwriteroutput.unlock = &OutputWavWriter::unlockCallback; + wavwriteroutput.gethandle = &OutputWavWriter::getHandleCallback; + + /* + Private members + */ + wavwriteroutput.mType = FMOD_OUTPUTTYPE_WAVWRITER; + wavwriteroutput.mSize = sizeof(OutputWavWriter); + + return &wavwriteroutput; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::getNumDrivers(int *numdrivers) +{ + *numdrivers = 1; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::getDriverName(int driver, char *name, int namelen) +{ + FMOD_strncpy(name, "fmodoutput.wav", namelen); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::getDriverCaps(int id, FMOD_CAPS *caps) +{ + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_MULTICHANNEL); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM8); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM16); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM24); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM32); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + FMOD_RESULT result; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + result = mSystem->getSoftwareFormat(&mRate, &mFormat, &mChannels, 0, 0, &mBits); + if (result != FMOD_OK) + { + return result; + } + + result = SoundI::getBytesFromSamples(dspbufferlength, &mBufferLengthBytes, mChannels, mFormat); + if (result != FMOD_OK) + { + return result; + } + + mBuffer = FMOD_Memory_Calloc(mBufferLengthBytes); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + if (!extradriverdata) + { + FMOD_strncpy(mFileName, "fmodoutput.wav", FMOD_STRING_MAXPATHLEN); + } + else + { + FMOD_strncpy(mFileName, (char *)extradriverdata, FMOD_STRING_MAXPATHLEN); + } + + mFP = fopen(mFileName, "wb"); + if (!mFP) + { + return FMOD_ERR_FILE_NOTFOUND; + } + + result = writeWavHeader(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::close() +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + writeWavHeader(); + + if (mFP) + { + fclose(mFP); + mFP = 0; + } + + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + mBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::getPosition(unsigned int *pcm) +{ + FMOD_RESULT result; + unsigned int pos = 0; + int outputrate; + + /* + Get the values FMOD had set for rate, format, channels. + */ + result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + FMOD_OS_Time_GetMs(&pos); + + pos *= outputrate; + pos /= 1000; + + *pcm = pos; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + offset %= mBufferLengthBytes; + + if (offset + length > mBufferLengthBytes) + { + *ptr1 = (char *)mBuffer + offset; + *ptr2 = (char *)mBuffer; + *len1 = mBufferLengthBytes - offset; + *len2 = length - (mBufferLengthBytes - offset); + } + else + { + *ptr1 = (char *)mBuffer + offset; + *ptr2 = NULL; + *len1 = length; + *len2 = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + unsigned int count = 0; + + if (ptr1 && len1) + { + if (mFormat == FMOD_SOUND_FORMAT_PCM8) + { + signed char *ptr = (signed char *)ptr1; + + for (count = 0; count < len1; count++) + { + ptr[count] ^= 128; + } + } +#ifdef PLATFORM_ENDIAN_BIG + else if (mFormat == FMOD_SOUND_FORMAT_PCM16) + { + signed short *wptr = (signed short *)ptr1; + + for (count = 0; count < len1 >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + } + else if (mFormat == FMOD_SOUND_FORMAT_PCM32 || mFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + signed int *dwptr = (signed int *)ptr1; + + for (count = 0; count < len1 >> 2; count++) + { + dwptr[count] = FMOD_SWAPENDIAN_DWORD(dwptr[count]); + } + } +#endif + + int written = fwrite(ptr1, 1, len1, mFP); + mLengthBytes += written; + } + + if (ptr2 && len2) + { + if (mFormat == FMOD_SOUND_FORMAT_PCM8) + { + signed char *ptr = (signed char *)ptr2; + + for (count = 0; count < len2; count++) + { + ptr[count] ^= 128; + } + } +#ifdef PLATFORM_ENDIAN_BIG + else if (mFormat == FMOD_SOUND_FORMAT_PCM16) + { + signed short *wptr = (signed short *)ptr2; + + for (count = 0; count < len2 >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + } + else if (mFormat == FMOD_SOUND_FORMAT_PCM32 || mFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + signed int *dwptr = (signed int *)ptr2; + + for (count = 0; count < len2 >> 2; count++) + { + dwptr[count] = FMOD_SWAPENDIAN_DWORD(dwptr[count]); + } + } +#endif + + int written = fwrite(ptr2, 1, len2, mFP); + mLengthBytes += written; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::getHandle(void **handle) +{ + *handle = mFP; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::writeWavHeader() +{ + if (!mFP) + { + return FMOD_ERR_INVALID_PARAM; + } + + fseek(mFP, 0, SEEK_SET); + + { + bool extensible = false; + + WAVE_CHUNK FmtHdr = + { + { 'f','m','t',' '}, + sizeof(WAVE_FORMATEXTENSIBLE) + }; + + if (mFormat == FMOD_SOUND_FORMAT_PCMFLOAT && mChannels > 2) + { + extensible = true; + } + + WAVE_FORMATEXTENSIBLE Fmt = + { + { + extensible ? WAVE_FORMAT_EXTENSIBLE : mFormat == FMOD_SOUND_FORMAT_PCMFLOAT ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM, + mChannels, + mRate, + mRate * mChannels * mBits / 8, + mChannels * mBits / 8, + mBits, + 0 + }, + 0 + }; + + if (Fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + Fmt.Format.cbSize = 22; + Fmt.Samples.wValidBitsPerSample = mBits; + Fmt.dwChannelMask = 0; /* FIXME - this should be set according to mulchaud.rtf */ + if (mFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + FMOD_memcpy(&Fmt.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FMOD_GUID)); + } + else + { + FMOD_memcpy(&Fmt.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(FMOD_GUID)); + } + } + + WAVE_CHUNK DataChunk = + { + {'d','a','t','a'}, mLengthBytes /* size is unknown at the moment so just make it huge */ + }; + + WAVE_CHUNK WavHeader = + { + {'R','I','F','F'}, + sizeof(FmtHdr) + sizeof(Fmt) + sizeof(DataChunk) + mLengthBytes, + }; + +#ifdef PLATFORM_ENDIAN_BIG + FmtHdr.size = FMOD_SWAPENDIAN_DWORD(FmtHdr.size); + DataChunk.size = FMOD_SWAPENDIAN_DWORD(DataChunk.size); + WavHeader.size = FMOD_SWAPENDIAN_DWORD(WavHeader.size); + + Fmt.Format.wFormatTag = FMOD_SWAPENDIAN_WORD(Fmt.Format.wFormatTag); + Fmt.Format.nChannels = FMOD_SWAPENDIAN_WORD(Fmt.Format.nChannels); + Fmt.Format.nSamplesPerSec = FMOD_SWAPENDIAN_DWORD(Fmt.Format.nSamplesPerSec); + Fmt.Format.nAvgBytesPerSec = FMOD_SWAPENDIAN_DWORD(Fmt.Format.nAvgBytesPerSec); + Fmt.Format.nBlockAlign = FMOD_SWAPENDIAN_WORD(Fmt.Format.nBlockAlign); + Fmt.Format.wBitsPerSample = FMOD_SWAPENDIAN_WORD(Fmt.Format.wBitsPerSample); + Fmt.Format.cbSize = FMOD_SWAPENDIAN_WORD(Fmt.Format.cbSize); + + Fmt.Samples.wValidBitsPerSample = FMOD_SWAPENDIAN_WORD(Fmt.Samples.wValidBitsPerSample); + Fmt.dwChannelMask = FMOD_SWAPENDIAN_DWORD(Fmt.dwChannelMask); + + Fmt.SubFormat.Data1 = FMOD_SWAPENDIAN_DWORD(Fmt.SubFormat.Data1); + Fmt.SubFormat.Data2 = FMOD_SWAPENDIAN_WORD(Fmt.SubFormat.Data2); + Fmt.SubFormat.Data3 = FMOD_SWAPENDIAN_WORD(Fmt.SubFormat.Data3); +#endif + + int written = 0; + written = fwrite(&WavHeader, sizeof(WavHeader), 1, mFP); + written = fwrite("WAVE", 4, 1, mFP); + written = fwrite(&FmtHdr, sizeof(FmtHdr), 1, mFP); + written = fwrite(&Fmt, sizeof(Fmt), 1, mFP); + written = fwrite(&DataChunk, sizeof(DataChunk), 1, mFP); + } + + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->getDriverName(id, name, namelen); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter::getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->getDriverCaps(id, caps); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::getPositionCallback(FMOD_OUTPUT_STATE *output, unsigned int *pcm) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->getPosition(pcm); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::lockCallback(FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->lock(offset, length, ptr1, ptr2, len1, len2); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::unlockCallback(FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->unlock(ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputWavWriter *wavwriter = (OutputWavWriter *)output; + + return wavwriter->getHandle(handle); +} + + + + +} + + +#endif + diff --git a/src/fmod_output_wavwriter.h b/src/fmod_output_wavwriter.h new file mode 100755 index 0000000..040c093 --- /dev/null +++ b/src/fmod_output_wavwriter.h @@ -0,0 +1,58 @@ +#ifndef _FMOD_OUTPUT_WAVWRITER_H +#define _FMOD_OUTPUT_WAVWRITER_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WAVWRITER + +#include "fmod_output_polled.h" +#include "fmod_codec_wav.h" + +#include <stdio.h> + +namespace FMOD +{ + class OutputWavWriter : public OutputPolled + { + private: + + char mFileName[FMOD_STRING_MAXPATHLEN]; + void *mBuffer; + unsigned int mBufferLengthBytes; + int mChannels, mRate, mBits; + unsigned int mLengthBytes; + FMOD_SOUND_FORMAT mFormat; + FILE *mFP; + + FMOD_RESULT writeWavHeader(); + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverName(int id, char *name, int namelen); + FMOD_RESULT getDriverCaps(int id, FMOD_CAPS *caps); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT getHandle(void **handle); + FMOD_RESULT getPosition(unsigned int *pcm); + FMOD_RESULT lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + + static FMOD_RESULT F_CALLBACK getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen); + static FMOD_RESULT F_CALLBACK getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getPositionCallback (FMOD_OUTPUT_STATE *output, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK lockCallback (FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + static FMOD_RESULT F_CALLBACK unlockCallback (FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + }; +} + +#endif /* #ifdef FMOD_SUPPORT_WAVWRITER */ + +#endif + diff --git a/src/fmod_output_wavwriter_nrt.cpp b/src/fmod_output_wavwriter_nrt.cpp new file mode 100755 index 0000000..6a7ef38 --- /dev/null +++ b/src/fmod_output_wavwriter_nrt.cpp @@ -0,0 +1,736 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WAVWRITER_NRT + +#include "fmod_memory.h" +#include "fmod_output_wavwriter_nrt.h" +#include "fmod_string.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +FMOD_OUTPUT_DESCRIPTION_EX wavwriteroutput_nrt; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputWavWriter_NRT::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 } }; +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 } }; + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputWavWriter_NRT::getDescriptionEx() +{ + FMOD_memset(&wavwriteroutput_nrt, 0, sizeof(FMOD_OUTPUT_DESCRIPTION)); + + wavwriteroutput_nrt.name = "FMOD WavWriter Output - Non real-time"; + wavwriteroutput_nrt.version = 0x00010100; + wavwriteroutput_nrt.polling = false; + wavwriteroutput_nrt.getnumdrivers = &OutputWavWriter_NRT::getNumDriversCallback; + wavwriteroutput_nrt.getdrivername = &OutputWavWriter_NRT::getDriverNameCallback; + wavwriteroutput_nrt.getdrivercaps = &OutputWavWriter_NRT::getDriverCapsCallback; + wavwriteroutput_nrt.init = &OutputWavWriter_NRT::initCallback; + wavwriteroutput_nrt.close = &OutputWavWriter_NRT::closeCallback; + wavwriteroutput_nrt.start = &OutputWavWriter_NRT::startCallback; + wavwriteroutput_nrt.stop = &OutputWavWriter_NRT::stopCallback; + wavwriteroutput_nrt.update = &OutputWavWriter_NRT::updateCallback; + wavwriteroutput_nrt.gethandle = &OutputWavWriter_NRT::getHandleCallback; + + /* + Private members + */ + wavwriteroutput_nrt.mType = FMOD_OUTPUTTYPE_WAVWRITER_NRT; + wavwriteroutput_nrt.mSize = sizeof(OutputWavWriter_NRT); + + return &wavwriteroutput_nrt; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::getNumDrivers(int *numdrivers) +{ + *numdrivers = 1; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::getDriverName(int driver, char *name, int namelen) +{ + FMOD_strncpy(name, "fmodoutput.wav", namelen); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::getDriverCaps(int id, FMOD_CAPS *caps) +{ + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_MULTICHANNEL); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM8); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM16); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM24); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM32); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + FMOD_RESULT result; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + result = mSystem->getSoftwareFormat(&mRate, &mFormat, &mChannels, 0, 0, &mBits); + if (result != FMOD_OK) + { + return result; + } + + mBufferLength = dspbufferlength; + + result = SoundI::getBytesFromSamples(mBufferLength, &mBufferLengthBytes, mChannels, mFormat); + if (result != FMOD_OK) + { + return result; + } + + mBuffer = FMOD_Memory_Calloc(mBufferLengthBytes); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + if (!extradriverdata) + { + FMOD_strncpy(mFileName, "fmodoutput.wav", FMOD_STRING_MAXPATHLEN); + } + else + { + FMOD_strncpy(mFileName, (char *)extradriverdata, FMOD_STRING_MAXPATHLEN); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::close() +{ + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + mBuffer = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::getHandle(void **handle) +{ + *handle = mFP; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::update() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result = FMOD_OK; + unsigned int count = 0; + + result = mix(mBuffer, mBufferLength); + if (result != FMOD_OK) + { + return FMOD_OK; + } + + if (mFormat == FMOD_SOUND_FORMAT_PCM8) + { + signed char *ptr = (signed char *)mBuffer; + + for (count = 0; count < mBufferLengthBytes; count++) + { + ptr[count] ^= 128; + } + } +#ifdef PLATFORM_ENDIAN_BIG + else if (mFormat == FMOD_SOUND_FORMAT_PCM16) + { + signed short *wptr = (signed short *)mBuffer; + + for (count = 0; count < mBufferLengthBytes >> 1; count++) + { + wptr[count] = FMOD_SWAPENDIAN_WORD(wptr[count]); + } + } + else if (mFormat == FMOD_SOUND_FORMAT_PCM32 || mFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + signed int *dwptr = (signed int *)mBuffer; + + for (count = 0; count < mBufferLengthBytes >> 2; count++) + { + dwptr[count] = FMOD_SWAPENDIAN_DWORD(dwptr[count]); + } + } +#endif + + int written = fwrite(mBuffer, 1, mBufferLengthBytes, mFP); + mLengthBytes += written; +#endif + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::writeWavHeader() +{ + if (!mFP) + { + return FMOD_ERR_INVALID_PARAM; + } + + fseek(mFP, 0, SEEK_SET); + + { + bool extensible = false; + + WAVE_CHUNK FmtHdr = + { + { 'f','m','t',' '}, + sizeof(WAVE_FORMATEXTENSIBLE) + }; + + if (mFormat == FMOD_SOUND_FORMAT_PCMFLOAT && mChannels > 2) + { + extensible = true; + } + + WAVE_FORMATEXTENSIBLE Fmt = + { + { + extensible ? WAVE_FORMAT_EXTENSIBLE : mFormat == FMOD_SOUND_FORMAT_PCMFLOAT ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM, + mChannels, + mRate, + mRate * mChannels * mBits / 8, + mChannels * mBits / 8, + mBits, + 0 + }, + 0 + }; + + if (Fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + Fmt.Format.cbSize = 22; + Fmt.Samples.wValidBitsPerSample = mBits; + Fmt.dwChannelMask = 0; /* FIXME - this should be set according to mulchaud.rtf */ + if (mFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + FMOD_memcpy(&Fmt.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FMOD_GUID)); + } + else + { + FMOD_memcpy(&Fmt.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(FMOD_GUID)); + } + } + + WAVE_CHUNK DataChunk = + { + {'d','a','t','a'}, mLengthBytes /* size is unknown at the moment so just make it huge */ + }; + + WAVE_CHUNK WavHeader = + { + {'R','I','F','F'}, + sizeof(FmtHdr) + sizeof(Fmt) + sizeof(DataChunk) + mLengthBytes, + }; + +#ifdef PLATFORM_ENDIAN_BIG + FmtHdr.size = FMOD_SWAPENDIAN_DWORD(FmtHdr.size); + DataChunk.size = FMOD_SWAPENDIAN_DWORD(DataChunk.size); + WavHeader.size = FMOD_SWAPENDIAN_DWORD(WavHeader.size); + + Fmt.Format.wFormatTag = FMOD_SWAPENDIAN_WORD(Fmt.Format.wFormatTag); + Fmt.Format.nChannels = FMOD_SWAPENDIAN_WORD(Fmt.Format.nChannels); + Fmt.Format.nSamplesPerSec = FMOD_SWAPENDIAN_DWORD(Fmt.Format.nSamplesPerSec); + Fmt.Format.nAvgBytesPerSec = FMOD_SWAPENDIAN_DWORD(Fmt.Format.nAvgBytesPerSec); + Fmt.Format.nBlockAlign = FMOD_SWAPENDIAN_WORD(Fmt.Format.nBlockAlign); + Fmt.Format.wBitsPerSample = FMOD_SWAPENDIAN_WORD(Fmt.Format.wBitsPerSample); + Fmt.Format.cbSize = FMOD_SWAPENDIAN_WORD(Fmt.Format.cbSize); + + Fmt.Samples.wValidBitsPerSample = FMOD_SWAPENDIAN_WORD(Fmt.Samples.wValidBitsPerSample); + Fmt.dwChannelMask = FMOD_SWAPENDIAN_DWORD(Fmt.dwChannelMask); + + Fmt.SubFormat.Data1 = FMOD_SWAPENDIAN_DWORD(Fmt.SubFormat.Data1); + Fmt.SubFormat.Data2 = FMOD_SWAPENDIAN_WORD(Fmt.SubFormat.Data2); + Fmt.SubFormat.Data3 = FMOD_SWAPENDIAN_WORD(Fmt.SubFormat.Data3); +#endif + + int written = 0; + written = fwrite(&WavHeader, sizeof(WavHeader), 1, mFP); + written = fwrite("WAVE", 4, 1, mFP); + written = fwrite(&FmtHdr, sizeof(FmtHdr), 1, mFP); + written = fwrite(&Fmt, sizeof(Fmt), 1, mFP); + written = fwrite(&DataChunk, sizeof(DataChunk), 1, mFP); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::start() +{ + FMOD_RESULT result; + + mFP = fopen(mFileName, "wb"); + if (!mFP) + { + return FMOD_ERR_FILE_NOTFOUND; + } + + result = writeWavHeader(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::stop() +{ + writeWavHeader(); + + if (mFP) + { + fclose(mFP); + mFP = 0; + } + + return FMOD_OK; +} + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->getDriverName(id, name, namelen); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWavWriter_NRT::getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->getDriverCaps(id, caps); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::startCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->start(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::stopCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::updateCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->update(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWavWriter_NRT::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputWavWriter_NRT *wavwriter = (OutputWavWriter_NRT *)output; + + return wavwriter->getHandle(handle); +} + +} + + +#endif + diff --git a/src/fmod_output_wavwriter_nrt.h b/src/fmod_output_wavwriter_nrt.h new file mode 100755 index 0000000..428cbae --- /dev/null +++ b/src/fmod_output_wavwriter_nrt.h @@ -0,0 +1,59 @@ +#ifndef _FMOD_OUTPUT_WAVWRITER_NRT_H +#define _FMOD_OUTPUT_WAVWRITER_NRT_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WAVWRITER_NRT + +#include "fmod_outputi.h" +#include "fmod_codec_wav.h" + +#include <stdio.h> + +namespace FMOD +{ + class OutputWavWriter_NRT : public Output + { + private: + + char mFileName[FMOD_STRING_MAXPATHLEN]; + void *mBuffer; + unsigned int mBufferLength; + unsigned int mBufferLengthBytes; + int mChannels, mRate, mBits; + unsigned int mLengthBytes; + FMOD_SOUND_FORMAT mFormat; + FILE *mFP; + + FMOD_RESULT writeWavHeader(); + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverName(int id, char *name, int namelen); + FMOD_RESULT getDriverCaps(int id, FMOD_CAPS *caps); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT getHandle(void **handle); + FMOD_RESULT update(); + FMOD_RESULT start(); + FMOD_RESULT stop(); + + static FMOD_RESULT F_CALLBACK getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen); + static FMOD_RESULT F_CALLBACK getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK startCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK stopCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK updateCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + }; +} + +#endif /* #ifdef FMOD_SUPPORT_WAVWRITER_NRT */ + +#endif + diff --git a/src/fmod_outputi.h b/src/fmod_outputi.h new file mode 100755 index 0000000..e62ec30 --- /dev/null +++ b/src/fmod_outputi.h @@ -0,0 +1,182 @@ +#ifndef _FMOD_OUTPUTI_H +#define _FMOD_OUTPUTI_H + +#include "fmod_settings.h" + +#include "fmod_channelpool.h" +#include "fmod_channelgroupi.h" +#include "fmod_dsp_resampler.h" +#include "fmod_output.h" +#include "fmod_linkedlist.h" +#include "fmod_os_misc.h" +#include "fmod_plugin.h" + +#define FMOD_OUTPUT_MAXDRIVERS 32 + +namespace FMOD +{ + class Sample; + class MemoryTracker; + struct FMOD_RECORDING_INFO; + + static const FMOD_OUTPUTTYPE FMOD_OUTPUTTYPE_EMULATED = (FMOD_OUTPUTTYPE)-1; + static const FMOD_OUTPUTTYPE FMOD_OUTPUTTYPE_SOFTWARE = (FMOD_OUTPUTTYPE)-2; + + typedef int (F_CALLBACK *FMOD_OUTPUT_GETSAMPLEMAXCHANNELS) (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETDRIVERINFOCALLBACK) (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETDRIVERINFOWCALLBACK) (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETDRIVERCAPSEXCALLBACK) (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETDRIVERCAPSEX2CALLBACK) (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_INITEXCALLBACK) (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_STARTCALLBACK) (FMOD_OUTPUT_STATE *output); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_STOPCALLBACK) (FMOD_OUTPUT_STATE *output); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_UPDATEFINISHEDCALLBACK) (FMOD_OUTPUT_STATE *output); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_CREATESAMPLECALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETSOUNDRAMCALLBACK) (FMOD_OUTPUT_STATE *output, int *currentalloced, int *maxalloced, int *total); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_RECORDGETDRIVERCAPSCALLBACK)(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_RECORDSTARTCALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_RECORDSTOPCALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_RECORDGETPOSITIONCALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_RECORDLOCKCALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_RECORDUNLOCKCALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_SETREVERBCALLBACK) (FMOD_OUTPUT_STATE *output, const FMOD_REVERB_PROPERTIES *prop); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETREVERBCALLBACK) (FMOD_OUTPUT_STATE *output, FMOD_REVERB_PROPERTIES *prop); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_POSTMIXCALLBACK) (FMOD_OUTPUT_STATE *output); + typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETMEMORYUSED) (FMOD_OUTPUT_STATE *output, MemoryTracker *tracker); + + + + struct FMOD_OUTPUT_DESCRIPTION_EX : public FMOD_OUTPUT_DESCRIPTION, public LinkedListNode + { + FMOD_OUTPUTTYPE mType; + int mSize; + FMOD_OS_LIBRARY *mModule; + unsigned int mHandle; + + FMOD_OUTPUT_GETSAMPLEMAXCHANNELS getsamplemaxchannels; + FMOD_OUTPUT_GETDRIVERINFOCALLBACK getdriverinfo; + FMOD_OUTPUT_GETDRIVERINFOWCALLBACK getdriverinfow; + FMOD_OUTPUT_GETDRIVERCAPSEXCALLBACK getdrivercapsex; + FMOD_OUTPUT_GETDRIVERCAPSEX2CALLBACK getdrivercapsex2; + FMOD_OUTPUT_INITEXCALLBACK initex; + FMOD_OUTPUT_STARTCALLBACK start; /* [in] Initialization function for the output device to start accepting audio data from the FMOD software mixer. This is called from System::init. */ + FMOD_OUTPUT_STOPCALLBACK stop; /* [in] Initialization function for the output device to stop accepting audio data from FMOD the software mixer. This is called from System::close. */ + FMOD_OUTPUT_UPDATEFINISHEDCALLBACK updatefinished; /* [in] Update finished function. PS2 only. */ + FMOD_OUTPUT_CREATESAMPLECALLBACK createsample; + FMOD_OUTPUT_GETSOUNDRAMCALLBACK getsoundram; /* [in] For information about sound ram usage. This callback is to give System::getSoundRam somethign to return */ + FMOD_OUTPUT_POSTMIXCALLBACK postmixcallback; /* [in] Called within mixer update, after the mix has happened, and within SystemI::mDSPCrit. */ + FMOD_OUTPUT_GETNUMDRIVERSCALLBACK record_getnumdrivers; /* [in] For recording device enumeration. This callback is to give System::getRecordNumDrivers somthing to return. */ + FMOD_OUTPUT_GETDRIVERINFOCALLBACK record_getdriverinfo; /* [in] For recording device enumeration. This callback is to give System::getRecordDriverInfo somthing to return. */ + FMOD_OUTPUT_GETDRIVERINFOWCALLBACK record_getdriverinfow; /* [in] For recording device enumeration. This callback is to give System::getRecordDriverInfo somthing to return. */ + FMOD_OUTPUT_RECORDGETDRIVERCAPSCALLBACK record_getdrivercaps; /* [in] For recording device enumeration. This callback is to give System::getRecordDriverCaps somthing to return. */ + FMOD_OUTPUT_RECORDSTARTCALLBACK record_start; /* [in] Initialization function for the recording device to start accepting audio data from the FMOD software mixer. This is called from System::recordStart. */ + FMOD_OUTPUT_RECORDSTOPCALLBACK record_stop; /* [in] Initialization function for the recording device to stop accepting audio data from FMOD the software mixer. This is called from System::recordStop. */ + FMOD_OUTPUT_RECORDGETPOSITIONCALLBACK record_getposition; /* [in] This is called from the FMOD software mixer thread. This returns a position value in samples so that FMOD knows where and when to fill its record buffer. */ + FMOD_OUTPUT_RECORDLOCKCALLBACK record_lock; /* [in] This is called from the FMOD software mixer thread. This function provides a pointer to data that FMOD can read from when updating its internal record buffer. */ + FMOD_OUTPUT_RECORDUNLOCKCALLBACK record_unlock; /* [in] This is called from the FMOD software mixer thread. This optional function accepts the data that has been read does whatever post read operation nescessary. */ + FMOD_OUTPUT_SETREVERBCALLBACK reverb_setproperties; /* [in] This is called from System::setReverbProperties. */ + FMOD_OUTPUT_GETMEMORYUSED getmemoryused; + }; + + class ChannelPool; + class ChannelReal; + class SoundI; + + struct FMOD_RECORDING_INFO : public LinkedListNode + { + int mRecordId; + int mRecordDriver; /* MGB Deprecated - This is a GUID, should be implemented via getDriverInfo */ + FMOD_GUID mRecordGUID; + bool mRecordFinished; + + int mRecordOffset; + bool mRecordLoop; + FMOD_SOUND_FORMAT mRecordFormat; + unsigned int mRecordLastCursorPos; + unsigned int mRecordBufferLength; + SoundI *mRecordSound; + int mRecordRate; + int mRecordChannels; + + DSPResampler *mRecordResamplerDSP; + float *mRecordTempBuffer; + unsigned int mRecordTempBufferLength; + void *mRecordPlatformSpecific; + }; + + class Output : public Plugin, public FMOD_OUTPUT_STATE + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class SystemI; + friend class PluginFactory; + friend class SoundI; + friend class ChannelGroupI; + #if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL) + friend class ProfileChannel; + #endif + + public: + + ChannelPool *mChannelPool; /* Used for generic 2d/3d channels, or 2d channels only */ + + protected: + + bool mEnumerated; + bool mPolling; + SystemI *mSystem; + ChannelPool *mChannelPool3D; /* Used for 3d channels only */ + int mMixAheadBlocks; + ChannelGroupI *mMusicChannelGroup; /* Music channelgroup for Xbox360 */ + + int mNum2DChannelsFromCaps; + int mNum3DChannelsFromCaps; + int mTotalChannelsFromCaps; + int mDSPTick; + +#ifdef FMOD_SUPPORT_RECORDING + bool mRecordEnumerated; + int mRecordNumDrivers; + int mRecordNumActive; + + FMOD_OS_CRITICALSECTION *mRecordInfoCrit; + FMOD_RECORDING_INFO mRecordInfoHead; + FMOD_RECORDING_INFO *mRecordInfoForResampler; + + FMOD_RESULT recordRead(FMOD_RECORDING_INFO *recordinfo, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK recordResamplerReadCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT recordFill(FMOD_RECORDING_INFO *recordinfo, unsigned int length); + FMOD_RESULT recordUpdate(); + FMOD_RESULT recordGetInfo(int id, FMOD_RECORDING_INFO **info); + FMOD_RESULT recordStop(FMOD_RECORDING_INFO *recordinfo); + FMOD_RESULT recordStopAll(bool finishedonly); +#endif + + public: + + FMOD_OUTPUT_DESCRIPTION_EX mDescription; + + Output(); + + virtual FMOD_RESULT release(); + +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT mix(void *buffer, unsigned int numsamples); +#endif + +#ifndef PLATFORM_PS3_SPU + virtual FMOD_RESULT getFreeChannel(FMOD_MODE mode, ChannelReal **realchannel, int numchannels, int numsoundchannels, int *found, bool ignorereserved = false); +#endif + +#ifdef FMOD_SUPPORT_HARDWAREXM + FMOD_RESULT reserveVoice(int voice, bool reserved); +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE + static FMOD_RESULT F_CALLBACK mixCallback(FMOD_OUTPUT_STATE *output, void *buffer, unsigned int length); +#endif + }; +} + +#endif + diff --git a/src/fmod_plugin.cpp b/src/fmod_plugin.cpp new file mode 100755 index 0000000..ab612c1 --- /dev/null +++ b/src/fmod_plugin.cpp @@ -0,0 +1,39 @@ +#include "fmod_settings.h" + +#include "fmod_plugin.h" +#include "fmod_file.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh + + [SEE_ALSO] +] +*/ +FMOD_RESULT Plugin::release() +{ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Plugin::release", "(%p)\n", this)); + + FMOD_Memory_Free(this); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Plugin::release", "done\n")); + + return FMOD_OK; +} + + +} + diff --git a/src/fmod_plugin.h b/src/fmod_plugin.h new file mode 100755 index 0000000..73b144a --- /dev/null +++ b/src/fmod_plugin.h @@ -0,0 +1,52 @@ +#ifndef _FMOD_PLUGIN_H +#define _FMOD_PLUGIN_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_debug.h" +#include "fmod_globals.h" +#include "fmod_linkedlist.h" +#include "fmod_memory.h" +#include "fmod_os_misc.h" +#include "fmod_string.h" + +#include <string.h> + +namespace FMOD +{ + class File; + class MemPool; + + class Plugin : public SortedLinkedListNode + { + friend class SystemI; + + public: + + SystemI *mSystem; + + /* + Stuff that is passed into the driver for internal use + */ + Global *mGlobal; + + public: + + Plugin() + { + mGlobal = gGlobal; + } + + virtual FMOD_RESULT release(); + + FMOD_RESULT init() + { + gGlobal = mGlobal; + return FMOD_OK; + } + }; +} + +#endif + diff --git a/src/fmod_pluginfactory.cpp b/src/fmod_pluginfactory.cpp new file mode 100755 index 0000000..ee7548e --- /dev/null +++ b/src/fmod_pluginfactory.cpp @@ -0,0 +1,2036 @@ +#include "fmod_settings.h" + +#include "fmod_pluginfactory.h" +#include "fmod_codeci.h" +#ifdef FMOD_SUPPORT_DSPCODEC +#include "fmod_dsp_codec.h" +#endif +#include "fmod_dsp_oscillator.h" +#include "fmod_dsp_resampler.h" +#include "fmod_dsp_resampler_multiinput.h" +#include "fmod_dsp_soundcard.h" +#ifdef FMOD_SUPPORT_VSTPLUGIN +#include "fmod_dsp_vstplugin.h" +#endif +#include "fmod_dsp_wavetable.h" +#ifdef FMOD_SUPPORT_WINAMPPLUGIN +#include "fmod_dsp_winampplugin.h" +#endif +#ifdef FMOD_SUPPORT_OPENAL +#include "fmod_output_openal.h" +#endif +#include "fmod_output_polled.h" +#include "fmod_memory.h" +#include "fmod_file.h" +#include "fmod_os_misc.h" +#include "fmod_systemi.h" + +#ifdef PLATFORM_PS3 +#include "fmod_codec_mpeg_pic.h" +#include "fmod_common_spu.h" +#include "fmod_output_ps3.h" +#endif + +#include <stdio.h> + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +PluginFactory::PluginFactory() +{ + mSystem = 0; + mCurrentPluginHandle = 1; + FMOD_memset(mPluginPath, 0, FMOD_STRING_MAXPATHLEN); + + mDSPHead.initNode(); + mCodecHead.initNode(); + mOutputHead.initNode(); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::release() +{ + FMOD_RESULT result; + int num, count; + + /* + Cleanup codecs + */ + result = getNumCodecs(&num); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < num; count++) + { + unsigned int handle; + + getCodecHandle(0, &handle); + + result = unloadPlugin(handle); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Cleanup DSPs + */ + result = getNumDSPs(&num); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < num; count++) + { + unsigned int handle; + + getDSPHandle(0, &handle); + + result = unloadPlugin(handle); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Cleanup Outputs + */ + result = getNumOutputs(&num); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < num; count++) + { + unsigned int handle; + + getOutputHandle(0, &handle); + + result = unloadPlugin(handle); + if (result != FMOD_OK) + { + return result; + } + } + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::setSystem(SystemI *system) +{ + mSystem = system; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getSystem(SystemI **system) +{ + *system = mSystem; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::setPluginPath(const char *path) +{ + if (FMOD_strlen(path) >= FMOD_STRING_MAXPATHLEN) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_strncpy(mPluginPath, path, FMOD_STRING_MAXPATHLEN); + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_DLLS + +FMOD_RESULT PluginFactory::tryLoadPlugin(const char *dllname, unsigned int *handle, bool calledinternally, unsigned int priority) +{ + FMOD_RESULT result = loadPlugin(dllname, handle, calledinternally, priority); + + if (result != FMOD_ERR_FILE_NOTFOUND && result != FMOD_ERR_FILE_BAD) + { + CHECK_RESULT(result); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::loadPlugin(const char *dllname, unsigned int *handle, bool calledinternally, unsigned int priority) +{ + FMOD_RESULT result; + FMOD_OS_LIBRARY *dllhandle; + FMOD_CODEC_DESCRIPTION *(F_CALLBACK *getcodecdesc )() = 0; + FMOD_CODEC_DESCRIPTION_EX *(F_CALLBACK *getcodecdescex )() = 0; + FMOD_DSP_DESCRIPTION *(F_CALLBACK *getdspdesc )() = 0; + FMOD_DSP_DESCRIPTION_EX *(F_CALLBACK *getdspdescex )() = 0; + FMOD_OUTPUT_DESCRIPTION *(F_CALLBACK *getoutputdesc )() = 0; + FMOD_OUTPUT_DESCRIPTION_EX *(F_CALLBACK *getoutputdescex)() = 0; +#ifdef FMOD_SUPPORT_VSTPLUGIN + AEffect *(VSTCALLBACK *VSTmain)(audioMasterCallback) = 0; +#endif +#ifdef FMOD_SUPPORT_WINAMPPLUGIN + winampDSPHeader *( *winampGetHeader)() = 0; +#endif + char path[FMOD_STRING_MAXPATHLEN]; + const char *beginning; + const char *end; + char symbol[50]; + + FMOD_strncpy(path, mPluginPath, FMOD_STRING_MAXPATHLEN); + if (FMOD_strlen(path) && path[FMOD_strlen(path) - 1] != '\\' && path[FMOD_strlen(path) - 1] != '/') + { + FMOD_strcat(path, "/"); + } + FMOD_strcat(path, dllname); + + #if defined(PLATFORM_WINDOWS64) || defined(PLATFORM_LINUX64) + if (calledinternally) + { + FMOD_strcat(path, "64"); + } + #endif + + if (FMOD_strlen(path)) + { + #if defined(PLATFORM_WINDOWS) + if (FMOD_strncmp(&path[FMOD_strlen(path)-4], ".dll", 4)) + { + FMOD_strcat(path, ".dll"); + } + #elif defined(PLATFORM_MAC) + if (FMOD_strncmp(&path[FMOD_strlen(path)-6], ".dylib", 6)) + { + /* + Could be a VST / Bundle + */ + if ((FMOD_strncmp(&path[FMOD_strlen(path)-4], ".vst", 4)) && (FMOD_strncmp(&path[FMOD_strlen(path)-7], ".bundle", 7))) + { + FMOD_strcat(path, ".dylib"); + } + } + #elif defined(PLATFORM_LINUX) || defined(PLATFORM_SOLARIS) + if (FMOD_strncmp(&path[FMOD_strlen(path)-3], ".so", 3)) + { + FMOD_strcat(path, ".so"); + } + #endif + } + + result = FMOD_OS_Library_Load(path, &dllhandle); + if (result != FMOD_OK) + { + #if defined(PLATFORM_WINDOWS64) || defined(PLATFORM_LINUX64) + if (calledinternally) + { + char dllname64[64]; /* No internal FMOD plugin names are more than 62 characters */ + FMOD_strcpy(dllname64, dllname); + FMOD_strcat(dllname64, "64"); + result = FMOD_OS_Library_Load(dllname64, &dllhandle); /* Try without the search path, in case they were actually using a relative path to the app */ + } + #else + result = FMOD_OS_Library_Load(dllname, &dllhandle); /* Try without the search path, in case they were actually using a relative path to the app */ + #endif + if (result != FMOD_OK) + { + return result; + } + } + + #if defined(PLATFORM_LINUX) || defined(PLATFORM_WINDOWS64) + beginning = ""; + end = ""; + #elif defined(PLATFORM_WINDOWS) + beginning = "_"; + end = "@0"; + #elif defined(PLATFORM_MAC_PPC) + beginning = "_"; + end = ""; + #else + beginning = ""; + end = ""; + #endif + + sprintf(symbol, "%sFMODGetCodecDescription%s", beginning, end); + + result = FMOD_OS_Library_GetProcAddress(dllhandle, symbol, (void **)&getcodecdesc); + if (result == FMOD_OK) + { + return registerCodec(getcodecdesc(), handle, priority); + } + + sprintf(symbol, "%sFMODGetCodecDescriptionEx%s", beginning, end); + result = FMOD_OS_Library_GetProcAddress(dllhandle, symbol, (void **)&getcodecdescex); + if (result == FMOD_OK) + { + return registerCodec(getcodecdescex(), handle, priority); + } + + sprintf(symbol, "%sFMODGetDSPDescription%s", beginning, end); + result = FMOD_OS_Library_GetProcAddress(dllhandle, symbol, (void **)&getdspdesc); + if (result == FMOD_OK) + { + return registerDSP(getdspdesc(), handle); + } + + sprintf(symbol, "%sFMODGetDSPDescriptionEx%s", beginning, end); + result = FMOD_OS_Library_GetProcAddress(dllhandle, symbol, (void **)&getdspdescex); + if (result == FMOD_OK) + { + return registerDSP(getdspdescex(), handle); + } + + sprintf(symbol, "%sFMODGetOutputDescription%s", beginning, end); + result = FMOD_OS_Library_GetProcAddress(dllhandle, symbol, (void **)&getoutputdesc); + if (result == FMOD_OK) + { + return registerOutput(getoutputdesc(), handle); + } + + sprintf(symbol, "%sFMODGetOutputDescriptionEx%s", beginning, end); + result = FMOD_OS_Library_GetProcAddress(dllhandle, symbol, (void **)&getoutputdescex); + if (result == FMOD_OK) + { + return registerOutput(getoutputdescex(), handle); + } + +#ifdef FMOD_SUPPORT_VSTPLUGIN + result = FMOD_OS_Library_GetProcAddress(dllhandle, "VSTPluginMain", (void **)&VSTmain); // VST 2.4 and onward use VSTPluginMain + if (result != FMOD_OK) + { + #ifdef PLATFORM_MAC + result = FMOD_OS_Library_GetProcAddress(dllhandle, "main_macho", (void **)&VSTmain); + #else + result = FMOD_OS_Library_GetProcAddress(dllhandle, "main", (void **)&VSTmain); + #endif + } + if (result == FMOD_OK) + { + AEffect *effect; + + effect = VSTmain(DSPVSTPlugin::audioMasterCB); + + if (effect->magic == kEffectMagic) + { + struct ERect + { + short top; + short left; + short bottom; + short right; + } *rect = 0; + FMOD_DSP_DESCRIPTION_EX *dspdesc; + FMOD_DSP_PARAMETERDESC *dspparams = (FMOD_DSP_PARAMETERDESC *)FMOD_Memory_Calloc(sizeof(FMOD_DSP_PARAMETERDESC) * effect->numParams); + + dspdesc = DSPVSTPlugin::getDescriptionEx(); + dspdesc->mAEffect = effect; + + dspdesc->version = effect->dispatcher(effect, effGetVendorVersion, 0, 0, NULL, 0); + + sprintf(dspdesc->name, "VST "); + effect->dispatcher(effect, effGetEffectName, 0, 0, (void *)(dspdesc->name + 4), 0); + effect->dispatcher(effect, effEditGetRect, 0, 0, &rect, 0); + + /* + If this plugin has no name, derive one from its filename + */ + if (FMOD_strlen(dspdesc->name) == 4) + { + int i; + + for (i=FMOD_strlen(dllname) - 1; i > 0; i--) + { + if ((dllname[i] == '/') || (dllname[i] == '\\')) + { + i++; + break; + } + } + + int len = FMOD_strlen(&dllname[i]); + char *p = (char *)&dllname[i] + len - 4; + if (p >= &dllname[i]) + { + if ((FMOD_tolower(p[0]) == '.') && + (FMOD_tolower(p[1]) == 'd') && + (FMOD_tolower(p[2]) == 'l') && + (FMOD_tolower(p[3]) == 'l')) + { + len -= 4; + } + } + + FMOD_strncat(dspdesc->name, &dllname[i], len); + } + + if (rect) + { + dspdesc->configheight = rect->bottom - rect->top; + dspdesc->configwidth = rect->right - rect->left; + } + + /* + Fill in dspparams + */ + for (int count = 0; count < effect->numParams; count++) + { + dspparams[count].min = 0.0f; + dspparams[count].max = 1.0f; + dspparams[count].defaultval = effect->getParameter(effect, count); + dspparams[count].description = NULL; + + effect->dispatcher(effect, effGetParamName, count, 0, dspparams[count].name, 0); + effect->dispatcher(effect, effGetParamLabel, count, 0, dspparams[count].label, 0); + } + dspdesc->paramdesc = dspparams; + dspdesc->numparameters = effect->numParams; + + return registerDSP(dspdesc, handle); + } + } +#endif + +#ifdef FMOD_SUPPORT_WINAMPPLUGIN + result = FMOD_OS_Library_GetProcAddress(dllhandle, "winampDSPGetHeader2", (void **)&winampGetHeader); + if (result == FMOD_OK) + { + winampDSPHeader *header; + + header = winampGetHeader(); + + /* + Register each module as a seperate plugin + */ + for (int count = 0; ; count++) + { + FMOD_DSP_DESCRIPTION_EX *dspdesc = NULL; + winampDSPModule *module = NULL; + + module = header->getModule(count); + if (!module) + { + break; + } + + module->hDllInstance = dllhandle; + + dspdesc = DSPWinampPlugin::getDescriptionEx(); + dspdesc->mAEffect = module; + + dspdesc->version = header->version; + + sprintf(dspdesc->name, "WINAMP "); + + FMOD_strncpy((dspdesc->name + 7), module->description, 25); + + dspdesc->name[31] = '\0'; + + /* + If this plugin has no name, derive one from its filename + */ + if (FMOD_strlen(dspdesc->name) == 7) + { + int i; + + for (i=FMOD_strlen(dllname) - 1; i > 0; i--) + { + if ((dllname[i] == '/') || (dllname[i] == '\\')) + { + i++; + break; + } + } + + int len = FMOD_strlen(&dllname[i]); + char *p = (char *)&dllname[i] + len - 4; + if (p >= &dllname[i]) + { + if ((FMOD_tolower(p[0]) == '.') && + (FMOD_tolower(p[1]) == 'd') && + (FMOD_tolower(p[2]) == 'l') && + (FMOD_tolower(p[3]) == 'l')) + { + len -= 4; + } + } + + FMOD_strncat(dspdesc->name, &dllname[i], len); + } + + dspdesc->numparameters = 0; + + result = registerDSP(dspdesc, handle); + } + } +#endif + + return result; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::unloadPlugin(unsigned int handle) +{ + FMOD_RESULT result; + FMOD_OUTPUT_DESCRIPTION_EX *output; + FMOD_CODEC_DESCRIPTION_EX *codec; + FMOD_DSP_DESCRIPTION_EX *dsp; + + result = getOutput(handle, &output); + if (result == FMOD_OK) + { + #ifdef FMOD_SUPPORT_DLLS + if (output->mModule) + { + FMOD_OS_Library_Free(output->mModule); + } + #endif + output->removeNode(); + FMOD_Memory_Free(output); + return FMOD_OK; + } + else if (result == FMOD_ERR_PLUGIN_MISSING) + { + result = getCodec(handle, &codec); + if (result == FMOD_OK) + { + #ifdef FMOD_SUPPORT_DLLS + if (codec->mModule) + { + FMOD_OS_Library_Free(codec->mModule); + } + #endif + codec->removeNode(); + FMOD_Memory_Free(codec); + } + else if (result == FMOD_ERR_PLUGIN_MISSING) + { + result = getDSP(handle, &dsp); + if (result == FMOD_OK) + { + #ifdef FMOD_SUPPORT_DLLS + if (dsp->mAEffect) + { + if (dsp->paramdesc) + { + FMOD_Memory_Free(dsp->paramdesc); + } + } + #endif + + #ifdef FMOD_SUPPORT_DLLS + if (dsp->mModule) + { + FMOD_OS_Library_Free(dsp->mModule); + } + #endif + dsp->removeNode(); + FMOD_Memory_Free(dsp); + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::registerCodec(FMOD_CODEC_DESCRIPTION_EX *description, unsigned int *handle, unsigned int priority) +{ + FMOD_CODEC_DESCRIPTION_EX *newdesc; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + + newdesc = FMOD_Object_Calloc(FMOD_CODEC_DESCRIPTION_EX); + if (!newdesc) + { + return FMOD_ERR_MEMORY; + } + + newdesc->name = description->name; + newdesc->version = description->version; + newdesc->timeunits = description->timeunits; + newdesc->defaultasstream = description->defaultasstream; + newdesc->open = description->open; + newdesc->close = description->close; + newdesc->read = description->read; + newdesc->getlength = description->getlength; + newdesc->setposition = description->setposition; + newdesc->getposition = description->getposition; + newdesc->soundcreate = description->soundcreate; + newdesc->getwaveformat = description->getwaveformat; + newdesc->mType = description->mType; + newdesc->mSize = description->mSize; + newdesc->mModule = description->mModule; + newdesc->reset = description->reset; + newdesc->canpoint = description->canpoint; + newdesc->getmusicnumchannels = description->getmusicnumchannels; + newdesc->setmusicchannelvolume = description->setmusicchannelvolume; + newdesc->getmusicchannelvolume = description->getmusicchannelvolume; + newdesc->gethardwaremusicchannel = description->gethardwaremusicchannel; + newdesc->update = description->update; + newdesc->getmemoryused = description->getmemoryused; + newdesc->mHandle = mCurrentPluginHandle++; + newdesc->init = description->init; + + newdesc->addAt(&mCodecHead, &mCodecHead, priority); + + if (handle) + { + *handle = newdesc->mHandle; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::registerCodec(FMOD_CODEC_DESCRIPTION *description, unsigned int *handle, unsigned int priority) +{ + FMOD_CODEC_DESCRIPTION_EX *newdesc; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + + newdesc = FMOD_Object_Calloc(FMOD_CODEC_DESCRIPTION_EX); + if (!newdesc) + { + return FMOD_ERR_MEMORY; + } + + newdesc->name = description->name; + newdesc->version = description->version; + newdesc->timeunits = description->timeunits; + newdesc->defaultasstream = description->defaultasstream; + newdesc->open = description->open; + newdesc->close = description->close; + newdesc->read = description->read; + newdesc->getlength = description->getlength; + newdesc->setposition = description->setposition; + newdesc->getposition = description->getposition; + newdesc->soundcreate = description->soundcreate; + newdesc->getwaveformat = description->getwaveformat; + newdesc->mType = FMOD_SOUND_TYPE_UNKNOWN; + newdesc->mSize = sizeof(Codec); + newdesc->mModule = 0; + newdesc->reset = 0; + newdesc->canpoint = 0; + newdesc->getmusicnumchannels = 0; + newdesc->setmusicchannelvolume = 0; + newdesc->getmusicchannelvolume = 0; + newdesc->gethardwaremusicchannel = 0; + newdesc->update = 0; + newdesc->getmemoryused = 0; + newdesc->mHandle = mCurrentPluginHandle++; + + newdesc->addAt(&mCodecHead, &mCodecHead, priority); + + if (handle) + { + *handle = newdesc->mHandle; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::registerDSP(FMOD_DSP_DESCRIPTION_EX *description, unsigned int *handle) +{ + FMOD_DSP_DESCRIPTION_EX *newdesc; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + + newdesc = FMOD_Object_Calloc(FMOD_DSP_DESCRIPTION_EX); + if (!newdesc) + { + return FMOD_ERR_MEMORY; + } + + FMOD_strcpy(newdesc->name, description->name); + newdesc->version = description->version; + newdesc->channels = description->channels; + newdesc->create = description->create; + newdesc->release = description->release; + newdesc->reset = description->reset; + newdesc->read = description->read; + newdesc->setposition = description->setposition; + + newdesc->numparameters = description->numparameters; + newdesc->paramdesc = description->paramdesc; + newdesc->setparameter = description->setparameter; + newdesc->getparameter = description->getparameter; + newdesc->config = description->config; + newdesc->configwidth = description->configwidth; + newdesc->configheight = description->configheight; + newdesc->userdata = description->userdata; + + newdesc->mType = description->mType; + newdesc->mCategory = description->mCategory; + newdesc->mSize = description->mSize; +#ifdef FMOD_SUPPORT_DLLS + newdesc->mModule = description->mModule; + newdesc->mAEffect = description->mAEffect; +#endif + newdesc->mFormat = description->mFormat; + newdesc->mResamplerBlockLength = description->mResamplerBlockLength; + newdesc->getmemoryused = description->getmemoryused; + newdesc->update = description->update; + newdesc->mHandle = mCurrentPluginHandle++; + +#ifdef FMOD_SUPPORT_DLLS + newdesc->configidle = description->configidle; +#endif + + newdesc->addBefore(&mDSPHead); + + if (handle) + { + *handle = newdesc->mHandle; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::registerDSP(FMOD_DSP_DESCRIPTION *description, unsigned int *handle) +{ + FMOD_DSP_DESCRIPTION_EX *newdesc; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + + newdesc = FMOD_Object_Calloc(FMOD_DSP_DESCRIPTION_EX); + if (!newdesc) + { + return FMOD_ERR_MEMORY; + } + + FMOD_strcpy(newdesc->name, description->name); + newdesc->version = description->version; + newdesc->channels = description->channels; + newdesc->create = description->create; + newdesc->release = description->release; + newdesc->reset = description->reset; + newdesc->read = description->read; + newdesc->setposition = description->setposition; + + newdesc->numparameters = description->numparameters; + newdesc->paramdesc = description->paramdesc; + newdesc->setparameter = description->setparameter; + newdesc->getparameter = description->getparameter; + newdesc->config = description->config; + newdesc->configwidth = description->configwidth; + newdesc->configheight = description->configheight; + newdesc->userdata = description->userdata; + + newdesc->mType = FMOD_DSP_TYPE_UNKNOWN; + newdesc->mSize = sizeof(DSPI); + newdesc->mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + newdesc->mHandle = mCurrentPluginHandle++; + + newdesc->addBefore(&mDSPHead); + + if (handle) + { + *handle = newdesc->mHandle; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::registerOutput(FMOD_OUTPUT_DESCRIPTION_EX *description, unsigned int *handle) +{ + FMOD_OUTPUT_DESCRIPTION_EX *newdesc; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + + newdesc = FMOD_Object_Calloc(FMOD_OUTPUT_DESCRIPTION_EX); + if (!newdesc) + { + return FMOD_ERR_MEMORY; + } + + newdesc->name = description->name; + newdesc->version = description->version; + newdesc->polling = description->polling; + newdesc->getnumdrivers = description->getnumdrivers; + newdesc->getdrivername = description->getdrivername; + newdesc->getdrivercaps = description->getdrivercaps; + newdesc->init = description->init; + newdesc->close = description->close; + newdesc->start = description->start; + newdesc->stop = description->stop; + newdesc->update = description->update; + newdesc->gethandle = description->gethandle; + newdesc->getposition = description->getposition; + newdesc->lock = description->lock; + newdesc->unlock = description->unlock; + + newdesc->getsamplemaxchannels = description->getsamplemaxchannels; + newdesc->getdriverinfo = description->getdriverinfo; + newdesc->getdriverinfow = description->getdriverinfow; + newdesc->getdrivercapsex = description->getdrivercapsex; + newdesc->getdrivercapsex2 = description->getdrivercapsex2; + newdesc->initex = description->initex; + newdesc->start = description->start; + newdesc->stop = description->stop; + newdesc->updatefinished = description->updatefinished; + newdesc->createsample = description->createsample; + newdesc->getsoundram = description->getsoundram; + newdesc->record_getnumdrivers = description->record_getnumdrivers; + newdesc->record_getdriverinfo = description->record_getdriverinfo; + newdesc->record_getdriverinfow = description->record_getdriverinfow; + newdesc->record_getdrivercaps = description->record_getdrivercaps; + newdesc->record_start = description->record_start; + newdesc->record_stop = description->record_stop; + newdesc->record_getposition = description->record_getposition; + newdesc->record_lock = description->record_lock; + newdesc->record_unlock = description->record_unlock; + newdesc->reverb_setproperties = description->reverb_setproperties; + newdesc->postmixcallback = description->postmixcallback; + newdesc->mType = description->mType; + newdesc->mSize = description->mSize; + newdesc->mModule = description->mModule; + newdesc->getmemoryused = description->getmemoryused; + newdesc->mHandle = mCurrentPluginHandle++; + + newdesc->addBefore(&mOutputHead); + + if (handle) + { + *handle = newdesc->mHandle; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::registerOutput(FMOD_OUTPUT_DESCRIPTION *description, unsigned int *handle) +{ + FMOD_OUTPUT_DESCRIPTION_EX *newdesc; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + + newdesc = FMOD_Object_Calloc(FMOD_OUTPUT_DESCRIPTION_EX); + if (!newdesc) + { + return FMOD_ERR_MEMORY; + } + + newdesc->name = description->name; + newdesc->version = description->version; + newdesc->polling = description->polling; + newdesc->getnumdrivers = description->getnumdrivers; + newdesc->getdrivername = description->getdrivername; + newdesc->init = description->init; + newdesc->close = description->close; + newdesc->update = description->update; + newdesc->gethandle = description->gethandle; + newdesc->getposition = description->getposition; + newdesc->lock = description->lock; + newdesc->unlock = description->unlock; + + newdesc->mType = FMOD_OUTPUTTYPE_UNKNOWN; + newdesc->mSize = sizeof(Output); + newdesc->mModule = 0; + newdesc->getmemoryused = 0; + newdesc->mHandle = mCurrentPluginHandle++; + + newdesc->addBefore(&mOutputHead); + + if (handle) + { + *handle = newdesc->mHandle; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getNumCodecs(int *numcodecs) +{ + if (!numcodecs) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numcodecs = mCodecHead.count(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getNumDSPs(int *numdsps) +{ + if (!numdsps) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numdsps = mDSPHead.count(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getNumOutputs(int *numoutputs) +{ + if (!numoutputs) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numoutputs = mOutputHead.count(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getCodecHandle(int index, unsigned int *handle) +{ + LinkedListNode *node; + + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + node = mCodecHead.getNodeByIndex(index); + if (!node) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = ((FMOD_CODEC_DESCRIPTION_EX *)node)->mHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getDSPHandle(int index, unsigned int *handle) +{ + LinkedListNode *node; + + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + node = mDSPHead.getNodeByIndex(index); + if (!node) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = ((FMOD_DSP_DESCRIPTION_EX *)node)->mHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getOutputHandle(int index, unsigned int *handle) +{ + LinkedListNode *node; + + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + node = mOutputHead.getNodeByIndex(index); + if (!node) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = ((FMOD_OUTPUT_DESCRIPTION_EX *)node)->mHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getCodec(unsigned int handle, FMOD_CODEC_DESCRIPTION_EX **codecdesc) +{ + FMOD_CODEC_DESCRIPTION_EX *head, *current; + + if (!codecdesc) + { + return FMOD_ERR_INVALID_PARAM; + } + + *codecdesc = 0; + + head = &mCodecHead; + current = (FMOD_CODEC_DESCRIPTION_EX *)(head->getNext()); + while (current != head) + { + if (current->mHandle == handle) + { + *codecdesc = current; + return FMOD_OK; + } + + current = (FMOD_CODEC_DESCRIPTION_EX *)(current->getNext()); + } + + return FMOD_ERR_PLUGIN_MISSING; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getDSP(unsigned int handle, FMOD_DSP_DESCRIPTION_EX **dspdesc) +{ + FMOD_DSP_DESCRIPTION_EX *head, *current; + + if (!dspdesc) + { + return FMOD_ERR_INVALID_PARAM; + } + + *dspdesc = 0; + + head = &mDSPHead; + current = (FMOD_DSP_DESCRIPTION_EX *)(head->getNext()); + while (current != head) + { + if (current->mHandle == handle) + { + *dspdesc = current; + return FMOD_OK; + } + + current = (FMOD_DSP_DESCRIPTION_EX *)(current->getNext()); + } + + return FMOD_ERR_PLUGIN_MISSING; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::getOutput(unsigned int handle, FMOD_OUTPUT_DESCRIPTION_EX **outputdesc) +{ + FMOD_OUTPUT_DESCRIPTION_EX *head, *current; + + if (!outputdesc) + { + return FMOD_ERR_INVALID_PARAM; + } + + *outputdesc = 0; + + head = &mOutputHead; + current = (FMOD_OUTPUT_DESCRIPTION_EX *)(head->getNext()); + while (current != head) + { + if (current->mHandle == handle) + { + *outputdesc = current; + return FMOD_OK; + } + + current = (FMOD_OUTPUT_DESCRIPTION_EX *)(current->getNext()); + } + + return FMOD_ERR_PLUGIN_MISSING; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::createCodec(FMOD_CODEC_DESCRIPTION_EX *codecdesc, Codec **codec) +{ + Codec *newcodec; + + if (!codecdesc || !codec) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Maybe this should call an 'alloc callback' to let the codec allocate itself? + This would allow the object to use proper inheritence etc. 'Codec' is not good enough. + + the 'Ex' stuff could possibly be deleted if it did this, as the hidden stuff could just + be a member of the codec class and the codecs just derive from it anyway? + */ + newcodec = FMOD_Object_CallocSize(Codec, (unsigned int)codecdesc->mSize); + if (!newcodec) + { + return FMOD_ERR_MEMORY; + } + + /* + Copy the description out of the source codec to the new codec. + */ + FMOD_memcpy(&(newcodec->mDescription), codecdesc, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + *codec = newcodec; + + if (!newcodec->mDescription.getwaveformat) + { + newcodec->mDescription.getwaveformat = &Codec::defaultGetWaveFormat; + } + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_SOFTWARE + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::createDSP(FMOD_DSP_DESCRIPTION_EX *dspdesc, DSPI **dsp) +{ + FMOD_RESULT result; + #ifdef PLATFORM_PS3 + DSPI *newdspmemory = 0; + #endif + DSPI *newdsp; + unsigned int size = 0; + + if (!dspdesc || !dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + size = dspdesc->mSize; + + if (*dsp) + { + newdsp = *dsp; + } + else + { + switch (dspdesc->mCategory) + { + case FMOD_DSP_CATEGORY_SOUNDCARD: +#if !defined(PLATFORM_PS3) && !defined(PLATFORM_WINDOWS_PS3MODE) + { + dspdesc->mSize = dspdesc->mSize < sizeof(DSPSoundCard) ? sizeof(DSPSoundCard) : (int)size; + + newdsp = FMOD_Object_CallocSize(DSPSoundCard, (unsigned int)dspdesc->mSize); + break; + } +#endif + case FMOD_DSP_CATEGORY_FILTER: + { + #ifdef PLATFORM_PS3 + + size = dspdesc->mSize < (int)sizeof(DSPFilter) ? sizeof(DSPFilter) : dspdesc->mSize; + size = (size + 15) & ~15; + + if (size > DSPSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit of size %d is too big! Must be <= %d\n", size, DSPSIZE)); + return FMOD_ERR_INTERNAL; + } + + dspdesc->mSize = size; + + newdspmemory = (DSPI *)FMOD_Memory_Calloc(size + 128); + if (!newdspmemory) + { + return FMOD_ERR_MEMORY; + } + newdsp = (DSPI *)FMOD_ALIGNPOINTER(newdspmemory, 128); + + if ( + FMOD_strcmp ("FMOD SoundCard Unit", dspdesc->name) && + FMOD_strcmp ("FMOD ChannelGroup Target Unit", dspdesc->name) && + FMOD_strcmp ("FMOD Mixer unit", dspdesc->name) && + FMOD_strncmp("ChannelGroup", dspdesc->name, 12) && + (FMOD_strncmp("FMOD IT", dspdesc->name, 7) || dspdesc->mType == FMOD_DSP_TYPE_ITLOWPASS || dspdesc->mType == FMOD_DSP_TYPE_ITECHO) /* It DSP units remain on PPU, IT lowpass can be used as a normal effect on SPU */ + ) + { + /* + Is an FMOD effect, or USER effect that needs to be streamed onto SPU + */ + int totalsize = 0; + int clearsize = 0; + + // FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "PluginFactory::createDSP", "Getting SPU PIC details for %s\n", dspdesc->name)); + + /* + Get info about the pic + */ + result = OutputPS3::getSPUPICProperties((unsigned int)dspdesc->read, &newdsp->mCodeMramAddress, &newdsp->mCodeEntryAddress, &newdsp->mCodeSize, &newdsp->mCodeLSOffset, &newdsp->mCodeFillLocation, &newdsp->mCodeFillSize, &newdsp->mCodeFillValue); + if (result != FMOD_OK) + { + FMOD_Memory_Free(newdspmemory); + return result; + } + + clearsize = newdsp->mCodeFillLocation - newdsp->mCodeSize + newdsp->mCodeFillSize; + clearsize = clearsize < 0 ? 0 : clearsize; + totalsize = dspdesc->mSize + newdsp->mCodeSize + clearsize; + + if (totalsize > DSPSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit (+ PIC code) of size %d is too big! Must be <= %d\n", totalsize, DSPSIZE)); + return FMOD_ERR_INTERNAL; + } + + // FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit (+ PIC code) size: %d\n", totalsize)); + + dspdesc->config = (FMOD_DSP_DIALOGCALLBACK)1; /* Tell SPU to upload code */ + } + else + { + dspdesc->config = (FMOD_DSP_DIALOGCALLBACK)0; + } + + #else + + dspdesc->mSize = size < sizeof(DSPFilter) ? sizeof(DSPFilter) : size; + + newdsp = (DSPI *)FMOD_Memory_Calloc(dspdesc->mSize); + if (!newdsp) + { + return FMOD_ERR_MEMORY; + } + + #endif + + new (newdsp) DSPFilter; + + break; + } +#ifdef FMOD_SUPPORT_DSPCODEC + case FMOD_DSP_CATEGORY_DSPCODECMPEG: + case FMOD_DSP_CATEGORY_DSPCODECADPCM: + case FMOD_DSP_CATEGORY_DSPCODECXMA: + case FMOD_DSP_CATEGORY_DSPCODECCELT: + case FMOD_DSP_CATEGORY_DSPCODECRAW: + { + #ifdef PLATFORM_PS3 + size = dspdesc->mSize < (int)sizeof(DSPCodec) ? sizeof(DSPCodec) : dspdesc->mSize; + size = (size + 15) & ~15; + + if (size > DSPSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit of size %d is too big! Must be <= %d\n", size, DSPSIZE)); + return FMOD_ERR_INTERNAL; + } + + dspdesc->mSize = size; + + newdspmemory = (DSPI *)FMOD_Memory_CallocType(size + 16, FMOD_MEMORY_PERSISTENT); + if (!newdspmemory) + { + return FMOD_ERR_MEMORY; + } + + newdsp = (DSPI *)FMOD_ALIGNPOINTER(newdspmemory, 16); + + { + int totalsize = 0; + unsigned int codec_entry = 0; + + /* + Get code address from codec + */ + + switch(dspdesc->mCategory) + { + #ifdef FMOD_SUPPORT_RAW + case FMOD_DSP_CATEGORY_DSPCODECRAW: + { + CodecRaw codecraw; + codec_entry = (unsigned int)codecraw.getDescriptionEx()->mModule; + + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "PluginFactory::createDSP", "Getting SPU PIC details for %s (%s)\n", dspdesc->name, codecraw.getDescriptionEx()->name)); + + /* + Get info about the pic + */ + result = OutputPS3::getSPUPICProperties(codec_entry, &newdsp->mCodeMramAddress, &newdsp->mCodeEntryAddress, &newdsp->mCodeSize, &newdsp->mCodeLSOffset, &newdsp->mCodeFillLocation, &newdsp->mCodeFillSize, &newdsp->mCodeFillValue); + if (result != FMOD_OK) + { + FMOD_Memory_Free(newdspmemory); + return result; + } + break; + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + case FMOD_DSP_CATEGORY_DSPCODECADPCM: + { + CodecWav codecwav; + codec_entry = (unsigned int)codecwav.getDescriptionEx()->mModule; + + + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "PluginFactory::createDSP", "Getting SPU PIC details for %s (%s)\n", dspdesc->name, codecwav.getDescriptionEx()->name)); + + /* + Get info about the pic + */ + result = OutputPS3::getSPUPICProperties(codec_entry, &newdsp->mCodeMramAddress, &newdsp->mCodeEntryAddress, &newdsp->mCodeSize, &newdsp->mCodeLSOffset, &newdsp->mCodeFillLocation, &newdsp->mCodeFillSize, &newdsp->mCodeFillValue); + if (result != FMOD_OK) + { + FMOD_Memory_Free(newdspmemory); + return result; + } + break; + } + #endif + #if defined(FMOD_SUPPORT_MPEG) && defined(FMOD_SUPPORT_MPEG_LAYER3) && !defined(FMOD_SUPPORT_MPEG_SONYDECODER) + case FMOD_DSP_CATEGORY_DSPCODECMPEG: + { + CodecMPEG codecmpeg; + + codec_entry = (unsigned int)FMODGetMPEGFunctionsPICDescriptionEx()->entry; + + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "PluginFactory::createDSP", "Getting SPU PIC details for %s (%s)\n", dspdesc->name, codecmpeg.getDescriptionEx()->name)); + + /* + Get info about the pic + */ + result = OutputPS3::getSPUPICProperties(codec_entry, &newdsp->mCodeMramAddress, &newdsp->mCodeEntryAddress, &newdsp->mCodeSize, &newdsp->mCodeLSOffset, &newdsp->mCodeFillLocation, &newdsp->mCodeFillSize, &newdsp->mCodeFillValue); + if (result != FMOD_OK) + { + FMOD_Memory_Free(newdspmemory); + return result; + } + break; + } + #endif + default: + break; + } + + totalsize = dspdesc->mSize + newdsp->mCodeSize + newdsp->mCodeFillSize; + + if (totalsize > DSPSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit (+ PIC code) of size %d is too big! Must be <= %d\n", totalsize, DSPSIZE)); + return FMOD_ERR_INTERNAL; + } + + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit (+ PIC code) size: %d\n", totalsize)); + } + #else + FMOD_MEMORY_TYPE memtype; + + size = size < sizeof(DSPCodec) ? sizeof(DSPCodec) : size; + + memtype = FMOD_MEMORY_PERSISTENT; + + #ifdef PLATFORM_XENON + if (dspdesc->mCategory == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + memtype |= FMOD_MEMORY_XBOX360_PHYSICAL; + } + #endif + + newdsp = (DSPI *)FMOD_Memory_CallocType(size, memtype); + if (!newdsp) + { + return FMOD_ERR_MEMORY; + } + #endif + + if (0) + { + } + #ifdef FMOD_SUPPORT_MPEG + else if (dspdesc->mCategory == FMOD_DSP_CATEGORY_DSPCODECMPEG) + { + new (newdsp) DSPCodecMPEG; + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (dspdesc->mCategory == FMOD_DSP_CATEGORY_DSPCODECADPCM) + { + new (newdsp) DSPCodecADPCM; + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (dspdesc->mCategory == FMOD_DSP_CATEGORY_DSPCODECXMA) + { + new (newdsp) DSPCodecXMA; + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (dspdesc->mCategory == FMOD_DSP_CATEGORY_DSPCODECCELT) + { + new (newdsp) DSPCodecCELT; + } + #endif + #ifdef FMOD_SUPPORT_RAW + else if (dspdesc->mCategory == FMOD_DSP_CATEGORY_DSPCODECRAW) + { + new (newdsp) DSPCodecRaw; + } + #endif + else + { + return FMOD_ERR_FORMAT; + } + break; + } +#endif + case FMOD_DSP_CATEGORY_RESAMPLER: + { + #ifdef PLATFORM_PS3 + + size = size < sizeof(DSPResamplerPS3) ? sizeof(DSPResamplerPS3) : size; + size = (size + 15) & ~15; + + if (size > DSPSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit of size %d is too big! Must be <= %d\n", size)); + return FMOD_ERR_MEMORY; + } + + dspdesc->mSize = size; + + newdspmemory = (DSPI *)FMOD_Memory_Calloc(size + 16); + if (!newdspmemory) + { + return FMOD_ERR_MEMORY; + } + + newdsp = (DSPI *)FMOD_ALIGNPOINTER(newdspmemory, 16); + + #else + + #ifdef PLATFORM_WINDOWS_PS3MODE + size = size < sizeof(DSPResampler) ? sizeof(DSPResampler) : size; + #else + size = size < sizeof(DSPResamplerMultiInput) ? sizeof(DSPResamplerMultiInput) : size; + #endif + + newdsp = (DSPI *)FMOD_Memory_Calloc(size); + if (!newdsp) + { + return FMOD_ERR_MEMORY; + } + + #endif + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + new (newdsp) DSPResampler; + #else + new (newdsp) DSPResamplerMultiInput; + #endif + + break; + } + case FMOD_DSP_CATEGORY_WAVETABLE: + { + #ifdef PLATFORM_PS3 + + size = dspdesc->mSize < (int)sizeof(DSPWaveTable) ? sizeof(DSPWaveTable) : dspdesc->mSize; + size = (size + 15) & ~15; + + if (size > DSPSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "PluginFactory::createDSP", "DSP unit of size %d is too big! Must be <= %d\n", size)); + return FMOD_ERR_INTERNAL; + } + + dspdesc->mSize = size; + + newdspmemory = (DSPI *)FMOD_Memory_Calloc(size + 16); + if (!newdspmemory) + { + return FMOD_ERR_MEMORY; + } + + newdsp = (DSPI *)FMOD_ALIGNPOINTER(newdspmemory, 16); + + #else + + size = size < sizeof(DSPWaveTable) ? sizeof(DSPWaveTable) : size; + + newdsp = (DSPI *)FMOD_Memory_Calloc(size); + if (!newdsp) + { + return FMOD_ERR_MEMORY; + } + + #endif + + new (newdsp) DSPWaveTable; + + break; + } + default: + { + return FMOD_ERR_INVALID_PARAM; + } + } + } + + if (!newdsp) + { + *dsp = 0; + return FMOD_ERR_MEMORY; + } + + newdsp->mSystem = mSystem; + + result = newdsp->alloc(dspdesc); + if (result != FMOD_OK) + { + #ifdef PLATFORM_PS3 + FMOD_Memory_Free(newdspmemory); + #else + FMOD_Memory_Free(newdsp); + #endif + return result; + } + + if (dspdesc->create) + { +#ifdef FMOD_SUPPORT_DLLS + if (dspdesc->mAEffect) + { + #ifdef FMOD_SUPPORT_VSTPLUGIN + { + AEffect *effect = (AEffect *)dspdesc->mAEffect; + + if (effect->magic == kEffectMagic) + { + ((DSPVSTPlugin *)newdsp)->mEffect = effect; + + LinkedListNode *newvstnode = FMOD_Object_Calloc(LinkedListNode); + if (!newvstnode) + { + return FMOD_ERR_MEMORY; + } + + newvstnode->setData((void *)newdsp); + newvstnode->addAfter(&mSystem->mVSTPluginsListHead); + } + } + #endif + + #ifdef FMOD_SUPPORT_WINAMPPLUGIN + { + winampDSPModule *effect = (winampDSPModule *)dspdesc->mAEffect; + + if (!FMOD_strncmp(dspdesc->name, "WINAMP", 6)) + { + ((DSPWinampPlugin *)newdsp)->mEffect = effect; + } + } + #endif + } +#endif + + newdsp->instance = (FMOD_DSP *)newdsp; + + result = dspdesc->create((FMOD_DSP_STATE *)newdsp); + if (result != FMOD_OK) + { + #ifdef PLATFORM_PS3 + FMOD_Memory_Free(newdspmemory); + #else + FMOD_Memory_Free(newdsp); + #endif + return result; + } + } + + #ifdef PLATFORM_PS3 + newdsp->mMemory = newdspmemory; + newdsp->mMramAddress = (unsigned int)newdsp; + #endif + + *dsp = newdsp; + + return FMOD_OK; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT PluginFactory::createOutput(FMOD_OUTPUT_DESCRIPTION_EX *outputdesc, Output **output) +{ + Output *newoutput; + + if (!outputdesc || !output) + { + return FMOD_ERR_INVALID_PARAM; + } + +#if defined(FMOD_SUPPORT_OPENAL) && !defined(FMOD_USE_PLUGINS) + if (outputdesc->mType == FMOD_OUTPUTTYPE_OPENAL) /* OpenAL has a special virtual function getFreeChannel so we need to detect it and object alloc it to set up vtables. */ + { + newoutput = FMOD_Object_Calloc(OutputOpenAL); + } + else +#endif + if (outputdesc->polling) + { + newoutput = FMOD_Object_CallocSize(OutputPolled, (unsigned int)outputdesc->mSize); + } + else + { + newoutput = FMOD_Object_CallocSize(Output, (unsigned int)outputdesc->mSize); + } + + if (!newoutput) + { + *output = 0; + return FMOD_ERR_MEMORY; + } + + /* + Copy the description out of the source output to the new output. + */ + FMOD_memcpy(&newoutput->mDescription, outputdesc, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + newoutput->mSystem = mSystem; + +#ifdef FMOD_SUPPORT_SOFTWARE + newoutput->readfrommixer = Output::mixCallback; +#endif + + *output = newoutput; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT PluginFactory::getMemoryUsedImpl(MemoryTracker *tracker) +{ + LinkedListNode *node; + + tracker->add(false, FMOD_MEMBITS_PLUGINS, sizeof(*this)); + + for (node = mCodecHead.getNext(); node != &mCodecHead; node = node->getNext()) + { + tracker->add(false, FMOD_MEMBITS_PLUGINS, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + } + + for (node = mDSPHead.getNext(); node != &mDSPHead; node = node->getNext()) + { + tracker->add(false, FMOD_MEMBITS_PLUGINS, sizeof(FMOD_DSP_DESCRIPTION_EX)); + } + + for (node = mOutputHead.getNext(); node != &mOutputHead; node = node->getNext()) + { + tracker->add(false, FMOD_MEMBITS_PLUGINS, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_pluginfactory.h b/src/fmod_pluginfactory.h new file mode 100755 index 0000000..6455dcf --- /dev/null +++ b/src/fmod_pluginfactory.h @@ -0,0 +1,74 @@ +#ifndef _FMOD_PLUGINFACTORY_H +#define _FMOD_PLUGINFACTORY_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_codeci.h" +#include "fmod_dspi.h" +#include "fmod_outputi.h" +#include "fmod_string.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class DSPI; + class SystemI; + + class PluginFactory + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + char mPluginPath[FMOD_STRING_MAXPATHLEN]; + FMOD_DSP_DESCRIPTION_EX mDSPHead; + FMOD_CODEC_DESCRIPTION_EX mCodecHead; + FMOD_OUTPUT_DESCRIPTION_EX mOutputHead; + SystemI *mSystem; + unsigned int mCurrentPluginHandle; + + public: + + PluginFactory(); + + FMOD_RESULT release (); + + FMOD_RESULT setSystem (SystemI *system); + FMOD_RESULT getSystem (SystemI **system); + + FMOD_RESULT setPluginPath (const char *path); +#ifdef FMOD_SUPPORT_DLLS + FMOD_RESULT tryLoadPlugin (const char *dllname, unsigned int *handle = 0, bool calledinternally = true, unsigned int priority = 0); + FMOD_RESULT loadPlugin (const char *dllname, unsigned int *handle = 0, bool calledinternally = true, unsigned int priority = 0); +#endif + FMOD_RESULT unloadPlugin (unsigned int handle); + + FMOD_RESULT registerCodec (FMOD_CODEC_DESCRIPTION *description, unsigned int *handle = 0, unsigned int priority = 0); + FMOD_RESULT registerCodec (FMOD_CODEC_DESCRIPTION_EX *description, unsigned int *handle = 0, unsigned int priority = 0); + FMOD_RESULT registerDSP (FMOD_DSP_DESCRIPTION *description, unsigned int *handle = 0); + FMOD_RESULT registerDSP (FMOD_DSP_DESCRIPTION_EX *description, unsigned int *handle = 0); + FMOD_RESULT registerOutput (FMOD_OUTPUT_DESCRIPTION *description, unsigned int *handle = 0); + FMOD_RESULT registerOutput (FMOD_OUTPUT_DESCRIPTION_EX *description, unsigned int *handle = 0); + + FMOD_RESULT getNumCodecs (int *numcodecs); + FMOD_RESULT getNumDSPs (int *numdsps); + FMOD_RESULT getNumOutputs (int *numoutputs); + FMOD_RESULT getCodecHandle (int index, unsigned int *handle); + FMOD_RESULT getDSPHandle (int index, unsigned int *handle); + FMOD_RESULT getOutputHandle (int index, unsigned int *handle); + FMOD_RESULT getCodec (unsigned int handle, FMOD_CODEC_DESCRIPTION_EX **codecdesc); + FMOD_RESULT getDSP (unsigned int handle, FMOD_DSP_DESCRIPTION_EX **dspdesc); + FMOD_RESULT getOutput (unsigned int handle, FMOD_OUTPUT_DESCRIPTION_EX **outputdesc); + + FMOD_RESULT createCodec (FMOD_CODEC_DESCRIPTION_EX *codecdesc, Codec **codec); + FMOD_RESULT createDSP (FMOD_DSP_DESCRIPTION_EX *dspdesc, DSPI **dsp); + FMOD_RESULT createOutput (FMOD_OUTPUT_DESCRIPTION_EX *outputdesc, Output **dsp); + }; +} + +#endif + diff --git a/src/fmod_profile.cpp b/src/fmod_profile.cpp new file mode 100755 index 0000000..4007391 --- /dev/null +++ b/src/fmod_profile.cpp @@ -0,0 +1,798 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_PROFILE + +#include "fmod_profile.h" +#include "fmod_systemi.h" +#include "fmod_os_net.h" + +namespace FMOD +{ + +FMOD_RESULT FMOD_Profile_Create(unsigned short port) +{ + if (gGlobal->gProfile) + { + return FMOD_OK; + } + + gGlobal->gProfile = FMOD_Object_Alloc(Profile); + if (!gGlobal->gProfile) + { + return FMOD_ERR_MEMORY; + } + + FMOD_RESULT result = gGlobal->gProfile->init(port); + if (result != FMOD_OK) + { + gGlobal->gProfile->release(); + gGlobal->gProfile = 0; + } + + return result; +} + + +FMOD_RESULT FMOD_Profile_Release() +{ + if (gGlobal->gProfile) + { + FMOD_RESULT result; + + result = gGlobal->gProfile->release(); + gGlobal->gProfile = 0; + } + + return FMOD_OK; +} + + +FMOD_RESULT FMOD_Profile_Update(SystemI *system, unsigned int dt) +{ + if (!gGlobal->gProfile) + { + return FMOD_ERR_UNINITIALIZED; + } + + return gGlobal->gProfile->update(system, dt); +} + + +/*===================================================================*/ + + +Profile::Profile() +{ + mListenSocket = 0; + mCrit = 0; + mTimeSinceLastUpdate = FMOD_PROFILE_UPDATE_SPEED; + mInitialTimestamp = 0; +} + + +FMOD_RESULT Profile::init(unsigned short port) +{ + FMOD_RESULT result = FMOD_OK; + unsigned short listenPort = port ? port : FMOD_PROFILE_PORT; + + + result = FMOD_OS_Net_Init(); + if (result != FMOD_OK) + { + return result; + } + + result = FMOD_OS_Net_Listen(listenPort, &mListenSocket); + if (result != FMOD_OK) + { + FMOD_OS_Net_Shutdown(); + return result; + } + + result = FMOD_OS_CriticalSection_Create(&mCrit); + if (result != FMOD_OK) + { + FMOD_OS_Net_Shutdown(); + return result; + } + + result = FMOD_OS_Time_GetMs(&mInitialTimestamp); + if (result != FMOD_OK) + { + FMOD_OS_Net_Shutdown(); + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Profile::init", "Profiler listening on port: %d\n", listenPort)); + + return FMOD_OK; +} + + +FMOD_RESULT Profile::release() +{ + FMOD_RESULT result; + LinkedListNode *node, *nextnode; + + if (mListenSocket) + { + FMOD_OS_Net_Close(mListenSocket); + } + + for (node = mClientHead.getNext(); node != &mClientHead; node = nextnode) + { + ProfileClient *client = (ProfileClient *)node; + + nextnode = node->getNext(); + client->removeNode(); + result = client->release(); + if (result != FMOD_OK) + { + return result; + } + } + + for (node = mModuleHead.getNext(); node != &mModuleHead; node = nextnode) + { + ProfileModule *module = (ProfileModule *)node; + + nextnode = node->getNext(); + module->removeNode(); + result = module->release(); + if (result != FMOD_OK) + { + return result; + } + + /* + now set the globals pointer to null + */ + if (FMOD::gGlobal->gProfileChannel == (ProfileChannel*)module) + { + FMOD::gGlobal->gProfileChannel = 0; + } + if (FMOD::gGlobal->gProfileCodec == (ProfileCodec*)module) + { + FMOD::gGlobal->gProfileCodec = 0; + } + if (FMOD::gGlobal->gProfileCpu == (ProfileCpu*)module) + { + FMOD::gGlobal->gProfileCpu = 0; + } + if (FMOD::gGlobal->gProfileDsp == (ProfileDsp*)module) + { + FMOD::gGlobal->gProfileDsp = 0; + } + } + + if (mCrit) + { + FMOD_OS_CriticalSection_Free(mCrit); + } + + FMOD_OS_Net_Shutdown(); + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +FMOD_RESULT Profile::update(FMOD::SystemI *system, unsigned int dt) +{ + FMOD_RESULT result; + LinkedListNode *node, *nextnode; + unsigned int profiledt; + LocalCriticalSection crit(mCrit, false); + + mTimeSinceLastUpdate += dt; + if (mTimeSinceLastUpdate < (unsigned int)FMOD_PROFILE_UPDATE_SPEED) + { + return FMOD_OK; + } + profiledt = mTimeSinceLastUpdate; + mTimeSinceLastUpdate = 0; + + /* + Accept connections from new clients + */ + void *clientsocket; + result = FMOD_OS_Net_Accept(mListenSocket, &clientsocket); + if (result == FMOD_OK) + { + ProfileClient *client = FMOD_Object_Alloc(ProfileClient); + if (!client) + { + return FMOD_ERR_MEMORY; + } + + result = client->init(clientsocket); + if (result != FMOD_OK) + { + return result; + } + + crit.enter(); + client->addBefore(&mClientHead); + crit.leave(); + } + + /* + Update all ProfileModules + */ + for (node = mModuleHead.getNext(); node != &mModuleHead; node = node->getNext()) + { + ProfileModule *module = (ProfileModule *)node; + + if (!module->mUpdateTime || (module->mUpdateTime && ((module->mTimeSinceLastUpdate += profiledt) > module->mUpdateTime))) + { + result = module->update(system, module->mTimeSinceLastUpdate); + if (result != FMOD_OK) + { + return result; + } + + module->mTimeSinceLastUpdate = 0; + } + } + + + crit.enter(); + + /* + Update all ProfileClients + */ + for (node = mClientHead.getNext(); node != &mClientHead; node = node->getNext()) + { + ProfileClient *client = (ProfileClient *)node; + + result = client->update(profiledt); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Remove dead ProfileClients + */ + for (node = mClientHead.getNext(); node != &mClientHead; node = nextnode) + { + ProfileClient *client = (ProfileClient *)node; + + nextnode = node->getNext(); + + if (client->isDead()) + { + client->removeNode(); + + result = client->release(); + if (result != FMOD_OK) + { + return result; + } + } + } + + crit.leave(); + + return FMOD_OK; +} + + +FMOD_RESULT Profile::registerModule(ProfileModule *module) +{ + module->addBefore(&mModuleHead); + + return FMOD_OK; +} + + +FMOD_RESULT Profile::unRegisterModule(ProfileModule *module) +{ + module->removeNode(); + + return FMOD_OK; +} + + +/* + This could be called from multiple threads +*/ +FMOD_RESULT Profile::addPacket(ProfilePacketHeader *packet) +{ + FMOD_RESULT result; + LinkedListNode *node; + unsigned int t; + LocalCriticalSection crit(mCrit, true); + + result = FMOD_OS_Time_GetMs(&t); + if (result != FMOD_OK) + { + return result; + } + packet->timestamp = t - mInitialTimestamp; + + /* + Add this packet to all clients that want it + */ + for (node = mClientHead.getNext(); node != &mClientHead; node = node->getNext()) + { + ProfileClient *client = (ProfileClient *)node; + + if (client->wantsData(packet)) + { + result = client->addPacket(packet); + if (result != FMOD_OK) + { + return result; + } + } + } + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +} + +#ifdef FMOD_SUPPORT_PROFILE_DSP + #include "fmod_profile_dsp.h" +#endif +#ifdef FMOD_SUPPORT_PROFILE_CPU + #include "fmod_profile_cpu.h" +#endif +#ifdef FMOD_SUPPORT_PROFILE_CHANNEL + #include "fmod_profile_channel.h" +#endif +#ifdef FMOD_SUPPORT_PROFILE_CODEC + #include "fmod_profile_codec.h" +#endif + +namespace FMOD +{ + +FMOD_RESULT Profile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(*this)); + + if (mCrit) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, gSizeofCriticalSection); + } + +#ifdef FMOD_SUPPORT_PROFILE_DSP + if (gGlobal->gProfileDsp) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(ProfileDsp)); + + if (gGlobal->gProfileDsp->mNodeStack) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(FMOD::DSPI*) * gGlobal->gProfileDsp->mMaxStackNodes); + } + + // Packet to be sent over the network + if (gGlobal->gProfileDsp->mDataPacket) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(ProfilePacketDspNetwork) + (gGlobal->gProfileDsp->mMaxPacketNodes * sizeof(ProfileDspRawNode))); + } + } +#endif + +#ifdef FMOD_SUPPORT_PROFILE_CPU + if (gGlobal->gProfileCpu) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(ProfileCpu)); + } +#endif + +#ifdef FMOD_SUPPORT_PROFILE_CHANNEL + if (gGlobal->gProfileChannel) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(ProfileChannel)); + } +#endif + +#ifdef FMOD_SUPPORT_PROFILE_CODEC + if (gGlobal->gProfileCodec) + { + tracker->add(false, FMOD_MEMBITS_PROFILE, sizeof(ProfileCodec)); + } +#endif + + return FMOD_OK; +} + +#endif + + +/*===================================================================*/ + + +ProfileModule::ProfileModule() +{ + mUpdateTime = 0; + mTimeSinceLastUpdate = 0; +} + + +FMOD_RESULT ProfileModule::init() +{ + return FMOD_OK; +} + + +FMOD_RESULT ProfileModule::release() +{ + return FMOD_OK; +} + + +FMOD_RESULT ProfileModule::update(SystemI *system, unsigned int dt) +{ + return FMOD_OK; +} + + +/*===================================================================*/ + + +ProfileClient::ProfileClient() +{ + mSocket = (void *)-1; + mBuffer = 0; + mBufferReadPos = 0; + mBufferWritePos = 0; + mBufferSize = 0; + mFlags = 0; + + for (int i = 0; i < FMOD_PROFILE_MAX_DATATYPES; i++) + { + mDataType[i].type = FMOD_PROFILE_DATATYPE_NONE; + mDataType[i].subtype = 0; + mDataType[i].updatetime = 0; + mDataType[i].lastdatatime = 0; + } +} + + +FMOD_RESULT ProfileClient::init(void *socket) +{ + mBufferSize = FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE; + mBuffer = (char *)FMOD_Memory_Alloc(mBufferSize); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + mBufferReadPos = mBuffer; + mBufferWritePos = mBuffer; + mSocket = socket; + + return FMOD_OK; +} + + +FMOD_RESULT ProfileClient::release() +{ + FMOD_OS_Net_Close(mSocket); + + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + } + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileClient::update(unsigned int dt) +{ + FMOD_RESULT result; + + if (isDead()) + { + return FMOD_OK; + } + + result = readData(); + if (result != FMOD_OK) + { + return result; + } + + result = sendData(); + if (result == FMOD_ERR_NET_WOULD_BLOCK) + { + // Try again later (next update) + return FMOD_OK; + } + else if (result != FMOD_OK) + { + mFlags |= FMOD_PROFILECLIENT_FLAGS_DEAD; + return result; + } + + return FMOD_OK; +} + + +FMOD_RESULT ProfileClient::addPacket(ProfilePacketHeader *packet) +{ + FMOD_RESULT result; + + if (isDead()) + { + return FMOD_OK; + } + + if (packet->size > mBufferSize) + { + unsigned int bufferreadoffset = (unsigned int)(mBufferReadPos - mBuffer); + unsigned int bufferwriteoffset = (unsigned int)(mBufferWritePos - mBuffer); + + mBufferSize = ((packet->size / FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE) + 1) * FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE; + + mBuffer = (char *)FMOD_Memory_ReAlloc(mBuffer, mBufferSize); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + mBufferReadPos = mBuffer + bufferreadoffset; + mBufferWritePos = mBuffer + bufferwriteoffset; + } + + if ((mBufferWritePos + packet->size) > (mBuffer + mBufferSize)) + { + result = sendData(); + if (result != FMOD_OK) + { + mFlags |= FMOD_PROFILECLIENT_FLAGS_DEAD; + return FMOD_OK; + } + } + + for (int i = 0; i < FMOD_PROFILE_MAX_DATATYPES; i++) + { + if ((mDataType[i].type == packet->type) && (mDataType[i].subtype == packet->subtype)) + { + mDataType[i].lastdatatime = packet->timestamp; + break; + } + } + + /* + Copy the packet, then endian swap the copied data in-place (if needed) + */ + FMOD_memcpy(mBufferWritePos, packet, packet->size); +#ifdef PLATFORM_ENDIAN_BIG + { + ProfilePacketHeader *bufferedPacket = (ProfilePacketHeader*)mBufferWritePos; + + bufferedPacket->size = FMOD_SWAPENDIAN_DWORD(bufferedPacket->size); + bufferedPacket->timestamp = FMOD_SWAPENDIAN_DWORD(bufferedPacket->timestamp); + } +#endif + mBufferWritePos += packet->size; + + return FMOD_OK; +} + + +FMOD_RESULT ProfileClient::requestDataType(unsigned char type, unsigned char subtype, unsigned int updatetime) +{ + int i; + + for (i = 0; i < FMOD_PROFILE_MAX_DATATYPES; i++) + { + if ((mDataType[i].type == type) && (mDataType[i].subtype == subtype)) + { + if (updatetime) + { + mDataType[i].updatetime = updatetime; + } + else + { + mDataType[i].type = FMOD_PROFILE_DATATYPE_NONE; + } + + return FMOD_OK; + } + } + + for (i = 0; i < FMOD_PROFILE_MAX_DATATYPES; i++) + { + if (mDataType[i].type == FMOD_PROFILE_DATATYPE_NONE) + { + mDataType[i].type = type; + mDataType[i].subtype = subtype; + mDataType[i].updatetime = updatetime; + mDataType[i].lastdatatime = 0; + + return FMOD_OK; + } + } + + return FMOD_OK; +} + + +bool ProfileClient::wantsData(ProfilePacketHeader *packet) +{ + if (isDead()) + { + return false; + } + + for (int i = 0; i < FMOD_PROFILE_MAX_DATATYPES; i++) + { + if ((mDataType[i].type == packet->type) && (mDataType[i].subtype == packet->subtype)) + { + return ((packet->timestamp - mDataType[i].lastdatatime) > mDataType[i].updatetime); + } + } + + return false; +} + + +FMOD_RESULT ProfileClient::readData() +{ + FMOD_RESULT result; + unsigned int bytesread, bytestoread; + char packetbuffer[FMOD_PROFILE_CLIENT_READ_BUFFERSIZE]; + ProfilePacketHeader *packetheader = (ProfilePacketHeader *)(&packetbuffer[0]); + + if (isDead()) + { + return FMOD_OK; + } + + for (;;) + { + /* + Read a packet header + */ + bytestoread = sizeof(ProfilePacketHeader); + result = FMOD_OS_Net_Read(mSocket, (char *)packetheader, bytestoread, &bytesread); + if (result == FMOD_ERR_NET_WOULD_BLOCK) + { + /* + No packets to be read + */ + break; + } + else if (result != FMOD_OK) + { + /* + Consider any problems fatal and kill this client + */ + mFlags |= FMOD_PROFILECLIENT_FLAGS_DEAD; + return FMOD_OK; + } + + if (bytesread != bytestoread) // This needs to be handled gracefully + { + mFlags |= FMOD_PROFILECLIENT_FLAGS_DEAD; + return FMOD_OK; + } + + /* + Endian swap the packet header if needed + */ +#ifdef PLATFORM_ENDIAN_BIG + packetheader->size = FMOD_SWAPENDIAN_DWORD(packetheader->size); + packetheader->timestamp = FMOD_SWAPENDIAN_DWORD(packetheader->timestamp); +#endif + + /* + Read the packet data + */ + for (;;) + { + bytestoread = packetheader->size - sizeof(ProfilePacketHeader); + result = FMOD_OS_Net_Read(mSocket, ((char *)packetheader) + sizeof(ProfilePacketHeader), bytestoread, &bytesread); + if (result == FMOD_OK) + { + break; + } + + if (result != FMOD_ERR_NET_WOULD_BLOCK) + { + /* + Consider any problems fatal and kill this client + */ + mFlags |= FMOD_PROFILECLIENT_FLAGS_DEAD; + return FMOD_OK; + } + + FMOD_OS_Time_Sleep(1); + } + + if (bytesread != bytestoread) // This needs to be handled gracefully + { + mFlags |= FMOD_PROFILECLIENT_FLAGS_DEAD; + return FMOD_OK; + } + + /* + Now actually process the packet + */ + switch (packetheader->type) + { + case FMOD_PROFILE_DATATYPE_CONTROL : + { + switch (packetheader->subtype) + { + case FMOD_PROFILE_DATASUBTYPE_CONTROL_REQUESTDATA : + { + ProfilePacketControlRequestData *p = (ProfilePacketControlRequestData *)packetheader; + + /* + Endian swap the subtype parameters if needed + */ +#ifdef PLATFORM_ENDIAN_BIG + p->updatetime = FMOD_SWAPENDIAN_DWORD(p->updatetime); +#endif + + result = requestDataType(p->type, p->subtype, p->updatetime); + if (result != FMOD_OK) + { + return result; + } + + break; + } + } + + break; + } + + default : + { + break; + } + } + } + + return FMOD_OK; +} + + +FMOD_RESULT ProfileClient::sendData() +{ + FMOD_RESULT result = FMOD_OK; + unsigned int byteswritten = 0; + unsigned int bytestowrite = (unsigned int)(mBufferWritePos - mBufferReadPos); + + if (isDead() || !bytestowrite) + { + return FMOD_OK; + } + + while (bytestowrite) + { + unsigned int chunksize = FMOD_MIN((unsigned int)FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE, bytestowrite); + + result = FMOD_OS_Net_Write(mSocket, mBufferReadPos, chunksize, &byteswritten); + if (result != FMOD_OK) + { + return result; + } + + mBufferReadPos += byteswritten; + bytestowrite -= byteswritten; + } + + // Buffer now fully emptied + mBufferReadPos = mBufferWritePos = mBuffer; + return FMOD_OK; +} + + +} + +#endif //FMOD_SUPPORT_PROFILE diff --git a/src/fmod_profile.h b/src/fmod_profile.h new file mode 100755 index 0000000..4c9ecf8 --- /dev/null +++ b/src/fmod_profile.h @@ -0,0 +1,140 @@ +#ifndef _FMOD_PROFILE_H +#define _FMOD_PROFILE_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#ifdef FMOD_SUPPORT_PROFILE + +#ifndef _FMOD_H +#include "fmod.h" +#endif +#ifndef _FMOD_LINKEDLIST_H +#include "fmod_linkedlist.h" +#endif +#ifndef _FMOD_LOCALCRITICALSECTION_H +#include "fmod_localcriticalsection.h" +#endif +#ifndef _FMOD_PROFILE_PKT_H +#include "fmod_profile_pkt.h" +#endif +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class SystemI; + class ProfileModule; + + const int FMOD_PROFILE_UPDATE_SPEED = 50; + const int FMOD_PROFILE_MAX_DATATYPES = 32; + const int FMOD_PROFILE_CLIENT_READ_BUFFERSIZE = 16384; + const int FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE = 16384; + + const unsigned int FMOD_PROFILECLIENT_FLAGS_DEAD = 0x00000001; + + typedef struct + { + unsigned char type; + unsigned char subtype; + unsigned int updatetime; // How often the client wants to get this data type + unsigned int lastdatatime; // Time of last update + + } ProfileDataType; + + + /* + The Profile class gets input from ProfileModules and distributes it to ProfileClients + */ + class Profile + { + DECLARE_MEMORYTRACKER + + private : + + void *mListenSocket; + LinkedListNode mClientHead; // List of connected client apps. Accessed from multiple threads - protected with mCrit. + LinkedListNode mModuleHead; // List of registered profiler modules + FMOD_OS_CRITICALSECTION *mCrit; + unsigned int mTimeSinceLastUpdate; + unsigned int mInitialTimestamp; + + + public : + + Profile(); + + FMOD_RESULT init(unsigned short port); + FMOD_RESULT release(); + FMOD_RESULT update(SystemI *system, unsigned int dt); + FMOD_RESULT registerModule(ProfileModule *module); + FMOD_RESULT unRegisterModule(ProfileModule *module); + FMOD_RESULT addPacket(ProfilePacketHeader *packet); + }; + + + /* + A ProfileModule collects data from FMOD and adds it to the profile data stream + */ + class ProfileModule : public LinkedListNode + { + public : + + unsigned int mUpdateTime; + unsigned int mTimeSinceLastUpdate; + + + ProfileModule(); + + virtual FMOD_RESULT init(); + virtual FMOD_RESULT release(); + virtual FMOD_RESULT update(SystemI *system, unsigned int dt); + }; + + + /* + A ProfileClient represents a remote app which is connected to the profile data stream + */ + class ProfileClient : public LinkedListNode + { + private : + + unsigned int mFlags; + void *mSocket; + char *mBuffer; // Buffer that data packets will be accumulated in before sending + char *mBufferWritePos; + char *mBufferReadPos; + unsigned int mBufferSize; + ProfileDataType mDataType[FMOD_PROFILE_MAX_DATATYPES]; // Array of data types that this client wants to see + + FMOD_RESULT readData(); // Read data packets from the client app and act on them + FMOD_RESULT sendData(); // Send all data in this client's queue to the client + + + public : + + ProfileClient(); + + FMOD_RESULT init(void *socket); + FMOD_RESULT release(); + FMOD_RESULT update(unsigned int dt); + FMOD_RESULT addPacket(ProfilePacketHeader *packet); // Add a data packet to this client's queue + FMOD_RESULT requestDataType(unsigned char type, unsigned char subtype, unsigned int updatetime); + bool wantsData(ProfilePacketHeader *packet); // Returns true if this client wants a packet of this type/subtype at this time + bool isDead() { return (mFlags & FMOD_PROFILECLIENT_FLAGS_DEAD); } // Returns true if this client's connection is broken in any way + }; + + + FMOD_RESULT FMOD_Profile_Create(unsigned short port); + FMOD_RESULT FMOD_Profile_Release(); + FMOD_RESULT FMOD_Profile_Update(SystemI *system, unsigned int dt); + + extern Profile *g_profile; +} + +#endif // FMOD_SUPPORT_PROFILE + +#endif // _FMOD_PROFILE_H + diff --git a/src/fmod_profile_channel.cpp b/src/fmod_profile_channel.cpp new file mode 100755 index 0000000..944ceb4 --- /dev/null +++ b/src/fmod_profile_channel.cpp @@ -0,0 +1,210 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL) + +#include "fmod_profile_channel.h" +#include "fmod_systemi.h" +#include "fmod_output_software.h" +#include "fmod_output_emulated.h" + +namespace FMOD +{ + +FMOD_RESULT FMOD_ProfileChannel_Create() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileChannel) + { + return FMOD_OK; + } + + gGlobal->gProfileChannel = FMOD_Object_Alloc(ProfileChannel); + if (!gGlobal->gProfileChannel) + { + return FMOD_ERR_MEMORY; + } + + result = gGlobal->gProfileChannel->init(); + if (result != FMOD_OK) + { + return result; + } + + return gGlobal->gProfile->registerModule(gGlobal->gProfileChannel); +} + + +FMOD_RESULT FMOD_ProfileChannel_Release() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileChannel) + { + result = gGlobal->gProfile->unRegisterModule(gGlobal->gProfileChannel); + if (result != FMOD_OK) + { + return result; + } + + result = gGlobal->gProfileChannel->release(); + gGlobal->gProfileChannel = 0; + } + + return result; +} + + +/*===================================================================*/ + + +ProfileChannel::ProfileChannel() +{ + +} + + +FMOD_RESULT ProfileChannel::init() +{ + return FMOD_OK; +} + + +FMOD_RESULT ProfileChannel::release() +{ + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileChannel::update(FMOD::SystemI *system, unsigned int delta) +{ + FMOD_RESULT result = FMOD_OK; + int softwareChannels = 0; + int totalSoftwareChannels = 0; + int hardwareChannels = 0; + int totalHardwareChannels = 0; + int emulatedChannels = 0; + int maximumChannels = 0; + ProfilePacketChannelTotals packet; + + /* + Get number of used software channels + */ +#ifdef FMOD_SUPPORT_SOFTWARE + if (system->mSoftware->mChannelPool) + { + result = system->mSoftware->mChannelPool->getChannelsUsed(&softwareChannels); + if (result != FMOD_OK) + { + return result; + } + + result = system->mSoftware->mChannelPool->getNumChannels(&totalSoftwareChannels); + if (result != FMOD_OK) + { + return result; + } + } +#endif + + /* + Get number of used hardware channels + */ + if (system->mOutput) + { + int totalCount = 0; + int usedCount = 0; + + if (system->mOutput->mChannelPool) + { + result = system->mOutput->mChannelPool->getChannelsUsed(&usedCount); + if (result != FMOD_OK) + { + return result; + } + + result = system->mOutput->mChannelPool->getNumChannels(&totalCount); + if (result != FMOD_OK) + { + return result; + } + + hardwareChannels += usedCount; + totalHardwareChannels += totalCount; + } + + if (system->mOutput->mChannelPool3D && (system->mOutput->mChannelPool != system->mOutput->mChannelPool3D)) + { + result = system->mOutput->mChannelPool3D->getChannelsUsed(&usedCount); + if (result != FMOD_OK) + { + return result; + } + + result = system->mOutput->mChannelPool3D->getNumChannels(&totalCount); + if (result != FMOD_OK) + { + return result; + } + + hardwareChannels += usedCount; + totalHardwareChannels += totalCount; + } + } + + /* + Get number of used emulated channels + */ + if (system->mEmulated && system->mEmulated->mChannelPool) + { + result = system->mEmulated->mChannelPool->getChannelsUsed(&emulatedChannels); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Get total number of channels allocated + */ + maximumChannels = system->mNumChannels; + + /* + Send the data packet + */ + packet.hdr.size = sizeof(ProfilePacketChannelTotals); + packet.hdr.type = FMOD_PROFILE_DATATYPE_CHANNEL; + packet.hdr.subtype = FMOD_PROFILE_DATASUBTYPE_CHANNEL_TOTALS; + packet.hdr.version = FMOD_PROFILE_CHANNEL_VERSION; + + packet.software = softwareChannels; + packet.softwareTotal = totalSoftwareChannels; + packet.hardware = hardwareChannels; + packet.hardwareTotal = totalHardwareChannels; + packet.emulated = emulatedChannels; + packet.maximum = maximumChannels; + +#ifdef PLATFORM_ENDIAN_BIG + // Endian swap data + packet.software = FMOD_SWAPENDIAN_DWORD(packet.software); + packet.softwareTotal = FMOD_SWAPENDIAN_DWORD(packet.softwareTotal); + packet.hardware = FMOD_SWAPENDIAN_DWORD(packet.hardware); + packet.hardwareTotal = FMOD_SWAPENDIAN_DWORD(packet.hardwareTotal); + packet.emulated = FMOD_SWAPENDIAN_DWORD(packet.emulated); + packet.maximum = FMOD_SWAPENDIAN_DWORD(packet.maximum); +#endif + + result = gGlobal->gProfile->addPacket((ProfilePacketHeader *)&packet); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + +} // namespace FMOD + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_CHANNEL diff --git a/src/fmod_profile_channel.h b/src/fmod_profile_channel.h new file mode 100755 index 0000000..8b637f1 --- /dev/null +++ b/src/fmod_profile_channel.h @@ -0,0 +1,38 @@ +#ifndef _FMOD_PROFILE_CHANNEL_H +#define _FMOD_PROFILE_CHANNEL_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL) + +#ifndef _FMOD_PROFILE_H +#include "fmod_profile.h" +#endif +#ifndef _FMOD_PROFILE_CHANNEL_PKT_H +#include "fmod_profile_channel_pkt.h" +#endif + +namespace FMOD +{ + class ProfileChannel : public ProfileModule + { + public : + + ProfileChannel(); + + FMOD_RESULT init(); + FMOD_RESULT release(); + FMOD_RESULT update(SystemI *system, unsigned int delta); + }; + + FMOD_RESULT FMOD_ProfileChannel_Create(); + FMOD_RESULT FMOD_ProfileChannel_Release(); + + extern ProfileChannel *g_profile_channel; +} + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_CHANNEL + +#endif // _FMOD_PROFILE_CHANNEL_H diff --git a/src/fmod_profile_channel_pkt.h b/src/fmod_profile_channel_pkt.h new file mode 100755 index 0000000..72f9be1 --- /dev/null +++ b/src/fmod_profile_channel_pkt.h @@ -0,0 +1,35 @@ +#ifndef _FMOD_PROFILE_CHANNEL_PKT_H +#define _FMOD_PROFILE_CHANNEL_PKT_H + +#ifndef _FMOD_PROFILE_PKT_H +#include "fmod_profile_pkt.h" +#endif + +namespace FMOD +{ + const char FMOD_PROFILE_CHANNEL_VERSION = 0; + + enum + { + FMOD_PROFILE_DATASUBTYPE_CHANNEL_TOTALS = 0 + }; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + typedef struct // FMOD_PROFILE_DATASUBTYPE_CHANNEL_TOTALS + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + int hardware FMOD_PACKED_INTERNAL; + int hardwareTotal FMOD_PACKED_INTERNAL; + int software FMOD_PACKED_INTERNAL; + int softwareTotal FMOD_PACKED_INTERNAL; + int emulated FMOD_PACKED_INTERNAL; + int maximum FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketChannelTotals; +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack() +#endif +} + +#endif // _FMOD_PROFILE_CHANNEL_PKT_H diff --git a/src/fmod_profile_codec.cpp b/src/fmod_profile_codec.cpp new file mode 100755 index 0000000..49a04bc --- /dev/null +++ b/src/fmod_profile_codec.cpp @@ -0,0 +1,173 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CODEC) + +#include "fmod_profile_codec.h" +#include "fmod_systemi.h" +#include "fmod_dsp_codec.h" + +namespace FMOD +{ + +FMOD_RESULT FMOD_ProfileCodec_Create() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileCodec) + { + return FMOD_OK; + } + + gGlobal->gProfileCodec = FMOD_Object_Alloc(ProfileCodec); + if (!gGlobal->gProfileCodec) + { + return FMOD_ERR_MEMORY; + } + + result = gGlobal->gProfileCodec->init(); + if (result != FMOD_OK) + { + return result; + } + + return gGlobal->gProfile->registerModule(gGlobal->gProfileCodec); +} + + +FMOD_RESULT FMOD_ProfileCodec_Release() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileCodec) + { + result = gGlobal->gProfile->unRegisterModule(gGlobal->gProfileCodec); + if (result != FMOD_OK) + { + return result; + } + + result = gGlobal->gProfileCodec->release(); + gGlobal->gProfileCodec = 0; + } + + return result; +} + + +/*===================================================================*/ + + +ProfileCodec::ProfileCodec() +{ + +} + + +FMOD_RESULT ProfileCodec::init() +{ + return FMOD_OK; +} + + +FMOD_RESULT ProfileCodec::release() +{ + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileCodec::update(FMOD::SystemI *system, unsigned int delta) +{ + FMOD_RESULT result = FMOD_OK; + int mpegCodecsUsed = 0; + int mpegCodecsTotal = 0; + int adpcmCodecsUsed = 0; + int adpcmCodecsTotal = 0; + int xmaCodecsUsed = 0; + int xmaCodecsTotal = 0; + int rawCodecsUsed = 0; + int rawCodecsTotal = 0; + ProfilePacketCodecTotals packet; + +#ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + mpegCodecsUsed = getNumFreeCodecs(system->mDSPCodecPool_MPEG); + mpegCodecsTotal = system->mDSPCodecPool_MPEG.mNumDSPCodecs; + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + adpcmCodecsUsed = getNumFreeCodecs(system->mDSPCodecPool_ADPCM); + adpcmCodecsTotal = system->mDSPCodecPool_ADPCM.mNumDSPCodecs; + #endif + #ifdef FMOD_SUPPORT_XMA + xmaCodecsUsed = getNumFreeCodecs(system->mDSPCodecPool_XMA); + xmaCodecsTotal = system->mDSPCodecPool_XMA.mNumDSPCodecs; + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + rawCodecsUsed = getNumFreeCodecs(system->mDSPCodecPool_RAW); + rawCodecsTotal = system->mDSPCodecPool_RAW.mNumDSPCodecs; + #endif +#endif + + /* + Send the data packet + */ + packet.hdr.size = sizeof(ProfilePacketCodecTotals); + packet.hdr.type = FMOD_PROFILE_DATATYPE_CODEC; + packet.hdr.subtype = FMOD_PROFILE_DATASUBTYPE_CODEC_TOTALS; + packet.hdr.version = FMOD_PROFILE_CODEC_VERSION; + + packet.mpeg = mpegCodecsUsed; + packet.mpegTotal = mpegCodecsTotal; + packet.adpcm = adpcmCodecsUsed; + packet.adpcmTotal = adpcmCodecsTotal; + packet.xma = xmaCodecsUsed; + packet.xmaTotal = xmaCodecsTotal; + packet.raw = rawCodecsUsed; + packet.rawTotal = rawCodecsTotal; + +#ifdef PLATFORM_ENDIAN_BIG + // Endian swap data + packet.mpeg = FMOD_SWAPENDIAN_DWORD(packet.mpeg); + packet.mpegTotal = FMOD_SWAPENDIAN_DWORD(packet.mpegTotal); + packet.adpcm = FMOD_SWAPENDIAN_DWORD(packet.adpcm); + packet.adpcmTotal = FMOD_SWAPENDIAN_DWORD(packet.adpcmTotal); + packet.xma = FMOD_SWAPENDIAN_DWORD(packet.xma); + packet.xmaTotal = FMOD_SWAPENDIAN_DWORD(packet.xmaTotal); + packet.raw = FMOD_SWAPENDIAN_DWORD(packet.raw); + packet.rawTotal = FMOD_SWAPENDIAN_DWORD(packet.rawTotal); +#endif + + result = gGlobal->gProfile->addPacket((ProfilePacketHeader *)&packet); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_SOFTWARE +int ProfileCodec::getNumFreeCodecs(const DSPCodecPool &codecPool) const +{ + int usedCodecCount = 0; + + for (int i = 0; i < codecPool.mNumDSPCodecs; i++) + { + bool finished; + + codecPool.mPool[i]->getFinished(&finished); + + if (!codecPool.mAllocated[i] && finished) + { + usedCodecCount++; + } + } + + return (codecPool.mNumDSPCodecs - usedCodecCount); +} +#endif + +} // namespace FMOD + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_CODEC diff --git a/src/fmod_profile_codec.h b/src/fmod_profile_codec.h new file mode 100755 index 0000000..d7d6516 --- /dev/null +++ b/src/fmod_profile_codec.h @@ -0,0 +1,47 @@ +#ifndef _FMOD_PROFILE_CODEC_H +#define _FMOD_PROFILE_CODEC_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CODEC) + +#ifndef _FMOD_PROFILE_H +#include "fmod_profile.h" +#endif +#ifndef _FMOD_PROFILE_CODEC_PKT_H +#include "fmod_profile_codec_pkt.h" +#endif +#ifndef _FMOD_DSP_CODECPOOL_H +#include "fmod_dsp_codecpool.h" +#endif + +namespace FMOD +{ + class ProfileCodec : public ProfileModule + { + public : + + ProfileCodec(); + + FMOD_RESULT init(); + FMOD_RESULT release(); + FMOD_RESULT update(SystemI *system, unsigned int delta); + + private : + +#ifdef FMOD_SUPPORT_SOFTWARE + int getNumFreeCodecs(const DSPCodecPool &codecPool) const; +#endif + }; + + FMOD_RESULT FMOD_ProfileCodec_Create(); + FMOD_RESULT FMOD_ProfileCodec_Release(); + + extern ProfileCodec *g_profile_codec; +} + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_CODEC + +#endif // _FMOD_PROFILE_CODEC_H diff --git a/src/fmod_profile_codec_pkt.h b/src/fmod_profile_codec_pkt.h new file mode 100755 index 0000000..7d276ef --- /dev/null +++ b/src/fmod_profile_codec_pkt.h @@ -0,0 +1,37 @@ +#ifndef _FMOD_PROFILE_CODEC_PKT_H +#define _FMOD_PROFILE_CODEC_PKT_H + +#ifndef _FMOD_PROFILE_PKT_H +#include "fmod_profile_pkt.h" +#endif + +namespace FMOD +{ + const char FMOD_PROFILE_CODEC_VERSION = 0; + + enum + { + FMOD_PROFILE_DATASUBTYPE_CODEC_TOTALS = 0 + }; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + typedef struct // FMOD_PROFILE_DATASUBTYPE_CODEC_TOTALS + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + int mpeg FMOD_PACKED_INTERNAL; + int mpegTotal FMOD_PACKED_INTERNAL; + int adpcm FMOD_PACKED_INTERNAL; + int adpcmTotal FMOD_PACKED_INTERNAL; + int xma FMOD_PACKED_INTERNAL; + int xmaTotal FMOD_PACKED_INTERNAL; + int raw FMOD_PACKED_INTERNAL; + int rawTotal FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketCodecTotals; +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack() +#endif +} + +#endif // _FMOD_PROFILE_CODEC_PKT_H diff --git a/src/fmod_profile_cpu.cpp b/src/fmod_profile_cpu.cpp new file mode 100755 index 0000000..f7f1d93 --- /dev/null +++ b/src/fmod_profile_cpu.cpp @@ -0,0 +1,122 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CPU) + +#include "fmod_profile_cpu.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +FMOD_RESULT FMOD_ProfileCpu_Create() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileCpu) + { + return FMOD_OK; + } + + gGlobal->gProfileCpu = FMOD_Object_Alloc(ProfileCpu); + if (!gGlobal->gProfileCpu) + { + return FMOD_ERR_MEMORY; + } + + result = gGlobal->gProfileCpu->init(); + if (result != FMOD_OK) + { + return result; + } + + return gGlobal->gProfile->registerModule(gGlobal->gProfileCpu); +} + + +FMOD_RESULT FMOD_ProfileCpu_Release() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileCpu) + { + result = gGlobal->gProfile->unRegisterModule(gGlobal->gProfileCpu); + if (result != FMOD_OK) + { + return result; + } + + result = gGlobal->gProfileCpu->release(); + gGlobal->gProfileCpu = 0; + } + + return result; +} + + +/*===================================================================*/ + + +ProfileCpu::ProfileCpu() +{ + +} + + +FMOD_RESULT ProfileCpu::init() +{ + return FMOD_OK; +} + + +FMOD_RESULT ProfileCpu::release() +{ + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileCpu::update(FMOD::SystemI *system, unsigned int delta) +{ + FMOD_RESULT result = FMOD_OK; + float dspUsage = 0.0f; + float streamUsage = 0.0f; + float updateUsage = 0.0f; + float geometryUsage = 0.0f; + ProfilePacketCpuTotals packet; + + result = system->getCPUUsage(&dspUsage, &streamUsage, &geometryUsage, &updateUsage, 0); + if (result != FMOD_OK) + { + return result; + } + + packet.hdr.size = sizeof(ProfilePacketCpuTotals); + packet.hdr.type = FMOD_PROFILE_DATATYPE_CPU; + packet.hdr.subtype = FMOD_PROFILE_DATASUBTYPE_CPU_TOTALS; + packet.hdr.version = FMOD_PROFILE_CPU_VERSION; + + packet.dspUsage = dspUsage; + packet.streamUsage = streamUsage; + packet.geometryUsage = geometryUsage; + packet.updateUsage = updateUsage; + +#ifdef PLATFORM_ENDIAN_BIG + // Endian swap usage data + FMOD_SWAPENDIAN_FLOAT(packet.dspUsage); + FMOD_SWAPENDIAN_FLOAT(packet.streamUsage); + FMOD_SWAPENDIAN_FLOAT(packet.updateUsage); +#endif + + result = gGlobal->gProfile->addPacket((ProfilePacketHeader *)&packet); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + +} // namespace FMOD + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_CPU diff --git a/src/fmod_profile_cpu.h b/src/fmod_profile_cpu.h new file mode 100755 index 0000000..c411130 --- /dev/null +++ b/src/fmod_profile_cpu.h @@ -0,0 +1,38 @@ +#ifndef _FMOD_PROFILE_CPU_H +#define _FMOD_PROFILE_CPU_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CPU) + +#ifndef _FMOD_PROFILE_H +#include "fmod_profile.h" +#endif +#ifndef _FMOD_PROFILE_CPU_PKT_H +#include "fmod_profile_cpu_pkt.h" +#endif + +namespace FMOD +{ + class ProfileCpu : public ProfileModule + { + public : + + ProfileCpu(); + + FMOD_RESULT init(); + FMOD_RESULT release(); + FMOD_RESULT update(SystemI *system, unsigned int delta); + }; + + FMOD_RESULT FMOD_ProfileCpu_Create(); + FMOD_RESULT FMOD_ProfileCpu_Release(); + + extern ProfileCpu *g_profile_cpu; +} + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_CPU + +#endif // _FMOD_PROFILE_CPU_H diff --git a/src/fmod_profile_cpu_pkt.h b/src/fmod_profile_cpu_pkt.h new file mode 100755 index 0000000..0003003 --- /dev/null +++ b/src/fmod_profile_cpu_pkt.h @@ -0,0 +1,41 @@ +#ifndef _FMOD_PROFILE_CPU_PKT_H +#define _FMOD_PROFILE_CPU_PKT_H + +#ifndef _FMOD_PROFILE_PKT_H +#include "fmod_profile_pkt.h" +#endif + +namespace FMOD +{ + const char FMOD_PROFILE_CPU_VERSION = 1; + + enum + { + FMOD_PROFILE_DATASUBTYPE_CPU_TOTALS = 0 + }; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + typedef struct // FMOD_PROFILE_DATASUBTYPE_CPU_TOTALS (version 0) + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + float dspUsage FMOD_PACKED_INTERNAL; + float streamUsage FMOD_PACKED_INTERNAL; + float updateUsage FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketCpuTotalsV0; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_CPU_TOTALS (version 1) + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + float dspUsage FMOD_PACKED_INTERNAL; + float streamUsage FMOD_PACKED_INTERNAL; + float updateUsage FMOD_PACKED_INTERNAL; + float geometryUsage FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketCpuTotals; +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack() +#endif +} + +#endif // _FMOD_PROFILE_CPU_PKT_H diff --git a/src/fmod_profile_dsp.cpp b/src/fmod_profile_dsp.cpp new file mode 100755 index 0000000..bb7bbd0 --- /dev/null +++ b/src/fmod_profile_dsp.cpp @@ -0,0 +1,274 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP) + +#include "fmod_profile_dsp.h" +#include "fmod_systemi.h" +#include "fmod_autocleanup.h" + +namespace FMOD +{ + +FMOD_RESULT FMOD_ProfileDsp_Create() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileDsp) + { + return FMOD_OK; + } + + gGlobal->gProfileDsp = FMOD_Object_Alloc(ProfileDsp); + if (!gGlobal->gProfileDsp) + { + return FMOD_ERR_MEMORY; + } + + result = gGlobal->gProfileDsp->init(); + if (result != FMOD_OK) + { + gGlobal->gProfileDsp->release(); + gGlobal->gProfileDsp = 0; + return result; + } + + return gGlobal->gProfile->registerModule(gGlobal->gProfileDsp); +} + + +FMOD_RESULT FMOD_ProfileDsp_Release() +{ + FMOD_RESULT result = FMOD_OK; + + if (gGlobal->gProfileDsp) + { + result = gGlobal->gProfile->unRegisterModule(gGlobal->gProfileDsp); + if (result != FMOD_OK) + { + return result; + } + + result = gGlobal->gProfileDsp->release(); + gGlobal->gProfileDsp = 0; + } + + return result; +} + + +/*===================================================================*/ + + +ProfileDsp::ProfileDsp() +{ + mNodeStack = NULL; + mMaxStackNodes = 32; + + mDataPacket = NULL; + mPacketHeader = NULL; + mPacketNodes = NULL; + mNumPacketNodes = 0; + mMaxPacketNodes = 300; +} + + +FMOD_RESULT ProfileDsp::init() +{ + AutoFreeClear<FMOD::DSPI **> autoNodeStack; + AutoFreeClear<char *> autoDataPacket; + + // Stack used for iterating through DSP nodes, stack size increases when max is reached + mNodeStack = (FMOD::DSPI **)FMOD_Memory_Alloc(sizeof(FMOD::DSPI*) * mMaxStackNodes); + autoNodeStack = &mNodeStack; + CHECK_RESULT(mNodeStack == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + // Packet to be sent over the network + mDataPacket = (char *)FMOD_Memory_Calloc(sizeof(ProfilePacketDspNetwork) + (mMaxPacketNodes * sizeof(ProfileDspRawNode))); + autoDataPacket = &mDataPacket; + CHECK_RESULT(mDataPacket == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + mPacketHeader = (ProfilePacketDspNetwork *)mDataPacket; + mPacketNodes = (ProfileDspRawNode *)(mDataPacket + sizeof(ProfilePacketDspNetwork)); + + autoNodeStack.releasePtr(); + autoDataPacket.releasePtr(); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileDsp::release() +{ + if (mNodeStack) + { + FMOD_Memory_Free(mNodeStack); + mNodeStack = NULL; + } + + if (mDataPacket) + { + FMOD_Memory_Free(mDataPacket); + mDataPacket = NULL; + mPacketHeader = NULL; + mPacketNodes = NULL; + } + + FMOD_Memory_Free(this); + return FMOD_OK; +} + + +FMOD_RESULT ProfileDsp::update(FMOD::SystemI *system, unsigned int delta) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int numStackNodes = 0; + LocalCriticalSection criticalsection(system->mDSPConnectionCrit, true); + + mNumPacketNodes = 0; + + result = system->getDSPHead(&mNodeStack[numStackNodes++]); + CHECK_RESULT(result); + + while (numStackNodes > 0) + { + FMOD::DSPI *dspNode = NULL; + ProfileDspRawNode *dspNetNode = NULL; + bool isActive = false; + bool isBypass = false; + + if (mNumPacketNodes + 1 >= mMaxPacketNodes) + { + result = growPacketSpace(); + CHECK_RESULT(result); + } + + dspNode = mNodeStack[--numStackNodes]; + dspNetNode = &mPacketNodes[mNumPacketNodes++]; + + FMOD_RESULT result = dspNode->getInfo(dspNetNode->name, NULL, NULL, NULL, NULL); + CHECK_RESULT(result); + + result = dspNode->getNumInputs(&dspNetNode->numInputs, false); + CHECK_RESULT(result); + + result = dspNode->getActive(&isActive); + CHECK_RESULT(result); + + result = dspNode->getBypass(&isBypass); + CHECK_RESULT(result); + + dspNetNode->id = (FMOD_UINT64)dspNode; + dspNetNode->active = isActive ? 1 : 0; + dspNetNode->bypass = isBypass ? 1 : 0; + dspNetNode->treeLevel = dspNode->mTreeLevel; + dspNetNode->exclusiveCPUTime = isActive ? dspNode->mCPUUsage : 0; +#ifdef FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS + dspNetNode->numChannels = isActive ? dspNode->mNumPeakVolumeChans : 0; + FMOD_memcpy(dspNetNode->peakVolume, dspNode->mPeakVolume, sizeof(dspNode->mPeakVolume)); +#endif + + if (!isNodeDuplicate(dspNetNode->id)) + { + for (int i = dspNetNode->numInputs - 1; i >= 0; i--) + { + if (numStackNodes >= mMaxStackNodes) + { + result = growNodeStackSpace(); + CHECK_RESULT(result); + } + + result = dspNode->getInput(i, &mNodeStack[numStackNodes++], NULL, false); + CHECK_RESULT(result); + } + } + } + + result = sendPacket(system); + CHECK_RESULT(result == FMOD_ERR_NET_WOULD_BLOCK ? FMOD_OK : result); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileDsp::growPacketSpace() +{ + mMaxPacketNodes *= 2; + + mDataPacket = (char *)FMOD_Memory_ReAlloc(mDataPacket, sizeof(ProfilePacketDspNetwork) + (mMaxPacketNodes * sizeof(ProfileDspRawNode))); + CHECK_RESULT(mDataPacket == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + mPacketHeader = (ProfilePacketDspNetwork *)mDataPacket; + mPacketNodes = (ProfileDspRawNode *)(mDataPacket + sizeof(ProfilePacketDspNetwork)); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileDsp::growNodeStackSpace() +{ + mMaxStackNodes *= 2; + + mNodeStack = (FMOD::DSPI **)FMOD_Memory_ReAlloc(mNodeStack, sizeof(FMOD::DSPI *) * mMaxStackNodes); + CHECK_RESULT(mNodeStack == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + return FMOD_OK; +} + + +bool ProfileDsp::isNodeDuplicate(FMOD_UINT64 nodeId) +{ + for (unsigned int i = 0; i < mNumPacketNodes - 1; i++) + { + if (mPacketNodes[i].id == nodeId) + { + return true; + } + } + + return false; +} + + +FMOD_RESULT ProfileDsp::sendPacket(FMOD::SystemI *system) +{ + FMOD_RESULT result = FMOD_OK; + float dspUsage = 0.0f; + int maxOutputChannels = 0; + int maxInputChannels = 0; + + result = system->getCPUUsage(&dspUsage, NULL, NULL, NULL, NULL); + CHECK_RESULT(result); + + result = system->getSoftwareFormat(NULL, NULL, &maxOutputChannels, &maxInputChannels, NULL, NULL); + CHECK_RESULT(result); + + mPacketHeader->hdr.size = sizeof(ProfilePacketDspNetwork) + (sizeof(ProfileDspRawNode) * mNumPacketNodes); + mPacketHeader->hdr.timestamp = 0; + mPacketHeader->hdr.type = FMOD_PROFILE_DATATYPE_DSP; + mPacketHeader->hdr.subtype = FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK; + mPacketHeader->hdr.version = FMOD_PROFILE_DSP_VERSION; + mPacketHeader->hdr.flags = 0; + mPacketHeader->dspCPUUsage = dspUsage / 100.0f; + mPacketHeader->maxNumChannels = (maxOutputChannels < maxInputChannels) ? maxInputChannels : maxOutputChannels; + +#ifdef PLATFORM_ENDIAN_BIG + FMOD_SWAPENDIAN_FLOAT(mPacketHeader->dspCPUUsage); + + for (unsigned int i = 0; i < mNumPacketNodes; i++) + { + mPacketNodes[i].id = FMOD_SWAPENDIAN_QWORD(mPacketNodes[i].id); + mPacketNodes[i].numInputs = FMOD_SWAPENDIAN_DWORD(mPacketNodes[i].numInputs); + mPacketNodes[i].treeLevel = FMOD_SWAPENDIAN_WORD(mPacketNodes[i].treeLevel); + mPacketNodes[i].exclusiveCPUTime = FMOD_SWAPENDIAN_WORD(mPacketNodes[i].exclusiveCPUTime); + } +#endif + + result = gGlobal->gProfile->addPacket((ProfilePacketHeader *)mPacketHeader); + CHECK_RESULT(result); + + return FMOD_OK; +} + +} // namespace FMOD + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_DSP diff --git a/src/fmod_profile_dsp.h b/src/fmod_profile_dsp.h new file mode 100755 index 0000000..15eb21f --- /dev/null +++ b/src/fmod_profile_dsp.h @@ -0,0 +1,58 @@ +#ifndef _FMOD_PROFILE_DSP_H +#define _FMOD_PROFILE_DSP_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP) + +#ifndef _FMOD_PROFILE_H +#include "fmod_profile.h" +#endif +#ifndef _FMOD_PROFILE_DSP_PKT_H +#include "fmod_profile_dsp_pkt.h" +#endif + +namespace FMOD +{ + class DSPI; + + class ProfileDsp : public ProfileModule + { + friend class Profile; + + private : + + FMOD::DSPI **mNodeStack; + unsigned int mMaxStackNodes; + + char *mDataPacket; + ProfilePacketDspNetwork *mPacketHeader; + ProfileDspRawNode *mPacketNodes; + unsigned int mNumPacketNodes; + unsigned int mMaxPacketNodes; + + FMOD_RESULT growPacketSpace(); + FMOD_RESULT growNodeStackSpace(); + bool isNodeDuplicate(FMOD_UINT64 nodeId); + FMOD_RESULT sendPacket(FMOD::SystemI *system); + + public : + + ProfileDsp(); + + FMOD_RESULT init(); + FMOD_RESULT release(); + FMOD_RESULT update(SystemI *system, unsigned int delta); + }; + + FMOD_RESULT FMOD_ProfileDsp_Create(); + FMOD_RESULT FMOD_ProfileDsp_Release(); + + extern ProfileDsp *g_profile_dsp; +} + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_DSP + +#endif // _FMOD_PROFILE_DSP_H diff --git a/src/fmod_profile_dsp_pkt.h b/src/fmod_profile_dsp_pkt.h new file mode 100755 index 0000000..7b4f7d4 --- /dev/null +++ b/src/fmod_profile_dsp_pkt.h @@ -0,0 +1,75 @@ +#ifndef _FMOD_PROFILE_DSP_PKT_H +#define _FMOD_PROFILE_DSP_PKT_H + +#ifndef _FMOD_PROFILE_PKT_H +#include "fmod_profile_pkt.h" +#endif + +namespace FMOD +{ + const char FMOD_PROFILE_DSP_VERSION = 2; + + enum + { + FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK = 0 + }; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + typedef struct // FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK header (version 0) + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketDspNetworkV0; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK header (version 1) + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + float dspCPUUsage FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketDspNetworkV1; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK header (version 2) + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + float dspCPUUsage FMOD_PACKED_INTERNAL; + unsigned char maxNumChannels FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketDspNetwork; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK bulk nodes (version 0) + { + int id FMOD_PACKED_INTERNAL; + char name[32] FMOD_PACKED_INTERNAL; + int numInputs FMOD_PACKED_INTERNAL; + char active FMOD_PACKED_INTERNAL; + int treeLevel FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfileDspRawNodeV0; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK bulk nodes (version 1) + { + int id FMOD_PACKED_INTERNAL; + char name[32] FMOD_PACKED_INTERNAL; + int numInputs FMOD_PACKED_INTERNAL; + char active FMOD_PACKED_INTERNAL; + int treeLevel FMOD_PACKED_INTERNAL; + int exclusiveCPUTime FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfileDspRawNodeV1; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_DSP_NETWORK bulk nodes (version 2) + { + FMOD_UINT64 id FMOD_PACKED_INTERNAL; + char name[32] FMOD_PACKED_INTERNAL; + int numInputs FMOD_PACKED_INTERNAL; + char active FMOD_PACKED_INTERNAL; + char bypass FMOD_PACKED_INTERNAL; + unsigned short treeLevel FMOD_PACKED_INTERNAL; + unsigned short exclusiveCPUTime FMOD_PACKED_INTERNAL; + unsigned char numChannels FMOD_PACKED_INTERNAL; + unsigned char peakVolume[10] FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfileDspRawNode; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack() +#endif +} + +#endif // _FMOD_PROFILE_DSP_PKT_H diff --git a/src/fmod_profile_memory.cpp b/src/fmod_profile_memory.cpp new file mode 100755 index 0000000..bfa67e2 --- /dev/null +++ b/src/fmod_profile_memory.cpp @@ -0,0 +1,175 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY) + +#include "fmod_profile_memory.h" +#include "fmod_systemi.h" +#include "fmod_memory.h" + +namespace FMOD +{ + +ProfileMemory *g_profile_memory = 0; + + +FMOD_RESULT FMOD_ProfileMemory_Create() +{ + FMOD_RESULT result; + + if (g_profile_memory) + { + return FMOD_OK; + } + + g_profile_memory = FMOD_Object_Alloc(ProfileMemory); + if (!g_profile_memory) + { + return FMOD_ERR_MEMORY; + } + + result = g_profile_memory->init(); + if (result != FMOD_OK) + { + return result; + } + + return g_profile->registerModule(g_profile_memory); +} + + +FMOD_RESULT FMOD_ProfileMemory_Release() +{ + FMOD_RESULT result = FMOD_OK; + + if (g_profile_memory) + { + result = g_profile->unRegisterModule(g_profile_memory); + if (result != FMOD_OK) + { + return result; + } + + result = g_profile_memory->release(); + g_profile_memory = 0; + } + + return result; +} + + +/*===================================================================*/ + + +ProfileMemory::ProfileMemory() +{ + mUpdateTime = 1000; + mBuffer = 0; +} + + +FMOD_RESULT ProfileMemory::init() +{ + mBuffer = (char *)FMOD_Memory_Alloc(FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + return FMOD_OK; +} + + +FMOD_RESULT ProfileMemory::release() +{ + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + } + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +FMOD_RESULT ProfileMemory::update(FMOD::SystemI *system, unsigned int dt) +{ + FMOD_RESULT result; + MemPool *mempool = gGlobal->gSystemPool; + + ProfilePacketMemoryDump *pkt = (ProfilePacketMemoryDump *)mBuffer; + ProfilePacketMemoryBlock *blockptr = (ProfilePacketMemoryBlock *)(mBuffer + sizeof(ProfilePacketMemoryDump)); + + pkt->hdr.size = sizeof(pkt); + pkt->hdr.timestamp = 0; + pkt->hdr.type = FMOD_PROFILE_DATATYPE_MEMORY; + pkt->hdr.subtype = FMOD_PROFILE_DATASUBTYPE_MEMORY_DUMP; + pkt->hdr.version = FMOD_PROFILE_MEMORY_VERSION; + pkt->hdr.flags = 0; + + pkt->blocksize = mempool->mBlockSize; + pkt->sizeblocks = mempool->mSizeBlocks; + pkt->numallocs = 0; + + unsigned int bytesalloced = 0; + unsigned int realbytesalloced = 0; + unsigned int wastage = 0; + + for (int blockoffset=0; blockoffset < mempool->mSizeBlocks; ) + { + int byteoffset = blockoffset / 8; + int bitoffset = blockoffset & 7; + + if (mempool->mBitmap[byteoffset] & (1 << bitoffset)) + { + // Will we run out of buffer room if we use the next block? + if (sizeof(ProfilePacketMemoryDump) + (sizeof(ProfilePacketMemoryBlock) * (pkt->numallocs + 1)) > FMOD_PROFILE_CLIENT_WRITE_BUFFERSIZE) + { + /* Need to handle this gracefully... */ + return FMOD_ERR_INTERNAL; + } + + // got an alloced block + // if so, output it, see how big it is and skip to just past it + MemBlockHeader *block = (MemBlockHeader *)(mempool->mData + (blockoffset * mempool->mBlockSize)); + +//AJS FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ProfileMemory::update", "%d %d %d\n", block->mSize, block->mNumBlocks, block->mBlockOffset)); + + blockptr->mSize = block->mSize; + blockptr->mNumBlocks = block->mNumBlocks; + blockptr->mBlockOffset = block->mBlockOffset; + blockptr++; + + blockoffset += block->mNumBlocks; + + pkt->numallocs++; + + // it doesn't match exactly with memool internal vars - not taking into account the headers? + + bytesalloced += block->mSize; + realbytesalloced += (block->mNumBlocks * mempool->mBlockSize); + wastage = (realbytesalloced - bytesalloced); + } + else + { + blockoffset++; + } + } + +//AJS FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "ProfileMemory::update", "%d allocs\n", pkt->numallocs)); + + pkt->hdr.size = sizeof(ProfilePacketMemoryDump) + (pkt->numallocs * sizeof(ProfilePacketMemoryBlock)); + + result = g_profile->addPacket((ProfilePacketHeader *)pkt); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +} + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_MEMORY diff --git a/src/fmod_profile_memory.h b/src/fmod_profile_memory.h new file mode 100755 index 0000000..f31bbe4 --- /dev/null +++ b/src/fmod_profile_memory.h @@ -0,0 +1,43 @@ +#ifndef _FMOD_PROFILE_MEMORY_H +#define _FMOD_PROFILE_MEMORY_H + +#ifndef _FMOD_SETTINGS_H +#include "fmod_settings.h" +#endif + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY) + +#ifndef _FMOD_PROFILE_H +#include "fmod_profile.h" +#endif +#ifndef _FMOD_PROFILE_MEMORY_PKT_H +#include "fmod_profile_memory_pkt.h" +#endif + +namespace FMOD +{ + class ProfileMemory : public ProfileModule + { + private : + + char *mBuffer; + + + public : + + ProfileMemory(); + + FMOD_RESULT init(); + FMOD_RESULT release(); + FMOD_RESULT update(SystemI *system, unsigned int dt); + }; + + FMOD_RESULT FMOD_ProfileMemory_Create(); + FMOD_RESULT FMOD_ProfileMemory_Release(); + + extern ProfileMemory *g_profile_memory; +} + +#endif // FMOD_SUPPORT_PROFILE && FMOD_SUPPORT_PROFILE_MEMORY + +#endif diff --git a/src/fmod_profile_memory_pkt.h b/src/fmod_profile_memory_pkt.h new file mode 100755 index 0000000..1c76ff6 --- /dev/null +++ b/src/fmod_profile_memory_pkt.h @@ -0,0 +1,58 @@ +#ifndef _FMOD_PROFILE_MEMORY_PKT_H +#define _FMOD_PROFILE_MEMORY_PKT_H + +#ifndef _FMOD_PROFILE_PKT_H +#include "fmod_profile_pkt.h" +#endif + +namespace FMOD +{ + const char FMOD_PROFILE_MEMORY_VERSION = 0; + + enum + { + FMOD_PROFILE_DATASUBTYPE_MEMORY_DUMP = 0, + FMOD_PROFILE_DATASUBTYPE_MEMORY_ALLOC, + FMOD_PROFILE_DATASUBTYPE_MEMORY_FREE + }; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + typedef struct // FMOD_PROFILE_DATASUBTYPE_MEMORY_DUMP + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + unsigned int blocksize FMOD_PACKED_INTERNAL; // bytesize of a single block + unsigned int sizeblocks FMOD_PACKED_INTERNAL; // total number of blocks in heap + unsigned int numallocs FMOD_PACKED_INTERNAL; // number of allocated chunks + } FMOD_PACKED ProfilePacketMemoryDump; + + typedef struct + { + int mSize FMOD_PACKED_INTERNAL; + int mNumBlocks FMOD_PACKED_INTERNAL; + int mBlockOffset FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketMemoryBlock; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_MEMORY_ALLOC + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + unsigned int blockptr FMOD_PACKED_INTERNAL; + unsigned int size FMOD_PACKED_INTERNAL; + // file + // line + } FMOD_PACKED ProfilePacketMemoryAlloc; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_MEMORY_FREE + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + unsigned int blockptr FMOD_PACKED_INTERNAL; + // file + // line + } FMOD_PACKED ProfilePacketMemoryFree; +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack() +#endif +} + +#endif diff --git a/src/fmod_profile_pkt.h b/src/fmod_profile_pkt.h new file mode 100755 index 0000000..7bcafed --- /dev/null +++ b/src/fmod_profile_pkt.h @@ -0,0 +1,55 @@ +#ifndef _FMOD_PROFILE_PKT_H +#define _FMOD_PROFILE_PKT_H + +#ifndef _FMOD_TYPES_H +#include "fmod_types.h" +#endif + +namespace FMOD +{ + const int FMOD_PROFILE_VERSION = 0; + const int FMOD_PROFILE_PORT = 9264; + + enum + { + FMOD_PROFILE_DATATYPE_CONTROL = 0, // Client control + FMOD_PROFILE_DATATYPE_DSP, // DSP network + FMOD_PROFILE_DATATYPE_MEMORY, // Memory alloc/free + FMOD_PROFILE_DATATYPE_CPU, // CPU usage + FMOD_PROFILE_DATATYPE_CHANNEL, // Channel pool usage + FMOD_PROFILE_DATATYPE_CODEC, // Codec pool usage + FMOD_PROFILE_DATATYPE_MAX, + FMOD_PROFILE_DATATYPE_NONE = 255 + }; + + enum + { + FMOD_PROFILE_DATASUBTYPE_CONTROL_REQUESTDATA = 0 // Client request for a particular data type + }; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + typedef struct + { + unsigned int size FMOD_PACKED_INTERNAL; + unsigned int timestamp FMOD_PACKED_INTERNAL; + unsigned char type FMOD_PACKED_INTERNAL; + unsigned char subtype FMOD_PACKED_INTERNAL; + unsigned char version FMOD_PACKED_INTERNAL; + unsigned char flags FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketHeader; + + typedef struct // FMOD_PROFILE_DATASUBTYPE_CONTROL_REQUESTDATA + { + ProfilePacketHeader hdr FMOD_PACKED_INTERNAL; + unsigned char type FMOD_PACKED_INTERNAL; + unsigned char subtype FMOD_PACKED_INTERNAL; + unsigned int updatetime FMOD_PACKED_INTERNAL; + } FMOD_PACKED ProfilePacketControlRequestData; +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack() +#endif +} + +#endif diff --git a/src/fmod_reverb.cpp b/src/fmod_reverb.cpp new file mode 100755 index 0000000..7c02b01 --- /dev/null +++ b/src/fmod_reverb.cpp @@ -0,0 +1,187 @@ +/*$ preserve start $*/ +#include "fmod_settings.h" +#include "fmod.hpp" + +#include "fmod_reverbi.h" + +namespace FMOD +{ + + +/*$ preserve end $*/ + + +FMOD_RESULT Reverb::release() +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->release(); + } +} + + +FMOD_RESULT Reverb::set3DAttributes(const FMOD_VECTOR *position, float mindistance, float maxdistance) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->set3DAttributes(position, mindistance, maxdistance); + } +} + + +FMOD_RESULT Reverb::get3DAttributes(FMOD_VECTOR *position, float *mindistance, float *maxdistance) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->get3DAttributes(position, mindistance, maxdistance); + } +} + + +FMOD_RESULT Reverb::setProperties(const FMOD_REVERB_PROPERTIES *properties) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->setProperties(properties); + } +} + + +FMOD_RESULT Reverb::getProperties(FMOD_REVERB_PROPERTIES *properties) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->getProperties(properties); + } +} + + +FMOD_RESULT Reverb::setActive(bool active) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->setActive(active); + } +} + + +FMOD_RESULT Reverb::getActive(bool *active) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->getActive(active); + } +} + + +FMOD_RESULT Reverb::setUserData(void *_userdata) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->setUserData(_userdata); + } +} + + +FMOD_RESULT Reverb::getUserData(void **_userdata) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->getUserData(_userdata); + } +} + + +FMOD_RESULT Reverb::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + ReverbI *reverbi; + + result = ReverbI::validate(this, &reverbi); + if (result != FMOD_OK) + { + return result; + } + else + { + return reverbi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} + +/*$ preserve end $*/ diff --git a/src/fmod_reverbi.cpp b/src/fmod_reverbi.cpp new file mode 100755 index 0000000..ef59dc3 --- /dev/null +++ b/src/fmod_reverbi.cpp @@ -0,0 +1,1353 @@ +#include "fmod_settings.h" + +#include "fmod_dsp_sfxreverb.h" +#include "fmod_pluginfactory.h" +#include "fmod_memory.h" +#include "fmod_systemi.h" +#include "fmod_reverbi.h" +#include "fmod_outputi.h" +#include "fmod_3d.h" +#include "fmod.h" + + +// A reverb's presence gain depends partly on the Distance Gain - +// the distance of the source/listener position to the reverb. +// +// The distance gain at (distance <= min radius) = 0 dB. +// The distance gain at (distance > min radius and < kMAXDISTANCE) = kMAXDISTANCE_GAINDB dB +// Between, the distance gain is a linear dB interpolation between these two values proportional to the +// distance. + +// Presence gain is also governed by the reverb occlusion factor between +// the reverb centre and the source/listener position. + + +namespace FMOD +{ + +#define kMAXDISTANCE_GAINDB (-60.0f) // The gain of the reverb at max distance +#define k20TH_MAXDISTANCE_GAINDB (-3.0f) // 1/20th of the gain of the reverb at max distance + +#define kREVERB_GAIN_COEF 0.002f // Coefficient for warping the power law for additive gain (see below) +#define kREVERB_GAIN_COEF_INV 500.0f // Reciprocal of kREVERB_GAIN_COEF (depends on the above) + +ReverbI::ReverbI() +{ + int count; + + for (count = 0; count < FMOD_REVERB_MAXINSTANCES; count++) + { + mInstance[count].mDSP = 0; + mInstance[count].mChannelData = 0; + } + + mSystem = 0; + mPosition.x = 0; + mPosition.y = 0; + mPosition.z = 0.0f; + mMinRadius = 0.0f; + mMaxRadius = 0.0f; + mMinMaxDistance = 0.0f; + mGain = 0.0f; + m3D = false; + mDisableIfNoEnvironment = false; + mActive = true; + mUserData = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::validate(Reverb *reverb, ReverbI **reverbi) +{ + if (!reverbi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!reverb) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *reverbi = (ReverbI *)reverb; + + return FMOD_OK; +} +/* +[ + [DESCRIPTION] + Initialise internals + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::init(SystemI* system, bool is_3d, FMOD_REVERB_MODE mode) +{ + FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF; // then initialise them to 'off' + int instance; + + release(false); /* Release any resources */ + + mSystem = system; + mMode = mode; + + for (instance = 0; instance < FMOD_REVERB_MAXINSTANCES; instance++) + { + FMOD_memset(&mInstance[instance].mProps, 0xcc, sizeof(FMOD_REVERB_PROPERTIES)); /* invalidate properties */ + mInstance[instance].mProps.Instance = instance; + setProperties(&prop); + } + + + /* + Create the first instance's channeldata. + */ + instance = 0; + if (!mInstance[instance].mChannelData) + { + int channel; + FMOD_REVERB_CHANNELPROPERTIES defaultprop = { 0, 0, 0, 0, 0, 0, 0, 0.25f, 1.5f, 1.0f, 0, 1.0f, 0, 0, 0, 0, 1.0f, FMOD_REVERB_CHANNELFLAGS_DEFAULT }; + + defaultprop.Flags &= ~FMOD_REVERB_CHANNELFLAGS_INSTANCE0; /* FMOD_REVERB_CHANNELFLAGS_DEFAULT has instance 0 set. */ + defaultprop.Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance; + + mInstance[instance].mChannelData = (FMOD_REVERB_CHANNELDATA *)FMOD_Memory_Calloc(mSystem->mNumChannels * sizeof(FMOD_REVERB_CHANNELDATA)); + if (!mInstance[instance].mChannelData) + { + return FMOD_ERR_MEMORY; + } + + for (channel=0; channel < mSystem->mNumChannels; channel++) + { + FMOD_memcpy(&mInstance[instance].mChannelData[channel].mChanProps, &defaultprop, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + mInstance[instance].mChannelData[channel].mDSPConnection = 0; + mInstance[instance].mChannelData[channel].mPresenceGain = 0; + } + } + + mPosition.x = mPosition.y = mPosition.z = 0.0f; + mMinRadius = 0.0f; + mMaxRadius = 0.0f; + mMinMaxDistance = 0.0f; + mGain = 0.0f; + m3D = is_3d; + + if (this == &system->mReverbGlobal) + { + mDisableIfNoEnvironment = true; + } +#ifdef FMOD_SUPPORT_MULTIREVERB + if (this == &system->mReverb3D) + { + mDisableIfNoEnvironment = true; + } +#endif + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + Clear up internals + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::release(bool freethis) +{ + int count; + + for (count = 0; count < FMOD_REVERB_MAXINSTANCES; count++) + { + if (mInstance[count].mChannelData) + { + FMOD_Memory_Free(mInstance[count].mChannelData); + mInstance[count].mChannelData = 0; + } + releaseDSP(count); + } + + removeNode(); + +#ifdef FMOD_SUPPORT_MULTIREVERB + if (mSystem) + { + mSystem->update3DReverbs(); + + /* + If no more reverbs left feeding into 3D reverb, make sure it's not active on environment -1 + */ + if (mSystem && !mSystem->count3DPhysicalReverbs()) + { + mSystem->mReverb3D.setDisableIfNoEnvironment(true); + } + + /* + If no more virtuals: turn morphing off. + */ + if (!mSystem->count3DVirtualReverbs()) + { + mSystem->set3DReverbActive(false); + } + } +#endif + + if (freethis) + { + FMOD_Memory_Free(this); + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + Initialise internals + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Instead of just using createDSPByType we have to enumerate them all, due to plugins possibly being there or not. + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::createDSP(int instance) +{ + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (!mSystem) + { + return FMOD_ERR_INITIALIZATION; + } + + if (!mInstance[instance].mChannelData) + { + int channel; + + mInstance[instance].mChannelData = (FMOD_REVERB_CHANNELDATA *)FMOD_Memory_Calloc(mSystem->mNumChannels * sizeof(FMOD_REVERB_CHANNELDATA)); + if (!mInstance[instance].mChannelData) + { + return FMOD_ERR_MEMORY; + } + + for (channel=0; channel < mSystem->mNumChannels; channel++) + { + resetChanProperties(instance, channel); + resetConnectionPointer(instance, channel); + + } + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (!mInstance[instance].mDSP) + { + FMOD_RESULT result; + int count, numdsps; + + result = mSystem->mPluginFactory->getNumDSPs(&numdsps); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < numdsps; count++) + { + FMOD_DSP_DESCRIPTION_EX *descriptionex = 0; + unsigned int handle; + + result = mSystem->mPluginFactory->getDSPHandle(count, &handle); + if (result != FMOD_OK) + { + continue; + } + + result = mSystem->mPluginFactory->getDSP(handle, &descriptionex); + if (result != FMOD_OK) + { + continue; + } + + if (descriptionex->mType == FMOD_DSP_TYPE_SFXREVERB) + { + result = mSystem->mPluginFactory->createDSP(descriptionex, &mInstance[instance].mDSP); + if (result != FMOD_OK) + { + return result; + } + + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_DRYLEVEL, -10000.0f); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; + } + } + + return FMOD_ERR_PLUGIN_MISSING; + } + + return FMOD_OK; + +#else + + return FMOD_ERR_UNSUPPORTED; + +#endif +} + +/* +[ + [DESCRIPTION] + Initialise internals + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::releaseDSP(int instance) +{ + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (mInstance[instance].mDSP) + { + FMOD_RESULT result; + + result = mInstance[instance].mDSP->disconnectFrom(0); + if (result != FMOD_OK) + { + return result; + } + + result = mInstance[instance].mDSP->release(); + if (result != FMOD_OK) + { + return result; + } + mInstance[instance].mDSP = 0; + } + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::getChanProperties(int instance, const int index, FMOD_REVERB_CHANNELPROPERTIES *props, DSPConnectionI **connection) +{ + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES) + { + if (connection) + { + *connection = 0; + } + return FMOD_ERR_REVERB_INSTANCE; + } + + if (!mInstance[instance].mChannelData) + { + if (connection) + { + *connection = 0; + } + return FMOD_ERR_REVERB_INSTANCE; + } + + if ((index >= 0) && (index < mSystem->mNumChannels)) + { + if (props) + { + FMOD_memcpy(props, &mInstance[instance].mChannelData[index].mChanProps, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + } + + if (connection) + { + *connection = mInstance[instance].mChannelData[index].mDSPConnection; + } + } + else + { + if (connection) + { + *connection = 0; + } + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::setChanProperties(int instance, const int index, const FMOD_REVERB_CHANNELPROPERTIES *props, DSPConnectionI *connection) +{ + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES || !mInstance[instance].mChannelData) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if ((index >= 0) && (index < mSystem->mNumChannels)) + { + if (props) + { + FMOD_memcpy(&mInstance[instance].mChannelData[index].mChanProps, props, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + + mInstance[instance].mChannelData[index].mChanProps.Flags &= ~(FMOD_REVERB_CHANNELFLAGS_INSTANCE0 | + FMOD_REVERB_CHANNELFLAGS_INSTANCE1 | + FMOD_REVERB_CHANNELFLAGS_INSTANCE2 | + FMOD_REVERB_CHANNELFLAGS_INSTANCE3); + mInstance[instance].mChannelData[index].mChanProps.Flags |= (FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance); + } + + if (connection) + { + mInstance[instance].mChannelData[index].mDSPConnection = connection; + } + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::resetChanProperties(int instance, int channel) +{ + FMOD_REVERB_CHANNELPROPERTIES defaultprop = { 0, 0, 0, 0, 0, 0, 0, 0.25f, 1.5f, 1.0f, 0, 1.0f, 0, 0, 0, 0, 1.0f, 0 }; + + if (!mInstance[instance].mChannelData) + { + return FMOD_ERR_INVALID_PARAM; + } + + defaultprop.Flags &= ~FMOD_REVERB_CHANNELFLAGS_INSTANCE0; /* FMOD_REVERB_CHANNELFLAGS_DEFAULT has instance 0 set. */ + defaultprop.Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0 << instance; + + FMOD_memcpy(&mInstance[instance].mChannelData[channel].mChanProps, &defaultprop, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + + mInstance[instance].mChannelData[channel].mPresenceGain = 0; + + return resetConnectionPointer(instance, channel); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::resetConnectionPointer(int instance, int channel) +{ + if (!mInstance[instance].mChannelData) + { + return FMOD_ERR_INVALID_PARAM; + } + + mInstance[instance].mChannelData[channel].mDSPConnection = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::set3DAttributes(const FMOD_VECTOR *position, float mindistance, float maxdistance) +{ + if (position) + { + mPosition = *position; + } + + m3D = true; + + mMinRadius = mindistance; + if (mMinRadius > mMaxRadius) + { + mMaxRadius = mMinRadius; + } + + mMaxRadius = maxdistance; + if (mMaxRadius < mMinRadius) + { + mMinRadius = mMaxRadius; + } + + mMinMaxDistance = (mMaxRadius - mMinRadius); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::get3DAttributes(FMOD_VECTOR *position, float *mindistance, float *maxdistance) +{ + if (position) + { + *position = mPosition; + } + + if (mindistance) + { + *mindistance = mMinRadius; + } + + if (maxdistance) + { + *maxdistance = mMaxRadius; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT ReverbI::setProperties(const FMOD_REVERB_PROPERTIES* prop_source) +{ + FMOD_REVERB_PROPERTIES oldprops; + FMOD_REVERB_PROPERTIES *newprops; + int instance = prop_source->Instance; + + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + newprops = &mInstance[instance].mProps; + FMOD_memcpy(&oldprops, newprops, sizeof(FMOD_REVERB_PROPERTIES)); + + newprops->Instance = prop_source->Instance < 0 ? 0 : prop_source->Instance > 3 ? 3 : prop_source->Instance ; /* [in] 0 , 3 , 0 , EAX4/GameCube only. Environment Instance. 3 (2 for GameCube) seperate reverbs simultaneously are possible. This specifies which one to set. (win32/gamecube) */ + newprops->Environment = prop_source->Environment < -1 ? -1 : prop_source->Environment > 25 ? 25 : prop_source->Environment ; /* [in/out] -1 , 25 , -1 , sets all listener properties. -1 = OFF. (win32/ps2) */ + newprops->EnvSize = prop_source->EnvSize < 1.0f ? 1.0f : prop_source->EnvSize > 100.0f ? 100.0f : prop_source->EnvSize ; /* [in/out] 1.0 , 100.0 , 7.5 , environment size in meters (win32 only) */ + newprops->EnvDiffusion = prop_source->EnvDiffusion < 0.0f ? 0.0f : prop_source->EnvDiffusion > 1.0f ? 1.0f : prop_source->EnvDiffusion ; /* [in/out] 0.0 , 1.0 , 1.0 , environment diffusion (win32/Xbox/gamecube) */ + newprops->Room = prop_source->Room < -10000 ? -10000 : prop_source->Room > 0 ? 0 : prop_source->Room ; /* [in/out] -10000, 0 , -1000 , room effect level (at mid frequencies) (win32/Xbox/gamecube) */ + newprops->RoomHF = prop_source->RoomHF < -10000 ? -10000 : prop_source->RoomHF > 0 ? 0 : prop_source->RoomHF ; /* [in/out] -10000, 0 , -100 , relative room effect level at high frequencies (win32/Xbox) */ + newprops->RoomLF = prop_source->RoomLF < -10000 ? -10000 : prop_source->RoomLF > 0 ? 0 : prop_source->RoomLF ; /* [in/out] -10000, 0 , 0 , relative room effect level at low frequencies (win32 only) */ + newprops->DecayTime = prop_source->DecayTime < 0.1f ? 0.1f : prop_source->DecayTime > 20.0f ? 20.0f : prop_source->DecayTime ; /* [in/out] 0.1 , 20.0 , 1.49 , reverberation decay time at mid frequencies (win32/Xbox/gamecube) */ + newprops->DecayHFRatio = prop_source->DecayHFRatio < 0.1f ? 0.1f : prop_source->DecayHFRatio > 2.0f ? 2.0f : prop_source->DecayHFRatio ; /* [in/out] 0.1 , 2.0 , 0.83 , high-frequency to mid-frequency decay time ratio (win32/Xbox) */ + newprops->DecayLFRatio = prop_source->DecayLFRatio < 0.1f ? 0.1f : prop_source->DecayLFRatio > 2.0f ? 2.0f : prop_source->DecayLFRatio ; /* [in/out] 0.1 , 2.0 , 1.0 , low-frequency to mid-frequency decay time ratio (win32 only) */ + newprops->Reflections = prop_source->Reflections < -10000 ? -10000 : prop_source->Reflections > 1000 ? 1000 : prop_source->Reflections ; /* [in/out] -10000, 1000 , -2602 , early reflections level relative to room effect (win32/Xbox/gamecube) */ + newprops->ReflectionsDelay = prop_source->ReflectionsDelay < 0.0f ? 0.0f : prop_source->ReflectionsDelay > 0.3f ? 0.3f : prop_source->ReflectionsDelay ; /* [in/out] 0.0 , 0.3 , 0.007 , initial reflection delay time (win32/Xbox) */ + newprops->ReflectionsPan[0] = prop_source->ReflectionsPan[0]; + newprops->ReflectionsPan[1] = prop_source->ReflectionsPan[1]; + newprops->ReflectionsPan[2] = prop_source->ReflectionsPan[2]; + newprops->Reverb = prop_source->Reverb < -10000 ? -10000 : prop_source->Reverb > 2000 ? 2000 : prop_source->Reverb ; /* [in/out] -10000, 2000 , 200 , late reverberation level relative to room effect (win32/Xbox) */ + newprops->ReverbDelay = prop_source->ReverbDelay < 0.0f ? 0.0f : prop_source->ReverbDelay > 0.1f ? 0.1f : prop_source->ReverbDelay ; /* [in/out] 0.0 , 0.1 , 0.011 , late reverberation delay time relative to initial reflection (win32/Xbox/gamecube) */ + newprops->ReverbPan[0] = prop_source->ReverbPan[0]; + newprops->ReverbPan[1] = prop_source->ReverbPan[1]; + newprops->ReverbPan[2] = prop_source->ReverbPan[2]; + newprops->EchoTime = prop_source->EchoTime < 0.075f ? 0.075f : prop_source->EchoTime > 0.25f ? 0.25f : prop_source->EchoTime ; /* [in/out] .075 , 0.25 , 0.25 , echo time (win32 only) */ + newprops->EchoDepth = prop_source->EchoDepth < 0.0f ? 0.0f : prop_source->EchoDepth > 1.0f ? 1.0f : prop_source->EchoDepth ; /* [in/out] 0.0 , 1.0 , 0.0 , echo depth (win32 only) */ + newprops->ModulationTime = prop_source->ModulationTime < 0.04f ? 0.04f : prop_source->ModulationTime > 4.0f ? 4.0f : prop_source->ModulationTime ; /* [in/out] 0.04 , 4.0 , 0.25 , modulation time (win32 only) */ + newprops->ModulationDepth = prop_source->ModulationDepth < 0.0f ? 0.0f : prop_source->ModulationDepth > 1.0f ? 1.0f : prop_source->ModulationDepth ; /* [in/out] 0.0 , 1.0 , 0.0 , modulation depth (win32/gamecube) */ + newprops->AirAbsorptionHF = prop_source->AirAbsorptionHF < -100.0f ? -100.0f : prop_source->AirAbsorptionHF > 0.0f ? 0.0f : prop_source->AirAbsorptionHF ; /* [in/out] -100 , 0.0 , -5.0 , change in level per meter at high frequencies (win32 only) */ + newprops->HFReference = prop_source->HFReference < 1000.0f ? 1000.0f : prop_source->HFReference > 20000 ? 20000 : prop_source->HFReference ; /* [in/out] 1000.0, 20000 , 5000.0 , reference high frequency (hz) (win32/Xbox) */ + newprops->LFReference = prop_source->LFReference < 20.0f ? 20.0f : prop_source->LFReference > 1000.0f ? 1000.0f : prop_source->LFReference ; /* [in/out] 20.0 , 1000.0, 250.0 , reference low frequency (hz) (win32 only) */ + newprops->RoomRolloffFactor = prop_source->RoomRolloffFactor < 0.0f ? 0.0f : prop_source->RoomRolloffFactor > 10.0f ? 10.0f : prop_source->RoomRolloffFactor ; /* [in/out] 0.0 , 10.0 , 0.0 , like FMOD_3D_Listener_SetRolloffFactor but for room effect (win32/Xbox) */ + newprops->Diffusion = prop_source->Diffusion < 0.0f ? 0.0f : prop_source->Diffusion > 100.0f ? 100.0f : prop_source->Diffusion ; /* [in/out] 0.0 , 100.0 , 100.0 , Value that controls the echo density in the late reverberation decay. (Xbox only) */ + newprops->Density = prop_source->Density < 0.0f ? 0.0f : prop_source->Density > 100.0f ? 100.0f : prop_source->Density ; /* [in/out] 0.0 , 100.0 , 100.0 , Value that controls the modal density in the late reverberation decay (Xbox only) */ + newprops->Flags = prop_source->Flags; + + +#ifdef FMOD_SUPPORT_SFXREVERB + if (mInstance[instance].mDSP) + { + FMOD_RESULT result; + bool reverb3d_active = false; + + #ifdef FMOD_SUPPORT_MULTIREVERB + if (mSystem) + { + mSystem->get3DReverbActive(&reverb3d_active); + } + #endif + + if (mDisableIfNoEnvironment) + { + if (newprops->Environment == -1) + { + mInstance[instance].mDSP->setActive(reverb3d_active); + if (!reverb3d_active) + { + mInstance[instance].mDSP->reset(); + } + } + else + { + mInstance[instance].mDSP->setActive(true); + } + } + else + { + mInstance[instance].mDSP->setActive(true); + } + + + if (newprops->Room != oldprops.Room) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_ROOM, (float)newprops->Room); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->RoomHF != oldprops.RoomHF) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_ROOMHF, (float)newprops->RoomHF); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->RoomRolloffFactor != oldprops.RoomRolloffFactor) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_ROOMROLLOFFFACTOR, newprops->RoomRolloffFactor); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->DecayTime != oldprops.DecayTime) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_DECAYTIME, newprops->DecayTime); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->DecayHFRatio != oldprops.DecayHFRatio) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_DECAYHFRATIO, newprops->DecayHFRatio); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->Reflections != oldprops.Reflections) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_REFLECTIONSLEVEL, (float)newprops->Reflections); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->ReflectionsDelay != oldprops.ReflectionsDelay) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_REFLECTIONSDELAY, newprops->ReflectionsDelay); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->Reverb != oldprops.Reverb) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_REVERBLEVEL, (float)newprops->Reverb); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->ReverbDelay != oldprops.ReverbDelay) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_REVERBDELAY, newprops->ReverbDelay); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->Diffusion != oldprops.Diffusion) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_DIFFUSION, newprops->Diffusion); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->Density != oldprops.Density) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_DENSITY, newprops->Density); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->HFReference != oldprops.HFReference) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_HFREFERENCE, newprops->HFReference); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->RoomLF != oldprops.RoomLF) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_ROOMLF, (float)newprops->RoomLF); + if (result != FMOD_OK) + { + return result; + } + } + if (newprops->LFReference != oldprops.LFReference) + { + result = mInstance[instance].mDSP->setParameter(FMOD_DSP_SFXREVERB_LFREFERENCE, newprops->LFReference); + if (result != FMOD_OK) + { + return result; + } + } + + } +#endif + + /* + Modify hardware reverb settings if we're targetting the 2D reverb + */ + if (this == &mSystem->mReverbGlobal) + { + if (mSystem->mOutput->mDescription.reverb_setproperties) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mSystem->mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#endif + return mSystem->mOutput->mDescription.reverb_setproperties(mSystem->mOutput, &mInstance[instance].mProps); + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::getProperties(FMOD_REVERB_PROPERTIES* properties) +{ + if (!properties) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (properties->Instance < 0 || properties->Instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + *properties = mInstance[properties->Instance].mProps; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::setActive(bool active) +{ + mActive = active; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::getActive(bool *active) +{ + if (!active) + { + return FMOD_ERR_INVALID_PARAM; + } + + *active = mActive; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void ReverbI::setDisableIfNoEnvironment(bool dis) +{ + int count; + + mDisableIfNoEnvironment = dis; + + for (count = 0; count < FMOD_REVERB_MAXINSTANCES; count++) + { + FMOD_REVERB_PROPERTIES props; + + props.Instance = count; + getProperties(&props); + setProperties(&props); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void ReverbI::calculateDistanceGain(FMOD_VECTOR *p, float *linear_gain, float *linear_coeff) +{ + FMOD_VECTOR a_vector; + float a,g,c; + + /* + Non-3d reverb is independent of distance + */ + if (!m3D) + { + if (linear_gain) + { + *linear_gain = 1.0f; + } + if (linear_coeff) + { + *linear_coeff = 1.0f; + } + return; + } + + /* + Find distance from p to reverb centre + */ + FMOD_Vector_Subtract(p, &mPosition, &a_vector); + + a = FMOD_Vector_GetLength(&a_vector); + + /* + Calculate the input gain of the reverb based on distance + */ + if (a <= mMinRadius) /* Unity on and inside min distance */ + { + g = c = 1.0f; + } + else if (a >= mMaxRadius) /* Zero at and outside max distance */ + { + g = c = 0.0f; + } + else /* Linear dB rolloff between min and max distances */ + { + if (mMinMaxDistance <= 0) + { + g = c = 1.0f; + } + else + { + float rolloff = (a - mMinRadius) / mMinMaxDistance; + + c = 1.0f - rolloff; + g = (float)FMOD_POW(10.0f, rolloff * k20TH_MAXDISTANCE_GAINDB); + } + } + + if (linear_gain) + { + *linear_gain = g; + } + if (linear_coeff) + { + *linear_coeff = c; + } + +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::setPresenceGain(int instance, int index, float lingain) +{ + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (index < 0 || index >= mSystem->mNumChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + mInstance[instance].mChannelData[index].mPresenceGain = lingain; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::getPresenceGain(int instance, int index, float *lingain) +{ + if (instance < 0 || instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (index < 0 || index >= mSystem->mNumChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!lingain) + { + return FMOD_ERR_INVALID_PARAM; + } + + *lingain = mInstance[instance].mChannelData[index].mPresenceGain; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void ReverbI::sumRoomProps(FMOD_REVERB_STDPROPERTIES *accprops, FMOD_REVERB_PROPERTIES *addprops, float factor) +{ + if (accprops && addprops) + { + accprops->Room += factor * FMOD_EXP((float)(addprops->Room*kREVERB_GAIN_COEF)); + } +} + + +/* +[ + [DESCRIPTION] + Add this reverb's properties, multiplied by 'factor', to the props parameter. + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void ReverbI::sumProps(FMOD_REVERB_STDPROPERTIES *accprops, FMOD_REVERB_PROPERTIES *addprops, float factor) +{ + if (accprops && addprops) + { + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "sumProps ", "factor=%f, adding %d -> %f\n",factor,addprops->Reflections, factor * FMOD_EXP((float)(addprops->Reflections*kREVERB_GAIN_COEF)))); + accprops->Room += factor * FMOD_EXP((float)(addprops->Room*kREVERB_GAIN_COEF)); + accprops->RoomHF += factor * FMOD_EXP((float)(addprops->RoomHF*kREVERB_GAIN_COEF)); + accprops->RoomLF += factor * FMOD_EXP((float)(addprops->RoomLF*kREVERB_GAIN_COEF)); + accprops->RoomRolloffFactor += factor * addprops->RoomRolloffFactor; + accprops->DecayTime += factor * addprops->DecayTime; + accprops->DecayHFRatio += factor * addprops->DecayHFRatio; + accprops->Reflections += factor * FMOD_EXP((float)(addprops->Reflections*kREVERB_GAIN_COEF)); + accprops->ReflectionsDelay += factor * addprops->ReflectionsDelay; + accprops->Reverb += factor * FMOD_EXP((float)(addprops->Reverb*kREVERB_GAIN_COEF)); + accprops->ReverbDelay += factor * addprops->ReverbDelay; + accprops->Diffusion += factor * addprops->Diffusion; + accprops->Density += factor * addprops->Density; + accprops->HFReference += (addprops->HFReference > 0.0f) ? factor * FMOD_LOG(addprops->HFReference) : 0.0f; + accprops->LFReference += (addprops->LFReference > 0.0f) ? factor * FMOD_LOG(addprops->LFReference) : 0.0f; + } +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void ReverbI::factorProps(FMOD_REVERB_PROPERTIES *quotientprops, FMOD_REVERB_STDPROPERTIES *divprops, float factor) +{ + FMOD_memset(quotientprops, 0, sizeof(FMOD_REVERB_PROPERTIES)); + + if (quotientprops && divprops) + { + //FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "factorProps ", "factor=%f -> %d\n",factor,(divprops->Reflections>0.0f) ? (int)(0.5f + FMOD_LOG(divprops->Reflections)* kREVERB_GAIN_COEF_INV * factor) : -10000)); + quotientprops->Room = (divprops->Room>0.0f) ? (int)(0.5f + FMOD_LOG(divprops->Room) * kREVERB_GAIN_COEF_INV * factor) : -10000; + quotientprops->Room = (divprops->Room >0.0f) ? (int)(0.5f + FMOD_LOG(divprops->Room * factor) * kREVERB_GAIN_COEF_INV) : -10000; + quotientprops->RoomHF = (divprops->RoomHF >0.0f) ? (int)(0.5f + FMOD_LOG(divprops->RoomHF * factor) * kREVERB_GAIN_COEF_INV) : -10000; + quotientprops->RoomLF = (divprops->RoomLF >0.0f) ? (int)(0.5f + FMOD_LOG(divprops->RoomLF * factor) * kREVERB_GAIN_COEF_INV) : -10000; + quotientprops->RoomRolloffFactor = divprops->RoomRolloffFactor * factor; + quotientprops->DecayTime = divprops->DecayTime * factor; + quotientprops->DecayHFRatio = divprops->DecayHFRatio * factor; + quotientprops->Reflections = (divprops->Reflections>0.0f) ? (int)(0.5f + FMOD_LOG(divprops->Reflections * factor)* kREVERB_GAIN_COEF_INV) : -10000; + quotientprops->ReflectionsDelay = divprops->ReflectionsDelay * factor; + quotientprops->Reverb = (divprops->Reverb>0.0f) ? (int)(0.5f + FMOD_LOG(divprops->Reverb * factor)* kREVERB_GAIN_COEF_INV) : -10000; + quotientprops->ReverbDelay = divprops->ReverbDelay * factor; + quotientprops->Diffusion = divprops->Diffusion * factor; + quotientprops->Density = divprops->Density * factor; + quotientprops->HFReference = FMOD_EXP(divprops->HFReference * factor); + quotientprops->LFReference = FMOD_EXP(divprops->LFReference * factor); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ReverbI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT ReverbI::getMemoryUsedImpl(MemoryTracker *tracker) +{ +//AJS it's static in SystemI::mReverbGlobal +// tracker->add(MEMTYPE_REVERBI, sizeof(*this)); + + for (int i=0; i < FMOD_REVERB_MAXINSTANCES; i++) + { + FMOD_REVERB_INSTANCE *reverbinstance = &mInstance[i]; + + if (reverbinstance->mDSP) + { + CHECK_RESULT(reverbinstance->mDSP->getMemoryUsed(tracker)); + } + + if (reverbinstance->mChannelData) + { + tracker->add(false, FMOD_MEMBITS_REVERB, mSystem->mNumChannels * sizeof(FMOD_REVERB_CHANNELDATA)); + + if (reverbinstance->mChannelData->mDSPConnection) + { + CHECK_RESULT(reverbinstance->mChannelData->mDSPConnection->getMemoryUsed(tracker)); + } + } + } + + return FMOD_OK; +} + +#endif + + +} // \namespace FMOD diff --git a/src/fmod_reverbi.h b/src/fmod_reverbi.h new file mode 100755 index 0000000..5b235e5 --- /dev/null +++ b/src/fmod_reverbi.h @@ -0,0 +1,132 @@ +#ifndef _FMOD_REVERBI_H +#define _FMOD_REVERBI_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_memory.h" +#include "fmod_linkedlist.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + + class SystemI; + class ChannelI; + class DSPI; + class DSPConnectionI; + + typedef enum + { + FMOD_REVERB_PHYSICAL = 0x1, + FMOD_REVERB_VIRTUAL = 0x2, + } FMOD_REVERB_MODE; + + #define FMOD_REVERB_MAXINSTANCES 4 + typedef struct FMOD_REVERB_STDPROPERTIES + { + int Environment; + float Room; + float RoomHF; + float RoomLF; + float DecayTime; + float DecayHFRatio; + float Reflections; + float ReflectionsDelay; + float Reverb; + float ReverbDelay; + float HFReference; + float LFReference; + float RoomRolloffFactor; + float Diffusion; + float Density; + } FMOD_REVERB_STDPROPERTIES; + + struct FMOD_REVERB_CHANNELDATA + { + FMOD_REVERB_CHANNELPROPERTIES mChanProps; // Channel properties which determine input levels + DSPConnectionI *mDSPConnection; + float mPresenceGain; + }; + + struct FMOD_REVERB_INSTANCE + { + DSPI *mDSP; // DSP which implements the reverb + FMOD_REVERB_CHANNELDATA *mChannelData; // Channeldata for each instance + FMOD_REVERB_PROPERTIES mProps; // ID3L2 listener properties determines nature of reverb + }; + + class ReverbI : public LinkedListNode + { + DECLARE_MEMORYTRACKER + + public: + ReverbI(); +#ifdef PLATFORM_PS2 + virtual ~ReverbI() {} +#endif + + static FMOD_RESULT validate(Reverb *reverb, ReverbI **reverbi); + + FMOD_RESULT init(SystemI *system, bool is_3d, FMOD_REVERB_MODE reverb_mode=FMOD_REVERB_PHYSICAL); + FMOD_RESULT release(bool freethis=true); + + // Reverb manipulation. + FMOD_RESULT set3DAttributes(const FMOD_VECTOR *position, float mindistance, float maxdistance); + FMOD_RESULT get3DAttributes(FMOD_VECTOR *position, float *mindistance, float *maxdistance); + FMOD_RESULT setProperties(const FMOD_REVERB_PROPERTIES* properties); + FMOD_RESULT getProperties(FMOD_REVERB_PROPERTIES* properties); + FMOD_RESULT setActive(bool active); + FMOD_RESULT getActive(bool *active); + + // Userdata set/get. + FMOD_RESULT setUserData(void *userdata); + FMOD_RESULT getUserData(void **userdata); + FMOD_RESULT getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + + public: + + FMOD_RESULT setChanProperties(int instance, const int index, const FMOD_REVERB_CHANNELPROPERTIES* props, DSPConnectionI *connection = 0); + FMOD_RESULT getChanProperties(int instance, const int index, FMOD_REVERB_CHANNELPROPERTIES* props, DSPConnectionI **connection = 0); + FMOD_RESULT resetChanProperties(int instance, int channel); + FMOD_RESULT resetConnectionPointer(int instance, int channel); + + FMOD_RESULT createDSP(int instance); + FMOD_RESULT releaseDSP(int instance); + FMOD_RESULT setPresenceGain(int instance, int index, float lingain); + FMOD_RESULT getPresenceGain(int instance, int index, float *lingain); + + bool is3d() { return m3D;} + FMOD_RESULT setGain(float lingain) { mGain = lingain; return FMOD_OK;} + float getGain() { return mGain; } + FMOD_REVERB_MODE getMode() { return mMode; } + + + void setDisableIfNoEnvironment(bool dis); + void calculateDistanceGain(FMOD_VECTOR *p, float *linear_gain, float *linear_coeff); + static void sumRoomProps(FMOD_REVERB_STDPROPERTIES *accprops, FMOD_REVERB_PROPERTIES *addprops, float factor); + static void sumProps(FMOD_REVERB_STDPROPERTIES *accprops, FMOD_REVERB_PROPERTIES *addprops, float factor); + static void factorProps(FMOD_REVERB_PROPERTIES *quotientprops, FMOD_REVERB_STDPROPERTIES *divprops, float factor); + + FMOD_REVERB_INSTANCE mInstance[FMOD_REVERB_MAXINSTANCES]; + + private: + SystemI *mSystem; // Instance of the system + void *mUserData; + float mGain; // Linear gain of component for listener's locale + + bool mDisableIfNoEnvironment; // Call setActive() with truth value of (environment==-1) + bool m3D; // Use this reverb as a 3D per-channel effect. Set by any of the radius/position functions. Cannot be unset! + bool mActive; + FMOD_REVERB_MODE mMode; // The type of 3D reverb this is (if it's 3d) + FMOD_VECTOR mPosition; // Spherical centre + float mMinRadius; // Spherical inner radius (0dB locus) + float mMaxRadius; // Spherical outer radius (-infinity dB locus) + float mMinMaxDistance;// Distance between min and max distance + }; +} + +#endif diff --git a/src/fmod_sample_software.cpp b/src/fmod_sample_software.cpp new file mode 100755 index 0000000..aa40fd9 --- /dev/null +++ b/src/fmod_sample_software.cpp @@ -0,0 +1,610 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_dsp_codec.h" +#include "fmod_memory.h" +#include "fmod_output.h" +#include "fmod_output_software.h" +#include "fmod_sample_software.h" +#include "fmod_systemi.h" + +#ifdef PLATFORM_PS3 +#include "fmod_output_ps3.h" +#endif + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +SampleSoftware::SampleSoftware() +{ + mBuffer = 0; + mBufferMemory = 0; + mLoopPointDataEndOffset = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::release(bool freethis) +{ + FMOD_RESULT result; + + if (mFlags & FMOD_SOUND_FLAG_PRELOADEDFSB) + { + return FMOD_ERR_PRELOADED; + } + + if (mFlags & FMOD_SOUND_FLAG_PROGRAMMERSOUND) + { + return FMOD_ERR_PROGRAMMERSOUND; + } + + /* + The reason there is a sleep loop here and not a criticalsection is that we don't want one sound release stalling on other non blocking sounds, + and we also don't want the non blocking thread stalling on release calls (not such a huge issue but it slows loading down unescessarily). + */ + while ((mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_ERROR) || (mFlags & FMOD_SOUND_FLAG_DONOTRELEASE)) + { + FMOD_OS_Time_Sleep(2); + } + + if (mSystem) + { + result = mSystem->stopSound(this); + if (result != FMOD_OK) + { + return result; + } + } + + if (mBufferMemory) + { + if (0) + {} + #ifdef PLATFORM_PS3 + else if ((mMode & FMOD_LOADSECONDARYRAM) && OutputPS3::mRSXPoolInitialised) + { + OutputPS3::mRSXPool.free(mBufferMemory, __FILE__, __LINE__); + } + #endif + else if ((mMode & FMOD_LOADSECONDARYRAM) && (FMOD::gGlobal->gMemoryTypeFlags & FMOD_MEMORY_SECONDARY)) + { + FMOD_Memory_FreeType(mBufferMemory, FMOD_MEMORY_SECONDARY); + } + else + { + FMOD_Memory_Free(mBufferMemory); + } + mBufferMemory = 0; + } + + if (mLoopPointDataEnd && mLoopPointDataEnd != mLoopPointDataEndMemory) + { + FMOD_Memory_Free(mLoopPointDataEnd); + mLoopPointDataEnd = 0; + } + + mBuffer = 0; + + return Sample::release(freethis); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + + result = Sample::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + + result = setLoopPointData(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD_RESULT result; + char *src = 0; + unsigned int overflowbytes, pointB; + + /* + If we're locking part of the sample that has been modified by the loop point stuff, restore the old data first. + */ + result = getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes); + if (result != FMOD_OK) + { + return result; + } + + result = getBytesFromSamples(mLoopStart + mLoopLength, &pointB); + if (result != FMOD_OK) + { + return result; + } + + if (offset >= pointB && offset < pointB + overflowbytes) + { + result = restoreLoopPointData(); + if (result != FMOD_OK) + { + return result; + } + } + + src = (char *)mBuffer; + + if (offset >= mLengthBytes || offset < 0 || length < 0 || length > mLengthBytes) + { + *ptr1 = 0; + if (ptr2) + { + *ptr2 = 0; + } + *len1 = 0; + if (len2) + { + *len2 = 0; + } + return FMOD_ERR_INVALID_PARAM; + } + + if (offset + length <= mLengthBytes) + { + *ptr1 = src + offset; + *len1 = length; + if (ptr2) + { + *ptr2 = 0; + } + if (len2) + { + *len2 = 0; + } + } + /* + Otherwise return wrapped pointers in pt1 and ptr2 + */ + else + { + *ptr1 = src + offset; + *len1 = mLengthBytes - offset; + if (ptr2) + { + *ptr2 = src; + } + if (len2) + { + *len2 = length - (mLengthBytes - offset); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + FMOD_RESULT result; + + result = setLoopPointData(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::setBufferData(void *data) +{ + #ifdef PLATFORM_PS3 + if ((unsigned int)data % 16) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "SampleSoftware::setBufferData", "data: %08x not 16 byte aligned!\n", data)); + return FMOD_ERR_MEMORY_CANTPOINT; + } + #endif + + mBuffer = data; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::setLoopPoints(unsigned int loopstart, unsigned int looplength) +{ + if (loopstart >= mLength || loopstart + looplength > mLength) + { + return FMOD_ERR_INVALID_PARAM; + } + + restoreLoopPointData(); + + mLoopStart = loopstart; + mLoopLength = looplength; + + setLoopPointData(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::setLoopPointData() +{ + FMOD_RESULT result; + unsigned int overflowbytes, pointA, pointB; + + if ((mFormat != FMOD_SOUND_FORMAT_PCM8 && + mFormat != FMOD_SOUND_FORMAT_PCM16 && + mFormat != FMOD_SOUND_FORMAT_PCM24 && + mFormat != FMOD_SOUND_FORMAT_PCM32 && + mFormat != FMOD_SOUND_FORMAT_PCMFLOAT) || !mLoopPointDataEnd) + { + return FMOD_OK; + } + + result = getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart, &pointA); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart + mLoopLength, &pointB); + if (result != FMOD_OK) + { + return result; + } + + if (mMode & FMOD_LOOP_BIDI) + { + int count; + + FMOD_memcpy(mLoopPointDataEnd, (char *)mBuffer + pointB, overflowbytes); /* backup the data first */ + mLoopPointDataEndOffset = pointB; + + switch (mFormat) + { + case FMOD_SOUND_FORMAT_PCM8: + { + char *srcptr, *destptr; + + srcptr = destptr = (char *)mBuffer + pointB; + srcptr -= mChannels; + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + *destptr = *srcptr; + destptr++; + srcptr--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM16: + { + short *srcptr, *destptr; + + srcptr = destptr = (short *)((char *)mBuffer + pointB); + srcptr -= mChannels; + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + *destptr = *srcptr; + destptr++; + srcptr--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM24: + { + char *srcptr, *destptr; + + srcptr = destptr = (char *)mBuffer + pointB; + srcptr -= (mChannels * 3); + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + destptr[0] = srcptr[0]; + destptr[1] = srcptr[1]; + destptr[2] = srcptr[2]; + destptr += 3; + srcptr -= 3; + } + break; + } + case FMOD_SOUND_FORMAT_PCM32: + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + int *srcptr, *destptr; + + srcptr = destptr = (int *)((char *)mBuffer + pointB); + srcptr -= mChannels; + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + *destptr = *srcptr; + destptr++; + srcptr--; + } + break; + } + default: + { + break; + } + } + } + else if (mMode & FMOD_LOOP_NORMAL) + { + if (mLoopPointDataEndOffset) + { + FMOD_memcpy((char *)mBuffer + mLoopPointDataEndOffset, mLoopPointDataEnd, overflowbytes); + } + + FMOD_memcpy(mLoopPointDataEnd, (char *)mBuffer + pointB, overflowbytes); + mLoopPointDataEndOffset = pointB; + + FMOD_memcpy((char *)mBuffer + pointB, (char *)mBuffer + pointA, overflowbytes); + } + else if (mMode & FMOD_LOOP_OFF) + { + if (mLoopPointDataEndOffset) + { + FMOD_memcpy((char *)mBuffer + mLoopPointDataEndOffset, mLoopPointDataEnd, overflowbytes); + mLoopPointDataEndOffset = 0; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleSoftware::restoreLoopPointData() +{ + FMOD_RESULT result; + unsigned int overflowbytes, pointA, pointB; + + if ((mFormat != FMOD_SOUND_FORMAT_PCM8 && + mFormat != FMOD_SOUND_FORMAT_PCM16 && + mFormat != FMOD_SOUND_FORMAT_PCM24 && + mFormat != FMOD_SOUND_FORMAT_PCM32 && + mFormat != FMOD_SOUND_FORMAT_PCMFLOAT) || !mLoopPointDataEnd) + { + return FMOD_OK; + } + + result = getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart, &pointA); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart + mLoopLength, &pointB); + if (result != FMOD_OK) + { + return result; + } + + if (mLoopPointDataEndOffset) + { + FMOD_memcpy((char *)mBuffer + mLoopPointDataEndOffset, mLoopPointDataEnd, overflowbytes); + mLoopPointDataEndOffset = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT SampleSoftware::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(SampleSoftware) - sizeof(Sample)); + + if (mBuffer) + { + unsigned int overflowbytes; + + if (mFormat == FMOD_SOUND_FORMAT_IMAADPCM || mFormat == FMOD_SOUND_FORMAT_XMA || mFormat == FMOD_SOUND_FORMAT_MPEG) + { + overflowbytes = 0; + } + else + { + SoundI::getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes, mChannels, mFormat); + } + + if (0){} + #ifdef PLATFORM_PS3 + else if ((mMode & FMOD_LOADSECONDARYRAM) && OutputPS3::mRSXPoolInitialised) + { + tracker->add(false, FMOD_MEMBITS_SOUND_SECONDARYRAM, mLengthBytes + (overflowbytes * 2) + 16); + } + #endif + else if ((mMode & FMOD_LOADSECONDARYRAM) && (FMOD::gGlobal->gMemoryTypeFlags & FMOD_MEMORY_SECONDARY)) + { + tracker->add(false, FMOD_MEMBITS_SOUND_SECONDARYRAM, mLengthBytes + (overflowbytes * 2) + 16); + } + else if (!(mMode & FMOD_OPENMEMORY_POINT)) + { + tracker->add(false, FMOD_MEMBITS_SOUND, mLengthBytes + (overflowbytes * 2) + 16); + } + } + + if (mLoopPointDataEnd != mLoopPointDataEndMemory) + { + unsigned int overflowbytes = 0; + + SoundI::getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes, mChannels, mFormat); + + tracker->add(false, FMOD_MEMBITS_SOUND, overflowbytes); + } + + return Sample::getMemoryUsedImpl(tracker); +} + +#endif + +} + +#endif diff --git a/src/fmod_sample_software.h b/src/fmod_sample_software.h new file mode 100755 index 0000000..69598c8 --- /dev/null +++ b/src/fmod_sample_software.h @@ -0,0 +1,49 @@ +#ifndef _FMOD_SAMPLE_SOFTWARE_H +#define _FMOD_SAMPLE_SOFTWARE_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_dsp_resampler.h" +#include "fmod_sound_sample.h" + +namespace FMOD +{ + class SampleSoftware : public Sample + { + DECLARE_MEMORYTRACKER + + friend class OutputSoftware; + friend class ChannelSoftware; + friend class DSPWaveTable; + friend class DSPCodec; + + private: + + void *mBuffer; + void *mBufferMemory; + char *mLoopPointDataEnd; + char mLoopPointDataEndMemory[8]; + unsigned int mLoopPointDataEndOffset; + + public: + + SampleSoftware(); + + FMOD_RESULT release(bool freethis = true); + FMOD_RESULT lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + FMOD_RESULT setBufferData(void *data); + + FMOD_RESULT setLoopPoints(unsigned int loopstart, unsigned int looplength); + FMOD_RESULT setLoopPointData(); + FMOD_RESULT restoreLoopPointData(); + FMOD_RESULT setMode(FMOD_MODE mode); + }; +} + +#endif + +#endif + diff --git a/src/fmod_settings.h b/src/fmod_settings.h new file mode 100755 index 0000000..00079e4 --- /dev/null +++ b/src/fmod_settings.h @@ -0,0 +1,1046 @@ +#ifndef _FMOD_SETTINGS_H +#define _FMOD_SETTINGS_H + +/* + PLATFORM SPECIFICATION DEFINES + Dont alter. PLATFORM_WINDOWS has to come last because xbox and ce define WIN32 as well. + PLATFORM_XENON has to come before xbox, because xenon defines _XBOX as well. +*/ +#if defined(_XENON) + + #include <xdk.h> /* For _XDK_VER */ + + #define PLATFORM_XENON + #define PLATFORM_ENDIAN_BIG + #define PLATFORM_32BIT + +#elif defined(_XBOX) + + #define PLATFORM_XBOX + #define PLATFORM_ENDIAN_LITTLE + #define PLATFORM_32BIT + +#elif defined(_WIN64) + + #define PLATFORM_WINDOWS + #define PLATFORM_WINDOWS64 + #define PLATFORM_64BIT + #define PLATFORM_ENDIAN_LITTLE + +#elif defined(_WIN32) || defined(WIN32) || defined(_WINDOWS) + + #define PLATFORM_WINDOWS + #define PLATFORM_ENDIAN_LITTLE + #define PLATFORM_32BIT + + /*#define PLATFORM_WINDOWS_PS3MODE*/ /* Emulation of PS3 (uses alternate mixer) */ + /*#define FMOD_SUPPORT_HARDWAREXM*/ /* Emulation of PS2/PSP hardware xm support (uses dsound buffers) */ + +#elif defined(__linux__) + + #define PLATFORM_LINUX + #define PLATFORM_ENDIAN_LITTLE + + #if defined(__amd64__) + #define PLATFORM_LINUX64 + #define PLATFORM_64BIT + #else + #define PLATFORM_32BIT + #endif + +#elif defined(__iphone__) + + #define PLATFORM_IPHONE + #define PLATFORM_32BIT + #define PLATFORM_ENDIAN_LITTLE + + #if defined(__arm__) + #include <arm/arch.h> + #define PLATFORM_IPHONE_DEVICE + + #if defined(_ARM_ARCH_7) // Check arm7 first as it is a superset of arm6 + #define PLATFORM_IPHONE_ARM7 + #elif defined(_ARM_ARCH_6) + #define PLATFORM_IPHONE_ARM6 + #endif + #else + #define PLATFORM_IPHONE_SIMULATOR + #endif + +#elif defined(__MACH__) + + #define PLATFORM_MAC + + #if defined(__LP64__) + #define PLATFORM_64BIT + #else + #define PLATFORM_32BIT + #endif + + #if defined(__LITTLE_ENDIAN__) + #define PLATFORM_MAC_INTEL + #define PLATFORM_ENDIAN_LITTLE + #else + #define PLATFORM_MAC_PPC + #define PLATFORM_ENDIAN_BIG + #endif + +#elif defined(__psp__) + + #define PLATFORM_PSP + #define PLATFORM_ENDIAN_LITTLE + #define PLATFORM_32BIT + +#elif defined(R3000) || defined(R5900) || defined(SN_TARGET_PS2) + + #define PLATFORM_PS2 + #define PLATFORM_ENDIAN_LITTLE + #define PLATFORM_32BIT + + #ifdef SN_TARGET_PS2 + #define R5900 + #endif + + #ifdef R3000 + #define PLATFORM_PS2_IOP + #define FMOD_NO_FPU + #define float int + #endif + #ifdef R5900 + #define PLATFORM_PS2_EE + #endif + + #ifdef __MWERKS__ + #define PLATFORM_PS2_CODEWARRIOR + #else + #define PLATFORM_PS2_GCC + #endif + +#elif defined(SN_TARGET_PS3) || defined(SN_TARGET_PS3_SPU) || defined(_PS3) + + #define PLATFORM_PS3 + #define PLATFORM_ENDIAN_BIG + #define PLATFORM_32BIT + + #ifdef SN_TARGET_PS3 + #define PLATFORM_PS3_PPU + #endif + + #ifdef SN_TARGET_PS3_SPU + #define PLATFORM_PS3_SPU + + #ifdef SPU_DECODE + #define PLATFORM_PS3_SPU_STREAMDECODE + #endif + #endif + +#elif defined(GEKKO) + + #if defined(_REVOLUTION) + #define PLATFORM_WII + #define PLATFORM_ENDIAN_BIG + #define PLATFORM_32BIT + #else + #define PLATFORM_GC + #define PLATFORM_ENDIAN_BIG + #define PLATFORM_32BIT + #endif + +#elif defined(__sun) + + #define PLATFORM_SOLARIS + #define PLATFORM_ENDIAN_LITTLE + #define PLATFORM_32BIT + +#else + + #error Please define a platform type. + +#endif + +#if defined(_DEBUG) && !defined(DEBUG) + #define DEBUG 1 +#endif + + +/* + PLATFORM INDEPENDENT SETTINGS + Comment out or undef features you dont wish to include in the FMOD build. +*/ + +/* + File formats +*/ +#define FMOD_SUPPORT_AIFF +#define FMOD_SUPPORT_ASF +#define FMOD_SUPPORT_CDDA +#define FMOD_SUPPORT_DLS +#define FMOD_SUPPORT_FLAC +#define FMOD_SUPPORT_FSB +#define FMOD_SUPPORT_IMAADPCM +#define FMOD_SUPPORT_IT +#define FMOD_SUPPORT_MIDI +#define FMOD_SUPPORT_MOD +#define FMOD_SUPPORT_MPEG +#define FMOD_SUPPORT_MPEG_LAYER2 +#define FMOD_SUPPORT_MPEG_LAYER3 +#define FMOD_SUPPORT_CELT +#define FMOD_SUPPORT_OGGVORBIS +/* #define FMOD_SUPPORT_TREMOR */ +#define FMOD_SUPPORT_PLAYLIST +#define FMOD_SUPPORT_RAW +#define FMOD_SUPPORT_S3M +/* #define FMOD_SUPPORT_SF2 */ +#define FMOD_SUPPORT_TAGS +#define FMOD_SUPPORT_USERCODEC +#define FMOD_SUPPORT_XM +#define FMOD_SUPPORT_WAV +#define FMOD_SUPPORT_VAG + +/* + Miscellaneous +*/ +#define FMOD_SUPPORT_SOFTWARE +#define FMOD_SUPPORT_GEOMETRY +#ifdef FMOD_SUPPORT_GEOMETRY +# define FMOD_SUPPORT_GEOMETRY_THREADED +#endif +#define FMOD_SUPPORT_REVERB +#define FMOD_SUPPORT_RECORDING +#define FMOD_SUPPORT_3DSOUND +#define FMOD_SUPPORT_GETSPECTRUM +#define FMOD_SUPPORT_MEMORYMANAGEMENT /* Advice is to leave this on unless you really know why you want to remove this */ +#define FMOD_SUPPORT_DLMALLOC /* Use Doug lea's memory manager instead of our own. */ +#define FMOD_SUPPORT_MEMORYTRACKER /* Support memory tracking in debug/logging builds */ +#define FMOD_SUPPORT_NET +#define FMOD_SUPPORT_FILE +#define FMOD_SUPPORT_WAVWRITER +#define FMOD_SUPPORT_WAVWRITER_NRT +#define FMOD_SUPPORT_NOSOUND +#define FMOD_SUPPORT_NOSOUND_NRT +#define FMOD_SUPPORT_NONBLOCKING +#define FMOD_SUPPORT_STREAMING +#define FMOD_SUPPORT_SENTENCING +#define FMOD_SUPPORT_DLLS +#define FMOD_SUPPORT_PCM8 +#define FMOD_SUPPORT_PCM16 +#define FMOD_SUPPORT_PCM24 +#define FMOD_SUPPORT_PCM32 +#define FMOD_SUPPORT_PCMFLOAT +#define FMOD_SUPPORT_NONBLOCKSETPOS + +//#define FMOD_SUPPORT_MYEARS + +#if defined(DEBUG) + #define FMOD_SUPPORT_RTTI +#endif + +/* #define FMOD_SUPPORT_CMDLOG */ +#define FMOD_SUPPORT_PROFILE +#ifdef FMOD_SUPPORT_PROFILE + #define FMOD_SUPPORT_PROFILE_DSP + #define FMOD_SUPPORT_PROFILE_DSP_VOLUMELEVELS + /* #define FMOD_SUPPORT_PROFILE_MEMORY */ + #define FMOD_SUPPORT_PROFILE_CPU + #define FMOD_SUPPORT_PROFILE_CHANNEL + #define FMOD_SUPPORT_PROFILE_CODEC +#endif + +#define FMOD_SUPPORT_SPEAKERMODE_RAW +#define FMOD_SUPPORT_SPEAKERMODE_MONO +#define FMOD_SUPPORT_SPEAKERMODE_STEREO +#define FMOD_SUPPORT_SPEAKERMODE_QUAD +#define FMOD_SUPPORT_SPEAKERMODE_SURROUND +#define FMOD_SUPPORT_SPEAKERMODE_5POINT1 +#define FMOD_SUPPORT_SPEAKERMODE_7POINT1 +#define FMOD_SUPPORT_SPEAKERMODE_PROLOGIC + + +/* + DSP effects and features +*/ +#ifdef FMOD_SUPPORT_SOFTWARE + #define FMOD_SUPPORT_OSCILLATOR + #define FMOD_SUPPORT_LOWPASS + #define FMOD_SUPPORT_LOWPASS2 + #define FMOD_SUPPORT_LOWPASS_SIMPLE + #define FMOD_SUPPORT_HIGHPASS + #define FMOD_SUPPORT_ECHO + #define FMOD_SUPPORT_DELAY + #define FMOD_SUPPORT_FLANGE + #define FMOD_SUPPORT_TREMOLO + #define FMOD_SUPPORT_DISTORTION + #define FMOD_SUPPORT_NORMALIZE + #define FMOD_SUPPORT_PARAMEQ + #define FMOD_SUPPORT_PITCHSHIFT + #define FMOD_SUPPORT_CHORUS + #define FMOD_SUPPORT_ITECHO + #define FMOD_SUPPORT_COMPRESSOR + #define FMOD_SUPPORT_FREEVERB + #define FMOD_SUPPORT_SFXREVERB + #define FMOD_SUPPORT_RESAMPLER_NOINTERP + #define FMOD_SUPPORT_RESAMPLER_LINEAR + #define FMOD_SUPPORT_RESAMPLER_CUBIC + #define FMOD_SUPPORT_RESAMPLER_SPLINE + #define FMOD_SUPPORT_DSPCODEC + #define FMOD_SUPPORT_MULTIREVERB +#else + #undef FMOD_SUPPORT_GETSPECTRUM + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_XM + #undef FMOD_SUPPORT_IT + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_MIDI +#endif + + +/* + Event system features +*/ + +//#define FMOD_EVENT_STRIPPED // Define this to strip out _all_ extraneous features in the event system + +#ifndef __FMOD_EVENT_TOOL + #ifndef FMOD_EVENT_STRIPPED + #define FMOD_EVENT_SUPPORT_SPAWN_INTENSITY + #define FMOD_EVENT_SUPPORT_REVERB_LEVEL + #define FMOD_EVENT_SUPPORT_FADE + #define FMOD_EVENT_SUPPORT_TIME_OFFSET + #define FMOD_EVENT_SUPPORT_STEALPRIORITY + #define FMOD_EVENT_SUPPORT_POSITION_RANDOMISATION + #define FMOD_EVENT_SUPPORT_CONES + #define FMOD_EVENT_SUPPORT_DOPPLER_FACTOR + #define FMOD_EVENT_SUPPORT_OCCLUSION + #define FMOD_EVENT_SUPPORT_SPEAKER_SPREAD + #define FMOD_EVENT_SUPPORT_PAN_LEVEL + #define FMOD_EVENT_SUPPORT_PITCH_RANDOMISATION + #define FMOD_EVENT_SUPPORT_VOLUME_RANDOMISATION + #define FMOD_EVENT_SUPPORT_MUSICSYSTEM + #endif +#else + /* + Don't touch these. FMOD Designer needs them set like this + */ + #define FMOD_EVENT_SUPPORT_SPAWN_INTENSITY + #define FMOD_EVENT_SUPPORT_REVERB_LEVEL + #define FMOD_EVENT_SUPPORT_FADE + #define FMOD_EVENT_SUPPORT_TIME_OFFSET + #define FMOD_EVENT_SUPPORT_STEALPRIORITY + #define FMOD_EVENT_SUPPORT_POSITION_RANDOMISATION + #define FMOD_EVENT_SUPPORT_CONES + #define FMOD_EVENT_SUPPORT_DOPPLER_FACTOR + #define FMOD_EVENT_SUPPORT_OCCLUSION + #define FMOD_EVENT_SUPPORT_SPEAKER_SPREAD + #define FMOD_EVENT_SUPPORT_PAN_LEVEL + #define FMOD_EVENT_SUPPORT_PITCH_RANDOMISATION + #define FMOD_EVENT_SUPPORT_VOLUME_RANDOMISATION + #define FMOD_EVENT_SUPPORT_MUSICSYSTEM +#endif + + +/* + These are extra plugins we support but only as plugins. They are not included in the main fmod4.dll. +*/ +#ifdef PLUGIN_EXPORTS + + #ifndef PLUGIN_NEEDS_CDDA + #undef FMOD_SUPPORT_CDDA + #endif + +#endif + + +/* + DEBUG SETTINGS +*/ + +#ifdef DEBUG + #define FMOD_DEBUG /* Enabling this writes output to FMOD.log or client program. See system_debug.c. */ +#endif + +/* + PLATFORM DEPENDENT SETTINGS +*/ + +#if defined(PLATFORM_WINDOWS64) + + #ifdef FMOD_SUPPORT_SOFTWARE /* WASAPI support needs software to run. */ + #define FMOD_SUPPORT_WASAPI + #endif + #define FMOD_SUPPORT_DSOUND + #define FMOD_SUPPORT_WINMM + #ifdef FMOD_SUPPORT_SOFTWARE /* OpenAL support needs software to run. */ + #define FMOD_SUPPORT_OPENAL + #endif + #define FMOD_SUPPORT_EAX + #define FMOD_SUPPORT_I3DL2 + #define FMOD_SUPPORT_PRAGMAPACK + #define FMOD_SUPPORT_NEURAL + + #undef FMOD_SUPPORT_CELT + +#elif defined(PLATFORM_WINDOWS) + + #ifdef FMOD_SUPPORT_SOFTWARE /* WASAPI support needs software to run. */ + #define FMOD_SUPPORT_WASAPI + #endif + #define FMOD_SUPPORT_DSOUND + #define FMOD_SUPPORT_WINMM + #ifdef FMOD_SUPPORT_SOFTWARE /* OpenAL support needs software to run. */ + #define FMOD_SUPPORT_OPENAL + #endif + #define FMOD_SUPPORT_ASIO /* Asio support */ + #define FMOD_SUPPORT_EAX + /* #define FMOD_SUPPORT_I3DL2 */ + #define FMOD_SUPPORT_VSTPLUGIN + #define FMOD_SUPPORT_WINAMPPLUGIN + #define FMOD_SUPPORT_PRAGMAPACK + #define FMOD_SUPPORT_SIMD + #define FMOD_SUPPORT_NEURAL + + #ifdef __MINGW32__ + #undef FMOD_SUPPORT_ASF + #endif + + #ifdef PLATFORM_WINDOWS_PS3MODE + #define FMOD_SUPPORT_RAWCODEC + #define FMOD_SUPPORT_MIXER_NONRECURSIVE + #undef FMOD_SUPPORT_OPENAL + #undef FMOD_SUPPORT_PITCHSHIFT + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_RECORDING + #undef FMOD_SUPPORT_RTTI + #undef FMOD_SUPPORT_DLS + #undef FMOD_SUPPORT_IT + #undef FMOD_SUPPORT_MIDI + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_XM + #undef FMOD_SUPPORT_WASAPI + #endif + +#elif defined(PLATFORM_LINUX64) + + #define FMOD_SUPPORT_OSS + #define FMOD_SUPPORT_ALSA + #define FMOD_SUPPORT_ESD + #define FMOD_SUPPORT_NEURAL + + #undef FMOD_SUPPORT_ASF + + #undef FMOD_SUPPORT_MYEARS + +#elif defined(PLATFORM_LINUX) + + #define FMOD_SUPPORT_OSS + #define FMOD_SUPPORT_ALSA + #define FMOD_SUPPORT_ESD + #define FMOD_SUPPORT_SIMD + #define FMOD_SUPPORT_NEURAL + + #undef FMOD_SUPPORT_ASF + + #undef FMOD_SUPPORT_MYEARS + +#elif defined(PLATFORM_XBOX) + + #define FMOD_SUPPORT_XWMA + #define FMOD_SUPPORT_PRAGMAPACK + #define FMOD_SUPPORT_SIMD + + #undef FMOD_SUPPORT_RECORDING + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_PROFILE + + #undef FMOD_SUPPORT_MYEARS + + #undef FMOD_SUPPORT_CELT + +#elif defined(PLATFORM_XENON) + + #define FMOD_SUPPORT_CMIXER /* Use C Mixers */ + #define FMOD_SUPPORT_SIMD + #define FMOD_SUPPORT_PRAGMAPACK + #define FMOD_SUPPORT_I3DL2 + #define FMOD_SUPPORT_XMA + #define FMOD_SUPPORT_XMA_NEWHAL + #ifdef FMOD_SUPPORT_SIMD + #define FMOD_SUPPORT_MPEG_SIMD /* SIMD based decoding */ + #endif + + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + + #undef FMOD_SUPPORT_MYEARS + + #define FMOD_SUPPORT_NEURAL + + #if _XDK_VER <= 7978 + #undef FMOD_SUPPORT_RECORDING + #endif + + +#elif defined(PLATFORM_PS2) + + #define FMOD_SUPPORT_HARDWAREXM + + #undef FMOD_SUPPORT_AIFF + #undef FMOD_SUPPORT_RECORDING + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_PROFILE + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_DLLS + #undef FMOD_SUPPORT_SF2 + #undef FMOD_SUPPORT_TAGS + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_NOSOUND + #undef FMOD_SUPPORT_NOSOUND_NRT + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_MULTIREVERB + #undef FMOD_SUPPORT_CELT + + #undef FMOD_SUPPORT_MYEARS + + #undef FMOD_SUPPORT_RESAMPLER_CUBIC + #undef FMOD_SUPPORT_RESAMPLER_SPLINE + + #ifdef PLATFORM_PS2_IOP + #undef FMOD_SUPPORT_MEMORYTRACKER + #endif + + #ifdef STRIPPED + #undef FMOD_SUPPORT_SOFTWARE + #undef FMOD_SUPPORT_DSPCODEC + + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_XM + #undef FMOD_SUPPORT_IT + #undef FMOD_SUPPORT_MIDI + #undef FMOD_SUPPORT_WAV + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_OGGVORBIS + #undef FMOD_SUPPORT_TREMOR + #undef FMOD_SUPPORT_DLS + #undef FMOD_SUPPORT_IMAADPCM + #undef FMOD_SUPPORT_PLAYLIST + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_TAGS + + #undef FMOD_SUPPORT_OSCILLATOR + #undef FMOD_SUPPORT_LOWPASS + #undef FMOD_SUPPORT_LOWPASS2 + #undef FMOD_SUPPORT_LOWPASS_SIMPLE + #undef FMOD_SUPPORT_HIGHPASS + #undef FMOD_SUPPORT_ECHO + #undef FMOD_SUPPORT_DELAY + #undef FMOD_SUPPORT_FLANGE + #undef FMOD_SUPPORT_TREMOLO + #undef FMOD_SUPPORT_DISTORTION + #undef FMOD_SUPPORT_NORMALIZE + #undef FMOD_SUPPORT_PARAMEQ + #undef FMOD_SUPPORT_PITCHSHIFT + #undef FMOD_SUPPORT_CHORUS + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_ITECHO + #undef FMOD_SUPPORT_COMPRESSOR + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_GETSPECTRUM + #undef FMOD_SUPPORT_DOLBYHEADPHONES + #endif + + #ifdef CODEWARRIOR + #define FMOD_SUPPORT_PRAGMAPACK + #endif + +#elif defined(PLATFORM_PS3) + + #define FMOD_SUPPORT_SPURS /* Support SPURS */ + #define FMOD_SUPPORT_SIMD + #define FMOD_SUPPORT_RAWCODEC + + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_RTTI + #undef FMOD_SUPPORT_DLLS + + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_PLAYLIST + + #undef FMOD_SUPPORT_MYEARS + + #define FMOD_SUPPORT_NEURAL + + #define FMOD_SUPPORT_MPEG_SONYDECODER + + #ifdef FMOD_SUPPORT_MPEG_SONYDECODER + #undef FMOD_SUPPORT_MPEG_LAYER2 + #endif + + /* + DSP effects and features + */ + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_RESAMPLER_NOINTERP + #undef FMOD_SUPPORT_RESAMPLER_CUBIC + #undef FMOD_SUPPORT_RESAMPLER_SPLINE + + #define FMOD_SUPPORT_MPEG_SPU /* SPU based mpeg decoding */ + + #ifdef PLATFORM_PS3_SPU + + #define FMOD_SUPPORT_MIXER_NONRECURSIVE + + #define FMOD_SUPPORT_SIMD + #define FMOD_SUPPORT_MPEG_SIMD + + #undef FMOD_SUPPORT_PCM8 + #undef FMOD_SUPPORT_PCM24 + #undef FMOD_SUPPORT_PCM32 + + #undef FMOD_SUPPORT_SPEAKERMODE_RAW + #undef FMOD_SUPPORT_SPEAKERMODE_MONO + #undef FMOD_SUPPORT_SPEAKERMODE_STEREO + #undef FMOD_SUPPORT_SPEAKERMODE_QUAD + #undef FMOD_SUPPORT_SPEAKERMODE_SURROUND + #undef FMOD_SUPPORT_SPEAKERMODE_5POINT1 + #undef FMOD_SUPPORT_SPEAKERMODE_PROLOGIC + + #undef FMOD_SUPPORT_MPEG_SPU /* It is supported, just we don't want to build ppu code on spu */ + + /* + Miscellaneous + */ + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_RECORDING + #undef FMOD_SUPPORT_3DSOUND + #undef FMOD_SUPPORT_GETSPECTRUM + #undef FMOD_SUPPORT_MEMORYMANAGEMENT + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_PROFILE + #undef FMOD_SUPPORT_FILE + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_NOSOUND + #undef FMOD_SUPPORT_NOSOUND_NRT + #undef FMOD_SUPPORT_NONBLOCKING + #endif + + #undef FMOD_SUPPORT_CELT + +#elif defined(PLATFORM_IPHONE) + + #define FMOD_SUPPORT_PRAGMAPACK + #ifdef PLATFORM_IPHONE_DEVICE + #define FMOD_SUPPORT_SIMD + #endif + + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_VAG + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_DLLS + #undef FMOD_SUPPORT_RESAMPLER_CUBIC + #undef FMOD_SUPPORT_RESAMPLER_SPLINE + + #ifdef STRIPPED + /* Support FSB, ADPCM, WAV, User Codec and IT, remove everything else */ + #undef FMOD_SUPPORT_AIFF + #undef FMOD_SUPPORT_DLS + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_MIDI + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_MPEG_LAYER2 + #undef FMOD_SUPPORT_MPEG_LAYER3 + #undef FMOD_SUPPORT_OGGVORBIS + #undef FMOD_SUPPORT_PLAYLIST + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_TAGS + #undef FMOD_SUPPORT_XM + + /* Remove all effects */ + #undef FMOD_SUPPORT_OSCILLATOR + #undef FMOD_SUPPORT_LOWPASS + #undef FMOD_SUPPORT_LOWPASS2 + #undef FMOD_SUPPORT_LOWPASS_SIMPLE + #undef FMOD_SUPPORT_HIGHPASS + #undef FMOD_SUPPORT_ECHO + #undef FMOD_SUPPORT_DELAY + #undef FMOD_SUPPORT_FLANGE + #undef FMOD_SUPPORT_TREMOLO + #undef FMOD_SUPPORT_DISTORTION + #undef FMOD_SUPPORT_NORMALIZE + #undef FMOD_SUPPORT_PARAMEQ + #undef FMOD_SUPPORT_PITCHSHIFT + #undef FMOD_SUPPORT_CHORUS + #undef FMOD_SUPPORT_REVERB + #undef FMOD_SUPPORT_ITECHO + #undef FMOD_SUPPORT_COMPRESSOR + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_MULTIREVERB + + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_GETSPECTRUM + #endif + + #undef FMOD_SUPPORT_CELT + +#elif defined(PLATFORM_MAC) + + #define FMOD_SUPPORT_COREAUDIO + #define FMOD_SUPPORT_VSTPLUGIN + #define FMOD_SUPPORT_PRAGMAPACK + #define FMOD_SUPPORT_NEURAL + + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_VAG + +#elif defined(PLATFORM_GC) + + #define FMOD_SUPPORT_GCADPCM /* Gamecube ADPCM format */ + #define FMOD_SUPPORT_GCDVD /* Gamecube hardware DVD streaming format */ + #define FMOD_SUPPORT_PRAGMAPACK + + #define FMOD_SUPPORT_GCADPCM_DSP /* Gamecube ADPCM DSP file format */ + + #undef FMOD_SUPPORT_RECORDING + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_PROFILE + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_VAG + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_MULTIREVERB + + #undef FMOD_SUPPORT_MYEARS + + #ifdef STRIPPED + #undef FMOD_SUPPORT_SOFTWARE + #undef FMOD_SUPPORT_DSPCODEC + + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_XM + #undef FMOD_SUPPORT_IT + #undef FMOD_SUPPORT_MIDI + #undef FMOD_SUPPORT_WAV + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_OGGVORBIS + #undef FMOD_SUPPORT_TREMOR + #undef FMOD_SUPPORT_DLS + #undef FMOD_SUPPORT_IMAADPCM + #undef FMOD_SUPPORT_PLAYLIST + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_TAGS + + #undef FMOD_SUPPORT_OSCILLATOR + #undef FMOD_SUPPORT_LOWPASS + #undef FMOD_SUPPORT_LOWPASS2 + #undef FMOD_SUPPORT_LOWPASS_SIMPLE + #undef FMOD_SUPPORT_HIGHPASS + #undef FMOD_SUPPORT_ECHO + #undef FMOD_SUPPORT_DELAY + #undef FMOD_SUPPORT_FLANGE + #undef FMOD_SUPPORT_TREMOLO + #undef FMOD_SUPPORT_DISTORTION + #undef FMOD_SUPPORT_NORMALIZE + #undef FMOD_SUPPORT_PARAMEQ + #undef FMOD_SUPPORT_PITCHSHIFT + #undef FMOD_SUPPORT_CHORUS + #undef FMOD_SUPPORT_REVERB + #undef FMOD_SUPPORT_ITECHO + #undef FMOD_SUPPORT_COMPRESSOR + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_GETSPECTRUM + #undef FMOD_SUPPORT_DOLBYHEADPHONES + #endif + + #undef FMOD_SUPPORT_CELT + +#elif defined (PLATFORM_WII) + + #pragma old_friend_lookup on + + #define FMOD_SUPPORT_SIMD + + #define FMOD_SUPPORT_GCADPCM /* Wii ADPCM format */ + #define FMOD_SUPPORT_PRAGMAPACK + + #define FMOD_SUPPORT_GCADPCM_DSP /* Wii ADPCM DSP file format */ + + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_PROFILE + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_VAG + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + + #undef FMOD_SUPPORT_MYEARS + + #ifdef STRIPPED + #undef FMOD_SUPPORT_SOFTWARE + #undef FMOD_SUPPORT_DSPCODEC + + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_XM + #undef FMOD_SUPPORT_IT + #undef FMOD_SUPPORT_MIDI + #undef FMOD_SUPPORT_WAV + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_OGGVORBIS + #undef FMOD_SUPPORT_TREMOR + #undef FMOD_SUPPORT_DLS + #undef FMOD_SUPPORT_IMAADPCM + #undef FMOD_SUPPORT_PLAYLIST + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_TAGS + + #undef FMOD_SUPPORT_OSCILLATOR + #undef FMOD_SUPPORT_LOWPASS + #undef FMOD_SUPPORT_LOWPASS2 + #undef FMOD_SUPPORT_LOWPASS_SIMPLE + #undef FMOD_SUPPORT_HIGHPASS + #undef FMOD_SUPPORT_ECHO + #undef FMOD_SUPPORT_DELAY + #undef FMOD_SUPPORT_FLANGE + #undef FMOD_SUPPORT_TREMOLO + #undef FMOD_SUPPORT_DISTORTION + #undef FMOD_SUPPORT_NORMALIZE + #undef FMOD_SUPPORT_PARAMEQ + #undef FMOD_SUPPORT_PITCHSHIFT + #undef FMOD_SUPPORT_CHORUS + #undef FMOD_SUPPORT_ITECHO + #undef FMOD_SUPPORT_COMPRESSOR + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_GETSPECTRUM + #undef FMOD_SUPPORT_DOLBYHEADPHONES + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_MULTIREVERB + #endif + + #undef FMOD_SUPPORT_CELT + +#elif defined(PLATFORM_PSP) + + #define FMOD_SUPPORT_AT3 + #define FMOD_SUPPORT_MPEGPSP + #define FMOD_SUPPORT_HARDWAREXM + + #define FMOD_SUPPORT_SIMD + + #undef FMOD_SUPPORT_DLLS + #undef FMOD_SUPPORT_RTTI + + #undef FMOD_SUPPORT_AIFF + #undef FMOD_SUPPORT_RECORDING + #undef FMOD_SUPPORT_ASF + #undef FMOD_SUPPORT_CDDA + #undef FMOD_SUPPORT_NET + #undef FMOD_SUPPORT_PROFILE + #undef FMOD_SUPPORT_FLAC + #undef FMOD_SUPPORT_SF2 + #undef FMOD_SUPPORT_TAGS + /* #undef FMOD_SUPPORT_OGGVORBIS */ + /* #undef FMOD_SUPPORT_TREMOR */ + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_NOSOUND + #undef FMOD_SUPPORT_NOSOUND_NRT + + #undef FMOD_SUPPORT_RESAMPLER_CUBIC + #undef FMOD_SUPPORT_RESAMPLER_SPLINE + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_MULTIREVERB + + #undef FMOD_SUPPORT_MYEARS + + #ifdef STRIPPED + #undef FMOD_SUPPORT_SOFTWARE + #undef FMOD_SUPPORT_DSPCODEC + + #undef FMOD_SUPPORT_MOD + #undef FMOD_SUPPORT_S3M + #undef FMOD_SUPPORT_XM + #undef FMOD_SUPPORT_IT + #undef FMOD_SUPPORT_MIDI + #undef FMOD_SUPPORT_WAV + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_OGGVORBIS + #undef FMOD_SUPPORT_TREMOR + #undef FMOD_SUPPORT_DLS + #undef FMOD_SUPPORT_IMAADPCM + #undef FMOD_SUPPORT_PLAYLIST + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_TAGS + + #undef FMOD_SUPPORT_OSCILLATOR + #undef FMOD_SUPPORT_LOWPASS + #undef FMOD_SUPPORT_LOWPASS2 + #undef FMOD_SUPPORT_LOWPASS_SIMPLE + #undef FMOD_SUPPORT_HIGHPASS + #undef FMOD_SUPPORT_ECHO + #undef FMOD_SUPPORT_DELAY + #undef FMOD_SUPPORT_FLANGE + #undef FMOD_SUPPORT_TREMOLO + #undef FMOD_SUPPORT_DISTORTION + #undef FMOD_SUPPORT_NORMALIZE + #undef FMOD_SUPPORT_PARAMEQ + #undef FMOD_SUPPORT_PITCHSHIFT + #undef FMOD_SUPPORT_CHORUS + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_ITECHO + #undef FMOD_SUPPORT_COMPRESSOR + #undef FMOD_SUPPORT_FREEVERB + #undef FMOD_SUPPORT_SFXREVERB + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_GETSPECTRUM + #undef FMOD_SUPPORT_DOLBYHEADPHONES + #endif + + #ifdef FMOD_SUPPORT_MPEGPSP + #define FMOD_SUPPORT_TAGS + #endif + + #ifdef FMOD_SUPPORT_MPEGPSP + #define FMOD_SUPPORT_TAGS + #endif + + #ifdef CODEWARRIOR + #define FMOD_SUPPORT_PRAGMAPACK + #endif + + #undef FMOD_SUPPORT_CELT + +#elif defined(PLATFORM_SOLARIS) + + #define FMOD_SUPPORT_OSS + #define FMOD_SUPPORT_NEURAL + + #undef FMOD_SUPPORT_ASF + + #undef FMOD_SUPPORT_MYEARS + + #undef FMOD_SUPPORT_CELT +#else + + #error Please set up a platform dependent settings field + +#endif + +#ifdef FMOD_STATICFORPLUGINS + + #undef FMOD_SUPPORT_DSOUND + #undef FMOD_SUPPORT_WINMM + #undef FMOD_SUPPORT_WASAPI + #undef FMOD_SUPPORT_OPENAL + #undef FMOD_SUPPORT_EAX + #undef FMOD_SUPPORT_ASIO + #undef FMOD_SUPPORT_OSS + #undef FMOD_SUPPORT_ALSA + #undef FMOD_SUPPORT_ESD + #undef FMOD_SUPPORT_WAVWRITER + #undef FMOD_SUPPORT_WAVWRITER_NRT + #undef FMOD_SUPPORT_GEOMETRY + #undef FMOD_SUPPORT_GEOMETRY_THREADED + #undef FMOD_SUPPORT_VSTPLUGIN + #undef FMOD_SUPPORT_WINAMPPLUGIN + #undef FMOD_SUPPORT_DSPCODEC + #undef FMOD_SUPPORT_REVERB + #undef FMOD_SUPPORT_MULTIREVERB + #undef FMOD_SUPPORT_PROFILE + #undef FMOD_SUPPORT_NONBLOCKING + #undef FMOD_SUPPORT_STREAMING + #undef FMOD_SUPPORT_GETSPECTRUM + +#endif + +#ifdef PLUGIN_MPEG + #undef FMOD_SUPPORT_IMAADPCM + #undef FMOD_SUPPORT_XMA + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_CELT +#endif +#ifdef PLUGIN_WAV + #undef FMOD_SUPPORT_XMA + #undef FMOD_SUPPORT_RAW + #undef FMOD_SUPPORT_MPEG + #undef FMOD_SUPPORT_CELT +#endif +#ifdef PLUGIN_FSB + #undef FMOD_SUPPORT_RAW +#endif + +/* + STACK SIZES FOR FMOD THREADS +*/ + +#if defined(FMOD_SUPPORT_MPEG) || defined(FMOD_SUPPORT_CELT) /* Mpeg uses about 10k in local arrays in CodecMPEG::decodeLayer3 which is hard to remove. */ + #define STREAM_STACKSIZE (48 * 1024) + #ifdef FMOD_DEBUG + #define ASYNC_STACKSIZE (64 * 1024) + #else + #define ASYNC_STACKSIZE (48 * 1024) + #endif + #define MIXER_STACKSIZE (32 * 1024) +#else + #define STREAM_STACKSIZE (16 * 1024) + #define ASYNC_STACKSIZE (32 * 1024) + #define MIXER_STACKSIZE (16 * 1024) +#endif + +#if defined(PLATFORM_GC) || defined(PLATFORM_WII) + #define FILE_STACKSIZE (16 * 1024) /* Nintendo file libraries use a lot of stack */ +#else + #define FILE_STACKSIZE (8 * 1024) +#endif + +#define GEOMETRY_STACKSIZE (16 * 1024) + +/* + THREAD PRIORITIES FOR FMOD THREADS +*/ + +#define MIXER_THREADPRIORITY Thread::PRIORITY_CRITICAL /* Absolutely must get through. */ +#define STREAM_THREADPRIORITY Thread::PRIORITY_VERYHIGH /* Pretty high, stuttering may result. */ +#define FILE_THREADPRIORITY Thread::PRIORITY_HIGH /* Above normal, otherwise it may not trigger on cooperative systems. */ +#define ASYNC_THREADPRIORITY Thread::PRIORITY_HIGH /* Above normal, otherwise it may not trigger on cooperative systems. */ +#define GEOMETRY_THREADPRIORITY Thread::PRIORITY_LOW /* Low priority background thread. */ + +#endif /* _FMOD_SETTINGS_H */ + + diff --git a/src/fmod_sound.cpp b/src/fmod_sound.cpp new file mode 100755 index 0000000..4cd9a10 --- /dev/null +++ b/src/fmod_sound.cpp @@ -0,0 +1,950 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod_async.h" +#include "fmod_soundi.h" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT Sound::release() +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundi->release(); + } +} + + +FMOD_RESULT Sound::getSystemObject(System **system) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundi->getSystemObject(system); + } +} + + +FMOD_RESULT Sound::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->lock(offset, length, ptr1, ptr2, len1, len2); + } +} + + +FMOD_RESULT Sound::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->unlock(ptr1, ptr2, len1, len2); + } +} + + +FMOD_RESULT Sound::setDefaults(float frequency, float volume, float pan, int priority) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setDefaults(frequency, volume, pan, priority); + } +} + + +FMOD_RESULT Sound::getDefaults(float *frequency, float *volume, float *pan, int *priority) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getDefaults(frequency, volume, pan, priority); + } +} + + +FMOD_RESULT Sound::setVariations(float frequencyvar, float volumevar, float panvar) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setVariations(frequencyvar, volumevar, panvar); + } +} + + +FMOD_RESULT Sound::getVariations(float *frequencyvar, float *volumevar, float *panvar) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getVariations(frequencyvar, volumevar, panvar); + } +} + + +FMOD_RESULT Sound::set3DMinMaxDistance(float min, float max) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->set3DMinMaxDistance(min, max); + } +} + + +FMOD_RESULT Sound::get3DMinMaxDistance(float *min, float *max) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->get3DMinMaxDistance(min, max); + } +} + + +FMOD_RESULT Sound::set3DConeSettings(float insideconeangle, float outsideconeangle, float outsidevolume) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->set3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); + } +} + + +FMOD_RESULT Sound::get3DConeSettings(float *insideconeangle, float *outsideconeangle, float *outsidevolume) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->get3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); + } +} + + +FMOD_RESULT Sound::set3DCustomRolloff(FMOD_VECTOR *points, int numpoints) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->set3DCustomRolloff(points, numpoints); + } +} + + +FMOD_RESULT Sound::get3DCustomRolloff(FMOD_VECTOR **points, int *numpoints) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->get3DCustomRolloff(points, numpoints); + } +} + + +FMOD_RESULT Sound::setSubSound(int index, Sound *subsound) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setSubSound(index, (SoundI *)subsound); + } +} + + +FMOD_RESULT Sound::getSubSound(int index, Sound **subsound) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + /* + If the async state was FMOD_ERR_FILE_DISKEJECTED, we need to let it do another + disc access to know if it was re-inserted or not. + */ + FMOD_RESULT asyncresult = soundi->mAsyncData ? soundi->mAsyncData->mResult : FMOD_OK; + + if (soundi->mOpenState != FMOD_OPENSTATE_READY && asyncresult != FMOD_ERR_FILE_DISKEJECTED) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getSubSound(index, (SoundI **)subsound); + } +} + + +FMOD_RESULT Sound::setSubSoundSentence(int *subsoundlist, int numsubsounds) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setSubSoundSentence(subsoundlist, numsubsounds); + } +} + + +FMOD_RESULT Sound::getName(char *name, int namelen) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getName(name, namelen); + } +} + + +FMOD_RESULT Sound::getLength(unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getLength(length, lengthtype); + } +} + + +FMOD_RESULT Sound::getFormat(FMOD_SOUND_TYPE *type, FMOD_SOUND_FORMAT *format, int *channels, int *bits) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getFormat(type, format, channels, bits); + } +} + + +FMOD_RESULT Sound::getNumSubSounds(int *numsubsounds) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getNumSubSounds(numsubsounds); + } +} + + +FMOD_RESULT Sound::getNumTags(int *numtags, int *numtagsupdated) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getNumTags(numtags, numtagsupdated); + } +} + + +FMOD_RESULT Sound::getTag(const char *name, int index, FMOD_TAG *tag) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getTag(name, index, tag); + } +} + + +FMOD_RESULT Sound::getOpenState(FMOD_OPENSTATE *openstate, unsigned int *percentbuffered, bool *starving) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundi->getOpenState(openstate, percentbuffered, starving); + } +} + + +FMOD_RESULT Sound::readData(void *buffer, unsigned int lenbytes, unsigned int *read) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->readData(buffer, lenbytes, read); + } +} + + +FMOD_RESULT Sound::seekData(unsigned int pcm) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->seekData(pcm); + } +} + + +FMOD_RESULT Sound::setSoundGroup(SoundGroup *soundgroup) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setSoundGroup((SoundGroupI *)soundgroup); + } +} + + +FMOD_RESULT Sound::getSoundGroup(SoundGroup **soundgroup) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getSoundGroup((SoundGroupI **)soundgroup); + } +} + + +FMOD_RESULT Sound::getNumSyncPoints(int *numsyncpoints) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getNumSyncPoints(numsyncpoints); + } +} + + +FMOD_RESULT Sound::getSyncPoint(int index, FMOD_SYNCPOINT **point) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getSyncPoint(index, point); + } +} + + +FMOD_RESULT Sound::getSyncPointInfo(FMOD_SYNCPOINT *point, char *name, int namelen, unsigned int *offset, FMOD_TIMEUNIT offsettype) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getSyncPointInfo(point, name, namelen, offset, offsettype); + } +} + + +FMOD_RESULT Sound::addSyncPoint(unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **point) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->addSyncPoint(offset, offsettype, name, point); + } +} + + +FMOD_RESULT Sound::deleteSyncPoint(FMOD_SYNCPOINT *point) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->deleteSyncPoint(point); + } +} + + +FMOD_RESULT Sound::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setMode(mode); + } +} + + +FMOD_RESULT Sound::getMode(FMOD_MODE *mode) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getMode(mode); + } +} + + +FMOD_RESULT Sound::setLoopCount(int loopcount) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setLoopCount(loopcount); + } +} + + +FMOD_RESULT Sound::getLoopCount(int *loopcount) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getLoopCount(loopcount); + } +} + + +FMOD_RESULT Sound::setLoopPoints(unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setLoopPoints(loopstart, loopstarttype, loopend, loopendtype); + } +} + + +FMOD_RESULT Sound::getLoopPoints(unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getLoopPoints(loopstart, loopstarttype, loopend, loopendtype); + } +} + + +FMOD_RESULT Sound::getMusicNumChannels(int *numchannels) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getMusicNumChannels(numchannels); + } +} + + +FMOD_RESULT Sound::setMusicChannelVolume(int channel, float volume) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setMusicChannelVolume(channel, volume); + } +} + + +FMOD_RESULT Sound::getMusicChannelVolume(int channel, float *volume) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getMusicChannelVolume(channel, volume); + } +} + + +FMOD_RESULT Sound::setUserData(void *_userdata) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->setUserData(_userdata); + } +} + + +FMOD_RESULT Sound::getUserData(void **_userdata) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundi->getUserData(_userdata); + } +} + + +FMOD_RESULT Sound::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + SoundI *soundi; + + result = SoundI::validate(this, &soundi); + if (result != FMOD_OK) + { + return result; + } + else + { + if (soundi->mOpenState != FMOD_OPENSTATE_READY && soundi->mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + return soundi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_sound_sample.cpp b/src/fmod_sound_sample.cpp new file mode 100755 index 0000000..34e2aac --- /dev/null +++ b/src/fmod_sound_sample.cpp @@ -0,0 +1,938 @@ +#include "fmod_settings.h" + +#include "fmod_codeci.h" +#include "fmod_sound_sample.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +Sample::Sample() +{ + mNumSubSamples = 0; + mLockCanRead = true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + int count; + unsigned int samples = 0; + unsigned int lenbytes; + + if (!ptr1 || !len1) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mNumSubSamples < 1) + { + return lockInternal(offset, length, ptr1, ptr2, len1, len2); + } + + if (mMode & FMOD_CREATECOMPRESSEDSAMPLE) + { + return FMOD_ERR_FORMAT; + } + + /* + Clamp the lock length to not go over length of sample, so it doesn't wrap-around. + */ + getBytesFromSamples(mLength, &lenbytes); + if (length + offset > lenbytes) + { + FLOG((FMOD_DEBUG_LEVEL_WARNING, __FILE__, __LINE__, "Sample::lock", "Trying to lock more than length (%d) of multi-subsample sound. Clamping %d to %d\n", lenbytes, length, lenbytes - offset)); + + length = lenbytes - offset; + } + + /* + Clamp the lock length to SOUND_READCHUNKSIZE + */ + length = length > SOUND_READCHUNKSIZE ? SOUND_READCHUNKSIZE : length; + + FMOD_OS_CriticalSection_Enter(mSystem->mMultiSubSampleLockBufferCrit); + + *ptr1 = mLockBuffer; + if (ptr2) + { + *ptr2 = 0; + } + *len1 = length; + if (len2) + { + *len2 = 0; + } + + mLockLength = length; + mLockOffset = offset; + + SoundI::getSamplesFromBytes(length, &samples); + + length /= mNumSubSamples; + offset /= mNumSubSamples; + + for (count = 0; count < mNumSubSamples; count++) + { + if (mSubSample[count]->mLockCanRead) + { + unsigned int count2; + unsigned int in_len1, in_len2; + void *in_ptr1, *in_ptr2; + + mSubSample[count]->lock(offset, length, &in_ptr1, &in_ptr2, &in_len1, &in_len2); + + switch (mFormat) + { + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *dest = ((signed char *)*ptr1) + count; + signed char *src = (signed char *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[mNumSubSamples * 1] = src[1]; + dest[mNumSubSamples * 2] = src[2]; + dest[mNumSubSamples * 3] = src[3]; + dest[mNumSubSamples * 4] = src[4]; + dest[mNumSubSamples * 5] = src[5]; + dest[mNumSubSamples * 6] = src[6]; + dest[mNumSubSamples * 7] = src[7]; + + dest += mNumSubSamples * 8; + src += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + dest += mNumSubSamples; + src ++; + count2--; + } + break; + } + case FMOD_SOUND_FORMAT_GCADPCM: + { + samples = length/2; + } + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *dest = ((signed short *)*ptr1) + count; + signed short *src = (signed short *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[mNumSubSamples * 1] = src[1]; + dest[mNumSubSamples * 2] = src[2]; + dest[mNumSubSamples * 3] = src[3]; + dest[mNumSubSamples * 4] = src[4]; + dest[mNumSubSamples * 5] = src[5]; + dest[mNumSubSamples * 6] = src[6]; + dest[mNumSubSamples * 7] = src[7]; + + dest += mNumSubSamples * 8; + src += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + dest += mNumSubSamples; + src ++; + count2--; + } + + break; + } + case FMOD_SOUND_FORMAT_PCM24: + { + for (count2 = 0; count2 < samples; count2++) + { + ((unsigned char *)*ptr1)[(((mNumSubSamples * count2) + count) * 3) + 0] = ((unsigned char *)in_ptr1)[(count2 * 3) + 0]; + ((unsigned char *)*ptr1)[(((mNumSubSamples * count2) + count) * 3) + 1] = ((unsigned char *)in_ptr1)[(count2 * 3) + 1]; + ((unsigned char *)*ptr1)[(((mNumSubSamples * count2) + count) * 3) + 2] = ((unsigned char *)in_ptr1)[(count2 * 3) + 2]; + } + break; + } + case FMOD_SOUND_FORMAT_PCM32: + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + unsigned int *dest = ((unsigned int *)*ptr1) + count; + unsigned int *src = (unsigned int *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[mNumSubSamples * 1] = src[1]; + dest[mNumSubSamples * 2] = src[2]; + dest[mNumSubSamples * 3] = src[3]; + dest[mNumSubSamples * 4] = src[4]; + dest[mNumSubSamples * 5] = src[5]; + dest[mNumSubSamples * 6] = src[6]; + dest[mNumSubSamples * 7] = src[7]; + + dest += mNumSubSamples * 8; + src += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + dest += mNumSubSamples; + src ++; + count2--; + } + break; + } + case FMOD_SOUND_FORMAT_IMAADPCM: + { + /* Stereo is a special case where it is interleaved every 4 bytes. */ + if (mNumSubSamples == 2) + { + samples = length / 4; + + unsigned int *dest = ((unsigned int *)*ptr1) + count; + unsigned int *src = (unsigned int *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[mNumSubSamples * 1] = src[1]; + dest[mNumSubSamples * 2] = src[2]; + dest[mNumSubSamples * 3] = src[3]; + dest[mNumSubSamples * 4] = src[4]; + dest[mNumSubSamples * 5] = src[5]; + dest[mNumSubSamples * 6] = src[6]; + dest[mNumSubSamples * 7] = src[7]; + + dest += mNumSubSamples * 8; + src += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + dest += mNumSubSamples; + src ++; + count2--; + } + break; + } + /* else drop through to the following case and use 36 byte blocks. */ + } + case FMOD_SOUND_FORMAT_VAG: + { + unsigned int blocksize = 0; + unsigned int blocks; + char *dest = (char *)*ptr1; + char *src = (char *)in_ptr1; + + SoundI::getBytesFromSamples(1, &blocksize, 1, mFormat); + + blocks = length / blocksize; + + dest += (count * blocksize); + + for (count2 = 0; count2 < blocks; count2++) + { + FMOD_memcpy(dest, src, blocksize); + dest += (blocksize * mNumSubSamples); + src += blocksize; + } + break; + } + case FMOD_SOUND_FORMAT_XMA: + { + signed char *dest = ((signed char *)*ptr1) + count; + signed char *src = (signed char *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[mNumSubSamples * 1] = src[1]; + dest[mNumSubSamples * 2] = src[2]; + dest[mNumSubSamples * 3] = src[3]; + dest[mNumSubSamples * 4] = src[4]; + dest[mNumSubSamples * 5] = src[5]; + dest[mNumSubSamples * 6] = src[6]; + dest[mNumSubSamples * 7] = src[7]; + + dest += mNumSubSamples * 8; + src += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + dest += mNumSubSamples; + src ++; + count2--; + } + break; + } + default: + { + FMOD_OS_CriticalSection_Leave(mSystem->mMultiSubSampleLockBufferCrit); + + return FMOD_ERR_FORMAT; + } + } + + mSubSample[count]->unlock(in_ptr1, in_ptr2, in_len1, in_len2); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + int count; + unsigned int length, offset, samples = 0; + + if (!ptr1 || !len1) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mNumSubSamples < 1) + { + return unlockInternal(ptr1, ptr2, len1, len2); + } + + SoundI::getSamplesFromBytes(mLockLength, &samples); + + length = mLockLength / mNumSubSamples; + offset = mLockOffset / mNumSubSamples; + + for (count = 0; count < mNumSubSamples; count++) + { + unsigned int count2; + unsigned int in_len1, in_len2; + void *in_ptr1, *in_ptr2; + + mSubSample[count]->lock(offset, length, &in_ptr1, &in_ptr2, &in_len1, &in_len2); + + switch (mFormat) + { + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *src = ((signed char *)ptr1) + count; + signed char *dest = (signed char *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[1] = src[mNumSubSamples * 1]; + dest[2] = src[mNumSubSamples * 2]; + dest[3] = src[mNumSubSamples * 3]; + dest[4] = src[mNumSubSamples * 4]; + dest[5] = src[mNumSubSamples * 5]; + dest[6] = src[mNumSubSamples * 6]; + dest[7] = src[mNumSubSamples * 7]; + + src += mNumSubSamples * 8; + dest += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + src += mNumSubSamples; + dest ++; + count2--; + } + break; + } + case FMOD_SOUND_FORMAT_GCADPCM: + { + samples = length/2; + } + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *src = ((signed short *)ptr1) + count; + signed short *dest = (signed short *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[1] = src[mNumSubSamples * 1]; + dest[2] = src[mNumSubSamples * 2]; + dest[3] = src[mNumSubSamples * 3]; + dest[4] = src[mNumSubSamples * 4]; + dest[5] = src[mNumSubSamples * 5]; + dest[6] = src[mNumSubSamples * 6]; + dest[7] = src[mNumSubSamples * 7]; + + src += mNumSubSamples * 8; + dest += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + src += mNumSubSamples; + dest ++; + count2--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM24: + { + for (count2 = 0; count2 < samples; count2++) + { + ((unsigned char *)in_ptr1)[(count2 * 3) + 0] = ((unsigned char *)ptr1)[(((mNumSubSamples * count2) + count) * 3) + 0]; + ((unsigned char *)in_ptr1)[(count2 * 3) + 1] = ((unsigned char *)ptr1)[(((mNumSubSamples * count2) + count) * 3) + 1]; + ((unsigned char *)in_ptr1)[(count2 * 3) + 2] = ((unsigned char *)ptr1)[(((mNumSubSamples * count2) + count) * 3) + 2]; + } + break; + } + case FMOD_SOUND_FORMAT_PCM32: + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + unsigned int *src = ((unsigned int *)ptr1) + count; + unsigned int *dest = (unsigned int *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[1] = src[mNumSubSamples * 1]; + dest[2] = src[mNumSubSamples * 2]; + dest[3] = src[mNumSubSamples * 3]; + dest[4] = src[mNumSubSamples * 4]; + dest[5] = src[mNumSubSamples * 5]; + dest[6] = src[mNumSubSamples * 6]; + dest[7] = src[mNumSubSamples * 7]; + + src += mNumSubSamples * 8; + dest += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + src += mNumSubSamples; + dest ++; + count2--; + } + break; + } + case FMOD_SOUND_FORMAT_IMAADPCM: + { + /* Stereo is a special case where it is interleaved every 4 bytes. */ + if (mNumSubSamples == 2) + { + samples = length / 4; + + unsigned int *src = ((unsigned int *)ptr1) + count; + unsigned int *dest = (unsigned int *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[1] = src[mNumSubSamples * 1]; + dest[2] = src[mNumSubSamples * 2]; + dest[3] = src[mNumSubSamples * 3]; + dest[4] = src[mNumSubSamples * 4]; + dest[5] = src[mNumSubSamples * 5]; + dest[6] = src[mNumSubSamples * 6]; + dest[7] = src[mNumSubSamples * 7]; + + src += mNumSubSamples * 8; + dest += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + src += mNumSubSamples; + dest ++; + count2--; + } + break; + } + /* else drop through to the following case and use 36byte blocks. */ + } + case FMOD_SOUND_FORMAT_VAG: + { + unsigned int blocks = length / 16; + unsigned int *dest = (unsigned int *)in_ptr1; + unsigned int *src = ((unsigned int *)ptr1) + (count * 4); + + for (count2 = 0; count2 < blocks; count2++) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + + dest += 4; + src += (4 * mNumSubSamples); + } + break; + } + case FMOD_SOUND_FORMAT_XMA: + { + signed char *src = ((signed char *)ptr1) + count; + signed char *dest = (signed char *)in_ptr1; + + count2 = samples >> 3; + while (count2) + { + dest[0] = src[0]; + dest[1] = src[mNumSubSamples * 1]; + dest[2] = src[mNumSubSamples * 2]; + dest[3] = src[mNumSubSamples * 3]; + dest[4] = src[mNumSubSamples * 4]; + dest[5] = src[mNumSubSamples * 5]; + dest[6] = src[mNumSubSamples * 6]; + dest[7] = src[mNumSubSamples * 7]; + + src += mNumSubSamples * 8; + dest += 8; + count2--; + } + + count2 = samples & 7; + while (count2) + { + dest[0] = src[0]; + + src += mNumSubSamples; + dest ++; + count2--; + } + break; + } + default: + { + FMOD_OS_CriticalSection_Leave(mSystem->mMultiSubSampleLockBufferCrit); + + return FMOD_ERR_FORMAT; + } + } + + mSubSample[count]->unlock(in_ptr1, in_ptr2, in_len1, in_len2); + } + + FMOD_OS_CriticalSection_Leave(mSystem->mMultiSubSampleLockBufferCrit); + +// FMOD_Memory_Free(mLockBuffer); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::release(bool freethis) +{ + int count; + + if (mFlags & FMOD_SOUND_FLAG_PRELOADEDFSB) + { + return FMOD_ERR_PRELOADED; + } + + if (mFlags & FMOD_SOUND_FLAG_PROGRAMMERSOUND) + { + return FMOD_ERR_PROGRAMMERSOUND; + } + + if (mSystem) + { + mSystem->stopSound(this); + + if (mLockBuffer) + { + mSystem->mMultiSubSampleLockBuffer.free(); + } + } + + for (count = 0; count < mNumSubSamples; count++) + { + if (mSubSample[count]) + { + mSubSample[count]->mCodec = 0; /* Dont free the codec multiple times */ + mSubSample[count]->release(); + mSubSample[count] = 0; + } + } + + return SoundI::release(freethis); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::setDefaults(float frequency, float volume, float pan, int priority) +{ + FMOD_RESULT result; + int count; + + result = SoundI::setDefaults(frequency, volume, pan, priority); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->setDefaults(frequency, volume, pan, priority); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::setVariations(float frequencyvar, float volumevar, float panvar) +{ + FMOD_RESULT result; + int count; + + result = SoundI::setVariations(frequencyvar, volumevar, panvar); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->setVariations(frequencyvar, volumevar, panvar); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::set3DMinMaxDistance(float min, float max) +{ + FMOD_RESULT result; + int count; + + result = SoundI::set3DMinMaxDistance(min, max); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->set3DMinMaxDistance(min, max); + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::set3DConeSettings(float insideconeangle, float outsideconeangle, float outsidevolume) +{ + FMOD_RESULT result; + int count; + + result = SoundI::set3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->set3DConeSettings(insideconeangle, outsideconeangle, outsidevolume); + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + int count; + + result = SoundI::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->setMode(mode); + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::setLoopCount(int loopcount) +{ + FMOD_RESULT result; + int count; + + result = SoundI::setLoopCount(loopcount); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->setLoopCount(loopcount); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Sample::setLoopPoints(unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + FMOD_RESULT result; + int count; + + result = SoundI::setLoopPoints(loopstart, loopstarttype, loopend, loopendtype); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mNumSubSamples; count++) + { + mSubSample[count]->setLoopPoints(loopstart, loopstarttype, loopend, loopendtype); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT Sample::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(Sample) - sizeof(SoundI)); + + return SoundI::getMemoryUsedImpl(tracker); +} + +#endif + +} diff --git a/src/fmod_sound_sample.h b/src/fmod_sound_sample.h new file mode 100755 index 0000000..2a71a3e --- /dev/null +++ b/src/fmod_sound_sample.h @@ -0,0 +1,48 @@ +#ifndef _FMOD_SOUND_SAMPLE_H +#define _FMOD_SOUND_SAMPLE_H + +#include "fmod_settings.h" + +#include "fmod_channeli.h" +#include "fmod_soundi.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class Sample : public SoundI + { + DECLARE_MEMORYTRACKER + + public: + + void *mLockBuffer; + unsigned int mLockLength; + unsigned int mLockOffset; + + public: + + Sample(); + + FMOD_RESULT release(bool freethis = true); + FMOD_RESULT lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + + virtual FMOD_RESULT lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) { return FMOD_ERR_SUBSOUNDS; } + virtual FMOD_RESULT unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) { return FMOD_OK; } + virtual FMOD_RESULT setBufferData(void *data) { return FMOD_ERR_NEEDSSOFTWARE; } + virtual FMOD_RESULT setMode(FMOD_MODE mode); + + FMOD_RESULT setDefaults (float frequency, float volume, float pan, int priority); + FMOD_RESULT setVariations (float frequencyvar, float volumevar, float panvar); + FMOD_RESULT set3DMinMaxDistance (float min, float max); + FMOD_RESULT set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume); + FMOD_RESULT setLoopCount (int loopcount); + FMOD_RESULT setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype); + }; +} + +#endif + diff --git a/src/fmod_sound_stream.cpp b/src/fmod_sound_stream.cpp new file mode 100755 index 0000000..fcd48f3 --- /dev/null +++ b/src/fmod_sound_stream.cpp @@ -0,0 +1,755 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_STREAMING + +#include "fmod_async.h" +#include "fmod_codeci.h" +#include "fmod_channel_stream.h" +#include "fmod_file.h" +#include "fmod_sound_sample.h" +#include "fmod_sound_stream.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +Stream::Stream() +{ + mLastPos = 0; + mSubSound = 0; + mLoopCountCurrent = -1; + mBlockSize = 1; + mChannel = 0; + mInitialPosition = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Stream::fill(unsigned int offset, unsigned int length, unsigned int *read_out, bool calledfromsentence) +{ + FMOD_RESULT result = FMOD_OK; + unsigned int read = 0; + Sample *sample = mSample; + + if (read_out) + { + *read_out = 0; + } + + if (mSubSoundParent) + { + sample = ((Stream *)mSubSoundParent)->mSample; + } + + if (!(mFlags & FMOD_SOUND_FLAG_FINISHED)) + { + unsigned int r; + unsigned int size; + unsigned int len; + +#ifdef FMOD_SUPPORT_SENTENCING + if (mSubSoundList) + { + Stream *stream = this; + int count = 0; + + do + { + stream = SAFE_CAST(Stream, mSubSound[mSubSoundList[mChannel->mSubSoundListCurrent].mIndex]); + if (!stream) + { + if (count >= mSubSoundListNum) + { + return FMOD_ERR_SUBSOUNDS; + } + + count++; + mChannel->mSubSoundListCurrent++; + if (mChannel->mSubSoundListCurrent >= mSubSoundListNum) + { + if (!(mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent)) + { + mChannel->mSubSoundListCurrent = mSubSoundListNum - 1; + mPosition = mLength; + mFlags |= FMOD_SOUND_FLAG_FINISHED; + return FMOD_ERR_FILE_EOF; + } + else + { + mChannel->mSubSoundListCurrent = 0; + mPosition = 0; + } + } + } + } while (!stream); + + len = length; + do + { + unsigned int r; + unsigned int endpoint; + + if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent) + { + endpoint = mLoopStart + mLoopLength - 1; + } + else + { + if (sample->mCodec->mFlags & FMOD_CODEC_ACCURATELENGTH) + { + endpoint = mLength - 1; + } + else + { + endpoint = (unsigned int)-1; + } + } + + size = len; + if (mPosition > endpoint) + { + size = 0; + } + else if (mPosition + size > endpoint) + { + size = (endpoint - mPosition) + 1; + } + + result = stream->fill(offset, size, &r, true); + if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF)) + { + return result; + } + + read += r; + len -= r; + size -= r; + offset += r; + + mPosition += r; + + if (mPosition > endpoint || result == FMOD_ERR_FILE_EOF) + { + bool finished = false; + + if (mPosition > endpoint) + { + if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent) + { + setPosition(mLoopStart, FMOD_TIMEUNIT_PCM); + + if (mLoopCountCurrent > 0) + { + mLoopCountCurrent--; + } + + stream = SAFE_CAST(Stream, mSubSound[mSubSoundIndex]); + } + else + { + mPosition = mLength; + mFlags |= FMOD_SOUND_FLAG_FINISHED; + break; + } + } + else + { + do + { + mChannel->mSubSoundListCurrent++; + if (mChannel->mSubSoundListCurrent >= mSubSoundListNum) + { + if (!(mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent)) + { + finished = true; + mChannel->mSubSoundListCurrent = mSubSoundListNum - 1; + mPosition = mLength; + mFlags |= FMOD_SOUND_FLAG_FINISHED; + break; + } + else + { + mChannel->mSubSoundListCurrent = 0; + mPosition = 0; + } + } + + mSubSoundIndex = mSubSoundList[mChannel->mSubSoundListCurrent].mIndex; + + stream = SAFE_CAST(Stream, mSubSound[mSubSoundIndex]); + + if (mSubSoundShared) + { + stream->updateSubSound(mSubSoundIndex, true); + } + + } while (!stream && !finished); /* While loop skips past null entries. */ + + if (finished) + { + break; + } + + if (stream) + { + sample->mCodec = stream->mCodec; /* Switch parent level codec to the new substream's codec */ + if (mCodec != sample->mCodec) + { + result = sample->seek(-1, 0); + } + else + { + result = sample->seek(mSubSoundIndex, 0); + } + stream->mPosition = 0; + stream->mFlags &= ~FMOD_SOUND_FLAG_FINISHED; /* If it was read and finished before, the seek to 0 will unfinish it. */ + } + } + } + + } while (len); + } + else +#endif + { + len = length; + do + { + unsigned int endpoint, streamlength = mLength; + Stream *stream = this; + unsigned int bytestoread = 0; + + if (mSubSound) + { + stream = SAFE_CAST(Stream, mSubSound[mSubSoundIndex]); + if (!stream) + { + break; + } + } + + if (mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + stream->mCodec->mDescription.getwaveformat(stream->mCodec, mSubSoundIndex, &waveformat); + + streamlength = waveformat.lengthpcm; + } + else + { + streamlength = stream->mLength; + } + + if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent && !calledfromsentence) /* Ignore loop modes/points in a sentence. */ + { + endpoint = stream->mLoopStart + stream->mLoopLength - 1; + } + else + { + if (sample->mCodec->mFlags & FMOD_CODEC_ACCURATELENGTH) + { + endpoint = streamlength - 1; + } + else + { + endpoint = (unsigned int)-1; + } + } + + size = len; + + if (offset + size > sample->mLength) + { + size = sample->mLength - offset; + } + if (stream->mPosition > endpoint) + { + size = 0; + } + else if (stream->mPosition + size > endpoint) + { + size = (endpoint - stream->mPosition) + 1; + } + + /* + Handle unaligned read at the end. + */ + getBytesFromSamples(size, &bytestoread, false); + if (!bytestoread) + { + r = 0; + result = FMOD_ERR_FILE_EOF; + } + else + { + result = sample->read(offset, size, &r); + if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF) && (result != FMOD_ERR_FILE_DISKEJECTED)) + { + return result; + } + } + + if (result == FMOD_OK && !r && size) + { + break; + } + + read += r; + len -= r; + size -= r; + offset += r; + if (offset >= sample->mLength) + { + offset = 0; + } + + stream->mLastPos = stream->mPosition; + stream->mPosition += r; + + if (read_out) + { + *read_out = read; + } + + if (stream->mPosition > endpoint || result == FMOD_ERR_FILE_EOF) + { + if (calledfromsentence) + { + return FMOD_ERR_FILE_EOF; /* Let the parent sentence behavior take care of the transition. */ + } + + if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent) + { + stream->mPosition = mLoopStart; + + if (mLength != (unsigned int)-1) + { + result = sample->seek(mSubSoundIndex, stream->mPosition); + if (result != FMOD_OK) + { + return result; + } + + stream->mPosition = sample->mPosition; + } + + if (mLoopCountCurrent > 0) + { + mLoopCountCurrent--; + } + } + else + { + if (stream != this) + { + mPosition = mLength; + mFlags |= FMOD_SOUND_FLAG_FINISHED; + } + stream->mPosition = streamlength; + stream->mFlags |= FMOD_SOUND_FLAG_FINISHED; + + if (stream->mSubSoundParent) + { + stream->mSubSoundParent->mFlags |= FMOD_SOUND_FLAG_FINISHED; + } + break; + } + } + else + { + if (!r) + { + break; + } + } + + } while (len); + } + } + + if (read < length) + { + unsigned int size; + unsigned int len; + + len = length - read; + do + { + size = len; + if (offset + size > sample->mLength) + { + size = sample->mLength - offset; + } + + sample->clear(offset, size); + + len -= size; + offset += size; + if (offset >= sample->mLength) + { + offset = 0; + } + } while (len); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Stream::flush() +{ + if (mSample) + { + FMOD_RESULT result; + unsigned int filllength; + + if (!(mMode & FMOD_OPENUSER) && mLength <= mSample->mLength && !mSubSoundList && mSample->mMode & FMOD_SOFTWARE) + { + filllength = mLength; + } + else + { + filllength = mSample->mLength; + } + + result = fill(0, filllength); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + if (filllength < mSample->mLength) + { + result = mSample->clear(filllength, mSample->mLength - filllength); + } + } + + mFlags &= ~FMOD_SOUND_FLAG_WANTSTOFLUSH; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Stream::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result = FMOD_OK; + bool seekable = true; + unsigned int endpoint; + + if (postype == FMOD_TIMEUNIT_PCM) + { + if (mMode & FMOD_LOOP_OFF) + { + endpoint = mLength - 1; + } + else + { + endpoint = mLoopStart + mLoopLength - 1; + } + + if (position > endpoint) + { + return FMOD_ERR_INVALID_POSITION; + } + } + + if (mCodec->mFile) + { + seekable = mCodec->mFile->mFlags & FMOD_FILE_SEEKABLE ? true : false; + } + + mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED); + if (mSubSoundParent) + { + mSubSoundParent->mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED); + } + + if (seekable) + { +#ifdef FMOD_SUPPORT_SENTENCING + if (mSubSound && mSubSoundList && postype == FMOD_TIMEUNIT_PCM) + { + int count; + unsigned int offset = 0; + + for (count = 0; count < mSubSoundListNum; count++) + { + SoundI *subsound = mSubSound[mSubSoundList[count].mIndex]; + + if (subsound) + { + FMOD_RESULT result; + + if (!mSubSoundList) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (position >= offset && position < offset + mSubSoundList[count].mLength) + { + Stream *substream = SAFE_CAST(Stream, subsound); + + mChannel->mSubSoundListCurrent = count; + mSubSoundIndex = mSubSoundList[count].mIndex; + + if (mSubSoundShared) /* Same codec. */ + { + result = substream->updateSubSound(mSubSoundIndex, true); + } + else + { + substream->mSubSoundIndex = mSubSoundIndex; + mSample->mCodec = substream->mCodec; /* Switch parent level codec to the new substream's codec */ + } + + result = substream->setPosition(position - offset, postype); + break; + } + + offset += mSubSoundList[count].mLength; + } + } + } + else +#endif + if (mSubSound && postype == FMOD_TIMEUNIT_PCM) + { + SoundI *subsound = mSubSound[mSubSoundIndex]; + + if (subsound) + { + Stream *substream = SAFE_CAST(Stream, subsound); + + result = substream->setPosition(position, postype); + } + } + else + { + mCodec->reset(); + + result = mCodec->setPosition(((mSubSoundParent && mSubSoundParent->mNumSubSounds) || mNumSubSounds) ? mSubSoundIndex : 0, position, postype); + if (result != FMOD_OK) + { + return result; + } + + if (mSubSoundParent) + { + mSubSoundParent->mSubSoundIndex = mSubSoundIndex; + } + } + + if (mSample && mSample->mPostSetPositionCallback) + { + mSample->mPostSetPositionCallback((FMOD_SOUND *)this, mSubSoundIndex, position, postype); + } + + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + position = 0; + } + + mLastPos = mPosition = position; + } + else + { + if (mLastPos != 0 || position != 0) + { + return FMOD_ERR_FILE_COULDNOTSEEK; + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Stream::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + FMOD_RESULT result; + + if (mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (postype == (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_SENTENCE_SUBSOUND | FMOD_TIMEUNIT_BUFFERED)) + { + *position = mChannel->mSubSoundListCurrent; + } + else if (postype == FMOD_TIMEUNIT_PCM || postype == FMOD_TIMEUNIT_PCMBYTES || postype == FMOD_TIMEUNIT_MS) + { + if (postype == FMOD_TIMEUNIT_PCM) + { + *position = mLastPos; + } + else if (postype == FMOD_TIMEUNIT_MS) + { + *position = (unsigned int)((float)mLastPos / 1000.0f * mDefaultFrequency); + } + else if (postype == FMOD_TIMEUNIT_PCM) + { + getBytesFromSamples(mLastPos, position); + } + } + else + { + result = mCodec->getPosition(position, postype); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Stream::setLoopCount(int loopcount) +{ + if (mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_SETPOSITION) + { + return FMOD_ERR_NOTREADY; + } + + mLoopCountCurrent = loopcount; + mLoopCount = loopcount; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT Stream::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(Stream) - sizeof(SoundI)); + + if (mSample && (!mSubSoundParent || (mSubSoundParent && ((Stream *)mSubSoundParent)->mSample != mSample))) + { + CHECK_RESULT(mSample->getMemoryUsed(tracker)); + } + + if (mChannel && (!mSubSoundParent || (mSubSoundParent && ((Stream *)mSubSoundParent)->mChannel != mChannel))) + { + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(ChannelStream)); + } + + return SoundI::getMemoryUsedImpl(tracker); +} + +#endif + +} + +#endif /* #ifdef FMOD_SUPPORT_STREAMING */ diff --git a/src/fmod_sound_stream.h b/src/fmod_sound_stream.h new file mode 100755 index 0000000..1a415c4 --- /dev/null +++ b/src/fmod_sound_stream.h @@ -0,0 +1,48 @@ +#ifndef _FMOD_SOUND_STREAM_H +#define _FMOD_SOUND_STREAM_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_STREAMING + +#ifndef _FMOD_SOUNDI_H + #include "fmod_soundi.h" +#endif + +namespace FMOD +{ + class Sample; + class ChannelStream; + + class Stream : public SoundI + { + DECLARE_MEMORYTRACKER + + public: + + LinkedListNode mStreamNode; + ChannelStream *mChannel; /* Each stream has its own unique channel. */ + Sample *mSample; + unsigned int mLastPos; + int mBlockSize; + int mLoopCountCurrent; + int mInitialPosition; + + bool isStream() { return true; } + + public: + + Stream(); + + FMOD_RESULT fill(unsigned int offset, unsigned int length, unsigned int *read = 0, bool calledfromsentence = false); + FMOD_RESULT flush(); + FMOD_RESULT setPosition(unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getPosition(unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT setLoopCount(int loopcount); + }; +} + +#endif + +#endif + diff --git a/src/fmod_soundgroup.cpp b/src/fmod_soundgroup.cpp new file mode 100755 index 0000000..f93899c --- /dev/null +++ b/src/fmod_soundgroup.cpp @@ -0,0 +1,322 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_soundgroupi.h" +#include "fmod_systemi.h" + +namespace FMOD +{ +/*$ preserve end $*/ + + +FMOD_RESULT SoundGroup::release() +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->release(); + } +} + + +FMOD_RESULT SoundGroup::getSystemObject(System **system) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getSystemObject(system); + } +} + + +FMOD_RESULT SoundGroup::setMaxAudible(int maxaudible) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->setMaxAudible(maxaudible); + } +} + + +FMOD_RESULT SoundGroup::getMaxAudible(int *maxaudible) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getMaxAudible(maxaudible); + } +} + + +FMOD_RESULT SoundGroup::setMaxAudibleBehavior(FMOD_SOUNDGROUP_BEHAVIOR behavior) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->setMaxAudibleBehavior(behavior); + } +} + + +FMOD_RESULT SoundGroup::getMaxAudibleBehavior(FMOD_SOUNDGROUP_BEHAVIOR *behavior) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getMaxAudibleBehavior(behavior); + } +} + + +FMOD_RESULT SoundGroup::setMuteFadeSpeed(float speed) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->setMuteFadeSpeed(speed); + } +} + + +FMOD_RESULT SoundGroup::getMuteFadeSpeed(float *speed) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getMuteFadeSpeed(speed); + } +} + + +FMOD_RESULT SoundGroup::setVolume(float volume) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->setVolume(volume); + } +} + + +FMOD_RESULT SoundGroup::getVolume(float *volume) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getVolume(volume); + } +} + + +FMOD_RESULT SoundGroup::stop() +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->stop(); + } +} + + +FMOD_RESULT SoundGroup::getName(char *name, int namelen) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getName(name, namelen); + } +} + + +FMOD_RESULT SoundGroup::getNumSounds(int *numsounds) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getNumSounds(numsounds); + } +} + + +FMOD_RESULT SoundGroup::getSound(int index, Sound **sound) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getSound(index, (Sound **)sound); + } +} + + +FMOD_RESULT SoundGroup::getNumPlaying(int *numplaying) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getNumPlaying(numplaying); + } +} + + +FMOD_RESULT SoundGroup::setUserData(void *_userdata) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->setUserData(_userdata); + } +} + + +FMOD_RESULT SoundGroup::getUserData(void **_userdata) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getUserData(_userdata); + } +} + + +FMOD_RESULT SoundGroup::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + SoundGroupI *soundgroupi; + + result = SoundGroupI::validate(this, &soundgroupi); + if (result != FMOD_OK) + { + return result; + } + else + { + return soundgroupi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_soundgroupi.cpp b/src/fmod_soundgroupi.cpp new file mode 100755 index 0000000..c4c5014 --- /dev/null +++ b/src/fmod_soundgroupi.cpp @@ -0,0 +1,775 @@ +#include "fmod_settings.h" + +#include "fmod_soundgroupi.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::validate(SoundGroup *soundgroup, SoundGroupI **soundgroupi) +{ + FMOD::SoundGroupI *sg = (FMOD::SoundGroupI *)soundgroup; + + if (!soundgroup) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!soundgroupi) + { + return FMOD_ERR_INVALID_PARAM; + } + + *soundgroupi = sg; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +SoundGroupI::SoundGroupI() +{ + mMaxAudible = -1; + mName = 0; + mFadeSpeed = 0.0f; + mVolume = 1.0f; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT SoundGroupI::release() +{ + if (this == mSystem->mSoundGroup) + { + return FMOD_ERR_INVALID_HANDLE; + } + + /* + First move all sounds back to the main group + */ + if (mSystem->mSoundGroup && this != mSystem->mSoundGroup) + { + LinkedListNode *currentchannel = mChannelListHead.getNext(); + + /* + Reset playing channels in this group. + */ + while (currentchannel != &mChannelListHead) + { + LinkedListNode *next = currentchannel->getNext(); + ChannelI *channeli = (ChannelI *)currentchannel->getData(); + channeli->mSoundGroupSortedListNode.removeNode(); + + channeli->mFadeVolume = 1.0f; + channeli->mFadeTarget = 1.0f; + channeli->mSoundGroupListPosition = 0; + + currentchannel->removeNode(); + currentchannel = next; + } + + while (mSoundHead.getNext() != &mSoundHead) + { + SoundI *sound = (SoundI *)mSoundHead.getNext()->getData(); + sound->setSoundGroup(mSystem->mSoundGroup); + } + + /* + Reset playing channels in this group. + */ + { + LinkedListNode *currentchannel = mSystem->mChannelSortedListHead.getNext(); + + while (currentchannel != &mSystem->mChannelSortedListHead) + { + ChannelI *channeli = (ChannelI *)currentchannel->getData(); + + if (channeli->mSoundGroupListPosition == 0) + { + channeli->setVolume(channeli->mVolume, true); + } + + currentchannel = currentchannel->getNext(); + } + } + } + + return releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::releaseInternal() +{ + if (mName) + { + FMOD_Memory_Free(mName); + } + + removeNode(); + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getSystemObject(System **system) +{ + if (!system) + { + return FMOD_ERR_INVALID_PARAM; + } + + *system = (System *)mSystem; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::setMaxAudible(int maxaudible) +{ + if (maxaudible < -1) + { + return FMOD_ERR_INVALID_PARAM; + } + + mMaxAudible = maxaudible; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getMaxAudible(int *maxaudible) +{ + if (!maxaudible) + { + return FMOD_ERR_INVALID_PARAM; + } + + *maxaudible = mMaxAudible; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT SoundGroupI::setMaxAudibleBehavior(FMOD_SOUNDGROUP_BEHAVIOR behavior) +{ + if (behavior < FMOD_SOUNDGROUP_BEHAVIOR_FAIL || behavior >= FMOD_SOUNDGROUP_BEHAVIOR_MAX) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Remove all the playing muted channels. Everything besides mute dissalows playing channels. + */ + if (mMaxAudibleBehavior == FMOD_SOUNDGROUP_BEHAVIOR_MUTE && behavior !=FMOD_SOUNDGROUP_BEHAVIOR_MUTE) + { + int count = 0; + LinkedListNode *currentchannel = mChannelListHead.getNext(); + + while (currentchannel != &mChannelListHead) + { + ChannelI *channeli; + LinkedListNode *next = currentchannel->getNext(); + channeli = (ChannelI *)currentchannel->getData(); + + count++; + + channeli->mFadeVolume = 1.0f; + channeli->mFadeTarget = 1.0f; + + if (count > mMaxAudible) + { + channeli->stop(); + } + + currentchannel = next; + } + } + + mMaxAudibleBehavior = behavior; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getMaxAudibleBehavior(FMOD_SOUNDGROUP_BEHAVIOR *behavior) +{ + if (!behavior) + { + return FMOD_ERR_INVALID_PARAM; + } + + *behavior = mMaxAudibleBehavior; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +FMOD_RESULT SoundGroupI::setMuteFadeSpeed(float speed) +{ + if (speed < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + mFadeSpeed = speed; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getMuteFadeSpeed(float *speed) +{ + if (!speed) + { + return FMOD_ERR_INVALID_PARAM; + } + + *speed = mFadeSpeed; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::setVolume(float volume) +{ + LinkedListNode *current; + + if (volume < 0) + { + volume = 0; + } + if (volume > 1) + { + volume = 1; + } + + mVolume = volume; + + for (current = mSoundHead.getNext(); current != &mSoundHead; current = current->getNext()) + { + ChannelI *channeli; + SoundI *sound = (SoundI *)current->getData(); + + for (channeli = SAFE_CAST(ChannelI, mSystem->mChannelUsedListHead.getNext()); channeli != &mSystem->mChannelUsedListHead; channeli = SAFE_CAST(ChannelI, channeli->getNext())) + { + if (channeli->mRealChannel[0]) + { + SoundI *channelsound = 0; + + channeli->getCurrentSound(&channelsound); + if (channelsound == sound) + { + channeli->setVolume(channeli->mVolume); + } + } + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getVolume(float *volume) +{ + if (!volume) + { + return FMOD_ERR_INVALID_PARAM; + } + + *volume = mVolume; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::stop() +{ + LinkedListNode *current; + + for (current = mSoundHead.getNext(); current != &mSoundHead; current = current->getNext()) + { + SoundI *sound = (SoundI *)current->getData(); + + mSystem->stopSound(sound); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getName(char *name, int namelen) +{ + if (!name) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (namelen > FMOD_STRING_MAXNAMELEN) + { + namelen = FMOD_STRING_MAXNAMELEN; + } + + if (mName) + { + FMOD_strncpy(name, mName, namelen); + } + else + { + FMOD_strncpy(name, "(null)", namelen); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getNumSounds(int *numsounds) +{ + if (!numsounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numsounds = mSoundHead.count(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getSound(int index, Sound **sound) +{ + int count = 0; + LinkedListNode *current; + + if (!sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + *sound = 0; + + current = mSoundHead.getNext(); + + while (current != &mSoundHead) + { + if (count == index) + { + *sound = (Sound *)((SoundI *)current->getData()); + return FMOD_OK; + } + + count++; + current = current->getNext(); + } + + return FMOD_ERR_INVALID_PARAM; +} + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getNumPlaying(int *numplaying) +{ + LinkedListNode *current; + + if (!numplaying) + { + return FMOD_ERR_INVALID_PARAM; + } + + current = mSoundHead.getNext(); + + *numplaying = 0; + + while (current != &mSoundHead) + { + SoundI *soundi = (SoundI *)current->getData(); + + *numplaying += soundi->mNumAudible; + + current = current->getNext(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundGroupI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT SoundGroupI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_SOUNDGROUP, sizeof(*this)); + + if (mName) + { + tracker->add(false, FMOD_MEMBITS_STRING, FMOD_strlen(mName) + 1); + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_soundgroupi.h b/src/fmod_soundgroupi.h new file mode 100755 index 0000000..9a2ae15 --- /dev/null +++ b/src/fmod_soundgroupi.h @@ -0,0 +1,74 @@ +#ifndef _FMOD_SOUNDGROUPI_H +#define _FMOD_SOUNDGROUPI_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_linkedlist.h" +#include "fmod_string.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class SoundI; + class SystemI; + + class SoundGroupI : public LinkedListNode /* This linked list node entry is for System::mSoundGroupHead */ + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + public: + + static FMOD_RESULT validate(SoundGroup *soundgroup, SoundGroupI **soundgroupi); + + SystemI *mSystem; + void *mUserData; + LinkedListNode mSoundHead; + SortedLinkedListNode mChannelListHead; + char *mName; + int mMaxAudible; + FMOD_SOUNDGROUP_BEHAVIOR mMaxAudibleBehavior; + + int mPlayCount; + float mFadeSpeed; + float mVolume; + + public: + + SoundGroupI(); + + FMOD_RESULT release (); + FMOD_RESULT releaseInternal (); + FMOD_RESULT getSystemObject (System **system); + + // SoundGroup control functions. + FMOD_RESULT setMaxAudible (int maxaudible); + FMOD_RESULT getMaxAudible (int *maxaudible); + FMOD_RESULT setMaxAudibleBehavior(FMOD_SOUNDGROUP_BEHAVIOR behavior); + FMOD_RESULT getMaxAudibleBehavior(FMOD_SOUNDGROUP_BEHAVIOR *behavior); + FMOD_RESULT setMuteFadeSpeed (float speed); + FMOD_RESULT getMuteFadeSpeed (float *speed); + FMOD_RESULT setVolume (float volume); + FMOD_RESULT getVolume (float *volume); + FMOD_RESULT stop (); + + // Information only functions. + FMOD_RESULT getName (char *name, int namelen); + FMOD_RESULT getNumSounds (int *numsounds); + FMOD_RESULT getSound (int index, Sound **sound); + FMOD_RESULT getNumPlaying (int *numplaying); + + // Userdata set/get. + FMOD_RESULT setUserData (void *userdata); + FMOD_RESULT getUserData (void **userdata); + + FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + }; +} + +#endif + + diff --git a/src/fmod_soundi.cpp b/src/fmod_soundi.cpp new file mode 100755 index 0000000..058f97b --- /dev/null +++ b/src/fmod_soundi.cpp @@ -0,0 +1,4150 @@ +#include "fmod_settings.h" + +#include "fmod_async.h" +#include "fmod_codeci.h" +#include "fmod_localcriticalsection.h" +#include "fmod_soundi.h" +#include "fmod_sound_sample.h" +#include "fmod_sound_stream.h" +#include "fmod_systemi.h" +#include "fmod_stringw.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::validate(Sound *sound, SoundI **soundi) +{ + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!sound) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *soundi = (SoundI *)sound; + + #if 0 + { + SystemI *system = SAFE_CAST(SystemI, gGlobal->gSystemHead->getNext()); + while (system != gGlobal->gSystemHead) + { + LinkedListNode *currentsound = SAFE_CAST(SoundI, system->mSoundListHead.getNext()); + + while (currentsound != &system->mSoundListHead) + { + if (currentsound == *soundi || ((*soundi)->mSubSoundParent && currentsound == (*soundi)->mSubSoundParent)) + { + return FMOD_OK; + } + + currentsound = currentsound->getNext(); + } + + system = SAFE_CAST(SystemI, system->getNext()); + } + + return FMOD_ERR_INVALID_HANDLE; + } + #endif + + return FMOD_OK; +} + +#ifndef __FMOD_EVENT + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +SoundI::SoundI() +{ + mMode = FMOD_DEFAULT; + mFormat = FMOD_SOUND_FORMAT_NONE; + mName = 0; + mChannels = 1; + mMinDistance = 1.0f; + mMaxDistance = 10000.0f; + mConeInsideAngle = 360.0f; + mConeOutsideAngle = 360.0f; + mConeOutsideVolume = 1.0f; + + mDefaultVolume = 1.0f; + mDefaultFrequency = DEFAULT_FREQUENCY; + mDefaultPan = 0.0f; + mDefaultPriority = FMOD_CHANNEL_DEFAULTPRIORITY; + mFrequencyVariation = 0; + mVolumeVariation = 0; + mPanVariation = 0; + mLoopStart = 0; + mLoopLength = 0; + mLoopCount = -1; + mUserData = 0; + mMemoryUsed = 0; + mCodec = 0; + mSubSound = 0; + mSubSoundParent = 0; +#ifdef FMOD_SUPPORT_SENTENCING + mSubSoundList = 0; +#endif + mNumSubSounds = 0; + mNumActiveSubSounds = 0; + mAsyncData = 0; + mNumSyncPoints = 0; + mOpenState = FMOD_OPENSTATE_READY; + mSubSampleParent = this; /* unless made a sample with subsamples where it will point to a higher sound */ + mRolloffPoint = 0; + mNumRolloffPoints = 0; + mSyncPointHead = 0; + mSyncPointTail = 0; + mPostReadCallback = 0; + mPostSetPositionCallback = 0; + mPostCallbackSound = 0; + mFlags = 0; + + mSoundGroupNode.initNode(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::loadSubSound(int index, FMOD_MODE mode) +{ + FMOD_RESULT result; + Sample *sample = 0; + FMOD_CODEC_WAVEFORMAT waveformat; + + if (!mNumSubSounds) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (index < 0 || index >= mNumSubSounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mCodec->mDescription.getwaveformat(mCodec, index, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + result = mSystem->createSample(mode, &waveformat, &sample); + if (result != FMOD_OK) + { + return result; + } + + sample->mType = mType; + sample->mCodec = mCodec; + + if (mCodec->mDescription.soundcreate) + { + SoundI *s = SAFE_CAST(SoundI, sample); + + result = mCodec->mDescription.soundcreate(mCodec, index, (FMOD_SOUND *)s); + if (result != FMOD_OK) + { + return result; + } + } + + mCodec->reset(); + + result = mCodec->setPosition(index, 0, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + if (mPostSetPositionCallback) + { + mPostSetPositionCallback((FMOD_SOUND *)this, index, 0, FMOD_TIMEUNIT_PCM); + } + + if (!(mode & FMOD_OPENONLY)) + { + result = sample->read(0, sample->mLength, 0); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + } + +// mSubSound[index] = sample; + result = setSubSound(index, sample); + if (result != FMOD_OK) + { + return result; + } + + result = sample->setPositionInternal(0); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::read(unsigned int offset, unsigned int numsamples, unsigned int *read) +{ + FMOD_RESULT result; + unsigned int offbytes, numbytes, readbytes, chunksize; + FMOD_CODEC_WAVEFORMAT waveformat; + + mPosition = offset; + + if (mMode & FMOD_CREATECOMPRESSEDSAMPLE) + { + offbytes = offset; + numbytes = numsamples; + } + else + { + getBytesFromSamples(offset, &offbytes, false); + getBytesFromSamples(numsamples, &numbytes, false); + } + + readbytes = 0; + + if (read) + { + *read = 0; + } + + result = mCodec->mDescription.getwaveformat(mCodec, mCodec->mSubSoundIndex, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + chunksize = SOUND_READCHUNKSIZE; + + if (mMode & FMOD_CREATESTREAM) + { + if (mCodec->mBlockAlign) + { + chunksize /= mCodec->mBlockAlign; + chunksize *= mCodec->mBlockAlign; + + if (!chunksize) + { + chunksize = mCodec->mBlockAlign; + } + } + } + else + { + if (waveformat.blockalign) + { + chunksize /= waveformat.blockalign; + chunksize *= waveformat.blockalign; + + if (!chunksize) + { + chunksize = waveformat.blockalign; + } + } + } + + if (chunksize > SOUND_READCHUNKSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "SoundI::read", "ERROR - Contact support. A codec has specified a block alignment of %d which is bigger than the maximum codec read size of %d\n", waveformat.blockalign, SOUND_READCHUNKSIZE)); + return FMOD_ERR_INTERNAL; + } + + if (mCodec->mNonInterleaved && mNumSubSamples) + { + numsamples /= mNumSubSamples; + offset /= mNumSubSamples; + + for(int count = 0; count < mNumSubSamples; count++) + { + getBytesFromSamples(offset, &offbytes); + numbytes = mSubSample[count]->mLengthBytes; + + while (numbytes) + { + FMOD_RESULT result2; + int size = numbytes > chunksize ? chunksize : numbytes; + unsigned int bytesreadtotal = 0; + void *ptr1, *ptr2; + unsigned int len1, len2; + + result = mSubSample[count]->lock(offbytes, size, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + if (!len1 && !len2) + { + mSubSample[count]->unlock(ptr1, ptr2, len1, len2); /* Unlock the resource before returning. */ + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "SoundI::read", "Lock error. len1 was 0 and len2 was 0.\n")); + return FMOD_ERR_FILE_BAD; + } + + if (ptr1 && len1) + { + unsigned int bytesread = 0; + + result = readData(ptr1, len1, &bytesread); + if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF)) + { + mSubSample[count]->unlock(ptr1, ptr2, len1, len2); /* Unlock the resource before returning. */ + return result; + } + + bytesreadtotal += bytesread; + } + + if (ptr2 && len2) + { + unsigned int bytesread = 0; + + result = readData(ptr2, len2, &bytesread); + if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF)) + { + mSubSample[count]->unlock(ptr1, ptr2, len1, len2); /* Unlock the resource before returning. */ + return result; + } + + bytesreadtotal += bytesread; + } + + /* + File was truncated somehow, but allow it to load. + */ + if (result == FMOD_ERR_FILE_EOF) + { + numbytes = bytesreadtotal; + } + + result2 = mSubSample[count]->unlock(ptr1, ptr2, len1, len2); + if (result2 != FMOD_OK) + { + return result2; + } + + numbytes -= bytesreadtotal; + offbytes += bytesreadtotal; + readbytes += bytesreadtotal; + } + } + } + else + { + while (numbytes) + { + FMOD_RESULT result2; + int size = numbytes > chunksize ? chunksize : numbytes; + unsigned int bytesreadtotal = 0; + void *ptr1, *ptr2; + unsigned int len1, len2; + + result = lock(offbytes, size, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + if (!len1 && !len2) + { + unlock(ptr1, ptr2, len1, len2); /* Unlock the resource before returning. */ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::read", "Lock error. len1 was 0 and len2 was 0.\n")); + return FMOD_ERR_FILE_BAD; + } + + if (ptr1 && len1) + { + unsigned int bytesread = 0; + + result = readData(ptr1, len1, &bytesread); + if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF)) + { + unlock(ptr1, ptr2, len1, len2); /* Unlock the resource before returning. */ + return result; + } + + bytesreadtotal += bytesread; + } + + if (ptr2 && len2) + { + unsigned int bytesread = 0; + + result = readData(ptr2, len2, &bytesread); + if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF)) + { + unlock(ptr1, ptr2, len1, len2); /* Unlock the resource before returning. */ + return result; + } + + bytesreadtotal += bytesread; + } + + /* + File was truncated somehow, but allow it to load. + */ + if (result == FMOD_ERR_FILE_EOF) + { + numbytes = bytesreadtotal; + } + + result2 = unlock(ptr1, ptr2, len1, len2); + if (result2 != FMOD_OK) + { + return result2; + } + + numbytes -= bytesreadtotal; + offbytes += bytesreadtotal; + readbytes += bytesreadtotal; + } + } + + if (read) + { + getSamplesFromBytes(readbytes, read); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::seek(int subsound, unsigned int position) +{ + FMOD_RESULT result; + unsigned int newpos; + + if (!mCodec->mFile) + { + return FMOD_ERR_FILE_COULDNOTSEEK; + } + + result = mCodec->setPosition(subsound, position, FMOD_TIMEUNIT_PCM); + + newpos = position; + if (mCodec->getPosition(&newpos, FMOD_TIMEUNIT_PCM) == FMOD_OK) + { + position = newpos; + } + + mPosition = newpos; + + if (mPostSetPositionCallback) + { + mPostSetPositionCallback((FMOD_SOUND *)this, subsound, newpos, FMOD_TIMEUNIT_PCM); + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::clear(unsigned int offset, unsigned int numsamples) +{ + FMOD_RESULT result; + unsigned int offbytes, numbytes; + void *ptr1, *ptr2; + unsigned int len1, len2, chunksize; + FMOD_CODEC_WAVEFORMAT waveformat; + + mPosition = offset; + + if (mMode & FMOD_CREATECOMPRESSEDSAMPLE) + { + offbytes = offset; + numbytes = numsamples; + } + else + { + getBytesFromSamples(offset, &offbytes, false); + getBytesFromSamples(numsamples, &numbytes, false); + } + + result = mCodec->mDescription.getwaveformat(mCodec, mCodec->mSubSoundIndex, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + chunksize = SOUND_READCHUNKSIZE; + + if (mMode & FMOD_CREATESTREAM) + { + if (mCodec->mBlockAlign) + { + chunksize /= mCodec->mBlockAlign; + chunksize *= mCodec->mBlockAlign; + + if (!chunksize) + { + chunksize = mCodec->mBlockAlign; + } + } + } + else + { + if (waveformat.blockalign) + { + chunksize /= waveformat.blockalign; + chunksize *= waveformat.blockalign; + + if (!chunksize) + { + chunksize = waveformat.blockalign; + } + } + } + + if (chunksize > SOUND_READCHUNKSIZE) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "SoundI::read", "ERROR - Contact support. A codec has specified a block alignment of %d which is bigger than the maximum codec read size of %d\n", waveformat.blockalign, SOUND_READCHUNKSIZE)); + return FMOD_ERR_INTERNAL; + } + + while (numbytes) + { + int size = numbytes > chunksize ? chunksize : numbytes, bytesreadtotal = 0; + + result = lock(offbytes, size, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + if (ptr1 && len1) + { + FMOD_memset(ptr1, 0, len1); + bytesreadtotal += len1; + } + + if (ptr2 && len2) + { + FMOD_memset(ptr2, 0, len2); + bytesreadtotal += len2; + } + + result = unlock(ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + return result; + } + + numbytes -= bytesreadtotal; + offbytes += bytesreadtotal; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::release(bool freethis) +{ + FMOD_RESULT result; + Codec *oldcodec; + AsyncData *oldasyncdata; + ChannelReal *oldstreamchannel; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::release", "%s (%p)\n", mName ? mName : "(null)", this)); + + if (mFlags & FMOD_SOUND_FLAG_RELEASING) + { + return FMOD_ERR_INVALID_HANDLE; /* If it gets recursive because stopsound below calls a channel callback which tries to release it again, return. */ + } + + if (mFlags & FMOD_SOUND_FLAG_PRELOADEDFSB) + { + return FMOD_ERR_PRELOADED; + } + + if (mFlags & FMOD_SOUND_FLAG_PROGRAMMERSOUND) + { + return FMOD_ERR_PROGRAMMERSOUND; + } + + mFlags |= FMOD_SOUND_FLAG_RELEASING; + + /* + The reason there is a sleep loop here and not a criticalsection is that we don't want one sound release stalling on other non blocking sounds, + and we also don't want the non blocking thread stalling on release calls (not such a huge issue but it slows loading down unescessarily). + */ + while ((mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_ERROR) || (mFlags & FMOD_SOUND_FLAG_DONOTRELEASE)) + { + FMOD_OS_Time_Sleep(2); + } + + if (mCodec) + { + /* + Cancel any pending file requests. + This is needed for Wii / GC. + */ + if (mCodec->mFile) + { + mCodec->mFile->cancel(); + } + } + + /* + Stop any channels that are playing this sound. + */ + if (mSystem) + { + result = mSystem->stopSound(this); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Delete all syncpoints if they exist. + */ + if (mSyncPointHead) + { + SyncPoint *point = SAFE_CAST(SyncPoint, mSyncPointHead->getNext()); + + while (point != mSyncPointTail) + { + deleteSyncPointInternal((FMOD_SYNCPOINT *)point, true); + + point = SAFE_CAST(SyncPoint, mSyncPointHead->getNext()); + } + + FMOD_Memory_Free(mSyncPointHead); // mSyncPointTail is part of this memory block as well + mSyncPointHead = 0; + mSyncPointTail = 0; + } + + if (mSyncPointMemory) + { + FMOD_Memory_Free(mSyncPointMemory); + mSyncPointMemory = 0; + } + + oldcodec = mCodec; + oldasyncdata = mAsyncData; + oldstreamchannel = 0; + + /* + Release the stream samples if this is a stream + */ + #ifdef FMOD_SUPPORT_STREAMING + if (isStream() && mCodec && (!mSubSoundParent || mSubSoundParent == this || (mSubSoundParent && mCodec != mSubSoundParent->mCodec))) + { + Stream *stream = SAFE_CAST(Stream, this); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::release", "remove stream samples. (%p)\n", this)); + + /* + Wait for the stream to be totally done with before removing. + */ + if (stream->mChannel) + { + stream->mChannel->mFinished = true; /* Make the thread set the finished flag. */ + + if (!(mFlags & FMOD_SOUND_FLAG_PLAYED)) + { + stream->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED; + } + } + + if (!(mSystem->mFlags & FMOD_INIT_STREAM_FROM_UPDATE) && !stream->mStreamNode.isEmpty()) + { + while (!(stream->mFlags & FMOD_SOUND_FLAG_THREADFINISHED)) + { + FMOD_OS_Time_Sleep(2); + } + } + + /* + Safely remove from list. (fast) + */ + FMOD_OS_CriticalSection_Enter(mSystem->mStreamListCrit); + { + stream->mStreamNode.removeNode(); + stream->mStreamNode.setData(0); + } + FMOD_OS_CriticalSection_Leave(mSystem->mStreamListCrit); + + oldstreamchannel = stream->mChannel; + + if (stream->mSample) + { + stream->mSample->mCodec = 0; + FLOG_INDENT(4); + { + stream->mSample->release(); + } + FLOG_INDENT(-4); + stream->mSample = 0; + } + } + #endif + + /* + Remove all subsounds of this sound if they exist! + */ + if (mNumSubSounds && mSubSound) + { + int count; + + if (mNumActiveSubSounds) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::release", "release subsounds. (%p)\n", this)); + FLOG_INDENT(4); + for (count = 0; count < mNumSubSounds; count++) + { + if (mSubSound[count]) + { + /* + Points to the same codec as the parent sound so don't release it more than once + */ + if (mSubSound[count]->mCodec == oldcodec) + { + mSubSound[count]->mCodec = 0; + } + if (mSubSound[count]->mAsyncData == oldasyncdata) + { + mSubSound[count]->mAsyncData = 0; + } + + #ifdef FMOD_SUPPORT_STREAMING + if (mSubSound[count]->isStream()) + { + Stream *stream = SAFE_CAST(Stream, mSubSound[count]); + + if (stream->mChannel == oldstreamchannel) + { + stream->mChannel = 0; + } + } + #endif + + if (mSubSound[count]->mSubSoundShared) /* Null out all the other shared pointers that point to this address. */ + { + int count2; + + for (count2 = count + 1; count2 < mNumSubSounds; count2++) + { + if (mSubSound[count2] == mSubSound[count]) + { + mSubSound[count2] = 0; + } + } + } + + mSubSound[count]->release(); + mSubSound[count] = 0; + } + } + FLOG_INDENT(-4); + } + + FMOD_Memory_Free(mSubSound); + mSubSound = 0; + + if (mSubSoundShared) + { + FMOD_Memory_Free(mSubSoundShared); + mSubSoundShared = 0; + } + } + + /* + Release the codec if it exists + */ + if (mCodec) + { + if (!mSubSoundParent || mSubSoundParent == this || (mSubSoundParent && mCodec != mSubSoundParent->mCodec)) /* Don't release the parent's codec. */ + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::release", "release codec. (%p)\n", this)); + + mCodec->release(); + mCodec = 0; + } + } + + #ifdef FMOD_SUPPORT_STREAMING + if (isStream()) + { + Stream *stream = SAFE_CAST(Stream, this); + Stream *parentstream = SAFE_CAST(Stream, stream->mSubSoundParent); + + if (parentstream && parentstream->mChannel == stream->mChannel) + { + stream->mChannel = 0; /* Don't free the parent's channel object. */ + } + + if (stream->mChannel) /* This must be freed after sample release, as stream->mSample tries to stopSound as well (and reference the channel). */ + { + FMOD_Memory_Free(stream->mChannel); + stream->mChannel = 0; + } + } + #endif + + if (mAsyncData) + { + if (!mSubSoundParent || mSubSoundParent->mAsyncData != mAsyncData) + { + FMOD_Memory_Free(mAsyncData); + } + mAsyncData = 0; + } + + /* + Null out any parent references to this sound if this sound is a subsound of another sound. + */ + if (mSubSoundParent) + { + int count; + + for (count = 0; count < mSubSoundParent->mNumSubSounds; count++) + { + if (mSubSoundParent->mSubSound && mSubSoundParent->mSubSound[count] == this) + { + mSubSoundParent->setSubSoundInternal(count, 0, true); + break; + } + } + } + +#ifdef FMOD_SUPPORT_SENTENCING + if (mSubSoundList) + { + FMOD_Memory_Free(mSubSoundList); + mSubSoundList = 0; + } +#endif + + if (mName) + { + FMOD_Memory_Free(mName); + mName = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::release", "free this. (%p)\n", this)); + + FMOD_OS_CriticalSection_Enter(mSystem->gSoundListCrit); + { + removeNode(); + mSoundGroupNode.removeNode(); + } + FMOD_OS_CriticalSection_Leave(mSystem->gSoundListCrit); + + if (freethis && !mSubSoundShared) + { + FMOD_Memory_Free(this); + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::release", "done (%p)\n", this)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getSystemObject(System **system) +{ + if (!system) + { + return FMOD_ERR_INVALID_PARAM; + } + + *system = (System *)mSystem; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + return FMOD_ERR_BADCOMMAND; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + return FMOD_ERR_BADCOMMAND; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setDefaults(float frequency, float volume, float pan, int priority) +{ + if (volume > 1) + { + volume = 1; + } + if (volume < 0) + { + volume = 0; + } + if (pan < -1) + { + pan = -1; + } + if (pan > 1) + { + pan = 1; + } + if (priority < 0) + { + priority = 0; + } + if (priority > 256) + { + priority = 256; + } + + mDefaultFrequency = frequency; + mDefaultVolume = volume; + mDefaultPan = pan; + mDefaultPriority = priority; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getDefaults(float *frequency, float *volume, float *pan, int *priority) +{ + if (frequency) + { + *frequency = mDefaultFrequency; + } + if (volume) + { + *volume = mDefaultVolume; + } + if (pan) + { + *pan = mDefaultPan; + } + if (priority) + { + *priority = mDefaultPriority; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setVariations(float frequencyvar, float volumevar, float panvar) +{ + if (frequencyvar >= 0) + { + mFrequencyVariation = frequencyvar; + } + if (volumevar >= 0) + { + mVolumeVariation = volumevar; + } + if (panvar >= 0) + { + mPanVariation = panvar; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getVariations(float *frequencyvar, float *volumevar, float *panvar) +{ + if (frequencyvar) + { + *frequencyvar = mFrequencyVariation; + } + + if (volumevar) + { + *volumevar = mVolumeVariation; + } + + if (panvar) + { + *panvar = mPanVariation; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::set3DMinMaxDistance(float min, float max) +{ + if (min < 0 || max < 0 || max < min) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::set3DMinMaxDistance", "ERROR! Invalid parameter passed in. min = %.02f = max %.02f\n", min, max)); + return FMOD_ERR_INVALID_PARAM; + } + + mMinDistance = min; + mMaxDistance = max; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::get3DMinMaxDistance(float *min, float *max) +{ + if (min) + { + *min = mMinDistance; + } + + if (max) + { + *max = mMaxDistance; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::set3DConeSettings(float insideconeangle, float outsideconeangle, float outsidevolume) +{ + if (insideconeangle < 0) + { + insideconeangle = 0; + } + if (outsideconeangle < 0) + { + outsideconeangle = 0; + } + if (insideconeangle > 360) + { + insideconeangle = 360; + } + if (outsideconeangle > 360) + { + outsideconeangle = 360; + } + if (outsidevolume < 0) + { + outsidevolume = 0; + } + if (outsidevolume > 1) + { + outsidevolume = 1; + } + + mConeInsideAngle = insideconeangle; + mConeOutsideAngle = outsideconeangle; + mConeOutsideVolume = outsidevolume; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::get3DConeSettings(float *insideconeangle, float *outsideconeangle, float *outsidevolume) +{ + if (insideconeangle) + { + *insideconeangle = mConeInsideAngle; + } + + if (outsideconeangle) + { + *outsideconeangle = mConeOutsideAngle; + } + + if (outsidevolume) + { + *outsidevolume = mConeOutsideVolume; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::set3DCustomRolloff(FMOD_VECTOR *points, int numpoints) +{ + if (numpoints < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (points) + { + int count; + + for (count = 1; count < numpoints; count++) + { + if (points[count].x <= points[count-1].x) + { + return FMOD_ERR_INVALID_PARAM; + } + if (points[count].y < 0 || points[count].y > 1) + { + return FMOD_ERR_INVALID_PARAM; + } + } + } + + mRolloffPoint = points; + mNumRolloffPoints = numpoints; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::get3DCustomRolloff(FMOD_VECTOR **points, int *numpoints) +{ + if (points) + { + *points = mRolloffPoint; + } + + if (numpoints) + { + *numpoints = mNumRolloffPoints; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setSubSound(int index, SoundI *subsound) +{ + return setSubSoundInternal(index, subsound, false); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setSubSoundInternal(int index, SoundI *subsound, bool calledfromrelease) +{ + int count; + unsigned int oldlength = 0, newlength = 0; + + if (index < 0 || index >= mNumSubSounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (subsound && subsound->mSubSoundParent) + { + return FMOD_ERR_SUBSOUND_ALLOCATED; + } + + /* + Cannot detach or replace a subsound if the parent stream shares subsounds + */ + if (mSubSoundShared) + { + return FMOD_ERR_SUBSOUND_CANTMOVE; + } + +#ifdef FMOD_SUPPORT_SENTENCING + /* + Do error checking that the new subsound is valid + */ + if (subsound && mSubSoundList) + { + if (subsound->isStream() != isStream()) + { + return FMOD_ERR_SUBSOUND_MODE; /* Tried to mix a stream and a sample. */ + } + + if ((subsound->mMode & FMOD_CREATECOMPRESSEDSAMPLE) != (mMode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + return FMOD_ERR_SUBSOUND_MODE; + } + + if (subsound->mFormat != mFormat) + { + return FMOD_ERR_FORMAT; /* Tried to mix different sample formats. */ + } + + if (subsound->mChannels != mChannels) + { + return FMOD_ERR_FORMAT; /* Tried to mix different channel counts. */ + } + + if (!(subsound->mMode & FMOD_SOFTWARE) && !subsound->isStream()) + { + return FMOD_ERR_NEEDSSOFTWARE; + } + } +#endif + +#ifdef FMOD_SUPPORT_STREAMING + bool blockstream = false; + + if (isStream()) + { + SoundI *soundi = this; + + if (soundi->mSubSoundShared) + { + soundi = soundi->mSubSoundShared; + } + else if (soundi->mSubSound) + { + soundi = soundi->mSubSound[soundi->mSubSoundIndex]; + } + + if (soundi && soundi->mFlags & FMOD_SOUND_FLAG_PLAYED && !(soundi->mFlags & FMOD_SOUND_FLAG_FINISHED)) + { + blockstream = true; + } + } + + if (blockstream) + { + FMOD_OS_CriticalSection_Enter(mSystem->mStreamUpdateCrit); + } +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE + bool blockdsp = false; + + if (mMode & FMOD_SOFTWARE && !calledfromrelease) + { + blockdsp = true; + } + + if (blockdsp) + { + FMOD_OS_CriticalSection_Enter(mSystem->mDSPCrit); + } +#endif + + SoundI *oldsubsound = mSubSound[index]; + + if (oldsubsound) + { + if (oldsubsound->mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + mCodec->mDescription.getwaveformat(mCodec, index, &waveformat); + + oldlength = waveformat.lengthpcm; + } + else + { + oldlength = oldsubsound->mLength; + } + } + + /* + Setup the new subsound + */ + if (subsound) + { + if (!subsound->isStream()) + { + Sample *sample = (Sample *)subsound; + + for (int i = 0; i < sample->mNumSubSamples; i++) + { + sample->mSubSample[i]->mCodec = mCodec; + + if (oldsubsound) + { + Sample *oldsample = (Sample *)oldsubsound; + + sample->mSubSample[i]->mSubSampleParent = oldsample->mSubSampleParent; + } + } + } + + subsound->mSubSoundIndex = index; + subsound->mSubSoundParent = this; + + if (subsound->mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + subsound->mCodec->mDescription.getwaveformat(mCodec, subsound->mSubSoundIndex, &waveformat); + + newlength = waveformat.lengthpcm; + } + else + { + newlength = subsound->mLength; + } + } + + /* + If there was an old subsound here, remove the link to this parent. + */ + if (oldsubsound) + { + if (!isStream() && oldsubsound->mSubSoundParent->mCodec == oldsubsound->mCodec) + { + oldsubsound->mCodec = 0; + } + + oldsubsound->mSubSoundParent = 0; + + if (!subsound) + { + mNumActiveSubSounds--; + } + } + else + { + if (subsound) + { + mNumActiveSubSounds++; + } + } + + mSubSound[index] = subsound; + + /* + Recalculate the length of the sentence if one exists. + */ +#ifdef FMOD_SUPPORT_SENTENCING + if (mSubSoundListNum) + { + if (!(mCodec && mCodec->mFlags & FMOD_CODEC_USERLENGTH)) + { + mLength -= oldlength; + mLength += newlength; + } + + for (count = 0; count < mSubSoundListNum; count++) + { + if (mSubSoundList[count].mIndex == index) + { + mSubSoundList[count].mLength = newlength; + } + } + } +#endif + + mLoopStart = 0; + mLoopLength = mLength; + +#ifdef FMOD_SUPPORT_STREAMING + if (isStream()) + { + Stream *stream = SAFE_CAST(Stream, this); + + stream->mChannel->setLoopPoints(0, mLength - 1); + } +#endif + +#ifndef PLATFORM_PS3 + /* + For wavetable pcm, a shift of mPosition is needed to adjust for subsound changes if the sentence is already playing. + PS3 doesn't use wavetable for PCM, it uses a codec instead. + */ +#ifdef FMOD_SUPPORT_STREAMING + else +#endif + if (mMode & FMOD_SOFTWARE && mFormat >= FMOD_SOUND_FORMAT_PCM8 && mFormat <= FMOD_SOUND_FORMAT_PCMFLOAT && oldlength != newlength) + { + for (count = 0; count < mSystem->mNumChannels; count++) + { + FMOD::SoundI *soundi = 0; + + mSystem->mChannel[count].getCurrentSound(&soundi); + + if (soundi == this) + { + unsigned int pos, currsubsound; + + mSystem->mChannel[count].setLoopPoints(mLoopStart, FMOD_TIMEUNIT_PCM, mLoopLength - 1, FMOD_TIMEUNIT_PCM); + + /* + Now adjust the channel's position by how much the sentence has been shifted by. + */ + mSystem->mChannel[count].getPosition(&pos, FMOD_TIMEUNIT_PCM); + mSystem->mChannel[count].getPosition(&currsubsound, FMOD_TIMEUNIT_SENTENCE_SUBSOUND); + + if (currsubsound > (unsigned int)index) + { + pos -= oldlength; + pos += newlength; + + mSystem->mChannel[count].setPosition(pos, FMOD_TIMEUNIT_PCM); + } + } + } + } +#endif + + +#ifdef FMOD_SUPPORT_SOFTWARE + if (blockdsp) + { + FMOD_OS_CriticalSection_Leave(mSystem->mDSPCrit); + } +#endif + +#ifdef FMOD_SUPPORT_STREAMING + if (isStream()) + { + Stream *stream = SAFE_CAST(Stream, this); + + /* + If there is a new subsound, and a new playsound occurs, tell the stream to reflush its buffer. + */ + if (subsound) + { + stream->mFlags |= FMOD_SOUND_FLAG_WANTSTOFLUSH; + } + + if (blockstream) + { + FMOD_OS_CriticalSection_Leave(mSystem->mStreamUpdateCrit); + } + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getSubSound(int index, SoundI **subsound) +{ + if (!subsound) + { + return FMOD_ERR_INVALID_PARAM; + } + + *subsound = 0; + + #ifdef FMOD_DEBUG + if (isStream()) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::getSubSound", "sound %p. Subsound index %d / %d\n", this, index, mNumSubSounds)); + } + #endif + + if (index < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (index >= mNumSubSounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + *subsound = mSubSound[index]; + +#ifdef FMOD_SUPPORT_NONBLOCKING + if (mSubSound[index] && mMode & FMOD_NONBLOCKING && mSubSound[index]->isStream()) + { + if (mSubSound[index]->mOpenState == FMOD_OPENSTATE_SEEKING) + { + return FMOD_ERR_NOTREADY; + } + } +#endif + + if (mSubSound[index] && mSubSound[index]->mSubSoundShared) + { + mSubSound[index]->updateSubSound(index, false); + } + + if (mSubSound[index]) + { +#ifdef FMOD_SUPPORT_STREAMING + if (mSubSound[index]->isStream()) + { + Stream *stream = SAFE_CAST(Stream, *subsound); + + #ifdef FMOD_SUPPORT_NONBLOCKING + if (mMode & FMOD_NONBLOCKING) + { + FMOD_RESULT result; + Stream *streamParent = SAFE_CAST(Stream, stream->mSubSoundParent); + + if ((index == stream->mSubSoundParent->mSubSoundIndex && (stream->mPosition == streamParent->mInitialPosition + stream->mSample->mLength || (stream->mLength < stream->mSample->mLength && stream->mPosition == streamParent->mInitialPosition + stream->mLength))) && !(stream->mFlags & FMOD_SOUND_FLAG_WANTSTOFLUSH)) + { + return FMOD_OK; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::getSubSound", "starting non blocking seek and setting sound to not ready state\n")); + + stream->mOpenState = FMOD_OPENSTATE_SEEKING; + mOpenState = FMOD_OPENSTATE_SEEKING; + + result = AsyncThread::getAsyncThread(stream); + if (result != FMOD_OK) + { + return result; + } + + FMOD_OS_CriticalSection_Enter(stream->mAsyncData->mThread->mCrit); + { + stream->mAsyncData->mNode.setData(stream); + stream->mAsyncData->mNode.addBefore(&stream->mAsyncData->mThread->mHead); + } + FMOD_OS_CriticalSection_Leave(stream->mAsyncData->mThread->mCrit); + + stream->mAsyncData->mThread->mThread.wakeupThread(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::getSubSound", "finished preparing nonblocking getSubSound seek\n")); + } + else + #endif + { + if (stream->mSubSoundIndex != stream->mCodec->mSubSoundIndex) + { + FMOD_RESULT result; + + result = stream->setPosition(0, FMOD_TIMEUNIT_PCM); + if (result == FMOD_OK) + { + stream->flush(); + } + } + } + } +#endif + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setSubSoundSentence(int *subsoundlist, int numsubsounds) +{ + FMOD_RESULT result; +#ifdef FMOD_SUPPORT_SENTENCING + int count; +#ifndef FMOD_STATICFORPLUGINS + LocalCriticalSection crit(mSystem->mStreamUpdateCrit, true); +#endif + + if (!mNumSubSounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (subsoundlist && !numsubsounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!(mMode & FMOD_SOFTWARE) && !isStream()) + { + return FMOD_ERR_NEEDSSOFTWARE; + } + + #ifdef PLATFORM_PS3 + if (numsubsounds > FMOD_PS3_SUBSOUNDLISTMAXITEMS) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "SoundI::setSubSoundSentence", "Maximum number of subsound sentences (%d) reached for PS3!", FMOD_PS3_SUBSOUNDLISTMAXITEMS)); + return FMOD_ERR_INTERNAL; + } + #endif + + result = mSystem->stopSound(this); + if (result != FMOD_OK) + { + return result; + } + + /* + Do error checking that the subsounds within the parent are valid. + */ + if (subsoundlist) + { + if (!isStream()) /* Samples are more flexible, there is no stream buffer to worry about. Reset the mode here */ + { + mFormat = FMOD_SOUND_FORMAT_NONE; + mChannels = 0; + } + + for (count = 0; count < numsubsounds; count++) + { + SoundI *subsound; + + if (subsoundlist[count] < 0 || subsoundlist[count] >= mNumSubSounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + subsound = mSubSound[subsoundlist[count]]; + if(!subsound) + { + continue; + } + + if (subsound->isStream() != isStream()) + { + return FMOD_ERR_SUBSOUND_MODE; /* Tried to mix a stream and a sample. */ + } + + if ((subsound->mMode & FMOD_CREATECOMPRESSEDSAMPLE) != (mMode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + return FMOD_ERR_SUBSOUND_MODE; + } + + if (mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + mCodec->mDescription.getwaveformat(mCodec, subsoundlist[count], &waveformat); + + if (!mFormat && !mChannels) + { + mFormat = waveformat.format; + mChannels = waveformat.channels; + } + else + { + if (mFormat != waveformat.format) + { + return FMOD_ERR_FORMAT; /* Tried to mix different sample formats. */ + } + if (mChannels != waveformat.channels) + { + return FMOD_ERR_FORMAT; /* Tried to mix different channel counts. */ + } + } + } + else + { + if (!mFormat && !mChannels) + { + mFormat = subsound->mFormat; + mChannels = subsound->mChannels; + } + else + { + if (mFormat != subsound->mFormat) + { + return FMOD_ERR_FORMAT; /* Tried to mix different sample formats. */ + } + if (mChannels != subsound->mChannels) + { + return FMOD_ERR_FORMAT; /* Tried to mix different channel counts. */ + } + } + } + + if (!(subsound->mMode & FMOD_SOFTWARE) && !subsound->isStream()) + { + return FMOD_ERR_NEEDSSOFTWARE; + } + } + } + + if (mSubSoundList) + { + FMOD_Memory_Free(mSubSoundList); + mSubSoundList = NULL; + } + + mSubSoundListNum = numsubsounds; + + if (mSubSoundListNum) + { + mSubSoundList = (SoundSentenceEntry *)FMOD_Memory_Calloc(sizeof(SoundSentenceEntry) * mSubSoundListNum); + if (!mSubSoundList) + { + return FMOD_ERR_MEMORY; + } + + if (!(mCodec && mCodec->mFlags & FMOD_CODEC_USERLENGTH)) + { + mLength = 0; + } + + for (count = 0; count < mSubSoundListNum; count++) + { + if (subsoundlist) + { + mSubSoundList[count].mIndex = subsoundlist[count]; + } + else + { + mSubSoundList[count].mIndex = count; + } + + if (mSubSound[mSubSoundList[count].mIndex]) + { + if (mSubSoundShared) + { + FMOD_CODEC_WAVEFORMAT waveformat; + + mCodec->mDescription.getwaveformat(mCodec, mSubSoundList[count].mIndex, &waveformat); + + mSubSoundList[count].mLength = waveformat.lengthpcm; + } + else + { + mSubSoundList[count].mLength = mSubSound[mSubSoundList[count].mIndex]->mLength; + } + + if (!(mCodec && mCodec->mFlags & FMOD_CODEC_USERLENGTH)) + { + mLength += mSubSoundList[count].mLength; + } + } + else + { + mSubSoundList[count].mLength = 0; + } + } + } + + if (!(mCodec && mCodec->mFlags & FMOD_CODEC_USERLENGTH)) + { + mLoopStart = 0; + mLoopLength = mLength; + } + +#ifdef FMOD_SUPPORT_STREAMING + if (isStream() && mSubSoundList) + { + Stream *parent = SAFE_CAST(Stream, this); + Stream *substream = SAFE_CAST(Stream, mSubSound[mSubSoundList[0].mIndex]); + + if (!substream) + { + return FMOD_OK; + } + + parent->mFlags &= ~FMOD_SOUND_FLAG_FULLYBUFFERED; + + #ifdef FMOD_SUPPORT_NONBLOCKING + if (mMode & FMOD_NONBLOCKING) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::getSubSound", "starting non blocking seek and setting sound to not ready state\n")); + + substream->mOpenState = FMOD_OPENSTATE_SEEKING; + mOpenState = FMOD_OPENSTATE_SEEKING; + + result = AsyncThread::getAsyncThread(this); + if (result != FMOD_OK) + { + return result; + } + + mAsyncData->mNode.setData(this); + + FMOD_OS_CriticalSection_Enter(mAsyncData->mThread->mCrit); + { + mAsyncData->mNode.addBefore(&mAsyncData->mThread->mHead); + } + FMOD_OS_CriticalSection_Leave(mAsyncData->mThread->mCrit); + + mAsyncData->mThread->mThread.wakeupThread(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SoundI::getSubSound", "finished preparing nonblocking getSubSound seek\n")); + } + else + #endif + { + FMOD_RESULT result; + + mFlags &= ~FMOD_SOUND_FLAG_FULLYBUFFERED; + + result = parent->setPosition(0, FMOD_TIMEUNIT_PCM); + if (result == FMOD_OK) + { + parent->flush(); + } + } + } +#endif + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getName(char *name, int namelen) +{ + if (!name) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (namelen > FMOD_STRING_MAXNAMELEN) + { + namelen = FMOD_STRING_MAXNAMELEN; + } + + if (mName) + { + if (mMode & FMOD_UNICODE) + { + FMOD_strncpyW((short *)name, (short *)mName, namelen / 2); + } + else + { + FMOD_strncpy(name, mName, namelen); + } + } + else + { + if (mMode & FMOD_UNICODE) + { + FMOD_strncpyW((short *)name, (const short *)L"(null)", namelen / 2); + } + else + { + FMOD_strncpy(name, "(null)", namelen); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getLength(unsigned int *length, FMOD_TIMEUNIT lengthtype) +{ + if (!length) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SENTENCING + if (lengthtype == FMOD_TIMEUNIT_SENTENCE) + { + *length = mSubSoundListNum; + } + else +#endif + if (lengthtype == FMOD_TIMEUNIT_PCM) + { + *length = mLength; + } + else if (lengthtype == FMOD_TIMEUNIT_MS) + { + if (!mDefaultFrequency) + { + *length = (unsigned int)-1; + } + else if (mLength == 0xFFFFFFFF) + { + *length = (unsigned int)-1; + } + else + { + *length = (unsigned int)((FMOD_UINT64)mLength * (FMOD_UINT64)1000 / (FMOD_UINT64)mDefaultFrequency); + } + } + else if (lengthtype == FMOD_TIMEUNIT_PCMBYTES) + { + if (mLength == 0xFFFFFFFF) + { + *length = (unsigned int)-1; + } + else if (mMode & FMOD_CREATECOMPRESSEDSAMPLE) + { + *length = mLength * sizeof(signed short) * mChannels; + } + else + { + getBytesFromSamples(mLength, length); + } + } + else + { + if (!mCodec) + { + return FMOD_ERR_INVALID_PARAM; + } + + return mCodec->getLength(length, lengthtype); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getFormat(FMOD_SOUND_TYPE *type, FMOD_SOUND_FORMAT *format, int *channels, int *bits) +{ + if (type) + { + *type = mType; + } + + if (format) + { + *format = mFormat; + } + + if (channels) + { + *channels = mChannels; + } + + if (bits) + { + SoundI::getBitsFromFormat(bits); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getNumSubSounds(int *numsubsounds) +{ + if (!numsubsounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numsubsounds = mNumSubSounds; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getNumTags(int *numtags, int *numtagsupdated) +{ + if (!numtags && !numtagsupdated) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (numtags) + { + *numtags = 0; + } + if (numtagsupdated) + { + *numtagsupdated = 0; + } + + if (mCodec && mCodec->mMetadata) + { + return mCodec->mMetadata->getNumTags(numtags, numtagsupdated); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getTag(const char *name, int index, FMOD_TAG *tag) +{ + if (!tag) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mCodec || !mCodec->mMetadata) + { + return FMOD_ERR_TAGNOTFOUND; + } + + return mCodec->mMetadata->getTag(name, index, tag); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getOpenState(FMOD_OPENSTATE *openstate, unsigned int *percentbuffered, bool *starving) +{ + if (openstate) + { + *openstate = mOpenState; + if (mOpenState == FMOD_OPENSTATE_READY && (mFlags & FMOD_SOUND_FLAG_DONOTRELEASE)) + { + *openstate = FMOD_OPENSTATE_BUFFERING; + } + + if (mFlags & FMOD_SOUND_FLAG_PLAYED) + { + #ifdef FMOD_SUPPORT_STREAMING + if (isStream()) + { + if (*openstate == FMOD_OPENSTATE_READY && mCodec && !(mSystem->mFlags & FMOD_INIT_STREAM_FROM_UPDATE)) + { + bool busy = false; + + if (mCodec && mCodec->mFile) + { + mCodec->mFile->isBusy(&busy, 0); + } + + if (!(mFlags & FMOD_SOUND_FLAG_THREADFINISHED) || busy) + { + *openstate = FMOD_OPENSTATE_STREAMING; + } + } + } + else + #endif // #ifdef FMOD_SUPPORT_STREAMING + { + if (mNumAudible || mLastAudibleDSPClock == mSystem->mDSPClock.mValue) + { + *openstate = FMOD_OPENSTATE_STREAMING; + } + } + } + } + + if (percentbuffered) + { + if (mCodec && mCodec->mFile && (mOpenState == FMOD_OPENSTATE_BUFFERING || mOpenState == FMOD_OPENSTATE_READY)) + { + mCodec->mFile->isBusy(0, percentbuffered); + } + else + { + *percentbuffered = 0; + } + } + + if (starving) + { + *starving = false; + + if (mCodec && mCodec->mFile) + { + mCodec->mFile->isStarving(starving); + } + } + + return mAsyncData ? mAsyncData->mResult : FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::readData(void *buffer, unsigned int numbytes, unsigned int *read) +{ + FMOD_RESULT result; + unsigned int offbytes, readbytes, scale, position = 0, chunksize, blockalign; + int bits; + FMOD_CODEC_WAVEFORMAT waveformat; + + if (!mCodec) + { + return FMOD_ERR_PLUGIN_MISSING; + } + + if (!buffer) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = getBitsFromFormat(&bits); + if (result != FMOD_OK) + { + return result; + } + + if (!mCodec) + { + return FMOD_ERR_UNSUPPORTED; + } + + if (!mCodec->mFile) + { + return FMOD_ERR_UNSUPPORTED; + } + + result = mCodec->mDescription.getwaveformat(mCodec, mCodec->mSubSoundIndex, &waveformat); + if (result != FMOD_OK) + { + mFlags &= ~FMOD_SOUND_FLAG_DONOTRELEASE; + return result; + } + + offbytes = 0; + readbytes = 0; + + scale = 1; + if (mChannels != waveformat.channels && mChannels == 1) + { + scale = waveformat.channels; + } + + chunksize = SOUND_READCHUNKSIZE; + + blockalign = 1; + if (mMode & FMOD_CREATESTREAM) + { + if (mCodec->mBlockAlign) + { + blockalign = mCodec->mBlockAlign; + } + } + else + { + if (waveformat.blockalign) + { + blockalign = waveformat.blockalign; + } + } + + chunksize /= blockalign; + chunksize *= blockalign; + if (!chunksize) + { + chunksize = blockalign; + } + + while (numbytes) + { + unsigned int size = numbytes > (chunksize / scale) ? (chunksize / scale) : numbytes; + unsigned int bytesreadtotal = 0, bytesread = 0; + void *ptr; + + ptr = (char *)buffer + offbytes; + + if (mMode & FMOD_CREATECOMPRESSEDSAMPLE) + { + result = mCodec->mFile->read(ptr, 1, size, &bytesread); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + mFlags &= ~FMOD_SOUND_FLAG_DONOTRELEASE; + return result; + } + } + else + { + result = mCodec->read(ptr, size, &bytesread); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + mFlags &= ~FMOD_SOUND_FLAG_DONOTRELEASE; + return result; + } + } + + if (mPostReadCallback && bytesread) + { + mPostReadCallback(mPostCallbackSound, ptr, bytesread); + } + + bytesreadtotal += (bytesread / scale); + + /* + File was truncated somehow, but allow it to load. + */ + if (result == FMOD_ERR_FILE_EOF) + { + numbytes = bytesreadtotal; + } + + numbytes -= bytesreadtotal; + offbytes += bytesreadtotal; + readbytes += bytesreadtotal; + + getSamplesFromBytes(bytesreadtotal, &position); + mPosition += position; + + if (mPosition > mLength) + { + mPosition = mLength; + } + } + + if (read) + { + *read = readbytes; + } + + mFlags &= ~FMOD_SOUND_FLAG_DONOTRELEASE; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::seekData(unsigned int position) +{ + if (mCodec) + { + mCodec->reset(); + } + + return seek(mSubSoundIndex, position); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setSoundGroup(SoundGroupI *soundgroup) +{ + Sample *streamsample = 0; + + #ifdef FMOD_SUPPORT_STREAMING + if (isStream()) + { + Stream *stream = SAFE_CAST(Stream, this); + + streamsample = stream->mSample; + } + #endif + + mSoundGroup = soundgroup; + if (streamsample) + { + streamsample->mSoundGroup = soundgroup; + } + + + if (!mSoundGroup) + { + mSoundGroup = mSystem->mSoundGroup; + if (streamsample) + { + streamsample->mSoundGroup = mSystem->mSoundGroup; + } + } + + FMOD_OS_CriticalSection_Enter(mSystem->gSoundListCrit); + { + mSoundGroupNode.removeNode(); + mSoundGroupNode.addBefore(&mSoundGroup->mSoundHead); + mSoundGroupNode.setData(this); + + mSoundGroup->removeNode(); + mSoundGroup->addAfter(&mSystem->mSoundGroupUsedHead); + } + FMOD_OS_CriticalSection_Leave(mSystem->gSoundListCrit); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getSoundGroup(SoundGroupI **soundgroup) +{ + if (!soundgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + + *soundgroup = mSoundGroup; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getNumSyncPoints(int *numsyncpoints) +{ + if (!numsyncpoints) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mSubSoundShared) /* The syncpoint list is global to the shared sound, so we have to manually scan and find out how many belong to this subsound. */ + { + *numsyncpoints = 0; + + if (mSyncPointHead) + { + SyncPoint *current = SAFE_CAST(SyncPoint, mSyncPointHead->getNext()); + + while (current != mSyncPointTail) + { + if (current->mSubSoundIndex == mSubSoundIndex) + { + (*numsyncpoints)++; + } + + current = SAFE_CAST(SyncPoint, current->getNext()); + } + } + } + else + { + *numsyncpoints = mNumSyncPoints; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getSyncPoint(int index, FMOD_SYNCPOINT **point) +{ + FMOD_RESULT result; + SyncPoint *current; + int count, numsyncpoints; + + result = getNumSyncPoints(&numsyncpoints); + if (result != FMOD_OK) + { + return result; + } + + if (index < 0 || index >= numsyncpoints || !point) + { + return FMOD_ERR_INVALID_PARAM; + } + + current = SAFE_CAST(SyncPoint, mSyncPointHead->getNext()); + + count = 0; + while (current != mSyncPointTail) + { + bool execute = true; + + if (mSubSoundShared && current->mSubSoundIndex != mSubSoundIndex) + { + execute = false; + } + + if (execute) + { + if (count >= index) + { + *point = (FMOD_SYNCPOINT *)current; + break; + } + count++; + } + current = SAFE_CAST(SyncPoint, current->getNext()); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getSyncPointInfo(FMOD_SYNCPOINT *point, char *name, int namelen, unsigned int *offset, FMOD_TIMEUNIT offsettype) +{ + SyncPoint *spoint = (SyncPoint *)point; + + if (!point) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!name && !offset) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name) + { + if (spoint->mName) + { + FMOD_strncpy(name, spoint->mName, namelen); + } + else + { + FMOD_strncpy(name, "", namelen); + } + } + + if (offset) + { + if (offsettype == FMOD_TIMEUNIT_PCM) + { + *offset = spoint->mOffset; + } + else if (offsettype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(spoint->mOffset, offset, mChannels, mFormat); + } + else if (offsettype == FMOD_TIMEUNIT_MS) + { + *offset = (unsigned int)((float)spoint->mOffset * 1000.0f / mDefaultFrequency); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::addSyncPoint(unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **syncpoint, int subsound, bool fixupindicies) +{ + if (syncpoint) + { + *syncpoint = 0; + } + + return addSyncPointInternal(offset, offsettype, name, syncpoint, subsound, fixupindicies); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::addSyncPointInternal(unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **syncpoint, int subsound, bool fixupindicies) +{ + unsigned int pcm = 0; + SyncPoint *point = 0; + + if (!mSyncPointHead) + { + mSyncPointHead = (SyncPoint *)FMOD_Memory_Calloc(sizeof(SyncPoint) * 2); + if (!mSyncPointHead) + { + return FMOD_ERR_MEMORY; + } + mSyncPointTail = mSyncPointHead + 1; + + new (mSyncPointHead) SyncPoint; + new (mSyncPointTail) SyncPoint; + + mSyncPointHead->mOffset = (unsigned int)0; + mSyncPointHead->mName = 0; + mSyncPointTail->mOffset = (unsigned int)-1; + mSyncPointTail->mName = 0; + + mSyncPointTail->addAfter(mSyncPointHead); + } + + if (syncpoint && *syncpoint) + { + point = (SyncPoint *)*syncpoint; + + if (name) + { + new (point) SyncPointNamed; + } + else + { + new (point) SyncPoint; + } + + point->mStatic = 1; + } + else + { + if (name) + { + point = FMOD_Object_Alloc(SyncPointNamed); + if (!point) + { + return FMOD_ERR_MEMORY; + } + point->mName = ((SyncPointNamed *)point)->mNameMemory; + } + else + { + point = FMOD_Object_Alloc(SyncPoint); + if (!point) + { + return FMOD_ERR_MEMORY; + } + point->mName = 0; + } + + if (syncpoint) + { + *syncpoint = (FMOD_SYNCPOINT *)point; + } + + point->mStatic = 0; + } + + if (offsettype == FMOD_TIMEUNIT_PCM) + { + pcm = offset; + } + else if (offsettype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(offset, &pcm, mChannels, mFormat); + } + else if (offsettype == FMOD_TIMEUNIT_MS) + { + pcm = (unsigned int)((float)offset / 1000.0f * mDefaultFrequency); + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + + if (point->mName && name) + { + FMOD_strncpy(point->mName, name, FMOD_STRING_MAXNAMELEN); + } + point->mOffset = pcm; + + point->addAt(mSyncPointHead, mSyncPointTail, pcm); + + if (subsound == -1) + { + subsound = mSubSoundIndex; + } + + point->mSubSoundIndex = subsound; + point->mSound = this; + + mNumSyncPoints++; + + /* + Fix up indicies. + */ + if (fixupindicies) + { + syncPointFixIndicies(); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::deleteSyncPointInternal(FMOD_SYNCPOINT *point, bool ignoresubsoundindices) +{ + SyncPoint *spoint = (SyncPoint *)point; + + if (!point) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (this != spoint->mSound) + { + return FMOD_ERR_INVALID_SYNCPOINT; + } + + spoint->removeNode(); + + if (!spoint->mStatic) + { + FMOD_Memory_Free(point); + } + + mNumSyncPoints--; + + if (!ignoresubsoundindices) + { + syncPointFixIndicies(); /* Resort/shuffle syncpoints. */ + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::deleteSyncPoint(FMOD_SYNCPOINT *point) +{ + return deleteSyncPointInternal(point, false); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setMode(FMOD_MODE mode) +{ + /* + Allow switching between loop modes. + */ + if (mode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)) + { + mMode &= ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI); + + if (mode & FMOD_LOOP_OFF) + { + mMode |= FMOD_LOOP_OFF; + } + else if (mode & FMOD_LOOP_NORMAL) + { + mMode |= FMOD_LOOP_NORMAL; + } + else if (mode & FMOD_LOOP_BIDI) + { + mMode |= FMOD_LOOP_BIDI; + } + +#ifdef FMOD_SUPPORT_STREAMING + if (isStream()) + { + Stream *stream = SAFE_CAST(Stream, this); + + if (!stream) + { + return FMOD_ERR_INTERNAL; + } + + /* Don't clear FMOD_SOUND_FLAG_FINISHED if we are fully buffered since we have already read everything required to loop */ + if ((mode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)) && !(stream->mFlags & FMOD_SOUND_FLAG_FULLYBUFFERED)) + { + stream->mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED); + if (stream->mSubSoundParent) + { + stream->mSubSoundParent->mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED); + } + } + + stream->mChannel->mMode &= ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI); + stream->mChannel->mMode |= (mMode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)); + } +#endif + + } + + /* + Allow switching between head relative and world relative. + */ + if (mode & FMOD_3D_HEADRELATIVE) + { + mMode &= ~FMOD_3D_WORLDRELATIVE; + mMode |= FMOD_3D_HEADRELATIVE; + } + else if (mode & FMOD_3D_WORLDRELATIVE) + { + mMode &= ~FMOD_3D_HEADRELATIVE; + mMode |= FMOD_3D_WORLDRELATIVE; + } + + /* + Allow switching between linear/log/custom rolloff. + */ + if (mode & FMOD_3D_LOGROLLOFF) + { + mMode &= ~FMOD_3D_LINEARROLLOFF; + mMode &= ~FMOD_3D_CUSTOMROLLOFF; + mMode |= FMOD_3D_LOGROLLOFF; + } + else if (mode & FMOD_3D_LINEARROLLOFF) + { + mMode &= ~FMOD_3D_LOGROLLOFF; + mMode &= ~FMOD_3D_CUSTOMROLLOFF; + mMode |= FMOD_3D_LINEARROLLOFF; + } + else if (mode & FMOD_3D_CUSTOMROLLOFF) + { + mMode &= ~FMOD_3D_LOGROLLOFF; + mMode &= ~FMOD_3D_LINEARROLLOFF; + mMode |= FMOD_3D_CUSTOMROLLOFF; + } + + if (mode & FMOD_3D_IGNOREGEOMETRY) + { + mMode |= FMOD_3D_IGNOREGEOMETRY; + } + else + { + mMode &= ~FMOD_3D_IGNOREGEOMETRY; + } + + if (mode & FMOD_VIRTUAL_PLAYFROMSTART) + { + mMode |= FMOD_VIRTUAL_PLAYFROMSTART; + } + else + { + mMode &= ~FMOD_VIRTUAL_PLAYFROMSTART; + } + + + /* + Allow switching between 2D and 3D only in software. + */ +#if !defined( PLATFORM_GC ) && !defined( PLATFORM_WII ) && !defined( PLATFORM_PSP ) && !defined( PLATFORM_PS2 ) + if (!(mMode & FMOD_HARDWARE)) +#endif + { + if (mode & FMOD_2D) + { + mMode &= ~FMOD_3D; + mMode |= FMOD_2D; + } + else if (mode & FMOD_3D) + { + mMode &= ~FMOD_2D; + mMode |= FMOD_3D; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getMode(FMOD_MODE *mode) +{ + if (!mode) + { + return FMOD_ERR_INVALID_PARAM; + } + + *mode = mMode; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setLoopCount(int loopcount) +{ + if (loopcount < -1) + { + return FMOD_ERR_INVALID_PARAM; + } + + mLoopCount = loopcount; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getLoopCount(int *loopcount) +{ + if (!loopcount) + { + return FMOD_ERR_INVALID_PARAM; + } + + *loopcount = mLoopCount; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setLoopPoints(unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) +{ + unsigned int loopstartpcm = 0, loopendpcm = 0; + + if ((loopstarttype != FMOD_TIMEUNIT_MS && loopstarttype != FMOD_TIMEUNIT_PCM && loopstarttype != FMOD_TIMEUNIT_PCMBYTES) || + (loopendtype != FMOD_TIMEUNIT_MS && loopendtype != FMOD_TIMEUNIT_PCM && loopendtype != FMOD_TIMEUNIT_PCMBYTES)) + { + return FMOD_ERR_FORMAT; + } + + if (loopstarttype == FMOD_TIMEUNIT_PCM) + { + loopstartpcm = loopstart; + } + else if (loopstarttype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(loopstart, &loopstartpcm, mChannels, mFormat); + } + else if (loopstarttype == FMOD_TIMEUNIT_MS) + { + loopstartpcm = (unsigned int)((float)loopstart / 1000.0f * mDefaultFrequency); + } + + if (loopendtype == FMOD_TIMEUNIT_PCM) + { + loopendpcm = loopend; + } + else if (loopendtype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(loopend, &loopendpcm, mChannels, mFormat); + } + else if (loopendtype == FMOD_TIMEUNIT_MS) + { + loopendpcm = (unsigned int)((float)loopend / 1000.0f * mDefaultFrequency); + } + + if (loopstartpcm >= mLength) + { + loopstartpcm = 0; + } + if (loopendpcm >= mLength) + { + loopendpcm = mLength - 1; + } + + if (!loopendpcm) + { + loopendpcm = mLength - 1; + } + + if (loopstartpcm >= loopendpcm) + { + return FMOD_ERR_INVALID_PARAM; + } + + mLoopStart = loopstartpcm; + mLoopLength = loopendpcm - loopstartpcm + 1; + + if (isStream() && (mLoopStart != 0 || mLoopLength != mLength) && mMode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)) + { + mFlags |= FMOD_SOUND_FLAG_WANTSTOFLUSH; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getLoopPoints(unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) +{ + if ((loopstarttype != FMOD_TIMEUNIT_MS && loopstarttype != FMOD_TIMEUNIT_PCM && loopstarttype != FMOD_TIMEUNIT_PCMBYTES) || + (loopendtype != FMOD_TIMEUNIT_MS && loopendtype != FMOD_TIMEUNIT_PCM && loopendtype != FMOD_TIMEUNIT_PCMBYTES)) + { + return FMOD_ERR_FORMAT; + } + + if (loopstart) + { + if (loopstarttype == FMOD_TIMEUNIT_PCM) + { + *loopstart = mLoopStart; + } + else if (loopstarttype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(mLoopStart, loopstart, mChannels, mFormat); + } + else if (loopstarttype == FMOD_TIMEUNIT_MS) + { + *loopstart = (unsigned int)((float)mLoopStart * 1000.0f / mDefaultFrequency); + } + } + + if (loopend) + { + unsigned int lend = mLoopStart + mLoopLength - 1; + + if (loopendtype == FMOD_TIMEUNIT_PCM) + { + *loopend = lend; + } + else if (loopendtype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(lend, loopend, mChannels, mFormat); + } + else if (loopendtype == FMOD_TIMEUNIT_MS) + { + *loopend = (unsigned int)((float)lend * 1000.0f / mDefaultFrequency); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setPosition(unsigned int pos) +{ + return setPositionInternal(pos); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setPositionInternal(unsigned int pcm) +{ + mPosition = pcm; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getPosition(unsigned int *pcm) +{ + if (!pcm) + { + return FMOD_ERR_INVALID_PARAM; + } + + *pcm = mPosition; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getMusicNumChannels(int *numchannels) +{ + if (mCodec && mCodec->mDescription.getmusicnumchannels) + { + return mCodec->mDescription.getmusicnumchannels(mCodec, numchannels); + } + + return FMOD_ERR_FORMAT; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setMusicChannelVolume(int channel, float volume) +{ + if (mCodec && mCodec->mDescription.setmusicchannelvolume) + { + return mCodec->mDescription.setmusicchannelvolume(mCodec, channel, volume); + } + + return FMOD_ERR_FORMAT; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getMusicChannelVolume(int channel, float *volume) +{ + if (mCodec && mCodec->mDescription.getmusicchannelvolume) + { + return mCodec->mDescription.getmusicchannelvolume(mCodec, channel, volume); + } + + return FMOD_ERR_FORMAT; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::setUserDataInternal(void *userdata) +{ + mUserDataInternal = userdata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getUserDataInternal(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserDataInternal; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::updateSubSound(int index, bool updateinfoonly) +{ + FMOD_RESULT result; + FMOD_CODEC_WAVEFORMAT waveformat; +#ifndef FMOD_STATICFORPLUGINS + LocalCriticalSection crit(mSystem->mStreamUpdateCrit); + bool frommainthread = true; + FMOD_UINT_NATIVE threadid; + + FMOD_OS_Thread_GetCurrentID(&threadid); + if (threadid != mSystem->mMainThreadID) + { + frommainthread = false; + } + + /* + If it is playing, we can't replace it while it is still running. + */ + if (frommainthread && !updateinfoonly) + { + mSubSoundIndex = index; + + mSystem->stopSound(this); + + /* + Just stop only if false, async thread will do stream swap later. + */ + if (mMode & FMOD_NONBLOCKING && isStream()) + { + return FMOD_OK; + } + } + + if (isStream() && (mFlags & FMOD_SOUND_FLAG_PLAYED)) + { + Stream *stream = (Stream *)this; + + if (!(stream->mFlags & FMOD_SOUND_FLAG_FINISHED)) + { + crit.enter(); /* Ok, in async thread, so lets fully block this against the stream thread. */ + } + + stream->mChannel->mFinished = false; + } +#endif + + mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED); + mFlags &= ~FMOD_SOUND_FLAG_PLAYED; + if (mSubSoundParent) + { + mSubSoundParent->mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED); + + mMode &= ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI); + mMode |= (mSubSoundParent->mMode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)); + } + + mSubSoundIndex = index; + + result = mCodec->mDescription.getwaveformat(mCodec, index, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + if (mName) + { + FMOD_strcpy(mName, waveformat.name); + } + mFormat = waveformat.format; + mChannels = waveformat.channels; + mDefaultFrequency = (float)waveformat.frequency; + mDefaultChannelMask = waveformat.channelmask; + mLoopStart = waveformat.loopstart; + mLoopLength = waveformat.loopend - waveformat.loopstart + 1; + mLength = waveformat.lengthpcm; + + setLoopPoints(waveformat.loopstart, FMOD_TIMEUNIT_PCM, waveformat.loopend, FMOD_TIMEUNIT_PCM); + +#ifndef FMOD_STATICFORPLUGINS + if (isStream()) + { + Stream *stream = (Stream *)this; + + // The channel gets the channelmask from mSample, so set it correctly here + stream->mSample->mDefaultChannelMask = waveformat.channelmask; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::syncPointFixIndicies() +{ + int count, numsyncpoints = 0; + FMOD_RESULT result; + + result = getNumSyncPoints(&numsyncpoints); + if (result == FMOD_OK) + { + for (count = 0; count < numsyncpoints; count++) + { + SyncPoint *point; + + result = getSyncPoint(count, (FMOD_SYNCPOINT **)&point); + if (result == FMOD_OK) + { + point->mIndex = count; + } + } + } + + return FMOD_OK; +} + + +/* +[API] +[ + [DESCRIPTION] + Used for 2 purposes.<br> + One use is for user created sounds when FMOD_OPENUSER is specified when creating the sound.<br> + The other use is to 'piggyback' on FMOD's read functions when opening a normal sound, therefore the callee can read (rip) or even write back new PCM data while FMOD is opening the sound. + + [PARAMETERS] + 'sound' Pointer to the sound. C++ users see remarks. + 'data' Pointer to raw PCM data that the user can either read or write to. + 'datalen' Length of the data in bytes. + + [RETURN_VALUE] + + [REMARKS] + <u>C++ Users</u>. Cast <b>FMOD_SOUND *</b> to <b>FMOD::Sound *</b> inside the callback and use as normal.<br> + <br> + The format of the sound can be retrieved with Sound::getFormat from this callback. This will allow the user to determine what type of pointer to use if they are not sure what format the sound is.<br> + If the callback is used for the purpose of 'piggybacking' normal FMOD sound loads, then you do not have to do anything at all, and it can be treated as purely informational. The return value is also ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + Sound::getFormat + FMOD_SOUND_PCMSETPOSCALLBACK + System::createSound + System::createStream + FMOD_CREATESOUNDEXINFO +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_SOUND_PCMREADCALLBACK(FMOD_SOUND *sound, void *data, unsigned int datalen) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Callback for when the caller calls a seeking function such as Channel::setTime or Channel::setPosition.<br> + If the sound is a user created sound, this can be used to seek within the user's resource.<br> + + [PARAMETERS] + 'sound' Pointer to the sound. C++ users see remarks. + 'subsound' In a multi subsound type sound (ie fsb/dls/cdda), this will contain the index into the list of sounds. + 'position' Position to seek to that has been requested. This value will be of format FMOD_TIMEUNIT and must be parsed to determine what it is. Generally FMOD_TIMEUNIT_PCM will be the most common format. + 'postype' Position type that the user wanted to seek with. If the sound is a user create sound and the seek type is unsupported return FMOD_ERR_FORMAT. + + [RETURN_VALUE] + + [REMARKS] + <u>C++ Users</u>. Cast <b>FMOD_SOUND *</b> to <b>FMOD::Sound *</b> inside the callback and use as normal.<br> + <br> + If the callback is used for the purpose of 'piggybacking' normal FMOD sound loads, then you do not have to do anything at all, and it can be treated as purely informational. The return value is also ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + FMOD_SOUND_PCMREADCALLBACK + System::createSound + System::createStream + FMOD_CREATESOUNDEXINFO +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_SOUND_PCMSETPOSCALLBACK(FMOD_SOUND *sound, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[API] +[ + [DESCRIPTION] + Callback to be called when a sound has finished loading. + + [PARAMETERS] + 'sound' Pointer to the sound. C++ users see remarks. + 'result' Error code. FMOD_OK if sound was created successfully, or an error code otherwise. + + [RETURN_VALUE] + + [REMARKS] + <u>C++ Users</u>. Cast <b>FMOD_SOUND *</b> to <b>FMOD::Sound *</b> inside the callback and use as normal.<br> + <br> + Return code currently ignored. + + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation 2, PlayStation Portable, PlayStation 3, Wii, Solaris, iPhone + + [SEE_ALSO] + System::createSound + FMOD_CREATESOUNDEXINFO +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_SOUND_NONBLOCKCALLBACK(FMOD_SOUND *sound, FMOD_RESULT result) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SoundI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT SoundI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(SoundI)); + + if (mName) + { + tracker->add(false, FMOD_MEMBITS_STRING, FMOD_STRING_MAXNAMELEN); + } + + if (mSyncPointHead) + { + SyncPoint *point = SAFE_CAST(SyncPoint, mSyncPointHead->getNext()); + + while (point != mSyncPointTail) + { + if (point->mName) + { + tracker->add(false, FMOD_MEMBITS_SYNCPOINT, sizeof(SyncPointNamed)); + } + else + { + tracker->add(false, FMOD_MEMBITS_SYNCPOINT, sizeof(SyncPoint)); + } + + point = SAFE_CAST(SyncPoint, point->getNext()); + } + + tracker->add(false, FMOD_MEMBITS_SYNCPOINT, sizeof(SyncPoint) * 2); /* mSyncPointHead & mSyncPointTail */ + } + +#ifdef FMOD_SUPPORT_STREAMING + if (isStream() && mCodec && (!mSubSoundParent || mSubSoundParent == this || (mSubSoundParent && mCodec != mSubSoundParent->mCodec))) + { + Stream *stream = SAFE_CAST(Stream, this); + + if (mCodec) + { + CHECK_RESULT(mCodec->getMemoryUsed(tracker)); + } + } + else +#endif + { + if (mCodec && ((mSubSoundParent && mCodec != mSubSoundParent->mCodec) || !mSubSoundParent)) + { + CHECK_RESULT(mCodec->getMemoryUsed(tracker)); + } + } + + /* + Remove all subsounds of this sound if they exist! + */ + if (mNumSubSounds && mSubSound) + { + int count; + + if (mNumActiveSubSounds) + { + for (count = 0; count < mNumSubSounds; count++) + { + if (mSubSound[count] && mSubSound[count] != mSubSoundShared) + { + mSubSound[count]->getMemoryUsed(tracker); + } + } + } + + tracker->add(false, FMOD_MEMBITS_SOUND, mNumSubSounds * sizeof(SoundI *)); /* mSubSound */ + + if (mSubSoundShared) + { + mSubSoundShared->getMemoryUsed(tracker); + } + } + + if (mAsyncData) + { + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(mAsyncData)); + } + +#ifdef FMOD_SUPPORT_SENTENCING + if (mSubSoundList) + { + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(SoundSentenceEntry) * mSubSoundListNum); + } +#endif + + return FMOD_OK; +} + +#endif + +#endif + +} diff --git a/src/fmod_soundi.h b/src/fmod_soundi.h new file mode 100755 index 0000000..5722106 --- /dev/null +++ b/src/fmod_soundi.h @@ -0,0 +1,322 @@ +#ifndef _FMOD_SOUNDI_H +#define _FMOD_SOUNDI_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_channeli.h" +#include "fmod_linkedlist.h" +#include "fmod_memory.h" +#include "fmod_string.h" +#include "fmod_syncpoint.h" +#include "fmod_types.h" +#include "fmod_codeci.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + const int DEFAULT_FREQUENCY = 44100; + + class Sample; + class DSPI; + class SystemI; + class SoundGroupI; + struct AsyncData; + + const unsigned int SOUND_READCHUNKSIZE = (16*1024); + + typedef unsigned int FMOD_SOUND_FLAG; + + #define FMOD_SOUND_FLAG_DONOTRELEASE 0x00000001 + #define FMOD_SOUND_FLAG_PLAYED 0x00000004 + #define FMOD_SOUND_FLAG_FINISHED 0x00000008 + #define FMOD_SOUND_FLAG_WANTSTOFLUSH 0x00000010 + #define FMOD_SOUND_FLAG_RELEASING 0x00000020 + #define FMOD_SOUND_FLAG_THREADFINISHED 0x00000040 + #define FMOD_SOUND_FLAG_FULLYBUFFERED 0x00000080 + #define FMOD_SOUND_FLAG_PRELOADEDFSB 0x00000100 + #define FMOD_SOUND_FLAG_PROGRAMMERSOUND 0x00000200 + #define FMOD_SOUND_FLAG_SETPOS_SAFE 0x00000400 + + #define FMOD_PS3_SUBSOUNDLISTMAXITEMS 512 // 4k max for PS3 + + struct SoundSentenceEntry + { + int mIndex; + unsigned int mLength; + }; + + class SoundI : public LinkedListNode + { + DECLARE_MEMORYTRACKER + + public: + + FMOD_SOUND_TYPE mType; + FMOD_SOUND_FORMAT mFormat; + FMOD_MODE mMode; + char *mName; + unsigned int mPosition; + unsigned int mLength; + unsigned int mLengthBytes; + unsigned int mLoopStart; + unsigned int mLoopLength; + int mLoopCount; + FMOD_SOUND_FLAG mFlags; + + Codec *mCodec; + int mChannels; + float mDefaultVolume; + float mDefaultFrequency; + float mDefaultPan; + int mDefaultPriority; + unsigned int mDefaultChannelMask; + float mFrequencyVariation; + float mVolumeVariation; + float mPanVariation; + + /* + 3D Stuff + */ + float mMinDistance; + float mMaxDistance; + float mConeInsideAngle; + float mConeOutsideAngle; + float mConeOutsideVolume; + FMOD_VECTOR *mRolloffPoint; + int mNumRolloffPoints; + + /* + SubSound stuff + */ + SoundI **mSubSound; /* Array of pointers to subsounds. */ + SoundI *mSubSoundShared; + int mNumSubSounds; + int mNumActiveSubSounds; + SoundI *mSubSoundParent; + int mSubSoundIndex; +#ifdef FMOD_SUPPORT_SENTENCING + SoundSentenceEntry *mSubSoundList; + int mSubSoundListNum; +#endif + + SoundI *mSubSampleParent; /* Pointer to parent subsample if multisample sound. */ + int mNumSubSamples; + SoundI *mSubSample[FMOD_CHANNEL_MAXREALSUBCHANNELS]; + bool mLockCanRead; + + void *mUserData; + void *mUserDataInternal; + SystemI *mSystem; + unsigned int mMemoryUsed; + + int mNumSyncPoints; + SyncPoint *mSyncPointHead, *mSyncPointTail; + SyncPoint *mSyncPointMemory; + + AsyncData *mAsyncData; /* Data for async loading. */ + FMOD_OPENSTATE mOpenState; + SoundGroupI *mSoundGroup; + LinkedListNode mSoundGroupNode; + int mNumAudible; + FMOD_UINT64 mLastAudibleDSPClock; + + FMOD_SOUND_PCMREADCALLBACK mPostReadCallback; + FMOD_SOUND_PCMSETPOSCALLBACK mPostSetPositionCallback; + FMOD_SOUND *mPostCallbackSound; + + FMOD_RESULT loadSubSound(int index, FMOD_MODE mode); + FMOD_RESULT read(unsigned int offset, unsigned int numsamples, unsigned int *read); + FMOD_RESULT seek(int subsound, unsigned int position); + FMOD_RESULT clear(unsigned int offset, unsigned int numsamples); + FMOD_RESULT setPositionInternal(unsigned int pcm); + FMOD_RESULT updateSubSound(int index, bool updateinfoonly); + FMOD_RESULT F_API setUserDataInternal(void *userdata); + FMOD_RESULT F_API getUserDataInternal(void **userdata); + FMOD_RESULT syncPointFixIndicies(); + FMOD_RESULT deleteSyncPointInternal(FMOD_SYNCPOINT *point, bool ignoresubsoundindices); + FMOD_RESULT setSubSoundInternal(int index, SoundI *subsound, bool calledfromrelease); + + + static FMOD_RESULT validate(Sound *sound, SoundI **soundi); + + FMOD_INLINE FMOD_RESULT getBytesFromSamples(unsigned int samples, unsigned int *bytes, bool roundup = true) + { + return getBytesFromSamples(samples, bytes, mChannels, mFormat, roundup); + } + static FMOD_INLINE FMOD_RESULT getBytesFromSamples(unsigned int samples, unsigned int *bytes, int channels, FMOD_SOUND_FORMAT format, bool roundup = true) + { + int bits = 0; + + getBitsFromFormat(format, &bits); + + if (bits) + { + *bytes = (unsigned int)((FMOD_UINT64)samples * (FMOD_UINT64)bits / 8); + } + else + { + switch (format) + { + case FMOD_SOUND_FORMAT_GCADPCM : { *bytes = ((samples + (roundup ? 13 : 0)) / 14) * 8; break; } + case FMOD_SOUND_FORMAT_IMAADPCM : { *bytes = ((samples + (roundup ? 63 : 0)) / 64) * 36; break; } + case FMOD_SOUND_FORMAT_VAG : { *bytes = ((samples + (roundup ? 27 : 0)) / 28) * 16; break; } + case FMOD_SOUND_FORMAT_XMA : { *bytes = samples; return FMOD_OK; } + case FMOD_SOUND_FORMAT_MPEG : { *bytes = samples; return FMOD_OK; } + case FMOD_SOUND_FORMAT_CELT : { *bytes = samples; return FMOD_OK; } + case FMOD_SOUND_FORMAT_NONE : { *bytes = 0; break; } + default : return FMOD_ERR_FORMAT; + } + } + + *bytes *= channels; + + return FMOD_OK; + } + + FMOD_INLINE FMOD_RESULT getSamplesFromBytes(unsigned int bytes, unsigned int *samples) + { + return getSamplesFromBytes(bytes, samples, mChannels, mFormat); + } + static FMOD_INLINE FMOD_RESULT getSamplesFromBytes(unsigned int bytes, unsigned int *samples, int channels, FMOD_SOUND_FORMAT format) + { + int bits = 0; + + if (!channels) + { + return FMOD_ERR_INVALID_PARAM; + } + + getBitsFromFormat(format, &bits); + + if (bits) + { + *samples = (unsigned int)((FMOD_UINT64)bytes * (FMOD_UINT64)8 / (FMOD_UINT64)bits); + } + else + { + switch (format) + { + case FMOD_SOUND_FORMAT_GCADPCM : { *samples = bytes * 14 / 8; break; } + case FMOD_SOUND_FORMAT_IMAADPCM : { *samples = bytes * 64 / 36; break; } + case FMOD_SOUND_FORMAT_VAG : { *samples = bytes * 28 / 16; break; } + case FMOD_SOUND_FORMAT_XMA : { *samples = bytes; return FMOD_OK; } + case FMOD_SOUND_FORMAT_MPEG : { *samples = bytes; return FMOD_OK; } + case FMOD_SOUND_FORMAT_CELT : { *samples = bytes; return FMOD_OK; } + case FMOD_SOUND_FORMAT_NONE : { *samples = 0; break; } + default : return FMOD_ERR_FORMAT; + } + } + + *samples /= channels; + + return FMOD_OK; + } + + FMOD_INLINE FMOD_RESULT getBitsFromFormat(int *bits) + { + return getBitsFromFormat(mFormat, bits); + } + static FMOD_INLINE FMOD_RESULT getBitsFromFormat(FMOD_SOUND_FORMAT format, int *bits) + { + switch (format) + { + case FMOD_SOUND_FORMAT_PCM8 : { *bits = 8; break; } + case FMOD_SOUND_FORMAT_PCM16 : { *bits = 16; break; } + case FMOD_SOUND_FORMAT_PCM24 : { *bits = 24; break; } + case FMOD_SOUND_FORMAT_PCM32 : { *bits = 32; break; } + case FMOD_SOUND_FORMAT_PCMFLOAT : { *bits = 32; break; } + case FMOD_SOUND_FORMAT_GCADPCM : { *bits = 0; break; } + case FMOD_SOUND_FORMAT_IMAADPCM : { *bits = 0; break; } + case FMOD_SOUND_FORMAT_XMA : { *bits = 0; break; } + case FMOD_SOUND_FORMAT_VAG : { *bits = 0; break; } + case FMOD_SOUND_FORMAT_MPEG : { *bits = 0; break; } + case FMOD_SOUND_FORMAT_CELT : { *bits = 0; break; } + case FMOD_SOUND_FORMAT_NONE : { *bits = 0; break; } + default : return FMOD_ERR_FORMAT; + } + return FMOD_OK; + } + static FMOD_INLINE FMOD_RESULT getFormatFromBits(int bits, FMOD_SOUND_FORMAT *format) + { + switch (bits) + { + case 8 : { *format = FMOD_SOUND_FORMAT_PCM8; break; } + case 16 : { *format = FMOD_SOUND_FORMAT_PCM16; break; } + case 24 : { *format = FMOD_SOUND_FORMAT_PCM24; break; } + case 32 : { *format = FMOD_SOUND_FORMAT_PCM32; break; } + default : return FMOD_ERR_FORMAT; + } + return FMOD_OK; + } + virtual bool isStream() { return false; } + + virtual FMOD_RESULT setPosition (unsigned int pcm); + virtual FMOD_RESULT getPosition (unsigned int *pcm); + + public: + + SoundI(); + + virtual FMOD_RESULT release (bool freethis = true); + virtual FMOD_RESULT getSystemObject (System **system); + virtual FMOD_RESULT lock (unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + virtual FMOD_RESULT unlock (void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + virtual FMOD_RESULT setDefaults (float frequency, float volume, float pan, int priority); + virtual FMOD_RESULT getDefaults (float *frequency, float *volume, float *pan, int *priority); + virtual FMOD_RESULT setVariations (float frequencyvar, float volumevar, float panvar); + virtual FMOD_RESULT getVariations (float *frequencyvar, float *volumevar, float *panvar); + virtual FMOD_RESULT set3DMinMaxDistance (float min, float max); + virtual FMOD_RESULT get3DMinMaxDistance (float *min, float *max); + virtual FMOD_RESULT set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume); + virtual FMOD_RESULT get3DConeSettings (float *insideconeangle, float *outsideconeangle, float *outsidevolume); + virtual FMOD_RESULT set3DCustomRolloff (FMOD_VECTOR *points, int numpoints); + virtual FMOD_RESULT get3DCustomRolloff (FMOD_VECTOR **points, int *numpoints); + virtual FMOD_RESULT setSubSound (int index, SoundI *subsound); + virtual FMOD_RESULT getSubSound (int index, SoundI **subsound); + virtual FMOD_RESULT setSubSoundSentence (int *subsoundlist, int numsubsounds); + + virtual FMOD_RESULT getName (char *name, int namelen); + virtual FMOD_RESULT getLength (unsigned int *length, FMOD_TIMEUNIT lengthtype); + virtual FMOD_RESULT getFormat (FMOD_SOUND_TYPE *type, FMOD_SOUND_FORMAT *format, int *channels, int *bits); + virtual FMOD_RESULT getNumSubSounds (int *numsubsounds); + virtual FMOD_RESULT getNumTags (int *numtags, int *numtagsupdated); + virtual FMOD_RESULT getTag (const char *name, int index, FMOD_TAG *tag); + virtual FMOD_RESULT getOpenState (FMOD_OPENSTATE *openstate, unsigned int *percentbuffered, bool *starving); + virtual FMOD_RESULT readData (void *buffer, unsigned int lenbytes, unsigned int *read); + virtual FMOD_RESULT seekData (unsigned int pcm); + + virtual FMOD_RESULT setSoundGroup (SoundGroupI *soundgroup); + virtual FMOD_RESULT getSoundGroup (SoundGroupI **soundgroup); + + virtual FMOD_RESULT getNumSyncPoints (int *numsyncpoints); + virtual FMOD_RESULT getSyncPoint (int index, FMOD_SYNCPOINT **point); + virtual FMOD_RESULT getSyncPointInfo (FMOD_SYNCPOINT *point, char *name, int namelen, unsigned int *offset, FMOD_TIMEUNIT offsettype); + virtual FMOD_RESULT addSyncPoint (unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **point, int subsound = -1, bool fixupindicies = true); + virtual FMOD_RESULT addSyncPointInternal (unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **point, int subsound = -1, bool fixupindicies = true); + virtual FMOD_RESULT deleteSyncPoint (FMOD_SYNCPOINT *point); + + virtual FMOD_RESULT setMode (FMOD_MODE mode); + virtual FMOD_RESULT getMode (FMOD_MODE *mode); + virtual FMOD_RESULT setLoopCount (int loopcount); + virtual FMOD_RESULT getLoopCount (int *loopcount); + virtual FMOD_RESULT setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype); + virtual FMOD_RESULT getLoopPoints (unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype); + + virtual FMOD_RESULT getMusicNumChannels (int *numchannels); + virtual FMOD_RESULT setMusicChannelVolume(int channel, float volume); + virtual FMOD_RESULT getMusicChannelVolume(int channel, float *volume); + + virtual FMOD_RESULT setUserData (void *userdata); + virtual FMOD_RESULT getUserData (void **userdata); + + virtual FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + }; +} + +#endif + + diff --git a/src/fmod_speakerlevels_pool.cpp b/src/fmod_speakerlevels_pool.cpp new file mode 100755 index 0000000..c91e640 --- /dev/null +++ b/src/fmod_speakerlevels_pool.cpp @@ -0,0 +1,226 @@ +#include "fmod_settings.h" + +#include "fmod_speakerlevels_pool.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +SpeakerLevelsPool::SpeakerLevelsPool() +{ + mLevelsPool = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SpeakerLevelsPool::alloc(float **levels) +{ + int freepos = mSystem->mNumChannels; + int numoutputchannels = mSystem->mMaxOutputChannels; + + if (!mLevelsPool) + { + mLevelsPool = (LevelsInfo *)FMOD_Memory_CallocType(sizeof(LevelsInfo) * mSystem->mNumChannels, FMOD_MEMORY_PERSISTENT); + if (!mLevelsPool) + { + return FMOD_ERR_MEMORY; + } + } + + /* + If it is PROLOGIC, we need enough room for 6 speakers, even though mMaxOutputChannels is 2 + */ + if (mSystem->mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC) + { + numoutputchannels = 6; + } + + /* + Search for already alloced mLevels, but not in use + */ + for (int i = 0; i < mSystem->mNumChannels; i++) + { + if(!mLevelsPool[i].inuse && mLevelsPool[i].levelsmemory) + { + /* + Use this + */ + FMOD_memset(mLevelsPool[i].levelsmemory, 0, sizeof(float) * numoutputchannels * mSystem->mMaxInputChannels); + + mLevelsPool[i].inuse = true; + *levels = mLevelsPool[i].levelsmemory; + + return FMOD_OK; + } + + if(!mLevelsPool[i].levelsmemory) + { + if (i < freepos) + { + freepos = i; + } + } + } + + /* + Didn't find anything, alloc a new one + */ + mLevelsPool[freepos].levelsmemory = (float *)FMOD_Memory_CallocType(sizeof(float) * numoutputchannels * mSystem->mMaxInputChannels, FMOD_MEMORY_PERSISTENT); + if (!mLevelsPool[freepos].levelsmemory) + { + return FMOD_ERR_MEMORY; + } + + mLevelsPool[freepos].inuse = true; + *levels = mLevelsPool[freepos].levelsmemory; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SpeakerLevelsPool::free(float *levels) +{ + if (mLevelsPool) + { + for (int i = 0; i < mSystem->mNumChannels; i++) + { + if (mLevelsPool[i].levelsmemory == levels) + { + mLevelsPool[i].inuse = false; + break; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SpeakerLevelsPool::release() +{ + if (mLevelsPool) + { + for (int i = 0; i < mSystem->mNumChannels; i++) + { + if (mLevelsPool[i].levelsmemory) + { + FMOD_Memory_Free(mLevelsPool[i].levelsmemory); + mLevelsPool[i].levelsmemory = NULL; + } + } + + FMOD_Memory_Free(mLevelsPool); + mLevelsPool = NULL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT SpeakerLevelsPool::getMemoryUsedImpl(MemoryTracker *tracker) +{ + if (mLevelsPool) + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(LevelsInfo) * mSystem->mNumChannels); + + int numoutputchannels = mSystem->mMaxOutputChannels; + + /* + If it is PROLOGIC, we need enough room for 6 speakers, even though mMaxOutputChannels is 2 + */ + if (mSystem->mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC) + { + numoutputchannels = 6; + } + + for (int i = 0; i < mSystem->mNumChannels; i++) + { + if (mLevelsPool[i].levelsmemory) + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(float) * numoutputchannels * mSystem->mMaxInputChannels); + } + } + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_speakerlevels_pool.h b/src/fmod_speakerlevels_pool.h new file mode 100755 index 0000000..04bb7d3 --- /dev/null +++ b/src/fmod_speakerlevels_pool.h @@ -0,0 +1,43 @@ +#ifndef _FMOD_SPEAKERLEVELS_POOL_H +#define _FMOD_SPEAKERLEVELS_POOL_H + +#include "fmod_settings.h" + +#include "fmod_memory.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + typedef struct LevelsInfo + { + bool inuse; + float *levelsmemory; + + } LevelsInfo; + + + class SpeakerLevelsPool + { + DECLARE_MEMORYTRACKER + + friend class SystemI; + + private: + LevelsInfo *mLevelsPool; + SystemI *mSystem; + + public: + + SpeakerLevelsPool(); + + FMOD_RESULT alloc(float **levels); + FMOD_RESULT free (float *levels); + FMOD_RESULT release(); + }; +} + +#endif + diff --git a/src/fmod_speakermap.h b/src/fmod_speakermap.h new file mode 100755 index 0000000..e0a7750 --- /dev/null +++ b/src/fmod_speakermap.h @@ -0,0 +1,71 @@ +#ifndef _FMOD_SPEAKERMAP_H +#define _FMOD_SPEAKERMAP_H + +#include "fmod_settings.h" + +#undef SPEAKER_RESERVED /* mmreg.h! */ +#undef SPEAKER_FRONT_LEFT +#undef SPEAKER_FRONT_RIGHT +#undef SPEAKER_FRONT_CENTER +#undef SPEAKER_LOW_FREQUENCY +#undef SPEAKER_BACK_LEFT +#undef SPEAKER_BACK_RIGHT +#undef SPEAKER_FRONT_LEFT_OF_CENTER +#undef SPEAKER_FRONT_RIGHT_OF_CENTER +#undef SPEAKER_BACK_CENTER +#undef SPEAKER_SIDE_LEFT +#undef SPEAKER_SIDE_RIGHT +#undef SPEAKER_TOP_CENTER +#undef SPEAKER_TOP_FRONT_LEFT +#undef SPEAKER_TOP_FRONT_CENTER +#undef SPEAKER_TOP_FRONT_RIGHT +#undef SPEAKER_TOP_BACK_LEFT +#undef SPEAKER_TOP_BACK_CENTER +#undef SPEAKER_TOP_BACK_RIGHT +#undef SPEAKER_WAVEFORMAT_MASK +#undef SPEAKER_ALLMONO +#undef SPEAKER_ALLSTEREO +#undef SPEAKER_RESERVED +#undef SPEAKER_DIRECTOUT +#undef SPEAKER_MONO +#undef SPEAKER_STEREO +#undef SPEAKER_QUAD +#undef SPEAKER_SURROUND +#undef SPEAKER_5POINT1 +#undef SPEAKER_7POINT1 + +#define SPEAKER_FRONT_LEFT 0x00000001 +#define SPEAKER_FRONT_RIGHT 0x00000002 +#define SPEAKER_FRONT_CENTER 0x00000004 +#define SPEAKER_LOW_FREQUENCY 0x00000008 +#define SPEAKER_BACK_LEFT 0x00000010 +#define SPEAKER_BACK_RIGHT 0x00000020 +#define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040 +#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080 +#define SPEAKER_BACK_CENTER 0x00000100 +#define SPEAKER_SIDE_LEFT 0x00000200 +#define SPEAKER_SIDE_RIGHT 0x00000400 +#define SPEAKER_TOP_CENTER 0x00000800 +#define SPEAKER_TOP_FRONT_LEFT 0x00001000 +#define SPEAKER_TOP_FRONT_CENTER 0x00002000 +#define SPEAKER_TOP_FRONT_RIGHT 0x00004000 +#define SPEAKER_TOP_BACK_LEFT 0x00008000 +#define SPEAKER_TOP_BACK_CENTER 0x00010000 +#define SPEAKER_TOP_BACK_RIGHT 0x00020000 +#define SPEAKER_WAVEFORMAT_MASK 0x000FFFFF + +#define SPEAKER_ALLMONO 0x10000000 +#define SPEAKER_ALLSTEREO 0x20000000 +#define SPEAKER_PROTOOLS 0x40000000 + +#define SPEAKER_RESERVED 0x80000000 + +#define SPEAKER_DIRECTOUT 0 +#define SPEAKER_MONO (SPEAKER_FRONT_CENTER) +#define SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) +#define SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +#define SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +#define SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +#define SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) + +#endif diff --git a/src/fmod_stack.cpp b/src/fmod_stack.cpp new file mode 100755 index 0000000..c2c7a1a --- /dev/null +++ b/src/fmod_stack.cpp @@ -0,0 +1,127 @@ +#include "fmod_settings.h" + +#include "fmod_stack.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + PS3 + + [SEE_ALSO] +] +*/ +Stack::Stack() +{ + mTop = 0; + mNext = 0; + mPrevious = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + PS3 + + [SEE_ALSO] +] +*/ +void Stack::push(Stack *object) +{ + if (!mTop) + { + /* + We have just pushed on the first object + */ + + mTop = object; + + return; + } + + mTop->mNext = object; + object->mPrevious = mTop; + mTop = object; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + PS3 + + [SEE_ALSO] +] +*/ +Stack *Stack::pop() +{ + Stack *popped; + + if (!mTop) + { + return 0; + } + + popped = mTop; + + mTop = mTop->mPrevious; + + return popped; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + PS3 + + [SEE_ALSO] +] +*/ +bool Stack::stackEmpty() +{ + if (mTop) + { + return false; + } + + return true; +} + +} diff --git a/src/fmod_stack.h b/src/fmod_stack.h new file mode 100755 index 0000000..fc00131 --- /dev/null +++ b/src/fmod_stack.h @@ -0,0 +1,26 @@ +#ifndef _FMOD_STACK_H +#define _FMOD_STACK_H + +namespace FMOD +{ + class Stack + { + private: + + Stack *mTop; + Stack *mNext; + Stack *mPrevious; + + public: + + Stack(); + + void push(Stack *object); + Stack *pop(); + bool stackEmpty(); + + }; +} + + +#endif diff --git a/src/fmod_string.cpp b/src/fmod_string.cpp new file mode 100755 index 0000000..2f09f60 --- /dev/null +++ b/src/fmod_string.cpp @@ -0,0 +1,338 @@ +#include <stdarg.h> +#include <stdio.h> + +#include "fmod_string.h" +#include "fmod_memory.h" + +using namespace FMOD; + +int FMOD_strlen(const char * s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) /* nothing */; + + return (int)(sc - s); +} + +char * FMOD_strcpy(char * dest,const char *src) +{ + char *tmp = dest; + + while ((*dest++ = *src++) != '\0') /* nothing */; + + return tmp; +} + +char * FMOD_strncpy(char * dest, const char *src, int count) +{ + char *tmp = dest; + + while (count-- && (*dest++ = *src++) != '\0') /* nothing */; + + return tmp; +} + + +char * FMOD_strcat(char * dest, const char * src) +{ + char *tmp = dest; + + while (*dest) dest++; + while ((*dest++ = *src++) != '\0') ; + + return tmp; +} + +char * FMOD_strncat(char *dest, const char *src, int count) +{ + char *tmp = dest; + + if (count) + { + while (*dest) dest++; + + while ((*dest++ = *src++)) + { + if (--count == 0) + { + *dest = '\0'; + break; + } + } + } + + return tmp; +} + +char FMOD_tolower(char in) +{ + if (in >= 'A' && in <= 'Z') + { + in += 'a' - 'A'; + } + + return in; +} + +char *FMOD_strupr(char *string) +{ + char * cp; + + for (cp=string; *cp; ++cp) + { + if (*cp >= 'a' && *cp <= 'z') + { + *cp += 'A' - 'a'; + } + } + + return(string); +} + +char *FMOD_strlwr(char *string) +{ + char * cp; + + for (cp=string; *cp; ++cp) + { + if (*cp >= 'A' && *cp <= 'Z') + { + *cp += 'a' - 'A'; + } + } + + return(string); +} + +int FMOD_strcmp( const char *string1, const char *string2 ) +{ + register char c1, c2; + do + { + c1 = *string1++; + c2 = *string2++; + } while ( c1 && (c1 == c2) ); + + return c1 - c2; +} + + +int FMOD_strncmp( const char *string1, const char *string2, int len ) +{ + register char c1, c2; + int count=0; + do + { + c1 = *string1++; + c2 = *string2++; + count++; + } while ( c1 && (c1 == c2) && count < len); + + return c1 - c2; +} + + +int FMOD_stricmp( const char *string1, const char *string2 ) +{ + register char c1, c2; + do + { + c1 = FMOD_tolower(*string1++); + c2 = FMOD_tolower(*string2++); + } while ( c1 && (c1 == c2) ); + + return c1 - c2; +} + +int FMOD_strnicmp( const char *string1, const char *string2, int len ) +{ + register char c1, c2; + int count=0; + do + { + c1 = FMOD_tolower(*string1++); + c2 = FMOD_tolower(*string2++); + count++; + } while ( c1 && (c1 == c2) && count < len); + + return c1 - c2; +} + +char * FMOD_strchr(const char * s1, int c) +{ + int l1; + + l1 = FMOD_strlen(s1); + + while (l1) + { + l1--; + + if (*s1 == c) return (char *) s1; + + s1++; + } + return (char *)0; +} + +char * FMOD_strstr(const char * s1,const char * s2) +{ + int l1, l2; + + l2 = FMOD_strlen(s2); + + if (!l2) + { + return (char *) s1; + } + + l1 = FMOD_strlen(s1); + + while (l1 >= l2) + { + l1--; + + if (!FMOD_memcmp(s1,s2,l2)) return (char *) s1; + + s1++; + } + return (char *)0; +} + + +int FMOD_memcmp(const void * cs,const void * ct, int count) +{ + const unsigned char *su1, *su2; + signed char res = 0; + + for( su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + + return res; +} + + +void * FMOD_memmove(void * dest,const void *src, int count) +{ + char *tmp, *s; + + if (dest <= src) + { + tmp = (char *) dest; + s = (char *) src; + while (count--) *tmp++ = *s++; + } + else + { + tmp = (char *) dest + count; + s = (char *) src + count; + while (count--) *--tmp = *--s; + } + + return dest; +} + + +char *FMOD_strdup(const char * src) +{ + char *ret = 0; + + ret = (char *)FMOD_Memory_Alloc(FMOD_strlen(src) + 1); + if (ret) + { + FMOD_strcpy(ret, src); + } + + return ret; +} + + +char *FMOD_eatwhite(const char *string) +{ + for (;*string && FMOD_isspace(*string);string++) ; + + return (char *)string; +} + + +/* + AJS This only handles decimal integers. Feel free to extend it if you need to +*/ +int FMOD_atoi(const char *s) +{ + if (!s) + { + return 0; + } + + int val = 0; + const char *p = s; + + for (; *p; p++) ; + + p--; + for (int i=1; p >= s; p--, i *= 10) + { + val += ((*p - '0') * i); + } + + return val; +} + +int FMOD_atoi_hex(const char *s) +{ + if (!s) + { + return 0; + } + + int val = 0; + + for(const char *p = s; *p; p++) + { + val *= 16; + + if(*p >= '0' && *p <= '9') + { + val += (*p - '0'); + } + else if(*p >= 'A' && *p <= 'F') + { + val += (*p - 'A' + 10); + } + else if(*p >= 'a' && *p <= 'f') + { + val += (*p - 'a' + 10); + } + } + + return val; +} + +int FMOD_snprintf(char *str, int size, const char *format, ...) +{ + va_list arglist; + + va_start(arglist, format); + + int result = FMOD_vsnprintf(str, size, format, arglist); + + va_end(arglist); + + return result; +} + +int FMOD_vsnprintf(char *str, int size, const char *format, va_list arglist) +{ +#ifdef PLATFORM_PS2_IOP + int result = vsprintf(str, format, arglist); +#else + int result = vsnprintf(str, size, format, arglist); +#endif + + str[size - 1] = 0; + + return result; +} diff --git a/src/fmod_string.h b/src/fmod_string.h new file mode 100755 index 0000000..e00a4ce --- /dev/null +++ b/src/fmod_string.h @@ -0,0 +1,48 @@ +#ifndef _SYSTEM_STRING_H +#define _SYSTEM_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> + +int FMOD_strlen(const char * s); +char * FMOD_strcpy(char * dest,const char *src); +char * FMOD_strncpy(char * dest, const char *src, int count); +char * FMOD_strcat(char * dest, const char * src); +char * FMOD_strncat(char *dest, const char *src, int count); +char FMOD_tolower(char in); +char * FMOD_strupr(char *string); +char * FMOD_strlwr(char *string); +int FMOD_strcmp( const char *string1, const char *string2 ); +int FMOD_strncmp( const char *string1, const char *string2, int len); +int FMOD_stricmp( const char *string1, const char *string2); +int FMOD_strnicmp( const char *string1, const char *string2, int len); +char * FMOD_strchr(const char * string, int c); +char * FMOD_strstr(const char * s1,const char * s2); +void * FMOD_memmove(void * dest,const void *src, int count); +int FMOD_memcmp(const void * cs,const void * ct, int count); + +char * FMOD_strdup(const char * src); +char * FMOD_eatwhite(const char *string); +int FMOD_atoi(const char *s); +int FMOD_atoi_hex(const char *s); + +int FMOD_snprintf(char *str, int size, const char *format, ...); +int FMOD_vsnprintf(char *str, int size, const char *format, va_list arglist); + +#define FMOD_isspace(j) (j==' ' || j=='\t' || j=='\n') +#define FMOD_isdigit(j) (j>='0' && j<='9') +#define FMOD_ishexdigit(j) (((j) >= '0' && (j) <= '9') || ((j) >= 'A' && (j) <= 'F') || ((j) >= 'a' && (j) <= 'f')) +#define FMOD_isalpha(j) ((((j) >= 'A') && ((j) <= 'Z')) || (((j) >= 'a') && ((j) <= 'z'))) + +#define FMOD_STRING_MAXPATHLEN 256 +#define FMOD_STRING_MAXNAMELEN 256 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fmod_stringw.cpp b/src/fmod_stringw.cpp new file mode 100755 index 0000000..f0bafdc --- /dev/null +++ b/src/fmod_stringw.cpp @@ -0,0 +1,249 @@ +#include "fmod_string.h" +#include "fmod_stringw.h" +#include "fmod_memory.h" + +int FMOD_strlenW(const short * s) +{ + const short *sc; + + for (sc = s; *sc != '\0'; ++sc) /* nothing */; + + return (int)(sc - s); +} + +short * FMOD_strcpyW(short * dest,const short *src) +{ + short *tmp = dest; + + while ((*dest++ = *src++) != '\0') /* nothing */; + + return tmp; +} + +short * FMOD_strncpyW(short * dest, const short *src, int count) +{ + short *tmp = dest; + + while (count-- && (*dest++ = *src++) != '\0') /* nothing */; + + return tmp; +} + + +short * FMOD_strcatW(short * dest, const short * src) +{ + short *tmp = dest; + + while (*dest) dest++; + while ((*dest++ = *src++) != '\0') ; + + return tmp; +} + +short * FMOD_strncatW(short *dest, const short *src, int count) +{ + short *tmp = dest; + + if (count) + { + while (*dest) dest++; + + while ((*dest++ = *src++)) + { + if (--count == 0) + { + *dest = '\0'; + break; + } + } + } + + return tmp; +} + +short FMOD_tolowerW(short in) +{ + if (in >= 'A' && in <= 'Z') + { + in += 'a' - 'A'; + } + + return in; +} + +short *FMOD_struprW(short *string) +{ + short * cp; + + for (cp=string; *cp; ++cp) + { + if (*cp >= 'a' && *cp <= 'z') + { + *cp += 'A' - 'a'; + } + } + + return(string); +} + +short *FMOD_strlwrW(short *string) +{ + short * cp; + + for (cp=string; *cp; ++cp) + { + if (*cp >= 'A' && *cp <= 'Z') + { + *cp += 'a' - 'A'; + } + } + + return(string); +} + + +int FMOD_strcmpW( const short *string1, const short *string2 ) +{ + register short c1, c2; + do + { + c1 = *string1++; + c2 = *string2++; + } while ( c1 && (c1 == c2) ); + + return c1 - c2; +} + + +int FMOD_strncmpW( const short *string1, const short *string2, int len ) +{ + register short c1, c2; + int count=0; + do + { + c1 = *string1++; + c2 = *string2++; + count++; + } while ( c1 && (c1 == c2) && count < len); + + return c1 - c2; +} + + +int FMOD_stricmpW( const short *string1, const short *string2 ) +{ + register short c1, c2; + do + { + c1 = FMOD_tolowerW(*string1++); + c2 = FMOD_tolowerW(*string2++); + } while ( c1 && (c1 == c2) ); + + return c1 - c2; +} + +int FMOD_strnicmpW( const short *string1, const short *string2, int len ) +{ + register short c1, c2; + int count=0; + do + { + c1 = FMOD_tolowerW(*string1++); + c2 = FMOD_tolowerW(*string2++); + count++; + } while ( c1 && (c1 == c2) && count < len); + + return c1 - c2; +} + +short * FMOD_strstrW(const short * s1,const short * s2) +{ + int l1, l2; + + l2 = FMOD_strlenW(s2); + + if (!l2) + { + return (short *) s1; + } + + l1 = FMOD_strlenW(s1); + + while (l1 >= l2) + { + l1--; + + if (!FMOD_memcmp(s1,s2,l2)) return (short *) s1; + + s1++; + } + return (short *)0; +} + +short *FMOD_strdupW(const short * src) +{ + short *ret = 0; + + ret = (short *)FMOD_Memory_Alloc(FMOD_strlenW(src) + 2); + if (ret) + { + FMOD_strcpyW(ret, src); + } + + return ret; +} + + +short *FMOD_eatwhiteW(const short *string) +{ + for (;*string && FMOD_isspaceW(*string);string++) ; + + return (short *)string; +} + + +/* + AJS This only handles decimal integers. Feel free to extend it if you need to +*/ +int FMOD_atoiW(const short *s) +{ + if (!s) + { + return 0; + } + + int val = 0; + const short *p = s; + + for (; *p; p++) ; + + p--; + for (int i=1; p >= s; p--, i *= 10) + { + val += ((*p - '0') * i); + } + + return val; +} + + +char *FMOD_wtoa(short *s) +{ + if (!s) + { + return NULL; + } + + char *from = (char *)s; + char *to = (char *)s; + + while (*from != '\0') + { + *to = *from; + to += 1; + from += 2; + } + *to = 0; + + return (char *)s; +} diff --git a/src/fmod_stringw.h b/src/fmod_stringw.h new file mode 100755 index 0000000..cd47ace --- /dev/null +++ b/src/fmod_stringw.h @@ -0,0 +1,35 @@ +#ifndef _SYSTEM_STRINGW_H +#define _SYSTEM_STRINGW_H + +#ifdef __cplusplus +extern "C" { +#endif + +int FMOD_strlenW(const short * s); +short * FMOD_strcpyW(short * dest,const short *src); +short * FMOD_strncpyW(short * dest, const short *src, int count); +short * FMOD_strcatW(short * dest, const short * src); +short * FMOD_strncatW(short *dest, const short *src, int count); +short FMOD_tolowerW(short in); +short * FMOD_struprW(short *string); +short * FMOD_strlwrW(short *string); +int FMOD_strcmpW( const short *string1, const short *string2 ); +int FMOD_strncmpW( const short *string1, const short *string2, int len); +int FMOD_stricmpW( const short *string1, const short *string2); +int FMOD_strnicmpW( const short *string1, const short *string2, int len); +short * FMOD_strstrW(const short * s1,const short * s2); +short * FMOD_strdupW(const short * src); +short * FMOD_eatwhiteW(const short *string); +int FMOD_atoiW(const short *s); +char * FMOD_wtoa(short *s); + +#define FMOD_isspaceW(j) (j==L' ' || j==L'\t' || j==L'\n') +#define FMOD_isdigitW(j) (j>=L'0' && j<=L'9') +#define FMOD_isalphaW(j) ((((j) >= L'A') && ((j) <= L'Z')) || (((j) >= L'a') && ((j) <= L'z'))) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/fmod_syncpoint.h b/src/fmod_syncpoint.h new file mode 100755 index 0000000..aebc4c2 --- /dev/null +++ b/src/fmod_syncpoint.h @@ -0,0 +1,44 @@ +#ifndef _FMOD_SYNCPOINT_H +#define _FMOD_SYNCPOINT_H + +#include "fmod_settings.h" + +namespace FMOD +{ + class SoundI; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) + #endif + + class SyncPoint : public SortedLinkedListNode + { + public: + char *mName FMOD_PACKED_INTERNAL; + SoundI *mSound FMOD_PACKED_INTERNAL; /* The subsound that owns it. */ + + unsigned int mOffset FMOD_PACKED_INTERNAL; + unsigned short mSubSoundIndex FMOD_PACKED_INTERNAL; /* The subsound index that owns it. */ + unsigned short mIndex FMOD_PACKED_INTERNAL; /* The relative index in the subsound. (needs to be fixed if a syncpoint is deleted) */ + int mStatic FMOD_PACKED_INTERNAL; /* 1 = memory points to a preallocated block, so when releasing, dont free it! */ + } FMOD_PACKED; + + class SyncPointNamed : public SyncPoint + { + public: + char mNameMemory[FMOD_STRING_MAXNAMELEN]; + }; + + #ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif + #endif + +} + +#endif + + diff --git a/src/fmod_system.cpp b/src/fmod_system.cpp new file mode 100755 index 0000000..2ad7dde --- /dev/null +++ b/src/fmod_system.cpp @@ -0,0 +1,1693 @@ +/*$ preserve start $*/ + +#include "fmod_settings.h" +#include "fmod_systemi.h" +#include "fmod_cmdlog.h" + +namespace FMOD +{ + + +//AJS +/*AJS +#ifdef FMOD_SUPPORT_CMDLOG + result = FMOD_CmdLog_Release(); + if (result != FMOD_OK) + { + return result; + } +#endif +AJS*/ + +/*$ preserve end $*/ + + +FMOD_RESULT System::release() +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->release(); + } +} + + +FMOD_RESULT System::setOutput(FMOD_OUTPUTTYPE output) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setOutput(output); + } +} + + +FMOD_RESULT System::getOutput(FMOD_OUTPUTTYPE *output) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getOutput(output); + } +} + + +FMOD_RESULT System::getNumDrivers(int *numdrivers) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getNumDrivers(numdrivers); + } +} + + +FMOD_RESULT System::getDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDriverInfo(id, name, namelen, guid); + } +} + + +FMOD_RESULT System::getDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDriverInfoW(id, name, namelen, guid); + } +} + + +FMOD_RESULT System::getDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDriverCaps(id, caps, minfrequency, maxfrequency, controlpanelspeakermode); + } +} + + +FMOD_RESULT System::setDriver(int driver) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setDriver(driver); + } +} + + +FMOD_RESULT System::getDriver(int *driver) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDriver(driver); + } +} + + +FMOD_RESULT System::setHardwareChannels(int min2d, int max2d, int min3d, int max3d) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setHardwareChannels(min2d, max2d, min3d, max3d); + } +} + + +FMOD_RESULT System::setSoftwareChannels(int numsoftwarechannels) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setSoftwareChannels(numsoftwarechannels); + } +} + + +FMOD_RESULT System::getSoftwareChannels(int *numsoftwarechannels) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getSoftwareChannels(numsoftwarechannels); + } +} + + +FMOD_RESULT System::setSoftwareFormat(int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplemethod) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setSoftwareFormat(samplerate, format, numoutputchannels, maxinputchannels, resamplemethod); + } +} + + +FMOD_RESULT System::getSoftwareFormat(int *samplerate, FMOD_SOUND_FORMAT *format, int *numoutputchannels, int *maxinputchannels, FMOD_DSP_RESAMPLER *resamplemethod, int *bits) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getSoftwareFormat(samplerate, format, numoutputchannels, maxinputchannels, resamplemethod, bits); + } +} + + +FMOD_RESULT System::setDSPBufferSize(unsigned int bufferlength, int numbuffers) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setDSPBufferSize(bufferlength, numbuffers); + } +} + + +FMOD_RESULT System::getDSPBufferSize(unsigned int *bufferlength, int *numbuffers) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDSPBufferSize(bufferlength, numbuffers); + } +} + + +FMOD_RESULT System::setFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setFileSystem(useropen, userclose, userread, userseek, blockalign); + } +} + + +FMOD_RESULT System::attachFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->attachFileSystem(useropen, userclose, userread, userseek); + } +} + + +FMOD_RESULT System::setAdvancedSettings(FMOD_ADVANCEDSETTINGS *settings) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setAdvancedSettings(settings); + } +} + + +FMOD_RESULT System::getAdvancedSettings(FMOD_ADVANCEDSETTINGS *settings) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getAdvancedSettings(settings); + } +} + + +FMOD_RESULT System::setSpeakerMode(FMOD_SPEAKERMODE speakermode) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setSpeakerMode(speakermode); + } +} + + +FMOD_RESULT System::getSpeakerMode(FMOD_SPEAKERMODE *speakermode) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getSpeakerMode(speakermode); + } +} + + +FMOD_RESULT System::setCallback(FMOD_SYSTEM_CALLBACK callback) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setCallback(callback); + } +} + + +FMOD_RESULT System::setPluginPath(const char *path) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setPluginPath(path); + } +} + + +FMOD_RESULT System::loadPlugin(const char *filename, unsigned int *handle, unsigned int priority) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->loadPlugin(filename, handle, priority); + } +} + + +FMOD_RESULT System::unloadPlugin(unsigned int handle) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->unloadPlugin(handle); + } +} + + +FMOD_RESULT System::getNumPlugins(FMOD_PLUGINTYPE plugintype, int *numplugins) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getNumPlugins(plugintype, numplugins); + } +} + + +FMOD_RESULT System::getPluginHandle(FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getPluginHandle(plugintype, index, handle); + } +} + + +FMOD_RESULT System::getPluginInfo(unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getPluginInfo(handle, plugintype, name, namelen, version); + } +} + + +FMOD_RESULT System::setOutputByPlugin(unsigned int handle) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setOutputByPlugin(handle); + } +} + + +FMOD_RESULT System::getOutputByPlugin(unsigned int *handle) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getOutputByPlugin(handle); + } +} + + +FMOD_RESULT System::createDSPByPlugin(unsigned int handle, DSP **dsp) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createDSPByPlugin(handle, (DSPI **)dsp); + } +} + + +FMOD_RESULT System::createCodec(FMOD_CODEC_DESCRIPTION *description, unsigned int priority) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createCodec(description, priority); + } +} + + +FMOD_RESULT System::init(int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->init(maxchannels, flags, extradriverdata); + } +} + + +FMOD_RESULT System::close() +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->close(); + } +} + + +FMOD_RESULT System::update() +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->update(); + } +} + + +FMOD_RESULT System::set3DSettings(float dopplerscale, float distancefactor, float rolloffscale) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->set3DSettings(dopplerscale, distancefactor, rolloffscale); + } +} + + +FMOD_RESULT System::get3DSettings(float *dopplerscale, float *distancefactor, float *rolloffscale) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->get3DSettings(dopplerscale, distancefactor, rolloffscale); + } +} + + +FMOD_RESULT System::set3DNumListeners(int numlisteners) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->set3DNumListeners(numlisteners); + } +} + + +FMOD_RESULT System::get3DNumListeners(int *numlisteners) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->get3DNumListeners(numlisteners); + } +} + + +FMOD_RESULT System::set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->set3DListenerAttributes(listener, pos, vel, forward, up); + } +} + + +FMOD_RESULT System::get3DListenerAttributes(int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->get3DListenerAttributes(listener, pos, vel, forward, up); + } +} + + +FMOD_RESULT System::set3DRolloffCallback(FMOD_3D_ROLLOFFCALLBACK callback) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->set3DRolloffCallback(callback); + } +} + + +FMOD_RESULT System::set3DSpeakerPosition(FMOD_SPEAKER speaker, float x, float y, bool active) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->set3DSpeakerPosition(speaker, x, y, active); + } +} + + +FMOD_RESULT System::get3DSpeakerPosition(FMOD_SPEAKER speaker, float *x, float *y, bool *active) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->get3DSpeakerPosition(speaker, x, y, active); + } +} + + +FMOD_RESULT System::setStreamBufferSize(unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setStreamBufferSize(filebuffersize, filebuffersizetype); + } +} + + +FMOD_RESULT System::getStreamBufferSize(unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getStreamBufferSize(filebuffersize, filebuffersizetype); + } +} + + +FMOD_RESULT System::getVersion(unsigned int *version) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getVersion(version); + } +} + + +FMOD_RESULT System::getOutputHandle(void **handle) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getOutputHandle(handle); + } +} + + +FMOD_RESULT System::getChannelsPlaying(int *channels) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getChannelsPlaying(channels); + } +} + + +FMOD_RESULT System::getHardwareChannels(int *num2d, int *num3d, int *total) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getHardwareChannels(num2d, num3d, total); + } +} + + +FMOD_RESULT System::getCPUUsage(float *dsp, float *stream, float *geometry, float *update, float *total) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getCPUUsage(dsp, stream, geometry, update, total); + } +} + + +FMOD_RESULT System::getSoundRAM(int *currentalloced, int *maxalloced, int *total) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getSoundRAM(currentalloced, maxalloced, total); + } +} + + +FMOD_RESULT System::getNumCDROMDrives(int *numdrives) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getNumCDROMDrives(numdrives); + } +} + + +FMOD_RESULT System::getCDROMDriveName(int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getCDROMDriveName(drive, drivename, drivenamelen, scsiname, scsinamelen, devicename, devicenamelen); + } +} + + +FMOD_RESULT System::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getSpectrum(spectrumarray, numvalues, channeloffset, windowtype); + } +} + + +FMOD_RESULT System::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getWaveData(wavearray, numvalues, channeloffset); + } +} + + +FMOD_RESULT System::createSound(const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createSound(name_or_data, mode, exinfo, (SoundI **)sound); + } +} + + +FMOD_RESULT System::createStream(const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createStream(name_or_data, mode, exinfo, (SoundI **)sound); + } +} + + +FMOD_RESULT System::createDSP(FMOD_DSP_DESCRIPTION *description, DSP **dsp) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createDSP(description, (DSPI **)dsp); + } +} + + +FMOD_RESULT System::createDSPByType(FMOD_DSP_TYPE type, DSP **dsp) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createDSPByType(type, (DSPI **)dsp); + } +} + + +FMOD_RESULT System::createChannelGroup(const char *name, ChannelGroup **channelgroup) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createChannelGroup(name, (ChannelGroupI **)channelgroup); + } +} + + +FMOD_RESULT System::createSoundGroup(const char *name, SoundGroup **soundgroup) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createSoundGroup(name, (SoundGroupI **)soundgroup); + } +} + + +FMOD_RESULT System::createReverb(Reverb **reverb) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createReverb((ReverbI **)reverb); + } +} + + +FMOD_RESULT System::playSound(FMOD_CHANNELINDEX channelid, Sound *sound, bool paused, Channel **channel) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->playSound(channelid, (SoundI *)sound, paused, (ChannelI **)channel); + } +} + + +FMOD_RESULT System::playDSP(FMOD_CHANNELINDEX channelid, DSP *dsp, bool paused, Channel **channel) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->playDSP(channelid, (DSPI *)dsp, paused, (ChannelI **)channel); + } +} + + +FMOD_RESULT System::getChannel(int channelid, Channel **channel) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getChannel(channelid, (ChannelI **)channel); + } +} + + +FMOD_RESULT System::getMasterChannelGroup(ChannelGroup **channelgroup) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getMasterChannelGroup((ChannelGroupI **)channelgroup); + } +} + + +FMOD_RESULT System::getMasterSoundGroup(SoundGroup **soundgroup) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getMasterSoundGroup((SoundGroupI **)soundgroup); + } +} + + +FMOD_RESULT System::setReverbProperties(const FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setReverbProperties(prop); + } +} + + +FMOD_RESULT System::getReverbProperties(FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getReverbProperties(prop); + } +} + + +FMOD_RESULT System::setReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setReverbAmbientProperties(prop); + } +} + + +FMOD_RESULT System::getReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getReverbAmbientProperties(prop); + } +} + + +FMOD_RESULT System::getDSPHead(DSP **dsp) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDSPHead((DSPI **)dsp); + } +} + + +FMOD_RESULT System::addDSP(DSP *dsp, DSPConnection **connection) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->addDSP((DSPI *)dsp, (DSPConnectionI **)connection); + } +} + + +FMOD_RESULT System::lockDSP() +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->lockDSP(); + } +} + + +FMOD_RESULT System::unlockDSP() +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->unlockDSP(); + } +} + + +FMOD_RESULT System::getDSPClock(unsigned int *hi, unsigned int *lo) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getDSPClock(hi, lo); + } +} + + +FMOD_RESULT System::getRecordNumDrivers(int *numdrivers) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getRecordNumDrivers(numdrivers); + } +} + + +FMOD_RESULT System::getRecordDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getRecordDriverInfo(id, name, namelen, guid); + } +} + + +FMOD_RESULT System::getRecordDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getRecordDriverInfoW(id, name, namelen, guid); + } +} + + +FMOD_RESULT System::getRecordDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getRecordDriverCaps(id, caps, minfrequency, maxfrequency); + } +} + + +FMOD_RESULT System::getRecordPosition(int id, unsigned int *position) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getRecordPosition(id, position); + } +} + + +FMOD_RESULT System::recordStart(int id, Sound *sound, bool loop) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->recordStart(id, (SoundI *)sound, loop); + } +} + + +FMOD_RESULT System::recordStop(int id) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->recordStop(id); + } +} + + +FMOD_RESULT System::isRecording(int id, bool *recording) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->isRecording(id, recording); + } +} + + +FMOD_RESULT System::createGeometry(int maxpolygons, int maxvertices, Geometry **geometry) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->createGeometry(maxpolygons, maxvertices, (GeometryI **)geometry); + } +} + + +FMOD_RESULT System::setGeometrySettings(float maxworldsize) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setGeometrySettings(maxworldsize); + } +} + + +FMOD_RESULT System::getGeometrySettings(float *maxworldsize) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getGeometrySettings(maxworldsize); + } +} + + +FMOD_RESULT System::loadGeometry(const void *data, int datasize, Geometry **geometry) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->loadGeometry(data, datasize, (GeometryI **)geometry); + } +} + + +FMOD_RESULT System::getGeometryOcclusion(const FMOD_VECTOR *listener, const FMOD_VECTOR *source, float *direct, float *reverb) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getGeometryOcclusion(listener, source, direct, reverb); + } +} + + +FMOD_RESULT System::setNetworkProxy(const char *proxy) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setNetworkProxy(proxy); + } +} + + +FMOD_RESULT System::getNetworkProxy(char *proxy, int proxylen) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getNetworkProxy(proxy, proxylen); + } +} + + +FMOD_RESULT System::setNetworkTimeout(int timeout) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setNetworkTimeout(timeout); + } +} + + +FMOD_RESULT System::getNetworkTimeout(int *timeout) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getNetworkTimeout(timeout); + } +} + + +FMOD_RESULT System::setUserData(void *_userdata) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->setUserData(_userdata); + } +} + + +FMOD_RESULT System::getUserData(void **_userdata) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getUserData(_userdata); + } +} + + +FMOD_RESULT System::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ + FMOD_RESULT result; + SystemI *systemi; + + result = SystemI::validate(this, &systemi); + if (result != FMOD_OK) + { + return result; + } + else + { + return systemi->getMemoryInfo(memorybits, event_memorybits, memoryused, memoryused_details); + } +} + + +/*$ preserve start $*/ +} +/*$ preserve end $*/ diff --git a/src/fmod_systemi.cpp b/src/fmod_systemi.cpp new file mode 100755 index 0000000..8de205c --- /dev/null +++ b/src/fmod_systemi.cpp @@ -0,0 +1,12754 @@ +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_3d.h" +#ifdef FMOD_SUPPORT_NONBLOCKING +#include "fmod_async.h" +#endif +#include "fmod_autocleanup.h" +#include "fmod_channeli.h" +#include "fmod_channel_real.h" +#include "fmod_channel_stream.h" +#include "fmod_channel_software.h" +#include "fmod_codec_user.h" +#include "fmod_codeci.h" +#include "fmod_debug.h" +#include "fmod_dsp_chorus.h" +#ifdef FMOD_SUPPORT_DSPCODEC +#include "fmod_dsp_codec.h" +#endif + +#ifndef FMOD_STATICFORPLUGINS +#include "fmod_dsp_distortion.h" +#include "fmod_dsp_echo.h" +#include "fmod_dsp_delay.h" +#include "fmod_dsp_fft.h" +#include "fmod_dsp_filter.h" +#include "fmod_dsp_flange.h" +#include "fmod_dsp_highpass.h" +#include "fmod_dsp_itecho.h" +#include "fmod_dsp_compressor.h" +#include "fmod_dsp_sfxreverb.h" +#include "fmod_dsp_lowpass.h" +#include "fmod_dsp_lowpass2.h" +#include "fmod_dsp_lowpass_simple.h" +#include "fmod_dsp_normalize.h" +#include "fmod_dsp_oscillator.h" +#include "fmod_dsp_parameq.h" +#include "fmod_dsp_pitchshift.h" +#include "fmod_dsp_reverb.h" +#include "fmod_dsp_tremolo.h" +#endif + +#include "fmod_dsp_resampler.h" +#ifndef PLATFORM_PS3 +#include "fmod_dsp_soundcard.h" +#endif +#ifdef FMOD_SUPPORT_VSTPLUGIN +#include "fmod_dsp_vstplugin.h" +#endif +#include "fmod_dsp_wavetable.h" +#include "fmod_dspi.h" +#include "fmod_file.h" +#include "fmod_file_cdda.h" +#include "fmod_file_disk.h" +#include "fmod_file_memory.h" +#include "fmod_file_net.h" +#include "fmod_file_null.h" +#include "fmod_file_user.h" +#include "fmod_localcriticalsection.h" +#include "fmod_memory.h" +#include "fmod_metadata.h" +#include "fmod_net.h" +#include "fmod_output_emulated.h" +#include "fmod_output_polled.h" +#include "fmod_output_software.h" +#include "fmod_os_cdda.h" +#include "fmod_os_output.h" +#include "fmod_pluginfactory.h" +#include "fmod_sample_software.h" +#include "fmod_soundi.h" +#include "fmod_sound_sample.h" + +#ifdef FMOD_SUPPORT_GEOMETRY +#include "fmod_geometryi.h" +#endif +#ifdef FMOD_SUPPORT_STREAMING +#include "fmod_sound_stream.h" +#endif +#include "fmod_string.h" +#include "fmod_stringw.h" +#include "fmod_systemi.h" +#ifdef FMOD_SUPPORT_OPENAL +#include "fmod_output_openal.h" +#endif +#include "fmod_downmix.h" +#ifdef FMOD_SUPPORT_NEURAL +#include "fmod_downmix_neuralthx.h" +#endif +#ifdef FMOD_SUPPORT_MYEARS +#include "fmod_downmix_myears.h" +#endif + +/* + Codecs +*/ +#ifdef FMOD_SUPPORT_AAC +#include "fmod_codec_aac.h" +#endif +#ifdef FMOD_SUPPORT_AIFF +#include "fmod_codec_aiff.h" +#endif +#ifdef FMOD_SUPPORT_ASF +#include "fmod_codec_asf.h" +#endif +#ifdef FMOD_SUPPORT_AT3 +#include "fmod_codec_at3.h" +#endif +#ifdef FMOD_SUPPORT_CDDA +#include "fmod_codec_cdda.h" +#endif +#ifdef FMOD_SUPPORT_DLS +#include "fmod_codec_dls.h" +#endif +#ifdef FMOD_SUPPORT_FLAC +#include "fmod_codec_flac.h" +#endif +#ifdef FMOD_SUPPORT_GCADPCM_DSP +#include "fmod_codec_dsp.h" +#endif +#ifdef FMOD_SUPPORT_FSB +#include "fmod_codec_fsb.h" +#endif +#ifdef FMOD_SUPPORT_IT +#include "fmod_codec_it.h" +#endif +#ifdef FMOD_SUPPORT_MOD +#include "fmod_codec_midi.h" +#endif +#ifdef FMOD_SUPPORT_MOD +#include "fmod_codec_mod.h" +#endif +#ifdef FMOD_SUPPORT_MPEG +#include "fmod_codec_mpeg.h" +#endif +#ifdef FMOD_SUPPORT_MPEGPSP +#include "fmod_codec_mpegpsp.h" +#endif +#ifdef FMOD_SUPPORT_OGGVORBIS +#include "fmod_codec_oggvorbis.h" +#endif +#ifdef FMOD_SUPPORT_CELT +#include "fmod_codec_celt.h" +#endif +#ifdef FMOD_SUPPORT_PLAYLIST +#include "fmod_codec_playlist.h" +#endif +#ifdef FMOD_SUPPORT_RAW +#include "fmod_codec_raw.h" +#endif +#ifdef FMOD_SUPPORT_SF2 +#include "fmod_codec_sf2.h" +#endif +#ifdef FMOD_SUPPORT_S3M +#include "fmod_codec_s3m.h" +#endif +#ifdef FMOD_SUPPORT_TAGS +#include "fmod_codec_tag.h" +#endif +#ifdef FMOD_SUPPORT_TREMOR +#include "fmod_codec_tremor.h" +#endif +#ifdef FMOD_SUPPORT_VAG + #if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + #include "fmod_codec_vag.h" + #else + #include "fmod_codec_swvag.h" + #endif +#endif +#ifdef FMOD_SUPPORT_WAV +#include "fmod_codec_wav.h" +#endif +#ifdef FMOD_SUPPORT_XM +#include "fmod_codec_xm.h" +#endif +#ifdef FMOD_SUPPORT_XMA +#include "fmod_codec_xma.h" +#endif +#ifdef FMOD_SUPPORT_XWMA +#include "fmod_codec_xwma.h" +#endif + +#ifdef FMOD_SUPPORT_IMAADPCM +#include "fmod_codec_wav_imaadpcm.h" +#endif + +/* + Output types +*/ +#ifdef FMOD_SUPPORT_NOSOUND +#include "fmod_output_nosound.h" +#endif +#ifdef FMOD_SUPPORT_NOSOUND_NRT +#include "fmod_output_nosound_nrt.h" +#endif +#ifdef FMOD_SUPPORT_WAVWRITER +#include "fmod_output_wavwriter.h" +#endif +#ifdef FMOD_SUPPORT_WAVWRITER_NRT +#include "fmod_output_wavwriter_nrt.h" +#endif + +/* + Profiler modules +*/ +#ifdef FMOD_SUPPORT_PROFILE +#include "fmod_profile.h" +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP) +#include "fmod_profile_dsp.h" +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY) +#include "fmod_profile_memory.h" +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CPU) +#include "fmod_profile_cpu.h" +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL) +#include "fmod_profile_channel.h" +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CODEC) +#include "fmod_profile_codec.h" +#endif + +#include "fmod_speakermap.h" /* Must be after mmreg include from codec wav */ + +namespace FMOD +{ + +FMOD_OS_CRITICALSECTION *SystemI::gSoundListCrit = 0; + +/* +[ + [DESCRIPTION] + Returns a pointer to the instance of system object with the specified id + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getInstance(unsigned int id, SystemI **sys) +{ + SystemI *current; + + if (sys) + { + *sys = 0; + } + + current = SAFE_CAST(SystemI, gGlobal->gSystemHead->getNext()); + while (current != gGlobal->gSystemHead) + { + if (current->mIndex == id) + { + if (sys) + { + *sys = current; + } + return FMOD_OK; + } + + current = SAFE_CAST(SystemI, current->getNext()); + } + + return FMOD_ERR_INVALID_PARAM; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::validate(System *system, SystemI **systemi) +{ + FMOD::SystemI *sys = (FMOD::SystemI *)system; + + if (!system) + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (!systemi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!FMOD::gGlobal->gSystemHead->exists(sys)) + { + return FMOD_ERR_INVALID_HANDLE; + } + + *systemi = sys; + + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_STREAMING + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void SystemI::streamThread(void *data) +{ + SystemI *system = (SystemI *)data; + + system->updateStreams(); +} + + +/* + ==================================================================================== + + PRIVATE MEMBER FUNCTIONS + + ==================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::updateStreams() +{ + mStreamTimeStamp.stampIn(); + + /* + Process Stream Channel list. Stream channels are added and removed with ChannelStream::alloc and ChannelStream::stop + */ + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + { + mStreamListChannelCurrent = mStreamListChannelHead.getNext(); + while (mStreamListChannelCurrent != &mStreamListChannelHead) + { + ChannelStream *channelstream = (ChannelStream *)mStreamListChannelCurrent->getData(); + mStreamListChannelNext = mStreamListChannelCurrent->getNext(); + + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + /* + Update the stream channel. + */ + FMOD_OS_CriticalSection_Enter(mStreamUpdateCrit); + if (!channelstream->mFinished) + { + channelstream->updateStream(); + } + FMOD_OS_CriticalSection_Leave(mStreamUpdateCrit); + + + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + + mStreamListChannelCurrent = mStreamListChannelNext; + } + mStreamListChannelNext = 0; + } + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + /* + Process stream sound list. Stream sounds are added at System::createSound and Sound::release. + Channel will always be valid until the sound is flagged as finished. + */ + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + { + LinkedListNode *current = mStreamListSoundHead.getNext(); + while (current != &mStreamListSoundHead) + { + Stream *stream = (Stream *)current->getData(); + + if ((stream->mChannel && stream->mChannel->mFinished)) + { + stream->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED; /* Main thread will wait for this flag while releasing. */ + if (stream->mSubSound) + { + if (stream->mSubSoundShared) + { + stream->mSubSoundShared->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED; + } + else if (stream->mSubSound[stream->mSubSoundIndex]) + { + stream->mSubSound[stream->mSubSoundIndex]->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED; + } + } + } + current = current->getNext(); + } + } + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + mStreamTimeStamp.stampOut(95); + + return FMOD_OK; +} + +#endif + + +/* +[ + [DESCRIPTION] + Updates virtual voices, and any per voice calculations, ie emulated voice calcs, 3d calcs or geometry. + + [PARAMETERS] + 'delta' Delta in milliseconds from last frame. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::updateChannels(int delta) +{ + int emulatedused; + ChannelI *current; + + /* + If the channel has stop, put it at the end of the list. + */ + current = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext()); + while (current != &mChannelUsedListHead) + { + FMOD_RESULT result; + ChannelI *next = SAFE_CAST(ChannelI, current->getNext()); + bool playing = false; + + result = current->isPlaying(&playing); + if (playing) + { + current->update(delta); + } + + result = current->isPlaying(&playing); + if (!playing) + { + current->stopEx(CHANNELI_STOPFLAG_REFSTAMP | + CHANNELI_STOPFLAG_UPDATELIST | + CHANNELI_STOPFLAG_RESETCALLBACKS | + CHANNELI_STOPFLAG_CALLENDCALLBACK | + CHANNELI_STOPFLAG_RESETCHANNELGROUP | + CHANNELI_STOPFLAG_PROCESSENDDELAY | + CHANNELI_STOPFLAG_UPDATESYNCPOINTS); /* Reset, return to freelist and call end callback. */ + } + + current = next; + } + + /* + Now swap any emulated channels that have made it to the front of the list with real channels + */ + if (mEmulated) + { + mEmulated->mChannelPool->getChannelsUsed(&emulatedused); + + if (emulatedused) + { + LinkedListNode *emucurrent, *realcurrent[CHANNELREAL_TYPE_MAX]; + int count; + + emucurrent = mChannelSortedListHead.getNext(); + + for (count = 0; count < CHANNELREAL_TYPE_MAX; count++) + { + realcurrent[count] = &mChannelSortedListHead; + } + + do + { + FMOD_RESULT result = FMOD_OK; + ChannelI *emuchannel = 0; + ChannelReal *emuchannelreal [FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + ChannelI *realchannel [FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + ChannelReal *realchannelreal[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + FMOD_MODE mode; + int realchannelsneeded, realchannelsfound, channelsfound; + bool isvirtual; + + /* + Search for an emulated channel from the head of the importance list. + */ + isvirtual = false; + do + { + emuchannel = (ChannelI *)emucurrent->getData(); + if (!emuchannel) + { + /* + Back to head of list, didn't find anything. Don't crash. + */ + break; + } + + result = emuchannel->isVirtual(&isvirtual); + if (isvirtual && !(emuchannel->mFlags & CHANNELI_FLAG_FORCEVIRTUAL)) + { + break; + } + + emucurrent = (ChannelI *)emucurrent->getNext(); + } while (emucurrent != &mChannelSortedListHead); + + if (emucurrent == &mChannelSortedListHead || + emucurrent == realcurrent[0] || + emucurrent == realcurrent[1] || + emucurrent == realcurrent[2] || + result != FMOD_OK) + { + break; + } + + /* + Now we've found an emulated channel, see if there are any free + or lower priority channels that we can swap with of the same type + */ + channelsfound = 0; /* ChannelIs */ + realchannelsfound = 0; /* RealChannels */ + + result = emuchannel->getRealChannel(emuchannelreal, 0); + if (result != FMOD_OK) + { + break; + } + + mode = emuchannelreal[0]->mMode; + + /* + Find out how many subsamples we have to query the number of real channels we need for this emulated voice. + */ + + if (emuchannelreal[0]->mSound) + { + #ifdef FMOD_SUPPORT_STREAMING + + if (emuchannelreal[0]->mSound->isStream()) + { + Stream *stream = SAFE_CAST(Stream, emuchannelreal[0]->mSound); + Sample *sample = stream->mSample; + + realchannelsneeded = sample->mNumSubSamples; + } + else + + #endif + { + Sample *sample = SAFE_CAST(Sample, emuchannelreal[0]->mSound); + + realchannelsneeded = sample->mNumSubSamples; + } + } + else if (emuchannelreal[0]->mDSP) + { + realchannelsneeded = 1; + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::updateChannels", "emuchannelreal[0] has no mSound or mDSP\n")); + return FMOD_ERR_INTERNAL; + } + + if (realchannelsneeded < 1) + { + realchannelsneeded = 1; + } + + /* + First try and find free real voices for this virtual channel, so we can swap with them. + */ + if (realchannelsfound < realchannelsneeded) + { + int found = 0; + ChannelReal *tempchannelreal[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + + if (mode & FMOD_HARDWARE) + { + if (emuchannelreal[0]->mSound) + { + result = mOutput->getFreeChannel(mode, tempchannelreal, realchannelsneeded, emuchannelreal[0]->mSound->mChannels, &found, true); + } + else + { + return FMOD_ERR_INTERNAL; + } + } + else + { + #ifdef FMOD_SUPPORT_SOFTWARE + /* + Before looking for a software channel (which there might be available), see if it is a realtime compressed sample. + */ + if (mode & FMOD_CREATECOMPRESSEDSAMPLE + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + || (emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16) + #endif + ) + { + Sample *sample = SAFE_CAST(Sample, emuchannelreal[0]->mSound); + + if (0) + { + } + #ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + if (sample->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + result = mDSPCodecPool_MPEG.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (sample->mFormat == FMOD_SOUND_FORMAT_XMA) + { + result = mDSPCodecPool_XMA.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + result = mDSPCodecPool_ADPCM.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (sample->mFormat == FMOD_SOUND_FORMAT_CELT) + { + result = mDSPCodecPool_CELT.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + else if (sample->mFormat == FMOD_SOUND_FORMAT_PCM16) + { + result = mDSPCodecPool_RAW.areAnyFree(); + } + #endif + #endif + else + { + result = FMOD_ERR_FORMAT; + } + } + else if (mode & (FMOD_CREATESTREAM | FMOD_NONBLOCKING) && emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mOpenState == FMOD_OPENSTATE_SETPOSITION) + { + result = FMOD_ERR_NOTREADY; + } + else + { + result = FMOD_OK; + } + + if (result == FMOD_OK) + { + if (emuchannelreal[0]->mSound) + { + result = mSoftware->getFreeChannel(mode, tempchannelreal, realchannelsneeded, emuchannelreal[0]->mSound->mChannels, &found, true); + } + else if(emuchannelreal[0]->mDSP) + { + result = mSoftware->getFreeChannel(mode, tempchannelreal, realchannelsneeded, 1, &found, true); + } + else + { + return FMOD_ERR_INTERNAL; + } + } + #else + result = FMOD_ERR_NEEDSSOFTWARE; + #endif + } + + for (count = 0; count < found; count++) + { + realchannelreal[realchannelsfound] = tempchannelreal[count]; + realchannelsfound ++; + } + } + + tryformore: + /* + If there are not enough stopped real channels lying around, find a real channel to swap with the virtual channel. + */ + if (realchannelsfound < realchannelsneeded) + { + CHANNELREAL_TYPE realtype; + + if (mOutput->mChannelPool == mOutput->mChannelPool3D) + { + realtype = mode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : CHANNELREAL_TYPE_HW; + } + else + { + realtype = mode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : mode & FMOD_3D ? CHANNELREAL_TYPE_HW3D : CHANNELREAL_TYPE_HW2D; + } + + if (realcurrent[realtype] == &mChannelSortedListHead) + { + realcurrent[realtype] = realcurrent[realtype]->getPrev(); + } + + do + { + if (realcurrent[realtype] == emucurrent) + { + result = FMOD_ERR_CHANNEL_ALLOC; + break; + } + + realchannel[channelsfound] = (ChannelI *)realcurrent[realtype]->getData(); + + /* + TEMPORARY FIX, ARE VOICES BEING SWAPPED AROUND AND THE EMUCURRENT/REALCURRENT pointing to the wrong places? + */ + if (realcurrent[realtype] == &mChannelSortedListHead) + { + result = FMOD_ERR_CHANNEL_ALLOC; + break; + } + + /* + Only want a real channel channel + */ + realchannel[channelsfound]->isVirtual(&isvirtual); + if (isvirtual) + { + realchannel[channelsfound]->mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL; /* Ok, it didnt swap, its going to come in virtual now. */ + } + else + { + FMOD_MODE currentmode; + CHANNELREAL_TYPE currentrealtype; + + /* + Only want a channel of the same type. + */ + realchannel[channelsfound]->getMode(¤tmode); + + if (mOutput->mChannelPool == mOutput->mChannelPool3D) + { + currentrealtype = currentmode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : CHANNELREAL_TYPE_HW; + } + else + { + currentrealtype = currentmode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : currentmode & FMOD_3D ? CHANNELREAL_TYPE_HW3D : CHANNELREAL_TYPE_HW2D; + } + + if (realtype == currentrealtype) + { + /* + AJS 20/09/06 If a FMOD_CREATECOMPRESSEDSAMPLE is going from virtual to real then we need to make sure there's + a dspcodec free for it to use. To ensure this, we make sure FMOD_CREATECOMPRESSEDSAMPLE only steals + FMOD_CREATECOMPRESSEDSAMPLE channels of the same format. This is so a dspcodec is freed for us to use. + */ + if (realtype == CHANNELREAL_TYPE_SW) + { + if (mode & FMOD_CREATECOMPRESSEDSAMPLE + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + || (emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16) + #endif + ) + { + Sample *sample = SAFE_CAST(Sample, emuchannelreal[0]->mSound); + + if (0) + { + } + #ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + if (sample->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + result = mDSPCodecPool_MPEG.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (sample->mFormat == FMOD_SOUND_FORMAT_XMA) + { + result = mDSPCodecPool_XMA.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + result = mDSPCodecPool_ADPCM.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (sample->mFormat == FMOD_SOUND_FORMAT_CELT) + { + result = mDSPCodecPool_CELT.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + else if (sample->mFormat == FMOD_SOUND_FORMAT_PCM16) + { + result = mDSPCodecPool_RAW.areAnyFree(); + } + #endif + #endif + else + { + result = FMOD_ERR_FORMAT; + } + + if (result == FMOD_OK) + { + break; + } + else + { + /* + If we stole this channel would it free up a dspcodec of the same type that we want? + */ + if (currentmode & FMOD_CREATECOMPRESSEDSAMPLE + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + || (emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16) + #endif + ) + { + ChannelReal *tempcr[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + + realchannel[channelsfound]->getRealChannel(tempcr, 0); + + Sample *currentsample = SAFE_CAST(Sample, tempcr[0]->mSound); + + if (currentsample->mFormat == sample->mFormat) + { + result = FMOD_OK; + break; + } + } + } + } + else + { + result = FMOD_OK; + break; + } + } + else + { + result = FMOD_OK; + break; + } + } + } + + realcurrent[realtype] = realcurrent[realtype]->getPrev(); + + } while (1); + + if (result == FMOD_OK) + { + int found = 0; + ChannelReal *tempchannelreal[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + + realchannel[channelsfound]->getRealChannel(tempchannelreal, &found); + + for (count = 0; count < found; count++) + { + realchannelreal[realchannelsfound] = tempchannelreal[count]; + realchannelsfound ++; + } + channelsfound++; + + /* + Ok, if we havent got enough 'realchannelreal's for this virtual channel to become active still, + start stealing multiple 'realchannel's (and their child 'realchannelreal's) :| + */ + if (realchannelsfound < realchannelsneeded) + { + realcurrent[realtype] = realcurrent[realtype]->getPrev(); + goto tryformore; + } + } + else + { + realchannel[channelsfound] = 0; + } + } + + /* + ============================================================================ + SWAP THE REALCHANNELS AROUND + ============================================================================ + */ + if (realchannelsfound >= realchannelsneeded) + { + FMOD_CHANNEL_INFO emuinfo; + FMOD_CHANNEL_INFO realinfo[FMOD_CHANNEL_MAXREALSUBCHANNELS]; + FMOD_MODE mode; + int emulatedtoswap = channelsfound; + + emuchannel->getMode(&mode); + + /* + ============================================================================ + Step 1. Get information from the real and emulated channel before swapping. + ============================================================================ + */ + + /* + If there is a real voice involved, stop it. + */ + if (channelsfound) + { + for (count = 0; count < channelsfound; count++) + { + realchannel[count]->getChannelInfo(&realinfo[count]); + realchannel[count]->stopEx(CHANNELI_STOPFLAG_DONTFREELEVELS); + } + + /* + Kill off any extra voices that we stole but aren't used, this will put them back in the 'free' pool. + */ + for (count = realchannelsneeded; count < realchannelsfound; count++) + { + realchannelreal[count]->mFlags &= ~(CHANNELREAL_FLAG_IN_USE | CHANNELREAL_FLAG_ALLOCATED | CHANNELREAL_FLAG_PLAYING | CHANNELREAL_FLAG_PAUSED); + realchannelreal[count]->mFlags |= CHANNELREAL_FLAG_STOPPED; + } + } + else + { + emuchannel->getChannelInfo(&emuinfo); + emuchannel->stopEx(CHANNELI_STOPFLAG_DONTFREELEVELS); + + /* + ==================================================================== + Step 2. Convert 'emulatedchannel' to 'real' and re-start it. + + Note: After this 'realchannel' will be pointing to an emulated voice + and 'emuchannel' will be pointing to a real voice! + ==================================================================== + */ + #ifdef FMOD_SUPPORT_STREAMING + // STREAM SPECIAL CASE GOING FROM EMULATED TO REAL. + if (emuinfo.mSound && emuinfo.mSound->mMode & FMOD_CREATESTREAM) + { + Stream *stream = SAFE_CAST(Stream, emuinfo.mSound); + ChannelStream *channelstream = stream->mChannel; + + emuchannel->mRealChannel[0] = channelstream; + + channelstream->mNumRealChannels = realchannelsneeded; + for (count = 0; count < realchannelsneeded; count++) + { + channelstream->mRealChannel[count] = realchannelreal[count]; /* Set the channelstream's realchannel (the dsound/software/emu channel). */ + channelstream->mRealChannel[count]->mSubChannelIndex = count; + } + } + else + #endif + { + // ORDINARY SAMPLE GOING FROM EMULATED TO REAL. + emuchannel->mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL; + emuchannel->mNumRealChannels = realchannelsneeded; + for (count = 0; count < realchannelsneeded; count++) + { + emuchannel->mRealChannel[count] = realchannelreal[count]; + emuchannel->mRealChannel[count]->mSubChannelIndex = count; + } + } + + if (emuinfo.mDSP) + { + result = emuchannel->play(emuinfo.mDSP, true, false, false); + } + else + { + result = emuchannel->play(emuinfo.mSound, true, false, false); + } + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::updateChannels", "swapped channel failed to play. ZOMBIE CHANNEL!\n")); + } + emuchannel->setChannelInfo(&emuinfo); + emuchannel->setChannelGroup(emuchannel->mChannelGroup); + emuchannel->setPaused(emuinfo.mPaused); + } + + /* + ==================================================================== + Step 3. Convert 'realchannel' to 'emulated' and re-start it. + ==================================================================== + */ + for (count = 0; count < emulatedtoswap; count++) + { + /* + ok we've done a swap with an emulated voice, now we'll have to create new emulated voices + */ + result = mEmulated->getFreeChannel(mode, emuchannelreal, 1, 1, 0); + + /* + This channel is going from real to emulated, so just give it 1 subchannel and point it to 1 emulated voice + */ + realchannel[count]->mNumRealChannels = 1; + realchannel[count]->mRealChannel[0] = emuchannelreal[0]; + + if (realinfo[count].mSound) + { + result = realchannel[count]->play(realinfo[count].mSound->mSubSampleParent, true, false, false); + realchannel[count]->setChannelInfo(&realinfo[count]); + realchannel[count]->setChannelGroup(realchannel[count]->mChannelGroup); + realchannel[count]->setPaused(realinfo[count].mPaused); + } + else if (realinfo[count].mDSP) + { + result = realchannel[count]->play(realinfo[count].mDSP, true, false, false); + realchannel[count]->setChannelInfo(&realinfo[count]); + realchannel[count]->setChannelGroup(realchannel[count]->mChannelGroup); + realchannel[count]->setPaused(realinfo[count].mPaused); + } + + emucurrent = (ChannelI *)emucurrent->getNext(); /* Don't let it try and swap the same channel again. */ + } + } + else + { + emuchannel->mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL; /* Ok, it didnt swap, its going to come in virtual now. */ + } + + } while (1); + } + } + + return FMOD_OK; +} + +#ifndef FMOD_STATICFORPLUGINS +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::updateSoundGroups(int delta) +{ + SoundGroupI *soundgroup; + + FMOD_OS_CriticalSection_Enter(gSoundListCrit); + { + soundgroup = (SoundGroupI *)mSoundGroupUsedHead.getNext(); + while (soundgroup != &mSoundGroupUsedHead) + { + int numaudible = 0; + SoundGroupI *next = (SoundGroupI *)soundgroup->getNext(); + + soundgroup->mPlayCount = 0; + + if (soundgroup->mMaxAudibleBehavior == FMOD_SOUNDGROUP_BEHAVIOR_MUTE) + { + soundgroup->getNumPlaying(&numaudible); + } + + if (numaudible) + { + LinkedListNode *current; + + current = soundgroup->mChannelListHead.getNext(); + while (current != &soundgroup->mChannelListHead) + { + ChannelI *channel; + LinkedListNode *next; + + channel = (ChannelI *)current->getData(); + next = current->getNext(); + + if (soundgroup->mMaxAudibleBehavior == FMOD_SOUNDGROUP_BEHAVIOR_MUTE && soundgroup->mMaxAudible >= 0) + { + soundgroup->mPlayCount++; + if (soundgroup->mPlayCount > soundgroup->mMaxAudible) + { + channel->mFadeTarget = 0.0f; + } + else + { + channel->mFadeTarget = 1.0f; + } + } + + if (channel->mFadeVolume != channel->mFadeTarget) + { + if (soundgroup->mFadeSpeed < 0.001f) + { + channel->mFadeVolume = channel->mFadeTarget; + } + else + { + if (channel->mFadeVolume < channel->mFadeTarget) + { + channel->mFadeVolume += ((float)delta / (soundgroup->mFadeSpeed * 1000.0f)); + if (channel->mFadeVolume > channel->mFadeTarget) + { + channel->mFadeVolume = channel->mFadeTarget; + } + } + if (channel->mFadeVolume > channel->mFadeTarget) + { + channel->mFadeVolume -= ((float)delta / (soundgroup->mFadeSpeed * 1000.0f)); + if (channel->mFadeVolume < channel->mFadeTarget) + { + channel->mFadeVolume = channel->mFadeTarget; + } + } + } + + channel->setVolume(channel->mVolume, true); + } + + current = next; + } + } + else + { + /* + Put back on the free list. + */ + soundgroup->removeNode(); + soundgroup->addAfter(&mSoundGroupFreeHead); + } + soundgroup = next; + } + } + FMOD_OS_CriticalSection_Leave(gSoundListCrit); + + return FMOD_OK; +} +#endif + + +#ifdef FMOD_SUPPORT_VSTPLUGIN +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::updateVSTPlugins() +{ + if (!mVSTPluginsListHead.isEmpty()) + { + LinkedListNode *dspvstnode = mVSTPluginsListHead.getNext(); + while (dspvstnode != &mVSTPluginsListHead) + { + DSPVSTPlugin *dspvst = (DSPVSTPlugin *)dspvstnode->getData(); + + /* + Call VST idle function + */ + #ifdef FMOD_SUPPORT_DLLS + if (dspvst->mDescription.configidle) + { + dspvst->mDescription.configidle((FMOD_DSP_STATE *)dspvst); + } + #endif + + dspvstnode = dspvstnode->getNext(); + } + } + + return FMOD_OK; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::findChannel(FMOD_CHANNELINDEX id, SoundI *sound, ChannelI **channel) +{ + ChannelI *chan = 0; + int count, subchannels, found; + ChannelReal *realchannel[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + FMOD_RESULT result; + FMOD_MODE mode; + + if (!channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = sound->getMode(&mode); + if (result != FMOD_OK) + { + return result; + } + + /* + If the sound is unique, stop any instances of it + */ + if (mode & FMOD_UNIQUE) + { + result = stopSound(sound); + if (result != FMOD_OK) + { + return result; + } + } + + if (id == FMOD_CHANNEL_REUSE) + { + chan = *channel; + + if (!chan) + { + id = FMOD_CHANNEL_FREE; + } + else + { + if (!chan->mRealChannel[0] || chan->mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED) + { + id = (FMOD_CHANNELINDEX)chan->mIndex; + } + else + { + chan->stopEx(CHANNELI_STOPFLAG_CALLENDCALLBACK | CHANNELI_STOPFLAG_RESETCHANNELGROUP); + } + } + } + + if (id != FMOD_CHANNEL_REUSE) /* id might be changed above, so it is not a simple else statement. */ + { + *channel = 0; + + if (id != FMOD_CHANNEL_FREE) + { + if (id < 0 || id >= mNumChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + chan = &mChannel[id]; + chan->stop(); + } + else + { + if (!mChannelFreeListHead.isEmpty()) + { + chan = SAFE_CAST(ChannelI, mChannelFreeListHead.getNext()); + } + else /* There would be no stopped voices otherwise they would be in the freelist, so find the lowest priority channel and steal it. */ + { + LinkedListNode *node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev()); + + if (mChannelSortedListHead.isEmpty()) + { + return FMOD_ERR_CHANNEL_ALLOC; /* This usually only happens if someone sets System::init to 0 channels. */ + } + + node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev()); + + chan = (ChannelI *)node->getData(); + chan->stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_CALLENDCALLBACK | CHANNELI_STOPFLAG_RESETCHANNELGROUP); + } + } + + chan->removeNode(); + chan->addBefore(&mChannelUsedListHead); + } + +#ifdef FMOD_SUPPORT_STREAMING + if (mode & FMOD_CREATESTREAM) + { + Stream *stream = SAFE_CAST(Stream, sound); + Sample *sample = SAFE_CAST(Sample, stream->mSample); + + if (sample) + { + subchannels = sample->mNumSubSamples; + } + else + { + subchannels = 0; + } + } + else +#endif + { + Sample *sample = SAFE_CAST(Sample, sound); + + subchannels = sample->mNumSubSamples; + } + + if (!subchannels) + { + subchannels = 1; + } + + /* + Assign a 'REAL' channel to the virtual channel + */ + if (((mode & FMOD_SOFTWARE || mOutputType == FMOD_OUTPUTTYPE_OPENAL) && mode & FMOD_CREATECOMPRESSEDSAMPLE) +#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + || (sound->mFormat == FMOD_SOUND_FORMAT_PCM16) +#endif + ) + { + #ifdef FMOD_SUPPORT_SOFTWARE + + /* + Before looking for a software channel (which there might be available), see if it is a realtime compressed sample. + */ + if (0) + { + } +#ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + else if (sound->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + result = mDSPCodecPool_MPEG.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_XMA + else if (sound->mFormat == FMOD_SOUND_FORMAT_XMA) + { + result = mDSPCodecPool_XMA.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + else if (sound->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + result = mDSPCodecPool_ADPCM.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_CELT + else if (sound->mFormat == FMOD_SOUND_FORMAT_CELT) + { + result = mDSPCodecPool_CELT.areAnyFree(); + } + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + else if (sound->mFormat == FMOD_SOUND_FORMAT_PCM16) + { + result = mDSPCodecPool_RAW.areAnyFree(); + } + #endif +#endif + else + { + result = FMOD_ERR_FORMAT; + } + } + + if ((mode & FMOD_CREATESTREAM) && sound->mCodec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES) + { + result = sound->mCodec->getHardwareMusicChannel(&realchannel[0]); + if (result == FMOD_OK) + { + found = 1; + } + } + else if (mode & FMOD_SOFTWARE || !mOutput->mDescription.createsample) + { + if (result == FMOD_OK) + { + result = mSoftware->getFreeChannel(mode, realchannel, subchannels, sound->mChannels, &found); + } + + #else + + result = FMOD_ERR_NEEDSSOFTWARE; + + #endif + } + else + { + result = mOutput->getFreeChannel(mode, realchannel, subchannels, sound->mChannels, &found); + } + + if (result != FMOD_OK || found != subchannels) + { + subchannels = 1; + result = mEmulated->getFreeChannel(mode, realchannel, subchannels, sound->mChannels, 0); + } + if (result != FMOD_OK) + { + return result; + } + + if (realchannel[0]->mOutput == mEmulated) + { + chan->mFlags |= CHANNELI_FLAG_JUSTWENTVIRTUAL; + } + + #ifdef FMOD_SUPPORT_STREAMING + if (mode & FMOD_CREATESTREAM) + { + if (realchannel[0]->mOutput == mEmulated) + { + chan->mNumRealChannels = 1; + chan->mRealChannel[0] = realchannel[0]; + chan->mRealChannel[0]->mSubChannelIndex = 0; + } + else + { + Stream *stream = SAFE_CAST(Stream, sound); + ChannelStream *channelstream = stream->mChannel; + + chan->mNumRealChannels = 1; + chan->mRealChannel[0] = channelstream; /* Set the ChannelI's realchannel (the channelstream). */ + + channelstream->mNumRealChannels = subchannels; + channelstream->mSubChannelIndex = 0; + for (count = 0; count < subchannels; count++) + { + channelstream->mRealChannel[count] = realchannel[count]; /* Set the channelstream's realchannel (the dsound/software/emu channel). */ + channelstream->mRealChannel[count]->mSubChannelIndex = count; + } + } + } + else + #endif + { + chan->mNumRealChannels = subchannels; + for (count = 0; count < subchannels; count++) + { + chan->mRealChannel[count] = realchannel[count]; + chan->mRealChannel[count]->mSubChannelIndex = count; + } + } + + *channel = chan; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::findChannel(FMOD_CHANNELINDEX id, DSPI *dsp, ChannelI **channel) +{ + ChannelI *chan = 0; + ChannelReal *realchannel[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 }; + FMOD_RESULT result; + + if (!channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (id == FMOD_CHANNEL_REUSE) + { + chan = *channel; + + if (!chan) + { + id = FMOD_CHANNEL_FREE; + } + else + { + if (chan->mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED) + { + id = (FMOD_CHANNELINDEX)chan->mIndex; + } + else + { + chan->stopEx(CHANNELI_STOPFLAG_RESETCHANNELGROUP); + } + } + } + + if (id != FMOD_CHANNEL_REUSE) /* id might be changed above, so it is not a simple else statement. */ + { + *channel = 0; + + if (id != FMOD_CHANNEL_FREE) + { + chan = &mChannel[id]; + chan->stop(); + } + else + { + if (!mChannelFreeListHead.isEmpty()) + { + chan = SAFE_CAST(ChannelI, mChannelFreeListHead.getNext()); + } + else /* There would be no stopped voices otherwise they would be in the freelist, so find the lowest priority channel and steal it. */ + { + LinkedListNode *node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev()); + + if (mChannelSortedListHead.isEmpty()) + { + return FMOD_ERR_CHANNEL_ALLOC; /* This usually only happens if someone sets System::init to 0 channels. */ + } + + node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev()); + + chan = (ChannelI *)node->getData(); + + chan->stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP); + } + } + + chan->removeNode(); + chan->addBefore(&mChannelUsedListHead); + } + + /* + Assign a 'REAL' channel to the virtual channel + */ +#ifdef FMOD_SUPPORT_SOFTWARE + result = mSoftware->getFreeChannel(FMOD_SOFTWARE, realchannel, 1, 1, 0); + if (result != FMOD_OK) +#endif + { + result = mEmulated->getFreeChannel(FMOD_SOFTWARE, realchannel, 1, 1, 0); + } + + if (result != FMOD_OK) + { + return result; + } + + chan->mRealChannel[0] = realchannel[0]; + chan->mNumRealChannels = 1; + + *channel = chan; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample_out) +{ + FMOD_RESULT result; + Output *output; + Sample *sample = 0; + AutoRelease<Sample> sample_cleanup; + int channels, subsamples, count; + unsigned int loopstart = 0, loopend = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "mode %08x length %d samples, lengthbytes %d\n", mode, waveformat ? waveformat->lengthpcm : 0, waveformat ? waveformat->lengthbytes : 0)); + + if (!sample_out) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (waveformat->lengthpcm == (unsigned int)-1) + { + return FMOD_ERR_MEMORY; + } + + /* + Check hardware/software bits in user mode and waveformat mode. + */ + if (!(mode & (FMOD_HARDWARE | FMOD_SOFTWARE))) + { + int num2d, num3d; + + mode |= (waveformat->mode & (FMOD_HARDWARE | FMOD_SOFTWARE | FMOD_2D | FMOD_3D)); + + #if defined(PLATFORM_PS2) + if (waveformat->format != FMOD_SOUND_FORMAT_VAG) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "Sound format = %d, not VAG so using software.\n", waveformat->format)); + mode |= FMOD_SOFTWARE; + } + #endif + + getHardwareChannels(&num2d, &num3d, 0); + + if (mode & FMOD_HARDWARE) + { + if (mode & FMOD_3D) + { + if (!num3d) + { + mode &= ~FMOD_HARDWARE; + mode |= FMOD_SOFTWARE; + } + } + else + { + if (!num2d) + { + mode &= ~FMOD_HARDWARE; + mode |= FMOD_SOFTWARE; + } + } + } + } + + if (mode & FMOD_SOFTWARE) + { +#ifdef FMOD_SUPPORT_SOFTWARE + if (!mSoftware) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "ERROR - Software not initialized\n")); + return FMOD_ERR_NEEDSSOFTWARE; + } + + mode &= ~FMOD_HARDWARE; + output = mSoftware; +#else + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "ERROR - Software is disabled. Tried to create a software sample.\n")); + return FMOD_ERR_NEEDSSOFTWARE; +#endif + } + else + { + mode |= FMOD_HARDWARE; + output = mOutput; + } + + /* + Check 2D/3D bits in user mode and waveformat mode. + */ + if (!(mode & (FMOD_2D | FMOD_3D))) + { + mode |= (waveformat->mode & (FMOD_2D | FMOD_3D)); + } + + if (mode & FMOD_3D) + { + mode &= ~FMOD_2D; + } + else + { + mode |= FMOD_2D; + } + + /* + Check loop bits in user mode and waveformat mode + */ + if (!(mode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI))) + { + mode |= (waveformat->mode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)); + } + + if (mode & FMOD_LOOP_NORMAL) + { + mode &= ~FMOD_LOOP_OFF; + mode &= ~FMOD_LOOP_BIDI; + mode |= FMOD_LOOP_NORMAL; + } + else if (mode & FMOD_LOOP_BIDI) + { + mode &= ~FMOD_LOOP_OFF; + mode |= FMOD_LOOP_BIDI; + } + else + { + mode |= FMOD_LOOP_OFF; + } + + if (waveformat->mode & FMOD_CREATECOMPRESSEDSAMPLE) + { + mode |= FMOD_CREATECOMPRESSEDSAMPLE; + mode &= ~FMOD_CREATESAMPLE; + } + + /* + Create the sample + */ + channels = waveformat->channels; + + /* + Check for valid number of channels + */ + if (channels > FMOD_CHANNEL_MAXREALSUBCHANNELS && channels > (output->mDescription.getsamplemaxchannels ? output->mDescription.getsamplemaxchannels(output, mode, waveformat->format) : 1)) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + /* + If a voice is 3d and hardware, and cannot support multichannel input, then make it a multi-mono sample sound. + When these samples are played, they take up multiple mono voices. + */ + if (channels > 1 && channels > (output->mDescription.getsamplemaxchannels ? output->mDescription.getsamplemaxchannels(output, mode, waveformat->format) : 1)) + { + subsamples = channels; + + if (*sample_out) + { + sample = *sample_out; + } + else + { + sample = FMOD_Object_Calloc(Sample); + sample_cleanup = sample; + } + if (!sample) + { + return FMOD_ERR_MEMORY; + } + + sample->mNumSubSamples = channels; + + if (!sample->mName && !(mode & FMOD_LOWMEM)) + { + sample->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN); + if (!sample->mName) + { + return FMOD_ERR_MEMORY; + } + } + + /* + Set the default values + */ + if (sample->mName) + { + FMOD_strcpy(sample->mName, waveformat->name); + } + sample->mDefaultFrequency = (float)waveformat->frequency; + sample->mDefaultChannelMask = waveformat->channelmask; + sample->mLength = waveformat->lengthpcm; + sample->mMode = mode; + sample->mLoopStart = 0; + sample->mLoopLength = sample->mLength; + sample->mFormat = waveformat->format; + sample->mChannels = channels; + sample->mCodec = 0; + sample->mType = FMOD_SOUND_TYPE_USER; + sample->mSystem = this; + sample->mMinDistance = 1.0f * mDistanceScale; + sample->mMaxDistance = 10000.0f * mDistanceScale; + sample->mLockBuffer = mMultiSubSampleLockBuffer.alloc(SOUND_READCHUNKSIZE); + if (!sample->mLockBuffer) + { + return FMOD_ERR_MEMORY; + } + + channels = 1; + } + else + { + subsamples = 1; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "subsamples = %d, channels = %d\n", subsamples, channels)); + + for (count = 0; count < subsamples; count++) + { + Sample *subsample = subsamples == 1 ? *sample_out : 0; + FMOD_CODEC_WAVEFORMAT subsamplewaveformat; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "subsample %d. output = %p\n", count, output)); + + FMOD_memcpy(&subsamplewaveformat, waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + subsamplewaveformat.channels = channels; + + if (output->mDescription.createsample) + { +#ifdef FMOD_SUPPORT_SOFTWARE + output->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "use hw\n", count)); + + result = output->mDescription.createsample(output, mode, &subsamplewaveformat, &subsample); + mCreatedHardwareSample = true; + } + else + { +#ifdef FMOD_SUPPORT_SOFTWARE + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "mSoftware = %p\n", mSoftware)); + + result = mSoftware->createSample(mode, &subsamplewaveformat, &subsample); +#else + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "ERROR - Software is disabled. Tried to create a software sample.\n")); + result = FMOD_ERR_NEEDSSOFTWARE; +#endif + } + if (result != FMOD_OK) + { + if(subsamples > 1) + { + /* free the other subsamples created in this loop */ + sample->release(false); + } + else if (subsample && subsample != *sample_out) + { + /* don't free sample_out that was passed in */ + subsample->release(); + } + return result; + } + + if (subsamples > 1) + { + sample->mSubSample[count] = subsample; + } + else + { + if (subsample != *sample_out) + { + sample_cleanup = subsample; + } + sample = subsample; + + if (!sample->mName && !(mode & FMOD_LOWMEM)) + { + sample->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN); + if (!sample->mName) + { + return FMOD_ERR_MEMORY; + } + } + } + + /* + Set the default values + */ + if (subsample->mName) + { + FMOD_strcpy(subsample->mName, waveformat->name); + } + subsample->mDefaultFrequency = (float)waveformat->frequency; + subsample->mDefaultChannelMask = waveformat->channelmask; + subsample->mMode = mode; + subsample->mLoopStart = 0; + subsample->mLoopLength = subsample->mLength; + subsample->mFormat = waveformat->format; + subsample->mChannels = channels; + subsample->mCodec = 0; + subsample->mType = FMOD_SOUND_TYPE_USER; + subsample->mSystem = this; + subsample->mMinDistance = 1.0f * mDistanceScale; + subsample->mMaxDistance = 10000.0f * mDistanceScale; + subsample->mSubSampleParent = sample; + } + + loopstart = waveformat->loopstart; + loopend = waveformat->loopend; + if (!loopend) + { + loopend = sample->mLength - 1; + } + sample->setLoopPoints(loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM); + + *sample_out = sample; + sample_cleanup.releasePtr(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createSoundInternal(const char *name_or_data, FMOD_MODE mode_in, unsigned int buffersize, FMOD_TIMEUNIT buffersizetype, FMOD_CREATESOUNDEXINFO *exinfo, bool calledfromasync, SoundI **sound) +{ + FMOD_RESULT result; + SoundI **subsound = 0; + AutoFree subsound_cleanup; + SoundI *soundi = 0; + Codec *codec = 0; + AutoRelease<Codec> codec_cleanup; + Metadata *metadata = 0; + File *file = 0; + AutoFree file_cleanup; + AutoClose<File> file_closer; + FMOD_CODEC_WAVEFORMAT waveformat; + bool netfile = false; + int count; + int numsubsounds; + int numcodecs; + int maxchannels = 0; + FMOD_MODE originalmode = mode_in; + void *filebufferstack = 0; + char filebufferstackmem[(16*1024) + 32]; /* +32 for wii/gc alignment */ + + #ifdef FMOD_DEBUG + if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "memory = %p : mode %08x\n", name_or_data, mode_in)); + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "filename = %s : mode %08x\n", name_or_data ? name_or_data : "", mode_in)); + } + if (exinfo) + { + if (exinfo->cbsize) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->cbsize = %d\n", exinfo->cbsize)); + } + if (exinfo->length) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->length = %d\n", exinfo->length)); + } + if (exinfo->fileoffset) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->fileoffset = %d\n", exinfo->fileoffset)); + } + if (exinfo->numchannels) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->numchannels = %d\n", exinfo->numchannels)); + } + if (exinfo->defaultfrequency) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->defaultfrequency = %d\n", exinfo->defaultfrequency)); + } + if (exinfo->format) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->format = %d\n", exinfo->format)); + } + if (exinfo->decodebuffersize) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->decodebuffersize = %d\n", exinfo->decodebuffersize)); + } + if (exinfo->initialsubsound) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialsubsound = %d\n", exinfo->initialsubsound)); + } + if (exinfo->numsubsounds) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->numsubsounds = %d\n", exinfo->numsubsounds)); + } + if (exinfo->inclusionlist) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->inclusionlist = %p\n", exinfo->inclusionlist)); + } + if (exinfo->inclusionlistnum) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->inclusionlistnum = %d\n", exinfo->inclusionlistnum)); + } + if (exinfo->pcmreadcallback) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->pcmreadcallback = %p\n", exinfo->pcmreadcallback)); + } + if (exinfo->pcmsetposcallback) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->pcmsetposcallback = %p\n", exinfo->pcmsetposcallback)); + } + if (exinfo->nonblockcallback) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->nonblockcallback = %p\n", exinfo->nonblockcallback)); + } + if (exinfo->dlsname) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->dlsname = %s\n", exinfo->dlsname)); + } + if (exinfo->encryptionkey) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->encryptionkey = %s\n", exinfo->encryptionkey)); + } + if (exinfo->maxpolyphony) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->maxpolyphony = %d\n", exinfo->maxpolyphony)); + } + if (exinfo->userdata) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userdata = %p\n", exinfo->userdata)); + } + if (exinfo->suggestedsoundtype) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->suggestedsoundtype = %d\n", exinfo->suggestedsoundtype)); + } + if (exinfo->userdata) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userdata = %p\n", exinfo->userdata)); + } + if (exinfo->useropen) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->useropen = %p\n", exinfo->useropen)); + } + if (exinfo->userclose) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userclose = %p\n", exinfo->userclose)); + } + if (exinfo->userread) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userread = %p\n", exinfo->userread)); + } + if (exinfo->userseek) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userseek = %p\n", exinfo->userseek)); + } + if (exinfo->speakermap) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->speakermap = %d\n", exinfo->speakermap)); + } + if (exinfo->initialsoundgroup) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialsoundgroup = %d\n", exinfo->initialsoundgroup)); + } + if (exinfo->initialseekposition) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialseekposition = %d\n", exinfo->initialseekposition)); + } + if (exinfo->initialseekpostype) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialseekpostype = %d\n", exinfo->initialseekpostype)); + } + } + #endif + + if (!sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!(mode_in & FMOD_NONBLOCKING)) + { + *sound = 0; + } + + /* + Check this is a valid exinfo structure. Future revisions may want to set version numbers + here to account for backwards compatibility. + */ + if (exinfo && exinfo->cbsize != sizeof(FMOD_CREATESOUNDEXINFO)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "VERSION MISMATCH : cbsize in code (%d) does not match cbsize in lib (%d). Make sure you are not mixing library and header versions of FMOD!\n", exinfo->cbsize, sizeof(FMOD_CREATESOUNDEXINFO))); + return FMOD_ERR_INVALID_PARAM; + } + + /* + 1. Stream overrides sample && realtime sample. + 2. Realtime sample overrides sample. + */ + if (mode_in & FMOD_CREATESTREAM) + { + mode_in &= ~FMOD_CREATESAMPLE; + mode_in &= ~FMOD_CREATECOMPRESSEDSAMPLE; /* Decode to PCM through stream thread. */ + } + else if (mode_in & FMOD_CREATECOMPRESSEDSAMPLE) + { + mode_in &= ~FMOD_CREATESAMPLE; + } + + /* + Turn off 3d flags if 2d is set + */ + if (mode_in & FMOD_2D) + { + mode_in &= ~(FMOD_3D_HEADRELATIVE | FMOD_3D_WORLDRELATIVE | FMOD_3D_LOGROLLOFF | FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF); + } + + /* + Turn on 3d if one of the 3d flags is used. + */ + if (mode_in & (FMOD_3D_HEADRELATIVE | FMOD_3D_WORLDRELATIVE | FMOD_3D_LOGROLLOFF | FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF)) + { + mode_in |= FMOD_3D; + } + + /* + Fall back to software if hardware doesnt exist. + */ + if (!(mode_in & FMOD_SOFTWARE)) + { + int num2d, num3d; + + getHardwareChannels(&num2d, &num3d, 0); + + if (mode_in & FMOD_3D) + { + if (!num3d) + { + mode_in &= ~FMOD_HARDWARE; + mode_in |= FMOD_SOFTWARE; + } + } + else + { + if (!num2d) + { + mode_in &= ~FMOD_HARDWARE; + mode_in |= FMOD_SOFTWARE; + } + } + } + + if (mode_in & FMOD_OPENUSER || mode_in & FMOD_OPENRAW) + { + if (!exinfo) + { + return FMOD_ERR_INVALID_PARAM; + } + if (exinfo->format == FMOD_SOUND_FORMAT_NONE || + exinfo->numchannels <= 0 || + exinfo->defaultfrequency == 0) + { + return FMOD_ERR_INVALID_PARAM; + } + } + + /* + First open the file + */ + if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) + { + file_cleanup = file = FMOD_Object_Alloc(MemoryFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + file->init(this, 0, 0); /* Set blocksize to 0 to stop it buffering, we don't need it. */ + } + else if (mode_in & FMOD_OPENUSER) + { + file_cleanup = file = FMOD_Object_Alloc(NullFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + file->init(this, 0, mBufferSize); + mode_in &= ~FMOD_OPENRAW; /* Just in case the user passed this in. */ + } + else + { + if (exinfo && exinfo->useropen && exinfo->userclose && exinfo->userread && exinfo->userseek && !exinfo->ignoresetfilesystem) + { + file_cleanup = file = FMOD_Object_Alloc(UserFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + result = ((UserFile *)file)->setUserCallbacks(exinfo->useropen, exinfo->userclose, exinfo->userread, exinfo->userseek); + CHECK_RESULT(result); + + file->init(this, 0, mBufferSize); + } + else if (mUsesUserCallbacks && (!exinfo || !exinfo->ignoresetfilesystem)) + { + file_cleanup = file = FMOD_Object_Alloc(UserFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + file->init(this, 0, mBufferSize); + } +#ifdef FMOD_SUPPORT_NET + else if ((mode_in & FMOD_UNICODE && + (!FMOD_strnicmpW((const short *)(L"http://"), (const short *)name_or_data, 7) || + !FMOD_strnicmpW((const short *)(L"http:\\\\"), (const short *)name_or_data, 7) || + !FMOD_strnicmpW((const short *)(L"https://"), (const short *)name_or_data, 8) || + !FMOD_strnicmpW((const short *)(L"https:\\\\"), (const short *)name_or_data, 8) || + !FMOD_strnicmpW((const short *)(L"mms://"), (const short *)name_or_data, 6) || + !FMOD_strnicmpW((const short *)(L"mms:\\\\"), (const short *)name_or_data, 6))) || + (!FMOD_strnicmp("http://", name_or_data, 7) || + !FMOD_strnicmp("http:\\\\", name_or_data, 7) || + !FMOD_strnicmp("https://", name_or_data, 8) || + !FMOD_strnicmp("https:\\\\", name_or_data, 8) || + !FMOD_strnicmp("mms://", name_or_data, 6) || + !FMOD_strnicmp("mms:\\\\", name_or_data, 6))) + { + file_cleanup = file = FMOD_Object_Alloc(NetFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + file->init(this, 0, mBufferSize); + netfile = true; + } +#endif +#ifdef FMOD_SUPPORT_CDDA + else if (FMOD_OS_CDDA_IsDeviceName((char *)name_or_data)) + { + file_cleanup = file = FMOD_Object_Alloc(CddaFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + result = ((CddaFile *)file)->init((mode_in & FMOD_CDDA_FORCEASPI) ? true : false, (mode_in & FMOD_CDDA_JITTERCORRECT) ? true : false); + CHECK_RESULT(result); + + file->init(this, 0, mBufferSize); + } +#endif + else + { + file_cleanup = file = FMOD_Object_Alloc(DiskFile); + CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK); + + file->init(this, 0, mBufferSize); + } + } + + if (*sound && netfile) + { + (*sound)->mOpenState = FMOD_OPENSTATE_CONNECTING; + } + + if (mode_in & FMOD_CREATESTREAM && file->mBlockSize && file->mBlockSize <= (16*1024)) + { + filebufferstack = (void *)FMOD_ALIGNPOINTER(filebufferstackmem, 32); + + /* + Stop it from doing an alloc, so that it doesn't fragment memory when it reallocs later. + If it failed (not enough stack) it will just do the normal realloc/heap thing. + */ + file->setBuffer(filebufferstack); + } + + if (mode_in & FMOD_CREATESTREAM) + { + /* + Set the streaming flag. This is for PS3 FIOS. + */ + file->mFlags |= FMOD_FILE_STREAMING; + } + + result = file->open(name_or_data, exinfo ? exinfo->length : 0, (mode_in & FMOD_UNICODE) ? 1 : 0, exinfo ? exinfo->encryptionkey : 0); + if (result != FMOD_OK) + { + return result; + } + file_closer = file; + + if (*sound && netfile) + { + (*sound)->mOpenState = FMOD_OPENSTATE_BUFFERING; + } + + result = file->setStartOffset(exinfo ? exinfo->fileoffset: 0); + if (result != FMOD_OK) + { + return result; + } + + /* + If the file exists, search for the appropriate codec. + */ + result = mPluginFactory->getNumCodecs(&numcodecs); + if (result != FMOD_OK) + { + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "%d codecs found. Scan all until one succeeds\n", numcodecs)); + + result = FMOD_ERR_FORMAT; + + for (count = 0; count < numcodecs; count++) + { + FMOD_CODEC_DESCRIPTION_EX *codecdesc; + unsigned int handle; + + result = mPluginFactory->getCodecHandle(count, &handle); + if (result != FMOD_OK) + { + continue; + } + + result = mPluginFactory->getCodec(handle, &codecdesc); + if (result != FMOD_OK) + { + continue; + } + + if (exinfo) + { + int memberoffset = (int)((FMOD_UINT_NATIVE)&exinfo->suggestedsoundtype - (FMOD_UINT_NATIVE)exinfo); + + if (exinfo->cbsize > memberoffset && exinfo->suggestedsoundtype && !(mode_in & FMOD_OPENUSER)) + { + /* + Ignore any codec that isn't the suggested type, except when the suggested type + is MPEG, we want to allow the tag codec to process any tags in the file first + */ + if (codecdesc->mType != exinfo->suggestedsoundtype +#ifdef FMOD_SUPPORT_TAGS + && !(exinfo->suggestedsoundtype == FMOD_SOUND_TYPE_MPEG && codecdesc->mType == FMOD_SOUND_TYPE_TAG) +#endif + ) + { + continue; + } + } + } + + if (mode_in & FMOD_OPENRAW) + { + if (codecdesc->mType != FMOD_SOUND_TYPE_RAW) + { + codecdesc = 0; + continue; + } + } + else if (mode_in & FMOD_OPENUSER) + { + if (codecdesc->mType != FMOD_SOUND_TYPE_USER) + { + codecdesc = 0; + continue; + } + } + else + { + if (codecdesc->mType == FMOD_SOUND_TYPE_RAW || codecdesc->mType == FMOD_SOUND_TYPE_USER) + { + codecdesc = 0; + continue; + } + } + + result = mPluginFactory->createCodec(codecdesc, &codec); + if (result != FMOD_OK) + { + if (result == FMOD_ERR_MEMORY) /* Better not just skip this error. */ + { + return result; + } + continue; + } + + codec->mFile = file; + codec->mMode = mode_in; + codec->mOriginalMode = originalmode; + codec->filehandle = file; + codec->filesize = file->mLength; + codec->mSystem = this; + codec->mFlags |= FMOD_CODEC_ACCURATELENGTH; + + result = codec->mDescription.open(codec, mode_in, exinfo); + + if (result == FMOD_OK) + { + mode_in = codec->mMode; /* The codec wanted to change the mode. */ + + result = codec->mDescription.getwaveformat(codec, 0, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + if (mode_in & FMOD_OPENUSER) + { + waveformat.format = exinfo->format; + waveformat.channels = exinfo->numchannels; + waveformat.frequency = exinfo->defaultfrequency; + + result = SoundI::getSamplesFromBytes(exinfo->length, &waveformat.lengthpcm, exinfo->numchannels, exinfo->format); + if (result != FMOD_OK) + { + return result; + } + + waveformat.blockalign = codec->waveformat[0].blockalign = 1; /* user codec can reference codec->waveformat here because it exists. */ + } + else if (mode_in & FMOD_OPENRAW) + { + unsigned int filesize; + + file->getSize(&filesize); + + waveformat.format = codec->waveformat[0].format = exinfo->format; /* raw codec can reference codec->waveformat here because it exists. */ + waveformat.channels = codec->waveformat[0].channels = exinfo->numchannels; /* raw codec can reference codec->waveformat here because it exists. */ + waveformat.frequency = codec->waveformat[0].frequency = exinfo->defaultfrequency; /* raw codec can reference codec->waveformat here because it exists. */ + + result = SoundI::getSamplesFromBytes(filesize, &waveformat.lengthpcm, exinfo->numchannels, exinfo->format); + if (result != FMOD_OK) + { + return result; + } + } + + maxchannels = waveformat.channels; + codec->mBlockAlign = waveformat.blockalign; /* keep the first one always */ + + if (codec->numsubsounds && mode_in & FMOD_CREATESTREAM) + { + for (count = 0; count < codec->numsubsounds; count++) + { + FMOD_CODEC_WAVEFORMAT waveformat2; + + result = codec->mDescription.getwaveformat(codec, count, &waveformat2); + if (result != FMOD_OK) + { + return result; + } + + if (waveformat2.channels > maxchannels) + { + maxchannels = waveformat2.channels; + codec->mBlockAlign = waveformat2.blockalign; + } + } + } + + #ifdef FMOD_SUPPORT_RAWCODEC + if (waveformat.format == FMOD_SOUND_FORMAT_PCM16) + { + if (!mDSPCodecPool_RAW.mNumDSPCodecs) + { + result = mDSPCodecPool_RAW.init(FMOD_DSP_CATEGORY_DSPCODECRAW, 256, mAdvancedSettings.maxPCMcodecs ? mAdvancedSettings.maxPCMcodecs : FMOD_ADVANCEDSETTINGS_MAXPCMCODECS); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + break; + } + +#ifdef FMOD_SUPPORT_TAGS + if (codec->mType == FMOD_SOUND_TYPE_TAG && !metadata) + { + metadata = codec->mMetadata; + } +#endif + + codec->mFile = 0; /* Dont let the release close the file */ + codec->mMetadata = 0; + codec->release(); + codec = 0; + + if (result != FMOD_ERR_FORMAT && result != FMOD_ERR_FILE_EOF) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Fatal error (%d) scanning the codecs. (ie not FMOD_ERR_FORMAT or FMOD_ERR_FILE_EOF)\n", result)); + + if (metadata) + { + metadata->release(); + } + + return result; + } + } + + if (!codec) + { + if (metadata) + { + metadata->release(); + } + return FMOD_ERR_FORMAT; + } + + numsubsounds = exinfo && exinfo->numsubsounds ? exinfo->numsubsounds : codec->numsubsounds; + + if (metadata) + { + if (codec->mMetadata) + { + codec->mMetadata->add(metadata); + metadata->release(); + } + else + { + codec->mMetadata = metadata; + } + } + + codec->getMetadataFromFile(); + + file_closer.releasePtr(); + file_cleanup.releasePtr(); + codec_cleanup = codec; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Format has %d subsounds.\n", numsubsounds)); + + if (numsubsounds > 0) + { + subsound = (SoundI **)FMOD_Memory_Calloc(numsubsounds * sizeof(SoundI *)); + if (!subsound) + { + return FMOD_ERR_MEMORY; + } + + subsound_cleanup = subsound; + } + + result = codec->mDescription.getwaveformat(codec, 0, &waveformat); + if (result != FMOD_OK) + { + return result; + } + + if (waveformat.channels > mMaxInputChannels) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + /* + Load as a stream if the codec told us to. + */ + if (codec->mDescription.defaultasstream && !(mode_in & FMOD_CREATESAMPLE)) + { + mode_in |= FMOD_CREATESTREAM; + } + + /* + The file has now been successfully opened via the codec, now load it or prepare a stream. + */ + if (codec->mType == FMOD_SOUND_TYPE_PLAYLIST) + { + /* + Just create an empty sound container if a playlist was loaded + */ + SoundI *soundcontainer = 0; + AutoRelease<SoundI> soundcontainer_cleanup; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Create sample for playlist\n")); + + if (mode_in & FMOD_NONBLOCKING) + { + soundcontainer = *sound; + } + else + { + soundcontainer = FMOD_Object_Calloc(Sample); + if (!soundcontainer) + { + return FMOD_ERR_MEMORY; + } + soundcontainer_cleanup = soundcontainer; + } + + if (!soundcontainer->mName && !(mode_in & FMOD_LOWMEM)) + { + soundcontainer->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN); + if (!soundcontainer->mName) + { + return FMOD_ERR_MEMORY; + } + } + + codec_cleanup.releasePtr(); + + soundcontainer->mSystem = this; + soundcontainer->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0; + soundcontainer->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0; + soundcontainer->mPostCallbackSound = (FMOD_SOUND *)soundcontainer; + soundcontainer->mType = codec->mType; + soundcontainer->mCodec = codec; + soundcontainer->mMinDistance = 1.0f * mDistanceScale; + soundcontainer->mMaxDistance = 10000.0f * mDistanceScale; + soundcontainer->mSubSound = 0; + soundcontainer->mNumSubSounds = 0; + soundcontainer->mSubSoundIndex = -1; + soundcontainer->mUserData = exinfo ? exinfo->userdata : 0; + + soundcontainer_cleanup.releasePtr(); + *sound = soundcontainer; + } + else +#ifdef FMOD_SUPPORT_STREAMING + if (mode_in & FMOD_CREATESTREAM) + { + unsigned int decodebuffersize; + unsigned int blocksize = 0; + Stream *stream; + Sample *sample = 0; + FMOD_MODE sample_mode = mode_in & ~(FMOD_CREATECOMPRESSEDSAMPLE); + FMOD_CODEC_WAVEFORMAT waveformatstream; + AutoRelease<Stream> stream_cleanup; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Create as FMOD_CREATESTREAM\n")); + + if (mode_in & FMOD_NONBLOCKING) + { + stream = SAFE_CAST(Stream, *sound); + } + else + { + stream = FMOD_Object_Calloc(Stream); + if (!stream) + { + return FMOD_ERR_MEMORY; + } + stream_cleanup = stream; + } + + if (!stream->mName && !(mode_in & FMOD_LOWMEM)) + { + stream->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN); + if (!stream->mName) + { + return FMOD_ERR_MEMORY; + } + } + + codec_cleanup.releasePtr(); + subsound_cleanup.releasePtr(); + + stream->mSubSound = subsound; + stream->mNumSubSounds = numsubsounds; + stream->mCodec = codec; + stream->mSystem = this; + + if (!(codec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES)) + { + stream->getSamplesFromBytes(codec->mBlockAlign, &blocksize, maxchannels, waveformat.format); + + /* + Calculate the PCM Buffersize in samples based on the user setting. + */ + decodebuffersize = exinfo ? exinfo->decodebuffersize : 0; + + if (!decodebuffersize) + { + decodebuffersize = mAdvancedSettings.defaultDecodeBufferSize; + decodebuffersize *= waveformat.frequency; + decodebuffersize /= 1000; + } + + if (!blocksize) + { + blocksize = decodebuffersize; + } + + if (blocksize < 256) + { + if (blocksize <= 0) + { + blocksize = 1; + } + + blocksize = ((256 + (blocksize - 1)) / blocksize) * blocksize; + } + + decodebuffersize /= blocksize; + decodebuffersize *= blocksize; + if (decodebuffersize <= blocksize) + { + decodebuffersize = blocksize * 2; + } + + if (!(mode_in & FMOD_OPENUSER) && numsubsounds == 0 && waveformat.lengthpcm <= decodebuffersize && sample_mode & FMOD_SOFTWARE) + { + decodebuffersize = waveformat.lengthpcm; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "decode buffersize = %d : blocksize = %d : format = %d\n", decodebuffersize, blocksize, waveformat.format)); + + /* + Create a small streaming sample to double buffer into + */ + sample_mode &= ~FMOD_LOOP_OFF; + sample_mode &= ~FMOD_LOOP_BIDI; + sample_mode &= ~FMOD_OPENMEMORY_POINT; + sample_mode |= FMOD_LOOP_NORMAL; + + FMOD_memcpy(&waveformatstream, &waveformat, sizeof(FMOD_CODEC_WAVEFORMAT)); + + if (exinfo && exinfo->speakermap) + { + if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + waveformatstream.channelmask = SPEAKER_ALLMONO; + } + else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO) + { + waveformatstream.channelmask = SPEAKER_ALLSTEREO; + } + else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + waveformatstream.channelmask = SPEAKER_PROTOOLS; + } + } + + waveformatstream.lengthpcm = decodebuffersize; + waveformatstream.channels = maxchannels; + waveformatstream.loopstart = 0; + waveformatstream.loopend = waveformatstream.lengthpcm - 1; + + result = createSample(sample_mode, &waveformatstream, &sample); + if (result != FMOD_OK) + { + return result; + } + + /* + Put in any post sample create info from the user. + */ + sample->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0; + sample->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0; + sample->mUserData = exinfo ? exinfo->userdata : 0; + sample->mPostCallbackSound = (FMOD_SOUND *)stream; + sample->mCodec = codec; + + if (sample->mNumSubSamples) + { + for (int count = 0; count < sample->mNumSubSamples; count++) + { + sample->mSubSample[count]->mCodec = codec; + } + } + } + + /* + Set up stream members. + */ + stream->mSample = sample; + stream->mSubSoundIndex = 0; + stream->mLength = waveformat.lengthpcm; + stream->mLengthBytes = waveformat.lengthbytes; + stream->mDefaultFrequency = (float)waveformat.frequency; + stream->mMode = sample_mode | FMOD_UNIQUE | FMOD_CREATESTREAM; + stream->mMode = stream->mMode & ~FMOD_LOOP_NORMAL; /* Don't inherit the loop mode */ + stream->mFormat = waveformat.format; + stream->mChannels = maxchannels; + stream->mSystem = this; + stream->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0; + stream->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0; + stream->mPostCallbackSound = (FMOD_SOUND *)stream; + stream->mBlockSize = blocksize; + stream->mType = codec->mType; + stream->mMinDistance = 1.0f * mDistanceScale; + stream->mMaxDistance = 10000.0f * mDistanceScale; + stream->mUserData = exinfo ? exinfo->userdata : 0; + if (stream->mName) + { + FMOD_strcpy(stream->mName, waveformat.name); + } + + if (sample) + { + if (exinfo && exinfo->speakermap) + { + if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + sample->mDefaultChannelMask = SPEAKER_ALLMONO; + } + else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO) + { + sample->mDefaultChannelMask = SPEAKER_ALLSTEREO; + } + else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + sample->mDefaultChannelMask = SPEAKER_PROTOOLS; + } + } + else + { + sample->mDefaultChannelMask = waveformat.channelmask; + } + + /* + Fix up the hardware/software and 2d/3d bits of the mode. + */ + stream->mMode &= ~(FMOD_2D | FMOD_3D | FMOD_HARDWARE | FMOD_SOFTWARE); + stream->mMode |= (sample->mMode & (FMOD_2D | FMOD_3D | FMOD_HARDWARE | FMOD_SOFTWARE)); + } + + /* + Loop information. + */ + if (waveformat.mode & FMOD_LOOP_OFF || mode_in & FMOD_LOOP_OFF) + { + stream->mMode |= FMOD_LOOP_OFF; + } + else if (waveformat.mode & FMOD_LOOP_NORMAL || mode_in & FMOD_LOOP_NORMAL) + { + stream->mMode |= FMOD_LOOP_NORMAL; + } + else + { + stream->mMode |= FMOD_LOOP_OFF; + } + stream->setLoopPoints(waveformat.loopstart, FMOD_TIMEUNIT_PCM, waveformat.loopend, FMOD_TIMEUNIT_PCM); + + if (!stream->mSoundGroupNode.getData()) + { + result = stream->setSoundGroup(mSoundGroup); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Allocate stream channel + */ + stream->mChannel = FMOD_Object_Calloc(ChannelStream); + if (!stream->mChannel) + { + return FMOD_ERR_MEMORY; + } + + stream->mChannel->mSystem = this; + + FMOD_OS_CriticalSection_Enter(SystemI::mStreamListCrit); + { + stream->mStreamNode.setData(stream); + stream->mStreamNode.addBefore(&mStreamListSoundHead); + } + FMOD_OS_CriticalSection_Leave(SystemI::mStreamListCrit); + + if (numsubsounds > 0 && !(mode_in & FMOD_OPENUSER)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "%d subsounds detected.\n", numsubsounds)); + + Stream *substream = 0; + + substream = (Stream *)FMOD_Object_Calloc(Stream); + if (!substream) + { + return FMOD_ERR_MEMORY; + } + AutoRelease<Stream> substream_cleanup(substream); + + if (numsubsounds > 1) + { + stream->mSubSoundShared = substream; + } + + if (!(mode_in & FMOD_LOWMEM)) + { + substream->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN); + if (!substream->mName) + { + return FMOD_ERR_MEMORY; + } + } + + substream->mSystem = this; + substream->mSample = sample; + substream->mCodec = codec; + substream->mSubSoundIndex = 0; + substream->mChannel = stream->mChannel; + substream->mMode = stream->mMode; + substream->mType = stream->mType; + substream->mAsyncData = stream->mAsyncData; + substream->mMinDistance = 1.0f * mDistanceScale; + substream->mMaxDistance = 10000.0f * mDistanceScale; + substream->mBlockSize = blocksize; + substream->mSubSoundParent = stream; + if (numsubsounds > 1) + { + substream->mSubSoundShared = substream; + } + + if (!substream->mSoundGroupNode.getData()) + { + result = substream->setSoundGroup(mSoundGroup); + if (result != FMOD_OK) + { + return result; + } + } + + for (count=0; count < numsubsounds; count++) + { + subsound[count] = substream; + + if (count < codec->numsubsounds) + { + FMOD_CODEC_WAVEFORMAT waveformat2; + + result = codec->mDescription.getwaveformat(codec, count, &waveformat2); + if (result != FMOD_OK) + { + return result; + } + + if (substream->mName) + { + FMOD_strcpy(substream->mName, waveformat2.name); + } + substream->mSubSoundIndex = count; + substream->mFormat = waveformat2.format; + substream->mChannels = waveformat2.channels; + substream->mDefaultFrequency = (float)waveformat2.frequency; + substream->mDefaultChannelMask = waveformat2.channelmask; + substream->mLoopStart = waveformat2.loopstart; + substream->mLoopLength = waveformat2.loopend - waveformat2.loopstart + 1; + substream->mLength = waveformat2.lengthpcm; + substream->mLengthBytes = waveformat2.lengthbytes; + substream->setLoopPoints(waveformat2.loopstart, FMOD_TIMEUNIT_PCM, waveformat2.loopend, FMOD_TIMEUNIT_PCM); + + if (codec->mDescription.soundcreate) + { + result = codec->mDescription.soundcreate(codec, count, (FMOD_SOUND *)substream); + if (result != FMOD_OK) + { + return result; + } + } + } + + stream->mNumActiveSubSounds++; + } + + if (codec->numsubsounds && substream->mSubSoundShared) + { + substream->updateSubSound(0, false); + } + + if (stream->mType == FMOD_SOUND_TYPE_CDDA && !(mode_in & FMOD_LOWMEM) && !(exinfo && exinfo->initialsubsound)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Setting up parent sound to contain a sentence of all subsounds.\n")); + + stream->setSubSoundSentence(0, numsubsounds); + } + + substream_cleanup.releasePtr(); + } + else + { + if (codec->mDescription.soundcreate) + { + result = codec->mDescription.soundcreate(codec, 0, (FMOD_SOUND *)stream); + if (result != FMOD_OK) + { + return result; + } + } + } + + /* + Convert the initial position time unit to PCM + */ + if (exinfo) + { + switch (exinfo->initialseekpostype) + { + case 0: // Defaults to FMOD_TIMEUNIT_PCM + { + break; + } + case FMOD_TIMEUNIT_PCM: + { + break; + } + case FMOD_TIMEUNIT_PCMBYTES: + { + stream->getSamplesFromBytes(exinfo->initialseekposition, &exinfo->initialseekposition); + exinfo->initialseekpostype = FMOD_TIMEUNIT_PCM; + break; + } + case FMOD_TIMEUNIT_MS: + { + exinfo->initialseekposition = (unsigned int)((float)exinfo->initialseekposition / 1000.0f * stream->mDefaultFrequency); + exinfo->initialseekpostype = FMOD_TIMEUNIT_PCM; + break; + } + default: // Non-supported time unit chosen + { + return FMOD_ERR_INVALID_PARAM; + } + } + } + + if (exinfo && exinfo->initialsubsound && stream->mSubSound) + { + Stream *substream = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Setting initial subsound. exinfo->initialsubsound = %d, stream->mNumSubSounds = %d.\n", exinfo->initialsubsound, stream->mNumSubSounds)); + + if (exinfo->initialsubsound < 0 || exinfo->initialsubsound >= stream->mNumSubSounds) + { + return FMOD_ERR_INVALID_PARAM; + } + + stream->mSubSoundIndex = exinfo->initialsubsound; + stream->mChannel->mSubSoundListCurrent = exinfo->initialsubsound; + + substream = SAFE_CAST(Stream, stream->mSubSound[stream->mSubSoundIndex]); + if (substream) + { + substream->mSubSoundIndex = exinfo->initialsubsound; + substream->mChannel->mSubSoundListCurrent = exinfo->initialsubsound; + + if (substream->mSubSoundShared) + { + result = substream->updateSubSound(exinfo->initialsubsound, false); + if (result != FMOD_OK) + { + return result; + } + } + + stream->mInitialPosition = exinfo->initialseekposition; + result = substream->setPosition(exinfo->initialseekposition, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Seek stream to start\n")); + + /* + Seek and pre-flush the buffer. + */ + stream->mInitialPosition = exinfo ? exinfo->initialseekposition : 0; + result = stream->setPosition(exinfo ? exinfo->initialseekposition : 0, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + + if (!(mode_in & FMOD_OPENONLY) && !(codec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "flush stream buffer\n")); + + result = stream->flush(); + if (result != FMOD_OK) + { + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "flush successful.\n")); + } + + /* + Change from a blocking single buffered sector sized file to a large doublebuffered file. + Dont do it for mod files, they dont have a file buffer at runtime anyway. + */ + if (!(mode_in & FMOD_OPENUSER) && !(codec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES) + && codec->mType != FMOD_SOUND_TYPE_IT + && codec->mType != FMOD_SOUND_TYPE_XM + && codec->mType != FMOD_SOUND_TYPE_S3M + && codec->mType != FMOD_SOUND_TYPE_MOD + && codec->mType != FMOD_SOUND_TYPE_MIDI) + { + unsigned int sizebytes = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "switch file handle from small blocking single buffered to large nonblocking doublebuffered.\n")); + + if (buffersizetype == FMOD_TIMEUNIT_RAWBYTES) + { + sizebytes = buffersize; + } + else + { + unsigned int pcm = buffersize; + + if (buffersizetype == FMOD_TIMEUNIT_MS) + { + pcm = buffersize * (unsigned int)stream->mDefaultFrequency / 1000; + } + else if (buffersizetype == FMOD_TIMEUNIT_PCMBYTES) + { + stream->getSamplesFromBytes(buffersize, &pcm); + } + + /* + This section tries to guess the size in bytes (sizebytes) it would take with + this compressed format, to read the buffersize worth of output samples (pcm). + */ + if (stream->mLength == (unsigned int)-1 || file->mLength == (unsigned int)-1) + { + stream->getBytesFromSamples(pcm, &sizebytes); /* In this case, length is infinite, so make a rough guess based on PCM bytes. */ + } + else + { + float frac; + + if (stream->mLengthBytes) + { + frac = (float)stream->mLengthBytes / (float)stream->mLength; + } + else + { + frac = (float)file->mLength / (float)stream->mLength; + } + + sizebytes = (unsigned int)((float)pcm * frac); + } + } + + result = file->enableDoubleBuffer(sizebytes, filebufferstack); /* Now double buffered, size determined by user */ + if (result != FMOD_OK) + { + return result; + } + } + + stream_cleanup.releasePtr(); + *sound = stream; + } + else /* ^^^^^^ (mode_in & FMOD_CREATESTREAM) ^^^^^^ */ +#endif + { + Sample *sample = 0, *samplecontainer = 0; + AutoRelease<Sample> samplecontainer_cleanup; + bool realtimesample = false; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Create as FMOD_CREATESAMPLE\n")); + + if (numsubsounds > 0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Multi-sample sound (%d subsounds), create a sample container.\n", numsubsounds)); + + if (mode_in & FMOD_NONBLOCKING) + { + samplecontainer = SAFE_CAST(Sample, *sound); + } + else + { + samplecontainer = FMOD_Object_Calloc(Sample); + if (!samplecontainer) + { + return FMOD_ERR_MEMORY; + } + samplecontainer_cleanup = samplecontainer; + } + + if (!samplecontainer->mName && !(mode_in & FMOD_LOWMEM)) + { + samplecontainer->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN); + if (!samplecontainer->mName) + { + return FMOD_ERR_MEMORY; + } + } + codec_cleanup.releasePtr(); + subsound_cleanup.releasePtr(); + + samplecontainer->mSystem = this; + samplecontainer->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0; + samplecontainer->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0; + samplecontainer->mPostCallbackSound = (FMOD_SOUND *)samplecontainer; + samplecontainer->mType = codec->mType; + samplecontainer->mCodec = codec; + samplecontainer->mMinDistance = 1.0f * mDistanceScale; + samplecontainer->mMaxDistance = 10000.0f * mDistanceScale; + samplecontainer->mSubSound = subsound; + samplecontainer->mNumSubSounds = numsubsounds; + samplecontainer->mSubSoundIndex = -1; + samplecontainer->mUserData = exinfo ? exinfo->userdata : 0; + samplecontainer->mMode = mode_in; + samplecontainer->mDefaultFrequency = (float)waveformat.frequency; + } + + for (count = 0; (count < (numsubsounds == 0 ? 1 : numsubsounds)) && (count < (numsubsounds == 0 ? 1 : codec->numsubsounds)); count++) + { + FMOD_MODE sample_mode = mode_in; + FMOD_CODEC_WAVEFORMAT waveformat2; + + result = codec->mDescription.getwaveformat(codec, count, &waveformat2); + if (result != FMOD_OK) + { + return result; + } + + if (!numsubsounds && (sample_mode & FMOD_NONBLOCKING)) + { + sample = SAFE_CAST(Sample, *sound); + } + else + { + sample = 0; + } + + /* + Check the user passed in inclusion list if this sample should be loaded or not. + */ + if (numsubsounds > 0 && exinfo && exinfo->inclusionlist) + { + bool found = false; + int count2; + + for (count2 = 0; count2 < exinfo->inclusionlistnum; count2++) + { + if (exinfo->inclusionlist[count2] == count) + { + found = true; + break; + } + } + + if (!found) + { + continue; + } + } + + if (exinfo && exinfo->speakermap) + { + if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO) + { + waveformat2.channelmask = SPEAKER_ALLMONO; + } + else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO) + { + waveformat2.channelmask = SPEAKER_ALLSTEREO; + } + else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS) + { + waveformat2.channelmask = SPEAKER_PROTOOLS; + } + } + + if (sample_mode & FMOD_CREATECOMPRESSEDSAMPLE) + { + if (waveformat2.format == FMOD_SOUND_FORMAT_MPEG || + waveformat2.format == FMOD_SOUND_FORMAT_IMAADPCM || + waveformat2.format == FMOD_SOUND_FORMAT_XMA || + waveformat2.format == FMOD_SOUND_FORMAT_CELT) /* Only allow these formats through as realtime samples. */ + { + realtimesample = true; + } + else + { + sample_mode &= ~FMOD_CREATECOMPRESSEDSAMPLE; + + if (samplecontainer) + { + samplecontainer->mMode &= ~FMOD_CREATECOMPRESSEDSAMPLE; + } + } + } + + if (!realtimesample && mode_in & FMOD_OPENMEMORY_POINT) + { + result = codec->canPointTo(); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Create the sample + */ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "creating subsound %d/%d\n", count, numsubsounds)); + + result = createSample(sample_mode, &waveformat2, &sample); + if (result != FMOD_OK) + { + return result; + } + + /* + Put in any post sample create info from the user. + */ + codec_cleanup.releasePtr(); + sample->mSubSoundIndex = count; + sample->mSubSoundParent = samplecontainer; + sample->mType = codec->mType; + sample->mCodec = codec; + sample->mMinDistance = 1.0f * mDistanceScale; + sample->mMaxDistance = 10000.0f * mDistanceScale; + sample->mUserData = exinfo ? exinfo->userdata : 0; + + if (sample->mNumSubSamples) + { + for (int count = 0; count < sample->mNumSubSamples; count++) + { + sample->mSubSample[count]->mCodec = codec; + sample->mSubSample[count]->mSubSoundIndex = sample->mSubSoundIndex; + } + } + + if (!sample->mSoundGroupNode.getData()) + { + result = sample->setSoundGroup(mSoundGroup); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Now load it. + */ + if (!numsubsounds && count == 0) + { + sample->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0; + sample->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0; + sample->mPostCallbackSound = (FMOD_SOUND *)sample; + } + + if (codec->mDescription.soundcreate) + { + SoundI *s = SAFE_CAST(SoundI, sample); + + result = codec->mDescription.soundcreate(codec, count, (FMOD_SOUND *)s); + if (result != FMOD_OK) + { + return result; + } + } + + result = codec->reset(); /* Reset the codec if it needs resetting, and clear PCM Buffer and reset it. */ + if (result != FMOD_OK) + { + return result; + } + + if (waveformat2.mode & FMOD_CREATECOMPRESSEDSAMPLE || sample_mode & FMOD_CREATECOMPRESSEDSAMPLE) + { + result = codec->setPosition(count, 0, FMOD_TIMEUNIT_RAWBYTES); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = codec->setPosition(count, 0, FMOD_TIMEUNIT_PCM); + if (result != FMOD_OK) + { + return result; + } + } + + if (sample->mPostSetPositionCallback) + { + if (numsubsounds > 0) + { + samplecontainer->mPostSetPositionCallback((FMOD_SOUND *)samplecontainer, count, 0, FMOD_TIMEUNIT_PCM); + } + else + { + sample->mPostSetPositionCallback((FMOD_SOUND *)sample, 0, 0, FMOD_TIMEUNIT_PCM); + } + } + + /* + Get the data for the sample (from the file or just point to it). + */ + if (sample_mode & FMOD_OPENMEMORY_POINT) /* If we are simply pointing to the user's memory address. */ + { + if (codec->mNonInterleaved && sample->mNumSubSamples) + { + unsigned int onechanlengthbytes = 0; + + sample->getBytesFromSamples(sample->mLength / sample->mNumSubSamples, &onechanlengthbytes); + + for (int count = 0; count < sample->mNumSubSamples; count++) + { + result = ((Sample *)(sample->mSubSample[count]))->setBufferData((char *)name_or_data + codec->mFile->mCurrentPosition + (onechanlengthbytes * count)); + if (result != FMOD_OK) + { + if (samplecontainer || codec) + { + sample->release(); /* Release it here because it hasnt been made a subsound yet. */ + } + return result; + } + } + } + else + { + result = sample->setBufferData((char *)name_or_data + codec->mFile->mCurrentPosition); + if (result != FMOD_OK) + { + if (samplecontainer || codec) + { + sample->release(); /* Release it here because it hasnt been made a subsound yet. */ + } + return result; + } + } + } + else if (!(sample_mode & FMOD_OPENONLY)) + { + unsigned int samplelength, read; + + if (waveformat2.mode & FMOD_CREATECOMPRESSEDSAMPLE || sample_mode & FMOD_CREATECOMPRESSEDSAMPLE) + { + samplelength = waveformat2.lengthbytes; + + if (codec->mType != FMOD_SOUND_TYPE_FSB && codec->mType != FMOD_SOUND_TYPE_XMA) + { + samplelength -= codec->mSrcDataOffset; + } + } + else + { + samplelength = waveformat2.lengthpcm; + } + + result = sample->read(0, samplelength, &read); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + if (!(sample->mMode & FMOD_NONBLOCKING) && (samplecontainer || codec)) + { + sample->release(); /* Release it here because it hasnt been made a subsound yet. */ + } + return result; + } + + if (samplelength != read && sample->mLoopLength == sample->mLength) + { + sample->mLoopLength = read; + } + + /* + Now after the read, set the position back to 0 again. + */ + result = sample->setPositionInternal(0); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + } + + if (numsubsounds > 0) + { + subsound[count] = sample; + + samplecontainer->mNumActiveSubSounds++; + } + } + + // Don't close the file handle for DLS files with inclusion lists, MIDI will + // need to specify which sub sounds to load after the DLS is created. + if (!(mode_in & FMOD_OPENONLY) && !(codec->mType == FMOD_SOUND_TYPE_DLS && numsubsounds > 0 && exinfo && exinfo->inclusionlist)) + { + if (file) + { + file->close(); + FMOD_Memory_Free(file); + } + codec->mFile = 0; +#if 0 + if (!(mode_in & FMOD_CREATECOMPRESSEDSAMPLE)) + { + codec->release(); + codec = 0; + sample->mCodec = 0; + } +#endif + } + + if (numsubsounds > 0) + { + samplecontainer_cleanup.releasePtr(); + *sound = samplecontainer; + } + else + { + *sound = sample; + } + } + + soundi = SAFE_CAST(SoundI, *sound); + FMOD_OS_CriticalSection_Enter(gSoundListCrit); + { + soundi->addAfter(&mSoundListHead); + } + FMOD_OS_CriticalSection_Leave(gSoundListCrit); + + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "sound open failed with error %d\n", result)); + + if (!(mode_in & FMOD_NONBLOCKING)) + { + *sound = 0; + } + + return result; + } + + /* + Put the name of the sound into the name field. + */ + if (soundi->mName && ((mode_in & FMOD_UNICODE && !((short *)soundi->mName)[0]) || !soundi->mName[0])) + { + bool usefilename = true; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "No name found in file, use filename.\n")); + + /* + First look for the name in a tag. + */ + if (codec->mMetadata) + { + FMOD_TAG tag; + + result = codec->mMetadata->getTag("TITLE", 0, &tag); + if (result == FMOD_OK) + { + FMOD_strncpy(soundi->mName, (char *)tag.data, FMOD_STRING_MAXNAMELEN); + usefilename = false; + + /* + Update tag so it is set back to "updated" status + */ + codec->mMetadata->addTag(tag.type, "TITLE", tag.data, tag.datalen, tag.datatype, true); + } + else + { + result = codec->mMetadata->getTag("TT2", 0, &tag); + if (result == FMOD_OK) + { + FMOD_strncpy(soundi->mName, (char *)tag.data, FMOD_STRING_MAXNAMELEN); + usefilename = false; + + /* + Update tag so it is set back to "updated" status + */ + codec->mMetadata->addTag(tag.type, "TT2", tag.data, tag.datalen, tag.datatype, true); + } + } + } + + if (usefilename && !(mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) && name_or_data) + { + if (mode_in & FMOD_UNICODE) + { + for (count = FMOD_strlenW((short *)name_or_data); count >= 0; count--) + { + if (((short *)name_or_data)[count] == L'\\' || ((short *)name_or_data)[count] == L'/') + { + count++; + break; + } + } + if (count < 0) + { + count = 0; + } + + FMOD_strncpyW((short *)soundi->mName, (short *)name_or_data + count, FMOD_STRING_MAXNAMELEN / 2); + } + else + { + for (count = FMOD_strlen(name_or_data); count >= 0; count--) + { + if (name_or_data[count] == '\\' || name_or_data[count] == '/') + { + count++; + break; + } + } + if (count < 0) + { + count = 0; + } + + FMOD_strncpy(soundi->mName, (char *)name_or_data + count, FMOD_STRING_MAXNAMELEN); + } + } + } + + if (!calledfromasync) + { + soundi->mOpenState = FMOD_OPENSTATE_READY; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "done. OpenState now = FMOD_OPENSTATE_READY.\n\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::checkDriverList(bool fromsystemupdate) +{ + const unsigned int UPDATEFREQUENCYMS = 1000; + FMOD_RESULT result = FMOD_OK; + bool devicelistchanged = false; + unsigned int timestamp = 0; + + if (fromsystemupdate && !mCallback) + { + return FMOD_OK; + } + + FMOD_OS_Time_GetMs(×tamp); + if (!fromsystemupdate || ((timestamp - mDeviceListLastCheckedTime) >= UPDATEFREQUENCYMS)) + { + mDeviceListLastCheckedTime = timestamp; + + result = FMOD_OS_CheckDriverList(&devicelistchanged); + if (result != FMOD_OK) + { + return result; + } + + if (devicelistchanged) + { + mDeviceListChanged = true; + + /* + Force the output mode to re-enumerate on next driver list related call. + */ + mOutput->mEnumerated = false; + #ifdef FMOD_SUPPORT_RECORDING + mOutput->mRecordEnumerated = false; + #endif + } + } + + if (fromsystemupdate && mDeviceListChanged) + { + mCallback((FMOD_SYSTEM *)this, FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED, 0, 0); + mDeviceListChanged = false; + } + + return FMOD_OK; +} + +/* + ==================================================================================== + + PUBLIC MEMBER FUNCTIONS + + ==================================================================================== +*/ + +#ifndef FMOD_STATICFORPLUGINS + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setUpPlugins() +{ + FMOD_RESULT result; + AutoReleaseClear<PluginFactory*> pluginFactoryCleanup; + + /* + Create the plugin factory here, because it is needed pre-init for output plugins! + */ + mPluginFactory = FMOD_Object_Alloc(FMOD::PluginFactory); + if (!mPluginFactory) + { + return FMOD_ERR_MEMORY; + } + + pluginFactoryCleanup = &mPluginFactory; + + result = mPluginFactory->setSystem(this); + if (result != FMOD_OK) + { + return result; + } + + /* + Register plugins for fmod to use + */ + mPluginFactory->setPluginPath(mPluginPath); + + /* + Register platform specific outputs + */ + CHECK_RESULT(FMOD_OS_Output_Register(mPluginFactory)); + + /* + Register cross platform outputs + */ +#ifdef FMOD_USE_PLUGINS + + CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_wavwriter")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_wavwriter_nrt")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_nosound")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_nosound_nrt")); + +#else + + #ifdef FMOD_SUPPORT_WAVWRITER + CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputWavWriter::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_WAVWRITER_NRT + CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputWavWriter_NRT::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_NOSOUND + CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputNoSound::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_NOSOUND_NRT + CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputNoSound_NRT::getDescriptionEx())); + #endif + +#endif + + + + + +#ifdef FMOD_USE_PLUGINS + /* + Register file format codec plugins + */ + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_tag", 0, true, 100)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_cdda", 0, true, 200)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_fsb", &mFSBPluginHandle, true, 300)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_vag", 0, true, 500)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_wav", &mWAVPluginHandle, true, 600)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_oggvorbis", 0, true, 800)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_tremor", 0, true, 900)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_aiff", 0, true, 1000)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_flac", 0, true, 1100)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_mod", 0, true, 1200)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_s3m", 0, true, 1300)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_xm", 0, true, 1400)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_it", 0, true, 1500)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_midi", 0, true, 1600)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_dls", 0, true, 1700)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_sf2", 0, true, 1800)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_asf", 0, true, 1900)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_mpeg", &mMPEGPluginHandle, true, 2400)); /* Second last, due to slow 4k bytescan if mpeg format header isn't found */ + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_playlist", 0, true, 2450)); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_celt", &mCELTPluginHandle, true, 2500)); + + #ifdef FMOD_SUPPORT_RAW + CHECK_RESULT(mPluginFactory->registerCodec(CodecRaw::getDescriptionEx())); + #endif + + /* + Register DSP plugins. + */ + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_oscillator")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_fft")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_lowpass")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_lowpass2")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_lowpass_simple")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_highpass")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_echo")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_delay")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_flange")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_tremolo")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_distortion")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_normalize")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_parameq")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_pitchshift")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_chorus")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_reverb")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_sfxreverb")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_itecho")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_compressor")); + CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_dolbyheadphones")); + +#else + + /* + Register file format codecs + */ + #ifdef FMOD_SUPPORT_TAGS + CHECK_RESULT(mPluginFactory->registerCodec(CodecTag::getDescriptionEx(), 0, 100)); + #endif + #ifdef FMOD_SUPPORT_CDDA + CHECK_RESULT(mPluginFactory->registerCodec(CodecCDDA::getDescriptionEx(), 0, 200)); + #endif + #ifdef FMOD_SUPPORT_FSB + CHECK_RESULT(mPluginFactory->registerCodec(CodecFSB::getDescriptionEx(), &mFSBPluginHandle, 300)); + #ifdef FMOD_FSB_USEHEADERCACHE + CodecFSB::gCacheHead.initNode(); + #endif + #endif + #ifdef FMOD_SUPPORT_GCADPCM_DSP + CHECK_RESULT(mPluginFactory->registerCodec(CodecDSP::getDescriptionEx(), 0, 400)); + #endif + #ifdef FMOD_SUPPORT_VAG + CHECK_RESULT(mPluginFactory->registerCodec(CodecVAG::getDescriptionEx(), 0, 500)); + #endif + #ifdef FMOD_SUPPORT_WAV + CHECK_RESULT(mPluginFactory->registerCodec(CodecWav::getDescriptionEx(), &mWAVPluginHandle, 600)); + #endif + #ifdef FMOD_SUPPORT_AT3 + CHECK_RESULT(mPluginFactory->registerCodec(CodecAT3::getDescriptionEx(), 0, 700)); + #endif + #ifdef FMOD_SUPPORT_OGGVORBIS + CHECK_RESULT(mPluginFactory->registerCodec(CodecOggVorbis::getDescriptionEx(), 0, 800)); + #endif + #ifdef FMOD_SUPPORT_TREMOR + CHECK_RESULT(mPluginFactory->registerCodec(CodecTremor::getDescriptionEx(), 0, 900)); + #endif + #ifdef FMOD_SUPPORT_AIFF + CHECK_RESULT(mPluginFactory->registerCodec(CodecAIFF::getDescriptionEx(), 0, 1000)); + #endif + #ifdef FMOD_SUPPORT_FLAC + CHECK_RESULT(mPluginFactory->registerCodec(CodecFLAC::getDescriptionEx(), 0, 1100)); + #endif + #ifdef FMOD_SUPPORT_MOD + CHECK_RESULT(mPluginFactory->registerCodec(CodecMOD::getDescriptionEx(), 0, 1200)); + #endif + #ifdef FMOD_SUPPORT_S3M + CHECK_RESULT(mPluginFactory->registerCodec(CodecS3M::getDescriptionEx(), 0, 1300)); + #endif + #ifdef FMOD_SUPPORT_XM + CHECK_RESULT(mPluginFactory->registerCodec(CodecXM::getDescriptionEx(), 0, 1400)); + #endif + #ifdef FMOD_SUPPORT_IT + CHECK_RESULT(mPluginFactory->registerCodec(CodecIT::getDescriptionEx(), 0, 1500)); + #endif + #ifdef FMOD_SUPPORT_MIDI + CHECK_RESULT(mPluginFactory->registerCodec(CodecMIDI::getDescriptionEx(), 0, 1600)); + #endif + #ifdef FMOD_SUPPORT_DLS + CHECK_RESULT(mPluginFactory->registerCodec(CodecDLS::getDescriptionEx(), 0, 1700)); + #endif + #ifdef FMOD_SUPPORT_SF2 + CHECK_RESULT(mPluginFactory->registerCodec(CodecSF2::getDescriptionEx(), 0, 1800)); + #endif + #ifdef FMOD_SUPPORT_ASF + CHECK_RESULT(mPluginFactory->registerCodec(CodecASF::getDescriptionEx(), 0, 1900)); + #endif + #ifdef FMOD_SUPPORT_XMA + CHECK_RESULT(mPluginFactory->registerCodec(CodecXMA::getDescriptionEx(), 0, 2000)); + #endif + #ifdef FMOD_SUPPORT_XWMA + CHECK_RESULT(mPluginFactory->registerCodec(CodecXWMA::getDescriptionEx(), 0, 2100)); + #endif + #ifdef FMOD_SUPPORT_MPEGPSP + CHECK_RESULT(mPluginFactory->registerCodec(CodecMPEGPSP::getDescriptionEx(), 0, 2300)); + #endif + #ifdef FMOD_SUPPORT_MPEG + CHECK_RESULT(mPluginFactory->registerCodec(CodecMPEG::getDescriptionEx(), &mMPEGPluginHandle, 2400)); /* Last, due to slow 4k bytescan if mpeg format header isn't found */ + #endif + #ifdef FMOD_SUPPORT_PLAYLIST + CHECK_RESULT(mPluginFactory->registerCodec(CodecPlaylist::getDescriptionEx(), 0, 2450)); + #endif + #ifdef FMOD_SUPPORT_RAW + CHECK_RESULT(mPluginFactory->registerCodec(CodecRaw::getDescriptionEx(), 0, 2500)); + #endif + #ifdef FMOD_SUPPORT_CELT + CHECK_RESULT(mPluginFactory->registerCodec(CodecCELT::getDescriptionEx(), &mCELTPluginHandle, 2600)); + #endif + + + /* + Register DSP plugins. + */ + #ifdef FMOD_SUPPORT_OSCILLATOR + CHECK_RESULT(mPluginFactory->registerDSP(DSPOscillator::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_LOWPASS + CHECK_RESULT(mPluginFactory->registerDSP(DSPLowPass::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_LOWPASS2 + CHECK_RESULT(mPluginFactory->registerDSP(DSPLowPass2::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_LOWPASS_SIMPLE + CHECK_RESULT(mPluginFactory->registerDSP(DSPLowPassSimple::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_HIGHPASS + CHECK_RESULT(mPluginFactory->registerDSP(DSPHighPass::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_ECHO + CHECK_RESULT(mPluginFactory->registerDSP(DSPEcho::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_DELAY + CHECK_RESULT(mPluginFactory->registerDSP(DSPDelay::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_FLANGE + CHECK_RESULT(mPluginFactory->registerDSP(DSPFlange::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_TREMOLO + CHECK_RESULT(mPluginFactory->registerDSP(DSPTremolo::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_DISTORTION + CHECK_RESULT(mPluginFactory->registerDSP(DSPDistortion::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_NORMALIZE + CHECK_RESULT(mPluginFactory->registerDSP(DSPNormalize::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_PARAMEQ + CHECK_RESULT(mPluginFactory->registerDSP(DSPParamEq::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_PITCHSHIFT + CHECK_RESULT(mPluginFactory->registerDSP(DSPPitchShift::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_CHORUS + CHECK_RESULT(mPluginFactory->registerDSP(DSPChorus::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_FREEVERB + CHECK_RESULT(mPluginFactory->registerDSP(DSPReverb::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_ITECHO + CHECK_RESULT(mPluginFactory->registerDSP(DSPITEcho::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_COMPRESSOR + CHECK_RESULT(mPluginFactory->registerDSP(DSPCompressor::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_SFXREVERB + CHECK_RESULT(mPluginFactory->registerDSP(DSPSfxReverb::getDescriptionEx())); + #endif + +#endif + + /* + Create the built in 'user' codec. This is for when people create a sound with FMOD_CREATEUSER + */ + #ifdef FMOD_SUPPORT_USERCODEC + CHECK_RESULT(mPluginFactory->registerCodec(CodecUser::getDescriptionEx(), 0, 2600)); + #endif + + pluginFactoryCleanup.releasePtr(); + + mPluginsLoaded = true; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::sortSpeakerList() +{ + int count, count2; + bool used[FMOD_SPEAKER_MAX]; + int outputchannels; + + if (mSpeakerMode == FMOD_SPEAKERMODE_RAW) + { + return FMOD_OK; + } + + /* + Make a sorted speaker list based on the angles. + */ + for (count = 0; count < FMOD_SPEAKER_MAX; count++) + { + mSpeakerList[count] =0; + } + + FMOD_memset(used, 0, sizeof(bool) * FMOD_SPEAKER_MAX); + + outputchannels = mMaxOutputChannels; + if (mSpeakerMode == FMOD_SPEAKERMODE_QUAD || mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC) + { + outputchannels = 6; /* 3d pretends it has rear right/rear left for SetSpeakerMix, so 5 instead of 4. */ + } + + for (count = 0; count < outputchannels; count++) + { + /* initialise to some high value */ + float lowest = 2 * FMOD_ANGLESORT_REVOLUTION; + + for (count2 = 0; count2 < outputchannels; count2++) + { + if (mSpeaker[count2].mSpeaker == FMOD_SPEAKER_LOW_FREQUENCY) + { + continue; + } + if (!mSpeaker[count2].mActive) + { + continue; + } + if (mSpeakerMode == FMOD_SPEAKERMODE_QUAD && mSpeaker[count2].mSpeaker == FMOD_SPEAKER_FRONT_CENTER) + { + continue; + } + + if (mSpeaker[count2].mXZAngle < lowest && !used[count2]) + { + lowest = mSpeaker[count2].mXZAngle; + mSpeakerList[count] = &mSpeaker[count2]; + } + } + if (mSpeakerList[count]) + { + used[mSpeakerList[count]->mSpeaker] = true; + } + } + + return prepareSpeakerPairs(); +} + + +FMOD_RESULT SystemI::prepareSpeakerPairs() +{ + int speaker = 0; + FMOD_SPEAKERCONFIG *spkA, *spkB; + + /* + Step 1: + - Calculate speaker angles and normals + */ + while (mSpeakerList[speaker]) + { + mSpeakerList[speaker]->mXZNormal = mSpeakerList[speaker]->mPosition; + mSpeakerList[speaker]->mXZNormal.y = 0; + FMOD_Vector_Normalize(&mSpeakerList[speaker]->mXZNormal); + + mSpeakerList[speaker]->mXZAngle = FMOD_AngleSort_GetValue(mSpeakerList[speaker]->mXZNormal.x, mSpeakerList[speaker]->mXZNormal.z); + + ++speaker; + } + + /* + Step 2: + - Limit speaker separation to a maximum of 180 degrees so that sources at poles + are rendered hard left or hard right. + */ + speaker = 0; + while (mSpeakerList[speaker]) + { + spkA = mSpeakerList[speaker++]; + if (mSpeakerList[speaker]) + { + spkB = mSpeakerList[speaker]; + } + else + { + spkB = mSpeakerList[0]; + } + + if (spkA->mXZAngle == spkB->mXZAngle) /* 2 speakers are at the same location so skip to the next pair */ + { + continue; + } + + if (FMOD_AngleSort_IsReflex(spkA->mXZAngle, spkB->mXZAngle)) + { + FMOD_VECTOR lateral; + FMOD_Vector_Subtract(&spkA->mXZNormal, &spkB->mXZNormal, &lateral); + FMOD_Vector_Normalize(&lateral); + spkA->mXZNormal = lateral; + FMOD_Vector_Scale(&lateral, -1.0f, &spkB->mXZNormal); + spkA->mXZAngle = FMOD_AngleSort_GetValue(spkA->mXZNormal.x, spkA->mXZNormal.z); + spkB->mXZAngle = FMOD_AngleSort_GetValue(spkB->mXZNormal.x, spkB->mXZNormal.z); + + // only possible to have one pair of reflex speakers + break; + } + } + + + /* + Step 3: + - Determine whether pairs will use VBAP or lateral panning only + - Precalculate some constants for panning + */ + speaker = 0; + while (mSpeakerList[speaker]) + { + spkA = mSpeakerList[speaker++]; + if (mSpeakerList[speaker]) + { + spkB = mSpeakerList[speaker]; + } + else + { + spkB = mSpeakerList[0]; + } + + if (spkA->mXZAngle == spkB->mXZAngle) /* 2 speakers are at the same location so skip to the next pair */ + { + continue; + } + + spkA->mPairUseVBAP = !FMOD_AngleSort_IsStraight(spkA->mXZAngle, spkB->mXZAngle); + if(spkA->mPairUseVBAP) + { + /* + we would normally use the determinant to solve the speaker gains however this is a waste + cpu as the gains are normalised for power. We need to keep its sign however so that gains + are always positive. + */ + float determinant = spkA->mXZNormal.x * spkB->mXZNormal.z - spkB->mXZNormal.x * spkA->mXZNormal.z; + spkA->mPairVBAPSign = determinant > 0.0f ? 1.0f : -1.0f; + } + } + + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_DSPCODEC +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::allocateDSPCodec(FMOD_SOUND_FORMAT format, DSPCodec **dsp) +{ + if (0) + { + } +#ifdef FMOD_SUPPORT_MPEG + else if (format == FMOD_SOUND_FORMAT_MPEG) + { + return mDSPCodecPool_MPEG.alloc(dsp); + } +#endif +#ifdef FMOD_SUPPORT_XMA + else if (format == FMOD_SOUND_FORMAT_XMA) + { + return mDSPCodecPool_XMA.alloc(dsp); + } +#endif +#ifdef FMOD_SUPPORT_IMAADPCM + else if (format == FMOD_SOUND_FORMAT_IMAADPCM) + { + return mDSPCodecPool_ADPCM.alloc(dsp); + } +#endif +#ifdef FMOD_SUPPORT_CELT + else if (format == FMOD_SOUND_FORMAT_CELT) + { + return mDSPCodecPool_CELT.alloc(dsp); + } +#endif +#ifdef FMOD_SUPPORT_RAWCODEC + else if (format == FMOD_SOUND_FORMAT_PCM16) + { + return mDSPCodecPool_RAW.alloc(dsp); + } +#endif + + return FMOD_ERR_FORMAT; +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +SystemI::SystemI() +{ + mInitialized = false; + mPluginsLoaded = false; + mOutputType = FMOD_OUTPUTTYPE_AUTODETECT; + mOutput = 0; + mEmulated = 0; + mMainThreadID = 0; + mChannel = 0; + mPluginFactory = 0; + mLastTimeStamp = 0; + mStreamFileBufferSize = 16*1024; + mStreamFileBufferSizeType = FMOD_TIMEUNIT_RAWBYTES; + mDeviceListLastCheckedTime = 0; + mDeviceListChanged = false; + mUserData = 0; + + mNumSoftwareChannels = 32; + mMinHardwareChannels2D = 0; + mMaxHardwareChannels2D = 1000; + mMinHardwareChannels3D = 0; + mMaxHardwareChannels3D = 1000; + mCreatedHardwareSample = false; + mBufferSize = FILE_SECTORSIZE; + mUsesUserCallbacks = false; + +#if defined(PLATFORM_XENON) || defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + mDSPBlockSize = 256; + mDSPBufferSize = mDSPBlockSize * 4; +#else + mDSPBlockSize = 1024; + mDSPBufferSize = mDSPBlockSize * 4; +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE + mSoftware = 0; + + mDSPTempBuff = 0; + mDSPTempBuffMem = 0; + + for (int count = 0; count < FMOD_DSP_MAXTREEDEPTH; count++) + { + mDSPMixBuff[count] = 0; + } + + mDSPClock.mHi = 0; + mDSPClock.mLo = 0; +#endif + + mMaxInputChannels = DSP_DEFAULTLEVELS_IN; + mMaxOutputChannels = 0; + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_LOW_FREQUENCY, 0.0f, 0.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f); + +#ifdef PLATFORM_PS3 + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -0.4f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 0.4f, -1.0f); +#else + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -1.0f, 0.0f); + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 1.0f, 0.0f); +#endif + +#if defined(PLATFORM_XENON) + mOutputFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + + setSpeakerMode(FMOD_SPEAKERMODE_5POINT1); +#elif defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + mOutputFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + + setSpeakerMode(FMOD_SPEAKERMODE_7POINT1); +#else + mOutputFormat = FMOD_SOUND_FORMAT_PCM16; + + setSpeakerMode(FMOD_SPEAKERMODE_STEREO); +#endif +#if defined(PLATFORM_IPHONE) + mOutputRate = 24000; +#else + mOutputRate = 48000; +#endif + mOutputHandle = 0; + mSelectedDriver = 0; + mResampleMethod = FMOD_DSP_RESAMPLER_LINEAR; + + mNumListeners = 1; + mDistanceScale = 1; + mRolloffScale = 1; + mDopplerScale = 1; +#ifdef FMOD_SUPPORT_MULTIREVERB + mReverb3DActive = false; + FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF; + setReverbAmbientProperties(&prop); +#endif + +#ifdef FMOD_SUPPORT_GEOMETRY + mGeometryList = 0; + mGeometryMgr.mSystem = this; +#endif + + FMOD_memset(mPluginPath, 0, FMOD_STRING_MAXPATHLEN); + + mAdvancedSettings.maxXMAcodecs = 0; + mAdvancedSettings.maxADPCMcodecs = 0; + mAdvancedSettings.maxMPEGcodecs = 0; + mAdvancedSettings.maxCELTcodecs = 0; + mAdvancedSettings.maxPCMcodecs = 0; + mAdvancedSettings.max3DReverbDSPs = FMOD_ADVANCEDSETTINGS_MAX3DREVERBDSPS; + mAdvancedSettings.HRTFMinAngle = 180; + mAdvancedSettings.HRTFMaxAngle = 360; + mAdvancedSettings.HRTFFreq = 4000; + mAdvancedSettings.vol0virtualvol = 0.0f; + mAdvancedSettings.eventqueuesize = 32; + mAdvancedSettings.defaultDecodeBufferSize = FMOD_STREAMDECODEBUFFERSIZE_DEFAULT; + mAdvancedSettings.geometryMaxFadeTime = 0; + + mMPEGPluginHandle = 0xFFFFFFFF; + mFSBPluginHandle = 0xFFFFFFFF; + mWAVPluginHandle = 0xFFFFFFFF; + +#ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + mDSPCodecPool_MPEG.mSystem = this; + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + mDSPCodecPool_ADPCM.mSystem = this; + #endif + #ifdef FMOD_SUPPORT_XMA + mDSPCodecPool_XMA.mSystem = this; + #endif + #ifdef FMOD_SUPPORT_CELT + mDSPCodecPool_CELT.mSystem = this; + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + mDSPCodecPool_RAW.mSystem = this; + #endif +#endif + +#ifdef FMOD_SUPPORT_ASIO + mASIOSpeakerList[0] = FMOD_SPEAKER_FRONT_LEFT; + mASIOSpeakerList[1] = FMOD_SPEAKER_FRONT_RIGHT; + #if (DSP_MAXLEVELS_OUT > 2) + mASIOSpeakerList[2] = FMOD_SPEAKER_FRONT_CENTER; + mASIOSpeakerList[3] = FMOD_SPEAKER_LOW_FREQUENCY; + mASIOSpeakerList[4] = FMOD_SPEAKER_BACK_LEFT; + mASIOSpeakerList[5] = FMOD_SPEAKER_BACK_RIGHT; + #endif + #if (DSP_MAXLEVELS_OUT > 6) + mASIOSpeakerList[6] = FMOD_SPEAKER_SIDE_LEFT; + mASIOSpeakerList[7] = FMOD_SPEAKER_SIDE_RIGHT; + #endif + + #if (DSP_MAXLEVELS_OUT > 8) + mASIOSpeakerList[8] = FMOD_SPEAKER_FRONT_LEFT; + mASIOSpeakerList[9] = FMOD_SPEAKER_FRONT_RIGHT; + mASIOSpeakerList[10] = FMOD_SPEAKER_FRONT_LEFT; + mASIOSpeakerList[11] = FMOD_SPEAKER_FRONT_RIGHT; + mASIOSpeakerList[12] = FMOD_SPEAKER_FRONT_LEFT; + mASIOSpeakerList[13] = FMOD_SPEAKER_FRONT_RIGHT; + mASIOSpeakerList[14] = FMOD_SPEAKER_FRONT_LEFT; + mASIOSpeakerList[15] = FMOD_SPEAKER_FRONT_RIGHT; + #endif +#endif + + mDownmix = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::release() +{ + FMOD_RESULT result; + + if (mInitialized) + { + result = close(); + if (result != FMOD_OK) + { + return result; + } + } + + if (mOutput) + { + mOutput->release(); + mOutput = 0; + } + + #if defined(FMOD_SUPPORT_GEOMETRY) && defined(FMOD_SUPPORT_GEOMETRY_THREADED) + mGeometryMgr.releaseOcclusionThread(); + #endif + + removeNode(); + + FMOD_Memory_Free(this); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setOutput(FMOD_OUTPUTTYPE outputtype) +{ + FMOD_RESULT result = FMOD_OK; + int count, numoutputs; + + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + if (mOutput) + { + if (outputtype == mOutputType) + { + return FMOD_OK; + } + + mOutput->release(); + mOutput = 0; + } + + if (!mPluginsLoaded) + { + result = setUpPlugins(); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Scan plugin list and find an equal type. + */ + result = mPluginFactory->getNumOutputs(&numoutputs); + if (result != FMOD_OK) + { + return result; + } + + if (outputtype == FMOD_OUTPUTTYPE_AUTODETECT) + { + FMOD_OS_Output_GetDefault(&outputtype); + } + + for (count = 0; count < numoutputs; count++) + { + FMOD_OUTPUT_DESCRIPTION_EX *outputdesc = 0; + unsigned int handle; + + result = mPluginFactory->getOutputHandle(count, &handle); + if (result != FMOD_OK) + { + continue; + } + + result = mPluginFactory->getOutput(handle, &outputdesc); + if (result != FMOD_OK) + { + continue; + } + + if (outputdesc->mType == outputtype) + { + result = mPluginFactory->createOutput(outputdesc, &mOutput); + if (result != FMOD_OK) + { + return result; + } + + mOutputType = mOutput->mDescription.mType; + mOutputHandle = mOutput->mDescription.mHandle; + + return FMOD_OK; + } + } + + return FMOD_ERR_PLUGIN_MISSING; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getOutput(FMOD_OUTPUTTYPE *output) +{ + if (!output) + { + return FMOD_ERR_INVALID_PARAM; + } + + *output = mOutputType; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setSoftwareFormat(int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplermethod) +{ + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + if (samplerate < 8000 || samplerate > 192000) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (maxinputchannels > DSP_MAXLEVELS_IN || numoutputchannels > DSP_MAXLEVELS_OUT) + { + return FMOD_ERR_TOOMANYCHANNELS; + } + + mOutputRate = samplerate; + mOutputFormat = format; + mResampleMethod = resamplermethod; + + #ifndef PLATFORM_PS3 + if (numoutputchannels) + { + mMaxOutputChannels = numoutputchannels; + + mSpeakerMode = FMOD_SPEAKERMODE_RAW; + } + #endif + if (maxinputchannels > 0) + { + mMaxInputChannels = maxinputchannels; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getNumDrivers(int *numdrivers) +{ + FMOD_RESULT result; + + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mInitialized) + { + result = setOutput(mOutputType); + if (result != FMOD_OK) + { + *numdrivers = 0; + return result; + } + } + + result = checkDriverList(false); + if (result != FMOD_OK) + { + return result; + } + + if (mOutput->mDescription.getnumdrivers) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.getnumdrivers(mOutput, numdrivers); + } + + *numdrivers = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + FMOD_RESULT result; + int numdrivers; + + result = getNumDrivers(&numdrivers); + if (result != FMOD_OK) + { + return result; + } + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mInitialized) + { + result = setOutput(mOutputType); + if (result != FMOD_OK) + { + return result; + } + } + + if (mOutput->mDescription.getdriverinfo) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.getdriverinfo(mOutput, id, name, namelen, guid); + } + + if (mOutput->mDescription.getdrivername) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.getdrivername(mOutput, id, name, namelen); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + FMOD_RESULT result; + int numdrivers; + + result = getNumDrivers(&numdrivers); + if (result != FMOD_OK) + { + return result; + } + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mInitialized) + { + result = setOutput(mOutputType); + if (result != FMOD_OK) + { + return result; + } + } + + if (mOutput->mDescription.getdriverinfow) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.getdriverinfow(mOutput, id, name, namelen, guid); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) +{ + FMOD_RESULT result; + FMOD_CAPS lcaps; + FMOD_SPEAKERMODE lcontrolpanelspeakermode; + int lminfrequency; + int lmaxfrequency; + int lnum2dchannels; + int lnum3dchannels; + int ltotalchannels; + int numdrivers; + + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + result = getNumDrivers(&numdrivers); + if (result != FMOD_OK) + { + return result; + } + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mInitialized) + { + result = setOutput(mOutputType); + if (result != FMOD_OK) + { + return result; + } + } + + lcaps = FMOD_CAPS_NONE; + lminfrequency = 0; + lmaxfrequency = 0; + lnum2dchannels = 0; + lnum3dchannels = 0; + ltotalchannels = 0; + lcontrolpanelspeakermode = FMOD_SPEAKERMODE_STEREO; + + if (mOutput->mDescription.getdrivercapsex2) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + result = mOutput->mDescription.getdrivercapsex2(mOutput, id, &lcaps, &lminfrequency, &lmaxfrequency, &lcontrolpanelspeakermode, &lnum2dchannels, &lnum3dchannels, <otalchannels); + if (result != FMOD_OK) + { + return result; + } + } + if (mOutput->mDescription.getdrivercapsex) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + result = mOutput->mDescription.getdrivercapsex(mOutput, id, &lcaps, &lminfrequency, &lmaxfrequency, &lcontrolpanelspeakermode); + if (result != FMOD_OK) + { + return result; + } + } + else if (mOutput->mDescription.getdrivercaps) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + result = mOutput->mDescription.getdrivercaps(mOutput, id, &lcaps); + if (result != FMOD_OK) + { + return result; + } + } + + mOutput->mNum2DChannelsFromCaps = lnum2dchannels; + mOutput->mNum3DChannelsFromCaps = lnum3dchannels; + mOutput->mTotalChannelsFromCaps = ltotalchannels; + + if (caps) + { + *caps = lcaps; + } + if (minfrequency) + { + *minfrequency = lminfrequency; + } + if (maxfrequency) + { + *maxfrequency = lmaxfrequency; + } + if (controlpanelspeakermode) + { + *controlpanelspeakermode = lcontrolpanelspeakermode; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setDriver(int driver) +{ + FMOD_RESULT result = FMOD_OK; + int numdrivers = 0; + + result = getNumDrivers(&numdrivers); + if (result != FMOD_OK) + { + return result; + } + + if (driver < -1 || driver >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (driver == -1) + { + driver = 0; + } + + /* + If the driver is already initialised, we need to reset it + */ + if (mInitialized) + { +#ifdef FMOD_SUPPORT_SOFTWARE + int samplerate = 0; + FMOD_SOUND_FORMAT soundformat = FMOD_SOUND_FORMAT_NONE; + FMOD_SPEAKERMODE speakermode = FMOD_SPEAKERMODE_STEREO; + + if (mCreatedHardwareSample) +#endif + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::setDriver", "Cannot change driver when hardware samples have been created.\n")); + return FMOD_ERR_NEEDSSOFTWARE; + } + +#ifdef FMOD_SUPPORT_RECORDING + if (mOutput->mRecordNumActive) + { + result = mOutput->recordStopAll(false); + CHECK_RESULT(result); + } +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE + /* + Stop the current driver + */ + if (mOutput->mDescription.stop) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + mOutput->mDescription.stop(mOutput); + } + else if (mOutput->mDescription.polling) + { + OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput); + + outputpolled->stop(); + } + + /* + Close the current driver + */ + if (mOutput->mDescription.close) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + mOutput->mDescription.close(mOutput); + } + + /* + Initialise the new driver + */ + speakermode = mSpeakerMode; + samplerate = mOutputRate; + soundformat = mOutputFormat; + + if (mOutput->mDescription.initex) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + result = mOutput->mDescription.initex(mOutput, driver, mFlags, &samplerate, mMaxOutputChannels, &soundformat, &speakermode, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, 0, 0, NULL); + if (result != FMOD_OK) + { + return result; + } + } + else if (mOutput->mDescription.init) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + result = mOutput->mDescription.init(mOutput, driver, mFlags, &samplerate, mMaxOutputChannels, &soundformat, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, NULL); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Check if output mode changed the sample rate, format or speaker mode + */ + if (speakermode != mSpeakerMode || samplerate != mOutputRate || soundformat != mOutputFormat) + { + /* + Stop the new driver + */ + if (mOutput->mDescription.stop) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + mOutput->mDescription.stop(mOutput); + } + else if (mOutput->mDescription.polling) + { + OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput); + + outputpolled->stop(); + } + + /* + Close the new driver + */ + if (mOutput->mDescription.close) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + mOutput->mDescription.close(mOutput); + } + + // The new driver is not compatible with the current FMOD state, user must now choose a driver that is compatible (possibly the original one if it is still connected) + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::setDriver", "Selected driver does not support current output format, sample rate or number of channels.\n")); + return FMOD_ERR_OUTPUT_INIT; + } + + /* + Start the new driver + */ + if (mOutput->mDescription.start) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + result = mOutput->mDescription.start(mOutput); + if (result != FMOD_OK) + { + return result; + } + } + else if (mOutput->mDescription.polling) + { + OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput); + + result = outputpolled->start(); + if (result != FMOD_OK) + { + return result; + } + } +#endif + } + + mSelectedDriver = driver; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDriver(int *driver) +{ + if (!driver) + { + return FMOD_ERR_INVALID_PARAM; + } + + *driver = mSelectedDriver; + + return FMOD_OK; +} + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setHardwareChannels(int min2d, int max2d, int min3d, int max3d) +{ + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + if (min2d < 0 || max2d < 0 || min3d < 0 || max3d < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + mMinHardwareChannels2D = min2d; + mMaxHardwareChannels2D = max2d; + mMinHardwareChannels3D = min3d; + mMaxHardwareChannels3D = max3d; + + return FMOD_OK; +} + +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getHardwareChannels(int *num2d, int *num3d, int *total) +{ + int numhw3d = 0, numhw2d = 0, numhwtotal = 0; + +#ifndef FMOD_STATICFORPLUGINS + + if (mInitialized) + { + if (mOutput) + { + FMOD_RESULT result; + + if (mOutput->mChannelPool) + { + result = mOutput->mChannelPool->getNumChannels(&numhw2d); + if (result != FMOD_OK) + { + return result; + } + } + + if (mOutput->mChannelPool3D) + { + result = mOutput->mChannelPool3D->getNumChannels(&numhw3d); + if (result != FMOD_OK) + { + return result; + } + } + } + numhwtotal = numhw3d + numhw2d; + } + else + { + FMOD_RESULT result; + result = setOutput(mOutputType); + if (result != FMOD_OK) + { + return result; + } + + numhw2d = mOutput->mNum2DChannelsFromCaps; + numhw3d = mOutput->mNum3DChannelsFromCaps; + numhwtotal = mOutput->mTotalChannelsFromCaps; + } +#endif + + if (num3d) + { + *num3d = numhw3d; + } + + if (num2d) + { + *num2d = numhw2d; + } + + if (total) + { + *total = numhwtotal; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setSoftwareChannels(int numsoftwarechannels) +{ + if (numsoftwarechannels < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + mNumSoftwareChannels = numsoftwarechannels; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getSoftwareChannels(int *numsoftwarechannels) +{ + if (!numsoftwarechannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numsoftwarechannels = mNumSoftwareChannels; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setDSPBufferSize(unsigned int bufferlength, int numbuffers) +{ + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + + return FMOD_ERR_UNSUPPORTED; + + #else + + if (bufferlength < 1 || numbuffers < 2) + { + return FMOD_ERR_INVALID_PARAM; + } + + mDSPBlockSize = bufferlength; + mDSPBufferSize = bufferlength * numbuffers; + + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDSPBufferSize(unsigned int *bufferlength, int *numbuffers) +{ + if (bufferlength) + { + *bufferlength = mDSPBlockSize; + } + if (numbuffers) + { + *numbuffers = mDSPBufferSize / mDSPBlockSize; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign) +{ + setGlobalUserCallbacks(useropen, userclose, userread, userseek); + + if (blockalign >= 0) + { + mBufferSize = blockalign; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::attachFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek) +{ + mOpenRiderCallback = useropen; + mCloseRiderCallback = userclose; + mReadRiderCallback = userread; + mSeekRiderCallback = userseek; + + return FMOD_OK; +} + +#ifndef FMOD_STATICFORPLUGINS + + +/* +[ + [DESCRIPTION] + Sets advanced features like configuring memory and cpu usage for FMOD_CREATEREALTIMESAMPLE usage. + + [PARAMETERS] + 'settings' Pointer to FMOD_ADVANCEDSETTINGS structure. + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] + FMOD_ADVANCEDSETTINGS + SystemI::getAdvancedSettings +] +*/ +FMOD_RESULT SystemI::setAdvancedSettings(FMOD_ADVANCEDSETTINGS *settings) +{ + int memberoffset; + + if (!settings) + { + return FMOD_ERR_INVALID_PARAM; + } + + memberoffset = (int)((FMOD_UINT_NATIVE)&settings->maxXMAcodecs - (FMOD_UINT_NATIVE)settings); + + if (settings->cbsize <= memberoffset) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (settings->maxADPCMcodecs < 0 || settings->maxADPCMcodecs > 65535) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->maxMPEGcodecs < 0 || settings->maxMPEGcodecs > 65535) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->maxXMAcodecs < 0 || settings->maxXMAcodecs > 65535) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->maxCELTcodecs < 0 || settings->maxCELTcodecs > 65535) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->maxPCMcodecs < 0 || settings->maxPCMcodecs > 65535) + { + return FMOD_ERR_INVALID_PARAM; + } + + { + FMOD_RESULT result; + + result = FMOD_CHECKFLOAT(settings->HRTFMinAngle); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(settings->HRTFMaxAngle); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(settings->HRTFFreq); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(settings->vol0virtualvol); + if (result != FMOD_OK) + { + return result; + } + } + + if (settings->HRTFMinAngle < 0 || settings->HRTFMinAngle > 360) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->HRTFMaxAngle < settings->HRTFMinAngle || settings->HRTFMaxAngle > 360.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->HRTFFreq < 0.0001f) + { + settings->HRTFFreq = mAdvancedSettings.HRTFFreq; + } + else if (settings->HRTFFreq < 10.0f || settings->HRTFFreq > 22050.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->vol0virtualvol < 0.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->eventqueuesize < 0 || settings->eventqueuesize > 65535) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->ASIONumChannels > DSP_MAXLEVELS_OUT || settings->ASIONumChannels < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->debugLogFilename && FMOD_strlen(settings->debugLogFilename) >= 255) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_DEBUG + if (settings->debugLogFilename) + { + FMOD_strcpy(FMOD::gGlobal->gDebugFilename, settings->debugLogFilename); + } +#endif + + if ((settings->defaultDecodeBufferSize == 0) || (settings->defaultDecodeBufferSize > 30000)) + { + settings->defaultDecodeBufferSize = mAdvancedSettings.defaultDecodeBufferSize; + } + + FMOD_memcpy(&mAdvancedSettings, settings, settings->cbsize); + +#ifdef FMOD_DEBUG + if (settings->debugLogFilename) /* Dont store a pointer to the user name, set it back to mAdvancedSettings debuglog filename here. */ + { + mAdvancedSettings.debugLogFilename = FMOD::gGlobal->gDebugFilename; + } +#endif + +#ifdef FMOD_SUPPORT_ASIO + if (settings->ASIOSpeakerList) + { + int count; + + for (count = 0; count < settings->ASIONumChannels; count++) + { + mASIOSpeakerList[count] = settings->ASIOSpeakerList[count]; + } + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] + FMOD_ADVANCEDSETTINGS + SystemI::setAdvancedSettings +] +*/ +FMOD_RESULT SystemI::getAdvancedSettings(FMOD_ADVANCEDSETTINGS *settings) +{ + int usercbsize; + int asioNumChannels; + FMOD_SPEAKER *speakerlist; + char *debuglogfilename; + + if (!settings) + { + return FMOD_ERR_INVALID_PARAM; + } + if (settings->ASIONumChannels > DSP_MAXLEVELS_OUT || settings->ASIONumChannels < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + usercbsize = settings->cbsize; + speakerlist = settings->ASIOSpeakerList; + asioNumChannels = settings->ASIONumChannels; + debuglogfilename = settings->debugLogFilename; + + FMOD_memcpy(settings, &mAdvancedSettings, usercbsize); + + settings->cbsize = usercbsize; + settings->ASIOSpeakerList = speakerlist; + settings->debugLogFilename = debuglogfilename; + +#ifdef FMOD_DEBUG + if (settings->debugLogFilename) + { + FMOD_strcpy(settings->debugLogFilename, FMOD::gGlobal->gDebugFilename); + } +#endif + + #ifdef FMOD_SUPPORT_ASIO + if (settings->ASIONumChannels > DSP_MAXLEVELS_OUT) + { + settings->ASIONumChannels = DSP_MAXLEVELS_OUT; + } + if (settings->ASIOSpeakerList) + { + int count; + + for (count = 0; count < asioNumChannels; count++) + { + settings->ASIOSpeakerList[count] = mASIOSpeakerList[count]; + } + } + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setSpeakerMode(FMOD_SPEAKERMODE speakermode) +{ + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + mSpeakerMode = speakermode; + + switch (mSpeakerMode) + { + case FMOD_SPEAKERMODE_RAW: + { + /* Dont change mMaxOutputChannels */ + return FMOD_OK; + } + case FMOD_SPEAKERMODE_MONO: + { + mMaxOutputChannels = 1; + break; + } + case FMOD_SPEAKERMODE_STEREO: + { + mMaxOutputChannels = 2; + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 0.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 0.0f); + break; + } + case FMOD_SPEAKERMODE_QUAD: + { + mMaxOutputChannels = 4; + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f); + break; + } + case FMOD_SPEAKERMODE_SURROUND: + { + mMaxOutputChannels = 5; + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f); + break; + } + case FMOD_SPEAKERMODE_5POINT1: + { + mMaxOutputChannels = 6; + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f); + break; + + } + case FMOD_SPEAKERMODE_7POINT1: + { + mMaxOutputChannels = 8; + + #if defined(FMOD_SUPPORT_MYEARS) && !defined(PLATFORM_PS3) + /* MyEars uses standard 7.1 speaker positioning whereas FMOD dos not */ + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -0.5f, 0.866f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 0.5f, 0.866f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -0.707f, -0.707f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 0.707f, -0.707f); + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -1.0f, 0.0f); + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 1.0f, 0.0f); + + #else + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f); + + #ifdef PLATFORM_PS3 + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -0.4f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 0.4f, -1.0f); + #else + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -1.0f, 0.0f); + set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 1.0f, 0.0f); + #endif + + #endif + + break; + } + case FMOD_SPEAKERMODE_PROLOGIC: + { + mMaxOutputChannels = 2; + + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f); + set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f); + break; + } + default: + { + break; + } + } + + return sortSpeakerList(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setCallback(FMOD_SYSTEM_CALLBACK callback) +{ + FMOD::gGlobal->gSystemCallback = callback; + mCallback = callback; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setPluginPath(const char *path) +{ + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + if (FMOD_strlen(path) >= FMOD_STRING_MAXPATHLEN) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_strncpy(mPluginPath, path, FMOD_STRING_MAXPATHLEN); + + if (mPluginFactory) + { + mPluginFactory->setPluginPath(mPluginPath); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::loadPlugin(const char *filename, unsigned int *handle, unsigned int priority) +{ +#ifdef FMOD_SUPPORT_DLLS + FMOD_RESULT result; + + if (!mPluginsLoaded) + { + result = setUpPlugins(); + if (result != FMOD_OK) + { + return result; + } + } + + return mPluginFactory->loadPlugin(filename, handle, false, priority); + +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getNumPlugins(FMOD_PLUGINTYPE type, int *numplugins) +{ + FMOD_RESULT result; + + if (!numplugins) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mPluginsLoaded) + { + result = setUpPlugins(); + if (result != FMOD_OK) + { + return result; + } + } + + switch (type) + { + case FMOD_PLUGINTYPE_OUTPUT: + { + mPluginFactory->getNumOutputs(numplugins); + break; + } + case FMOD_PLUGINTYPE_CODEC: + { + mPluginFactory->getNumCodecs(numplugins); + break; + } + case FMOD_PLUGINTYPE_DSP: + { + mPluginFactory->getNumDSPs(numplugins); + break; + } + default: + { + return FMOD_ERR_INVALID_PARAM; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getPluginHandle(FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle) +{ + switch (plugintype) + { + case FMOD_PLUGINTYPE_CODEC: + { + return mPluginFactory->getCodecHandle(index, handle); + } + case FMOD_PLUGINTYPE_DSP: + { + return mPluginFactory->getDSPHandle(index, handle); + } + case FMOD_PLUGINTYPE_OUTPUT: + { + return mPluginFactory->getOutputHandle(index, handle); + } + default: + { + return FMOD_ERR_INVALID_PARAM; + } + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getPluginInfo(unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version) +{ + FMOD_RESULT result; + FMOD_OUTPUT_DESCRIPTION_EX *outputdesc; + FMOD_CODEC_DESCRIPTION_EX *codecdesc; + FMOD_DSP_DESCRIPTION_EX *dspdesc; + + if (!mPluginsLoaded) + { + result = setUpPlugins(); + if (result != FMOD_OK) + { + return result; + } + } + + result = mPluginFactory->getOutput(handle, &outputdesc); + if (result == FMOD_OK) + { + if (name) + { + FMOD_strncpy(name, outputdesc->name, namelen); + } + if (version) + { + *version = outputdesc->version; + } + if (plugintype) + { + *plugintype = FMOD_PLUGINTYPE_OUTPUT; + } + } + else if (result == FMOD_ERR_PLUGIN_MISSING) + { + result = mPluginFactory->getCodec(handle, &codecdesc); + if (result == FMOD_OK) + { + if (name) + { + FMOD_strncpy(name, codecdesc->name, namelen); + } + if (version) + { + *version = codecdesc->version; + } + if (plugintype) + { + *plugintype = FMOD_PLUGINTYPE_CODEC; + } + } + else if (result == FMOD_ERR_PLUGIN_MISSING) + { + result = mPluginFactory->getDSP(handle, &dspdesc); + if (result == FMOD_OK) + { + if (name) + { + FMOD_strncpy(name, dspdesc->name, namelen); + } + if (version) + { + *version = dspdesc->version; + } + if (plugintype) + { + *plugintype = FMOD_PLUGINTYPE_DSP; + } + } + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::unloadPlugin(unsigned int handle) +{ + FMOD_RESULT result; + + if (!mPluginsLoaded) + { + result = setUpPlugins(); + if (result != FMOD_OK) + { + return result; + } + } + + return mPluginFactory->unloadPlugin(handle); +} + + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setOutputByPlugin(unsigned int handle) +{ + FMOD_RESULT result = FMOD_OK; + FMOD_OUTPUT_DESCRIPTION_EX *outputdesc = 0; + + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + if (mOutput) + { + mOutput->release(); + mOutput = 0; + } + + if (!mPluginsLoaded) + { + result = setUpPlugins(); + if (result != FMOD_OK) + { + return result; + } + } + + result = mPluginFactory->getOutput(handle, &outputdesc); + if (result != FMOD_OK) + { + return result; + } + + result = mPluginFactory->createOutput(outputdesc, &mOutput); + if (result != FMOD_OK) + { + return result; + } + + mOutputType = mOutput->mDescription.mType; + mOutputHandle = mOutput->mDescription.mHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getOutputByPlugin(unsigned int *handle) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = mOutputHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +class SystemInitCleanup +{ +public: + SystemInitCleanup() + : mSystem(0), mSpeakerMode(FMOD_SPEAKERMODE_MAX), + mOutputFormat(FMOD_SOUND_FORMAT_MAX), mOutputRate(0), + mSoundListCrit(0), mFileCrit(0), mAsyncCrit(0), + mProfilerInitialized(false) + { } + + void setSystem(SystemI *system) + { + mSystem = system; + mSpeakerMode = system->mSpeakerMode; + mOutputFormat = system->mOutputFormat; + mOutputRate = system->mOutputRate; + } + + void profilerInitialized() { mProfilerInitialized = true; } + void setSoundListCrit(FMOD_OS_CRITICALSECTION **crit) { mSoundListCrit = crit; } + void setFileCrit(FMOD_OS_CRITICALSECTION **crit) { mFileCrit = crit; } + void setAsyncCrit(FMOD_OS_CRITICALSECTION **crit) { mAsyncCrit = crit; } + + void cleanup(bool) + { + if(mSystem) + { + if(mSpeakerMode != FMOD_SPEAKERMODE_MAX) + { + mSystem->setSpeakerMode(mSpeakerMode); + } + if (mOutputFormat != FMOD_SOUND_FORMAT_MAX) + { + mSystem->mOutputFormat = mOutputFormat; + } + if (mOutputRate != 0) + { + mSystem->mOutputRate = mOutputRate; + } + mSystem->close(); + +#ifdef FMOD_SUPPORT_PROFILE + if (FMOD::gGlobal->gSystemInitCount == 0 && mProfilerInitialized) + { + FMOD_Profile_Release(); + } +#endif + } + if(mAsyncCrit) + { + FMOD_OS_CriticalSection_Free(*mAsyncCrit); + *mAsyncCrit = 0; + } + if(mFileCrit) + { + FMOD_OS_CriticalSection_Free(*mFileCrit); + *mFileCrit = 0; + } + if(mSoundListCrit) + { + FMOD_OS_CriticalSection_Free(*mSoundListCrit); + *mSoundListCrit = 0; + } + } + +private: + SystemI *mSystem; + FMOD_SPEAKERMODE mSpeakerMode; + FMOD_SOUND_FORMAT mOutputFormat; + int mOutputRate; + FMOD_OS_CRITICALSECTION **mSoundListCrit; + FMOD_OS_CRITICALSECTION **mFileCrit; + FMOD_OS_CRITICALSECTION **mAsyncCrit; + bool mProfilerInitialized; +}; + + +FMOD_RESULT SystemI::init(int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata) +{ + int count; + FMOD_RESULT result; + int channellimit = (int)(1 << CHANINDEX_BITS); + FMOD_SPEAKERMODE speakerMode; + AutoCleanup<bool, SystemInitCleanup> initCleanup(true); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "FMOD Ex Version: %08x\n", FMOD_VERSION)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "maxchannels = %d, flags = %08x, extradriverdata = %p\n", maxchannels, flags, extradriverdata)); + + if (maxchannels < 0 || maxchannels >= channellimit) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mInitialized) + { + return FMOD_ERR_INITIALIZED; + } + + result = closeEx(true); + if (result != FMOD_OK) + { + return result; + } + + initCleanup.setSystem(this); + + mFlags = flags; + FMOD_OS_Thread_GetCurrentID(&mMainThreadID); + + /* + Set up output driver for this system + */ + result = setOutput(mOutputType); + if (result != FMOD_OK) + { + return result; + } + +#if defined(FMOD_SUPPORT_MYEARS) && !defined(PLATFORM_PS3) + bool enablemyears = !(mFlags & FMOD_INIT_DISABLE_MYEARS) && !mDownmix && DownmixMyEars::dataExists(); + if (enablemyears) + { + mOutputRate = DownmixMyEars::getSampleRate(); + } +#endif + + if (mOutput->mDescription.initex) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + speakerMode = mSpeakerMode; + result = mOutput->mDescription.initex(mOutput, mSelectedDriver, flags, &mOutputRate, mMaxOutputChannels, &mOutputFormat, &speakerMode, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, 0, 0, extradriverdata); + if (result != FMOD_OK) + { + return result; + } + + if (speakerMode != mSpeakerMode) + { + setSpeakerMode(speakerMode); + } + } + else if (mOutput->mDescription.init) + { + int outputchannels = mMaxOutputChannels; + +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + result = mOutput->mDescription.init(mOutput, mSelectedDriver, flags, &mOutputRate, outputchannels, &mOutputFormat, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, extradriverdata); + if (result != FMOD_OK) + { + return result; + } + } + +#if defined(FMOD_SUPPORT_NEURAL) && !defined(PLATFORM_PS3) + if ((flags & FMOD_INIT_DTS_NEURALSURROUND) && (mSpeakerMode == FMOD_SPEAKERMODE_STEREO || mSpeakerMode == FMOD_SPEAKERMODE_5POINT1) && !mDownmix) + { + mDownmix = FMOD_Object_Calloc(DownmixNeural); + if (!mDownmix) + { + return FMOD_ERR_MEMORY; + } + + result = mDownmix->init(mDSPBufferSize, mOutputRate, mSpeakerMode); + if (result != FMOD_OK) + { + return result; + } + + /* + Software mixer should always be mixing in 7.1 if we are using Neural + */ + setSpeakerMode(FMOD_SPEAKERMODE_7POINT1); + } +#endif + +#if defined(FMOD_SUPPORT_MYEARS) && !defined(PLATFORM_PS3) + /* + MyEars will be enabled by default as long as the data exists + and another downmixer such as NEURALSURROUND has not been set. + */ + if (enablemyears && !mDownmix) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Load MyEars data\n")); + + mDownmix = FMOD_Object_Calloc(DownmixMyEars); + if (!mDownmix) + { + return FMOD_ERR_MEMORY; + } + + result = mDownmix->init(mDSPBufferSize, mOutputRate, mSpeakerMode, 0); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Failed to initialise MyEars. Returned error (%d)\n", result)); + + mDownmix->shutdown(); + + FMOD_Memory_Free(mDownmix); + mDownmix = 0; + enablemyears = false; + } + else + { + /* + Software mixer should always be mixing in 7.1 if we are using MyEars (or 5.1 for testing) + */ + int inputchannels; + mDownmix->getInputChannels(&inputchannels); + switch(inputchannels) + { + case 8: + setSpeakerMode(FMOD_SPEAKERMODE_7POINT1); + break; + case 6: + setSpeakerMode(FMOD_SPEAKERMODE_5POINT1); + break; + default: + result = mDownmix->shutdown(); + if (result != FMOD_OK) + { + return result; + } + FMOD_Memory_Free(mDownmix); + mDownmix = 0; + return FMOD_ERR_INTERNAL; + } + + /* + Override software HRTF to false when using MyEars + */ + mFlags &= ~FMOD_INIT_SOFTWARE_HRTF; + } + } +#endif + +#ifdef FMOD_SUPPORT_SOFTWARE + if (!(mFlags & FMOD_INIT_SOFTWARE_DISABLE)) + { + FMOD_DSP_DESCRIPTION description; + FMOD_DSP_DESCRIPTION_EX descriptionex; + FMOD_SOUND_FORMAT bufferformat; + int bufferchannels; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Set up software engine\n")); + + /* + Set up the 'software' output object. + */ + mSoftware = FMOD_Object_Alloc(OutputSoftware); + if (!mSoftware) + { + return FMOD_ERR_MEMORY; + } + mSoftware->mSystem = this; + + if (!mDSPCrit) + { + result = FMOD_OS_CriticalSection_Create(&mDSPCrit); + if (result != FMOD_OK) + { + return result; + } + } + if (!mDSPLockCrit) + { + result = FMOD_OS_CriticalSection_Create(&mDSPLockCrit); + if (result != FMOD_OK) + { + return result; + } + } + if (!mDSPConnectionCrit) + { + result = FMOD_OS_CriticalSection_Create(&mDSPConnectionCrit); + if (result != FMOD_OK) + { + return result; + } + } +#ifdef FMOD_NEED_DSPCODECPOOLINITCRIT + if(!mDSPCodecPoolInitCrit) + { + result = FMOD_OS_CriticalSection_Create(&mDSPCodecPoolInitCrit); + if (result != FMOD_OK) + { + return result; + } + } +#endif + + mConnectionRequestUsedHead.initNode(); + mConnectionRequestFreeHead.initNode(); + + for (count = 0; count < FMOD_DSP_CONNECTION_REQUEST_MAX; count++) + { + DSPConnectionRequest *request = &mConnectionRequest[count]; + + request->initNode(); + request->addBefore(&mConnectionRequestFreeHead); + + } + + result = getSoftwareFormat(0, &bufferformat, &bufferchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (mMaxInputChannels < bufferchannels) + { + mMaxInputChannels = bufferchannels; + } + + /* + Create a global intermediate pair of buffers for the DSP engine to use. + */ + mDSPTempBuffMem = (float *)FMOD_Memory_Calloc((mDSPBlockSize * (bufferchannels < mMaxInputChannels ? mMaxInputChannels : bufferchannels) * sizeof(float)) + 16); + if (!mDSPTempBuffMem) + { + return FMOD_ERR_MEMORY; + } + mDSPTempBuff = (float *)FMOD_ALIGNPOINTER(mDSPTempBuffMem, 16); + + /* + Create a pool of connections so we dont have to use malloc/free each time a connection/disconnection happens. + Usually with the default software engine it is: + 1 for soundcard to mixer. + 'mNumSoftwareChannels' for mixer to channel head(s) + 'mNumSoftwareChannels' for channelhead(s) to wavetable(s)/generator(s). + */ + result = mDSPConnectionPool.init(this, 1 + (mNumSoftwareChannels * 2), bufferchannels < 2 ? 2 : bufferchannels, mMaxInputChannels); + if (result != FMOD_OK) + { + return result; + } + + /* + Create the soundcard unit and main mixer unit then connect them together. + */ + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD SoundCard Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = bufferchannels; + descriptionex.create = 0; + descriptionex.release = 0; + descriptionex.read = 0; + descriptionex.setposition = 0; + descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + descriptionex.mFormat = bufferformat; + result = createDSP(&descriptionex, &mDSPSoundCard); + if (result != FMOD_OK) + { + return result; + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + mDSPSoundCard->updateTreeLevel(0); /* Set root to 0 to get initial tree level for rest of network to base off. */ + #endif + mDSPSoundCard->setActive(true); + + FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION)); + FMOD_strcpy(description.name, "FMOD ChannelGroup Target Unit"); + description.version = 0x00010100; + description.channels = 0; + description.create = 0; + description.release = 0; + description.read = 0; + description.setposition = 0; + result = createDSP(&description, &mDSPChannelGroupTarget); + if (result != FMOD_OK) + { + return result; + } + + mDSPChannelGroupTarget->setDefaults((float)mOutputRate, -1, -1, -1); + mDSPChannelGroupTarget->setActive(true); + + result = mDSPSoundCard->addInput(mDSPChannelGroupTarget); + if (result != FMOD_OK) + { + return result; + } + } +#endif + + result = createChannelGroup("FMOD master group", &mChannelGroup); + if (result != FMOD_OK) + { + return result; + } + + result = createSoundGroup("FMOD master group", &mSoundGroup); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (!(mFlags & FMOD_INIT_SOFTWARE_DISABLE)) + { + result = mSoftware->init(mNumSoftwareChannels); + if (result != FMOD_OK) + { + return result; + } + + /* + If the user wants to add extra code around the polled start, + check here for overriden start function. + Plugin writer must call OutputPolled::start. + */ + if (mOutput->mDescription.start) + { + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ + + result = mOutput->mDescription.start(mOutput); + if (result != FMOD_OK) + { + return result; + } + } + else if (mOutput->mDescription.polling) + { + OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput); + + result = outputpolled->start(); + if (result != FMOD_OK) + { + return result; + } + } + } +#endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Set up emulated output\n")); + + /* + Set up emulated output for virtual channels + */ + if (maxchannels > 0) + { + mEmulated = FMOD_Object_Alloc(OutputEmulated); + if (!mEmulated) + { + return FMOD_ERR_MEMORY; + } + + mEmulated->mSystem = this; + + result = mEmulated->init(maxchannels); + if (result != FMOD_OK) + { + return result; + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "create the channel pool\n")); + + /* + Create a pool of channels the user will interface with, that will contain pointers to real or virtual channels + */ + if (maxchannels) + { + mChannel = (ChannelI *)FMOD_Memory_Calloc(sizeof(ChannelI) * maxchannels); + if (!mChannel) + { + return FMOD_ERR_MEMORY; + } + + mNumChannels = maxchannels; + + for (count = 0; count < mNumChannels; count++) + { + new (&mChannel[count]) ChannelI(count, this); + + mChannel[count].addAfter(&mChannelFreeListHead); + mChannel[count].setChannelGroup(mChannelGroup); /* Put channels into primary channel group */ + } + } + + /* + Global list of sounds critical section + */ + if (!gSoundListCrit) + { + result = FMOD_OS_CriticalSection_Create(&gSoundListCrit); + if (result != FMOD_OK) + { + return result; + } + + initCleanup.setSoundListCrit(&gSoundListCrit); + } + + /* + Sound lock critical section + */ + if (!mMultiSubSampleLockBufferCrit) + { + result = FMOD_OS_CriticalSection_Create(&mMultiSubSampleLockBufferCrit); + if (result != FMOD_OK) + { + return result; + } + } + +#ifdef FMOD_SUPPORT_STREAMING + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Set up streamer\n")); + + /* + Create stream thread. + */ + if (!mStreamThreadActive && !(mFlags & FMOD_INIT_STREAM_FROM_UPDATE)) /* Single buffered is not nonblocking! */ + { + AutoFreeCrit realchanCritCleanup; + AutoFreeCrit updateCritCleanup; + AutoFreeCrit listCritCleanup; + + result = FMOD_OS_CriticalSection_Create(&mStreamRealchanCrit); + if (result != FMOD_OK) + { + return result; + } + realchanCritCleanup = mStreamRealchanCrit; + result = FMOD_OS_CriticalSection_Create(&mStreamUpdateCrit); + if (result != FMOD_OK) + { + return result; + } + updateCritCleanup = mStreamUpdateCrit; + result = FMOD_OS_CriticalSection_Create(&mStreamListCrit); + if (result != FMOD_OK) + { + return result; + } + listCritCleanup = mStreamListCrit; + + result = mStreamThread.initThread("FMOD stream thread", streamThread, this, STREAM_THREADPRIORITY, 0, STREAM_STACKSIZE, false, 10, this); + if (result != FMOD_OK) + { + return result; + } + mStreamThreadActive = true; + realchanCritCleanup.releasePtr(); + updateCritCleanup.releasePtr(); + listCritCleanup.releasePtr(); + } + + if (!FMOD::gGlobal->gFileCrit) + { + result = FMOD_OS_CriticalSection_Create(&FMOD::gGlobal->gFileCrit); + if (result != FMOD_OK) + { + return result; + } + initCleanup.setFileCrit(&FMOD::gGlobal->gFileCrit); + } + + if (!FMOD::gGlobal->gAsyncCrit) + { + result = FMOD_OS_CriticalSection_Create(&FMOD::gGlobal->gAsyncCrit); + if (result != FMOD_OK) + { + return result; + } + initCleanup.setAsyncCrit(&FMOD::gGlobal->gAsyncCrit); + } + +#endif + + /* + Initialize codecs up front if specified. + */ +#ifdef FMOD_SUPPORT_DSPCODEC + if (!(mFlags & FMOD_INIT_SOFTWARE_DISABLE)) + { + #ifdef FMOD_SUPPORT_IMAADPCM + if (mAdvancedSettings.maxADPCMcodecs) + { + int count; + + result = mDSPCodecPool_ADPCM.init(FMOD_DSP_CATEGORY_DSPCODECADPCM, 64, mAdvancedSettings.maxADPCMcodecs); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mDSPCodecPool_ADPCM.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_ADPCM.mPool[count]); + CodecWav *wav = (CodecWav *)dspcodec->mCodec; + + wav->mSrcFormat = &wav->mSrcFormatMemory; + wav->mReadBuffer = mDSPCodecPool_ADPCM.mReadBuffer; + wav->mSrcFormat->Format.wFormatTag = WAVE_FORMAT_IMA_ADPCM; + } + } + #endif + #ifdef FMOD_SUPPORT_MPEG + if (mAdvancedSettings.maxMPEGcodecs) + { + int count; + + result = mDSPCodecPool_MPEG.init(FMOD_DSP_CATEGORY_DSPCODECMPEG, 1152, mAdvancedSettings.maxMPEGcodecs); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mDSPCodecPool_MPEG.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_MPEG.mPool[count]); + CodecMPEG *mpeg = (CodecMPEG *)dspcodec->mCodec; + + mpeg->mSrcDataOffset = 0; /* Raw data will start at 0. */ + mpeg->mWaveFormatMemory = 0; /* This will be set up upon play. */ + mpeg->reset(); + } + } + #endif + #ifdef FMOD_SUPPORT_XMA + if (mAdvancedSettings.maxXMAcodecs) + { + int count; + + result = mDSPCodecPool_XMA.init(FMOD_DSP_CATEGORY_DSPCODECXMA, 512, mAdvancedSettings.maxXMAcodecs); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < mDSPCodecPool_XMA.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_XMA.mPool[count]); + CodecXMA *xma = (CodecXMA *)dspcodec->mCodec; + + xma->mSrcDataOffset = 0; /* Raw data will start at 0. */ + xma->mWaveFormatMemory = 0; /* This will be set up upon play. */ + + result = xma->XMAInit(1); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + #ifdef FMOD_SUPPORT_CELT + if (mAdvancedSettings.maxCELTcodecs) + { + int count; + FMOD_CODEC_DESCRIPTION_EX *desc; + + result = mPluginFactory->getCodec(mCELTPluginHandle, &desc); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPCodecPool_CELT.init(FMOD_DSP_CATEGORY_DSPCODECCELT, FMOD_CELT_FRAMESIZESAMPLES, mAdvancedSettings.maxCELTcodecs); + if (result != FMOD_OK) + { + close(); + return result; + } + + for (count = 0; count < mDSPCodecPool_CELT.mNumDSPCodecs; count++) + { + DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_CELT.mPool[count]); + CodecCELT *celt = (CodecCELT *)dspcodec->mCodec; + + celt->mSrcDataOffset = 0; /* Raw data will start at 0. */ + celt->mWaveFormatMemory = 0; /* This will be set up upon play. */ + + result = desc->init(celt, 1); + if (result != FMOD_OK) + { + return result; + } + } + } + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + if (mAdvancedSettings.maxPCMcodecs) + { + result = mDSPCodecPool_RAW.init(FMOD_DSP_CATEGORY_DSPCODECRAW, 256, mAdvancedSettings.maxPCMcodecs); + if (result != FMOD_OK) + { + return result; + } + } + #endif + } +#endif + + /* + Initialize 2D and 3D reverb masters + */ + { + const FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF; + + result = mReverbGlobal.init(this, false); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_REVERB + result = setReverbProperties(&prop); + if (result != FMOD_OK) + { + return result; + } +#endif + +#ifdef FMOD_SUPPORT_MULTIREVERB + result = mReverb3D.init(this, false); + if (result != FMOD_OK) + { + return result; + } + + result = set3DReverbProperties(&prop); + if (result != FMOD_OK) + { + return result; + } +#endif + } + + if (mFlags & FMOD_INIT_ENABLE_PROFILE) + { +#ifdef FMOD_SUPPORT_PROFILE + result = FMOD_Profile_Create(mAdvancedSettings.profileport); + if (result != FMOD_OK) + { + return result; + } + initCleanup.profilerInitialized(); +#endif + +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP) + result = FMOD_ProfileDsp_Create(); + if (result != FMOD_OK) + { + return result; + } +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY) + result = FMOD_ProfileMemory_Create(); + if (result != FMOD_OK) + { + return result; + } +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CPU) + result = FMOD_ProfileCpu_Create(); + if (result != FMOD_OK) + { + return result; + } +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL) + result = FMOD_ProfileChannel_Create(); + if (result != FMOD_OK) + { + return result; + } +#endif +#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CODEC) + result = FMOD_ProfileCodec_Create(); + if (result != FMOD_OK) + { + return result; + } +#endif + } + + +#ifdef FMOD_SUPPORT_VSTPLUGIN + mVSTPluginsListHead.initNode(); +#endif + + mSpeakerLevelsPool.mSystem = this; + result = mHistoryBufferPool.init(mAdvancedSettings.maxSpectrumWaveDataBuffers, FMOD_MAX(mMaxOutputChannels, mMaxInputChannels) ); + if (result != FMOD_OK) + { + return result; + } + + mInitialized = true; + + gGlobal->gSystemInitCount++; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "done\n\n")); + + initCleanup.releasePtr(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::close() +{ + return closeEx(false); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::closeEx(bool calledfrominit) +{ + FMOD_RESULT result; + int count; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "\n")); + + update(); /* Execute an update to make sure everything is in sync. */ + +#ifdef FMOD_SUPPORT_RECORDING + if (mOutput && mOutput->mRecordNumActive) + { + result = mOutput->recordStopAll(false); + CHECK_RESULT(result); + } +#endif + + /* + Stop all sounds + */ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Stop all sounds\n")); + for (count = 0; count < mNumChannels; count++) + { + mChannel[count].stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP |CHANNELI_STOPFLAG_UPDATESYNCPOINTS); + } + + update(); /* For ps2 it needs to execute an update to get the channel commands to execute. */ + + #ifdef FMOD_SUPPORT_STREAMING + if (mStreamThreadActive) + { + mStreamThread.closeThread(); + mStreamThreadActive = false; + + FMOD_OS_CriticalSection_Free(mStreamRealchanCrit); + mStreamRealchanCrit = 0; + + FMOD_OS_CriticalSection_Free(mStreamUpdateCrit); + mStreamUpdateCrit = 0; + + FMOD_OS_CriticalSection_Free(mStreamListCrit); + mStreamListCrit = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Stream thread destroyed\n")); + } + #endif + + /* + Shut down streamer and async thread, only if this is the last instance of a system object. + */ + if (gGlobal->gSystemInitCount == 1 && mInitialized) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Shut down streamer and FMOD_NONBLOCKING and FileSystem thread.\n")); + + #ifdef FMOD_SUPPORT_NONBLOCKING + result = AsyncThread::shutDown(); + if (result!= FMOD_OK) + { + return result; + } + #endif + + if (FMOD::gGlobal->gAsyncCrit) + { + FMOD_OS_CriticalSection_Free(FMOD::gGlobal->gAsyncCrit); + FMOD::gGlobal->gAsyncCrit = 0; + } + + if (gSoundListCrit) + { + FMOD_OS_CriticalSection_Free(gSoundListCrit); + gSoundListCrit = 0; + } + + if (mFlags & FMOD_INIT_ENABLE_PROFILE) + { +#ifdef FMOD_SUPPORT_PROFILE + result = FMOD_Profile_Release(); + if (result != FMOD_OK) + { + return result; + } +#endif + } + + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Shut down file system.\n")); + result = File::shutDown(); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Recursively remove all channelgroups. + */ + if (mChannelGroup) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free master channel group.\n")); + + result = mChannelGroup->releaseInternal(true); + if (result != FMOD_OK) + { + return result; + } + mChannelGroup = 0; + } + + /* + Remove 'main' sound group. + */ + if (mSoundGroup) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Remove 'master' sound group.\n")); + + result = mSoundGroup->releaseInternal(); + if (result != FMOD_OK) + { + return result; + } + mSoundGroup = 0; + } + + if (mOutput) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Shut down output.\n")); + + if (mOutput->mDescription.stop) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + mOutput->mDescription.stop(mOutput); + } + else if (mOutput->mDescription.polling) + { + OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput); + + outputpolled->stop(); + } + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mDSPChannelGroupTarget) + { + result = mDSPChannelGroupTarget->release(); + mDSPChannelGroupTarget = 0; + } +#endif + +#ifdef FMOD_SUPPORT_MULTIREVERB + set3DReverbActive(false); /* Stop it from creating new reverbs inside update functions. */ + + ReverbI *reverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext()); + ReverbI *reverb_tmp; + + while (reverb_c != &mReverb3DHead) + { + reverb_tmp = reverb_c; + reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()); + reverb_tmp->release(true); + } + mReverb3D.release(false); +#endif + + mReverbGlobal.release(false); + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mDSPTempBuffMem) + { + FMOD_Memory_Free(mDSPTempBuffMem); + mDSPTempBuff = mDSPTempBuffMem = 0; + } +#endif + if (mOutput) + { + /* + Dont clean up the output if it is coming from init, because the user set up the output pre-init. + */ + if (calledfrominit) + { + if (mOutput->mDescription.close) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + mOutput->mDescription.close(mOutput); + } + } + else + { + + result = mOutput->release(); + mOutput = 0; + } + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mSoftware) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free software output.\n")); + + result = mSoftware->release(); + mSoftware = 0; + } +#endif + + if (mEmulated) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free emulated output.\n")); + + result = mEmulated->release(); + mEmulated = 0; + } + + + /* + Free codec pools for FMOD_CREATECOMPRESSEDSAMPLE. + */ +#ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + result = mDSPCodecPool_MPEG.close(); + if (result != FMOD_OK) + { + return result; + } + #endif + #ifdef FMOD_SUPPORT_XMA + result = mDSPCodecPool_XMA.close(); + if (result != FMOD_OK) + { + return result; + } + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + result = mDSPCodecPool_ADPCM.close(); + if (result != FMOD_OK) + { + return result; + } + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + result = mDSPCodecPool_RAW.close(); + if (result != FMOD_OK) + { + return result; + } + #endif +#endif + + if (mChannel) + { + int count; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free channel pool.\n")); + + for (count = 0; count < mNumChannels; count++) + { + if (mChannel[count].mLevels) + { + FMOD_Memory_Free(mChannel[count].mLevels); + mChannel[count].mLevels = 0; + } + } + + FMOD_Memory_Free(mChannel); + mChannel = 0; + mNumChannels = 0; + } + + mChannelFreeListHead.initNode(); + +#ifdef FMOD_SUPPORT_SOFTWARE + + if (mDSPSoundCard) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Remove DSP Soundcard unit.\n")); + + result = mDSPSoundCard->release(); /* Release last because some things above point to it. */ + mDSPSoundCard = 0; + } + + /* + Remove miscllaneous DSP stuff + */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Remove miscllaneous DSP stuff.\n")); + + result = mDSPConnectionPool.close(); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < FMOD_DSP_MAXTREEDEPTH; count++) + { + if (mDSPMixBuff[count]) + { + FMOD_Memory_Free(mDSPMixBuff[count]); + mDSPMixBuff[count] = 0; + } + } + + if (mDSPCrit) + { + result = FMOD_OS_CriticalSection_Free(mDSPCrit); + if (result != FMOD_OK) + { + return result; + } + mDSPCrit = 0; + } + if (mDSPLockCrit) + { + result = FMOD_OS_CriticalSection_Free(mDSPLockCrit); + if (result != FMOD_OK) + { + return result; + } + mDSPLockCrit = 0; + } + if (mDSPConnectionCrit) + { + result = FMOD_OS_CriticalSection_Free(mDSPConnectionCrit); + if (result != FMOD_OK) + { + return result; + } + mDSPConnectionCrit = 0; + } +#ifdef FMOD_NEED_DSPCODECPOOLINITCRIT + if (mDSPCodecPoolInitCrit) + { + result = FMOD_OS_CriticalSection_Free(mDSPCodecPoolInitCrit); + if (result != FMOD_OK) + { + return result; + } + mDSPCodecPoolInitCrit = 0; + } +#endif + +#endif + +#ifdef FMOD_SUPPORT_MPEG_SPU + CodecMPEG::closeAll(); +#endif + + /* + Sound lock critical section + */ + if (mMultiSubSampleLockBufferCrit) + { + FMOD_OS_CriticalSection_Free(mMultiSubSampleLockBufferCrit); + mMultiSubSampleLockBufferCrit = 0; + } + + mSpeakerLevelsPool.release(); + mHistoryBufferPool.release(); + + if (mDownmix) + { + result = mDownmix->shutdown(); + if (result != FMOD_OK) + { + return result; + } + FMOD_Memory_Free(mDownmix); + mDownmix = 0; + } + + if (mPluginFactory && !calledfrominit) + { + CHECK_RESULT(mPluginFactory->release()); + mPluginFactory = 0; + mPluginsLoaded = false; + } + + if (mInitialized) + { + gGlobal->gSystemInitCount--; + } + + mInitialized = false; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "done.\n\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::update() +{ + FMOD_RESULT result; + unsigned int currenttimestamp; + int delta, count; + + if (!mInitialized) + { + return FMOD_ERR_UNINITIALIZED; + } + + #ifdef FMOD_DEBUG + { + FMOD_UINT_NATIVE id; + + FMOD_OS_Thread_GetCurrentID(&id); + + if (id != mMainThreadID) + { + FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "SystemI::update", "Warning! You are calling FMOD from different threads! This is not safe!\n")); + } + } + #endif + + mUpdateTimeStamp.stampIn(); + +// unsigned int a, b; +// FMOD_OS_Time_GetNs(&a); + + /* + Calculate the time delta since this was last called. + */ + if (!mLastTimeStamp) + { + FMOD_OS_Time_GetMs(&mLastTimeStamp); + } + + FMOD_OS_Time_GetMs(¤ttimestamp); + + if (currenttimestamp >= mLastTimeStamp) + { + delta = currenttimestamp - mLastTimeStamp; + } + else + { + /* + Timestamp has wrapped around? + */ + delta = currenttimestamp; + } + + mLastTimeStamp = currenttimestamp; + + /* + Update emulated output object. + */ + if (mEmulated) + { + result = mEmulated->update(); + if (result != FMOD_OK) + { + return result; + } + } + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + Update reverb-specific details + */ + result = update3DReverbs(); + if (result != FMOD_OK) + { + return result; + } +#endif + + /* + Update per channel stuff including virtual voice swapping. + */ + result = updateChannels(delta); + if (result != FMOD_OK) + { + return result; + } + + /* + Update any fading or volume logic by sound groups. + */ + result = updateSoundGroups(delta); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (!mSoftware) +#endif + { + FMOD_OS_Time_GetMs(&FMOD::gGlobal->gDSPClockTimeStamp); + FMOD::gGlobal->gDSPClock.mHi += delta; + + mDSPClock.mValue += ((FMOD_UINT64)delta * (FMOD_UINT64)mOutputRate / (FMOD_UINT64)1000); + } + + /* + Update hardware output object. + */ + if (mOutput && mOutput->mDescription.update) + { + mUpdateTimeStamp.setPaused(true); + +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + result = mOutput->mDescription.update(mOutput); + if (result != FMOD_OK) + { + return result; + } + + mUpdateTimeStamp.setPaused(false); + } + + /* + Determine if the device list has changed + */ + result = checkDriverList(true); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_RECORDING + /* + Clean up recording device list, stop finished devices + */ + if (mOutput && mOutput->mRecordNumActive) + { + result = mOutput->recordStopAll(true); + CHECK_RESULT(result); + } +#endif + + /* + Clear moved flag from listeners. + */ + for (count = 0; count < mNumListeners; count++) + { + mListener[count].mMoved = false; + mListener[count].mRotated = false; + } + +#ifdef FMOD_SUPPORT_GEOMETRY + mGeometryMgr.mMoved = false; +#endif + +#ifdef FMOD_SUPPORT_NONBLOCKING + result = AsyncThread::update(); + if (result != FMOD_OK) + { + return result; + } +#endif + + mUpdateTimeStamp.stampOut(95); + +#ifdef FMOD_SUPPORT_STREAMING + if (mFlags & FMOD_INIT_STREAM_FROM_UPDATE) + { + updateStreams(); + } +#endif + + if (mFlags & FMOD_INIT_SYNCMIXERWITHUPDATE) + { + if(mOutput->mDescription.polling) + { + OutputPolled *outputpolled = (OutputPolled *)mOutput; + + outputpolled->wakeupThread(); + } + } + +#ifdef FMOD_SUPPORT_PROFILE + if (mFlags & FMOD_INIT_ENABLE_PROFILE) + { + result = FMOD_Profile_Update(this, delta); + if (result != FMOD_OK) + { + return result; + } + } +#endif + +#ifdef FMOD_SUPPORT_VSTPLUGIN + updateVSTPlugins(); +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::set3DSettings(float dopplerscale, float distancescale, float rolloffscale) +{ + if (dopplerscale < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + if (distancescale <= 0) + { + return FMOD_ERR_INVALID_PARAM; + } + if (rolloffscale < 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + mDopplerScale = dopplerscale; + mDistanceScale = distancescale; + mRolloffScale = rolloffscale; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::set3DNumListeners(int numlisteners) +{ + if (numlisteners < 1 || numlisteners > LISTENER_MAX) + { + return FMOD_ERR_INVALID_PARAM; + } + + mNumListeners = numlisteners; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up) +{ + if (listener < 0 || listener >= LISTENER_MAX) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (forward) + { + #ifdef FMOD_DEBUG + { + FMOD_RESULT result; + float length; + + result = FMOD_CHECKFLOAT(forward->x); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(forward->y); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(forward->z); + if (result != FMOD_OK) + { + return result; + } + + length = FMOD_Vector_DotProduct(forward, forward); + if (length < 0.9f || length > 1.1f) + { + return FMOD_ERR_INVALID_VECTOR; + } + } + #endif + + if (mListener[listener].mLastFront.x != forward->x || + mListener[listener].mLastFront.y != forward->y || + mListener[listener].mLastFront.z != forward->z) + { + mListener[listener].mRotated = true; + } + + mListener[listener].mLastFront = mListener[listener].mFront; + mListener[listener].mFront = *forward; + } + + if (up) + { + #ifdef FMOD_DEBUG + { + FMOD_RESULT result; + float length; + + result = FMOD_CHECKFLOAT(up->x); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(up->y); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(up->z); + if (result != FMOD_OK) + { + return result; + } + + length = FMOD_Vector_DotProduct(up, up); + if (length < 0.9f || length > 1.1f) + { + return FMOD_ERR_INVALID_VECTOR; + } + } + #endif + + if (mListener[listener].mLastUp.x != up->x || + mListener[listener].mLastUp.y != up->y || + mListener[listener].mLastUp.z != up->z) + { + mListener[listener].mRotated = true; + } + + mListener[listener].mLastUp = mListener[listener].mUp; + mListener[listener].mUp = *up; + } + + if (pos) + { + #ifdef FMOD_DEBUG + { + FMOD_RESULT result; + + result = FMOD_CHECKFLOAT(pos->x); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(pos->y); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(pos->z); + if (result != FMOD_OK) + { + return result; + } + } + #endif + + if (mListener[listener].mLastPosition.x != pos->x || + mListener[listener].mLastPosition.y != pos->y || + mListener[listener].mLastPosition.z != pos->z) + { + mListener[listener].mMoved = true; + } + + mListener[listener].mPosition = *pos; + mListener[listener].mLastPosition = mListener[listener].mPosition; + } + + if (vel) + { + #ifdef FMOD_DEBUG + { + FMOD_RESULT result; + + result = FMOD_CHECKFLOAT(vel->x); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(vel->y); + if (result != FMOD_OK) + { + return result; + } + result = FMOD_CHECKFLOAT(vel->z); + if (result != FMOD_OK) + { + return result; + } + } + #endif + + if (mListener[listener].mLastVelocity.x != vel->x || + mListener[listener].mLastVelocity.y != vel->y || + mListener[listener].mLastVelocity.z != vel->z) + { + mListener[listener].mMoved = true; + } + + mListener[listener].mLastVelocity = mListener[listener].mVelocity; + mListener[listener].mVelocity = *vel; + } + + { + FMOD_VECTOR l_front, l_up; + + l_up = mListener[listener].mUp; + l_front = mListener[listener].mFront; + + if (mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + l_up.z = -l_up.z; + l_front.z = -l_front.z; + } + + #ifdef FMOD_DEBUG + { + float dot = FMOD_Vector_DotProduct(&l_up, &l_front); + + if (dot < -0.01f || dot > 0.01f) + { + return FMOD_ERR_INVALID_VECTOR; + } + } + #endif + + FMOD_Vector_CrossProduct(&l_up, &l_front, &mListener[listener].mRight); + } + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::set3DRolloffCallback(FMOD_3D_ROLLOFFCALLBACK callback) +{ + mRolloffCallback = callback; + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::set3DSpeakerPosition(FMOD_SPEAKER speaker, float x, float y, bool active) +{ + if (speaker < 0 || speaker >= FMOD_SPEAKER_MAX) + { + return FMOD_ERR_INVALID_PARAM; + } + + mSpeaker[speaker].mSpeaker = speaker; + mSpeaker[speaker].mPosition.x = x; + mSpeaker[speaker].mPosition.y = 0; + mSpeaker[speaker].mPosition.z = y; + mSpeaker[speaker].mActive = active; + + /* calculate XZ angle for sorting */ + mSpeaker[speaker].mXZAngle = FMOD_AngleSort_GetValue(mSpeaker[speaker].mPosition.x, mSpeaker[speaker].mPosition.z); + + return sortSpeakerList(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::get3DSpeakerPosition(FMOD_SPEAKER speaker, float *x, float *y, bool *active) +{ + if (speaker < 0 || speaker >= FMOD_SPEAKER_MAX) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (x) + { + *x = mSpeaker[speaker].mPosition.x; + } + if (y) + { + *y = mSpeaker[speaker].mPosition.z; + } + if (active) + { + *active = mSpeaker[speaker].mActive; + } + + return FMOD_OK; +} + +#endif // #ifndef FMOD_STATICFORPLUGINS + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::get3DNumListeners(int *numlisteners) +{ + if (!numlisteners) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numlisteners = mNumListeners; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::get3DListenerAttributes(int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up) +{ + if (listener < 0 || listener >= LISTENER_MAX) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (pos) + { + *pos = mListener[listener].mPosition; + } + + if (vel) + { + *vel = mListener[listener].mVelocity; + } + + if (forward) + { + *forward = mListener[listener].mFront; + } + + if (up) + { + *up = mListener[listener].mUp; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::get3DSettings(float *dopplerscale, float *distancescale, float *rolloffscale) +{ + if (dopplerscale) + { + *dopplerscale = mDopplerScale; + } + if (distancescale) + { + *distancescale = mDistanceScale; + } + if (rolloffscale) + { + *rolloffscale = mRolloffScale; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getVersion(unsigned int *version) +{ + if (!version) + { + return FMOD_ERR_INVALID_PARAM; + } + + *version = FMOD_VERSION; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getOutputHandle(void **handle) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutput) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (mOutput->mDescription.gethandle) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.gethandle(mOutput, handle); + } + + return FMOD_OK; +} + + +#ifndef FMOD_STATICFORPLUGINS + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getChannelsPlaying(int *channels) +{ + if (!channels) + { + return FMOD_ERR_INVALID_PARAM; + } + + *channels = mChannelUsedListHead.count(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getCPUUsage(float *dsp, float *stream, float *geometry, float *update, float *total) +{ + FMOD_RESULT result; + float totalcpu, usage; + + totalcpu = 0; + +#ifdef FMOD_SUPPORT_SOFTWARE + result = mDSPTimeStamp.getCPUUsage(&usage); + if (result == FMOD_OK) + { + totalcpu += usage; + + if (dsp) + { + *dsp = usage; + } + } +#else + if (dsp) + { + *dsp = 0; + } +#endif + +#ifdef FMOD_SUPPORT_STREAMING + result = mStreamTimeStamp.getCPUUsage(&usage); + if (result == FMOD_OK) + { + totalcpu += usage; + + if (stream) + { + *stream = usage; + } + } +#else + if (stream) + { + *stream = 0; + } +#endif + +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + result = mGeometryTimeStamp.getCPUUsage(&usage); + if (result == FMOD_OK) + { + totalcpu += usage; + + if (geometry) + { + *geometry = usage; + } + } +#else + if (geometry) + { + *geometry = 0; + } +#endif + + result = mUpdateTimeStamp.getCPUUsage(&usage); + if (result == FMOD_OK) + { + totalcpu += usage; + + if (update) + { + *update = usage; + } + } + + if (total) + { + *total = totalcpu; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getSoundRAM(int *currentalloced, int *maxalloced, int *total) +{ + if (mOutput && mOutput->mDescription.getsoundram) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + mOutput->mDescription.getsoundram(mOutput, currentalloced, maxalloced, total); + } + else + { + if (currentalloced) + { + *currentalloced = 0; + } + if (maxalloced) + { + *maxalloced = 0; + } + if (total) + { + *total = 0; + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getNumCDROMDrives(int *numdrives) +{ +#ifdef FMOD_SUPPORT_CDDA + return FMOD_OS_CDDA_GetNumDevices(numdrives); + +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getCDROMDriveName(int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen) +{ +#ifdef FMOD_SUPPORT_CDDA + return FMOD_OS_CDDA_GetDeviceName(drive, drivename, drivenamelen, scsiname, scsinamelen, devicename, devicenamelen); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + + +/* +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal. + + [PARAMETERS] + 'spectrumarray' Pointer to an array of floats to receive spectrum data. Data range is 0-1. Decibels = 10.0f * (float)log10(val) * 2.0f; + 'numvalues' Size of array in floating point values being passed to the function. Must be a power of 2. (ie 128/256/512 etc). Min = 64. Max = 8192. + 'channeloffset' Channel to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' Pre-FFT window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the windowsize, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.<br> + The larger the windowsize, the more 'lag' the spectrum will seem to inherit. This is because the window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.<br> + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).<br> + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.<br> + + [PLATFORMS] + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW +] +*/ +FMOD_RESULT SystemI::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ +#ifdef FMOD_SUPPORT_GETSPECTRUM + FMOD_RESULT result = FMOD_OK; + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + DSPI *soundcard; + #else + DSPSoundCard *soundcard; + #endif + float *buffer; + unsigned int position, length; + int numchannels, windowsize; + static DSPFFT fft; + + if (!mDSPSoundCard) + { + return FMOD_ERR_INITIALIZATION; + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + soundcard = SAFE_CAST(DSPI, mDSPSoundCard); + #else + soundcard = SAFE_CAST(DSPSoundCard, mDSPSoundCard); + #endif + if (!soundcard) + { + return FMOD_ERR_INITIALIZATION; + } + + windowsize = numvalues * 2; + + if (windowsize != (1 << 7) && + windowsize != (1 << 8) && + windowsize != (1 << 9) && + windowsize != (1 << 10) && + windowsize != (1 << 11) && + windowsize != (1 << 12) && + windowsize != (1 << 13) && + windowsize != (1 << 14)) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = getSoftwareFormat(0, 0, &numchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = soundcard->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = soundcard->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (windowsize > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + position -= windowsize; + if ((int)position < 0) + { + position += length; + } + + mUpdateTimeStamp.stampIn(); + + result = fft.getSpectrum(buffer, position, length, spectrumarray, windowsize, channeloffset, numchannels, windowtype); + + mUpdateTimeStamp.stampOut(95); + + return result; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif // FMOD_SUPPORT_GETSPECTRUM +} + +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW +] +*/ +FMOD_RESULT SystemI::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result = FMOD_OK; + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + DSPI *soundcard; + #else + DSPSoundCard *soundcard; + #endif + float *buffer; + unsigned int position, length; + int numchannels, count; + + if (!mDSPSoundCard) + { + return FMOD_ERR_INITIALIZATION; + } + + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + soundcard = SAFE_CAST(DSPI, mDSPSoundCard); + #else + soundcard = SAFE_CAST(DSPSoundCard, mDSPSoundCard); + #endif + if (!soundcard) + { + return FMOD_ERR_INITIALIZATION; + } + + result = getSoftwareFormat(0, 0, &numchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = soundcard->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = soundcard->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (numvalues > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + position -= numvalues; + if ((int)position < 0) + { + position += length; + } + + for (count = 0; count < numvalues; count++) + { + wavearray[count] = buffer[position*numchannels+channeloffset]; + position++; + if (position >= length) + { + position = 0; + } + } + + return result; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createSound(const char *name_or_data, FMOD_MODE mode_in, FMOD_CREATESOUNDEXINFO *exinfo, SoundI **sound) +{ + FMOD_RESULT result; + + if (!mInitialized) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!sound) + { + return FMOD_ERR_INVALID_PARAM; + } + if (!name_or_data && !(mode_in & FMOD_OPENUSER)) + { + return FMOD_ERR_INVALID_PARAM; + } + if (mode_in & FMOD_NONBLOCKING && !(mode_in & (FMOD_SOFTWARE | FMOD_HARDWARE))) + { + /* + When using nonblocking, we can't rely on the codec to determine hardware or software if + neither is specified. Since the sample is created of the type of the output mode and + returned to the caller, we can't change it later if the codec requires it. + */ + mode_in |= FMOD_HARDWARE; + } + if (!(mode_in & FMOD_SOFTWARE)) + { + if (!mOutput) + { + return FMOD_ERR_OUTPUT_NOHARDWARE; + } + } + + #if !defined(PLATFORM_WII) && !defined(PLATFORM_PSP) // We can allow this on the Wii, and PSP + if (mode_in & FMOD_HARDWARE && mode_in & FMOD_OPENMEMORY_POINT && !(mode_in & FMOD_CREATESTREAM)) + { + return FMOD_ERR_NEEDSSOFTWARE; + } + #endif + + *sound = 0; + +#ifdef FMOD_SUPPORT_NONBLOCKING + + if (mode_in & FMOD_NONBLOCKING) + { + SoundI *soundi; + + #ifdef FMOD_DEBUG + if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "memory = %p : mode %08x\n", name_or_data, mode_in)); + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "filename = %s : mode %08x\n", name_or_data, mode_in)); + } + #endif + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "FMOD_NONBLOCKING specified. Putting into queue to be opened asynchronously!\n")); + +#ifdef FMOD_SUPPORT_STREAMING + if (mode_in & FMOD_CREATESTREAM) + { + Stream *stream = FMOD_Object_Calloc(Stream); + if (!stream) + { + return FMOD_ERR_MEMORY; + } + + *sound = stream; + } + else +#endif + { + Sample *sample = 0; + int hwchannels = 0; + + if (!(mode_in & FMOD_SOFTWARE)) + { + getHardwareChannels(0, 0, &hwchannels); + } + + if (hwchannels && mOutput->mDescription.createsample) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + result = mOutput->mDescription.createsample(mOutput, (FMOD_MODE)0, 0, &sample); + mCreatedHardwareSample = true; + } + else + { +#ifdef FMOD_SUPPORT_SOFTWARE + result = mSoftware->createSample((FMOD_MODE)0, 0, &sample); +#else + result = FMOD_ERR_NEEDSSOFTWARE; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "ERROR - Software not initialized\n")); +#endif + } + if (result != FMOD_OK) + { + return result; + } + + *sound = sample; + } + + soundi = SAFE_CAST(SoundI, *sound); + + /* + Calculate the size of the AsyncData plus any data that's pointed to by the exinfo struct. + We need to do a deep copy of this exinfo struct soon. + */ + int sizeof_asyncdata = sizeof(AsyncData); + + if (exinfo) + { + sizeof_asyncdata += (exinfo->inclusionlistnum * sizeof(int)); + + if (exinfo->dlsname) + { + sizeof_asyncdata += (FMOD_strlen(exinfo->dlsname) + 1); + } + + if (exinfo->encryptionkey) + { + sizeof_asyncdata += (FMOD_strlen(exinfo->encryptionkey) + 1); + } + } + + soundi->mAsyncData = (AsyncData *)FMOD_Memory_Calloc(sizeof_asyncdata); + if (!soundi->mAsyncData) + { + return FMOD_ERR_MEMORY; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "allocated async data mem\n")); + + if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) + { + soundi->mAsyncData->mNameData = (void *)name_or_data; + } + else if (name_or_data) + { + if (mode_in & FMOD_UNICODE) + { + // don't need to divide FMOD_STRING_MAXNAMELEN by 2 here, as AsyncData::mName is big enough + FMOD_strncpyW((short *)soundi->mAsyncData->mName, (short *)name_or_data, FMOD_STRING_MAXNAMELEN); + } + else + { + FMOD_strncpy(soundi->mAsyncData->mName, name_or_data, FMOD_STRING_MAXNAMELEN); + } + } + + soundi->mAsyncData->mBufferSize = mStreamFileBufferSize; + soundi->mAsyncData->mBufferSizeType = mStreamFileBufferSizeType; + + soundi->mMode = mode_in; + soundi->mSystem = this; + soundi->mOpenState = FMOD_OPENSTATE_LOADING; + + if (exinfo) + { + FMOD_memcpy(&soundi->mAsyncData->mExInfo, exinfo, sizeof(FMOD_CREATESOUNDEXINFO)); + soundi->mAsyncData->mExInfoExists = true; + + if (exinfo->initialsoundgroup) + { + soundi->setSoundGroup((FMOD::SoundGroupI *)exinfo->initialsoundgroup); + } + + /* + Now do a deep copy of things that are pointed to by the exinfo struct by tacking them + on the end of the soundi->mAsyncData memory block that we previously allocated and diddling + the pointers to them. We calculated the size of this block to take into account this extra + stuff so we're not going to stomp on memory. + */ + char *p = (char *)(soundi->mAsyncData + 1); // Pointer arithmetic. This puts us at the first byte after the end of the mAsyncData struct + + if (soundi->mAsyncData->mExInfo.inclusionlistnum) + { + FMOD_memcpy(p, soundi->mAsyncData->mExInfo.inclusionlist, soundi->mAsyncData->mExInfo.inclusionlistnum * sizeof(int)); + soundi->mAsyncData->mExInfo.inclusionlist = (int *)p; + p += (soundi->mAsyncData->mExInfo.inclusionlistnum * sizeof(int)); + } + + if (soundi->mAsyncData->mExInfo.dlsname) + { + FMOD_strcpy(p, soundi->mAsyncData->mExInfo.dlsname); + soundi->mAsyncData->mExInfo.dlsname = p; + p += (FMOD_strlen(soundi->mAsyncData->mExInfo.dlsname) + 1); + } + + if (soundi->mAsyncData->mExInfo.encryptionkey) + { + FMOD_strcpy(p, soundi->mAsyncData->mExInfo.encryptionkey); + soundi->mAsyncData->mExInfo.encryptionkey = p; + p += (FMOD_strlen(soundi->mAsyncData->mExInfo.encryptionkey) + 1); + } + } + else + { + soundi->mAsyncData->mExInfoExists = false; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "getasyncthread\n")); + + result = AsyncThread::getAsyncThread(soundi); + if (result != FMOD_OK) + { + soundi->mOpenState = FMOD_OPENSTATE_ERROR; /* Stop it from hanging in release because state is 'loading'. */ + soundi->release(); + *sound = 0; + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "setdata soundi = %p : node = %p\n", soundi, &soundi->mAsyncData->mNode)); + + FMOD_OS_CriticalSection_Enter(soundi->mAsyncData->mThread->mCrit); + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "add node to async list : head = %p. list count = %d\n", &soundi->mAsyncData->mThread->mHead, soundi->mAsyncData->mThread->mHead.count())); + soundi->mAsyncData->mNode.setData(soundi); + soundi->mAsyncData->mNode.addBefore(&soundi->mAsyncData->mThread->mHead); + } + FMOD_OS_CriticalSection_Leave(soundi->mAsyncData->mThread->mCrit); + + soundi->mAsyncData->mThread->mThread.wakeupThread(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "done\n")); + } + else +#endif + { + if (exinfo) + { + FMOD_CREATESOUNDEXINFO exinfo_copy; + + memcpy(&exinfo_copy, exinfo, sizeof(FMOD_CREATESOUNDEXINFO)); /* Just in case the createSoundInternal modifies the exinfo struct internally somewhere. */ + + result = createSoundInternal(name_or_data, mode_in, mStreamFileBufferSize, mStreamFileBufferSizeType, &exinfo_copy, false, sound); + } + else + { + result = createSoundInternal(name_or_data, mode_in, mStreamFileBufferSize, mStreamFileBufferSizeType, exinfo, false, sound); + } + + if (*sound && exinfo && exinfo->initialsoundgroup) + { + (*sound)->setSoundGroup((FMOD::SoundGroupI *)exinfo->initialsoundgroup); + } + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createStream(const char *name_or_data, FMOD_MODE mode_in, FMOD_CREATESOUNDEXINFO *exinfo, SoundI **sound) +{ +#ifdef FMOD_SUPPORT_STREAMING + return createSound(name_or_data, mode_in | FMOD_CREATESTREAM, exinfo, sound); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createCodec(FMOD_CODEC_DESCRIPTION *description, unsigned int priority) +{ + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mFlags & FMOD_INIT_SOFTWARE_DISABLE) +#endif + { + return FMOD_ERR_NEEDSSOFTWARE; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + { + FMOD_RESULT result; + FMOD_CODEC_DESCRIPTION_EX descriptionex; + + descriptionex.name = description->name; + descriptionex.version = description->version; + descriptionex.timeunits = description->timeunits; + descriptionex.defaultasstream = description->defaultasstream; + descriptionex.open = description->open; + descriptionex.close = description->close; + descriptionex.read = description->read; + descriptionex.getlength = description->getlength; + descriptionex.setposition = description->setposition; + descriptionex.getposition = description->getposition; + descriptionex.soundcreate = description->soundcreate; + descriptionex.getwaveformat = description->getwaveformat; + descriptionex.mType = FMOD_SOUND_TYPE_UNKNOWN; + descriptionex.mSize = sizeof(Codec); + descriptionex.mModule = 0; + descriptionex.reset = 0; + + result = mPluginFactory->registerCodec(&descriptionex, 0, priority); + if (result != FMOD_OK) + { + return result; + } + + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createDSP(FMOD_DSP_DESCRIPTION *description, DSPI **dsp) +{ + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + *dsp = 0; + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mFlags & FMOD_INIT_SOFTWARE_DISABLE) +#endif + { + return FMOD_ERR_NEEDSSOFTWARE; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + { + FMOD_RESULT result; + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_strcpy(descriptionex.name, description->name); + descriptionex.version = description->version; + descriptionex.channels = description->channels; + descriptionex.create = description->create; + descriptionex.release = description->release; + descriptionex.reset = description->reset; + descriptionex.read = description->read; + descriptionex.setposition = description->setposition; + + descriptionex.numparameters = description->numparameters; + descriptionex.paramdesc = description->paramdesc; + descriptionex.setparameter = description->setparameter; + descriptionex.getparameter = description->getparameter; + descriptionex.config = description->config; + descriptionex.configwidth = description->configwidth; + descriptionex.configheight = description->configheight; + descriptionex.userdata = description->userdata; + descriptionex.getmemoryused = 0; + + descriptionex.mSize = 0; + descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER; + descriptionex.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; +#ifdef FMOD_SUPPORT_DLLS + descriptionex.mModule = 0; + descriptionex.mAEffect = 0; +#endif + descriptionex.mResamplerBlockLength = 0; + descriptionex.mType = FMOD_DSP_TYPE_UNKNOWN; + descriptionex.mDSPSoundCard = mDSPSoundCard; + + result = mPluginFactory->createDSP(&descriptionex, dsp); + if (result != FMOD_OK) + { + return result; + } + + (*dsp)->mSystem = this; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createDSP(FMOD_DSP_DESCRIPTION_EX *description, DSPI **dsp, bool allocate) +{ + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (allocate) + { + *dsp = 0; + } + + if (!description) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mFlags & FMOD_INIT_SOFTWARE_DISABLE) +#endif + { + return FMOD_ERR_NEEDSSOFTWARE; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + { + FMOD_RESULT result; + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_strcpy(descriptionex.name, description->name); + descriptionex.version = description->version; + descriptionex.channels = description->channels; + descriptionex.create = description->create; + descriptionex.release = description->release; + descriptionex.reset = description->reset; + descriptionex.read = description->read; + descriptionex.setposition = description->setposition; + + descriptionex.numparameters = description->numparameters; + descriptionex.paramdesc = description->paramdesc; + descriptionex.setparameter = description->setparameter; + descriptionex.getparameter = description->getparameter; + descriptionex.config = description->config; + descriptionex.configwidth = description->configwidth; + descriptionex.configheight = description->configheight; + descriptionex.userdata = description->userdata; + descriptionex.getmemoryused = description->getmemoryused; + + descriptionex.mSize = description->mSize; + descriptionex.mCategory = description->mCategory; + descriptionex.mFormat = description->mFormat; +#ifdef FMOD_SUPPORT_DLLS + descriptionex.mModule = 0; + descriptionex.mAEffect = description->mAEffect; +#endif + descriptionex.mResamplerBlockLength = description->mResamplerBlockLength; + descriptionex.mType = description->mType; + descriptionex.mDSPSoundCard = description->mDSPSoundCard; + + result = mPluginFactory->createDSP(&descriptionex, dsp); + if (result != FMOD_OK) + { + return result; + } + + (*dsp)->mSystem = this; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createDSPByType(FMOD_DSP_TYPE type, DSPI **dsp) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT result; + + if (!mPluginFactory) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + *dsp = 0; + + if (type == FMOD_DSP_TYPE_MIXER) + { + FMOD_DSP_DESCRIPTION desc; + + FMOD_memset(&desc, 0, sizeof(FMOD_DSP_DESCRIPTION)); + + FMOD_strcpy(desc.name, "FMOD Mixer unit"); + + result = createDSP(&desc, dsp); + if (result != FMOD_OK) + { + return result; + } + + (*dsp)->mDescription.mType = FMOD_DSP_TYPE_MIXER; + } + else + { + int count, numdsps; + + result = mPluginFactory->getNumDSPs(&numdsps); + if (result != FMOD_OK) + { + return result; + } + + for (count = 0; count < numdsps; count++) + { + FMOD_DSP_DESCRIPTION_EX *descriptionex = 0; + unsigned int handle; + + result = mPluginFactory->getDSPHandle(count, &handle); + if (result != FMOD_OK) + { + continue; + } + + result = mPluginFactory->getDSP(handle, &descriptionex); + if (result != FMOD_OK) + { + continue; + } + + if (descriptionex->mType == type) + { + result = mPluginFactory->createDSP(descriptionex, dsp); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; + } + } + + return FMOD_ERR_PLUGIN_MISSING; + } + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createDSPByPlugin(unsigned int handle, DSPI **dsp) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_DSP_DESCRIPTION_EX *descriptionex = 0; + FMOD_RESULT result; + + if (!mPluginFactory) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + *dsp = 0; + + result = mPluginFactory->getDSP(handle, &descriptionex); + if (result != FMOD_OK) + { + return result; + } + + result = mPluginFactory->createDSP(descriptionex, dsp); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createChannelGroupInternal(const char *name, ChannelGroupI **channelgroup, bool createdsp, bool storenameinchannelgroup) +{ + ChannelGroupI *newchannelgroup; + AutoRelease<ChannelGroupI> newchannelgroupauto; + + if (!channelgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mSoftware && createdsp) + { + newchannelgroup = FMOD_Object_Calloc(ChannelGroupSoftware); + } + else +#endif + { + newchannelgroup = FMOD_Object_Calloc(ChannelGroupI); + } + + if (!newchannelgroup) + { + return FMOD_ERR_MEMORY; + } + + newchannelgroupauto = newchannelgroup; + + newchannelgroup->addAfter(&mChannelGroupHead); + newchannelgroup->mSystem = this; + + if (name && storenameinchannelgroup) + { + newchannelgroup->mName = FMOD_strdup(name); + if (!newchannelgroup->mName) + { + return FMOD_ERR_MEMORY; + } + } + else + { + newchannelgroup->mName = 0; + } + + /* + Create a DSP unit for this channelgroup then add it to the soundcard unit. + */ +#ifdef FMOD_SUPPORT_SOFTWARE + if (mSoftware) + { + if (createdsp) + { + FMOD_RESULT result; + FMOD_DSP_DESCRIPTION_EX description; + + FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(description.name, "ChannelGroup"); + if (name) + { + FMOD_strcat(description.name, ":"); + /* + description.name is hardwired to 32 so don't overflow it + */ + FMOD_strncat(description.name, name, 18); + } + description.version = 0x00010100; + + newchannelgroup->mDSPHead = &((ChannelGroupSoftware *)newchannelgroup)->mDSPHeadMemory; + #if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE) + newchannelgroup->mDSPHead = (DSPI *)FMOD_ALIGNPOINTER(newchannelgroup->mDSPHead, 128); + new (newchannelgroup->mDSPHead) DSPFilter; + #endif + + result = createDSP(&description, (DSPI **)&newchannelgroup->mDSPHead, false); + if (result != FMOD_OK) + { + return result; + } + newchannelgroup->mDSPHead->setDefaults((float)mOutputRate, -1, -1, -1); + newchannelgroup->mDSPHead->setActive(true); + + result = mDSPChannelGroupTarget->addInputQueued(newchannelgroup->mDSPHead, false, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + newchannelgroup->mDSPMixTarget = newchannelgroup->mDSPHead; + } + else + { + newchannelgroup->mDSPMixTarget = mDSPChannelGroupTarget; + } + } +#endif + + newchannelgroupauto.releasePtr(); + + if (name && !FMOD_stricmp("music", name)) + { + /* + Store pointer to "music" channelgroup. + */ + + mOutput->mMusicChannelGroup = newchannelgroup; + } + + *channelgroup = newchannelgroup; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createChannelGroup(const char *name, ChannelGroupI **channelgroup) +{ + if (!channelgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mSoftware) + { + return createChannelGroupInternal(name, channelgroup, true, true); + } + else +#endif + + return createChannelGroupInternal(name, channelgroup, false, true); +} + + +#ifndef FMOD_STATICFORPLUGINS +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createSoundGroup(const char *name, SoundGroupI **soundgroup) +{ + SoundGroupI *newsoundgroup; + AutoCritRelease<SoundGroupI> autonewsoundgroup(gSoundListCrit); + + if (!soundgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + + newsoundgroup = FMOD_Object_Calloc(SoundGroupI); + if (!newsoundgroup) + { + return FMOD_ERR_MEMORY; + } + + FMOD_OS_CriticalSection_Enter(gSoundListCrit); + { + newsoundgroup->addAfter(&mSoundGroupFreeHead); + newsoundgroup->mSystem = this; + } + FMOD_OS_CriticalSection_Leave(gSoundListCrit); + + autonewsoundgroup = newsoundgroup; + + if (name) + { + newsoundgroup->mName = FMOD_strdup(name); + if (!newsoundgroup->mName) + { + return FMOD_ERR_MEMORY; + } + } + else + { + newsoundgroup->mName = 0; + } + + autonewsoundgroup.releasePtr(); + + *soundgroup = newsoundgroup; + + return FMOD_OK; +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::playSound(FMOD_CHANNELINDEX channelid, SoundI *sound, bool paused, ChannelI **channel) +{ + FMOD_RESULT result; + ChannelI *chan = 0; + bool mute = false; + + if (channel) + { + if (channelid == FMOD_CHANNEL_REUSE) + { + result = ChannelI::validate((Channel *)*channel, &chan); + } + } + + if (!sound) + { + if (channel) + { + *channel = 0; + } + return FMOD_ERR_INVALID_PARAM; + } + + if (sound->mOpenState != FMOD_OPENSTATE_READY) + { + if (channel) + { + *channel = 0; + } + return FMOD_ERR_NOTREADY; + } + + if (sound->mType == FMOD_SOUND_TYPE_PLAYLIST) + { + return FMOD_ERR_FORMAT; + } + +#ifndef FMOD_STATICFORPLUGINS + if (sound->mSoundGroup && sound->mSoundGroup->mMaxAudible >= 0) + { + int numplaying; + + result = sound->mSoundGroup->getNumPlaying(&numplaying); + if (result != FMOD_OK) + { + return result; + } + + if (numplaying >= sound->mSoundGroup->mMaxAudible) + { + switch (sound->mSoundGroup->mMaxAudibleBehavior) + { + case FMOD_SOUNDGROUP_BEHAVIOR_FAIL: + { + return FMOD_ERR_MAXAUDIBLE; + } + case FMOD_SOUNDGROUP_BEHAVIOR_MUTE: + { + mute = true; + break; + } + case FMOD_SOUNDGROUP_BEHAVIOR_STEALLOWEST: + { + LinkedListNode *current; + float lowest = 9999.0f; + + /* + Find least important sound and replace it. + */ + current = mChannelUsedListHead.getNext(); + while (current != &mChannelUsedListHead) + { + ChannelI *channel = (ChannelI *)current; + + if (channel->mRealChannel[0] && channel->mRealChannel[0]->mSound) + { + SoundGroupI *soundgroup = channel->mRealChannel[0]->mSound->mSoundGroup; + + if (soundgroup == sound->mSoundGroup) + { + float audibility; + + channel->getAudibility(&audibility); + + if (audibility < lowest) + { + chan = channel; + lowest = audibility; + channelid = (FMOD_CHANNELINDEX)chan->mIndex; + } + } + } + + current = current->getNext(); + } + break; + } + default: + { + break; + } + }; + } + } +#endif + + /* + First find a channel + */ + result = findChannel(channelid, sound, &chan); + if (result != FMOD_OK) + { + if (channel) + { + *channel = 0; + } + return result; + } + + result = chan->play(sound, paused, true, mute); + if (result != FMOD_OK) + { + if (channel) + { + *channel = 0; + } + chan->stopEx(CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_UPDATESYNCPOINTS); + return result; + } + + result = chan->updatePosition(); + if (result != FMOD_OK) + { + if (channel) + { + *channel = 0; + } + return result; + } + + if (channelid == FMOD_CHANNEL_REUSE && *channel) + { + chan->mHandleCurrent = chan->mHandleOriginal; + } + else + { + result = chan->referenceStamp(true); + if (result != FMOD_OK) + { + if (channel) + { + *channel = 0; + } + return result; + } + } + + if (channel) + { + *channel = (ChannelI *)((FMOD_UINT_NATIVE)chan->mHandleCurrent); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::playDSP(FMOD_CHANNELINDEX channelid, DSPI *dsp, bool paused, ChannelI **channel) +{ + FMOD_RESULT result; + ChannelI *chan = 0; + + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (channel) + { + if (channelid == FMOD_CHANNEL_REUSE) + { + ChannelI::validate((Channel *)*channel, &chan); + } + if (channel) + { + *channel = 0; + } + } + + /* + First find a channel + */ + result = findChannel(channelid, dsp, &chan); + if (result != FMOD_OK) + { + return result; + } + + result = chan->play(dsp, paused, true, false); + if (result != FMOD_OK) + { + chan->stopEx(CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_UPDATESYNCPOINTS); + return result; + } + + result = chan->updatePosition(); + if (result != FMOD_OK) + { + return result; + } + + if (channelid == FMOD_CHANNEL_REUSE && *channel) + { + chan->mHandleCurrent = chan->mHandleOriginal; + } + else + { + result = chan->referenceStamp(true); + if (result != FMOD_OK) + { + if (channel) + { + *channel = 0; + } + return result; + } + } + + if (channel) + { + *channel = (ChannelI *)((FMOD_UINT_NATIVE)chan->mHandleCurrent); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getChannel(int id, ChannelI **channel) +{ + FMOD_UINT_NATIVE handle; + + if (!channel) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (id < 0 || id >= mNumChannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + handle = (mIndex << SYSTEMID_SHIFT) | ((id << CHANINDEX_SHIFT) & (CHANINDEX_MASK << CHANINDEX_SHIFT)); + *channel = (ChannelI *)handle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getMasterChannelGroup(ChannelGroupI **channelgroup) +{ + if (!channelgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mChannelGroup) + { + *channelgroup = 0; + return FMOD_ERR_UNINITIALIZED; + } + + *channelgroup = mChannelGroup; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getMasterSoundGroup(SoundGroupI **soundgroup) +{ + if (!soundgroup) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mSoundGroup) + { + *soundgroup = 0; + return FMOD_ERR_UNINITIALIZED; + } + + *soundgroup = mSoundGroup; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createReverb(ReverbI **reverb) +{ +#ifdef FMOD_SUPPORT_MULTIREVERB + FMOD_RESULT result; + ReverbI *newreverb; + + /* + initialise a new reverb instance + */ + newreverb = (ReverbI*)FMOD_Object_Alloc(ReverbI); + if (!newreverb) + { + return FMOD_ERR_MEMORY; + } + + /* + Initialise the reverb object. + */ + result = newreverb->init(this, true, FMOD_REVERB_VIRTUAL); + if (result != FMOD_OK) + { + FMOD_Memory_Free(newreverb); + return result; + } + + /* + Add the reverb object to the system's channel reverb list + */ + newreverb->addBefore(&mReverb3DHead); + + if (reverb) + { + *reverb = newreverb; + } + + mReverbGlobal.setDisableIfNoEnvironment(false); + + mReverb3D.setDisableIfNoEnvironment(false); + + set3DReverbActive(true); + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +#ifdef FMOD_SUPPORT_MULTIREVERB +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +unsigned int SystemI::count3DPhysicalReverbs() +{ + unsigned int count = 0; + + ReverbI* chanreverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext()); + while (chanreverb_c != &mReverb3DHead) + { + if (chanreverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + ++count; + } + chanreverb_c = SAFE_CAST(ReverbI, chanreverb_c->getNext()); + } + return count; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +unsigned int SystemI::count3DVirtualReverbs() +{ + unsigned int count = 0; + + ReverbI* chanreverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext()); + while (chanreverb_c != &mReverb3DHead) + { + if (chanreverb_c->getMode() == FMOD_REVERB_VIRTUAL) + { + ++count; + } + chanreverb_c = SAFE_CAST(ReverbI, chanreverb_c->getNext()); + } + return count; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::set3DReverbActive(bool state) +{ + mReverb3DActive = state; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::get3DReverbActive(bool *state) +{ + if (state) + { + *state = mReverb3DActive; + } + return FMOD_OK; +} + +#endif + +#ifndef FMOD_STATICFORPLUGINS + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setReverbProperties(const FMOD_REVERB_PROPERTIES *prop, bool force_create) +{ +#ifdef FMOD_SUPPORT_REVERB + FMOD_RESULT result; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (prop->Instance < 0 || prop->Instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + int instance = prop->Instance; + +#ifdef FMOD_SUPPORT_SOFTWARE + /* + Create DSP if necessary + */ + if ((!mReverbGlobal.mInstance[instance].mDSP) && (force_create || (prop->Environment!=-1)) && mSoftware) + { + /* + Create + */ + result = mReverbGlobal.createDSP(instance); + if (result == FMOD_OK) + { + /* + Link into network + */ + if (mDSPChannelGroupTarget) + { + result = mDSPChannelGroupTarget->addInput(mReverbGlobal.mInstance[instance].mDSP); + if (result != FMOD_OK) + { + return result; + } + mReverbGlobal.setGain(1.0f); + } + else + { + return FMOD_ERR_UNINITIALIZED; + } + + /* + Connect all channels to the reverb input + */ + ChannelI *channel_c = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext()); + + while (channel_c != &mChannelUsedListHead) + { + FMOD_RESULT result; + FMOD_REVERB_CHANNELPROPERTIES cprops; + + FMOD_memset(&cprops, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + + cprops.Flags |= (instance == 0 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE0 : + instance == 1 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE1 : + instance == 2 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE2 : + instance == 3 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE3 : 0); + + /* + Flush the channel properties through + */ + result = channel_c->getReverbProperties(&cprops); + if (result != FMOD_OK) + { + return result; + } + result = channel_c->setReverbProperties( &cprops); + if (result != FMOD_OK) + { + return result; + } + + channel_c = SAFE_CAST(ChannelI, channel_c->getNext()); + } + + mReverbGlobal.mInstance[instance].mDSP->setActive(true); + } + } +#endif + + /* + Set DSP (sw/hw handled by ReverbI) + */ + result = mReverbGlobal.setProperties(prop); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getReverbProperties(FMOD_REVERB_PROPERTIES *prop) +{ + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (prop->Instance < 0 || prop->Instance >= FMOD_REVERB_MAXINSTANCES) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + return mReverbGlobal.getProperties(prop); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MULTIREVERB +FMOD_RESULT SystemI::set3DReverbProperties(const FMOD_REVERB_PROPERTIES *prop, bool force_create) +{ + FMOD_RESULT result; + int instance = 0; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Create DSP if necessary + */ + if ((!mReverb3D.mInstance[instance].mDSP) && (force_create || (prop->Environment!=-1))) + { + /* + Create + */ + result = mReverb3D.createDSP(instance); + if (result != FMOD_OK) + { + return result; + } + + /* + Link into network + */ + if (mDSPChannelGroupTarget) + { + result = mDSPChannelGroupTarget->addInput(mReverb3D.mInstance[instance].mDSP); + if (result != FMOD_OK) + { + return result; + } + mReverb3D.setGain(1.0f); + } + else + { + return FMOD_ERR_UNINITIALIZED; + } + + /* + Connect all channels to the reverb input + */ + ChannelI* channel_c = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext()); + while (channel_c != &mChannelUsedListHead) + { + /* + Flush the channel properties through + */ + FMOD_REVERB_CHANNELPROPERTIES cprops; + + FMOD_memset(&cprops, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + + result = channel_c->getReverbProperties(&cprops); + if (result != FMOD_OK) + { + return result; + } + result = channel_c->setReverbProperties( &cprops); + if (result != FMOD_OK) + { + return result; + } + + channel_c = SAFE_CAST(ChannelI, channel_c->getNext()); + } + + mReverb3D.mInstance[instance].mDSP->setActive(true); + } + + /* + Set DSP (sw/hw handled by ReverbI) + */ + result = mReverb3D.setProperties(prop); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} +#endif + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MULTIREVERB +FMOD_RESULT SystemI::get3DReverbProperties(FMOD_REVERB_PROPERTIES *prop) +{ + return mReverb3D.getProperties(prop); +} +#endif + +#endif + +/* +[ + [DESCRIPTION] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDSPHead(DSPI **dsp) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mDSPSoundCard) + { + return FMOD_ERR_INTERNAL; + } + + *dsp = mDSPSoundCard; + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::addDSP(DSPI *dsp, DSPConnectionI **dspconnection) +{ + FMOD_RESULT result; + DSPI *dsphead = 0; + + if (!dsp) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = getDSPHead(&dsphead); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->insertInputBetween(dsp, 0, false, dspconnection); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::lockDSP() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_OS_CriticalSection_Enter(mDSPLockCrit); + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::unlockDSP() +{ +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_OS_CriticalSection_Leave(mDSPLockCrit); + + return FMOD_OK; +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getDSPClock(unsigned int *hi, unsigned int *lo) +{ + if (hi) + { + *hi = mDSPClock.mHi; + } + if (lo) + { + *lo = mDSPClock.mLo; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getRecordNumDrivers(int *numdrivers) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + + if (!mOutput) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = checkDriverList(false); + CHECK_RESULT(result); + + if (mOutput->mDescription.record_getnumdrivers) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.record_getnumdrivers(mOutput, numdrivers); + } + + *numdrivers = 0; + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getRecordDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + int numdrivers = 0; + + if (!mOutput) + { + return FMOD_ERR_UNINITIALIZED; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mOutput->mDescription.record_getdriverinfo) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.record_getdriverinfo(mOutput, id, name, namelen, guid); + } + + return FMOD_OK; + +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getRecordDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + int numdrivers = 0; + + if (!mOutput) + { + return FMOD_ERR_UNINITIALIZED; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mOutput->mDescription.record_getdriverinfow) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + return mOutput->mDescription.record_getdriverinfow(mOutput, id, name, namelen, guid); + } + + return FMOD_OK; + +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* + [ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] + ] + */ +FMOD_RESULT SystemI::getRecordDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + FMOD_CAPS lcaps = FMOD_CAPS_NONE; + int lminfrequency = 0; + int lmaxfrequency = 0; + int numdrivers = 0; + + if (!mOutput) + { + return FMOD_ERR_UNINITIALIZED; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mOutput->mDescription.record_getdrivercaps) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + result = mOutput->mDescription.record_getdrivercaps(mOutput, id, &lcaps, &lminfrequency, &lmaxfrequency); + CHECK_RESULT(result); + } + + if (caps) + { + *caps = lcaps; + } + if (minfrequency) + { + *minfrequency = lminfrequency; + } + if (maxfrequency) + { + *maxfrequency = lmaxfrequency; + } + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::recordStart(int id, SoundI *sound, bool loop) +{ +#ifdef FMOD_SUPPORT_RECORDING + int numdrivers = 0; + FMOD_RESULT result = FMOD_OK; + FMOD_RECORDING_INFO *recordinfo = NULL; + + if (!mInitialized) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!sound || (sound->mMode & FMOD_CREATESTREAM)) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + recordStop(id); + + /* + Create recording info for this record instance + */ + recordinfo = FMOD_Object_Calloc(FMOD_RECORDING_INFO); + if (!recordinfo) + { + return FMOD_ERR_MEMORY; + } + + recordinfo->mRecordId = id; + recordinfo->mRecordDriver = -1; + recordinfo->mRecordLoop = loop; + recordinfo->mRecordSound = sound; + recordinfo->mRecordRate = (int)sound->mDefaultFrequency; /* This will get set to what the hardware can do in the driver */ + + /* + Multi-mic support requires GUIDs to be implemented for the platform + */ + result = getRecordDriverInfo(id, NULL, 0, &recordinfo->mRecordGUID); + CHECK_RESULT(result); + + if (mOutput->mDescription.record_start) + { +#ifdef FMOD_SUPPORT_SOFTWARE + mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */ +#else + mOutput->readfrommixer = 0; +#endif + + result = mOutput->mDescription.record_start(mOutput, recordinfo, (FMOD_SOUND *)sound, loop); + CHECK_RESULT(result); + } + + /* + Create a temp buffer to assist in moving data from hardware to sound sample + */ + { + unsigned int bufferlengthbytes = 0; + + SoundI::getBytesFromSamples(FMOD_RECORD_TEMPBUFFERSIZE, &bufferlengthbytes, sound->mChannels, FMOD_SOUND_FORMAT_PCMFLOAT); + recordinfo->mRecordTempBufferLength = FMOD_RECORD_TEMPBUFFERSIZE; + + recordinfo->mRecordTempBuffer = (float*)FMOD_Memory_Calloc(bufferlengthbytes); + if (!recordinfo->mRecordTempBuffer) + { + return FMOD_ERR_MEMORY; + } + } + + /* + Setup a DSPResampler to handle record resampling if required + */ + if (recordinfo->mRecordRate != sound->mDefaultFrequency) + { +#ifdef FMOD_SUPPORT_SOFTWARE + unsigned int blocklength = 0; + FMOD_DSP_DESCRIPTION_EX description; + + /* + Total latency for resampling is ~30ms, we need 3 blocks for the resampler, + so each block will be ~10ms. + */ + blocklength = (int)(0.01f * recordinfo->mRecordRate); + blocklength /= 16; /* Round down to nearest 16 bytes. */ + blocklength *= 16; + + FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + description.channels = sound->mChannels; + description.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; /* Record read callback will always give floats. */ + description.userdata = mOutput; + description.read = Output::recordResamplerReadCallback; + description.mResamplerBlockLength = blocklength; + + recordinfo->mRecordResamplerDSP = FMOD_Object_Calloc(DSPResampler); + if (!recordinfo->mRecordResamplerDSP) + { + return FMOD_ERR_MEMORY; + } + + recordinfo->mRecordResamplerDSP->mSystem = this; + recordinfo->mRecordResamplerDSP->mBuffer = recordinfo->mRecordTempBuffer; + recordinfo->mRecordResamplerDSP->alloc(&description); + recordinfo->mRecordResamplerDSP->mTargetFrequency = (int)sound->mDefaultFrequency; + recordinfo->mRecordResamplerDSP->setFrequency((float)recordinfo->mRecordRate); + recordinfo->mRecordResamplerDSP->setFinished(false); +#else + return FMOD_ERR_NEEDSSOFTWARE; +#endif + } + + /* + Make the new record device "live" by putting in the list + */ + FMOD_OS_CriticalSection_Enter(mOutput->mRecordInfoCrit); + { + recordinfo->addAfter(&mOutput->mRecordInfoHead); + mOutput->mRecordNumActive++; + } + FMOD_OS_CriticalSection_Leave(mOutput->mRecordInfoCrit); + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::recordStop(int id) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + int numdrivers = 0; + FMOD_RECORDING_INFO *currentrecordinfo = 0; + + if (!mInitialized) + { + return FMOD_ERR_UNINITIALIZED; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mOutput->recordGetInfo(id, ¤trecordinfo); + CHECK_RESULT(result); + + if (currentrecordinfo) + { + result = mOutput->recordStop(currentrecordinfo); + CHECK_RESULT(result); + } + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getRecordPosition(int id, unsigned int *position) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + FMOD_RECORDING_INFO *info = NULL; + int numdrivers = 0; + + if (!mInitialized) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + mOutput->recordGetInfo(id, &info); + if (info) + { + *position = info->mRecordOffset; + } + else + { + *position = 0; + } + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::isRecording(int id, bool *recording) +{ +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT result = FMOD_OK; + FMOD_RECORDING_INFO *info = NULL; + int numdrivers = 0; + + if (!mInitialized) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!recording) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = getRecordNumDrivers(&numdrivers); + CHECK_RESULT(result); + + if (id < 0 || id >= numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + mOutput->recordGetInfo(id, &info); + if (info) + { + *recording = true; + } + else + { + *recording = false; + } + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::createGeometry(int maxNumPolygons, int maxNumVertices, GeometryI **geometry) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD_RESULT result; + + if (!geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (maxNumPolygons <= 0) + { + return FMOD_ERR_INVALID_PARAM; + } + if (maxNumVertices <= 0) + { + return FMOD_ERR_INVALID_PARAM; + } + if (geometry == 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + *geometry = (GeometryI *)FMOD_Memory_Alloc(sizeof (GeometryI)); + if (!*geometry) + { + return FMOD_ERR_MEMORY; + } + + new (*geometry) GeometryI(&mGeometryMgr); + + result = (*geometry)->alloc(maxNumPolygons, maxNumVertices); + if (result != FMOD_OK) + { + return result; + } + + if (mGeometryList) + { + (*geometry)->addBefore(mGeometryList); + } + + mGeometryList = *geometry; + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setGeometrySettings(float maxWorldSize) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD_RESULT result; + + if (maxWorldSize <= 0.0f) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mGeometryMgr.setWorldSize(maxWorldSize); + if (result != FMOD_OK) + { + return result; + } + + if (mGeometryList) + { + // we have to remove everything from the tree and re-add + // it when the world size changes + GeometryI* current = mGeometryList; + do + { + current->removeFromTree(); + current = SAFE_CAST(GeometryI, current->getNext()); + } + while (current != mGeometryList); + + current = mGeometryList; + do + { + result = current->setWorldSize(maxWorldSize); // re-adds automatically + if (result != FMOD_OK) + { + return result; + } + + current = SAFE_CAST(GeometryI, current->getNext()); + } + while (current != mGeometryList); + } + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getGeometrySettings(float *maxWorldSize) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + if (!maxWorldSize) + { + return FMOD_ERR_INVALID_PARAM; + } + + *maxWorldSize = mGeometryMgr.getWorldSize(); + + return FMOD_OK; +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::loadGeometry(const void *data, int dataSize, GeometryI **geometry) +{ +#ifdef FMOD_SUPPORT_GEOMETRY + FMOD_RESULT result; + + if (!data) + { + return FMOD_ERR_INVALID_PARAM; + } + if (!geometry) + { + return FMOD_ERR_INVALID_PARAM; + } + *geometry = (GeometryI *)FMOD_Memory_Alloc(sizeof (GeometryI)); + if (!*geometry) + { + return FMOD_ERR_MEMORY; + } + + new (*geometry) GeometryI(&mGeometryMgr); + + result = (*geometry)->load(data, dataSize); + if (result != FMOD_OK) + { + return result; + } + + if (mGeometryList) + { + (*geometry)->addBefore(mGeometryList); + } + + mGeometryList = *geometry; + + return FMOD_OK; + +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + +FMOD_RESULT SystemI::getGeometryOcclusion(const FMOD_VECTOR *listener, + const FMOD_VECTOR *source, + float *direct, float *reverb) +{ + if(!source || !listener) + { + return FMOD_ERR_INVALID_PARAM; + } + + float direct_occlusion = 0.0f, reverb_occlusion = 0.0f; + +#ifdef FMOD_SUPPORT_GEOMETRY + CHECK_RESULT(mGeometryMgr.lineTestAll(listener, source, + &direct_occlusion, &reverb_occlusion)); +#endif + + if(direct) + { + *direct = direct_occlusion; + } + + if(reverb) + { + *reverb = reverb_occlusion; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setNetworkProxy(const char *proxy) +{ +#ifdef FMOD_SUPPORT_NET + return FMOD_Net_SetProxy(proxy); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getNetworkProxy(char *proxy, int proxylen) +{ +#ifdef FMOD_SUPPORT_NET + return FMOD_Net_GetProxy(proxy, proxylen); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setNetworkTimeout(int timeout) +{ +#ifdef FMOD_SUPPORT_NET + return FMOD_Net_SetTimeout(timeout); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getNetworkTimeout(int *timeout) +{ +#ifdef FMOD_SUPPORT_NET + return FMOD_Net_GetTimeout(timeout); +#else + return FMOD_ERR_UNSUPPORTED; +#endif +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setStreamBufferSize(unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype) +{ + if (filebuffersize <= 0) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (filebuffersizetype != FMOD_TIMEUNIT_MS && + filebuffersizetype != FMOD_TIMEUNIT_PCM && + filebuffersizetype != FMOD_TIMEUNIT_PCMBYTES && + filebuffersizetype != FMOD_TIMEUNIT_RAWBYTES) + { + return FMOD_ERR_INVALID_PARAM; + } + + mStreamFileBufferSize = filebuffersize; + mStreamFileBufferSizeType = filebuffersizetype; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getStreamBufferSize(unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype) +{ + if (filebuffersize) + { + *filebuffersize = mStreamFileBufferSize; + } + if (filebuffersizetype) + { + *filebuffersizetype = mStreamFileBufferSizeType; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setUserData(void *userdata) +{ + mUserData = userdata; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getUserData(void **userdata) +{ + if (!userdata) + { + return FMOD_ERR_INVALID_PARAM; + } + + *userdata = mUserData; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getSoundList(SoundI **sound) +{ + if (!sound) + { + return FMOD_ERR_INVALID_PARAM; + } + + *sound = &mSoundListHead; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Stops all playing channels using this sound. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::stopSound(SoundI *sound) +{ + ChannelI *current; + bool streamstopped = false; + +#ifdef FMOD_SUPPORT_RECORDING + /* + If this sound is currently recording, stop the recording + */ + if (mOutput && mOutput->mRecordNumActive) + { + FMOD_RECORDING_INFO *currentrecordinfo = NULL; + + currentrecordinfo = SAFE_CAST(FMOD_RECORDING_INFO, mOutput->mRecordInfoHead.getNext()); + while (currentrecordinfo != &mOutput->mRecordInfoHead) + { + FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, currentrecordinfo->getNext()); + + if (currentrecordinfo->mRecordSound == sound) + { + mOutput->recordStop(currentrecordinfo); + break; + } + currentrecordinfo = next; + } + } +#endif + +#ifdef FMOD_SUPPORT_STREAMING + if (sound->isStream()) + { + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + + if (!mStreamListChannelHead.isEmpty()) + { + LinkedListNode *current; + Stream *stream = SAFE_CAST(Stream, sound); + + current = mStreamListChannelHead.getNext(); + while (current != &mStreamListChannelHead) + { + ChannelStream *channelstream = (ChannelStream *)current->getData(); + Stream *currentstream = SAFE_CAST(Stream, channelstream->mSound); + LinkedListNode *next = current->getNext(); + + streamstopped = false; + + /* + Might not have been played yet. + */ + if (!currentstream) + { + current = next; + continue; + } + + /* + If this channel is playing this stream, stop the channel. + */ + if (currentstream == stream) + { + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + channelstream->mParent->stop(); + streamstopped = true; + + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + } + + /* + If this channel is playing a sentence and it is the sound that is currently playing, stop the channel. + */ +#ifdef FMOD_SUPPORT_SENTENCING + else if (currentstream->mSubSoundList) + { + if (currentstream->mSubSound[currentstream->mSubSoundList[channelstream->mSubSoundListCurrent].mIndex] == sound) + { + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + channelstream->mParent->stop(); + streamstopped = true; + + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + } + else + { + int count; + + for (count = 0; count < currentstream->mNumSubSounds; count++) + { + if (currentstream->mSubSound[count] == sound) + { + if (!(currentstream->mCodec && currentstream->mCodec->mFlags & FMOD_CODEC_USERLENGTH)) + { + currentstream->mLength -= sound->mLength; + } + currentstream->mSubSound[count] = 0; + break; /* found it, so break out of subsound search. */ + } + } + } + } +#endif + + /* + If this channel is playing a substream, stop the channel. + */ + else if (currentstream->mSubSound) + { + int count; + + for (count = 0; count < currentstream->mNumSubSounds; count++) + { + if (currentstream->mSubSound[count] == sound) + { + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + channelstream->mParent->stop(); + streamstopped = true; + + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + break; /* found it, so break out of subsound search. */ + } + } + } + + /* + If this channel is using this stream's sample: Stop that channel. + */ + else if (currentstream->mSample == stream->mSample) + { + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + + channelstream->mParent->stop(); + streamstopped = true; + + FMOD_OS_CriticalSection_Enter(mStreamListCrit); + } + + current = next; + } + } + + FMOD_OS_CriticalSection_Leave(mStreamListCrit); + } +#endif + + /* + Note that release/stopSound() can be called from the event system, on sounds that have never played before. + So don't let these sounds execute an async channel loop, it will crash. The 'Played' flag is never set on these types of sound, they're just temporary storage. + */ + if (sound->mFlags & FMOD_SOUND_FLAG_PLAYED && !streamstopped) + { + if (sound->mNumAudible) /* Channel::stop hasn't been called on all instances of this sound yet. Let's do it for them. */ + { + if (sound->mMode & FMOD_SOFTWARE) /* This is slow, but they didn't stop the sound. Make sure the mixer is not continuing to do things with the channel after we stop. */ + { + lockDSP(); + } + + current = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext()); + while (current != &mChannelUsedListHead) + { + ChannelI *next = SAFE_CAST(ChannelI, current->getNext()); + + if (current->mRealChannel[0]) + { + SoundI *channelsound = 0; + + current->getCurrentSound(&channelsound); + if (channelsound == sound) + { + /* + Same call as current->stop, except we dont want end callbacks when releasing. + */ + current->stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_UPDATESYNCPOINTS); + } + } + + current = next; + } + + if (sound->mMode & FMOD_SOFTWARE) + { + unlockDSP(); + } + + } + else if (sound->mMode & FMOD_SOFTWARE) /* Apparently all instances of this voice have been stopped, BUT! Are they still active in the mixer? */ + { + FMOD_OPENSTATE openstate; + + sound->getOpenState(&openstate, 0,0); + + if (openstate == FMOD_OPENSTATE_STREAMING) + { + lockDSP(); /* Just stall for 1 DSP tick. */ + unlockDSP(); + } + } + + if (!sound->isStream()) + { + sound->mFlags &= ~FMOD_SOUND_FLAG_PLAYED; /* For sample banks, avoid constant dsp locks over and over again for nothing. */ + } + } + + #ifdef PLATFORM_PS2 + update(); + #endif + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::stopDSP(DSPI *dsp) +{ + ChannelI *current; + + current = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext()); + while (current != &mChannelUsedListHead) + { + ChannelI *next = SAFE_CAST(ChannelI, current->getNext()); + DSPI *channeldsp; + + current->getCurrentDSP(&channeldsp); + if (channeldsp == dsp) + { + current->stop(); + } + + current = next; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + On success, FMOD_OK is returned. + On failure, an error code is returned. + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getListenerObject(int listener, Listener **listenerobject) +{ + if (!listenerobject || (listener < 0) || (listener >= mNumListeners)) + { + return FMOD_ERR_INVALID_PARAM; + } + + *listenerobject = &mListener[listener]; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + On success, FMOD_OK is returned. + On failure, an error code is returned. + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setGlobalUserCallbacks(FMOD_FILE_OPENCALLBACK open, FMOD_FILE_CLOSECALLBACK close, FMOD_FILE_READCALLBACK read, FMOD_FILE_SEEKCALLBACK seek) +{ + if (!open || !close || !read || !seek) + { + open = 0; + close = 0; + read = 0; + seek = 0; + + mUsesUserCallbacks = false; + } + else + { + mUsesUserCallbacks = true; + } + + mOpenCallback = open; + mCloseCallback = close; + mReadCallback = read; + mSeekCallback = seek; + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_SOFTWARE +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::flushDSPConnectionRequests(bool calledfrommainthread) +{ + FMOD_OS_CriticalSection_Enter(mDSPConnectionCrit); + { + DSPConnectionRequest *request; + + if (mConnectionRequestFlushing) + { + FMOD_OS_CriticalSection_Leave(mDSPConnectionCrit); + return FMOD_OK; /* addinput and disconnect can trigger this function to recursively execute. */ + } + + if (!mConnectionRequestUsedHead.isEmpty()) + { + mConnectionRequestFlushing = true; + + if (calledfrommainthread) + { + FMOD_OS_CriticalSection_Enter(mDSPCrit); + } + + request = (DSPConnectionRequest *)mConnectionRequestUsedHead.getNext(); + while (request != &mConnectionRequestUsedHead) + { + switch (request->mRequest) + { + case DSPCONNECTION_REQUEST_ADDINPUT: + { + request->mThis->addInputInternal(request->mTarget, false, request->mConnection, 0, false); + break; + } + case DSPCONNECTION_REQUEST_ADDINPUT_ERRCHECK: + { + request->mThis->addInputInternal(request->mTarget, true, request->mConnection, 0, false); + break; + } + case DSPCONNECTION_REQUEST_DISCONNECTFROM: + { + request->mThis->disconnectFromInternal(request->mTarget, request->mConnection, false); + if (request->mTarget) + { + request->mTarget->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + } + else + { + request->mThis->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + } + break; + } + case DSPCONNECTION_REQUEST_DISCONNECTALLINPUTS: + { + request->mThis->disconnectAllInternal(true, false, false); + break; + } + case DSPCONNECTION_REQUEST_DISCONNECTALLOUTPUTS: + { + request->mThis->disconnectAllInternal(false, true, false); + request->mThis->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + break; + } + case DSPCONNECTION_REQUEST_DISCONNECTALL: + { + request->mThis->disconnectAllInternal(true, true, false); + request->mThis->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT; + break; + } + case DSPCONNECTION_REQUEST_INSERTINBETWEEN: + case DSPCONNECTION_REQUEST_INSERTINBETWEEN_SEARCH: + { + request->mThis->insertInputBetweenInternal(request->mTarget, request->mInputIndex, request->mRequest == DSPCONNECTION_REQUEST_INSERTINBETWEEN_SEARCH ? true : false, request->mConnection, false); + request->mTarget->mFlags |= FMOD_DSP_FLAG_USEDADDDSP; + request->mTarget->reset(); + request->mTarget->setActive(true); + break; + } + case DSPCONNECTION_REQUEST_REVERBUPDATEPARAMETERS: + { + #ifdef FMOD_SUPPORT_SFXREVERB + DSPI *reverb = (DSPI *)request->mThis; + if (reverb->mDescription.update) + { + reverb->mDescription.update((FMOD_DSP_STATE *)reverb); + } + #endif + break; + } + #ifdef FMOD_DSP_NONBLOCKING_REMOVE_RELEASE + case DSPCONNECTION_REQUEST_REMOVE: + { + request->mThis->removeInternal(false); + break; + } + case DSPCONNECTION_REQUEST_RELEASE: + { + request->mThis->releaseInternal(request->mTarget ? true : false, false); + break; + } + #endif + } + + request->removeNode(); + request->addBefore(&mConnectionRequestFreeHead); /* Put back on free list. */ + + request = (DSPConnectionRequest *)mConnectionRequestUsedHead.getNext(); + } + mConnectionRequestFlushing = false; + + if (calledfrommainthread) + { + FMOD_OS_CriticalSection_Leave(mDSPCrit); + } + } + } + FMOD_OS_CriticalSection_Leave(mDSPConnectionCrit); + + return FMOD_OK; +} +#endif + + +#ifndef FMOD_STATICFORPLUGINS +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getGlobals(Global **global) +{ + if (!global) + { + return FMOD_ERR_INVALID_PARAM; + } + + *global = gGlobal; + + return FMOD_OK; +} + +FMOD_RESULT F_API System_SetDebugLevel(unsigned int level) +{ +#ifdef FMOD_DEBUG + gGlobal->gDebugLevel = (FMOD_DEBUGLEVEL)level; +#endif + return FMOD_OK; +} + +unsigned int F_API System_GetDebugLevel() +{ +#ifdef FMOD_DEBUG + return (unsigned int )gGlobal->gDebugLevel; +#else + return 0; +#endif +} + +FMOD_RESULT F_API System_SetDebugMode(unsigned int mode) +{ +#ifdef FMOD_DEBUG + gGlobal->gDebugMode = (FMOD_DEBUGMODE)mode; +#endif + return FMOD_OK; +} +#endif + +#ifdef FMOD_SUPPORT_MULTIREVERB +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::update3DReverbs() +{ + float t = 0.0f; + bool reverb3d_active; + ReverbI *reverb_c; + + // + // Standardised properties are all in floating point form for correct arithmetic + // + FMOD_REVERB_STDPROPERTIES stdprops; + FMOD_memset(&stdprops, 0, sizeof(FMOD_REVERB_STDPROPERTIES)); + + // + // For all General mode reverbs, set the level of the listener gain + // + for (reverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext()); reverb_c != &mReverb3DHead; reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext())) + { + float distance_gain, distance_coeff; + bool active; + + reverb_c->getActive(&active); + + if (active) + { + /* + Find listener-reverb distance gain + */ + reverb_c->calculateDistanceGain(&mListener[0].mPosition, &distance_gain, &distance_coeff); + +#ifdef FMOD_SUPPORT_GEOMETRY + /* + Find reverb occlusion gain of the listener-reverb path + */ + if (distance_gain > 0.0f) + { + float direct_occlusion, reverb_occlusion; + FMOD_RESULT result; + FMOD_VECTOR pos; + + reverb_c->get3DAttributes(&pos, 0, 0); + + result = mGeometryMgr.lineTestAll(&mListener[0].mPosition, &pos, &direct_occlusion, &reverb_occlusion); + if (result != FMOD_OK) + { + return result; + } + distance_gain *= (1.0f - reverb_occlusion); + distance_coeff *= (1.0f - reverb_occlusion); + } +#endif + + /* + Reverb modules - ones which have DSP : set the presence gain + */ + if (reverb_c->getMode() == FMOD_REVERB_PHYSICAL) + { + if (distance_gain != reverb_c->getGain()) + { + reverb_c->setGain(distance_gain); + } + } + // + // Virtual reverbs - ones without DSP and just have properties and geometry : add to running average + // + else if (reverb_c->getMode() == FMOD_REVERB_VIRTUAL) + { + if (distance_coeff >= 0.001f) + { + FMOD_REVERB_PROPERTIES reverbprops; + + FMOD_memset(&reverbprops, 0, sizeof(FMOD_REVERB_PROPERTIES)); + + reverb_c->getProperties(&reverbprops); + + ReverbI::sumProps(&stdprops, &reverbprops, distance_coeff); + t += distance_coeff; + } + } + } + } + + get3DReverbActive(&reverb3d_active); + + if (reverb3d_active) + { + FMOD_REVERB_PROPERTIES qprops; + + /* + Complement with ambient setting if there's not enough reverb presence + */ + if (t < 1.0f) + { + FMOD_REVERB_PROPERTIES ambience; + + getReverbAmbientProperties(&ambience); + + if (ambience.Environment == -1) + { + ambience.Room = -10000; + ReverbI::sumRoomProps(&stdprops, &ambience, 1.0f - t); + } + else + { + ReverbI::sumProps(&stdprops, &ambience, 1.0f - t); + } + + t = 1.0f; + } + + /* + Normalise the properties with the accumulated t value + */ + ReverbI::factorProps(&qprops, &stdprops, 1.0f/t); + set3DReverbProperties(&qprops, true); + } + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::setReverbAmbientProperties(FMOD_REVERB_PROPERTIES* prop) +{ + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (prop->Environment != -1) + { + set3DReverbActive(true); + } + + FMOD_memcpy(&mReverb3DAmbientProperties, prop, sizeof(FMOD_REVERB_PROPERTIES)); + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getReverbAmbientProperties(FMOD_REVERB_PROPERTIES* prop) +{ + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(prop, &mReverb3DAmbientProperties, sizeof(FMOD_REVERB_PROPERTIES)); + + return FMOD_OK; +} +#endif + +unsigned int F_API System_GetDebugMode() +{ +#ifdef FMOD_DEBUG + return (unsigned int )gGlobal->gDebugMode; +#else + return 0; +#endif +} + + +/* +[API] +[ + [DESCRIPTION] + Callback for system events. + + [PARAMETERS] + 'system' Pointer to a system handle. Note this could be null if FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED is triggered from the EventSystem. + 'type' The type of callback. Refer to FMOD_SYSTEM_CALLBACKTYPE. + 'commanddata1' The first callback type specific data generated by the callback. See remarks for meaning. + 'commanddata2' The second callback type specific data generated by the callback. See remarks for meaning. + + [RETURN_VALUE] + + [REMARKS] + <u>C++ Users</u>. Cast <b>FMOD_SYSTEM *</b> to <b>FMOD::System *</b> inside the callback and use as normal.<br> + <br> + <u>'commanddata1' and 'commanddata2' meanings.</u><br> + These 2 values are set by the callback depending on what is happening in the callback and the type of callback.<br> + <li><b>FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED</b><br> + <i>commanddata1</i>: Always 0.<br> + <i>commanddata2</i>: Always 0.<br> + <br> + <li><b>FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED</b><br> + <i>commanddata1</i>: A string (char*) which represents the file and line number of the allocation inside FMOD.<br> + <i>commanddata2</i>: The size (int) of the requested allocation.<br> + <br> + <li><b>FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED</b><br> + <i>commanddata1</i>: The handle of the created thread. See notes below for thread handle types<br> + <i>commanddata2</i>: A string (char*) which represents the name of the thread.<br> + <br> + <li><b>FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION</b><br> + <i>commanddata1</i>: Pointer to a FMOD::DSP object that was the target of the DSP connection.<br> + <i>commanddata2</i>: Pointer to a FMOD::DSP object that was the source of the DSP connection.<br> + <br> + <li><b>FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL</b><br> + <i>commanddata1</i>: Pointer to a FMOD::DSP object that was trying to exceed the DSP tree level maximum.<br> + <i>commanddata2</i>: 0.<br> + <br> + + <b>Note!</b> For FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED, the user must call System::update for the callback to trigger! See FMOD_SYSTEM_CALLBACKTYPE for details.<br> + <br> + <b>Note!</b> For FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED, the handle that is returned (via commanddata1) is different on each platform. The types to cast to are as follows.<br> + <li>iPhone, Linux, Mac, Solaris : pthread_t + <li>PS2 : int + <li>PS3 : sys_ppu_thread_t + <li>PSP : PSPThreadWrapper. This is a custom struct you can define as typedef struct PSPThreadWrapper { SceUID id; int (*func)(void *param); void *param; }; + <li>Wii : OSThread + <li>Win32, Win64, Xbox360 : HANDLE + <br> + <br> + <br> + Here is an example of a system callback.<br> + <br> + <PRE> + FMOD_RESULT F_CALLBACK systemcallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void *commanddata1, void *commanddata2) + { + <ul>FMOD::System *sys = (FMOD::System *)system; + <br> + switch (type) + { + <ul>case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED: + { + <ul>int numdrivers; + <br> + printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED occured.\n"); + <br> + sys->getNumDrivers(&numdrivers); + <br> + printf("Numdevices = %d\n", numdrivers); + break; + </ul>} + case FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED: + { + <ul>printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED occured.\n"); + printf("%s.\n", commanddata1); + printf("%d bytes.\n", commanddata2); + break; + </ul>} + case FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED: + { + <ul>printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED occured.\n"); + printf("Thread ID = %d\n", (int)commanddata1); + printf("Thread Name = %s\n", (char *)commanddata2); + break; + </ul>} + case FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION: + { + <ul>FMOD::DSP *source = (FMOD::DSP *)commanddata1; + FMOD::DSP *dest = (FMOD::DSP *)commanddata2; + <br> + printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION occured.\n"); + if (source) + { + <ul>char name[256]; + source->getInfo(name, 0,0,0,0); + printf("SOURCE = %s\n", name); + </ul>} + if (dest) + { + <ul>char name[256]; + dest->getInfo(name, 0,0,0,0); + printf("DEST = %s\n", name); + </ul>} + break; + </ul>} + case FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL: + { + <ul>FMOD::DSP *source = (FMOD::DSP *)commanddata1; + <br> + printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL occured.\n"); + if (source) + { + <ul>char name[256]; + source->getInfo(name, 0,0,0,0); + printf("SOURCE = %s\n", name); + </ul>} + break; + </ul>} + </ul>} + <br> + return FMOD_OK; + </ul>} + </PRE> + <br> + [PLATFORMS] + Win32, Win64, Linux, Linux64, Macintosh, Xbox, Xbox360, PlayStation 2, GameCube, PlayStation Portable, PlayStation 3, Wii, Solaris + + [SEE_ALSO] + System::setCallback + FMOD_SYSTEM_CALLBACKTYPE + System::update +] +*/ +/* +FMOD_RESULT F_CALLBACK FMOD_SYSTEM_CALLBACK(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void* commanddata1, void* commanddata2) +{ +#if 0 + // here purely for documentation purposes. +#endif +} +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details) +{ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + GETMEMORYINFO_IMPL +#else + return FMOD_ERR_UNIMPLEMENTED; +#endif +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SystemI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + FMOD_RESULT result = FMOD_OK; + +#ifndef FMOD_STATICFORPLUGINS + LinkedListNode *node; + int count; + + tracker->add(false, FMOD_MEMBITS_SYSTEM, sizeof(*this)); + + for (node = mSoundListHead.getNext(); node != &mSoundListHead; node = node->getNext()) + { + SoundI *soundi = (SoundI *)node; + CHECK_RESULT(soundi->getMemoryUsed(tracker)); + } + + if (gSoundListCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (mChannel) + { + for (count=0; count < mNumChannels; count++) + { + CHECK_RESULT(mChannel[count].getMemoryUsed(tracker)); + } + } + + if (mOutput && mOutput->mDescription.getmemoryused) + { + CHECK_RESULT(mOutput->mDescription.getmemoryused(mOutput, tracker)); + } + + if (mEmulated) + { + CHECK_RESULT(mEmulated->getMemoryUsed(tracker)); + } + +#ifdef FMOD_SUPPORT_SOFTWARE + if (mDSPTempBuffMem) + { + int bufferchannels; + result = getSoftwareFormat(0, 0, &bufferchannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + tracker->add(false, FMOD_MEMBITS_SYSTEM, (mDSPBlockSize * (bufferchannels < mMaxInputChannels ? mMaxInputChannels : bufferchannels) * sizeof(float)) + 16); + } + + for (count = 0; count < FMOD_DSP_MAXTREEDEPTH; count++) + { + if (mDSPMixBuff[count]) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, (mDSPBlockSize * (mMaxOutputChannels < mMaxInputChannels ? mMaxInputChannels : mMaxOutputChannels) * sizeof(float)) + 16); + } + } + + CHECK_RESULT(mDSPConnectionPool.getMemoryUsed(tracker)); + + if (mDSPCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (mDSPLockCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (mDSPConnectionCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + +#ifdef FMOD_NEED_DSPCODECPOOLINITCRIT + if (mDSPCodecPoolInitCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } +#endif + +// FMOD_OS_GetMemoryUsed(tracker); + + { + LinkedListNode *current; + + current = FMOD::gGlobal->gFileThreadHead.getNext(); + while (current != &FMOD::gGlobal->gFileThreadHead) + { + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(FileThread)); + tracker->add(false, FMOD_MEMBITS_FILE, gSizeofSemaphore); + tracker->add(false, FMOD_MEMBITS_FILE, gSizeofCriticalSection); + current = current->getNext(); + } + } + + if (mDSPSoundCard) + { +#ifdef PLATFORM_PS3 + CHECK_RESULT(((DSPI *)mDSPSoundCard)->getMemoryUsed(tracker)); +#else + CHECK_RESULT(((DSPSoundCard *)mDSPSoundCard)->getMemoryUsed(tracker)); +#endif + } + + if (mDSPChannelGroupTarget) + { + CHECK_RESULT(mDSPChannelGroupTarget->getMemoryUsed(tracker)); + } + + if (mSoftware) + { + CHECK_RESULT(mSoftware->getMemoryUsed(tracker)); + } +#endif + + if (mPluginFactory) + { + CHECK_RESULT(mPluginFactory->getMemoryUsed(tracker)); + } + + for (node = mChannelGroupHead.getNext(); node != &mChannelGroupHead; node = node->getNext()) + { + ChannelGroupI *channelgroupi = (ChannelGroupI *)node; + CHECK_RESULT(channelgroupi->getMemoryUsed(tracker)); + } + + if (mSoundGroup) + { + CHECK_RESULT(mSoundGroup->getMemoryUsed(tracker)); + } + +// MemSingleton mMultiSubSampleLockBuffer; + + if (mMultiSubSampleLockBufferCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (FMOD::gGlobal->gFileCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (FMOD::gGlobal->gAsyncCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + CHECK_RESULT(mSpeakerLevelsPool.getMemoryUsed(tracker)); + CHECK_RESULT(mHistoryBufferPool.getMemoryUsed(tracker)); + +#ifdef FMOD_SUPPORT_STREAMING +// LinkedListNode mStreamListSoundHead; + CHECK_RESULT(mStreamThread.getMemoryUsed(tracker)); + + if (mStreamRealchanCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (mStreamUpdateCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } + + if (mStreamListCrit) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection); + } +#endif + +#ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + CHECK_RESULT(mDSPCodecPool_MPEG.getMemoryUsed(tracker)); + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + CHECK_RESULT(mDSPCodecPool_ADPCM.getMemoryUsed(tracker)); + #endif + #ifdef FMOD_SUPPORT_XMA + CHECK_RESULT(mDSPCodecPool_XMA.getMemoryUsed(tracker)); + #endif + #ifdef FMOD_SUPPORT_CELT + CHECK_RESULT(mDSPCodecPool_CELT.getMemoryUsed(tracker)); + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + CHECK_RESULT(mDSPCodecPool_RAW.getMemoryUsed(tracker)); + #endif +#endif + +#ifdef FMOD_SUPPORT_GEOMETRY +// GeometryI *mGeometryList; +// GeometryMgr mGeometryMgr; +#endif + + CHECK_RESULT(mReverbGlobal.getMemoryUsed(tracker)); + +#ifdef FMOD_SUPPORT_MULTIREVERB + CHECK_RESULT(mReverb3D.getMemoryUsed(tracker)); + + for (node = mReverb3DHead.getNext(); node != &mReverb3DHead; node = node->getNext()) + { + CHECK_RESULT(((ReverbI *)node)->getMemoryUsed(tracker)); + } +#endif + + #ifdef FMOD_SUPPORT_PROFILE + if (gGlobal->gProfile) + { + CHECK_RESULT(gGlobal->gProfile->getMemoryUsed(tracker)); + } + #endif + + + tracker->add(false, FMOD_MEMBITS_SYSTEM, FMOD_OS_GetMemoryUsed()); + + #ifdef FMOD_SUPPORT_PROFILE + if (gGlobal->gProfile) + { + CHECK_RESULT(gGlobal->gProfile->getMemoryUsed(tracker)); + } + #endif + +#endif // #ifndef FMOD_STATICFORPLUGINS + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_systemi.h b/src/fmod_systemi.h new file mode 100755 index 0000000..d33a550 --- /dev/null +++ b/src/fmod_systemi.h @@ -0,0 +1,524 @@ +#ifndef _FMOD_SYSTEMI_H +#define _FMOD_SYSTEMI_H + +#include "fmod_settings.h" + +#include "fmod.hpp" +#include "fmod_channeli.h" +#include "fmod_channel_stream.h" +#include "fmod_channelgroupi.h" +#include "fmod_dsp_connectionpool.h" +#include "fmod_dsp_codecpool.h" +#include "fmod_dspi.h" +#include "fmod_listener.h" +#include "fmod_soundi.h" +#include "fmod_soundgroupi.h" +#include "fmod_speakerlevels_pool.h" +#include "fmod_historybuffer_pool.h" +#include "fmod_reverbi.h" +#include "fmod_geometry_mgr.h" +#include "fmod_thread.h" +#include "fmod_time.h" +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +#define FMOD_NEED_DSPCODECPOOLINITCRIT \ + defined(FMOD_SUPPORT_DSPCODEC) && \ + (defined(FMOD_SUPPORT_MPEG) || defined(FMOD_SUPPORT_IMAADPCM) || \ + defined(FMOD_SUPPORT_XMA) || defined(FMOD_SUPPORT_RAWCODEC) || defined(FMOD_SUPPORT_CELT) + +namespace FMOD +{ + class Output; + class OutputSoftware; + class OutputEmulated; + class PluginFactory; + class GeometryI; + class MemPool; + class ReverbI; + class Downmix; + + typedef struct + { + FMOD_SPEAKER mSpeaker; + FMOD_VECTOR mPosition; + FMOD_VECTOR mXZNormal; + float mXZAngle; + float mDistance; + bool mActive; + bool mPairUseVBAP; + float mPairVBAPSign; + } FMOD_SPEAKERCONFIG; + + const FMOD_SPEAKERMODE FMOD_SPEAKERMODE_STEREO_LINEAR = (FMOD_SPEAKERMODE)1000; /* The speakers are stereo with non constant power (linear) panning. */ + const int FMOD_MAXPRIORITIES = 256; + const int FMOD_MAXAUDIBIILITY = 1000; + const int FMOD_STREAMDECODEBUFFERSIZE_DEFAULT = 400; /* 400ms */ + + const int FMOD_ADVANCEDSETTINGS_MAXXMACODECS = 32; + const int FMOD_ADVANCEDSETTINGS_MAXADPCMCODECS = 32; + const int FMOD_ADVANCEDSETTINGS_MAXMPEGCODECS = 16; + const int FMOD_ADVANCEDSETTINGS_MAXCELTCODECS = 16; + const int FMOD_ADVANCEDSETTINGS_MAXPCMCODECS = 16; + const int FMOD_ADVANCEDSETTINGS_MAX3DREVERBDSPS = 0; + + const int FMOD_RECORD_TEMPBUFFERSIZE = 2048; + + /* + ========================================================================================== + WARNING!!! Don't #ifdef stuff (ABOVE THE LINE - see below) here that might be included in + the static version of fmodex.dll and not in the plugin version of fmodex.dll! If you do + the shared class will be out of sync and it will crash! + ========================================================================================== + */ + class SystemI : public LinkedListNode + { + DECLARE_MEMORYTRACKER_NONVIRTUAL_EXPORT + + public: + bool mInitialized; + bool mPluginsLoaded; + FMOD_UINT_NATIVE mMainThreadID; + FMOD_INITFLAGS mFlags; + SoundI mSoundListHead; + static FMOD_OS_CRITICALSECTION *gSoundListCrit; // This protects mSoundListHead, mSoundGroupUsedHead, mSoundGroupFreeHead + + + /* + Channels & channel management + */ + int mNumChannels; + ChannelI *mChannel; + ChannelI mChannelUsedListHead; + ChannelI mChannelFreeListHead; + SortedLinkedListNode mChannelSortedListHead; + + /* + Output variables. + */ + Output *mOutput; + FMOD_OUTPUTTYPE mOutputType; + FMOD_SOUND_FORMAT mOutputFormat; + int mOutputRate; + int mOutputHandle; + int mMaxInputChannels; + int mMaxOutputChannels; + int mSelectedDriver; + OutputEmulated *mEmulated; + + /* + DSP system + */ + unsigned int mDSPBlockSize; + unsigned int mDSPBufferSize; +#ifdef FMOD_SUPPORT_SOFTWARE + float *mDSPTempBuff; + float *mDSPTempBuffMem; + float *mDSPMixBuff[FMOD_DSP_MAXTREEDEPTH]; + DSPConnectionPool mDSPConnectionPool; + FMOD_OS_CRITICALSECTION *mDSPCrit; + FMOD_OS_CRITICALSECTION *mDSPLockCrit; + FMOD_OS_CRITICALSECTION *mDSPConnectionCrit; + /* one crit for all codec pools; this means e.g. ADPCM can block MPEG + unnecessarily, but this is a very rare situation anyway + */ + FMOD_OS_CRITICALSECTION *mDSPCodecPoolInitCrit; + bool mDSPActive; + DSPI *mDSPSoundCard; + DSPI *mDSPChannelGroupTarget; + TimeStamp mDSPTimeStamp; + DSPConnectionRequest mConnectionRequest[FMOD_DSP_CONNECTION_REQUEST_MAX]; + DSPConnectionRequest mConnectionRequestUsedHead; + DSPConnectionRequest mConnectionRequestFreeHead; + bool mConnectionRequestFlushing; + OutputSoftware *mSoftware; +#endif + + FMOD_PPCALIGN16(FMOD_UINT64P mDSPClock); + + /* + 3D Controls + */ + Listener mListener[LISTENER_MAX]; + int mNumListeners; + float mDistanceScale; + float mRolloffScale; + float mDopplerScale; + FMOD_3D_ROLLOFFCALLBACK mRolloffCallback; + + /* + Plugins + */ + PluginFactory *mPluginFactory; + char mPluginPath[FMOD_STRING_MAXPATHLEN]; + unsigned int mFSBPluginHandle; + unsigned int mWAVPluginHandle; + unsigned int mMPEGPluginHandle; + unsigned int mCELTPluginHandle; + + /* + Miscellaneous + */ + FMOD_ADVANCEDSETTINGS mAdvancedSettings; + void *mUserData; + TimeStamp mUpdateTimeStamp; + unsigned int mLastTimeStamp; + unsigned int mIndex; + int mNumSoftwareChannels; + int mMinHardwareChannels2D; + int mMaxHardwareChannels2D; + int mMinHardwareChannels3D; + int mMaxHardwareChannels3D; + unsigned int mDeviceListLastCheckedTime; + bool mDeviceListChanged; + bool mCreatedHardwareSample; + ChannelGroupI *mChannelGroup; + ChannelGroupI mChannelGroupHead; + SoundGroupI *mSoundGroup; + LinkedListNode mSoundGroupUsedHead; + LinkedListNode mSoundGroupFreeHead; + FMOD_DSP_RESAMPLER mResampleMethod; + MemSingleton mMultiSubSampleLockBuffer; + FMOD_OS_CRITICALSECTION *mMultiSubSampleLockBufferCrit; + bool mUsesUserCallbacks; + unsigned int mBufferSize; + + FMOD_FILE_OPENCALLBACK mOpenCallback; + FMOD_FILE_CLOSECALLBACK mCloseCallback; + FMOD_FILE_READCALLBACK mReadCallback; + FMOD_FILE_SEEKCALLBACK mSeekCallback; + + FMOD_FILE_OPENCALLBACK mOpenRiderCallback; + FMOD_FILE_CLOSECALLBACK mCloseRiderCallback; + FMOD_FILE_READCALLBACK mReadRiderCallback; + FMOD_FILE_SEEKCALLBACK mSeekRiderCallback; + + FMOD_SYSTEM_CALLBACK mCallback; + + SpeakerLevelsPool mSpeakerLevelsPool; + HistoryBufferPool mHistoryBufferPool; + + /* + Stream thread handles etc. + */ + unsigned int mStreamFileBufferSize; + FMOD_TIMEUNIT mStreamFileBufferSizeType; + + /* + Speaker Variables + */ + FMOD_SPEAKERMODE mSpeakerMode; + FMOD_SPEAKERCONFIG mSpeaker[FMOD_SPEAKER_MAX]; + FMOD_SPEAKERCONFIG *mSpeakerList[FMOD_SPEAKER_MAX]; + + /* + Standard global reverb + */ + ReverbI mReverbGlobal; + + Downmix *mDownmix; + + /* + /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ + + ======================================================= + WARNING!!! Don't #ifdef stuff (ABOVE THIS LINE) here + that might be included in the static version of + fmodex.dll and not in the plugin version of fmodex.dll! + If you do the shared class will be out of sync and it + will crash! + ======================================================= + + \/ \/ \/ \/ \/ \/ ok to ifdef stuff \/ \/ \/ \/ \/ \/ + */ +#ifdef FMOD_SUPPORT_STREAMING + static void streamThread(void *data); + LinkedListNode mStreamListChannelHead; + LinkedListNode *mStreamListChannelCurrent; + LinkedListNode *mStreamListChannelNext; + LinkedListNode mStreamListSoundHead; + Thread mStreamThread; + bool mStreamThreadActive; + FMOD_OS_CRITICALSECTION *mStreamRealchanCrit; + FMOD_OS_CRITICALSECTION *mStreamUpdateCrit; + FMOD_OS_CRITICALSECTION *mStreamListCrit; + TimeStamp mStreamTimeStamp; +#endif + + /* + Output variables. + */ +#ifdef FMOD_SUPPORT_ASIO + FMOD_SPEAKER mASIOSpeakerList[DSP_MAXLEVELS_OUT]; +#endif + + /* + DSP Codec pools. + */ + +#ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_MPEG + DSPCodecPool mDSPCodecPool_MPEG; + #endif + #ifdef FMOD_SUPPORT_IMAADPCM + DSPCodecPool mDSPCodecPool_ADPCM; + #endif + #ifdef FMOD_SUPPORT_XMA + DSPCodecPool mDSPCodecPool_XMA; + #endif + #ifdef FMOD_SUPPORT_CELT + DSPCodecPool mDSPCodecPool_CELT; + #endif + #ifdef FMOD_SUPPORT_RAWCODEC + DSPCodecPool mDSPCodecPool_RAW; + #endif +#endif + + /* + Geometry + */ +#ifdef FMOD_SUPPORT_GEOMETRY + GeometryI *mGeometryList; + GeometryMgr mGeometryMgr; +#ifdef FMOD_SUPPORT_GEOMETRY_THREADED + TimeStamp mGeometryTimeStamp; +#endif +#endif // FMOD_SUPPORT_GEOMETRY + +#ifdef FMOD_SUPPORT_MULTIREVERB + /* + 3D reverbs + */ + + ReverbI mReverb3D; + + ReverbI mReverb3DHead; + bool mReverb3DActive; + FMOD_REVERB_PROPERTIES mReverb3DAmbientProperties; + FMOD_RESULT set3DReverbProperties(const FMOD_REVERB_PROPERTIES *prop, bool force_create=false); + FMOD_RESULT get3DReverbProperties(FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT set3DReverbProperties (int instance, const FMOD_REVERB_PROPERTIES *prop, const FMOD_VECTOR *pos, const float mindistance, const float maxdistance); + FMOD_RESULT get3DReverbProperties (int instance, FMOD_REVERB_PROPERTIES *prop, FMOD_VECTOR *pos, float *mindistance, float *maxdistance); + FMOD_RESULT setReverbAmbientProperties(FMOD_REVERB_PROPERTIES* prop); + FMOD_RESULT getReverbAmbientProperties(FMOD_REVERB_PROPERTIES* prop); + FMOD_RESULT update3DReverbs(); + FMOD_RESULT set3DReverbActive(bool reverb_active); + FMOD_RESULT get3DReverbActive(bool *reverb_active); + unsigned int count3DPhysicalReverbs(); + unsigned int count3DVirtualReverbs(); + +#else + FMOD_RESULT setReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop) { return FMOD_ERR_UNSUPPORTED; } + FMOD_RESULT getReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop) { return FMOD_ERR_UNSUPPORTED; } +#endif + + /* + Internal member functions. + */ + static FMOD_RESULT getInstance(unsigned int index, SystemI **sys); + static FMOD_RESULT validate(System *sound, SystemI **systemi); + + FMOD_RESULT updateStreams(); + FMOD_RESULT updateChannels(int delta); + FMOD_RESULT updateSoundGroups(int delta); +#ifdef FMOD_SUPPORT_VSTPLUGIN + FMOD_RESULT updateVSTPlugins(); + LinkedListNode mVSTPluginsListHead; +#endif + FMOD_RESULT findChannel(FMOD_CHANNELINDEX id, SoundI *sound, ChannelI **channel); + FMOD_RESULT findChannel(FMOD_CHANNELINDEX id, DSPI *dsp, ChannelI **channel); + FMOD_RESULT createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + FMOD_RESULT createDSP(FMOD_DSP_DESCRIPTION_EX *description, DSPI **dsp, bool allocate = true); + FMOD_RESULT createSoundInternal(const char *name_or_data, FMOD_MODE mode, unsigned int buffersize, FMOD_TIMEUNIT buffersizetype, FMOD_CREATESOUNDEXINFO *exinfo, bool calledfromasync, SoundI **sound); + FMOD_RESULT checkDriverList(bool fromsystemupdate); + FMOD_RESULT setUpPlugins(); + FMOD_RESULT sortSpeakerList(); + FMOD_RESULT prepareSpeakerPairs(); +#ifdef FMOD_SUPPORT_DSPCODEC + FMOD_RESULT allocateDSPCodec(FMOD_SOUND_FORMAT format, DSPCodec **dsp); +#endif + FMOD_RESULT getSoftwareFormat(int *samplerate, FMOD_SOUND_FORMAT *format, int *numoutputchannels, int *maxinputchannels, FMOD_DSP_RESAMPLER *resamplemethod, int *bits) + { + if (samplerate) + { + *samplerate = mOutputRate; + } + if (format) + { + *format = mOutputFormat; + } + if (numoutputchannels) + { + *numoutputchannels = mMaxOutputChannels; + } + if (maxinputchannels) + { + *maxinputchannels = mMaxInputChannels; + } + if (resamplemethod) + { + *resamplemethod = mResampleMethod; + } + if (bits) + { + SoundI::getBitsFromFormat(mOutputFormat, bits); + } + return FMOD_OK; + } + + + SystemI(); + virtual ~SystemI() {} + + FMOD_RESULT release (); + + // Pre-init functions. + FMOD_RESULT setOutput (FMOD_OUTPUTTYPE output); + FMOD_RESULT getOutput (FMOD_OUTPUTTYPE *output); + FMOD_RESULT getNumDrivers (int *numdrivers); + FMOD_RESULT getDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverInfoW (int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverCaps (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode); + FMOD_RESULT setDriver (int driver); + FMOD_RESULT getDriver (int *driver); + FMOD_RESULT setHardwareChannels (int min2d, int max2d, int min3d, int max3d); + FMOD_RESULT getHardwareChannels (int *num2d, int *num3d, int *total); + FMOD_RESULT setSoftwareChannels (int numsoftwarechannels); + FMOD_RESULT getSoftwareChannels (int *numsoftwarechannels); + FMOD_RESULT setSoftwareFormat (int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplemethod); + //FMOD_RESULT getSoftwareFormat // see above + FMOD_RESULT setDSPBufferSize (unsigned int bufferlength, int numbuffers); + FMOD_RESULT getDSPBufferSize (unsigned int *bufferlength, int *numbuffers); + FMOD_RESULT setFileSystem (FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int buffersize); + FMOD_RESULT attachFileSystem (FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek); + FMOD_RESULT setAdvancedSettings (FMOD_ADVANCEDSETTINGS *settings); + FMOD_RESULT getAdvancedSettings (FMOD_ADVANCEDSETTINGS *settings); + FMOD_RESULT setSpeakerMode (FMOD_SPEAKERMODE speakermode); + FMOD_RESULT getSpeakerMode (FMOD_SPEAKERMODE *speakermode) { if (!speakermode) { return FMOD_ERR_INVALID_PARAM; } *speakermode = mSpeakerMode; return FMOD_OK; } + FMOD_RESULT setCallback (FMOD_SYSTEM_CALLBACK callback); + + // Plug-in support + FMOD_RESULT setPluginPath (const char *path); + FMOD_RESULT loadPlugin (const char *filename, unsigned int *handle, unsigned int priority); + FMOD_RESULT unloadPlugin (unsigned int handle); + FMOD_RESULT getNumPlugins (FMOD_PLUGINTYPE plugintype, int *numplugins); + FMOD_RESULT getPluginHandle (FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle); + FMOD_RESULT getPluginInfo (unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version); + FMOD_RESULT setOutputByPlugin (unsigned int handle); + FMOD_RESULT getOutputByPlugin (unsigned int *handle); + FMOD_RESULT createDSPByPlugin (unsigned int handle, DSPI **dsp); + FMOD_RESULT createCodec (FMOD_CODEC_DESCRIPTION *description, unsigned int priority); + + // Init/Close + FMOD_RESULT init (int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata); + FMOD_RESULT close (); + FMOD_RESULT closeEx (bool calledfrominit); + // Post-init system functions + FMOD_RESULT update (); + + FMOD_RESULT set3DSettings (float dopplerscale, float distancefactor, float rolloffscale); + FMOD_RESULT get3DSettings (float *dopplerscale, float *distancefactor, float *rolloffscale); + FMOD_RESULT set3DNumListeners (int numlisteners); + FMOD_RESULT get3DNumListeners (int *numlisteners); + FMOD_RESULT set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up); + FMOD_RESULT get3DListenerAttributes(int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up); + FMOD_RESULT set3DRolloffCallback (FMOD_3D_ROLLOFFCALLBACK callback); + FMOD_RESULT set3DSpeakerPosition (FMOD_SPEAKER speaker, float x, float y, bool active = true); + FMOD_RESULT get3DSpeakerPosition (FMOD_SPEAKER speaker, float *x, float *y, bool *active = 0); + + FMOD_RESULT setStreamBufferSize (unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype); + FMOD_RESULT getStreamBufferSize (unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype); + + // System information functions. + FMOD_RESULT getVersion (unsigned int *version); + FMOD_RESULT getOutputHandle (void **handle); + FMOD_RESULT getChannelsPlaying (int *channels); + FMOD_RESULT getCPUUsage (float *dsp, float *stream, float *geometry, float *update, float *total); + FMOD_RESULT getSoundRAM (int *currentalloced, int *maxalloced, int *total); + FMOD_RESULT getNumCDROMDrives (int *numdrives); + FMOD_RESULT getCDROMDriveName (int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen); + FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset); + + // Sound/DSP/Channel/FX creation and retrieval. + FMOD_RESULT createSound (const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, SoundI **sound); + FMOD_RESULT createStream (const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, SoundI **sound); + FMOD_RESULT createDSP (FMOD_DSP_DESCRIPTION *description, DSPI **dsp); + FMOD_RESULT createDSPByType (FMOD_DSP_TYPE type, DSPI **dsp); + FMOD_RESULT createChannelGroup (const char *name, ChannelGroupI **channelgroup); + FMOD_RESULT createSoundGroup (const char *name, SoundGroupI **soundgroup); + FMOD_RESULT createReverb (ReverbI **reverb); + + FMOD_RESULT playSound (FMOD_CHANNELINDEX channelid, SoundI *sound, bool paused, ChannelI **channel); + FMOD_RESULT playDSP (FMOD_CHANNELINDEX channelid, DSPI *dsp, bool paused, ChannelI **channel); + FMOD_RESULT getChannel (int channelid, ChannelI **channel); + FMOD_RESULT getMasterChannelGroup (ChannelGroupI **channelgroup); + FMOD_RESULT getMasterSoundGroup (SoundGroupI **soundgroup); + + // Reverb api + FMOD_RESULT setReverbProperties (const FMOD_REVERB_PROPERTIES *prop, bool force_create=false); + FMOD_RESULT getReverbProperties (FMOD_REVERB_PROPERTIES *prop); + + // System level DSP access. + FMOD_RESULT getDSPHead (DSPI **dsp); + FMOD_RESULT addDSP (DSPI *dsp, DSPConnectionI **connection); + FMOD_RESULT lockDSP (); + FMOD_RESULT unlockDSP (); + FMOD_RESULT getDSPClock (unsigned int *hi, unsigned int *lo); + + // Recording api + FMOD_RESULT getRecordNumDrivers (int *numdrivers); + FMOD_RESULT getRecordDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getRecordDriverInfoW (int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getRecordDriverCaps (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency); + FMOD_RESULT getRecordPosition (int id, unsigned int *position); + + FMOD_RESULT recordStart (int id, SoundI *sound, bool loop); + FMOD_RESULT recordStop (int id); + FMOD_RESULT isRecording (int id, bool *recording); + + // Geometry api + FMOD_RESULT createGeometry (int maxpolygons, int maxvertices, GeometryI **geometry); + FMOD_RESULT setGeometrySettings (float maxworldsize); + FMOD_RESULT getGeometrySettings (float *maxworldsize); + FMOD_RESULT loadGeometry (const void *data, int datasize, GeometryI **geometry); + FMOD_RESULT getGeometryOcclusion (const FMOD_VECTOR *listener, const FMOD_VECTOR *source, float *direct, float *reverb); + + // Network functions. + FMOD_RESULT setNetworkProxy (const char *proxy); + FMOD_RESULT getNetworkProxy (char *proxy, int proxylen); + FMOD_RESULT setNetworkTimeout (int timeout); + FMOD_RESULT getNetworkTimeout (int *timeout); + + // Userdata set/get. + FMOD_RESULT setUserData (void *userdata); + FMOD_RESULT getUserData (void **userdata); + + FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); + + // =================================================================================================== + // Functions not exposed in FMOD::System. + // =================================================================================================== + + FMOD_RESULT F_API getSoundList (SoundI **sound); + FMOD_RESULT F_API createChannelGroupInternal (const char *name, ChannelGroupI **channelgroup, bool createdsp, bool storenameinchannelgroup); + + FMOD_RESULT stopSound (SoundI *sound); + FMOD_RESULT stopDSP (DSPI *dsp); + FMOD_RESULT getListenerObject (int listener, Listener **listenerobject); + + FMOD_RESULT setGlobalUserCallbacks (FMOD_FILE_OPENCALLBACK open, FMOD_FILE_CLOSECALLBACK close, FMOD_FILE_READCALLBACK read, FMOD_FILE_SEEKCALLBACK seek); + +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT flushDSPConnectionRequests(bool calledfrommainthread = true); +#endif + +#ifndef FMOD_STATICFORPLUGINS + static FMOD_RESULT F_API getGlobals(Global **global); +#endif + }; +} + +#endif + diff --git a/src/fmod_thread.cpp b/src/fmod_thread.cpp new file mode 100755 index 0000000..c813621 --- /dev/null +++ b/src/fmod_thread.cpp @@ -0,0 +1,424 @@ +#include "fmod_settings.h" + +#include "fmod_autocleanup.h" +#include "fmod_debug.h" +#include "fmod_thread.h" +#include "fmod_memory.h" +#include "fmod_os_misc.h" +#include "fmod_time.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +THREAD_RETURNTYPE THREAD_CALLCONV Thread::callback(void *data) +{ + FMOD_UINT_NATIVE id; + Thread *thread = (Thread *)data; + + FMOD_OS_Thread_GetCurrentID(&id); + + gGlobal->gSystemPool->getCurrentThreadID(id); + + thread->mRunning = true; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::callback", "* %s started\n", thread->mName)); + + while (thread->mRunning) + { + if (thread->mSema) + { + FMOD_OS_Semaphore_Wait(thread->mSema); + } + + if (thread->mRunning) + { + if (thread->mUserCallback) + { + thread->mUserCallback(thread->mUserData); + } + else + { + thread->threadFunc(); + } + + if (thread->mPeriod) + { + FMOD_OS_Time_Sleep(thread->mPeriod); + } + } + } + + gGlobal->gSystemPool->clearThreadID(id); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::callback", "* %s finished\n", thread->mName)); + + FMOD_OS_Semaphore_Signal(thread->mEndSema, false); + + data = 0; + + THREAD_RETURN +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Thread::threadFunc() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +Thread::Thread() +{ + mHandle = 0; + mRunning = false; + mStack = 0; + mUserData = 0; + mSema = 0; + mEndSema = 0; + mPeriod = 0; + mUserCallback = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Thread::initThread(const char *name, void (*func)(void *userdata), void *userdata, PRIORITY priority, void *stack, int stacksize, bool usesemaphore, int sleepperiod, SystemI *system) +{ + FMOD_RESULT result; + FMOD_THREAD_PRIORITY pri; + AutoFreeSema endsema_cleanup; + AutoFreeSema sema_cleanup; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::initThread", "Initializing %s. priority %d\n", name ? name : "(null)", priority)); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::initThread", "- Stacksize %d. Stack pointer %p : usesemaphore = %d : sleeptime = %d\n", stacksize, stack, usesemaphore ? 1 : 0, sleepperiod)); + + mUserCallback = func; + mUserData = userdata; + mPeriod = sleepperiod; + mRunning = false; + + result = FMOD_OS_Semaphore_Create(&mEndSema); + if (result != FMOD_OK) + { + return result; + } + endsema_cleanup = mEndSema; + + if (usesemaphore) + { + result = FMOD_OS_Semaphore_Create(&mSema); + if (result != FMOD_OK) + { + return result; + } + sema_cleanup = mSema; + } + + switch(priority) + { + case PRIORITY_VERYLOW: + { + pri = FMOD_THREAD_PRIORITY_VERYLOW; + break; + } + case PRIORITY_LOW: + { + pri = FMOD_THREAD_PRIORITY_LOW; + break; + } + case PRIORITY_NORMAL: + { + pri = FMOD_THREAD_PRIORITY_NORMAL; + break; + } + case PRIORITY_HIGH: + { + pri = FMOD_THREAD_PRIORITY_HIGH; + break; + } + case PRIORITY_VERYHIGH: + { + pri = FMOD_THREAD_PRIORITY_VERYHIGH; + break; + } + case PRIORITY_CRITICAL: + { + pri = FMOD_THREAD_PRIORITY_CRITICAL; + break; + } + default: + { + return FMOD_ERR_INVALID_PARAM; + } + } + + if (name) + { + FMOD_strncpy(mName, name, FMOD_STRING_MAXNAMELEN); + } + else + { + FMOD_strcpy(mName, "?????"); + } + + #ifdef PLATFORM_PS2_EE + if (!stack) + { + #define ALIGNMENT 16 + stack = mStack = FMOD_Memory_Calloc(stacksize + ALIGNMENT); + if (!stack) + { + return FMOD_ERR_MEMORY; + } + stack = (void *)FMOD_ALIGNPOINTER(stack, ALIGNMENT); + #undef ALIGNMENT + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::initThread", "allocated a thread from the heap. Address = %p.\n", mStack)); + } + #endif + + result = FMOD_OS_Thread_Create(name, callback, this, pri, stack, stacksize, &mHandle); + if (result != FMOD_OK) + { + return result; + } + + /* + Wait for thread to actually start before continuing. + */ + while (!mRunning) + { + FMOD_OS_Time_Sleep(1); + } + + if (FMOD::gGlobal->gSystemCallback) + { + FMOD::gGlobal->gSystemCallback((FMOD_SYSTEM *)system, FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED, mHandle, (void*)name); + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::initThread", "done.\n")); + sema_cleanup.releasePtr(); + endsema_cleanup.releasePtr(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Thread::closeThread() +{ + if (mRunning) + { + FMOD_RESULT result; + + mRunning = false; + + if (mSema) + { + result = FMOD_OS_Semaphore_Signal(mSema, false); /* make thread execute */ + if (result != FMOD_OK) + { + return result; + } + } + + result = FMOD_OS_Semaphore_Wait(mEndSema); /* wait for thread to exit */ + if (result != FMOD_OK) + { + return result; + } + + if (mSema) + { + result = FMOD_OS_Semaphore_Free(mSema); + if (result != FMOD_OK) + { + return result; + } + mSema = 0; + } + + result = FMOD_OS_Semaphore_Free(mEndSema); + if (result != FMOD_OK) + { + return result; + } + mEndSema = 0; + + result = FMOD_OS_Thread_Destroy(mHandle); + if (result != FMOD_OK) + { + return result; + } + mHandle = 0; + + if (mStack) + { + FMOD_Memory_Free(mStack); + mStack = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Thread::close","%s thread destroyed\n", mName)); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Thread::getCurrentThreadID(FMOD_UINT_NATIVE *id) +{ + FMOD_OS_Thread_GetCurrentID(id); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT Thread::wakeupThread(bool frominterrupt) +{ + if (mSema) + { + return FMOD_OS_Semaphore_Signal(mSema, frominterrupt); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT Thread::getMemoryUsedImpl(MemoryTracker *tracker) +{ +//AJS void *mStack; + + if (mSema) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofSemaphore); + } + + if (mEndSema) + { + tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofSemaphore); + } + + return FMOD_OK; +} + +#endif + +} diff --git a/src/fmod_thread.h b/src/fmod_thread.h new file mode 100755 index 0000000..7cb12dc --- /dev/null +++ b/src/fmod_thread.h @@ -0,0 +1,63 @@ +#ifndef _FMOD_THREAD_H +#define _FMOD_THREAD_H + +#include "fmod_settings.h" + +#include "fmod_string.h" +#include "fmod_os_misc.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +namespace FMOD +{ + class SystemI; + + class Thread + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + char mName[FMOD_STRING_MAXNAMELEN]; + void *mHandle; + bool mRunning; + void *mUserData; + void *mStack; + FMOD_OS_SEMAPHORE *mSema; + FMOD_OS_SEMAPHORE *mEndSema; + void (*mUserCallback)(void *userdata); + + static THREAD_RETURNTYPE THREAD_CALLCONV callback(void *userdata); + + protected: + int mPeriod; + virtual FMOD_RESULT threadFunc(); /* Override this instead of callback if you like */ + + public: + + typedef enum + { + PRIORITY_VERYLOW = -2, + PRIORITY_LOW = -1, + PRIORITY_NORMAL = 0, + PRIORITY_HIGH = 1, + PRIORITY_VERYHIGH = 2, + PRIORITY_CRITICAL = 3 + } PRIORITY; + + Thread(); + virtual ~Thread() { } + + FMOD_RESULT initThread(const char *name, void (*func)(void *userdata), void *userdata, PRIORITY priority, void *stack, int stacksize, bool usesemaphore, int sleepperiod, SystemI *system); + FMOD_RESULT closeThread(); + FMOD_RESULT getCurrentThreadID(FMOD_UINT_NATIVE *id); + FMOD_RESULT wakeupThread(bool frominterrupt = false); + FMOD_RESULT setPeriod(int period) { mPeriod = period; return FMOD_OK; } + }; +} + +#endif + + diff --git a/src/fmod_time.cpp b/src/fmod_time.cpp new file mode 100755 index 0000000..08b3347 --- /dev/null +++ b/src/fmod_time.cpp @@ -0,0 +1,198 @@ +#include "fmod_settings.h" + +#include "fmod_time.h" +#include "fmod_os_misc.h" + + +namespace FMOD +{ + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +TimeStamp::TimeStamp() +{ + mIn = 0; + mOut = 0; + mTotalIn = 0; + mTotalOut = 0; + mPaused = false; + mPausedIn = 0; + mPausedOut = 0; + mPausedTotal = 0; + mPausedRefCount = 0; + mTiming = false; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT TimeStamp::stampIn() +{ + FMOD_OS_Time_GetNs(&mIn); + + mTiming = true; + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT TimeStamp::stampOut(int damppercentage) +{ + unsigned int val; + FMOD_UFLOAT total,smoothedtotal; + FMOD_UFLOAT dampratio = FMOD_SCALEUP(damppercentage) / 100; + + FMOD_OS_Time_GetNs(&val); + + mOut = val; + mTotalOut = val; + + if (mTotalOut < mTotalIn) + { + total = 0; + } + else + { + total = (FMOD_UFLOAT)(mTotalOut - mTotalIn); + } + + /* + Smooth total + */ + mAvTotal = FMOD_SCALEDOWN(mAvTotal * dampratio); + mAvTotal += total; + smoothedtotal = FMOD_SCALEDOWN(mAvTotal * (FMOD_SCALEUP(1) - dampratio)); + + /* + Smooth cpu usage + */ + mPercent = FMOD_FMUL(mPercent, dampratio); + if (mOut > mIn) + { + unsigned int delta = mOut - mIn - mPausedTotal; + + mPercent += ((FMOD_SCALEUP(delta) * (FMOD_UFLOAT)100 / smoothedtotal) ); + } + + mCPUUsage = FMOD_FMUL(mPercent, (FMOD_SCALEUP(1) - dampratio)); + + mTotalIn = val; + + mPausedTotal = 0; + mPausedRefCount = 0;; + mTiming = false; + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Returns the current system time value in milliseconds. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Calls the nanosecond counter and divides by 1000. + May wrap regularly on certain platforms, only to be used on very short deltas. + + [SEE_ALSO] +*/ +FMOD_RESULT TimeStamp::getCPUUsage(FMOD_UFLOAT *cpuusage) +{ + if (!cpuusage) + { + return FMOD_ERR_INVALID_PARAM; + } + + *cpuusage = mCPUUsage; + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Returns the current system time value in milliseconds. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + Calls the nanosecond counter and divides by 1000. + May wrap regularly on certain platforms, only to be used on very short deltas. + + [SEE_ALSO] +*/ +FMOD_RESULT TimeStamp::setPaused(bool paused) +{ + if (!mTiming) + { + return FMOD_OK; + } + + if (paused) + { + if (!mPausedRefCount) + { + FMOD_OS_Time_GetNs(&mPausedIn); + } + mPausedRefCount++; + } + else if (!paused) + { + mPausedRefCount--; + if (mPausedRefCount < 0) + { + mPausedRefCount = mPausedRefCount; + } + + if (!mPausedRefCount) + { + FMOD_OS_Time_GetNs(&mPausedOut); + + if (mPausedOut > mPausedIn) + { + mPausedTotal += (mPausedOut - mPausedIn); + } + } + } + + mPaused = paused; + + return FMOD_OK; +} + +} diff --git a/src/fmod_time.h b/src/fmod_time.h new file mode 100755 index 0000000..4f16544 --- /dev/null +++ b/src/fmod_time.h @@ -0,0 +1,34 @@ +#ifndef _FMOD_TIME_H +#define _FMOD_TIME_H + +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_types.h" + +namespace FMOD +{ + class TimeStamp + { + public: + + unsigned int mIn, mOut; + unsigned int mPausedIn, mPausedOut; + unsigned int mTotalIn, mTotalOut; + FMOD_UFLOAT mPercent, mAvTotal, mAvCurrent, mCPUUsage; + bool mPaused; + unsigned int mPausedTotal; + int mPausedRefCount; + bool mTiming; + + TimeStamp(); + + FMOD_RESULT stampIn(); + FMOD_RESULT stampOut(int damppercentage); + FMOD_RESULT getCPUUsage(FMOD_UFLOAT *cpuusage); + FMOD_RESULT setPaused(bool paused); + }; +} + +#endif + diff --git a/src/fmod_types.h b/src/fmod_types.h new file mode 100755 index 0000000..87ff61d --- /dev/null +++ b/src/fmod_types.h @@ -0,0 +1,446 @@ +#ifndef _FMOD_TYPES_H +#define _FMOD_TYPES_H + +#include "fmod_settings.h" + +/* + Error-checking macro +*/ +#define CHECK_RESULT(_x) { FMOD_RESULT _result = (_x); if (_result != FMOD_OK) { return _result; } } + +/* + Structure packing +*/ + +#if defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + #define FMOD_PACKED __attribute__((packed)) /* gcc packed */ + #define FMOD_PACKED_INTERNAL __attribute__((packed)) /* gcc packed */ +#elif defined(PLATFORM_LINUX) || defined(PLATFORM_PS3) || defined(PLATFORM_SOLARIS) || (defined(__GNUC__) && defined(WIN32)) + #define FMOD_PACKED __attribute__((packed)) /* gcc packed */ + #define FMOD_PACKED_INTERNAL /* dummy */ +#else + #define FMOD_PACKED /* dummy */ + #define FMOD_PACKED_INTERNAL /* dummy */ +#endif + +#if defined(PLATFORM_PS3) + #define FMOD_ALIGN16(__var) __var __attribute__((aligned(16))) + #define FMOD_ALIGN128(__var) __var __attribute__((aligned(128))) + #define FMOD_PPCALIGN16(__var) __var __attribute__((aligned(16))) + #define FMOD_PPCALIGN128(__var) __var __attribute__((aligned(128))) + #define FMOD_RESTRICT +#elif (defined(PLATFORM_XENON) || defined(PLATFORM_WINDOWS) || defined(PLATFORM_XBOX)) + #define FMOD_ALIGN16(__var) __declspec(align(16)) __var + #define FMOD_ALIGN128(__var) __declspec(align(128)) __var + #define FMOD_PPCALIGN16(__var) __var + #define FMOD_PPCALIGN128(__var) __var + #define FMOD_RESTRICT __restrict +#elif (defined(PLATFORM_IPHONE) || defined(PLATFORM_MAC)) + #define FMOD_ALIGN16(__var) __var + #define FMOD_ALIGN128(__var) __var + #define FMOD_PPCALIGN16(__var) __var + #define FMOD_PPCALIGN128(__var) __var + #define FMOD_RESTRICT __restrict +#else + #define FMOD_ALIGN16(__var) __var + #define FMOD_ALIGN128(__var) __var + #define FMOD_PPCALIGN16(__var) __var + #define FMOD_PPCALIGN128(__var) __var + #define FMOD_RESTRICT +#endif + +#define FMOD_ALIGNPOINTER(_ptr, _align) ((((FMOD_UINT_NATIVE)(_ptr)) + ((_align) - 1)) & ~((_align) - 1)) +#define FMOD_MEMBER_EXISTS(__x, __y) (__x && ((int)(__x->size) >= (((char *)(&__x->__y) - (char *)__x) + sizeof(__x->__y)))) + +/* + Custom platform specific types. +*/ +#if defined(PLATFORM_WINDOWS) && !defined(__MINGW32__) || defined(PLATFORM_CE) || defined(PLATFORM_XBOX) || defined(PLATFORM_XENON) + + typedef signed __int64 FMOD_SINT64; + typedef unsigned __int64 FMOD_UINT64; + + #define FMOD_INLINE __forceinline + +#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MAC) || defined(PLATFORM_IPHONE) || defined(PLATFORM_SOLARIS) || defined(__MINGW32__) + + typedef signed long long FMOD_SINT64; + typedef unsigned long long FMOD_UINT64; + + #define FMOD_INLINE inline + +#elif defined(PLATFORM_PS2) + + #ifdef R5900 + typedef signed long FMOD_SINT64; + typedef unsigned long FMOD_UINT64; + #elif defined (R3000) + typedef signed long long FMOD_SINT64; + typedef unsigned long long FMOD_UINT64; + #endif + + #define FMOD_INLINE __inline + +#elif defined(PLATFORM_GC) || defined(PLATFORM_WII) + + typedef signed long long FMOD_SINT64; + typedef unsigned long long FMOD_UINT64; + + #define FMOD_INLINE inline + +#elif defined(PLATFORM_PSP) + + typedef signed long long FMOD_SINT64; + typedef unsigned long long FMOD_UINT64; + + #define FMOD_INLINE __inline + + #ifdef __SNC__ + typedef int SNQuad __attribute__((mode(VF), aligned(16))) ; + // typedef int SNScalar __attribute__((mode(VS))) ; + #endif + +#elif defined(PLATFORM_PS3) + + typedef signed long long FMOD_SINT64; + typedef unsigned long long FMOD_UINT64; + + #define FMOD_INLINE inline + +#else + + #error define a 64bit int and inline type + +#endif + +#ifdef PLATFORM_64BIT + typedef FMOD_UINT64 FMOD_UINT_NATIVE; + typedef FMOD_SINT64 FMOD_SINT_NATIVE; +#else + typedef unsigned int FMOD_UINT_NATIVE; + typedef signed int FMOD_SINT_NATIVE; +#endif + +typedef unsigned int FMOD_UFIXED; +typedef signed int FMOD_SFIXED; + +#ifdef FMOD_NO_FPU + + #define FMOD_FRAC 8 + #define FMOD_SCALE (1<<FMOD_FRAC) + + typedef FMOD_SFIXED FMOD_FLOAT; + typedef FMOD_UFIXED FMOD_UFLOAT; + typedef FMOD_SINT64 FMOD_FLOAT64; + typedef FMOD_UINT64 FMOD_UFLOAT64; + #define FMOD_SCALEUP(_x) ((FMOD_FLOAT) (_x) << FMOD_FRAC) + #define FMOD_SCALEUP64(_x) ((FMOD_FLOAT64)(_x) << FMOD_FRAC) + #define FMOD_SCALEDOWN(_x) ((FMOD_FLOAT) (_x) >> FMOD_FRAC) + #define FMOD_SCALEDOWN64(_x) ((FMOD_FLOAT64)(_x) >> FMOD_FRAC) + #define FMOD_NEARLYZERO 1 + + #define FMOD_FMUL(_a, _b) (((_a) * (_b)) >> FMOD_FRAC) + +#else + + typedef float FMOD_FLOAT; + typedef float FMOD_UFLOAT; + typedef float FMOD_FLOAT64; + typedef float FMOD_UFLOAT64; + #define FMOD_SCALEUP(_x) (FMOD_FLOAT)(_x) + #define FMOD_SCALEUP64(_x) (FMOD_FLOAT64)(_x) + #define FMOD_SCALEDOWN(_x) (FMOD_FLOAT)(_x) + #define FMOD_SCALEDOWN64(_x) (FMOD_FLOAT64)(_x) + #define FMOD_NEARLYZERO 0.00001f + + #define FMOD_FMUL(_a, _b) ((_a) * (_b)) + +#endif + +#define FMOD_SWAPENDIAN_WORD(_x) ((((unsigned short)(_x) & 0xFF00) >> 8) | \ + (((unsigned short)(_x) & 0x00FF) << 8)) + +#define FMOD_SWAPENDIAN_DWORD(_x) ((((unsigned int)(_x) & 0x000000FF) << 24) | \ + (((unsigned int)(_x) & 0x0000FF00) << 8) | \ + (((unsigned int)(_x) & 0x00FF0000) >> 8) | \ + (((unsigned int)(_x) & 0xFF000000) >> 24)) + +#define FMOD_SWAPENDIAN_QWORD(_x) ((((FMOD_UINT64)(_x) & 0x00000000000000FFULL) << 56) | \ + (((FMOD_UINT64)(_x) & 0x000000000000FF00ULL) << 40) | \ + (((FMOD_UINT64)(_x) & 0x0000000000FF0000ULL) << 24) | \ + (((FMOD_UINT64)(_x) & 0x00000000FF000000ULL) << 8) | \ + (((FMOD_UINT64)(_x) & 0x000000FF00000000ULL) >> 8) | \ + (((FMOD_UINT64)(_x) & 0x0000FF0000000000ULL) >> 24) | \ + (((FMOD_UINT64)(_x) & 0x00FF000000000000ULL) >> 40) | \ + (((FMOD_UINT64)(_x) & 0xFF00000000000000ULL) >> 56)) + +#define FMOD_SWAPENDIAN_FLOAT(_x) { unsigned int *__tmp = (unsigned int *)&(_x); *__tmp = FMOD_SWAPENDIAN_DWORD(*__tmp); } + +#ifdef PLATFORM_PS2_IOP + +/* + Doesn't allow anonymous unions. Just fool it for now to make headers compile, because IOP doesnt use this stuff (software mixer etc). +*/ +typedef struct +{ + unsigned int mHi; + unsigned int mLo; +} FMOD_UINT64P; + +typedef struct +{ + signed int mHi; + unsigned int mLo; +} FMOD_SINT64P; + +#else + +typedef union +{ + FMOD_UINT64 mValue; + + #ifdef PLATFORM_ENDIAN_LITTLE + struct + { + unsigned int mLo; + unsigned int mHi; + }; + #else + struct + { + unsigned int mHi; + unsigned int mLo; + }; + #endif +} FMOD_UINT64P; + +typedef union +{ + FMOD_SINT64 mValue; + #ifdef PLATFORM_ENDIAN_LITTLE + struct + { + unsigned int mLo; + signed int mHi; + }; + #else + struct + { + signed int mHi; + unsigned int mLo; + }; + #endif +} FMOD_SINT64P; + +#endif + +typedef struct +{ + float re; + float im; +} FMOD_COMPLEX; + +#ifdef FMOD_SUPPORT_PRAGMAPACK +#pragma pack(1) +#endif + +typedef struct +{ + unsigned char val[3]; +} FMOD_PACKED FMOD_INT24; + +#ifdef FMOD_SUPPORT_PRAGMAPACK +#pragma pack() +#endif + +#define FMOD_ABS(_x) ((_x) < 0 ? -(_x) : (_x)) +#define FMOD_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define FMOD_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) + +#define FMOD_PI (3.14159265358979323846f) +#define FMOD_PI2 (3.14159265358979323846f * 2.0f) +#define FMOD_PI_2 (3.14159265358979323846f / 2.0f) +#define FMOD_SQRT2 1.41421356237309504880f +#define FMOD_LOG2 0.693147180559945309417f + +#define FMOD_ENCRYPT(x, y) { unsigned char __tmp = (x) ^ (y); \ + (x) = 0; \ + (x) |= (__tmp & 0x01) << 7; \ + (x) |= (__tmp & 0x02) << 5; \ + (x) |= (__tmp & 0x04) << 3; \ + (x) |= (__tmp & 0x08) << 1; \ + (x) |= (__tmp & 0x10) >> 1; \ + (x) |= (__tmp & 0x20) >> 3; \ + (x) |= (__tmp & 0x40) >> 5; \ + (x) |= (__tmp & 0x80) >> 7; } + +#define FMOD_DECRYPT(x, y) { unsigned char __tmp = (x); \ + (x) = 0; \ + (x) |= (__tmp & 0x01) << 7; \ + (x) |= (__tmp & 0x02) << 5; \ + (x) |= (__tmp & 0x04) << 3; \ + (x) |= (__tmp & 0x08) << 1; \ + (x) |= (__tmp & 0x10) >> 1; \ + (x) |= (__tmp & 0x20) >> 3; \ + (x) |= (__tmp & 0x40) >> 5; \ + (x) |= (__tmp & 0x80) >> 7; \ + (x) ^= (y); } + +/* Simply reverses the bits the xor's it with the input. */ +#define FMOD_ENCRYPT_OLD(x, y) { unsigned char __tmp = (x); \ + (x) = 0; \ + (x) |= (__tmp & 0x01) << 7; \ + (x) |= (__tmp & 0x02) << 5; \ + (x) |= (__tmp & 0x04) << 3; \ + (x) |= (__tmp & 0x08) << 1; \ + (x) |= (__tmp & 0x10) >> 1; \ + (x) |= (__tmp & 0x20) >> 3; \ + (x) |= (__tmp & 0x40) >> 5; \ + (x) |= (__tmp & 0x80) >> 7; \ + (x) ^= (y); } + + +#define FMOD_DECRYPT_OLD(x, y) { unsigned char __tmp = (x) ^ (y); \ + (x) = 0; \ + (x) |= (__tmp & 0x01) << 7; \ + (x) |= (__tmp & 0x02) << 5; \ + (x) |= (__tmp & 0x04) << 3; \ + (x) |= (__tmp & 0x08) << 1; \ + (x) |= (__tmp & 0x10) >> 1; \ + (x) |= (__tmp & 0x20) >> 3; \ + (x) |= (__tmp & 0x40) >> 5; \ + (x) |= (__tmp & 0x80) >> 7; } + +#if defined(PLATFORM_PS3) || defined(PLATFORM_PS2) || defined(PLATFORM_PSP) + + #if defined(PLATFORM_PS3) || defined(PLATFORM_PS2_EE) || defined (PLATFORM_PSP) + #include <math.h> + #endif + + #ifdef PLATFORM_PSP + + #include <libvfpu.h> + + #define FMOD_SIN sceVfpuScalarSin + #define FMOD_COS(_s_) FMOD_SIN(_s_ + FMOD_PI_2) + #define FMOD_SQRT sceVfpuScalarSqrt + #else + #define FMOD_SIN sinf + #define FMOD_COS cosf + #define FMOD_SQRT sqrtf + #endif + + #define FMOD_SINH sinhf + #define FMOD_TAN tanf + #define FMOD_POW powf + #define FMOD_ACOS acosf + #define FMOD_ATAN atanf + #define FMOD_EXP expf + #define FMOD_LDEXP ldexpf + #define FMOD_FABS fabsf + #define FMOD_LOG logf + #define FMOD_LOG10 log10f + #define FMOD_EXP2(_val) powf(2.0f, _val) + #define FMOD_FMOD fmodf + #define FMOD_FLOOR floorf + +#else + + #include <math.h> + + #define FMOD_SIN (float)sin + #define FMOD_SINH (float)sinh + #define FMOD_COS (float)cos + #define FMOD_TAN (float)tan + #define FMOD_SQRT (float)sqrt + #define FMOD_POW (float)pow + #define FMOD_ACOS (float)acos + #define FMOD_ATAN (float)atan + #define FMOD_EXP (float)exp + #define FMOD_LDEXP (float)ldexp + #define FMOD_FABS fabsf + #define FMOD_LOG (float)log + #define FMOD_LOG10 (float)log10 + #define FMOD_FMOD (float)fmod + #define FMOD_FLOOR (float)floor + + #ifdef PLATFORM_XENON + #include <xtl.h> + FMOD_INLINE float FMOD_EXP2(float val) + { + __vector4 v_r, v_a = { val, 1.0f, 1.0f, 1.0f }; + v_r = __vexptefp(v_a); + return v_r.x; + } + #else + #define FMOD_EXP2(_val) FMOD_POW(2.0f, _val) + #endif +#endif + +#define FMOD_SRAND(_seed) ( FMOD::gGlobal->gRandomValue = _seed ) /* From MSVC CRT code. */ +#define FMOD_RAND() ( ((FMOD::gGlobal->gRandomValue = gGlobal->gRandomValue * 214013L + 2531011L) >> 16) & 0x7fff ) /* From MSVC CRT code. */ + +#define FMOD_FUZZYEQ(_a, _b) (FMOD_FABS((_a) - (_b)) < 1e-5) + +#if defined(PLATFORM_GC) || defined(PLATFORM_WII) + + #ifdef __MWERKS__ + #include <alloca.h> + #else + #include <malloc.h> + #endif + + #define FMOD_alloca(x) alloca(x) + +#elif defined(PLATFORM_PS2) + + #ifdef PLATFORM_PS2_EE + #include <malloc.h> + + #if defined(__MWERKS__) + #define FMOD_alloca __alloca + #else + #define FMOD_alloca alloca + #endif + #endif + +#elif defined(PLATFORM_PS3) || (defined(PLATFORM_PSP) && defined(__psp_gcc__)) || defined(PLATFORM_LINUX) || defined(PLATFORM_MAC) || defined(PLATFORM_IPHONE) || defined(PLATFORM_SOLARIS) + + #include <alloca.h> + #define FMOD_alloca(x) alloca(x) + +#else + + #include <malloc.h> + #define FMOD_alloca alloca + +#endif + +#if defined(PLATFORM_PSP) +#define FMOD_memset sceVfpuMemset +#define FMOD_memcpy sceVfpuMemcpy +#elif defined(PLATFORM_XENON) +#define FMOD_memset XMemSet +#define FMOD_memcpy XMemCpy +#else +#define FMOD_memset memset +#define FMOD_memcpy memcpy +#endif + + +#if defined(FMOD_SUPPORT_RTTI) + + #define SAFE_CAST(_type, _obj) dynamic_cast<_type *>(_obj) + +#else + + #define SAFE_CAST(_type, _obj) static_cast<_type *>(_obj) + +#endif + +#if defined(PLATFORM_WINDOWS) && defined(_MSC_VER) && (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + + +#endif diff --git a/win32/bin/nasmw.exe b/win32/bin/nasmw.exe new file mode 100755 index 0000000..a7e9bf9 Binary files /dev/null and b/win32/bin/nasmw.exe differ diff --git a/win32/lib/wmsdk/include/asferr.h b/win32/lib/wmsdk/include/asferr.h new file mode 100755 index 0000000..b6da992 --- /dev/null +++ b/win32/lib/wmsdk/include/asferr.h @@ -0,0 +1,480 @@ +/////////////////////////////////////////////////////////////////////////// +// +// ASFErr.h - definition of ASF HRESULT codes +// +//========================================================================= +// +// Microsoft Windows Media Technologies +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//========================================================================= +// +// This file is generated by the MC tool from ASFErr.mc +// + +#ifndef _ASFERR_H +#define _ASFERR_H + + +#define STATUS_SEVERITY(hr) (((hr) >> 30) & 0x3) + +#ifdef RC_INVOKED +#define _ASF_HRESULT_TYPEDEF_(_sc) _sc +#else // RC_INVOKED +#define _ASF_HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) +#endif // RC_INVOKED + + +/////////////////////////////////////////////////////////////////////////// +// +// Advanced Streaming Format (ASF) Errors (2000 - 2999) +// +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// +#define FACILITY_NS 0xD + + +// +// Define the severity codes +// +#define STATUS_SEVERITY_WARNING 0x2 +#define STATUS_SEVERITY_SUCCESS 0x0 +#define STATUS_SEVERITY_INFORMATIONAL 0x1 +#define STATUS_SEVERITY_ERROR 0x3 + + +// +// MessageId: ASF_E_BUFFEROVERRUN +// +// MessageText: +// +// An attempt was made to seek or position past the end of a buffer.%0 +// +#define ASF_E_BUFFEROVERRUN _ASF_HRESULT_TYPEDEF_(0xC00D07D0L) + +// +// MessageId: ASF_E_BUFFERTOOSMALL +// +// MessageText: +// +// The supplied input or output buffer was too small.%0 +// +#define ASF_E_BUFFERTOOSMALL _ASF_HRESULT_TYPEDEF_(0xC00D07D1L) + +// +// MessageId: ASF_E_BADLANGUAGEID +// +// MessageText: +// +// The language ID was not found.%0 +// +#define ASF_E_BADLANGUAGEID _ASF_HRESULT_TYPEDEF_(0xC00D07D2L) + +// +// MessageId: ASF_E_NOPAYLOADLENGTH +// +// MessageText: +// +// The multiple payload packet is missing the payload length.%0 +// +#define ASF_E_NOPAYLOADLENGTH _ASF_HRESULT_TYPEDEF_(0xC00D07DBL) + +// +// MessageId: ASF_E_TOOMANYPAYLOADS +// +// MessageText: +// +// The packet contains too many payloads.%0 +// +#define ASF_E_TOOMANYPAYLOADS _ASF_HRESULT_TYPEDEF_(0xC00D07DCL) + +// +// MessageId: ASF_E_PACKETCONTENTTOOLARGE +// +// MessageText: +// +// ASF_E_PACKETCONTENTTOOLARGE +// +#define ASF_E_PACKETCONTENTTOOLARGE _ASF_HRESULT_TYPEDEF_(0xC00D07DEL) + +// +// MessageId: ASF_E_UNKNOWNPACKETSIZE +// +// MessageText: +// +// Expecting a fixed packet size but min. and max. are not equal.%0 +// +#define ASF_E_UNKNOWNPACKETSIZE _ASF_HRESULT_TYPEDEF_(0xC00D07E0L) + +// +// MessageId: ASF_E_INVALIDHEADER +// +// MessageText: +// +// ASF_E_INVALIDHEADER +// +#define ASF_E_INVALIDHEADER _ASF_HRESULT_TYPEDEF_(0xC00D07E2L) + +// +// MessageId: ASF_E_NOCLOCKOBJECT +// +// MessageText: +// +// The object does not have a valid clock object.%0 +// +#define ASF_E_NOCLOCKOBJECT _ASF_HRESULT_TYPEDEF_(0xC00D07E6L) + +// +// MessageId: ASF_E_UNKNOWNCLOCKTYPE +// +// MessageText: +// +// ASF_E_UNKNOWNCLOCKTYPE +// +#define ASF_E_UNKNOWNCLOCKTYPE _ASF_HRESULT_TYPEDEF_(0xC00D07EBL) + +// +// MessageId: ASF_E_OPAQUEPACKET +// +// MessageText: +// +// An attempt was made to restore or access an opaque packet.%0 +// +#define ASF_E_OPAQUEPACKET _ASF_HRESULT_TYPEDEF_(0xC00D07EDL) + +// +// MessageId: ASF_E_WRONGVERSION +// +// MessageText: +// +// ASF_E_WRONGVERSION +// +#define ASF_E_WRONGVERSION _ASF_HRESULT_TYPEDEF_(0xC00D07EEL) + +// +// MessageId: ASF_E_OVERFLOW +// +// MessageText: +// +// An attempt was made to store a value which was larger than then destination's maximum value.%0 +// +#define ASF_E_OVERFLOW _ASF_HRESULT_TYPEDEF_(0xC00D07EFL) + +// +// MessageId: ASF_E_NOTFOUND +// +// MessageText: +// +// The object was not found.%0 +// +#define ASF_E_NOTFOUND _ASF_HRESULT_TYPEDEF_(0xC00D07F0L) + +// +// Someone else is using MessageIds 2033 & 2034, so we skip them +// +// 2033 = NS_E_NOTHING_TO_DO +// 2034 = NS_E_NO_MULTICAST + +// +// MessageId: ASF_E_OBJECTTOOBIG +// +// MessageText: +// +// The object is too large to be processed in the requested manner.%0 +// +#define ASF_E_OBJECTTOOBIG _ASF_HRESULT_TYPEDEF_(0xC00D07F3L) + +// +// MessageId: ASF_E_UNEXPECTEDVALUE +// +// MessageText: +// +// A value was not set as expected.%0 +// +#define ASF_E_UNEXPECTEDVALUE _ASF_HRESULT_TYPEDEF_(0xC00D07F4L) + +// +// MessageId: ASF_E_INVALIDSTATE +// +// MessageText: +// +// The request is not valid in the object's current state.%0 +// +#define ASF_E_INVALIDSTATE _ASF_HRESULT_TYPEDEF_(0xC00D07F5L) + +// +// MessageId: ASF_E_NOLIBRARY +// +// MessageText: +// +// This object does not have a valid library pointer; it was not properly created or it has been Shutdown().%0 +// +#define ASF_E_NOLIBRARY _ASF_HRESULT_TYPEDEF_(0xC00D07F6L) + +// +// MessageId: ASF_E_ALREADYINITIALIZED +// +// MessageText: +// +// This object has already been initialized; the setting cannot be changed.%0 +// +#define ASF_E_ALREADYINITIALIZED _ASF_HRESULT_TYPEDEF_(0xC00D07F7L) + +// +// MessageId: ASF_E_INVALIDINIT +// +// MessageText: +// +// This object has not been initialized properly; that operation cannot be performed.%0 +// +#define ASF_E_INVALIDINIT _ASF_HRESULT_TYPEDEF_(0xC00D07F8L) + +// +// MessageId: ASF_E_NOHEADEROBJECT +// +// MessageText: +// +// The ASF Header object could not be found.%0 +// +#define ASF_E_NOHEADEROBJECT _ASF_HRESULT_TYPEDEF_(0xC00D07F9L) + +// +// MessageId: ASF_E_NODATAOBJECT +// +// MessageText: +// +// The ASF Data object could not be found.%0 +// +#define ASF_E_NODATAOBJECT _ASF_HRESULT_TYPEDEF_(0xC00D07FAL) + +// +// MessageId: ASF_E_NOINDEXOBJECT +// +// MessageText: +// +// The ASF Index object could not be found.%0 +// +#define ASF_E_NOINDEXOBJECT _ASF_HRESULT_TYPEDEF_(0xC00D07FBL) + +// +// MessageId: ASF_E_NOSTREAMPROPS +// +// MessageText: +// +// A Stream Properties object with the correct stream number could not be found.%0 +// +#define ASF_E_NOSTREAMPROPS _ASF_HRESULT_TYPEDEF_(0xC00D07FCL) + +// +// MessageId: ASF_E_NOFILEPROPS +// +// MessageText: +// +// The File Properties object could not be found.%0 +// +#define ASF_E_NOFILEPROPS _ASF_HRESULT_TYPEDEF_(0xC00D07FDL) + +// +// MessageId: ASF_E_NOLANGUAGELIST +// +// MessageText: +// +// The Language List object could not be found.%0 +// +#define ASF_E_NOLANGUAGELIST _ASF_HRESULT_TYPEDEF_(0xC00D07FEL) + +// +// MessageId: ASF_E_NOINDEXPARAMETERS +// +// MessageText: +// +// The Index Parameters object could not be found.%0 +// +#define ASF_E_NOINDEXPARAMETERS _ASF_HRESULT_TYPEDEF_(0xC00D07FFL) + +// +// MessageId: ASF_E_UNSUPPORTEDERRORCONCEALMENT +// +// MessageText: +// +// The requested error concealment strategy is not supported by this component.%0 +// +#define ASF_E_UNSUPPORTEDERRORCONCEALMENT _ASF_HRESULT_TYPEDEF_(0xC00D0800L) + +// +// MessageId: ASF_E_INVALIDFLAGS +// +// MessageText: +// +// The flags for this object or set of objects are not properly set.%0 +// +#define ASF_E_INVALIDFLAGS _ASF_HRESULT_TYPEDEF_(0xC00D0801L) + +// +// MessageId: ASF_E_BADDATADESCRIPTOR +// +// MessageText: +// +// One or more data descriptors is not properly set.%0 +// +#define ASF_E_BADDATADESCRIPTOR _ASF_HRESULT_TYPEDEF_(0xC00D0802L) + +// +// MessageId: ASF_E_BADINDEXINTERVAL +// +// MessageText: +// +// The index has an invalid time interval (probably zero).%0 +// +#define ASF_E_BADINDEXINTERVAL _ASF_HRESULT_TYPEDEF_(0xC00D0803L) + +// +// MessageId: ASF_E_INVALIDTIME +// +// MessageText: +// +// The given time value is not valid.%0 +// +#define ASF_E_INVALIDTIME _ASF_HRESULT_TYPEDEF_(0xC00D0804L) + +// +// MessageId: ASF_E_INVALIDINDEX +// +// MessageText: +// +// The given index value is not valid.%0 +// +#define ASF_E_INVALIDINDEX _ASF_HRESULT_TYPEDEF_(0xC00D0805L) + +// +// MessageId: ASF_E_STREAMNUMBERINUSE +// +// MessageText: +// +// The specified stream number is already in use.%0 +// +#define ASF_E_STREAMNUMBERINUSE _ASF_HRESULT_TYPEDEF_(0xC00D0806L) + +// +// MessageId: ASF_E_BADMEDIATYPE +// +// MessageText: +// +// The specified media type does not work with this component.%0 +// +#define ASF_E_BADMEDIATYPE _ASF_HRESULT_TYPEDEF_(0xC00D0807L) + +// +// MessageId: ASF_E_WRITEFAILED +// +// MessageText: +// +// The object could not be written as specified.%0 +// +#define ASF_E_WRITEFAILED _ASF_HRESULT_TYPEDEF_(0xC00D0808L) + +// +// MessageId: ASF_E_NOTENOUGHDESCRIPTORS +// +// MessageText: +// +// The given data unit requires a larger number of descriptors to be fully parsed.%0 +// +#define ASF_E_NOTENOUGHDESCRIPTORS _ASF_HRESULT_TYPEDEF_(0xC00D0809L) + +// +// MessageId: ASF_E_INDEXBLOCKUNLOADED +// +// MessageText: +// +// The index entries for the specified index block have been unloaded from memory and are not available.%0 +// +#define ASF_E_INDEXBLOCKUNLOADED _ASF_HRESULT_TYPEDEF_(0xC00D080AL) + +// +// MessageId: ASF_E_NOTENOUGHBANDWIDTH +// +// MessageText: +// +// The specified bandwidth is not large enough.%0 +// +#define ASF_E_NOTENOUGHBANDWIDTH _ASF_HRESULT_TYPEDEF_(0xC00D080BL) + +// +// MessageId: ASF_E_EXCEEDEDMAXIMUMOBJECTSIZE +// +// MessageText: +// +// The object has exceeded its maximum size.%0 +// +#define ASF_E_EXCEEDEDMAXIMUMOBJECTSIZE _ASF_HRESULT_TYPEDEF_(0xC00D080CL) + +// +// MessageId: ASF_E_BADDATAUNIT +// +// MessageText: +// +// The given data unit is corrupted, badly formatted, or otherwise not valid.%0 +// +#define ASF_E_BADDATAUNIT _ASF_HRESULT_TYPEDEF_(0xC00D080DL) + +// +// MessageId: ASF_E_HEADERSIZE +// +// MessageText: +// +// The ASF header has exceeded the specified maximum size.%0 +// +#define ASF_E_HEADERSIZE _ASF_HRESULT_TYPEDEF_(0xC00D080EL) + + +/////////////////////////////////////////////////////////////////////////// +// +// Advanced Streaming Format (ASF) Success Codes (2000 - 2999) +// + +// +// MessageId: ASF_S_OPAQUEPACKET +// +// MessageText: +// +// ASF_S_OPAQUEPACKET +// +#define ASF_S_OPAQUEPACKET _ASF_HRESULT_TYPEDEF_(0x000D07F0L) + + +/////////////////////////////////////////////////////////////////////////// +// +// Advanced Streaming Format (ASF) Warnings (2000 - 2999) +// + + +#endif // _ASFERR_H + diff --git a/win32/lib/wmsdk/include/drmexternals.h b/win32/lib/wmsdk/include/drmexternals.h new file mode 100755 index 0000000..d94706a --- /dev/null +++ b/win32/lib/wmsdk/include/drmexternals.h @@ -0,0 +1,170 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 6.00.0361 */ +/* Compiler settings for drmexternals.idl: + Oicf, W1, Zp8, env=Win32 (32b run) + protocol : dce , ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + + +/* verify that the <rpcndr.h> version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 475 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of <rpcndr.h> +#endif // __RPCNDR_H_VERSION__ + + +#ifndef __drmexternals_h__ +#define __drmexternals_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +/* Forward Declarations */ + +/* header files for imported files */ +#include "oaidl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +void * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void * ); + +/* interface __MIDL_itf_drmexternals_0000 */ +/* [local] */ + +//========================================================================= +// +// Microsoft Windows Media Technologies +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//========================================================================= +static const WCHAR *g_wszWMDRM_RIGHT_PLAYBACK = L"Play"; +static const WCHAR *g_wszWMDRM_RIGHT_COPY_TO_CD = L"Print.redbook"; +static const WCHAR *g_wszWMDRM_RIGHT_COPY_TO_SDMI_DEVICE = L"Transfer.SDMI"; +static const WCHAR *g_wszWMDRM_RIGHT_COPY_TO_NON_SDMI_DEVICE = L"Transfer.NONSDMI"; +static const WCHAR *g_wszWMDRM_RIGHT_BACKUP = L"Backup"; +static const WCHAR *g_wszWMDRM_IsDRM = L"IsDRM"; +static const WCHAR *g_wszWMDRM_IsDRMCached = L"IsDRMCached"; +static const WCHAR *g_wszWMDRM_BaseLicenseAcqURL = L"BaseLAURL"; +static const WCHAR *g_wszWMDRM_Rights = L"Rights"; +static const WCHAR *g_wszWMDRM_LicenseID = L"LID"; +static const WCHAR *g_wszWMDRM_ActionAllowed = L"ActionAllowed."; +static const WCHAR *g_wszWMDRM_ActionAllowed_Playback = L"ActionAllowed.Play"; +static const WCHAR *g_wszWMDRM_ActionAllowed_CopyToCD = L"ActionAllowed.Print.redbook"; +static const WCHAR *g_wszWMDRM_ActionAllowed_CopyToSDMIDevice = L"ActionAllowed.Transfer.SDMI"; +static const WCHAR *g_wszWMDRM_ActionAllowed_CopyToNonSDMIDevice = L"ActionAllowed.Transfer.NONSDMI"; +static const WCHAR *g_wszWMDRM_ActionAllowed_Backup = L"ActionAllowed.Backup"; +static const WCHAR *g_wszWMDRM_LicenseState = L"LicenseStateData."; +static const WCHAR *g_wszWMDRM_LicenseState_Playback = L"LicenseStateData.Play"; +static const WCHAR *g_wszWMDRM_LicenseState_CopyToCD = L"LicenseStateData.Print.redbook"; +static const WCHAR *g_wszWMDRM_LicenseState_CopyToSDMIDevice = L"LicenseStateData.Transfer.SDMI"; +static const WCHAR *g_wszWMDRM_LicenseState_CopyToNonSDMIDevice = L"LicenseStateData.Transfer.NONSDMI"; +static const WCHAR *g_wszWMDRM_DRMHeader = L"DRMHeader."; +static const WCHAR *g_wszWMDRM_DRMHeader_KeyID = L"DRMHeader.KID"; +static const WCHAR *g_wszWMDRM_DRMHeader_LicenseAcqURL = L"DRMHeader.LAINFO"; +static const WCHAR *g_wszWMDRM_DRMHeader_ContentID = L"DRMHeader.CID"; +static const WCHAR *g_wszWMDRM_DRMHeader_IndividualizedVersion = L"DRMHeader.SECURITYVERSION"; +static const WCHAR *g_wszWMDRM_DRMHeader_ContentDistributor = L"DRMHeader.ContentDistributor"; +static const WCHAR *g_wszWMDRM_DRMHeader_SubscriptionContentID = L"DRMHeader.SubscriptionContentID"; +typedef +enum DRM_LICENSE_STATE_CATEGORY + { WM_DRM_LICENSE_STATE_NORIGHT = 0, + WM_DRM_LICENSE_STATE_UNLIM = WM_DRM_LICENSE_STATE_NORIGHT + 1, + WM_DRM_LICENSE_STATE_COUNT = WM_DRM_LICENSE_STATE_UNLIM + 1, + WM_DRM_LICENSE_STATE_FROM = WM_DRM_LICENSE_STATE_COUNT + 1, + WM_DRM_LICENSE_STATE_UNTIL = WM_DRM_LICENSE_STATE_FROM + 1, + WM_DRM_LICENSE_STATE_FROM_UNTIL = WM_DRM_LICENSE_STATE_UNTIL + 1, + WM_DRM_LICENSE_STATE_COUNT_FROM = WM_DRM_LICENSE_STATE_FROM_UNTIL + 1, + WM_DRM_LICENSE_STATE_COUNT_UNTIL = WM_DRM_LICENSE_STATE_COUNT_FROM + 1, + WM_DRM_LICENSE_STATE_COUNT_FROM_UNTIL = WM_DRM_LICENSE_STATE_COUNT_UNTIL + 1, + WM_DRM_LICENSE_STATE_EXPIRATION_AFTER_FIRSTUSE = WM_DRM_LICENSE_STATE_COUNT_FROM_UNTIL + 1 + } DRM_LICENSE_STATE_CATEGORY; + +typedef struct _DRM_LICENSE_STATE_DATA + { + DWORD dwStreamId; + DRM_LICENSE_STATE_CATEGORY dwCategory; + DWORD dwNumCounts; + DWORD dwCount[ 4 ]; + DWORD dwNumDates; + FILETIME datetime[ 4 ]; + DWORD dwVague; + } DRM_LICENSE_STATE_DATA; + +typedef +enum DRM_HTTP_STATUS + { HTTP_NOTINITIATED = 0, + HTTP_CONNECTING = HTTP_NOTINITIATED + 1, + HTTP_REQUESTING = HTTP_CONNECTING + 1, + HTTP_RECEIVING = HTTP_REQUESTING + 1, + HTTP_COMPLETED = HTTP_RECEIVING + 1 + } DRM_HTTP_STATUS; + +typedef +enum DRM_INDIVIDUALIZATION_STATUS + { INDI_UNDEFINED = 0, + INDI_BEGIN = 0x1, + INDI_SUCCEED = 0x2, + INDI_FAIL = 0x4, + INDI_CANCEL = 0x8, + INDI_DOWNLOAD = 0x10, + INDI_INSTALL = 0x20 + } DRM_INDIVIDUALIZATION_STATUS; + +typedef struct _WMIndividualizeStatus + { + HRESULT hr; + DRM_INDIVIDUALIZATION_STATUS enIndiStatus; + LPSTR pszIndiRespUrl; + DWORD dwHTTPRequest; + DRM_HTTP_STATUS enHTTPStatus; + DWORD dwHTTPReadProgress; + DWORD dwHTTPReadTotal; + } WM_INDIVIDUALIZE_STATUS; + +typedef struct _WMGetLicenseData + { + DWORD dwSize; + HRESULT hr; + WCHAR *wszURL; + WCHAR *wszLocalFilename; + BYTE *pbPostData; + DWORD dwPostDataSize; + } WM_GET_LICENSE_DATA; + + + +extern RPC_IF_HANDLE __MIDL_itf_drmexternals_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_drmexternals_0000_v0_0_s_ifspec; + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/win32/lib/wmsdk/include/nserror.h b/win32/lib/wmsdk/include/nserror.h new file mode 100755 index 0000000..e1fc014 --- /dev/null +++ b/win32/lib/wmsdk/include/nserror.h @@ -0,0 +1,9219 @@ +/*++ + + Microsoft Windows Media Technology + Copyright (C) Microsoft Corporation. All Rights Reserved. + +Module Name: + + nserror.mc + +Abstract: + + Definitions for Windows Media events. + +Author: + + +Revision History: + +Notes: + + This file is used by the MC tool to generate the nserror.h file + +**************************** READ ME ****************************************** + + Here are the commented error ranges for the Windows Media Technologies Group + + + LEGACY RANGES + + 0 - 199 = General NetShow errors + + 200 - 399 = NetShow error events + + 400 - 599 = NetShow monitor events + + 600 - 799 = NetShow IMmsAutoServer errors + + 1000 - 1199 = NetShow MCMADM errors + + + NEW RANGES + + 2000 - 2999 = ASF (defined in ASFERR.MC) + + 3000 - 3999 = Windows Media SDK + + 4000 - 4999 = Windows Media Player + + 5000 - 5999 = Windows Media Server + + 6000 - 6999 = Windows Media HTTP/RTSP result codes (defined in NETERROR.MC) + + 7000 - 7999 = Windows Media Tools + + 8000 - 8999 = Windows Media Content Discovery + + 9000 - 9999 = Windows Media Real Time Collaboration + + 10000 - 10999 = Windows Media Digital Rights Management + + 11000 - 11999 = Windows Media Setup + + 12000 - 12999 = Windows Media Networking + + 13000 - 13999 = Windows Media Client Media Services + +**************************** READ ME ****************************************** + +--*/ + +#ifndef _NSERROR_H +#define _NSERROR_H + + +#define STATUS_SEVERITY(hr) (((hr) >> 30) & 0x3) + +#ifdef RC_INVOKED +#define _HRESULT_TYPEDEF_(_sc) _sc +#else // RC_INVOKED +#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) +#endif // RC_INVOKED + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Success Events +// +///////////////////////////////////////////////////////////////////////// + +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// +#define FACILITY_NS_WIN32 0x7 +#define FACILITY_NS 0xD + + +// +// Define the severity codes +// +#define STATUS_SEVERITY_WARNING 0x2 +#define STATUS_SEVERITY_SUCCESS 0x0 +#define STATUS_SEVERITY_INFORMATIONAL 0x1 +#define STATUS_SEVERITY_ERROR 0x3 + + +// +// MessageId: NS_S_CALLPENDING +// +// MessageText: +// +// The requested operation is pending completion.%0 +// +#define NS_S_CALLPENDING _HRESULT_TYPEDEF_(0x000D0000L) + +// +// MessageId: NS_S_CALLABORTED +// +// MessageText: +// +// The requested operation was aborted by the client.%0 +// +#define NS_S_CALLABORTED _HRESULT_TYPEDEF_(0x000D0001L) + +// +// MessageId: NS_S_STREAM_TRUNCATED +// +// MessageText: +// +// The stream was purposefully stopped before completion.%0 +// +#define NS_S_STREAM_TRUNCATED _HRESULT_TYPEDEF_(0x000D0002L) + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Warning Events +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_W_SERVER_BANDWIDTH_LIMIT +// +// MessageText: +// +// The maximum filebitrate value specified is greater than the server's configured maximum bandwidth.%0 +// +#define NS_W_SERVER_BANDWIDTH_LIMIT _HRESULT_TYPEDEF_(0x800D0003L) + +// +// MessageId: NS_W_FILE_BANDWIDTH_LIMIT +// +// MessageText: +// +// The maximum bandwidth value specified is less than the maximum filebitrate.%0 +// +#define NS_W_FILE_BANDWIDTH_LIMIT _HRESULT_TYPEDEF_(0x800D0004L) + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Error Events +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_NOCONNECTION +// +// MessageText: +// +// There is no connection established with the Windows Media server. The operation failed.%0 +// +#define NS_E_NOCONNECTION _HRESULT_TYPEDEF_(0xC00D0005L) + +// +// MessageId: NS_E_CANNOTCONNECT +// +// MessageText: +// +// Unable to establish a connection to the server.%0 +// +#define NS_E_CANNOTCONNECT _HRESULT_TYPEDEF_(0xC00D0006L) + +// +// MessageId: NS_E_CANNOTDESTROYTITLE +// +// MessageText: +// +// Unable to destroy the title.%0 +// +#define NS_E_CANNOTDESTROYTITLE _HRESULT_TYPEDEF_(0xC00D0007L) + +// +// MessageId: NS_E_CANNOTRENAMETITLE +// +// MessageText: +// +// Unable to rename the title.%0 +// +#define NS_E_CANNOTRENAMETITLE _HRESULT_TYPEDEF_(0xC00D0008L) + +// +// MessageId: NS_E_CANNOTOFFLINEDISK +// +// MessageText: +// +// Unable to offline disk.%0 +// +#define NS_E_CANNOTOFFLINEDISK _HRESULT_TYPEDEF_(0xC00D0009L) + +// +// MessageId: NS_E_CANNOTONLINEDISK +// +// MessageText: +// +// Unable to online disk.%0 +// +#define NS_E_CANNOTONLINEDISK _HRESULT_TYPEDEF_(0xC00D000AL) + +// +// MessageId: NS_E_NOREGISTEREDWALKER +// +// MessageText: +// +// There is no file parser registered for this type of file.%0 +// +#define NS_E_NOREGISTEREDWALKER _HRESULT_TYPEDEF_(0xC00D000BL) + +// +// MessageId: NS_E_NOFUNNEL +// +// MessageText: +// +// There is no data connection established.%0 +// +#define NS_E_NOFUNNEL _HRESULT_TYPEDEF_(0xC00D000CL) + +// +// MessageId: NS_E_NO_LOCALPLAY +// +// MessageText: +// +// Failed to load the local play DLL.%0 +// +#define NS_E_NO_LOCALPLAY _HRESULT_TYPEDEF_(0xC00D000DL) + +// +// MessageId: NS_E_NETWORK_BUSY +// +// MessageText: +// +// The network is busy.%0 +// +#define NS_E_NETWORK_BUSY _HRESULT_TYPEDEF_(0xC00D000EL) + +// +// MessageId: NS_E_TOO_MANY_SESS +// +// MessageText: +// +// The server session limit was exceeded.%0 +// +#define NS_E_TOO_MANY_SESS _HRESULT_TYPEDEF_(0xC00D000FL) + +// +// MessageId: NS_E_ALREADY_CONNECTED +// +// MessageText: +// +// The network connection already exists.%0 +// +#define NS_E_ALREADY_CONNECTED _HRESULT_TYPEDEF_(0xC00D0010L) + +// +// MessageId: NS_E_INVALID_INDEX +// +// MessageText: +// +// Index %1 is invalid.%0 +// +#define NS_E_INVALID_INDEX _HRESULT_TYPEDEF_(0xC00D0011L) + +// +// MessageId: NS_E_PROTOCOL_MISMATCH +// +// MessageText: +// +// There is no protocol or protocol version supported by both the client and the server.%0 +// +#define NS_E_PROTOCOL_MISMATCH _HRESULT_TYPEDEF_(0xC00D0012L) + +// +// MessageId: NS_E_TIMEOUT +// +// MessageText: +// +// The server, a computer set up to offer multimedia content to other computers, could not handle your request for multimedia content in a timely manner. Please try again later.%0 +// +#define NS_E_TIMEOUT _HRESULT_TYPEDEF_(0xC00D0013L) + +// +// MessageId: NS_E_NET_WRITE +// +// MessageText: +// +// Error writing to the network.%0 +// +#define NS_E_NET_WRITE _HRESULT_TYPEDEF_(0xC00D0014L) + +// +// MessageId: NS_E_NET_READ +// +// MessageText: +// +// Error reading from the network.%0 +// +#define NS_E_NET_READ _HRESULT_TYPEDEF_(0xC00D0015L) + +// +// MessageId: NS_E_DISK_WRITE +// +// MessageText: +// +// Error writing to a disk.%0 +// +#define NS_E_DISK_WRITE _HRESULT_TYPEDEF_(0xC00D0016L) + +// +// MessageId: NS_E_DISK_READ +// +// MessageText: +// +// Error reading from a disk.%0 +// +#define NS_E_DISK_READ _HRESULT_TYPEDEF_(0xC00D0017L) + +// +// MessageId: NS_E_FILE_WRITE +// +// MessageText: +// +// Error writing to a file.%0 +// +#define NS_E_FILE_WRITE _HRESULT_TYPEDEF_(0xC00D0018L) + +// +// MessageId: NS_E_FILE_READ +// +// MessageText: +// +// Error reading from a file.%0 +// +#define NS_E_FILE_READ _HRESULT_TYPEDEF_(0xC00D0019L) + +// +// MessageId: NS_E_FILE_NOT_FOUND +// +// MessageText: +// +// The system cannot find the file specified.%0 +// +#define NS_E_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D001AL) + +// +// MessageId: NS_E_FILE_EXISTS +// +// MessageText: +// +// The file already exists.%0 +// +#define NS_E_FILE_EXISTS _HRESULT_TYPEDEF_(0xC00D001BL) + +// +// MessageId: NS_E_INVALID_NAME +// +// MessageText: +// +// The file name, directory name, or volume label syntax is incorrect.%0 +// +#define NS_E_INVALID_NAME _HRESULT_TYPEDEF_(0xC00D001CL) + +// +// MessageId: NS_E_FILE_OPEN_FAILED +// +// MessageText: +// +// Failed to open a file.%0 +// +#define NS_E_FILE_OPEN_FAILED _HRESULT_TYPEDEF_(0xC00D001DL) + +// +// MessageId: NS_E_FILE_ALLOCATION_FAILED +// +// MessageText: +// +// Unable to allocate a file.%0 +// +#define NS_E_FILE_ALLOCATION_FAILED _HRESULT_TYPEDEF_(0xC00D001EL) + +// +// MessageId: NS_E_FILE_INIT_FAILED +// +// MessageText: +// +// Unable to initialize a file.%0 +// +#define NS_E_FILE_INIT_FAILED _HRESULT_TYPEDEF_(0xC00D001FL) + +// +// MessageId: NS_E_FILE_PLAY_FAILED +// +// MessageText: +// +// Unable to play a file.%0 +// +#define NS_E_FILE_PLAY_FAILED _HRESULT_TYPEDEF_(0xC00D0020L) + +// +// MessageId: NS_E_SET_DISK_UID_FAILED +// +// MessageText: +// +// Could not set the disk UID.%0 +// +#define NS_E_SET_DISK_UID_FAILED _HRESULT_TYPEDEF_(0xC00D0021L) + +// +// MessageId: NS_E_INDUCED +// +// MessageText: +// +// An error was induced for testing purposes.%0 +// +#define NS_E_INDUCED _HRESULT_TYPEDEF_(0xC00D0022L) + +// +// MessageId: NS_E_CCLINK_DOWN +// +// MessageText: +// +// Two Content Servers failed to communicate.%0 +// +#define NS_E_CCLINK_DOWN _HRESULT_TYPEDEF_(0xC00D0023L) + +// +// MessageId: NS_E_INTERNAL +// +// MessageText: +// +// An unknown error occurred.%0 +// +#define NS_E_INTERNAL _HRESULT_TYPEDEF_(0xC00D0024L) + +// +// MessageId: NS_E_BUSY +// +// MessageText: +// +// The requested resource is in use.%0 +// +#define NS_E_BUSY _HRESULT_TYPEDEF_(0xC00D0025L) + +// +// MessageId: NS_E_UNRECOGNIZED_STREAM_TYPE +// +// MessageText: +// +// The specified protocol is not recognized. Be sure that the file name and syntax, such as slashes, are correct for the protocol.%0 +// +#define NS_E_UNRECOGNIZED_STREAM_TYPE _HRESULT_TYPEDEF_(0xC00D0026L) + +// +// MessageId: NS_E_NETWORK_SERVICE_FAILURE +// +// MessageText: +// +// The network service provider failed.%0 +// +#define NS_E_NETWORK_SERVICE_FAILURE _HRESULT_TYPEDEF_(0xC00D0027L) + +// +// MessageId: NS_E_NETWORK_RESOURCE_FAILURE +// +// MessageText: +// +// An attempt to acquire a network resource failed.%0 +// +#define NS_E_NETWORK_RESOURCE_FAILURE _HRESULT_TYPEDEF_(0xC00D0028L) + +// +// MessageId: NS_E_CONNECTION_FAILURE +// +// MessageText: +// +// The network connection has failed.%0 +// +#define NS_E_CONNECTION_FAILURE _HRESULT_TYPEDEF_(0xC00D0029L) + +// +// MessageId: NS_E_SHUTDOWN +// +// MessageText: +// +// The session is being terminated locally.%0 +// +#define NS_E_SHUTDOWN _HRESULT_TYPEDEF_(0xC00D002AL) + +// +// MessageId: NS_E_INVALID_REQUEST +// +// MessageText: +// +// The request is invalid in the current state.%0 +// +#define NS_E_INVALID_REQUEST _HRESULT_TYPEDEF_(0xC00D002BL) + +// +// MessageId: NS_E_INSUFFICIENT_BANDWIDTH +// +// MessageText: +// +// There is insufficient bandwidth available to fulfill the request.%0 +// +#define NS_E_INSUFFICIENT_BANDWIDTH _HRESULT_TYPEDEF_(0xC00D002CL) + +// +// MessageId: NS_E_NOT_REBUILDING +// +// MessageText: +// +// The disk is not rebuilding.%0 +// +#define NS_E_NOT_REBUILDING _HRESULT_TYPEDEF_(0xC00D002DL) + +// +// MessageId: NS_E_LATE_OPERATION +// +// MessageText: +// +// An operation requested for a particular time could not be carried out on schedule.%0 +// +#define NS_E_LATE_OPERATION _HRESULT_TYPEDEF_(0xC00D002EL) + +// +// MessageId: NS_E_INVALID_DATA +// +// MessageText: +// +// Invalid or corrupt data was encountered.%0 +// +#define NS_E_INVALID_DATA _HRESULT_TYPEDEF_(0xC00D002FL) + +// +// MessageId: NS_E_FILE_BANDWIDTH_LIMIT +// +// MessageText: +// +// The bandwidth required to stream a file is higher than the maximum file bandwidth allowed on the server.%0 +// +#define NS_E_FILE_BANDWIDTH_LIMIT _HRESULT_TYPEDEF_(0xC00D0030L) + +// +// MessageId: NS_E_OPEN_FILE_LIMIT +// +// MessageText: +// +// The client cannot have any more files open simultaneously.%0 +// +#define NS_E_OPEN_FILE_LIMIT _HRESULT_TYPEDEF_(0xC00D0031L) + +// +// MessageId: NS_E_BAD_CONTROL_DATA +// +// MessageText: +// +// The server received invalid data from the client on the control connection.%0 +// +#define NS_E_BAD_CONTROL_DATA _HRESULT_TYPEDEF_(0xC00D0032L) + +// +// MessageId: NS_E_NO_STREAM +// +// MessageText: +// +// There is no stream available.%0 +// +#define NS_E_NO_STREAM _HRESULT_TYPEDEF_(0xC00D0033L) + +// +// MessageId: NS_E_STREAM_END +// +// MessageText: +// +// There is no more data in the stream.%0 +// +#define NS_E_STREAM_END _HRESULT_TYPEDEF_(0xC00D0034L) + +// +// MessageId: NS_E_SERVER_NOT_FOUND +// +// MessageText: +// +// The specified server could not be found.%0 +// +#define NS_E_SERVER_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D0035L) + +// +// MessageId: NS_E_DUPLICATE_NAME +// +// MessageText: +// +// The specified name is already in use. +// +#define NS_E_DUPLICATE_NAME _HRESULT_TYPEDEF_(0xC00D0036L) + +// +// MessageId: NS_E_DUPLICATE_ADDRESS +// +// MessageText: +// +// The specified address is already in use. +// +#define NS_E_DUPLICATE_ADDRESS _HRESULT_TYPEDEF_(0xC00D0037L) + +// +// MessageId: NS_E_BAD_MULTICAST_ADDRESS +// +// MessageText: +// +// The specified address is not a valid multicast address. +// +#define NS_E_BAD_MULTICAST_ADDRESS _HRESULT_TYPEDEF_(0xC00D0038L) + +// +// MessageId: NS_E_BAD_ADAPTER_ADDRESS +// +// MessageText: +// +// The specified adapter address is invalid. +// +#define NS_E_BAD_ADAPTER_ADDRESS _HRESULT_TYPEDEF_(0xC00D0039L) + +// +// MessageId: NS_E_BAD_DELIVERY_MODE +// +// MessageText: +// +// The specified delivery mode is invalid. +// +#define NS_E_BAD_DELIVERY_MODE _HRESULT_TYPEDEF_(0xC00D003AL) + +// +// MessageId: NS_E_INVALID_CHANNEL +// +// MessageText: +// +// The specified station does not exist. +// +#define NS_E_INVALID_CHANNEL _HRESULT_TYPEDEF_(0xC00D003BL) + +// +// MessageId: NS_E_INVALID_STREAM +// +// MessageText: +// +// The specified stream does not exist. +// +#define NS_E_INVALID_STREAM _HRESULT_TYPEDEF_(0xC00D003CL) + +// +// MessageId: NS_E_INVALID_ARCHIVE +// +// MessageText: +// +// The specified archive could not be opened. +// +#define NS_E_INVALID_ARCHIVE _HRESULT_TYPEDEF_(0xC00D003DL) + +// +// MessageId: NS_E_NOTITLES +// +// MessageText: +// +// The system cannot find any titles on the server.%0 +// +#define NS_E_NOTITLES _HRESULT_TYPEDEF_(0xC00D003EL) + +// +// MessageId: NS_E_INVALID_CLIENT +// +// MessageText: +// +// The system cannot find the client specified.%0 +// +#define NS_E_INVALID_CLIENT _HRESULT_TYPEDEF_(0xC00D003FL) + +// +// MessageId: NS_E_INVALID_BLACKHOLE_ADDRESS +// +// MessageText: +// +// The Blackhole Address is not initialized.%0 +// +#define NS_E_INVALID_BLACKHOLE_ADDRESS _HRESULT_TYPEDEF_(0xC00D0040L) + +// +// MessageId: NS_E_INCOMPATIBLE_FORMAT +// +// MessageText: +// +// The station does not support the stream format. +// +#define NS_E_INCOMPATIBLE_FORMAT _HRESULT_TYPEDEF_(0xC00D0041L) + +// +// MessageId: NS_E_INVALID_KEY +// +// MessageText: +// +// The specified key is not valid. +// +#define NS_E_INVALID_KEY _HRESULT_TYPEDEF_(0xC00D0042L) + +// +// MessageId: NS_E_INVALID_PORT +// +// MessageText: +// +// The specified port is not valid. +// +#define NS_E_INVALID_PORT _HRESULT_TYPEDEF_(0xC00D0043L) + +// +// MessageId: NS_E_INVALID_TTL +// +// MessageText: +// +// The specified TTL is not valid. +// +#define NS_E_INVALID_TTL _HRESULT_TYPEDEF_(0xC00D0044L) + +// +// MessageId: NS_E_STRIDE_REFUSED +// +// MessageText: +// +// The request to fast forward or rewind could not be fulfilled. +// +#define NS_E_STRIDE_REFUSED _HRESULT_TYPEDEF_(0xC00D0045L) + +// +// IMmsAutoServer Errors +// +// +// MessageId: NS_E_MMSAUTOSERVER_CANTFINDWALKER +// +// MessageText: +// +// Unable to load the appropriate file parser.%0 +// +#define NS_E_MMSAUTOSERVER_CANTFINDWALKER _HRESULT_TYPEDEF_(0xC00D0046L) + +// +// MessageId: NS_E_MAX_BITRATE +// +// MessageText: +// +// Cannot exceed the maximum bandwidth limit.%0 +// +#define NS_E_MAX_BITRATE _HRESULT_TYPEDEF_(0xC00D0047L) + +// +// MessageId: NS_E_LOGFILEPERIOD +// +// MessageText: +// +// Invalid value for LogFilePeriod.%0 +// +#define NS_E_LOGFILEPERIOD _HRESULT_TYPEDEF_(0xC00D0048L) + +// +// MessageId: NS_E_MAX_CLIENTS +// +// MessageText: +// +// Cannot exceed the maximum client limit.%0 +// +// +#define NS_E_MAX_CLIENTS _HRESULT_TYPEDEF_(0xC00D0049L) + +// +// MessageId: NS_E_LOG_FILE_SIZE +// +// MessageText: +// +// The maximum log file size has been reached.%0 +// +// +#define NS_E_LOG_FILE_SIZE _HRESULT_TYPEDEF_(0xC00D004AL) + +// +// MessageId: NS_E_MAX_FILERATE +// +// MessageText: +// +// Cannot exceed the maximum file rate.%0 +// +#define NS_E_MAX_FILERATE _HRESULT_TYPEDEF_(0xC00D004BL) + +// +// File Walker Errors +// +// +// MessageId: NS_E_WALKER_UNKNOWN +// +// MessageText: +// +// Unknown file type.%0 +// +#define NS_E_WALKER_UNKNOWN _HRESULT_TYPEDEF_(0xC00D004CL) + +// +// MessageId: NS_E_WALKER_SERVER +// +// MessageText: +// +// The specified file, %1, cannot be loaded onto the specified server, %2.%0 +// +#define NS_E_WALKER_SERVER _HRESULT_TYPEDEF_(0xC00D004DL) + +// +// MessageId: NS_E_WALKER_USAGE +// +// MessageText: +// +// There was a usage error with file parser.%0 +// +#define NS_E_WALKER_USAGE _HRESULT_TYPEDEF_(0xC00D004EL) + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Monitor Events +// +///////////////////////////////////////////////////////////////////////// + + + // Tiger Events + + // %1 is the tiger name + +// +// MessageId: NS_I_TIGER_START +// +// MessageText: +// +// The Title Server %1 is running.%0 +// +#define NS_I_TIGER_START _HRESULT_TYPEDEF_(0x400D004FL) + +// +// MessageId: NS_E_TIGER_FAIL +// +// MessageText: +// +// The Title Server %1 has failed.%0 +// +#define NS_E_TIGER_FAIL _HRESULT_TYPEDEF_(0xC00D0050L) + + + // Cub Events + + // %1 is the cub ID + // %2 is the cub name + +// +// MessageId: NS_I_CUB_START +// +// MessageText: +// +// Content Server %1 (%2) is starting.%0 +// +#define NS_I_CUB_START _HRESULT_TYPEDEF_(0x400D0051L) + +// +// MessageId: NS_I_CUB_RUNNING +// +// MessageText: +// +// Content Server %1 (%2) is running.%0 +// +#define NS_I_CUB_RUNNING _HRESULT_TYPEDEF_(0x400D0052L) + +// +// MessageId: NS_E_CUB_FAIL +// +// MessageText: +// +// Content Server %1 (%2) has failed.%0 +// +#define NS_E_CUB_FAIL _HRESULT_TYPEDEF_(0xC00D0053L) + + + // Disk Events + + // %1 is the tiger disk ID + // %2 is the device name + // %3 is the cub ID +// +// MessageId: NS_I_DISK_START +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, is running.%0 +// +#define NS_I_DISK_START _HRESULT_TYPEDEF_(0x400D0054L) + +// +// MessageId: NS_E_DISK_FAIL +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, has failed.%0 +// +#define NS_E_DISK_FAIL _HRESULT_TYPEDEF_(0xC00D0055L) + +// +// MessageId: NS_I_DISK_REBUILD_STARTED +// +// MessageText: +// +// Started rebuilding disk %1 ( %2 ) on Content Server %3.%0 +// +#define NS_I_DISK_REBUILD_STARTED _HRESULT_TYPEDEF_(0x400D0056L) + +// +// MessageId: NS_I_DISK_REBUILD_FINISHED +// +// MessageText: +// +// Finished rebuilding disk %1 ( %2 ) on Content Server %3.%0 +// +#define NS_I_DISK_REBUILD_FINISHED _HRESULT_TYPEDEF_(0x400D0057L) + +// +// MessageId: NS_I_DISK_REBUILD_ABORTED +// +// MessageText: +// +// Aborted rebuilding disk %1 ( %2 ) on Content Server %3.%0 +// +#define NS_I_DISK_REBUILD_ABORTED _HRESULT_TYPEDEF_(0x400D0058L) + + + // Admin Events + +// +// MessageId: NS_I_LIMIT_FUNNELS +// +// MessageText: +// +// A NetShow administrator at network location %1 set the data stream limit to %2 streams.%0 +// +#define NS_I_LIMIT_FUNNELS _HRESULT_TYPEDEF_(0x400D0059L) + +// +// MessageId: NS_I_START_DISK +// +// MessageText: +// +// A NetShow administrator at network location %1 started disk %2.%0 +// +#define NS_I_START_DISK _HRESULT_TYPEDEF_(0x400D005AL) + +// +// MessageId: NS_I_STOP_DISK +// +// MessageText: +// +// A NetShow administrator at network location %1 stopped disk %2.%0 +// +#define NS_I_STOP_DISK _HRESULT_TYPEDEF_(0x400D005BL) + +// +// MessageId: NS_I_STOP_CUB +// +// MessageText: +// +// A NetShow administrator at network location %1 stopped Content Server %2.%0 +// +#define NS_I_STOP_CUB _HRESULT_TYPEDEF_(0x400D005CL) + +// +// MessageId: NS_I_KILL_USERSESSION +// +// MessageText: +// +// A NetShow administrator at network location %1 aborted user session %2 from the system.%0 +// +#define NS_I_KILL_USERSESSION _HRESULT_TYPEDEF_(0x400D005DL) + +// +// MessageId: NS_I_KILL_CONNECTION +// +// MessageText: +// +// A NetShow administrator at network location %1 aborted obsolete connection %2 from the system.%0 +// +#define NS_I_KILL_CONNECTION _HRESULT_TYPEDEF_(0x400D005EL) + +// +// MessageId: NS_I_REBUILD_DISK +// +// MessageText: +// +// A NetShow administrator at network location %1 started rebuilding disk %2.%0 +// +#define NS_I_REBUILD_DISK _HRESULT_TYPEDEF_(0x400D005FL) + +// +// MessageId: NS_W_UNKNOWN_EVENT +// +// MessageText: +// +// Unknown %1 event encountered.%0 +// +#define NS_W_UNKNOWN_EVENT _HRESULT_TYPEDEF_(0x800D0060L) + + + // Alerts + +// +// MessageId: NS_E_MAX_FUNNELS_ALERT +// +// MessageText: +// +// The NetShow data stream limit of %1 streams was reached.%0 +// +#define NS_E_MAX_FUNNELS_ALERT _HRESULT_TYPEDEF_(0xC00D0060L) + +// +// MessageId: NS_E_ALLOCATE_FILE_FAIL +// +// MessageText: +// +// The NetShow Video Server was unable to allocate a %1 block file named %2.%0 +// +#define NS_E_ALLOCATE_FILE_FAIL _HRESULT_TYPEDEF_(0xC00D0061L) + +// +// MessageId: NS_E_PAGING_ERROR +// +// MessageText: +// +// A Content Server was unable to page a block.%0 +// +#define NS_E_PAGING_ERROR _HRESULT_TYPEDEF_(0xC00D0062L) + +// +// MessageId: NS_E_BAD_BLOCK0_VERSION +// +// MessageText: +// +// Disk %1 has unrecognized control block version %2.%0 +// +#define NS_E_BAD_BLOCK0_VERSION _HRESULT_TYPEDEF_(0xC00D0063L) + +// +// MessageId: NS_E_BAD_DISK_UID +// +// MessageText: +// +// Disk %1 has incorrect uid %2.%0 +// +#define NS_E_BAD_DISK_UID _HRESULT_TYPEDEF_(0xC00D0064L) + +// +// MessageId: NS_E_BAD_FSMAJOR_VERSION +// +// MessageText: +// +// Disk %1 has unsupported file system major version %2.%0 +// +#define NS_E_BAD_FSMAJOR_VERSION _HRESULT_TYPEDEF_(0xC00D0065L) + +// +// MessageId: NS_E_BAD_STAMPNUMBER +// +// MessageText: +// +// Disk %1 has bad stamp number in control block.%0 +// +#define NS_E_BAD_STAMPNUMBER _HRESULT_TYPEDEF_(0xC00D0066L) + +// +// MessageId: NS_E_PARTIALLY_REBUILT_DISK +// +// MessageText: +// +// Disk %1 is partially reconstructed.%0 +// +#define NS_E_PARTIALLY_REBUILT_DISK _HRESULT_TYPEDEF_(0xC00D0067L) + +// +// MessageId: NS_E_ENACTPLAN_GIVEUP +// +// MessageText: +// +// EnactPlan gives up.%0 +// +#define NS_E_ENACTPLAN_GIVEUP _HRESULT_TYPEDEF_(0xC00D0068L) + + + // MCMADM warnings/errors + +// +// MessageId: MCMADM_I_NO_EVENTS +// +// MessageText: +// +// Event initialization failed, there will be no MCM events.%0 +// +#define MCMADM_I_NO_EVENTS _HRESULT_TYPEDEF_(0x400D0069L) + +// +// MessageId: MCMADM_E_REGKEY_NOT_FOUND +// +// MessageText: +// +// The key was not found in the registry.%0 +// +#define MCMADM_E_REGKEY_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D006AL) + +// +// MessageId: NS_E_NO_FORMATS +// +// MessageText: +// +// The publishing point cannot be started because the server does not have the appropriate stream formats. Use the Multicast Announcement Wizard to create a new announcement for this publishing point.%0 +// +#define NS_E_NO_FORMATS _HRESULT_TYPEDEF_(0xC00D006BL) + +// +// MessageId: NS_E_NO_REFERENCES +// +// MessageText: +// +// No reference URLs were found in an ASX file.%0 +// +#define NS_E_NO_REFERENCES _HRESULT_TYPEDEF_(0xC00D006CL) + +// +// MessageId: NS_E_WAVE_OPEN +// +// MessageText: +// +// Error opening wave device, the device might be in use.%0 +// +#define NS_E_WAVE_OPEN _HRESULT_TYPEDEF_(0xC00D006DL) + +// +// MessageId: NS_I_LOGGING_FAILED +// +// MessageText: +// +// The logging operation failed. +// +#define NS_I_LOGGING_FAILED _HRESULT_TYPEDEF_(0x400D006EL) + +// +// MessageId: NS_E_CANNOTCONNECTEVENTS +// +// MessageText: +// +// Unable to establish a connection to the NetShow event monitor service.%0 +// +#define NS_E_CANNOTCONNECTEVENTS _HRESULT_TYPEDEF_(0xC00D006FL) + +// +// MessageId: NS_I_LIMIT_BANDWIDTH +// +// MessageText: +// +// A NetShow administrator at network location %1 set the maximum bandwidth limit to %2 bps.%0 +// +#define NS_I_LIMIT_BANDWIDTH _HRESULT_TYPEDEF_(0x400D0070L) + +// +// MessageId: NS_E_NO_DEVICE +// +// MessageText: +// +// No device driver is present on the system.%0 +// +#define NS_E_NO_DEVICE _HRESULT_TYPEDEF_(0xC00D0071L) + +// +// MessageId: NS_E_NO_SPECIFIED_DEVICE +// +// MessageText: +// +// No specified device driver is present.%0 +// +#define NS_E_NO_SPECIFIED_DEVICE _HRESULT_TYPEDEF_(0xC00D0072L) + + +// NOTENOTE!!! +// +// Due to legacy problems these error codes live inside the ASF error code range +// +// +// MessageId: NS_E_NOTHING_TO_DO +// +// MessageText: +// +// NS_E_NOTHING_TO_DO +// +#define NS_E_NOTHING_TO_DO _HRESULT_TYPEDEF_(0xC00D07F1L) + +// +// MessageId: NS_E_NO_MULTICAST +// +// MessageText: +// +// Not receiving data from the server.%0 +// +#define NS_E_NO_MULTICAST _HRESULT_TYPEDEF_(0xC00D07F2L) + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Error Events +// +// IdRange = 200..399 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_MONITOR_GIVEUP +// +// MessageText: +// +// Netshow Events Monitor is not operational and has been disconnected.%0 +// +#define NS_E_MONITOR_GIVEUP _HRESULT_TYPEDEF_(0xC00D00C8L) + +// +// MessageId: NS_E_REMIRRORED_DISK +// +// MessageText: +// +// Disk %1 is remirrored.%0 +// +#define NS_E_REMIRRORED_DISK _HRESULT_TYPEDEF_(0xC00D00C9L) + +// +// MessageId: NS_E_INSUFFICIENT_DATA +// +// MessageText: +// +// Insufficient data found.%0 +// +#define NS_E_INSUFFICIENT_DATA _HRESULT_TYPEDEF_(0xC00D00CAL) + +// +// MessageId: NS_E_ASSERT +// +// MessageText: +// +// %1 failed in file %2 line %3.%0 +// +#define NS_E_ASSERT _HRESULT_TYPEDEF_(0xC00D00CBL) + +// +// MessageId: NS_E_BAD_ADAPTER_NAME +// +// MessageText: +// +// The specified adapter name is invalid.%0 +// +#define NS_E_BAD_ADAPTER_NAME _HRESULT_TYPEDEF_(0xC00D00CCL) + +// +// MessageId: NS_E_NOT_LICENSED +// +// MessageText: +// +// The application is not licensed for this feature.%0 +// +#define NS_E_NOT_LICENSED _HRESULT_TYPEDEF_(0xC00D00CDL) + +// +// MessageId: NS_E_NO_SERVER_CONTACT +// +// MessageText: +// +// Unable to contact the server.%0 +// +#define NS_E_NO_SERVER_CONTACT _HRESULT_TYPEDEF_(0xC00D00CEL) + +// +// MessageId: NS_E_TOO_MANY_TITLES +// +// MessageText: +// +// Maximum number of titles exceeded.%0 +// +#define NS_E_TOO_MANY_TITLES _HRESULT_TYPEDEF_(0xC00D00CFL) + +// +// MessageId: NS_E_TITLE_SIZE_EXCEEDED +// +// MessageText: +// +// Maximum size of a title exceeded.%0 +// +#define NS_E_TITLE_SIZE_EXCEEDED _HRESULT_TYPEDEF_(0xC00D00D0L) + +// +// MessageId: NS_E_UDP_DISABLED +// +// MessageText: +// +// UDP protocol not enabled. Not trying %1!ls!.%0 +// +#define NS_E_UDP_DISABLED _HRESULT_TYPEDEF_(0xC00D00D1L) + +// +// MessageId: NS_E_TCP_DISABLED +// +// MessageText: +// +// TCP protocol not enabled. Not trying %1!ls!.%0 +// +#define NS_E_TCP_DISABLED _HRESULT_TYPEDEF_(0xC00D00D2L) + +// +// MessageId: NS_E_HTTP_DISABLED +// +// MessageText: +// +// HTTP protocol not enabled. Not trying %1!ls!.%0 +// +#define NS_E_HTTP_DISABLED _HRESULT_TYPEDEF_(0xC00D00D3L) + +// +// MessageId: NS_E_LICENSE_EXPIRED +// +// MessageText: +// +// The product license has expired.%0 +// +#define NS_E_LICENSE_EXPIRED _HRESULT_TYPEDEF_(0xC00D00D4L) + +// +// MessageId: NS_E_TITLE_BITRATE +// +// MessageText: +// +// Source file exceeds the per title maximum bitrate. See NetShow Theater documentation for more information.%0 +// +#define NS_E_TITLE_BITRATE _HRESULT_TYPEDEF_(0xC00D00D5L) + +// +// MessageId: NS_E_EMPTY_PROGRAM_NAME +// +// MessageText: +// +// The program name cannot be empty.%0 +// +#define NS_E_EMPTY_PROGRAM_NAME _HRESULT_TYPEDEF_(0xC00D00D6L) + +// +// MessageId: NS_E_MISSING_CHANNEL +// +// MessageText: +// +// Station %1 does not exist.%0 +// +#define NS_E_MISSING_CHANNEL _HRESULT_TYPEDEF_(0xC00D00D7L) + +// +// MessageId: NS_E_NO_CHANNELS +// +// MessageText: +// +// You need to define at least one station before this operation can complete.%0 +// +#define NS_E_NO_CHANNELS _HRESULT_TYPEDEF_(0xC00D00D8L) + + +///////////////////////////////////////////////////////////////////// +// This error message is to replace previous NS_E_INVALID_INDEX which +// takes an index value for the error message string. For some application +// obtain the idex value at reporting error time is very difficult, so we +// use this string to avoid the problem. +////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_INVALID_INDEX2 +// +// MessageText: +// +// The index specified is invalid.%0 +// +#define NS_E_INVALID_INDEX2 _HRESULT_TYPEDEF_(0xC00D00D9L) + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Monitor Events +// +// IdRange = 400..599 +// +// Admin Events: +// +// Alerts: +// +// Title Server: +// %1 is the Title Server name +// +// Content Server: +// %1 is the Content Server ID +// %2 is the Content Server name +// %3 is the Peer Content Server name (optional) +// +// Disks: +// %1 is the Title Server disk ID +// %2 is the device name +// %3 is the Content Server ID +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_CUB_FAIL_LINK +// +// MessageText: +// +// Content Server %1 (%2) has failed its link to Content Server %3.%0 +// +#define NS_E_CUB_FAIL_LINK _HRESULT_TYPEDEF_(0xC00D0190L) + +// +// MessageId: NS_I_CUB_UNFAIL_LINK +// +// MessageText: +// +// Content Server %1 (%2) has established its link to Content Server %3.%0 +// +#define NS_I_CUB_UNFAIL_LINK _HRESULT_TYPEDEF_(0x400D0191L) + +// +// MessageId: NS_E_BAD_CUB_UID +// +// MessageText: +// +// Content Server %1 (%2) has incorrect uid %3.%0 +// +#define NS_E_BAD_CUB_UID _HRESULT_TYPEDEF_(0xC00D0192L) + +// +// MessageId: NS_I_RESTRIPE_START +// +// MessageText: +// +// Restripe operation has started.%0 +// +#define NS_I_RESTRIPE_START _HRESULT_TYPEDEF_(0x400D0193L) + +// +// MessageId: NS_I_RESTRIPE_DONE +// +// MessageText: +// +// Restripe operation has completed.%0 +// +#define NS_I_RESTRIPE_DONE _HRESULT_TYPEDEF_(0x400D0194L) + +// +// MessageId: NS_E_GLITCH_MODE +// +// MessageText: +// +// Server unreliable because multiple components failed.%0 +// +#define NS_E_GLITCH_MODE _HRESULT_TYPEDEF_(0xC00D0195L) + +// +// MessageId: NS_I_RESTRIPE_DISK_OUT +// +// MessageText: +// +// Content disk %1 (%2) on Content Server %3 has been restriped out.%0 +// +#define NS_I_RESTRIPE_DISK_OUT _HRESULT_TYPEDEF_(0x400D0196L) + +// +// MessageId: NS_I_RESTRIPE_CUB_OUT +// +// MessageText: +// +// Content server %1 (%2) has been restriped out.%0 +// +#define NS_I_RESTRIPE_CUB_OUT _HRESULT_TYPEDEF_(0x400D0197L) + +// +// MessageId: NS_I_DISK_STOP +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, has been offlined.%0 +// +#define NS_I_DISK_STOP _HRESULT_TYPEDEF_(0x400D0198L) + +// +// MessageId: NS_I_CATATONIC_FAILURE +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, will be failed because it is catatonic.%0 +// +#define NS_I_CATATONIC_FAILURE _HRESULT_TYPEDEF_(0x800D0199L) + +// +// MessageId: NS_I_CATATONIC_AUTO_UNFAIL +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, auto online from catatonic state.%0 +// +#define NS_I_CATATONIC_AUTO_UNFAIL _HRESULT_TYPEDEF_(0x800D019AL) + +// +// MessageId: NS_E_NO_MEDIA_PROTOCOL +// +// MessageText: +// +// Content Server %1 (%2) is unable to communicate with the Media System Network Protocol.%0 +// +#define NS_E_NO_MEDIA_PROTOCOL _HRESULT_TYPEDEF_(0xC00D019BL) + + +// +// Advanced Streaming Format (ASF) codes occupy MessageIds 2000-2999 +// +// See ASFErr.mc for more details - please do not define any symbols +// in that range in this file. +// + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media SDK Errors +// +// IdRange = 3000-3199 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_INVALID_INPUT_FORMAT +// +// MessageText: +// +// The input media format is invalid.%0 +// +#define NS_E_INVALID_INPUT_FORMAT _HRESULT_TYPEDEF_(0xC00D0BB8L) + +// +// MessageId: NS_E_MSAUDIO_NOT_INSTALLED +// +// MessageText: +// +// The MSAudio codec is not installed on this system.%0 +// +#define NS_E_MSAUDIO_NOT_INSTALLED _HRESULT_TYPEDEF_(0xC00D0BB9L) + +// +// MessageId: NS_E_UNEXPECTED_MSAUDIO_ERROR +// +// MessageText: +// +// An unexpected error occurred with the MSAudio codec.%0 +// +#define NS_E_UNEXPECTED_MSAUDIO_ERROR _HRESULT_TYPEDEF_(0xC00D0BBAL) + +// +// MessageId: NS_E_INVALID_OUTPUT_FORMAT +// +// MessageText: +// +// The output media format is invalid.%0 +// +#define NS_E_INVALID_OUTPUT_FORMAT _HRESULT_TYPEDEF_(0xC00D0BBBL) + +// +// MessageId: NS_E_NOT_CONFIGURED +// +// MessageText: +// +// The object must be fully configured before audio samples can be processed.%0 +// +#define NS_E_NOT_CONFIGURED _HRESULT_TYPEDEF_(0xC00D0BBCL) + +// +// MessageId: NS_E_PROTECTED_CONTENT +// +// MessageText: +// +// You need a license to perform the requested operation on this media file.%0 +// +#define NS_E_PROTECTED_CONTENT _HRESULT_TYPEDEF_(0xC00D0BBDL) + +// +// MessageId: NS_E_LICENSE_REQUIRED +// +// MessageText: +// +// You need a license to perform the requested operation on this media file.%0 +// +#define NS_E_LICENSE_REQUIRED _HRESULT_TYPEDEF_(0xC00D0BBEL) + +// +// MessageId: NS_E_TAMPERED_CONTENT +// +// MessageText: +// +// This media file is corrupted or invalid. Contact the content provider for a new file.%0 +// +#define NS_E_TAMPERED_CONTENT _HRESULT_TYPEDEF_(0xC00D0BBFL) + +// +// MessageId: NS_E_LICENSE_OUTOFDATE +// +// MessageText: +// +// The license for this media file has expired. Get a new license or contact the content provider for further assistance.%0 +// +#define NS_E_LICENSE_OUTOFDATE _HRESULT_TYPEDEF_(0xC00D0BC0L) + +// +// MessageId: NS_E_LICENSE_INCORRECT_RIGHTS +// +// MessageText: +// +// You are not allowed to open this file. Contact the content provider for further assistance.%0 +// +#define NS_E_LICENSE_INCORRECT_RIGHTS _HRESULT_TYPEDEF_(0xC00D0BC1L) + +// +// MessageId: NS_E_AUDIO_CODEC_NOT_INSTALLED +// +// MessageText: +// +// The requested audio codec is not installed on this system.%0 +// +#define NS_E_AUDIO_CODEC_NOT_INSTALLED _HRESULT_TYPEDEF_(0xC00D0BC2L) + +// +// MessageId: NS_E_AUDIO_CODEC_ERROR +// +// MessageText: +// +// An unexpected error occurred with the audio codec.%0 +// +#define NS_E_AUDIO_CODEC_ERROR _HRESULT_TYPEDEF_(0xC00D0BC3L) + +// +// MessageId: NS_E_VIDEO_CODEC_NOT_INSTALLED +// +// MessageText: +// +// The requested video codec is not installed on this system.%0 +// +#define NS_E_VIDEO_CODEC_NOT_INSTALLED _HRESULT_TYPEDEF_(0xC00D0BC4L) + +// +// MessageId: NS_E_VIDEO_CODEC_ERROR +// +// MessageText: +// +// An unexpected error occurred with the video codec.%0 +// +#define NS_E_VIDEO_CODEC_ERROR _HRESULT_TYPEDEF_(0xC00D0BC5L) + +// +// MessageId: NS_E_INVALIDPROFILE +// +// MessageText: +// +// The Profile is invalid.%0 +// +#define NS_E_INVALIDPROFILE _HRESULT_TYPEDEF_(0xC00D0BC6L) + +// +// MessageId: NS_E_INCOMPATIBLE_VERSION +// +// MessageText: +// +// A new version of the SDK is needed to play the requested content.%0 +// +#define NS_E_INCOMPATIBLE_VERSION _HRESULT_TYPEDEF_(0xC00D0BC7L) + +// +// MessageId: NS_S_REBUFFERING +// +// MessageText: +// +// The requested operation has caused the source to rebuffer.%0 +// +#define NS_S_REBUFFERING _HRESULT_TYPEDEF_(0x000D0BC8L) + +// +// MessageId: NS_S_DEGRADING_QUALITY +// +// MessageText: +// +// The requested operation has caused the source to degrade codec quality.%0 +// +#define NS_S_DEGRADING_QUALITY _HRESULT_TYPEDEF_(0x000D0BC9L) + +// +// MessageId: NS_E_OFFLINE_MODE +// +// MessageText: +// +// The requested URL is not available in offline mode.%0 +// +#define NS_E_OFFLINE_MODE _HRESULT_TYPEDEF_(0xC00D0BCAL) + +// +// MessageId: NS_E_NOT_CONNECTED +// +// MessageText: +// +// The requested URL cannot be accessed because there is no network connection.%0 +// +#define NS_E_NOT_CONNECTED _HRESULT_TYPEDEF_(0xC00D0BCBL) + +// +// MessageId: NS_E_TOO_MUCH_DATA +// +// MessageText: +// +// The encoding process was unable to keep up with the amount of supplied data.%0 +// +#define NS_E_TOO_MUCH_DATA _HRESULT_TYPEDEF_(0xC00D0BCCL) + +// +// MessageId: NS_E_UNSUPPORTED_PROPERTY +// +// MessageText: +// +// The given property is not supported.%0 +// +#define NS_E_UNSUPPORTED_PROPERTY _HRESULT_TYPEDEF_(0xC00D0BCDL) + +// +// MessageId: NS_E_8BIT_WAVE_UNSUPPORTED +// +// MessageText: +// +// Windows Media Player cannot copy the files to the CD because they are 8-bit. Convert the files to 16-bit, 44-kHz stereo files by using Sound Recorder or another audio-processing program, and then try again.%0 +// +#define NS_E_8BIT_WAVE_UNSUPPORTED _HRESULT_TYPEDEF_(0xC00D0BCEL) + +// +// MessageId: NS_E_NO_MORE_SAMPLES +// +// MessageText: +// +// There are no more samples in the current range.%0 +// +#define NS_E_NO_MORE_SAMPLES _HRESULT_TYPEDEF_(0xC00D0BCFL) + +// +// MessageId: NS_E_INVALID_SAMPLING_RATE +// +// MessageText: +// +// The given sampling rate is invalid.%0 +// +#define NS_E_INVALID_SAMPLING_RATE _HRESULT_TYPEDEF_(0xC00D0BD0L) + +// +// MessageId: NS_E_MAX_PACKET_SIZE_TOO_SMALL +// +// MessageText: +// +// The given maximum packet size is too small to accommodate this profile +// +#define NS_E_MAX_PACKET_SIZE_TOO_SMALL _HRESULT_TYPEDEF_(0xC00D0BD1L) + +// +// MessageId: NS_E_LATE_PACKET +// +// MessageText: +// +// The packet arrived too late to be of use +// +#define NS_E_LATE_PACKET _HRESULT_TYPEDEF_(0xC00D0BD2L) + +// +// MessageId: NS_E_DUPLICATE_PACKET +// +// MessageText: +// +// The packet is a duplicate of one received before +// +#define NS_E_DUPLICATE_PACKET _HRESULT_TYPEDEF_(0xC00D0BD3L) + +// +// MessageId: NS_E_SDK_BUFFERTOOSMALL +// +// MessageText: +// +// Supplied buffer is too small +// +#define NS_E_SDK_BUFFERTOOSMALL _HRESULT_TYPEDEF_(0xC00D0BD4L) + +// +// MessageId: NS_E_INVALID_NUM_PASSES +// +// MessageText: +// +// The wrong number of preprocessing passes was used for the stream's output type +// +#define NS_E_INVALID_NUM_PASSES _HRESULT_TYPEDEF_(0xC00D0BD5L) + +// +// MessageId: NS_E_ATTRIBUTE_READ_ONLY +// +// MessageText: +// +// An attempt was made to add, modify, or delete a read only attribute +// +#define NS_E_ATTRIBUTE_READ_ONLY _HRESULT_TYPEDEF_(0xC00D0BD6L) + +// +// MessageId: NS_E_ATTRIBUTE_NOT_ALLOWED +// +// MessageText: +// +// An attempt was made to add attribute that is not allowed for the given media type +// +#define NS_E_ATTRIBUTE_NOT_ALLOWED _HRESULT_TYPEDEF_(0xC00D0BD7L) + +// +// MessageId: NS_E_INVALID_EDL +// +// MessageText: +// +// The EDL provided is invalid +// +#define NS_E_INVALID_EDL _HRESULT_TYPEDEF_(0xC00D0BD8L) + +// +// MessageId: NS_E_DATA_UNIT_EXTENSION_TOO_LARGE +// +// MessageText: +// +// The Data Unit Extension data was too large to be used.%0 +// +#define NS_E_DATA_UNIT_EXTENSION_TOO_LARGE _HRESULT_TYPEDEF_(0xC00D0BD9L) + +// +// MessageId: NS_E_CODEC_DMO_ERROR +// +// MessageText: +// +// An unexpected error occurred with a DMO codec.%0 +// +#define NS_E_CODEC_DMO_ERROR _HRESULT_TYPEDEF_(0xC00D0BDAL) + + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Player Errors +// +// IdRange = 4000 - 4999 +// +///////////////////////////////////////////////////////////////////////// + +// +// WMP CD Filter Error codes +// +// +// MessageId: NS_E_NO_CD +// +// MessageText: +// +// There is no CD in the CD-ROM drive. Insert a CD, and try again.%0 +// +#define NS_E_NO_CD _HRESULT_TYPEDEF_(0xC00D0FA0L) + +// +// MessageId: NS_E_CANT_READ_DIGITAL +// +// MessageText: +// +// Unable to perform digital reads on this compact disc drive. Please try analog playback via the Tools Options menu.%0 +// +#define NS_E_CANT_READ_DIGITAL _HRESULT_TYPEDEF_(0xC00D0FA1L) + +// +// MessageId: NS_E_DEVICE_DISCONNECTED +// +// MessageText: +// +// Windows Media Player no longer detects a connected portable device. Reconnect your portable device, and then try copying the file again.%0 +// +#define NS_E_DEVICE_DISCONNECTED _HRESULT_TYPEDEF_(0xC00D0FA2L) + +// +// MessageId: NS_E_DEVICE_NOT_SUPPORT_FORMAT +// +// MessageText: +// +// Windows Media Player cannot play the file. The portable device does not support the specified format.%0 +// +#define NS_E_DEVICE_NOT_SUPPORT_FORMAT _HRESULT_TYPEDEF_(0xC00D0FA3L) + +// +// MessageId: NS_E_SLOW_READ_DIGITAL +// +// MessageText: +// +// Windows Media Player encountered a problem while attempting to play the CD using digital playback. The Player has automatically switched the CD-ROM drive to analog playback. To switch back to digital CD playback, use the Devices tab.%0 +// +#define NS_E_SLOW_READ_DIGITAL _HRESULT_TYPEDEF_(0xC00D0FA4L) + +// +// MessageId: NS_E_MIXER_INVALID_LINE +// +// MessageText: +// +// An invalid line error occurred in the mixer.%0 +// +#define NS_E_MIXER_INVALID_LINE _HRESULT_TYPEDEF_(0xC00D0FA5L) + +// +// MessageId: NS_E_MIXER_INVALID_CONTROL +// +// MessageText: +// +// An invalid control error occurred in the mixer.%0 +// +#define NS_E_MIXER_INVALID_CONTROL _HRESULT_TYPEDEF_(0xC00D0FA6L) + +// +// MessageId: NS_E_MIXER_INVALID_VALUE +// +// MessageText: +// +// An invalid value error occurred in the mixer.%0 +// +#define NS_E_MIXER_INVALID_VALUE _HRESULT_TYPEDEF_(0xC00D0FA7L) + +// +// MessageId: NS_E_MIXER_UNKNOWN_MMRESULT +// +// MessageText: +// +// An unrecognized MMRESULT occurred in the mixer.%0 +// +#define NS_E_MIXER_UNKNOWN_MMRESULT _HRESULT_TYPEDEF_(0xC00D0FA8L) + +// +// MessageId: NS_E_USER_STOP +// +// MessageText: +// +// User has stopped the operation.%0 +// +#define NS_E_USER_STOP _HRESULT_TYPEDEF_(0xC00D0FA9L) + +// +// MessageId: NS_E_MP3_FORMAT_NOT_FOUND +// +// MessageText: +// +// Windows Media Player cannot copy the file because a compatible MP3 encoder is not installed on your computer. Install a compatible MP3 encoder, or choose a different format to copy to (such as Windows Media Audio).%0 +// +#define NS_E_MP3_FORMAT_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D0FAAL) + +// +// MessageId: NS_E_CD_READ_ERROR_NO_CORRECTION +// +// MessageText: +// +// Windows Media Player cannot read the CD. It may contain flaws. Turn on error correction and try again.%0 +// +#define NS_E_CD_READ_ERROR_NO_CORRECTION _HRESULT_TYPEDEF_(0xC00D0FABL) + +// +// MessageId: NS_E_CD_READ_ERROR +// +// MessageText: +// +// Windows Media Player cannot read the CD. Be sure the CD is free of dirt and scratches and the CD-ROM drive is functioning properly.%0 +// +#define NS_E_CD_READ_ERROR _HRESULT_TYPEDEF_(0xC00D0FACL) + +// +// MessageId: NS_E_CD_SLOW_COPY +// +// MessageText: +// +// To speed up the copy process, do not play CD tracks while copying.%0 +// +#define NS_E_CD_SLOW_COPY _HRESULT_TYPEDEF_(0xC00D0FADL) + +// +// MessageId: NS_E_CD_COPYTO_CD +// +// MessageText: +// +// Cannot copy directly from a CDROM to a CD drive.%0 +// +#define NS_E_CD_COPYTO_CD _HRESULT_TYPEDEF_(0xC00D0FAEL) + +// +// MessageId: NS_E_MIXER_NODRIVER +// +// MessageText: +// +// Could not open a sound mixer driver.%0 +// +#define NS_E_MIXER_NODRIVER _HRESULT_TYPEDEF_(0xC00D0FAFL) + +// +// MessageId: NS_E_REDBOOK_ENABLED_WHILE_COPYING +// +// MessageText: +// +// Windows Media Player has detected that a setting for the CD-ROM drive will cause audio CDs to copy incorrectly; no audio is copied. Change the CD-ROM drive setting in Device Manager, and then try again.%0 +// +#define NS_E_REDBOOK_ENABLED_WHILE_COPYING _HRESULT_TYPEDEF_(0xC00D0FB0L) + +// +// MessageId: NS_E_CD_REFRESH +// +// MessageText: +// +// Trying to refresh the CD playlist.%0 +// +#define NS_E_CD_REFRESH _HRESULT_TYPEDEF_(0xC00D0FB1L) + +// +// MessageId: NS_E_CD_DRIVER_PROBLEM +// +// MessageText: +// +// Windows Media Player must switch to analog mode because there is a problem reading the CD-ROM drive in digital mode. Verify that the CD-ROM drive is installed correctly or try to update the drivers for the CD-ROM drive, and then try to use digital mode again.%0 +// +#define NS_E_CD_DRIVER_PROBLEM _HRESULT_TYPEDEF_(0xC00D0FB2L) + +// +// MessageId: NS_E_WONT_DO_DIGITAL +// +// MessageText: +// +// Windows Media Player must switch to analog mode because there is a problem reading the CD-ROM drive in digital mode.%0 +// +#define NS_E_WONT_DO_DIGITAL _HRESULT_TYPEDEF_(0xC00D0FB3L) + +// +// WMP IWMPXMLParser Error codes +// +// +// MessageId: NS_E_WMPXML_NOERROR +// +// MessageText: +// +// A call was made to GetParseError on the XML parser but there was no error to retrieve.%0 +// +#define NS_E_WMPXML_NOERROR _HRESULT_TYPEDEF_(0xC00D0FB4L) + +// +// MessageId: NS_E_WMPXML_ENDOFDATA +// +// MessageText: +// +// The XML Parser ran out of data while parsing.%0 +// +#define NS_E_WMPXML_ENDOFDATA _HRESULT_TYPEDEF_(0xC00D0FB5L) + +// +// MessageId: NS_E_WMPXML_PARSEERROR +// +// MessageText: +// +// A generic parse error occurred in the XML parser but no information is available.%0 +// +#define NS_E_WMPXML_PARSEERROR _HRESULT_TYPEDEF_(0xC00D0FB6L) + +// +// MessageId: NS_E_WMPXML_ATTRIBUTENOTFOUND +// +// MessageText: +// +// A call get GetNamedAttribute or GetNamedAttributeIndex on the XML parser resulted in the index not being found.%0 +// +#define NS_E_WMPXML_ATTRIBUTENOTFOUND _HRESULT_TYPEDEF_(0xC00D0FB7L) + +// +// MessageId: NS_E_WMPXML_PINOTFOUND +// +// MessageText: +// +// A call was made go GetNamedPI on the XML parser, but the requested Processing Instruction was not found.%0 +// +#define NS_E_WMPXML_PINOTFOUND _HRESULT_TYPEDEF_(0xC00D0FB8L) + +// +// MessageId: NS_E_WMPXML_EMPTYDOC +// +// MessageText: +// +// Persist was called on the XML parser, but the parser has no data to persist.%0 +// +#define NS_E_WMPXML_EMPTYDOC _HRESULT_TYPEDEF_(0xC00D0FB9L) + +// +// Miscellaneous Media Player Error codes +// +// +// MessageId: NS_E_WMP_WINDOWSAPIFAILURE +// +// MessageText: +// +// A Windows API call failed but no error information was available.%0 +// +#define NS_E_WMP_WINDOWSAPIFAILURE _HRESULT_TYPEDEF_(0xC00D0FC8L) + +// +// MessageId: NS_E_WMP_RECORDING_NOT_ALLOWED +// +// MessageText: +// +// Windows Media Player cannot copy the file. Either the license restricts copying, or you must obtain a license to copy the file.%0 +// +#define NS_E_WMP_RECORDING_NOT_ALLOWED _HRESULT_TYPEDEF_(0xC00D0FC9L) + +// +// MessageId: NS_E_DEVICE_NOT_READY +// +// MessageText: +// +// Windows Media Player no longer detects a connected portable device. Reconnect your portable device, and try again.%0 +// +#define NS_E_DEVICE_NOT_READY _HRESULT_TYPEDEF_(0xC00D0FCAL) + +// +// MessageId: NS_E_DAMAGED_FILE +// +// MessageText: +// +// Windows Media Player cannot play the file because it is either damaged or corrupt.%0 +// +#define NS_E_DAMAGED_FILE _HRESULT_TYPEDEF_(0xC00D0FCBL) + +// +// MessageId: NS_E_MPDB_GENERIC +// +// MessageText: +// +// An error occurred when the Player was attempting to access information in your media library. Try closing and then reopening the Player.%0 +// +#define NS_E_MPDB_GENERIC _HRESULT_TYPEDEF_(0xC00D0FCCL) + +// +// MessageId: NS_E_FILE_FAILED_CHECKS +// +// MessageText: +// +// The file cannot be added to Media Library because it is smaller than the minimum-size requirement. Adjust the size requirements, and then try again.%0 +// +#define NS_E_FILE_FAILED_CHECKS _HRESULT_TYPEDEF_(0xC00D0FCDL) + +// +// MessageId: NS_E_MEDIA_LIBRARY_FAILED +// +// MessageText: +// +// Windows Media Player could not create Media Library. Check with your system administrator to get the necessary permissions to create Media Library on your computer, and then try installing the Player again.%0 +// +#define NS_E_MEDIA_LIBRARY_FAILED _HRESULT_TYPEDEF_(0xC00D0FCEL) + +// +// MessageId: NS_E_SHARING_VIOLATION +// +// MessageText: +// +// The file is already in use. Close other programs that may be using the file, or stop playing the file, and try again.%0 +// +#define NS_E_SHARING_VIOLATION _HRESULT_TYPEDEF_(0xC00D0FCFL) + +// +// MessageId: NS_E_NO_ERROR_STRING_FOUND +// +// MessageText: +// +// Windows Media Player has encountered an unknown error.%0 +// +#define NS_E_NO_ERROR_STRING_FOUND _HRESULT_TYPEDEF_(0xC00D0FD0L) + +// +// MessageId: NS_E_WMPOCX_NO_REMOTE_CORE +// +// MessageText: +// +// The Windows Media Player control was unable to connect to remote media services, but will continue with local media services.%0 +// +#define NS_E_WMPOCX_NO_REMOTE_CORE _HRESULT_TYPEDEF_(0xC00D0FD1L) + +// +// MessageId: NS_E_WMPOCX_NO_ACTIVE_CORE +// +// MessageText: +// +// The requested method or property is not available because the Windows Media Player control has not been properly activated.%0 +// +#define NS_E_WMPOCX_NO_ACTIVE_CORE _HRESULT_TYPEDEF_(0xC00D0FD2L) + +// +// MessageId: NS_E_WMPOCX_NOT_RUNNING_REMOTELY +// +// MessageText: +// +// Windows Media Player ActiveX control is not running in remote mode.%0 +// +#define NS_E_WMPOCX_NOT_RUNNING_REMOTELY _HRESULT_TYPEDEF_(0xC00D0FD3L) + +// +// MessageId: NS_E_WMPOCX_NO_REMOTE_WINDOW +// +// MessageText: +// +// An error occurred when trying to get remote Windows Media Player window.%0 +// +#define NS_E_WMPOCX_NO_REMOTE_WINDOW _HRESULT_TYPEDEF_(0xC00D0FD4L) + +// +// MessageId: NS_E_WMPOCX_ERRORMANAGERNOTAVAILABLE +// +// MessageText: +// +// Windows Media Player has encountered an unknown error.%0 +// +#define NS_E_WMPOCX_ERRORMANAGERNOTAVAILABLE _HRESULT_TYPEDEF_(0xC00D0FD5L) + +// +// MessageId: NS_E_PLUGIN_NOTSHUTDOWN +// +// MessageText: +// +// Windows Media Player was not closed properly. A damaged or incompatible plug-in may have caused the problem to occur. As a precaution, all third-party plug-ins have been disabled.%0 +// +#define NS_E_PLUGIN_NOTSHUTDOWN _HRESULT_TYPEDEF_(0xC00D0FD6L) + +// +// MessageId: NS_E_WMP_CANNOT_FIND_FOLDER +// +// MessageText: +// +// Windows Media Player cannot find the specified path. Be sure the path is typed correctly. If it is, the path does not exist in the specified location, or the computer where the path is located is offline.%0 +// +#define NS_E_WMP_CANNOT_FIND_FOLDER _HRESULT_TYPEDEF_(0xC00D0FD7L) + +// +// MessageId: NS_E_WMP_STREAMING_RECORDING_NOT_ALLOWED +// +// MessageText: +// +// Windows Media Player cannot copy streaming media.%0 +// +#define NS_E_WMP_STREAMING_RECORDING_NOT_ALLOWED _HRESULT_TYPEDEF_(0xC00D0FD8L) + +// +// MessageId: NS_E_WMP_PLUGINDLL_NOTFOUND +// +// MessageText: +// +// Windows Media Player cannot find the selected plug-in. The Player will try to remove it from the menu. To use this plug-in, install it again.%0 +// +#define NS_E_WMP_PLUGINDLL_NOTFOUND _HRESULT_TYPEDEF_(0xC00D0FD9L) + +// +// MessageId: NS_E_NEED_TO_ASK_USER +// +// MessageText: +// +// Action requires input from the user.%0 +// +#define NS_E_NEED_TO_ASK_USER _HRESULT_TYPEDEF_(0xC00D0FDAL) + +// +// MessageId: NS_E_WMPOCX_PLAYER_NOT_DOCKED +// +// MessageText: +// +// The Windows Media Player control must be in a docked state for this action to succeed.%0 +// +#define NS_E_WMPOCX_PLAYER_NOT_DOCKED _HRESULT_TYPEDEF_(0xC00D0FDBL) + +// +// MessageId: NS_E_WMP_EXTERNAL_NOTREADY +// +// MessageText: +// +// Media Player external object is not ready.%0 +// +#define NS_E_WMP_EXTERNAL_NOTREADY _HRESULT_TYPEDEF_(0xC00D0FDCL) + +// +// MessageId: NS_E_WMP_MLS_STALE_DATA +// +// MessageText: +// +// Metadata is stale. The operation failed.%0 +// +#define NS_E_WMP_MLS_STALE_DATA _HRESULT_TYPEDEF_(0xC00D0FDDL) + +// +// Generic Media PlayerUI error codes +// +// +// MessageId: NS_E_WMP_UI_SUBCONTROLSNOTSUPPORTED +// +// MessageText: +// +// The control (%s) does not support creation of sub-controls, yet (%d) sub-controls have been specified.%0 +// +#define NS_E_WMP_UI_SUBCONTROLSNOTSUPPORTED _HRESULT_TYPEDEF_(0xC00D0FDEL) + +// +// MessageId: NS_E_WMP_UI_VERSIONMISMATCH +// +// MessageText: +// +// Version mismatch: (%.1f required, %.1f found).%0 +// +#define NS_E_WMP_UI_VERSIONMISMATCH _HRESULT_TYPEDEF_(0xC00D0FDFL) + +// +// MessageId: NS_E_WMP_UI_NOTATHEMEFILE +// +// MessageText: +// +// The layout manager was given valid XML that wasn't a theme file.%0 +// +#define NS_E_WMP_UI_NOTATHEMEFILE _HRESULT_TYPEDEF_(0xC00D0FE0L) + +// +// MessageId: NS_E_WMP_UI_SUBELEMENTNOTFOUND +// +// MessageText: +// +// The %s subelement could not be found on the %s object.%0 +// +#define NS_E_WMP_UI_SUBELEMENTNOTFOUND _HRESULT_TYPEDEF_(0xC00D0FE1L) + +// +// MessageId: NS_E_WMP_UI_VERSIONPARSE +// +// MessageText: +// +// An error occurred parsing the version tag.\nValid version tags are of the form:\n\n\t<?wmp version='1.0'?>.%0 +// +#define NS_E_WMP_UI_VERSIONPARSE _HRESULT_TYPEDEF_(0xC00D0FE2L) + +// +// MessageId: NS_E_WMP_UI_VIEWIDNOTFOUND +// +// MessageText: +// +// The view specified in for the 'currentViewID' property (%s) was not found in this theme file.%0 +// +#define NS_E_WMP_UI_VIEWIDNOTFOUND _HRESULT_TYPEDEF_(0xC00D0FE3L) + +// +// MessageId: NS_E_WMP_UI_PASSTHROUGH +// +// MessageText: +// +// This error used internally for hit testing.%0 +// +#define NS_E_WMP_UI_PASSTHROUGH _HRESULT_TYPEDEF_(0xC00D0FE4L) + +// +// MessageId: NS_E_WMP_UI_OBJECTNOTFOUND +// +// MessageText: +// +// Attributes were specified for the %s object, but the object was not available to send them to.%0 +// +#define NS_E_WMP_UI_OBJECTNOTFOUND _HRESULT_TYPEDEF_(0xC00D0FE5L) + +// +// MessageId: NS_E_WMP_UI_SECONDHANDLER +// +// MessageText: +// +// The %s event already has a handler, the second handler was ignored.%0 +// +#define NS_E_WMP_UI_SECONDHANDLER _HRESULT_TYPEDEF_(0xC00D0FE6L) + +// +// MessageId: NS_E_WMP_UI_NOSKININZIP +// +// MessageText: +// +// No .wms file found in skin archive.%0 +// +#define NS_E_WMP_UI_NOSKININZIP _HRESULT_TYPEDEF_(0xC00D0FE7L) + +// +// MessageId: NS_S_WMP_UI_VERSIONMISMATCH +// +// MessageText: +// +// An upgrade may be needed for the theme manager to correctly show this skin. Skin reports version: %.1f.%0 +// +#define NS_S_WMP_UI_VERSIONMISMATCH _HRESULT_TYPEDEF_(0x000D0FE8L) + +// +// MessageId: NS_S_WMP_EXCEPTION +// +// MessageText: +// +// An error occurred in one of the UI components.%0 +// +#define NS_S_WMP_EXCEPTION _HRESULT_TYPEDEF_(0x000D0FE9L) + +// +// MessageId: NS_E_WMP_URLDOWNLOADFAILED +// +// MessageText: +// +// Windows Media Player cannot save the file.%0 +// +#define NS_E_WMP_URLDOWNLOADFAILED _HRESULT_TYPEDEF_(0xC00D0FEAL) + +// +// MessageId: NS_E_WMPOCX_UNABLE_TO_LOAD_SKIN +// +// MessageText: +// +// The Windows Media Player Control was unable to load the requested uiMode and could not successfully roll back to the existing uiMode.%0 +// +#define NS_E_WMPOCX_UNABLE_TO_LOAD_SKIN _HRESULT_TYPEDEF_(0xC00D0FEBL) + +// +// MessageId: NS_E_WMP_INVALID_SKIN +// +// MessageText: +// +// The skin file is invalid.%0 +// +#define NS_E_WMP_INVALID_SKIN _HRESULT_TYPEDEF_(0xC00D0FECL) + +// +// MessageId: NS_E_WMP_SENDMAILFAILED +// +// MessageText: +// +// Windows Media Player cannot send the link because your e-mail program is not responding. Verify that your e-mail program is configured properly, and then try again. For more information about e-mail, see Windows Help%0 +// +#define NS_E_WMP_SENDMAILFAILED _HRESULT_TYPEDEF_(0xC00D0FEDL) + +//Save As +// +// MessageId: NS_E_WMP_SAVEAS_READONLY +// +// MessageText: +// +// The Windows Media Player cannot overwrite a read only file. Choose another file to save as or change the file attributes.%0 +// +#define NS_E_WMP_SAVEAS_READONLY _HRESULT_TYPEDEF_(0xC00D0FF0L) + +// +// WMP Regional button control +// +// +// MessageId: NS_E_WMP_RBC_JPGMAPPINGIMAGE +// +// MessageText: +// +// JPG Images are not recommended for use as a mappingImage.%0 +// +#define NS_E_WMP_RBC_JPGMAPPINGIMAGE _HRESULT_TYPEDEF_(0xC00D1004L) + +// +// MessageId: NS_E_WMP_JPGTRANSPARENCY +// +// MessageText: +// +// JPG Images are not recommended when using a transparencyColor.%0 +// +#define NS_E_WMP_JPGTRANSPARENCY _HRESULT_TYPEDEF_(0xC00D1005L) + +// +// WMP Slider control +// +// +// MessageId: NS_E_WMP_INVALID_MAX_VAL +// +// MessageText: +// +// The Max property cannot be less than Min property.%0 +// +#define NS_E_WMP_INVALID_MAX_VAL _HRESULT_TYPEDEF_(0xC00D1009L) + +// +// MessageId: NS_E_WMP_INVALID_MIN_VAL +// +// MessageText: +// +// The Min property cannot be greater than Max property.%0 +// +#define NS_E_WMP_INVALID_MIN_VAL _HRESULT_TYPEDEF_(0xC00D100AL) + +// +// WMP CustomSlider control +// +// +// MessageId: NS_E_WMP_CS_JPGPOSITIONIMAGE +// +// MessageText: +// +// JPG Images are not recommended for use as a positionImage.%0 +// +#define NS_E_WMP_CS_JPGPOSITIONIMAGE _HRESULT_TYPEDEF_(0xC00D100EL) + +// +// MessageId: NS_E_WMP_CS_NOTEVENLYDIVISIBLE +// +// MessageText: +// +// The (%s) image's size is not evenly divisible by the positionImage's size.%0 +// +#define NS_E_WMP_CS_NOTEVENLYDIVISIBLE _HRESULT_TYPEDEF_(0xC00D100FL) + +// +// WMP ZIP Decoder +// +// +// MessageId: NS_E_WMPZIP_NOTAZIPFILE +// +// MessageText: +// +// The ZIP reader opened a file and its signature didn't match that of ZIP files.%0 +// +#define NS_E_WMPZIP_NOTAZIPFILE _HRESULT_TYPEDEF_(0xC00D1018L) + +// +// MessageId: NS_E_WMPZIP_CORRUPT +// +// MessageText: +// +// The ZIP reader has detected that the file is corrupt.%0 +// +#define NS_E_WMPZIP_CORRUPT _HRESULT_TYPEDEF_(0xC00D1019L) + +// +// MessageId: NS_E_WMPZIP_FILENOTFOUND +// +// MessageText: +// +// GetFileStream, SaveToFile, or SaveTemp file was called on the ZIP reader with a filename that was not found in the zip file.%0 +// +#define NS_E_WMPZIP_FILENOTFOUND _HRESULT_TYPEDEF_(0xC00D101AL) + +// +// WMP Image Decoding Error codes +// +// +// MessageId: NS_E_WMP_IMAGE_FILETYPE_UNSUPPORTED +// +// MessageText: +// +// Image type not supported.%0 +// +#define NS_E_WMP_IMAGE_FILETYPE_UNSUPPORTED _HRESULT_TYPEDEF_(0xC00D1022L) + +// +// MessageId: NS_E_WMP_IMAGE_INVALID_FORMAT +// +// MessageText: +// +// Image file may be corrupt.%0 +// +#define NS_E_WMP_IMAGE_INVALID_FORMAT _HRESULT_TYPEDEF_(0xC00D1023L) + +// +// MessageId: NS_E_WMP_GIF_UNEXPECTED_ENDOFFILE +// +// MessageText: +// +// Unexpected end of file. GIF file may be corrupt.%0 +// +#define NS_E_WMP_GIF_UNEXPECTED_ENDOFFILE _HRESULT_TYPEDEF_(0xC00D1024L) + +// +// MessageId: NS_E_WMP_GIF_INVALID_FORMAT +// +// MessageText: +// +// Invalid GIF file.%0 +// +#define NS_E_WMP_GIF_INVALID_FORMAT _HRESULT_TYPEDEF_(0xC00D1025L) + +// +// MessageId: NS_E_WMP_GIF_BAD_VERSION_NUMBER +// +// MessageText: +// +// Invalid GIF version. Only 87a or 89a supported.%0 +// +#define NS_E_WMP_GIF_BAD_VERSION_NUMBER _HRESULT_TYPEDEF_(0xC00D1026L) + +// +// MessageId: NS_E_WMP_GIF_NO_IMAGE_IN_FILE +// +// MessageText: +// +// No images found in GIF file.%0 +// +#define NS_E_WMP_GIF_NO_IMAGE_IN_FILE _HRESULT_TYPEDEF_(0xC00D1027L) + +// +// MessageId: NS_E_WMP_PNG_INVALIDFORMAT +// +// MessageText: +// +// Invalid PNG image file format.%0 +// +#define NS_E_WMP_PNG_INVALIDFORMAT _HRESULT_TYPEDEF_(0xC00D1028L) + +// +// MessageId: NS_E_WMP_PNG_UNSUPPORTED_BITDEPTH +// +// MessageText: +// +// PNG bitdepth not supported.%0 +// +#define NS_E_WMP_PNG_UNSUPPORTED_BITDEPTH _HRESULT_TYPEDEF_(0xC00D1029L) + +// +// MessageId: NS_E_WMP_PNG_UNSUPPORTED_COMPRESSION +// +// MessageText: +// +// Compression format defined in PNG file not supported,%0 +// +#define NS_E_WMP_PNG_UNSUPPORTED_COMPRESSION _HRESULT_TYPEDEF_(0xC00D102AL) + +// +// MessageId: NS_E_WMP_PNG_UNSUPPORTED_FILTER +// +// MessageText: +// +// Filter method defined in PNG file not supported.%0 +// +#define NS_E_WMP_PNG_UNSUPPORTED_FILTER _HRESULT_TYPEDEF_(0xC00D102BL) + +// +// MessageId: NS_E_WMP_PNG_UNSUPPORTED_INTERLACE +// +// MessageText: +// +// Interlace method defined in PNG file not supported.%0 +// +#define NS_E_WMP_PNG_UNSUPPORTED_INTERLACE _HRESULT_TYPEDEF_(0xC00D102CL) + +// +// MessageId: NS_E_WMP_PNG_UNSUPPORTED_BAD_CRC +// +// MessageText: +// +// Bad CRC in PNG file.%0 +// +#define NS_E_WMP_PNG_UNSUPPORTED_BAD_CRC _HRESULT_TYPEDEF_(0xC00D102DL) + +// +// MessageId: NS_E_WMP_BMP_INVALID_BITMASK +// +// MessageText: +// +// Invalid bitmask in BMP file.%0 +// +#define NS_E_WMP_BMP_INVALID_BITMASK _HRESULT_TYPEDEF_(0xC00D102EL) + +// +// MessageId: NS_E_WMP_BMP_TOPDOWN_DIB_UNSUPPORTED +// +// MessageText: +// +// Topdown DIB not supported.%0 +// +#define NS_E_WMP_BMP_TOPDOWN_DIB_UNSUPPORTED _HRESULT_TYPEDEF_(0xC00D102FL) + +// +// MessageId: NS_E_WMP_BMP_BITMAP_NOT_CREATED +// +// MessageText: +// +// Bitmap could not be created.%0 +// +#define NS_E_WMP_BMP_BITMAP_NOT_CREATED _HRESULT_TYPEDEF_(0xC00D1030L) + +// +// MessageId: NS_E_WMP_BMP_COMPRESSION_UNSUPPORTED +// +// MessageText: +// +// Compression format defined in BMP not supported.%0 +// +#define NS_E_WMP_BMP_COMPRESSION_UNSUPPORTED _HRESULT_TYPEDEF_(0xC00D1031L) + +// +// MessageId: NS_E_WMP_BMP_INVALID_FORMAT +// +// MessageText: +// +// Invalid Bitmap format.%0 +// +#define NS_E_WMP_BMP_INVALID_FORMAT _HRESULT_TYPEDEF_(0xC00D1032L) + +// +// MessageId: NS_E_WMP_JPG_JERR_ARITHCODING_NOTIMPL +// +// MessageText: +// +// JPEG Arithmetic coding not supported.%0 +// +#define NS_E_WMP_JPG_JERR_ARITHCODING_NOTIMPL _HRESULT_TYPEDEF_(0xC00D1033L) + +// +// MessageId: NS_E_WMP_JPG_INVALID_FORMAT +// +// MessageText: +// +// Invalid JPEG format.%0 +// +#define NS_E_WMP_JPG_INVALID_FORMAT _HRESULT_TYPEDEF_(0xC00D1034L) + +// +// MessageId: NS_E_WMP_JPG_BAD_DCTSIZE +// +// MessageText: +// +// Invalid JPEG format.%0 +// +#define NS_E_WMP_JPG_BAD_DCTSIZE _HRESULT_TYPEDEF_(0xC00D1035L) + +// +// MessageId: NS_E_WMP_JPG_BAD_VERSION_NUMBER +// +// MessageText: +// +// Internal version error. Unexpected JPEG library version.%0 +// +#define NS_E_WMP_JPG_BAD_VERSION_NUMBER _HRESULT_TYPEDEF_(0xC00D1036L) + +// +// MessageId: NS_E_WMP_JPG_BAD_PRECISION +// +// MessageText: +// +// Internal JPEG Library error. Unsupported JPEG data precision.%0 +// +#define NS_E_WMP_JPG_BAD_PRECISION _HRESULT_TYPEDEF_(0xC00D1037L) + +// +// MessageId: NS_E_WMP_JPG_CCIR601_NOTIMPL +// +// MessageText: +// +// JPEG CCIR601 not supported.%0 +// +#define NS_E_WMP_JPG_CCIR601_NOTIMPL _HRESULT_TYPEDEF_(0xC00D1038L) + +// +// MessageId: NS_E_WMP_JPG_NO_IMAGE_IN_FILE +// +// MessageText: +// +// No image found in JPEG file.%0 +// +#define NS_E_WMP_JPG_NO_IMAGE_IN_FILE _HRESULT_TYPEDEF_(0xC00D1039L) + +// +// MessageId: NS_E_WMP_JPG_READ_ERROR +// +// MessageText: +// +// Could not read JPEG file.%0 +// +#define NS_E_WMP_JPG_READ_ERROR _HRESULT_TYPEDEF_(0xC00D103AL) + +// +// MessageId: NS_E_WMP_JPG_FRACT_SAMPLE_NOTIMPL +// +// MessageText: +// +// JPEG Fractional sampling not supported.%0 +// +#define NS_E_WMP_JPG_FRACT_SAMPLE_NOTIMPL _HRESULT_TYPEDEF_(0xC00D103BL) + +// +// MessageId: NS_E_WMP_JPG_IMAGE_TOO_BIG +// +// MessageText: +// +// JPEG image too large. Maximum image size supported is 65500 X 65500.%0 +// +#define NS_E_WMP_JPG_IMAGE_TOO_BIG _HRESULT_TYPEDEF_(0xC00D103CL) + +// +// MessageId: NS_E_WMP_JPG_UNEXPECTED_ENDOFFILE +// +// MessageText: +// +// Unexpected end of file reached in JPEG file.%0 +// +#define NS_E_WMP_JPG_UNEXPECTED_ENDOFFILE _HRESULT_TYPEDEF_(0xC00D103DL) + +// +// MessageId: NS_E_WMP_JPG_SOF_UNSUPPORTED +// +// MessageText: +// +// Unsupported JPEG SOF marker found.%0 +// +#define NS_E_WMP_JPG_SOF_UNSUPPORTED _HRESULT_TYPEDEF_(0xC00D103EL) + +// +// MessageId: NS_E_WMP_JPG_UNKNOWN_MARKER +// +// MessageText: +// +// Unknown JPEG marker found.%0 +// +#define NS_E_WMP_JPG_UNKNOWN_MARKER _HRESULT_TYPEDEF_(0xC00D103FL) + +// +// MessageId: NS_S_WMP_LOADED_GIF_IMAGE +// +// MessageText: +// +// Successfully loaded a GIF file.%0 +// +#define NS_S_WMP_LOADED_GIF_IMAGE _HRESULT_TYPEDEF_(0x000D1040L) + +// +// MessageId: NS_S_WMP_LOADED_PNG_IMAGE +// +// MessageText: +// +// Successfully loaded a PNG file.%0 +// +#define NS_S_WMP_LOADED_PNG_IMAGE _HRESULT_TYPEDEF_(0x000D1041L) + +// +// MessageId: NS_S_WMP_LOADED_BMP_IMAGE +// +// MessageText: +// +// Successfully loaded a BMP file.%0 +// +#define NS_S_WMP_LOADED_BMP_IMAGE _HRESULT_TYPEDEF_(0x000D1042L) + +// +// MessageId: NS_S_WMP_LOADED_JPG_IMAGE +// +// MessageText: +// +// Successfully loaded a JPG file.%0 +// +#define NS_S_WMP_LOADED_JPG_IMAGE _HRESULT_TYPEDEF_(0x000D1043L) + +// +// WMP WM Runtime Error codes +// +// +// MessageId: NS_E_WMG_RATEUNAVAILABLE +// +// MessageText: +// +// The requested playback rate is unavailable on this content.%0 +// +#define NS_E_WMG_RATEUNAVAILABLE _HRESULT_TYPEDEF_(0xC00D104AL) + +// +// MessageId: NS_E_WMG_PLUGINUNAVAILABLE +// +// MessageText: +// +// The rendering or digital signal processing plugin could not be instantiated.%0 +// +#define NS_E_WMG_PLUGINUNAVAILABLE _HRESULT_TYPEDEF_(0xC00D104BL) + +// +// MessageId: NS_E_WMG_CANNOTQUEUE +// +// MessageText: +// +// The file cannot be queued for seamless playback.%0 +// +#define NS_E_WMG_CANNOTQUEUE _HRESULT_TYPEDEF_(0xC00D104CL) + +// +// MessageId: NS_E_WMG_PREROLLLICENSEACQUISITIONNOTALLOWED +// +// MessageText: +// +// Windows Media Player cannot acquire the license for a file that is being prerolled.%0 +// +#define NS_E_WMG_PREROLLLICENSEACQUISITIONNOTALLOWED _HRESULT_TYPEDEF_(0xC00D104DL) + +// +// MessageId: NS_E_WMG_UNEXPECTEDPREROLLSTATUS +// +// MessageText: +// +// Windows Media Player received an unexpected message while attempting to preroll a file.%0 +// +#define NS_E_WMG_UNEXPECTEDPREROLLSTATUS _HRESULT_TYPEDEF_(0xC00D104EL) + +// +// MessageId: NS_E_WMG_INVALIDSTATE +// +// MessageText: +// +// Operation attempted in an invalid graph state.%0 +// +#define NS_E_WMG_INVALIDSTATE _HRESULT_TYPEDEF_(0xC00D1054L) + +// +// MessageId: NS_E_WMG_SINKALREADYEXISTS +// +// MessageText: +// +// A renderer cannot be inserted in a stream while one already exists.%0 +// +#define NS_E_WMG_SINKALREADYEXISTS _HRESULT_TYPEDEF_(0xC00D1055L) + +// +// MessageId: NS_E_WMG_NOSDKINTERFACE +// +// MessageText: +// +// A necessary WM SDK interface to complete the operation doesn't exist at this time.%0 +// +#define NS_E_WMG_NOSDKINTERFACE _HRESULT_TYPEDEF_(0xC00D1056L) + +// +// MessageId: NS_E_WMG_NOTALLOUTPUTSRENDERED +// +// MessageText: +// +// Windows Media Player cannot play the file. The file may be formatted with an unsupported codec, or the Player could not download the codec.%0 +// +#define NS_E_WMG_NOTALLOUTPUTSRENDERED _HRESULT_TYPEDEF_(0xC00D1057L) + +// +// MessageId: NS_E_WMG_FILETRANSFERNOTALLOWED +// +// MessageText: +// +// File transfer streams are not allowed in the stand alone player.%0 +// +#define NS_E_WMG_FILETRANSFERNOTALLOWED _HRESULT_TYPEDEF_(0xC00D1058L) + +// +// MessageId: NS_E_WMR_UNSUPPORTEDSTREAM +// +// MessageText: +// +// Windows Media Player cannot play the file. The Player does not support the format you are trying to play.%0 +// +#define NS_E_WMR_UNSUPPORTEDSTREAM _HRESULT_TYPEDEF_(0xC00D1059L) + +// +// MessageId: NS_E_WMR_PINNOTFOUND +// +// MessageText: +// +// An operation was attempted on a pin that doesn't exist in the DirectShow filter graph.%0 +// +#define NS_E_WMR_PINNOTFOUND _HRESULT_TYPEDEF_(0xC00D105AL) + +// +// MessageId: NS_E_WMR_WAITINGONFORMATSWITCH +// +// MessageText: +// +// Specified operation cannot be completed while waiting for a media format change from the SDK.%0 +// +#define NS_E_WMR_WAITINGONFORMATSWITCH _HRESULT_TYPEDEF_(0xC00D105BL) + +// +// MessageId: NS_E_WMR_NOSOURCEFILTER +// +// MessageText: +// +// Specified operation cannot be completed because the source filter does not exist.%0 +// +#define NS_E_WMR_NOSOURCEFILTER _HRESULT_TYPEDEF_(0xC00D105CL) + +// +// MessageId: NS_E_WMR_PINTYPENOMATCH +// +// MessageText: +// +// The specified type does not match this pin.%0 +// +#define NS_E_WMR_PINTYPENOMATCH _HRESULT_TYPEDEF_(0xC00D105DL) + +// +// MessageId: NS_E_WMR_NOCALLBACKAVAILABLE +// +// MessageText: +// +// The WMR Source Filter does not have a callback available.%0 +// +#define NS_E_WMR_NOCALLBACKAVAILABLE _HRESULT_TYPEDEF_(0xC00D105EL) + +// +// MessageId: NS_S_WMR_ALREADYRENDERED +// +// MessageText: +// +// The specified stream has already been rendered.%0 +// +#define NS_S_WMR_ALREADYRENDERED _HRESULT_TYPEDEF_(0x000D105FL) + +// +// MessageId: NS_S_WMR_PINTYPEPARTIALMATCH +// +// MessageText: +// +// The specified type partially matches this pin type.%0 +// +#define NS_S_WMR_PINTYPEPARTIALMATCH _HRESULT_TYPEDEF_(0x000D1060L) + +// +// MessageId: NS_S_WMR_PINTYPEFULLMATCH +// +// MessageText: +// +// The specified type fully matches this pin type.%0 +// +#define NS_S_WMR_PINTYPEFULLMATCH _HRESULT_TYPEDEF_(0x000D1061L) + +// +// MessageId: NS_E_WMR_SAMPLEPROPERTYNOTSET +// +// MessageText: +// +// The specified property has not been set on this sample.%0 +// +#define NS_E_WMR_SAMPLEPROPERTYNOTSET _HRESULT_TYPEDEF_(0xC00D1062L) + +// +// MessageId: NS_E_WMR_CANNOT_RENDER_BINARY_STREAM +// +// MessageText: +// +// A plugin is required to correctly play this file. To determine if this plugin is available to download from the Web, click Web Help.%0 +// +#define NS_E_WMR_CANNOT_RENDER_BINARY_STREAM _HRESULT_TYPEDEF_(0xC00D1063L) + +// +// MessageId: NS_E_WMG_LICENSE_TAMPERED +// +// MessageText: +// +// The file cannot be played, the content has been tampered.%0 +// +#define NS_E_WMG_LICENSE_TAMPERED _HRESULT_TYPEDEF_(0xC00D1064L) + +// +// MessageId: NS_E_WMR_WILLNOT_RENDER_BINARY_STREAM +// +// MessageText: +// +// The content you are trying to play is protected content and the player will not render binary streams from protected content.%0 +// +#define NS_E_WMR_WILLNOT_RENDER_BINARY_STREAM _HRESULT_TYPEDEF_(0xC00D1065L) + +// +// WMP Playlist Error codes +// +// +// MessageId: NS_E_WMX_UNRECOGNIZED_PLAYLIST_FORMAT +// +// MessageText: +// +// The format of this file was not recognized as a valid playlist format.%0 +// +#define NS_E_WMX_UNRECOGNIZED_PLAYLIST_FORMAT _HRESULT_TYPEDEF_(0xC00D1068L) + +// +// MessageId: NS_E_ASX_INVALIDFORMAT +// +// MessageText: +// +// This file was believed to be an ASX playlist, but the format was not recognized.%0 +// +#define NS_E_ASX_INVALIDFORMAT _HRESULT_TYPEDEF_(0xC00D1069L) + +// +// MessageId: NS_E_ASX_INVALIDVERSION +// +// MessageText: +// +// The version of this playlist is not supported. Click More Information to go to the Microsoft web site and see if there is a newer version of the player to install.%0 +// +#define NS_E_ASX_INVALIDVERSION _HRESULT_TYPEDEF_(0xC00D106AL) + +// +// MessageId: NS_E_ASX_INVALID_REPEAT_BLOCK +// +// MessageText: +// +// Format of a REPEAT loop within the current playlist file is invalid.%0 +// +#define NS_E_ASX_INVALID_REPEAT_BLOCK _HRESULT_TYPEDEF_(0xC00D106BL) + +// +// MessageId: NS_E_ASX_NOTHING_TO_WRITE +// +// MessageText: +// +// Windows Media Player cannot export the playlist because it is empty.%0 +// +#define NS_E_ASX_NOTHING_TO_WRITE _HRESULT_TYPEDEF_(0xC00D106CL) + +// +// MessageId: NS_E_URLLIST_INVALIDFORMAT +// +// MessageText: +// +// Windows Media Player does not recognize this file as a supported playlist.%0 +// +#define NS_E_URLLIST_INVALIDFORMAT _HRESULT_TYPEDEF_(0xC00D106DL) + +// +// MessageId: NS_E_WMX_ATTRIBUTE_DOES_NOT_EXIST +// +// MessageText: +// +// The specified attribute does not exist.%0 +// +#define NS_E_WMX_ATTRIBUTE_DOES_NOT_EXIST _HRESULT_TYPEDEF_(0xC00D106EL) + +// +// MessageId: NS_E_WMX_ATTRIBUTE_ALREADY_EXISTS +// +// MessageText: +// +// The specified attribute already exists.%0 +// +#define NS_E_WMX_ATTRIBUTE_ALREADY_EXISTS _HRESULT_TYPEDEF_(0xC00D106FL) + +// +// MessageId: NS_E_WMX_ATTRIBUTE_UNRETRIEVABLE +// +// MessageText: +// +// Can not retrieve the specified attribute.%0 +// +#define NS_E_WMX_ATTRIBUTE_UNRETRIEVABLE _HRESULT_TYPEDEF_(0xC00D1070L) + +// +// MessageId: NS_E_WMX_ITEM_DOES_NOT_EXIST +// +// MessageText: +// +// The specified item does not exist in the current playlist.%0 +// +#define NS_E_WMX_ITEM_DOES_NOT_EXIST _HRESULT_TYPEDEF_(0xC00D1071L) + +// +// MessageId: NS_E_WMX_ITEM_TYPE_ILLEGAL +// +// MessageText: +// +// Items of the specified type can not be created within the current playlist.%0 +// +#define NS_E_WMX_ITEM_TYPE_ILLEGAL _HRESULT_TYPEDEF_(0xC00D1072L) + +// +// MessageId: NS_E_WMX_ITEM_UNSETTABLE +// +// MessageText: +// +// The specified item can not be set in the current playlist.%0 +// +#define NS_E_WMX_ITEM_UNSETTABLE _HRESULT_TYPEDEF_(0xC00D1073L) + +// +// MessageId: NS_E_WMX_PLAYLIST_EMPTY +// +// MessageText: +// +// The specified playlist is empty.%0 +// +#define NS_E_WMX_PLAYLIST_EMPTY _HRESULT_TYPEDEF_(0xC00D1074L) + +// +// MessageId: NS_E_MLS_SMARTPLAYLIST_FILTER_NOT_REGISTERED +// +// MessageText: +// +// Playlist load error: The specified autoplaylist contains a filter type which is either invalid or is not installed on this computer%0 +// +#define NS_E_MLS_SMARTPLAYLIST_FILTER_NOT_REGISTERED _HRESULT_TYPEDEF_(0xC00D1075L) + +// +// MessageId: NS_E_WMX_INVALID_FORMAT_OVER_NESTING +// +// MessageText: +// +// Windows Media Player cannot play the file because the associated Windows Media metafile playlist is not valid.%0 +// +#define NS_E_WMX_INVALID_FORMAT_OVER_NESTING _HRESULT_TYPEDEF_(0xC00D1076L) + +// +// WMP Core Error codes +// +// +// MessageId: NS_E_WMPCORE_NOSOURCEURLSTRING +// +// MessageText: +// +// Windows Media Player cannot find the file. Be sure the path is typed correctly. If it is, the file may not exist in the specified location, or the computer where the file is stored may be offline.%0 +// +#define NS_E_WMPCORE_NOSOURCEURLSTRING _HRESULT_TYPEDEF_(0xC00D107CL) + +// +// MessageId: NS_E_WMPCORE_COCREATEFAILEDFORGITOBJECT +// +// MessageText: +// +// Failed to create the Global Interface Table.%0 +// +#define NS_E_WMPCORE_COCREATEFAILEDFORGITOBJECT _HRESULT_TYPEDEF_(0xC00D107DL) + +// +// MessageId: NS_E_WMPCORE_FAILEDTOGETMARSHALLEDEVENTHANDLERINTERFACE +// +// MessageText: +// +// Failed to get the marshaled graph event handler interface.%0 +// +#define NS_E_WMPCORE_FAILEDTOGETMARSHALLEDEVENTHANDLERINTERFACE _HRESULT_TYPEDEF_(0xC00D107EL) + +// +// MessageId: NS_E_WMPCORE_BUFFERTOOSMALL +// +// MessageText: +// +// Buffer is too small for copying media type.%0 +// +#define NS_E_WMPCORE_BUFFERTOOSMALL _HRESULT_TYPEDEF_(0xC00D107FL) + +// +// MessageId: NS_E_WMPCORE_UNAVAILABLE +// +// MessageText: +// +// Current state of the player does not allow the operation.%0 +// +#define NS_E_WMPCORE_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D1080L) + +// +// MessageId: NS_E_WMPCORE_INVALIDPLAYLISTMODE +// +// MessageText: +// +// Playlist manager does not understand the current play mode (shuffle, normal etc).%0 +// +#define NS_E_WMPCORE_INVALIDPLAYLISTMODE _HRESULT_TYPEDEF_(0xC00D1081L) + +// +// MessageId: NS_E_WMPCORE_ITEMNOTINPLAYLIST +// +// MessageText: +// +// The item is not in the playlist.%0 +// +#define NS_E_WMPCORE_ITEMNOTINPLAYLIST _HRESULT_TYPEDEF_(0xC00D1086L) + +// +// MessageId: NS_E_WMPCORE_PLAYLISTEMPTY +// +// MessageText: +// +// There are no items in this playlist. Add items to the playlist, and try again.%0 +// +#define NS_E_WMPCORE_PLAYLISTEMPTY _HRESULT_TYPEDEF_(0xC00D1087L) + +// +// MessageId: NS_E_WMPCORE_NOBROWSER +// +// MessageText: +// +// The Web site cannot be accessed. A Web browser is not detected on your computer.%0 +// +#define NS_E_WMPCORE_NOBROWSER _HRESULT_TYPEDEF_(0xC00D1088L) + +// +// MessageId: NS_E_WMPCORE_UNRECOGNIZED_MEDIA_URL +// +// MessageText: +// +// Windows Media Player cannot find the specified file. Be sure the path is typed correctly. If it is, the file does not exist in the specified location, or the computer where the file is stored is offline.%0 +// +#define NS_E_WMPCORE_UNRECOGNIZED_MEDIA_URL _HRESULT_TYPEDEF_(0xC00D1089L) + +// +// MessageId: NS_E_WMPCORE_GRAPH_NOT_IN_LIST +// +// MessageText: +// +// Graph with the specified URL was not found in the prerolled graph list.%0 +// +#define NS_E_WMPCORE_GRAPH_NOT_IN_LIST _HRESULT_TYPEDEF_(0xC00D108AL) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_EMPTY_OR_SINGLE_MEDIA +// +// MessageText: +// +// There is only one item in the playlist.%0 +// +#define NS_E_WMPCORE_PLAYLIST_EMPTY_OR_SINGLE_MEDIA _HRESULT_TYPEDEF_(0xC00D108BL) + +// +// MessageId: NS_E_WMPCORE_ERRORSINKNOTREGISTERED +// +// MessageText: +// +// An error sink was never registered for the calling object.%0 +// +#define NS_E_WMPCORE_ERRORSINKNOTREGISTERED _HRESULT_TYPEDEF_(0xC00D108CL) + +// +// MessageId: NS_E_WMPCORE_ERRORMANAGERNOTAVAILABLE +// +// MessageText: +// +// The error manager is not available to respond to errors.%0 +// +#define NS_E_WMPCORE_ERRORMANAGERNOTAVAILABLE _HRESULT_TYPEDEF_(0xC00D108DL) + +// +// MessageId: NS_E_WMPCORE_WEBHELPFAILED +// +// MessageText: +// +// Failed launching WebHelp URL.%0 +// +#define NS_E_WMPCORE_WEBHELPFAILED _HRESULT_TYPEDEF_(0xC00D108EL) + +// +// MessageId: NS_E_WMPCORE_MEDIA_ERROR_RESUME_FAILED +// +// MessageText: +// +// Could not resume playing next item in playlist.%0 +// +#define NS_E_WMPCORE_MEDIA_ERROR_RESUME_FAILED _HRESULT_TYPEDEF_(0xC00D108FL) + +// +// MessageId: NS_E_WMPCORE_NO_REF_IN_ENTRY +// +// MessageText: +// +// Windows Media Player cannot play the file because the associated Windows Media metafile playlist is not valid.%0 +// +#define NS_E_WMPCORE_NO_REF_IN_ENTRY _HRESULT_TYPEDEF_(0xC00D1090L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_NAME_EMPTY +// +// MessageText: +// +// An empty string for playlist attribute name was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_NAME_EMPTY _HRESULT_TYPEDEF_(0xC00D1091L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_NAME_ILLEGAL +// +// MessageText: +// +// An invalid playlist attribute name was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_NAME_ILLEGAL _HRESULT_TYPEDEF_(0xC00D1092L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_VALUE_EMPTY +// +// MessageText: +// +// An empty string for a playlist attribute value was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_VALUE_EMPTY _HRESULT_TYPEDEF_(0xC00D1093L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_VALUE_ILLEGAL +// +// MessageText: +// +// An illegal value for a playlist attribute was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ATTRIBUTE_VALUE_ILLEGAL _HRESULT_TYPEDEF_(0xC00D1094L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ITEM_ATTRIBUTE_NAME_EMPTY +// +// MessageText: +// +// An empty string for a playlist item attribute name was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ITEM_ATTRIBUTE_NAME_EMPTY _HRESULT_TYPEDEF_(0xC00D1095L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ITEM_ATTRIBUTE_NAME_ILLEGAL +// +// MessageText: +// +// An illegal value for a playlist item attribute name was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ITEM_ATTRIBUTE_NAME_ILLEGAL _HRESULT_TYPEDEF_(0xC00D1096L) + +// +// MessageId: NS_E_WMPCORE_WMX_LIST_ITEM_ATTRIBUTE_VALUE_EMPTY +// +// MessageText: +// +// An illegal value for a playlist item attribute was found.%0 +// +#define NS_E_WMPCORE_WMX_LIST_ITEM_ATTRIBUTE_VALUE_EMPTY _HRESULT_TYPEDEF_(0xC00D1097L) + +// +// MessageId: NS_E_WMPCORE_LIST_ENTRY_NO_REF +// +// MessageText: +// +// No entries found in the playlist file.%0 +// +#define NS_E_WMPCORE_LIST_ENTRY_NO_REF _HRESULT_TYPEDEF_(0xC00D1098L) + +// +// MessageId: NS_E_WMPCORE_MISNAMED_FILE +// +// MessageText: +// +// Windows Media Player cannot play the file. The file is either corrupt or the Player does not support the format you are trying to play.%0 +// +#define NS_E_WMPCORE_MISNAMED_FILE _HRESULT_TYPEDEF_(0xC00D1099L) + +// +// MessageId: NS_E_WMPCORE_CODEC_NOT_TRUSTED +// +// MessageText: +// +// The codec downloaded for this media does not appear to be properly signed. Installation is not possible.%0 +// +#define NS_E_WMPCORE_CODEC_NOT_TRUSTED _HRESULT_TYPEDEF_(0xC00D109AL) + +// +// MessageId: NS_E_WMPCORE_CODEC_NOT_FOUND +// +// MessageText: +// +// Windows Media Player cannot play the file. One or more codecs required to play the file could not be found.%0 +// +#define NS_E_WMPCORE_CODEC_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D109BL) + +// +// MessageId: NS_E_WMPCORE_CODEC_DOWNLOAD_NOT_ALLOWED +// +// MessageText: +// +// Some of the codecs required by this media are not installed on your system. Since the option for automatic codec acquisition is disabled, no codecs will be downloaded.%0 +// +#define NS_E_WMPCORE_CODEC_DOWNLOAD_NOT_ALLOWED _HRESULT_TYPEDEF_(0xC00D109CL) + +// +// MessageId: NS_E_WMPCORE_ERROR_DOWNLOADING_PLAYLIST +// +// MessageText: +// +// Failed to download the playlist file.%0 +// +#define NS_E_WMPCORE_ERROR_DOWNLOADING_PLAYLIST _HRESULT_TYPEDEF_(0xC00D109DL) + +// +// MessageId: NS_E_WMPCORE_FAILED_TO_BUILD_PLAYLIST +// +// MessageText: +// +// Failed to build the playlist.%0 +// +#define NS_E_WMPCORE_FAILED_TO_BUILD_PLAYLIST _HRESULT_TYPEDEF_(0xC00D109EL) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_NONE +// +// MessageText: +// +// Playlist has no alternates to switch into.%0 +// +#define NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_NONE _HRESULT_TYPEDEF_(0xC00D109FL) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_EXHAUSTED +// +// MessageText: +// +// No more playlist alternates available to switch to.%0 +// +#define NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_EXHAUSTED _HRESULT_TYPEDEF_(0xC00D10A0L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_NAME_NOT_FOUND +// +// MessageText: +// +// Could not find the name of the alternate playlist to switch into.%0 +// +#define NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_NAME_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D10A1L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_MORPH_FAILED +// +// MessageText: +// +// Failed to switch to an alternate for this media.%0 +// +#define NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_MORPH_FAILED _HRESULT_TYPEDEF_(0xC00D10A2L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_INIT_FAILED +// +// MessageText: +// +// Failed to initialize an alternate for the media.%0 +// +#define NS_E_WMPCORE_PLAYLIST_ITEM_ALTERNATE_INIT_FAILED _HRESULT_TYPEDEF_(0xC00D10A3L) + +// +// MessageId: NS_E_WMPCORE_MEDIA_ALTERNATE_REF_EMPTY +// +// MessageText: +// +// No URL specified for the roll over Refs in the playlist file.%0 +// +#define NS_E_WMPCORE_MEDIA_ALTERNATE_REF_EMPTY _HRESULT_TYPEDEF_(0xC00D10A4L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_NO_EVENT_NAME +// +// MessageText: +// +// Encountered a playlist with no name.%0 +// +#define NS_E_WMPCORE_PLAYLIST_NO_EVENT_NAME _HRESULT_TYPEDEF_(0xC00D10A5L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_EVENT_ATTRIBUTE_ABSENT +// +// MessageText: +// +// A required attribute in the event block of the playlist was not found.%0 +// +#define NS_E_WMPCORE_PLAYLIST_EVENT_ATTRIBUTE_ABSENT _HRESULT_TYPEDEF_(0xC00D10A6L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_EVENT_EMPTY +// +// MessageText: +// +// No items were found in the event block of the playlist.%0 +// +#define NS_E_WMPCORE_PLAYLIST_EVENT_EMPTY _HRESULT_TYPEDEF_(0xC00D10A7L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_STACK_EMPTY +// +// MessageText: +// +// No playlist was found while returning from a nested playlist.%0 +// +#define NS_E_WMPCORE_PLAYLIST_STACK_EMPTY _HRESULT_TYPEDEF_(0xC00D10A8L) + +// +// MessageId: NS_E_WMPCORE_CURRENT_MEDIA_NOT_ACTIVE +// +// MessageText: +// +// The media item is not active currently.%0 +// +#define NS_E_WMPCORE_CURRENT_MEDIA_NOT_ACTIVE _HRESULT_TYPEDEF_(0xC00D10A9L) + +// +// MessageId: NS_E_WMPCORE_USER_CANCEL +// +// MessageText: +// +// Open was aborted by user.%0 +// +#define NS_E_WMPCORE_USER_CANCEL _HRESULT_TYPEDEF_(0xC00D10ABL) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_REPEAT_EMPTY +// +// MessageText: +// +// No items were found inside the playlist repeat block.%0 +// +#define NS_E_WMPCORE_PLAYLIST_REPEAT_EMPTY _HRESULT_TYPEDEF_(0xC00D10ACL) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_REPEAT_START_MEDIA_NONE +// +// MessageText: +// +// Media object corresponding to start of a playlist repeat block was not found.%0 +// +#define NS_E_WMPCORE_PLAYLIST_REPEAT_START_MEDIA_NONE _HRESULT_TYPEDEF_(0xC00D10ADL) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_REPEAT_END_MEDIA_NONE +// +// MessageText: +// +// Media object corresponding to the end of a playlist repeat block was not found.%0 +// +#define NS_E_WMPCORE_PLAYLIST_REPEAT_END_MEDIA_NONE _HRESULT_TYPEDEF_(0xC00D10AEL) + +// +// MessageId: NS_E_WMPCORE_INVALID_PLAYLIST_URL +// +// MessageText: +// +// Playlist URL supplied to the playlist manager is invalid.%0 +// +#define NS_E_WMPCORE_INVALID_PLAYLIST_URL _HRESULT_TYPEDEF_(0xC00D10AFL) + +// +// MessageId: NS_E_WMPCORE_MISMATCHED_RUNTIME +// +// MessageText: +// +// Windows Media Player cannot play the file because it is corrupted.%0 +// +#define NS_E_WMPCORE_MISMATCHED_RUNTIME _HRESULT_TYPEDEF_(0xC00D10B0L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_IMPORT_FAILED_NO_ITEMS +// +// MessageText: +// +// Windows Media Player cannot import the playlist to Media Library because the playlist is empty.%0 +// +#define NS_E_WMPCORE_PLAYLIST_IMPORT_FAILED_NO_ITEMS _HRESULT_TYPEDEF_(0xC00D10B1L) + +// +// MessageId: NS_E_WMPCORE_VIDEO_TRANSFORM_FILTER_INSERTION +// +// MessageText: +// +// An error has occurred that could prevent the changing of the video contrast on this media.%0 +// +#define NS_E_WMPCORE_VIDEO_TRANSFORM_FILTER_INSERTION _HRESULT_TYPEDEF_(0xC00D10B2L) + +// +// MessageId: NS_E_WMPCORE_MEDIA_UNAVAILABLE +// +// MessageText: +// +// Windows Media Player cannot play this file. Connect to the Internet or insert the removable media on which the file is located, and then try to play the file again.%0 +// +#define NS_E_WMPCORE_MEDIA_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D10B3L) + +// +// MessageId: NS_E_WMPCORE_WMX_ENTRYREF_NO_REF +// +// MessageText: +// +// The playlist contains an ENTRYREF for which no href was parsed. Check the syntax of playlist file.%0 +// +#define NS_E_WMPCORE_WMX_ENTRYREF_NO_REF _HRESULT_TYPEDEF_(0xC00D10B4L) + +// +// MessageId: NS_E_WMPCORE_NO_PLAYABLE_MEDIA_IN_PLAYLIST +// +// MessageText: +// +// Windows Media Player cannot play any items in this playlist. For additional information, right-click an item that cannot be played, and then click Error Details.%0 +// +#define NS_E_WMPCORE_NO_PLAYABLE_MEDIA_IN_PLAYLIST _HRESULT_TYPEDEF_(0xC00D10B5L) + +// +// MessageId: NS_E_WMPCORE_PLAYLIST_EMPTY_NESTED_PLAYLIST_SKIPPED_ITEMS +// +// MessageText: +// +// Windows Media Player cannot play some or all of the playlist items.%0 +// +#define NS_E_WMPCORE_PLAYLIST_EMPTY_NESTED_PLAYLIST_SKIPPED_ITEMS _HRESULT_TYPEDEF_(0xC00D10B6L) + +// +// MessageId: NS_E_WMPCORE_BUSY +// +// MessageText: +// +// Windows Media Player cannot play the file at this time. Try again later.%0 +// +#define NS_E_WMPCORE_BUSY _HRESULT_TYPEDEF_(0xC00D10B7L) + +// +// MessageId: NS_E_WMPCORE_MEDIA_CHILD_PLAYLIST_UNAVAILABLE +// +// MessageText: +// +// There is no child playlist available for this media item at this time.%0 +// +#define NS_E_WMPCORE_MEDIA_CHILD_PLAYLIST_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D10B8L) + +// +// MessageId: NS_E_WMPCORE_MEDIA_NO_CHILD_PLAYLIST +// +// MessageText: +// +// There is no child playlist for this media item.%0 +// +#define NS_E_WMPCORE_MEDIA_NO_CHILD_PLAYLIST _HRESULT_TYPEDEF_(0xC00D10B9L) + +// +// MessageId: NS_E_WMPCORE_FILE_NOT_FOUND +// +// MessageText: +// +// Windows Media Player cannot play one or more files. Right-click the file, and then click Error Details to view information about the error.%0 +// +#define NS_E_WMPCORE_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D10BAL) + +// +// MessageId: NS_E_WMPCORE_TEMP_FILE_NOT_FOUND +// +// MessageText: +// +// The temporary file was not found.%0 +// +#define NS_E_WMPCORE_TEMP_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D10BBL) + +// +// MessageId: NS_E_WMDM_REVOKED +// +// MessageText: +// +// Windows Media Player cannot transfer media to the portable device without an update. Please click More Information to find out how to update your device.%0 +// +#define NS_E_WMDM_REVOKED _HRESULT_TYPEDEF_(0xC00D10BCL) + +// +// MessageId: NS_E_DDRAW_GENERIC +// +// MessageText: +// +// Windows Media Player cannot play the video stream because of a problem with your video card.%0 +// +#define NS_E_DDRAW_GENERIC _HRESULT_TYPEDEF_(0xC00D10BDL) + +// +// MessageId: NS_E_DISPLAY_MODE_CHANGE_FAILED +// +// MessageText: +// +// Windows Media Player failed to change the screen mode for fullscreen video playback.%0 +// +#define NS_E_DISPLAY_MODE_CHANGE_FAILED _HRESULT_TYPEDEF_(0xC00D10BEL) + +// +// MessageId: NS_E_PLAYLIST_CONTAINS_ERRORS +// +// MessageText: +// +// One or more items in the playlist cannot be played. For more details, right-click an item in the playlist, and then click Error Details.%0 +// +#define NS_E_PLAYLIST_CONTAINS_ERRORS _HRESULT_TYPEDEF_(0xC00D10BFL) + +// +// MessageId: NS_E_CHANGING_PROXY_NAME +// +// MessageText: +// +// Can't change proxy name if the proxy setting is not set to custom.%0 +// +#define NS_E_CHANGING_PROXY_NAME _HRESULT_TYPEDEF_(0xC00D10C0L) + +// +// MessageId: NS_E_CHANGING_PROXY_PORT +// +// MessageText: +// +// Can't change proxy port if the proxy setting is not set to custom.%0 +// +#define NS_E_CHANGING_PROXY_PORT _HRESULT_TYPEDEF_(0xC00D10C1L) + +// +// MessageId: NS_E_CHANGING_PROXY_EXCEPTIONLIST +// +// MessageText: +// +// Can't change proxy exception list if the proxy setting is not set to custom.%0 +// +#define NS_E_CHANGING_PROXY_EXCEPTIONLIST _HRESULT_TYPEDEF_(0xC00D10C2L) + +// +// MessageId: NS_E_CHANGING_PROXYBYPASS +// +// MessageText: +// +// Can't change proxy bypass flag if the proxy setting is not set to custom.%0 +// +#define NS_E_CHANGING_PROXYBYPASS _HRESULT_TYPEDEF_(0xC00D10C3L) + +// +// MessageId: NS_E_CHANGING_PROXY_PROTOCOL_NOT_FOUND +// +// MessageText: +// +// Can't find specified protocol.%0 +// +#define NS_E_CHANGING_PROXY_PROTOCOL_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D10C4L) + +// +// MessageId: NS_E_GRAPH_NOAUDIOLANGUAGE +// +// MessageText: +// +// Can't change language settings. Either the graph has no audio, or the audio only supports one language.%0 +// +#define NS_E_GRAPH_NOAUDIOLANGUAGE _HRESULT_TYPEDEF_(0xC00D10C5L) + +// +// MessageId: NS_E_GRAPH_NOAUDIOLANGUAGESELECTED +// +// MessageText: +// +// The graph has no audio language selected.%0 +// +#define NS_E_GRAPH_NOAUDIOLANGUAGESELECTED _HRESULT_TYPEDEF_(0xC00D10C6L) + +// +// MessageId: NS_E_CORECD_NOTAMEDIACD +// +// MessageText: +// +// This is not a media CD.%0 +// +#define NS_E_CORECD_NOTAMEDIACD _HRESULT_TYPEDEF_(0xC00D10C7L) + +// +// MessageId: NS_E_WMPCORE_MEDIA_URL_TOO_LONG +// +// MessageText: +// +// Windows Media Player cannot play this file because the URL is too long.%0 +// +#define NS_E_WMPCORE_MEDIA_URL_TOO_LONG _HRESULT_TYPEDEF_(0xC00D10C8L) + +// +// MessageId: NS_E_WMPFLASH_CANT_FIND_COM_SERVER +// +// MessageText: +// +// Windows Media Player needs the Macromedia Flash Player to play this content. Windows Media Player was not able to detect the Flash player on your system. To play the selected item, you must install the Macromedia Flash Player from the Macromedia Web site, and then try to play the item again.%0 +// +#define NS_E_WMPFLASH_CANT_FIND_COM_SERVER _HRESULT_TYPEDEF_(0xC00D10C9L) + +// +// MessageId: NS_E_WMPFLASH_INCOMPATIBLEVERSION +// +// MessageText: +// +// To play the selected item, you must install an updated version of the Macromedia Flash Player from the Macromedia Web site, and then try to play the item again.%0 +// +#define NS_E_WMPFLASH_INCOMPATIBLEVERSION _HRESULT_TYPEDEF_(0xC00D10CAL) + +// +// MessageId: NS_E_WMPOCXGRAPH_IE_DISALLOWS_ACTIVEX_CONTROLS +// +// MessageText: +// +// The use of ActiveX controls has been turned off in Internet Explorer. As a result Windows Media Player will not be able to playback this content.%0 +// +#define NS_E_WMPOCXGRAPH_IE_DISALLOWS_ACTIVEX_CONTROLS _HRESULT_TYPEDEF_(0xC00D10CBL) + +// +// MessageId: NS_E_NEED_CORE_REFERENCE +// +// MessageText: +// +// The use of this method requires an existing reference to the Player object.%0 +// +#define NS_E_NEED_CORE_REFERENCE _HRESULT_TYPEDEF_(0xC00D10CCL) + +// +// MessageId: NS_E_MEDIACD_READ_ERROR +// +// MessageText: +// +// There was an error reading from the CD-ROM.%0 +// +#define NS_E_MEDIACD_READ_ERROR _HRESULT_TYPEDEF_(0xC00D10CDL) + +// +// MessageId: NS_E_IE_DISALLOWS_ACTIVEX_CONTROLS +// +// MessageText: +// +// Internet Explorer is set to disallow ActiveX controls.%0 +// +#define NS_E_IE_DISALLOWS_ACTIVEX_CONTROLS _HRESULT_TYPEDEF_(0xC00D10CEL) + +// +// MessageId: NS_E_FLASH_PLAYBACK_NOT_ALLOWED +// +// MessageText: +// +// Flash playback has been turned off in Windows Media Player.%0 +// +#define NS_E_FLASH_PLAYBACK_NOT_ALLOWED _HRESULT_TYPEDEF_(0xC00D10CFL) + +// +// MessageId: NS_E_UNABLE_TO_CREATE_RIP_LOCATION +// +// MessageText: +// +// Media Player was unable to create a valid location to copy the CD track.%0 +// +#define NS_E_UNABLE_TO_CREATE_RIP_LOCATION _HRESULT_TYPEDEF_(0xC00D10D0L) + +// +// MessageId: NS_E_WMPCORE_SOME_CODECS_MISSING +// +// MessageText: +// +// One or more codecs required to open this content could not be found.%0 +// +#define NS_E_WMPCORE_SOME_CODECS_MISSING _HRESULT_TYPEDEF_(0xC00D10D1L) + +// +// WMP Core Success codes +// +// +// MessageId: NS_S_WMPCORE_PLAYLISTCLEARABORT +// +// MessageText: +// +// Failed to clear playlist because it was aborted by user.%0 +// +#define NS_S_WMPCORE_PLAYLISTCLEARABORT _HRESULT_TYPEDEF_(0x000D10FEL) + +// +// MessageId: NS_S_WMPCORE_PLAYLISTREMOVEITEMABORT +// +// MessageText: +// +// Failed to remove item in the playlist since it was aborted by user.%0 +// +#define NS_S_WMPCORE_PLAYLISTREMOVEITEMABORT _HRESULT_TYPEDEF_(0x000D10FFL) + +// +// MessageId: NS_S_WMPCORE_PLAYLIST_CREATION_PENDING +// +// MessageText: +// +// Playlist is being generated asynchronously.%0 +// +#define NS_S_WMPCORE_PLAYLIST_CREATION_PENDING _HRESULT_TYPEDEF_(0x000D1102L) + +// +// MessageId: NS_S_WMPCORE_MEDIA_VALIDATION_PENDING +// +// MessageText: +// +// Validation of the media is pending...%0 +// +#define NS_S_WMPCORE_MEDIA_VALIDATION_PENDING _HRESULT_TYPEDEF_(0x000D1103L) + +// +// MessageId: NS_S_WMPCORE_PLAYLIST_REPEAT_SECONDARY_SEGMENTS_IGNORED +// +// MessageText: +// +// Encountered more than one Repeat block during ASX processing.%0 +// +#define NS_S_WMPCORE_PLAYLIST_REPEAT_SECONDARY_SEGMENTS_IGNORED _HRESULT_TYPEDEF_(0x000D1104L) + +// +// MessageId: NS_S_WMPCORE_COMMAND_NOT_AVAILABLE +// +// MessageText: +// +// Current state of WMP disallows calling this method or property.%0 +// +#define NS_S_WMPCORE_COMMAND_NOT_AVAILABLE _HRESULT_TYPEDEF_(0x000D1105L) + +// +// MessageId: NS_S_WMPCORE_PLAYLIST_NAME_AUTO_GENERATED +// +// MessageText: +// +// Name for the playlist has been auto generated.%0 +// +#define NS_S_WMPCORE_PLAYLIST_NAME_AUTO_GENERATED _HRESULT_TYPEDEF_(0x000D1106L) + +// +// MessageId: NS_S_WMPCORE_PLAYLIST_IMPORT_MISSING_ITEMS +// +// MessageText: +// +// The imported playlist does not contain all items from the original.%0 +// +#define NS_S_WMPCORE_PLAYLIST_IMPORT_MISSING_ITEMS _HRESULT_TYPEDEF_(0x000D1107L) + +// +// MessageId: NS_S_WMPCORE_PLAYLIST_COLLAPSED_TO_SINGLE_MEDIA +// +// MessageText: +// +// The M3U playlist has been ignored because it only contains one item.%0 +// +#define NS_S_WMPCORE_PLAYLIST_COLLAPSED_TO_SINGLE_MEDIA _HRESULT_TYPEDEF_(0x000D1108L) + +// +// MessageId: NS_S_WMPCORE_MEDIA_CHILD_PLAYLIST_OPEN_PENDING +// +// MessageText: +// +// The open for the child playlist associated with this media is pending.%0 +// +#define NS_S_WMPCORE_MEDIA_CHILD_PLAYLIST_OPEN_PENDING _HRESULT_TYPEDEF_(0x000D1109L) + +// +// MessageId: NS_S_WMPCORE_MORE_NODES_AVAIABLE +// +// MessageText: +// +// More nodes support the interface requested, but the array for returning them is full.%0 +// +#define NS_S_WMPCORE_MORE_NODES_AVAIABLE _HRESULT_TYPEDEF_(0x000D110AL) + +// +// WMP Internet Manager error codes +// +// +// MessageId: NS_E_WMPIM_USEROFFLINE +// +// MessageText: +// +// Windows Media Player has detected that you are not connected to the Internet. Connect to the Internet, and then try again.%0 +// +#define NS_E_WMPIM_USEROFFLINE _HRESULT_TYPEDEF_(0xC00D1126L) + +// +// MessageId: NS_E_WMPIM_USERCANCELED +// +// MessageText: +// +// User cancelled attempt to connect to the Internet.%0 +// +#define NS_E_WMPIM_USERCANCELED _HRESULT_TYPEDEF_(0xC00D1127L) + +// +// MessageId: NS_E_WMPIM_DIALUPFAILED +// +// MessageText: +// +// Attempt to dial connection to the Internet failed.%0 +// +#define NS_E_WMPIM_DIALUPFAILED _HRESULT_TYPEDEF_(0xC00D1128L) + +// +// MessageId: NS_E_WINSOCK_ERROR_STRING +// +// MessageText: +// +// Windows Media Player has encountered an unknown network error.%0 +// +#define NS_E_WINSOCK_ERROR_STRING _HRESULT_TYPEDEF_(0xC00D1129L) + +// +// WMP Backup and restore error and success codes +// +// +// MessageId: NS_E_WMPBR_NOLISTENER +// +// MessageText: +// +// No window is currently listening to Backup and Restore events.%0 +// +#define NS_E_WMPBR_NOLISTENER _HRESULT_TYPEDEF_(0xC00D1130L) + +// +// MessageId: NS_E_WMPBR_BACKUPCANCEL +// +// MessageText: +// +// Backup of your licenses has been cancelled. Please try again to ensure license backup.%0 +// +#define NS_E_WMPBR_BACKUPCANCEL _HRESULT_TYPEDEF_(0xC00D1131L) + +// +// MessageId: NS_E_WMPBR_RESTORECANCEL +// +// MessageText: +// +// The licenses were not restored because the restoration was cancelled.%0 +// +#define NS_E_WMPBR_RESTORECANCEL _HRESULT_TYPEDEF_(0xC00D1132L) + +// +// MessageId: NS_E_WMPBR_ERRORWITHURL +// +// MessageText: +// +// An error occurred during the backup or restore operation that requires a web page be displayed to the user.%0 +// +#define NS_E_WMPBR_ERRORWITHURL _HRESULT_TYPEDEF_(0xC00D1133L) + +// +// MessageId: NS_E_WMPBR_NAMECOLLISION +// +// MessageText: +// +// The licenses were not backed up because the backup was cancelled.%0 +// +#define NS_E_WMPBR_NAMECOLLISION _HRESULT_TYPEDEF_(0xC00D1134L) + +// +// MessageId: NS_S_WMPBR_SUCCESS +// +// MessageText: +// +// Backup or Restore successful!.%0 +// +#define NS_S_WMPBR_SUCCESS _HRESULT_TYPEDEF_(0x000D1135L) + +// +// MessageId: NS_S_WMPBR_PARTIALSUCCESS +// +// MessageText: +// +// Transfer complete with limitations.%0 +// +#define NS_S_WMPBR_PARTIALSUCCESS _HRESULT_TYPEDEF_(0x000D1136L) + +// +// MessageId: NS_E_WMPBR_DRIVE_INVALID +// +// MessageText: +// +// The location specified for restoring licenses is not valid. Choose another location, and then try again.%0 +// +#define NS_E_WMPBR_DRIVE_INVALID _HRESULT_TYPEDEF_(0xC00D1137L) + +// +// WMP Effects Success codes +// +// +// MessageId: NS_S_WMPEFFECT_TRANSPARENT +// +// MessageText: +// +// Request to the effects control to change transparency status to transparent.%0 +// +#define NS_S_WMPEFFECT_TRANSPARENT _HRESULT_TYPEDEF_(0x000D1144L) + +// +// MessageId: NS_S_WMPEFFECT_OPAQUE +// +// MessageText: +// +// Request to the effects control to change transparency status to opaque.%0 +// +#define NS_S_WMPEFFECT_OPAQUE _HRESULT_TYPEDEF_(0x000D1145L) + +// +// WMP Application Success codes +// +// +// MessageId: NS_S_OPERATION_PENDING +// +// MessageText: +// +// The requested application pane is performing an operation and will not be relased.%0 +// +#define NS_S_OPERATION_PENDING _HRESULT_TYPEDEF_(0x000D114EL) + +// +// WMP DVD Error Codes +// +// +// MessageId: NS_E_DVD_NO_SUBPICTURE_STREAM +// +// MessageText: +// +// Windows Media Player cannot display subtitles or highlights in menus. Reinstall the DVD decoder or contact your device manufacturer to obtain an updated decoder, and then try again.%0 +// +#define NS_E_DVD_NO_SUBPICTURE_STREAM _HRESULT_TYPEDEF_(0xC00D1162L) + +// +// MessageId: NS_E_DVD_COPY_PROTECT +// +// MessageText: +// +// Windows Media Player cannot play this DVD because a problem occurred with digital copyright protection.%0 +// +#define NS_E_DVD_COPY_PROTECT _HRESULT_TYPEDEF_(0xC00D1163L) + +// +// MessageId: NS_E_DVD_AUTHORING_PROBLEM +// +// MessageText: +// +// Windows Media Player cannot play this DVD because the disc is incompatible with the Player.%0 +// +#define NS_E_DVD_AUTHORING_PROBLEM _HRESULT_TYPEDEF_(0xC00D1164L) + +// +// MessageId: NS_E_DVD_INVALID_DISC_REGION +// +// MessageText: +// +// Windows Media Player cannot play this DVD because the disc prohibits playback in your region of the world. You must obtain a disc that is intended for your geographic region.%0 +// +#define NS_E_DVD_INVALID_DISC_REGION _HRESULT_TYPEDEF_(0xC00D1165L) + +// +// MessageId: NS_E_DVD_COMPATIBLE_VIDEO_CARD +// +// MessageText: +// +// Windows Media Player cannot play this DVD because your video card does not support DVD playback.%0 +// +#define NS_E_DVD_COMPATIBLE_VIDEO_CARD _HRESULT_TYPEDEF_(0xC00D1166L) + +// +// MessageId: NS_E_DVD_MACROVISION +// +// MessageText: +// +// Windows Media Player cannot play this DVD because a problem occurred with copyright protection.%0 +// +#define NS_E_DVD_MACROVISION _HRESULT_TYPEDEF_(0xC00D1167L) + +// +// MessageId: NS_E_DVD_SYSTEM_DECODER_REGION +// +// MessageText: +// +// Windows Media Player cannot play this DVD because the region assigned to your DVD drive does not match the region assigned to your DVD decoder.%0 +// +#define NS_E_DVD_SYSTEM_DECODER_REGION _HRESULT_TYPEDEF_(0xC00D1168L) + +// +// MessageId: NS_E_DVD_DISC_DECODER_REGION +// +// MessageText: +// +// Windows Media Player cannot play this DVD because the disc prohibits playback in your region of the world. To play the disc by using the Player, you must obtain a disc that is intended for your geographic region.%0 +// +#define NS_E_DVD_DISC_DECODER_REGION _HRESULT_TYPEDEF_(0xC00D1169L) + +// +// MessageId: NS_E_DVD_NO_VIDEO_STREAM +// +// MessageText: +// +// Windows Media Player is currently unable to play DVD video. Try decreasing the number of colors displayed on your monitor or decreasing the screen resolution. For additional solutions, click More Information to access the DVD Troubleshooter.%0 +// +#define NS_E_DVD_NO_VIDEO_STREAM _HRESULT_TYPEDEF_(0xC00D116AL) + +// +// MessageId: NS_E_DVD_NO_AUDIO_STREAM +// +// MessageText: +// +// Windows Media Player cannot play DVD audio. Verify that your sound card is set up correctly, and then try again.%0 +// +#define NS_E_DVD_NO_AUDIO_STREAM _HRESULT_TYPEDEF_(0xC00D116BL) + +// +// MessageId: NS_E_DVD_GRAPH_BUILDING +// +// MessageText: +// +// Windows Media Player cannot play DVD video. Close any open files and quit any other running programs, and then try again. If the problem continues, restart your computer.%0 +// +#define NS_E_DVD_GRAPH_BUILDING _HRESULT_TYPEDEF_(0xC00D116CL) + +// +// MessageId: NS_E_DVD_NO_DECODER +// +// MessageText: +// +// Windows Media Player cannot play this DVD because a compatible DVD decoder is not installed on your computer.%0 +// +#define NS_E_DVD_NO_DECODER _HRESULT_TYPEDEF_(0xC00D116DL) + +// +// MessageId: NS_E_DVD_PARENTAL +// +// MessageText: +// +// Windows Media Player cannot play this DVD segment because the segment has a parental rating higher than the rating you are authorized to view.%0 +// +#define NS_E_DVD_PARENTAL _HRESULT_TYPEDEF_(0xC00D116EL) + +// +// MessageId: NS_E_DVD_CANNOT_JUMP +// +// MessageText: +// +// Windows Media Player cannot skip to the requested location in the DVD at this time.%0 +// +#define NS_E_DVD_CANNOT_JUMP _HRESULT_TYPEDEF_(0xC00D116FL) + +// +// MessageId: NS_E_DVD_DEVICE_CONTENTION +// +// MessageText: +// +// Windows Media Player cannot play this DVD because it is currently in use by another program. Quit the other program that is using the DVD, and then try to play it again.%0 +// +#define NS_E_DVD_DEVICE_CONTENTION _HRESULT_TYPEDEF_(0xC00D1170L) + +// +// MessageId: NS_E_DVD_NO_VIDEO_MEMORY +// +// MessageText: +// +// Windows Media Player cannot play DVD video. Double-click Display in Control Panel to lower your screen resolution and color quality settings.%0 +// +#define NS_E_DVD_NO_VIDEO_MEMORY _HRESULT_TYPEDEF_(0xC00D1171L) + +// +// MessageId: NS_E_DVD_CANNOT_COPY_PROTECTED +// +// MessageText: +// +// Windows Media Player cannot copy this DVD because it is copy protected.%0 +// +#define NS_E_DVD_CANNOT_COPY_PROTECTED _HRESULT_TYPEDEF_(0xC00D1172L) + +// +// MessageId: NS_E_DVD_REQUIRED_PROPERTY_NOT_SET +// +// MessageText: +// +// One of more of the required properties has not been set.%0 +// +#define NS_E_DVD_REQUIRED_PROPERTY_NOT_SET _HRESULT_TYPEDEF_(0xC00D1173L) + +// +// MessageId: NS_E_DVD_INVALID_TITLE_CHAPTER +// +// MessageText: +// +// The specified title and/or chapter number does not exist on this DVD.%0 +// +#define NS_E_DVD_INVALID_TITLE_CHAPTER _HRESULT_TYPEDEF_(0xC00D1174L) + +// +// WMP PDA Error codes +// +// +// MessageId: NS_E_NO_CD_BURNER +// +// MessageText: +// +// A CD recorder (burner) was not detected. Connect a CD recorder, and try copying again.%0 +// +#define NS_E_NO_CD_BURNER _HRESULT_TYPEDEF_(0xC00D1176L) + +// +// MessageId: NS_E_DEVICE_IS_NOT_READY +// +// MessageText: +// +// Windows Media Player does not detect any storage media in the selected device. Insert media into the device.%0 +// +#define NS_E_DEVICE_IS_NOT_READY _HRESULT_TYPEDEF_(0xC00D1177L) + +// +// MessageId: NS_E_PDA_UNSUPPORTED_FORMAT +// +// MessageText: +// +// Windows Media Player cannot play the specified file. Your portable device does not support the specified format.%0 +// +#define NS_E_PDA_UNSUPPORTED_FORMAT _HRESULT_TYPEDEF_(0xC00D1178L) + +// +// MessageId: NS_E_NO_PDA +// +// MessageText: +// +// Windows Media Player cannot detect a connected portable device. Connect your portable device, and try again.%0 +// +#define NS_E_NO_PDA _HRESULT_TYPEDEF_(0xC00D1179L) + +// +// MessageId: NS_E_PDA_UNSPECIFIED_ERROR +// +// MessageText: +// +// Windows Media Player has encountered an error on the portable device.%0 +// +#define NS_E_PDA_UNSPECIFIED_ERROR _HRESULT_TYPEDEF_(0xC00D117AL) + +// +// MessageId: NS_E_MEMSTORAGE_BAD_DATA +// +// MessageText: +// +// Windows Media Player encountered an internal error in accessing a memory-based storage during a CD burning task.%0 +// +#define NS_E_MEMSTORAGE_BAD_DATA _HRESULT_TYPEDEF_(0xC00D117BL) + +// +// MessageId: NS_E_PDA_FAIL_SELECT_DEVICE +// +// MessageText: +// +// Windows Media Player encountered an internal error when selecting a PDA or CD device.%0 +// +#define NS_E_PDA_FAIL_SELECT_DEVICE _HRESULT_TYPEDEF_(0xC00D117CL) + +// +// MessageId: NS_E_PDA_FAIL_READ_WAVE_FILE +// +// MessageText: +// +// Windows Media Player failed to open or read data from a wave file.%0 +// +#define NS_E_PDA_FAIL_READ_WAVE_FILE _HRESULT_TYPEDEF_(0xC00D117DL) + +// +// MessageId: NS_E_IMAPI_LOSSOFSTREAMING +// +// MessageText: +// +// Windows Media Player did not copy all the selected items. The Player has reduced the recording speed of your CD drive to solve the problem. To copy all the selected items, insert a blank CD in the drive, and try again.%0 +// +#define NS_E_IMAPI_LOSSOFSTREAMING _HRESULT_TYPEDEF_(0xC00D117EL) + +// +// MessageId: NS_E_PDA_DEVICE_FULL +// +// MessageText: +// +// There is not enough storage space on the portable device to complete this operation. Delete some unneeded files on the portable device, and then try again.%0 +// +#define NS_E_PDA_DEVICE_FULL _HRESULT_TYPEDEF_(0xC00D117FL) + +// +// MessageId: NS_E_FAIL_LAUNCH_ROXIO_PLUGIN +// +// MessageText: +// +// Windows Media Player cannot copy the files to the CD. Verify that a CD-R or CD-RW drive is connected to your computer, and then try again. If the problem continues, reinstall the Player.%0 +// +#define NS_E_FAIL_LAUNCH_ROXIO_PLUGIN _HRESULT_TYPEDEF_(0xC00D1180L) + +// +// MessageId: NS_E_PDA_DEVICE_FULL_IN_SESSION +// +// MessageText: +// +// Windows Media Player failed to copy some files to device because the device is out of space.%0 +// +#define NS_E_PDA_DEVICE_FULL_IN_SESSION _HRESULT_TYPEDEF_(0xC00D1181L) + +// +// MessageId: NS_E_IMAPI_MEDIUM_INVALIDTYPE +// +// MessageText: +// +// The medium in the drive is invalid. Please insert a blank CD-R or a CD-RW into the drive, and then try again.%0 +// +#define NS_E_IMAPI_MEDIUM_INVALIDTYPE _HRESULT_TYPEDEF_(0xC00D1182L) + +// +// General Remapped Error codes in WMP +// +// +// MessageId: NS_E_WMP_PROTOCOL_PROBLEM +// +// MessageText: +// +// Windows Media Player could not open the specified URL. Be sure Windows Media Player is configured to use all available protocols, and then try again.%0 +// +#define NS_E_WMP_PROTOCOL_PROBLEM _HRESULT_TYPEDEF_(0xC00D1194L) + +// +// MessageId: NS_E_WMP_NO_DISK_SPACE +// +// MessageText: +// +// Windows Media Player cannot open the file because there is not enough disk space on your computer. Delete some unneeded files on your hard disk, and then try again.%0 +// +#define NS_E_WMP_NO_DISK_SPACE _HRESULT_TYPEDEF_(0xC00D1195L) + +// +// MessageId: NS_E_WMP_LOGON_FAILURE +// +// MessageText: +// +// Windows Media Player cannot copy or play the file because the server denied access to it. Verify that you have access rights to the file, and then try again.%0 +// +#define NS_E_WMP_LOGON_FAILURE _HRESULT_TYPEDEF_(0xC00D1196L) + +// +// MessageId: NS_E_WMP_CANNOT_FIND_FILE +// +// MessageText: +// +// Windows Media Player cannot find the specified file. Be sure the path is typed correctly. If it is, the file does not exist at the specified location, or the computer where the file is stored is offline.%0 +// +#define NS_E_WMP_CANNOT_FIND_FILE _HRESULT_TYPEDEF_(0xC00D1197L) + +// +// MessageId: NS_E_WMP_SERVER_INACCESSIBLE +// +// MessageText: +// +// Windows Media Player cannot connect to the server. The server name may be incorrect or the server is busy. Try again later.%0 +// +#define NS_E_WMP_SERVER_INACCESSIBLE _HRESULT_TYPEDEF_(0xC00D1198L) + +// +// MessageId: NS_E_WMP_UNSUPPORTED_FORMAT +// +// MessageText: +// +// Windows Media Player cannot play the file. The file is either corrupt or the Player does not support the format you are trying to play.%0 +// +#define NS_E_WMP_UNSUPPORTED_FORMAT _HRESULT_TYPEDEF_(0xC00D1199L) + +// +// MessageId: NS_E_WMP_DSHOW_UNSUPPORTED_FORMAT +// +// MessageText: +// +// Windows Media Player cannot play the file. The file may be formatted with an unsupported codec, or the Internet security setting on your computer is set too high. Lower your browser's security setting, and then try again.%0 +// +#define NS_E_WMP_DSHOW_UNSUPPORTED_FORMAT _HRESULT_TYPEDEF_(0xC00D119AL) + +// +// MessageId: NS_E_WMP_PLAYLIST_EXISTS +// +// MessageText: +// +// Windows Media Player cannot create the playlist because the name already exists. Type a different playlist name.%0 +// +#define NS_E_WMP_PLAYLIST_EXISTS _HRESULT_TYPEDEF_(0xC00D119BL) + +// +// MessageId: NS_E_WMP_NONMEDIA_FILES +// +// MessageText: +// +// Windows Media Player could not delete the playlist because it contains non-digital media files. Any digital media files in the playlist were deleted. Use Windows Explorer to delete non-digital media files, and then try deleting the playlist again.%0 +// +#define NS_E_WMP_NONMEDIA_FILES _HRESULT_TYPEDEF_(0xC00D119CL) + +// +// MessageId: NS_E_WMP_INVALID_ASX +// +// MessageText: +// +// Windows Media Player cannot play the file because the associated playlist is not valid.%0 +// +#define NS_E_WMP_INVALID_ASX _HRESULT_TYPEDEF_(0xC00D119DL) + +// +// MessageId: NS_E_WMP_ALREADY_IN_USE +// +// MessageText: +// +// Windows Media Player is already in use. Stop playing any content and close all Player dialog boxes and then try again.%0 +// +#define NS_E_WMP_ALREADY_IN_USE _HRESULT_TYPEDEF_(0xC00D119EL) + +// +// MessageId: NS_E_WMP_IMAPI_FAILURE +// +// MessageText: +// +// Windows Media Player has encountered an unknown error with your recordable disc.%0 +// +#define NS_E_WMP_IMAPI_FAILURE _HRESULT_TYPEDEF_(0xC00D119FL) + +// +// MessageId: NS_E_WMP_WMDM_FAILURE +// +// MessageText: +// +// Windows Media Player has encountered an unknown error with your portable device. Reconnect your portable device and try again.%0 +// +#define NS_E_WMP_WMDM_FAILURE _HRESULT_TYPEDEF_(0xC00D11A0L) + +// +// MessageId: NS_E_WMP_CODEC_NEEDED_WITH_4CC +// +// MessageText: +// +// The %s codec is required to play this file. To determine if this codec is available to download from the Web, click Web Help.%0 +// +#define NS_E_WMP_CODEC_NEEDED_WITH_4CC _HRESULT_TYPEDEF_(0xC00D11A1L) + +// +// MessageId: NS_E_WMP_CODEC_NEEDED_WITH_FORMATTAG +// +// MessageText: +// +// The audio codec identified by the format tag %s is required to play this file. To determine if this codec is available to download from the Web, click Web Help.%0 +// +#define NS_E_WMP_CODEC_NEEDED_WITH_FORMATTAG _HRESULT_TYPEDEF_(0xC00D11A2L) + +// +// MessageId: NS_E_WMP_MSSAP_NOT_AVAILABLE +// +// MessageText: +// +// To play this file, you must install the latest Windows XP service pack. To install the service pack from the Windows Update Web site, click Web Help.%0 +// +#define NS_E_WMP_MSSAP_NOT_AVAILABLE _HRESULT_TYPEDEF_(0xC00D11A3L) + +// +// MessageId: NS_E_WMP_WMDM_INTERFACEDEAD +// +// MessageText: +// +// Windows Media Player no longer detects a portable device. Reconnect your portable device, and try again.%0 +// +#define NS_E_WMP_WMDM_INTERFACEDEAD _HRESULT_TYPEDEF_(0xC00D11A4L) + +// +// MessageId: NS_E_WMP_WMDM_NOTCERTIFIED +// +// MessageText: +// +// Windows Media Player cannot copy the file because the portable device does not support protected files.%0 +// +#define NS_E_WMP_WMDM_NOTCERTIFIED _HRESULT_TYPEDEF_(0xC00D11A5L) + +// +// MessageId: NS_E_WMP_WMDM_LICENSE_NOTEXIST +// +// MessageText: +// +// Windows Media Player cannot copy the file because a license for this file could not be found on your computer. If you obtained this file from a Web site, return to the site, and try downloading the file again.%0 +// +#define NS_E_WMP_WMDM_LICENSE_NOTEXIST _HRESULT_TYPEDEF_(0xC00D11A6L) + +// +// MessageId: NS_E_WMP_WMDM_LICENSE_EXPIRED +// +// MessageText: +// +// Windows Media Player cannot copy the file because the license for this file has expired. If you obtained this file from a Web site, return to the site, and try downloading the file again.%0 +// +#define NS_E_WMP_WMDM_LICENSE_EXPIRED _HRESULT_TYPEDEF_(0xC00D11A7L) + +// +// MessageId: NS_E_WMP_WMDM_BUSY +// +// MessageText: +// +// The portable device is already in use. Wait until the current process finishes or quit other programs that may be using the portable device, and then try again.%0 +// +#define NS_E_WMP_WMDM_BUSY _HRESULT_TYPEDEF_(0xC00D11A8L) + +// +// MessageId: NS_E_WMP_WMDM_NORIGHTS +// +// MessageText: +// +// Windows Media Player cannot copy the file because the license or device restricts it.%0 +// +#define NS_E_WMP_WMDM_NORIGHTS _HRESULT_TYPEDEF_(0xC00D11A9L) + +// +// MessageId: NS_E_WMP_WMDM_INCORRECT_RIGHTS +// +// MessageText: +// +// Windows Media Player cannot copy the file because the license associated with the file restricts it.%0 +// +#define NS_E_WMP_WMDM_INCORRECT_RIGHTS _HRESULT_TYPEDEF_(0xC00D11AAL) + +// +// MessageId: NS_E_WMP_IMAPI_GENERIC +// +// MessageText: +// +// Windows Media Player cannot copy files to the recordable disc.%0 +// +#define NS_E_WMP_IMAPI_GENERIC _HRESULT_TYPEDEF_(0xC00D11ABL) + +// +// MessageId: NS_E_WMP_IMAPI_DEVICE_NOTPRESENT +// +// MessageText: +// +// Windows Media Player cannot copy files to the recordable disc. Verify that the CD-R or CD-RW drive is connected, and then try again.%0 +// +#define NS_E_WMP_IMAPI_DEVICE_NOTPRESENT _HRESULT_TYPEDEF_(0xC00D11ADL) + +// +// MessageId: NS_E_WMP_IMAPI_STASHINUSE +// +// MessageText: +// +// The CD-R or CD-RW drive may already be in use. Wait until the current process finishes or quit other programs that may be using the CD-R or CD-RW drive, and then try again.%0 +// +#define NS_E_WMP_IMAPI_STASHINUSE _HRESULT_TYPEDEF_(0xC00D11AEL) + +// +// MessageId: NS_E_WMP_IMAPI_LOSS_OF_STREAMING +// +// MessageText: +// +// Windows Media Player cannot copy files to the recordable disc.%0 +// +#define NS_E_WMP_IMAPI_LOSS_OF_STREAMING _HRESULT_TYPEDEF_(0xC00D11AFL) + +// +// MessageId: NS_E_WMP_SERVER_UNAVAILABLE +// +// MessageText: +// +// Windows Media Player cannot play the file because the server is busy. Try again later.%0 +// +#define NS_E_WMP_SERVER_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D11B0L) + +// +// MessageId: NS_E_WMP_FILE_OPEN_FAILED +// +// MessageText: +// +// Windows Media Player cannot open the file.%0 +// +#define NS_E_WMP_FILE_OPEN_FAILED _HRESULT_TYPEDEF_(0xC00D11B1L) + +// +// MessageId: NS_E_WMP_VERIFY_ONLINE +// +// MessageText: +// +// To play the file, Windows Media Player must obtain a license from the Internet. Connect to the Internet, and then try again.%0 +// +#define NS_E_WMP_VERIFY_ONLINE _HRESULT_TYPEDEF_(0xC00D11B2L) + +// +// MessageId: NS_E_WMP_SERVER_NOT_RESPONDING +// +// MessageText: +// +// Windows Media Player cannot play the file because the server is not responding. If you entered a URL or path to play the file, verify that it is correct. If you clicked a link to play the file, the link may not be valid.%0 +// +#define NS_E_WMP_SERVER_NOT_RESPONDING _HRESULT_TYPEDEF_(0xC00D11B3L) + +// +// MessageId: NS_E_WMP_DRM_CORRUPT_BACKUP +// +// MessageText: +// +// Windows Media Player cannot restore your licenses because no backed up licenses were found on your computer.%0 +// +#define NS_E_WMP_DRM_CORRUPT_BACKUP _HRESULT_TYPEDEF_(0xC00D11B4L) + +// +// MessageId: NS_E_WMP_DRM_LICENSE_SERVER_UNAVAILABLE +// +// MessageText: +// +// To play the file, Windows Media Player must obtain a license from the Internet. However, the license server is currently not available. Try again later.%0 +// +#define NS_E_WMP_DRM_LICENSE_SERVER_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D11B5L) + +// +// MessageId: NS_E_WMP_NETWORK_FIREWALL +// +// MessageText: +// +// Windows Media Player cannot play the file. A network firewall may be preventing the Player from opening the file by using the UDP transport protocol. To play this file, try opening the file without specifying UDP.%0 +// +#define NS_E_WMP_NETWORK_FIREWALL _HRESULT_TYPEDEF_(0xC00D11B6L) + +// +// MessageId: NS_E_WMP_NO_REMOVABLE_MEDIA +// +// MessageText: +// +// Insert the removable media, and then try again.%0 +// +#define NS_E_WMP_NO_REMOVABLE_MEDIA _HRESULT_TYPEDEF_(0xC00D11B7L) + +// +// MessageId: NS_E_WMP_PROXY_CONNECT_TIMEOUT +// +// MessageText: +// +// Windows Media Player cannot play the file because the proxy server is not responding. The proxy server may be temporarily unavailable or your Player proxy settings may not be valid.%0 +// +#define NS_E_WMP_PROXY_CONNECT_TIMEOUT _HRESULT_TYPEDEF_(0xC00D11B8L) + +// +// MessageId: NS_E_WMP_NEED_UPGRADE +// +// MessageText: +// +// To play this file, you must upgrade to the latest version of Windows Media Player. To upgrade the Player, on the Help menu, click Check For Player Updates, and then follow the instructions.%0 +// +#define NS_E_WMP_NEED_UPGRADE _HRESULT_TYPEDEF_(0xC00D11B9L) + +// +// MessageId: NS_E_WMP_AUDIO_HW_PROBLEM +// +// MessageText: +// +// Windows Media Player cannot play the file because there is a problem with your sound device. There may not be a sound device installed on your computer, it may be in use by another program, or it may not be functioning properly.%0 +// +#define NS_E_WMP_AUDIO_HW_PROBLEM _HRESULT_TYPEDEF_(0xC00D11BAL) + +// +// MessageId: NS_E_WMP_INVALID_PROTOCOL +// +// MessageText: +// +// Windows Media Player cannot play the file because the specified protocol is not supported. In the Open URL dialog, try opening the file using a different transport protocol (for example, "http:" or "rtsp:").%0 +// +#define NS_E_WMP_INVALID_PROTOCOL _HRESULT_TYPEDEF_(0xC00D11BBL) + +// +// MessageId: NS_E_WMP_INVALID_LIBRARY_ADD +// +// MessageText: +// +// Windows Media Player cannot add the file to Media Library because the file format is not supported.%0 +// +#define NS_E_WMP_INVALID_LIBRARY_ADD _HRESULT_TYPEDEF_(0xC00D11BCL) + +// +// MessageId: NS_E_WMP_MMS_NOT_SUPPORTED +// +// MessageText: +// +// Windows Media Player cannot play the file because the specified protocol is not supported. In the Open URL dialog, try opening the file using a different transport protocol (for example, "mms:").%0 +// +#define NS_E_WMP_MMS_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xC00D11BDL) + +// +// MessageId: NS_E_WMP_NO_PROTOCOLS_SELECTED +// +// MessageText: +// +// Windows Media Player cannot play the file because there are no streaming protocols selected. Select one or more protocols, and then try again.%0 +// +#define NS_E_WMP_NO_PROTOCOLS_SELECTED _HRESULT_TYPEDEF_(0xC00D11BEL) + +// +// MessageId: NS_E_WMP_GOFULLSCREEN_FAILED +// +// MessageText: +// +// To use the Full Screen command, you may need to adjust your Windows display settings. Open Display in Control Panel, and try setting Hardware acceleration to Full.%0 +// +#define NS_E_WMP_GOFULLSCREEN_FAILED _HRESULT_TYPEDEF_(0xC00D11BFL) + +// +// MessageId: NS_E_WMP_NETWORK_ERROR +// +// MessageText: +// +// Windows Media Player cannot play the file because a network error occurred.%0 +// +#define NS_E_WMP_NETWORK_ERROR _HRESULT_TYPEDEF_(0xC00D11C0L) + +// +// MessageId: NS_E_WMP_CONNECT_TIMEOUT +// +// MessageText: +// +// Windows Media Player cannot play the file because the server is not responding. Verify that you are connected to the network, and then try again later.%0 +// +#define NS_E_WMP_CONNECT_TIMEOUT _HRESULT_TYPEDEF_(0xC00D11C1L) + +// +// MessageId: NS_E_WMP_MULTICAST_DISABLED +// +// MessageText: +// +// Windows Media Player cannot play the file because the multicast protocol is not enabled. On the Tools menu, click Options, click the Network tab, and then select the Multicast check box.%0 +// +#define NS_E_WMP_MULTICAST_DISABLED _HRESULT_TYPEDEF_(0xC00D11C2L) + +// +// MessageId: NS_E_WMP_SERVER_DNS_TIMEOUT +// +// MessageText: +// +// Windows Media Player cannot play the file because a network problem occurred. Verify that you are connected to the network, and then try again later.%0 +// +#define NS_E_WMP_SERVER_DNS_TIMEOUT _HRESULT_TYPEDEF_(0xC00D11C3L) + +// +// MessageId: NS_E_WMP_PROXY_NOT_FOUND +// +// MessageText: +// +// Windows Media Player cannot play the file because the network proxy server could not be found. Verify your proxy settings, and then try again.%0 +// +#define NS_E_WMP_PROXY_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D11C4L) + +// +// MessageId: NS_E_WMP_TAMPERED_CONTENT +// +// MessageText: +// +// Windows Media Player cannot play the file because it is damaged or corrupted.%0 +// +#define NS_E_WMP_TAMPERED_CONTENT _HRESULT_TYPEDEF_(0xC00D11C5L) + +// +// MessageId: NS_E_WMP_OUTOFMEMORY +// +// MessageText: +// +// Your computer is running low on memory. Quit other programs, and then try again.%0 +// +#define NS_E_WMP_OUTOFMEMORY _HRESULT_TYPEDEF_(0xC00D11C6L) + +// +// MessageId: NS_E_WMP_AUDIO_CODEC_NOT_INSTALLED +// +// MessageText: +// +// Windows Media Player cannot play the file because the required audio codec is not installed on your computer.%0 +// +#define NS_E_WMP_AUDIO_CODEC_NOT_INSTALLED _HRESULT_TYPEDEF_(0xC00D11C7L) + +// +// MessageId: NS_E_WMP_VIDEO_CODEC_NOT_INSTALLED +// +// MessageText: +// +// Windows Media Player cannot play the file because the required video codec is not installed on your computer.%0 +// +#define NS_E_WMP_VIDEO_CODEC_NOT_INSTALLED _HRESULT_TYPEDEF_(0xC00D11C8L) + +// +// MessageId: NS_E_WMP_IMAPI_DEVICE_INVALIDTYPE +// +// MessageText: +// +// Windows Media Player cannot copy the files to the recordable disc.%0 +// +#define NS_E_WMP_IMAPI_DEVICE_INVALIDTYPE _HRESULT_TYPEDEF_(0xC00D11C9L) + +// +// MessageId: NS_E_WMP_DRM_DRIVER_AUTH_FAILURE +// +// MessageText: +// +// Windows Media Player cannot play the licensed file because there is a problem with your sound device. Try installing a new device driver or use a different sound device.%0 +// +#define NS_E_WMP_DRM_DRIVER_AUTH_FAILURE _HRESULT_TYPEDEF_(0xC00D11CAL) + +// +// MessageId: NS_E_WMP_NETWORK_RESOURCE_FAILURE +// +// MessageText: +// +// Windows Media Player encountered a network problem. Restart the Player.%0 +// +#define NS_E_WMP_NETWORK_RESOURCE_FAILURE _HRESULT_TYPEDEF_(0xC00D11CBL) + +// +// MessageId: NS_E_WMP_UPGRADE_APPLICATION +// +// MessageText: +// +// Windows Media Player is not installed properly. Reinstall the Player.%0 +// +#define NS_E_WMP_UPGRADE_APPLICATION _HRESULT_TYPEDEF_(0xC00D11CCL) + +// +// MessageId: NS_E_WMP_UNKNOWN_ERROR +// +// MessageText: +// +// Windows Media Player encountered an unknown error.%0 +// +#define NS_E_WMP_UNKNOWN_ERROR _HRESULT_TYPEDEF_(0xC00D11CDL) + +// +// MessageId: NS_E_WMP_INVALID_KEY +// +// MessageText: +// +// Windows Media Player cannot play the file because the required codec is not valid.%0 +// +#define NS_E_WMP_INVALID_KEY _HRESULT_TYPEDEF_(0xC00D11CEL) + +// +// MessageId: NS_E_WMP_CD_ANOTHER_USER +// +// MessageText: +// +// The CD drive is in use by another user. Wait for the operation to complete, and then try again.%0 +// +#define NS_E_WMP_CD_ANOTHER_USER _HRESULT_TYPEDEF_(0xC00D11CFL) + +// +// MessageId: NS_E_WMP_DRM_NEEDS_AUTHORIZATION +// +// MessageText: +// +// Windows Media Player cannot play the licensed file because the required security upgrade is not available for download.%0 +// +#define NS_E_WMP_DRM_NEEDS_AUTHORIZATION _HRESULT_TYPEDEF_(0xC00D11D0L) + +// +// MessageId: NS_E_WMP_BAD_DRIVER +// +// MessageText: +// +// Windows Media Player cannot play the file because there may be a problem with your sound or video device. Try installing a new device driver.%0 +// +#define NS_E_WMP_BAD_DRIVER _HRESULT_TYPEDEF_(0xC00D11D1L) + +// +// MessageId: NS_E_WMP_ACCESS_DENIED +// +// MessageText: +// +// Windows Media Player cannot access the file.%0 +// +#define NS_E_WMP_ACCESS_DENIED _HRESULT_TYPEDEF_(0xC00D11D2L) + +// +// MessageId: NS_E_WMP_LICENSE_RESTRICTS +// +// MessageText: +// +// Windows Media Player cannot copy the file to the device because the license restricts it.%0 +// +#define NS_E_WMP_LICENSE_RESTRICTS _HRESULT_TYPEDEF_(0xC00D11D3L) + +// +// MessageId: NS_E_WMP_INVALID_REQUEST +// +// MessageText: +// +// Windows Media Player cannot perform the requested action at this time.%0 +// +#define NS_E_WMP_INVALID_REQUEST _HRESULT_TYPEDEF_(0xC00D11D4L) + +// +// MessageId: NS_E_WMP_CD_STASH_NO_SPACE +// +// MessageText: +// +// Windows Media Player cannot copy the files because there is not enough free disk space to store the temporary files. Delete some unneeded files on your hard disk, and then try again.%0 +// +#define NS_E_WMP_CD_STASH_NO_SPACE _HRESULT_TYPEDEF_(0xC00D11D5L) + +// +// MessageId: NS_E_WMP_DRM_NEW_HARDWARE +// +// MessageText: +// +// Windows Media Player cannot play the file because the associated license is either corrupted or not valid. The license may no longer be valid if you have replaced hardware components in your computer.%0 +// +#define NS_E_WMP_DRM_NEW_HARDWARE _HRESULT_TYPEDEF_(0xC00D11D6L) + +// +// MessageId: NS_E_WMP_DRM_INVALID_SIG +// +// MessageText: +// +// The required security upgrade cannot be validated. Try installing the latest Internet Explorer service pack. To install the service pack from the Windows Update Web site, click Web Help.%0 +// +#define NS_E_WMP_DRM_INVALID_SIG _HRESULT_TYPEDEF_(0xC00D11D7L) + +// +// MessageId: NS_E_WMP_DRM_CANNOT_RESTORE +// +// MessageText: +// +// Windows Media Player cannot restore your licenses because you have exceeded the restore limit for the day. Try again tomorrow.%0 +// +#define NS_E_WMP_DRM_CANNOT_RESTORE _HRESULT_TYPEDEF_(0xC00D11D8L) + +// +// WMP CD Filter Error codes extension +// +// +// MessageId: NS_E_CD_NO_BUFFERS_READ +// +// MessageText: +// +// Windows Media Player encountered an error when reading the CD-ROM drive in digital mode. You can try to use digital mode again, or you can switch the Player to analog mode.%0 +// +#define NS_E_CD_NO_BUFFERS_READ _HRESULT_TYPEDEF_(0xC00D11F8L) + +// +// MessageId: NS_E_CD_EMPTY_TRACK_QUEUE +// +// MessageText: +// +// No CD track was specified for playback.%0 +// +#define NS_E_CD_EMPTY_TRACK_QUEUE _HRESULT_TYPEDEF_(0xC00D11F9L) + +// +// MessageId: NS_E_CD_NO_READER +// +// MessageText: +// +// The CD filter was not able to create the CD reader.%0 +// +#define NS_E_CD_NO_READER _HRESULT_TYPEDEF_(0xC00D11FAL) + +// +// MessageId: NS_E_CD_ISRC_INVALID +// +// MessageText: +// +// Invalid ISRC code.%0 +// +#define NS_E_CD_ISRC_INVALID _HRESULT_TYPEDEF_(0xC00D11FBL) + +// +// MessageId: NS_E_CD_MEDIA_CATALOG_NUMBER_INVALID +// +// MessageText: +// +// Invalid Media Catalog Number.%0 +// +#define NS_E_CD_MEDIA_CATALOG_NUMBER_INVALID _HRESULT_TYPEDEF_(0xC00D11FCL) + +// +// MessageId: NS_E_SLOW_READ_DIGITAL_WITH_ERRORCORRECTION +// +// MessageText: +// +// Media Player has detected that your CD drive cannot play back audio CDs correctly because the drive is too slow with error correction turned on. Please turn off error correction for this drive.%0 +// +#define NS_E_SLOW_READ_DIGITAL_WITH_ERRORCORRECTION _HRESULT_TYPEDEF_(0xC00D11FDL) + +// +// MessageId: NS_E_CD_SPEEDDETECT_NOT_ENOUGH_READS +// +// MessageText: +// +// The CD track is too small to make a good estimate of the CD's speed.%0 +// +#define NS_E_CD_SPEEDDETECT_NOT_ENOUGH_READS _HRESULT_TYPEDEF_(0xC00D11FEL) + +// +// MessageId: NS_E_CD_QUEUEING_DISABLED +// +// MessageText: +// +// Cannot queue the given CD track as queuing is disabled.%0 +// +#define NS_E_CD_QUEUEING_DISABLED _HRESULT_TYPEDEF_(0xC00D11FFL) + +// +// WMP Policy error codes +// +// +// MessageId: NS_E_WMP_POLICY_VALUE_NOT_CONFIGURED +// +// MessageText: +// +// Media Player failed to read a policy. This can happen when the policy is not present in the registry or when there is a failure to read from the registry.%0 +// +#define NS_E_WMP_POLICY_VALUE_NOT_CONFIGURED _HRESULT_TYPEDEF_(0xC00D122AL) + +// +//Background download plugin +// +// +// MessageId: NS_E_WMP_HWND_NOTFOUND +// +// MessageText: +// +// Windows Media Player main window not found. The download manager needs to find it to work properly. Please try to run Windows Media Player again.%0 +// +#define NS_E_WMP_HWND_NOTFOUND _HRESULT_TYPEDEF_(0xC00D125CL) + +// +// MessageId: NS_E_BKGDOWNLOAD_WRONG_NO_FILES +// +// MessageText: +// +// Windows media player encountered a download with wrong number of files. This may happen if some other application is trying to create jobs with same signature as Windows Media Player.%0 +// +#define NS_E_BKGDOWNLOAD_WRONG_NO_FILES _HRESULT_TYPEDEF_(0xC00D125DL) + +// +// MessageId: NS_E_BKGDOWNLOAD_COMPLETECANCELLEDJOB +// +// MessageText: +// +// Windows Media Player tried to complete a download that was already cancelled. The file will not be available.%0 +// +#define NS_E_BKGDOWNLOAD_COMPLETECANCELLEDJOB _HRESULT_TYPEDEF_(0xC00D125EL) + +// +// MessageId: NS_E_BKGDOWNLOAD_CANCELCOMPLETEDJOB +// +// MessageText: +// +// Windows Media Player tried to cancel a download that was already completed. The file will not be removed.%0 +// +#define NS_E_BKGDOWNLOAD_CANCELCOMPLETEDJOB _HRESULT_TYPEDEF_(0xC00D125FL) + +// +// MessageId: NS_E_BKGDOWNLOAD_NOJOBPOINTER +// +// MessageText: +// +// Windows Media Player is trying to access an invalid download.%0 +// +#define NS_E_BKGDOWNLOAD_NOJOBPOINTER _HRESULT_TYPEDEF_(0xC00D1260L) + +// +// MessageId: NS_E_BKGDOWNLOAD_INVALIDJOBSIGNATURE +// +// MessageText: +// +// This download was not created by Windows Media Player.%0 +// +#define NS_E_BKGDOWNLOAD_INVALIDJOBSIGNATURE _HRESULT_TYPEDEF_(0xC00D1261L) + +// +// MessageId: NS_E_BKGDOWNLOAD_FAILED_TO_CREATE_TEMPFILE +// +// MessageText: +// +// The Windows Media Player download manager failed to create a temporary file name. This may happen if the path in invalid or if the disk is full. Please check your system and try again.%0 +// +#define NS_E_BKGDOWNLOAD_FAILED_TO_CREATE_TEMPFILE _HRESULT_TYPEDEF_(0xC00D1262L) + +// +// MessageId: NS_E_BKGDOWNLOAD_PLUGIN_FAILEDINITIALIZE +// +// MessageText: +// +// The Windows Media Player download manager plugin failed to initialize. This may happen if the system is out of resources. Please check your system and try again.%0 +// +#define NS_E_BKGDOWNLOAD_PLUGIN_FAILEDINITIALIZE _HRESULT_TYPEDEF_(0xC00D1263L) + +// +// MessageId: NS_E_BKGDOWNLOAD_PLUGIN_FAILEDTOMOVEFILE +// +// MessageText: +// +// The Windows Media Player download manager failed to move the file.%0 +// +#define NS_E_BKGDOWNLOAD_PLUGIN_FAILEDTOMOVEFILE _HRESULT_TYPEDEF_(0xC00D1264L) + +// +// MessageId: NS_E_BKGDOWNLOAD_CALLFUNCFAILED +// +// MessageText: +// +// Download manager failed to accomplish a task. This happened because the system has no resources to allocate.%0 +// +#define NS_E_BKGDOWNLOAD_CALLFUNCFAILED _HRESULT_TYPEDEF_(0xC00D1265L) + +// +// MessageId: NS_E_BKGDOWNLOAD_CALLFUNCTIMEOUT +// +// MessageText: +// +// The Windows Media Player download manager failed to accomplish a task because the task took too long to execute.%0 +// +#define NS_E_BKGDOWNLOAD_CALLFUNCTIMEOUT _HRESULT_TYPEDEF_(0xC00D1266L) + +// +// MessageId: NS_E_BKGDOWNLOAD_CALLFUNCENDED +// +// MessageText: +// +// The Windows Media Player download manager failed to accomplish a task because Windows Media Player is terminating the service. The task will be recovered when Windows Media Player starts again.%0 +// +#define NS_E_BKGDOWNLOAD_CALLFUNCENDED _HRESULT_TYPEDEF_(0xC00D1267L) + +// +// MessageId: NS_E_BKGDOWNLOAD_WMDUNPACKFAILED +// +// MessageText: +// +// The Windows Media Player download manager failed to unpack a WMD package that was transferred. The file will be deleted and the operation will not be successfully completed.%0 +// +#define NS_E_BKGDOWNLOAD_WMDUNPACKFAILED _HRESULT_TYPEDEF_(0xC00D1268L) + +// +// MessageId: NS_E_BKGDOWNLOAD_FAILEDINITIALIZE +// +// MessageText: +// +// The Windows Media Player download manager failed to initialize. This may happen if the system is out of resources. Please check your system and try again.%0 +// +#define NS_E_BKGDOWNLOAD_FAILEDINITIALIZE _HRESULT_TYPEDEF_(0xC00D1269L) + +// +// MessageId: NS_E_INTERFACE_NOT_REGISTERED_IN_GIT +// +// MessageText: +// +// Windows Media Player failed to access a required functionality. This could be caused by wrong system or media player dlls being loaded.%0 +// +#define NS_E_INTERFACE_NOT_REGISTERED_IN_GIT _HRESULT_TYPEDEF_(0xC00D126AL) + +// +// MessageId: NS_E_BKGDOWNLOAD_INVALID_FILE_NAME +// +// MessageText: +// +// Windows Media Player failed to get the file name of the requested download. The requested download will be canceled.%0 +// +#define NS_E_BKGDOWNLOAD_INVALID_FILE_NAME _HRESULT_TYPEDEF_(0xC00D126BL) + +// +//Image Graph Errors 4750 -- 4800 +// +// +// MessageId: NS_E_IMAGE_DOWNLOAD_FAILED +// +// MessageText: +// +// An error was encountered downloading the image.%0 +// +#define NS_E_IMAGE_DOWNLOAD_FAILED _HRESULT_TYPEDEF_(0xC00D128EL) + +// +// UDRM errors +// +// +// MessageId: NS_E_WMP_UDRM_NOUSERLIST +// +// MessageText: +// +// There was a problem while trying to get the list of activated users for this machine. The License acquisition process will be stopped.%0 +// +#define NS_E_WMP_UDRM_NOUSERLIST _HRESULT_TYPEDEF_(0xC00D12C0L) + +// +// MessageId: NS_E_WMP_DRM_NOT_ACQUIRING +// +// MessageText: +// +// The Windows Media Player is trying to acquire a license for a file that is not being used anymore. The License acquisition process will stop.%0 +// +#define NS_E_WMP_DRM_NOT_ACQUIRING _HRESULT_TYPEDEF_(0xC00D12C1L) + +// +// String is too large +// +// +// MessageId: NS_E_WMP_BSTR_TOO_LONG +// +// MessageText: +// +// The parameter is invalid.%0 +// +#define NS_E_WMP_BSTR_TOO_LONG _HRESULT_TYPEDEF_(0xC00D12F2L) + +// +// Autoplay errors 4860 --- 4870 +// +// +// MessageId: NS_E_WMP_AUTOPLAY_INVALID_STATE +// +// MessageText: +// +// Invalid state for this request.%0 +// +#define NS_E_WMP_AUTOPLAY_INVALID_STATE _HRESULT_TYPEDEF_(0xC00D12FCL) + +// +// CURL Errors 4900 -- 4949 +// +// +// MessageId: NS_E_CURL_NOTSAFE +// +// MessageText: +// +// The URL is not safe for the operation specified.%0 +// +#define NS_E_CURL_NOTSAFE _HRESULT_TYPEDEF_(0xC00D1324L) + +// +// MessageId: NS_E_CURL_INVALIDCHAR +// +// MessageText: +// +// The URL contains one or more invalid characters.%0 +// +#define NS_E_CURL_INVALIDCHAR _HRESULT_TYPEDEF_(0xC00D1325L) + +// +// MessageId: NS_E_CURL_INVALIDHOSTNAME +// +// MessageText: +// +// The URL contains an invalid hostname.%0 +// +#define NS_E_CURL_INVALIDHOSTNAME _HRESULT_TYPEDEF_(0xC00D1326L) + +// +// MessageId: NS_E_CURL_INVALIDPATH +// +// MessageText: +// +// The URL contains an invalid path.%0 +// +#define NS_E_CURL_INVALIDPATH _HRESULT_TYPEDEF_(0xC00D1327L) + +// +// MessageId: NS_E_CURL_INVALIDSCHEME +// +// MessageText: +// +// The URL contains an invalid scheme.%0 +// +#define NS_E_CURL_INVALIDSCHEME _HRESULT_TYPEDEF_(0xC00D1328L) + +// +// MessageId: NS_E_CURL_INVALIDURL +// +// MessageText: +// +// The URL is invalid.%0 +// +#define NS_E_CURL_INVALIDURL _HRESULT_TYPEDEF_(0xC00D1329L) + +// +// MessageId: NS_E_CURL_CANTWALK +// +// MessageText: +// +// The URL would change the root.%0 +// +#define NS_E_CURL_CANTWALK _HRESULT_TYPEDEF_(0xC00D132BL) + +// +// MessageId: NS_E_CURL_INVALIDPORT +// +// MessageText: +// +// The URL port is invalid.%0 +// +#define NS_E_CURL_INVALIDPORT _HRESULT_TYPEDEF_(0xC00D132CL) + +// +// MessageId: NS_E_CURLHELPER_NOTADIRECTORY +// +// MessageText: +// +// The URL is not a directory.%0 +// +#define NS_E_CURLHELPER_NOTADIRECTORY _HRESULT_TYPEDEF_(0xC00D132DL) + +// +// MessageId: NS_E_CURLHELPER_NOTAFILE +// +// MessageText: +// +// The URL is not a file.%0 +// +#define NS_E_CURLHELPER_NOTAFILE _HRESULT_TYPEDEF_(0xC00D132EL) + +// +// MessageId: NS_E_CURL_CANTDECODE +// +// MessageText: +// +// The URL contains characters that could not be decoded. The URL may be truncated or incomplete.%0 +// +#define NS_E_CURL_CANTDECODE _HRESULT_TYPEDEF_(0xC00D132FL) + +// +// MessageId: NS_E_CURLHELPER_NOTRELATIVE +// +// MessageText: +// +// The specified relative URL is actually not a relative URL.%0 +// +#define NS_E_CURLHELPER_NOTRELATIVE _HRESULT_TYPEDEF_(0xC00D1330L) + +// +// MessageId: NS_E_CURL_INVALIDBUFFERSIZE +// +// MessageText: +// +// The buffer is smaller than the size specified.%0 +// +#define NS_E_CURL_INVALIDBUFFERSIZE _HRESULT_TYPEDEF_(0xC00D1355L) + +// +// Subscription Service Errors 4950 -- 4969 +// +// +// MessageId: NS_E_SUBSCRIPTIONSERVICE_PLAYBACK_DISALLOWED +// +// MessageText: +// +// This content is provided by a music service. The content cannot be played, possibly because a valid license does not exist. Please contact the music service with questions.%0 +// +#define NS_E_SUBSCRIPTIONSERVICE_PLAYBACK_DISALLOWED _HRESULT_TYPEDEF_(0xC00D1356L) + +// +// Advanced Edit Dialog Errors 4970 -- 4989 +// +// +// MessageId: NS_E_ADVANCEDEDIT_TOO_MANY_PICTURES +// +// MessageText: +// +// Not all your images were saved to the file. Only 7MB of images may be saved to a media file.%0 +// +#define NS_E_ADVANCEDEDIT_TOO_MANY_PICTURES _HRESULT_TYPEDEF_(0xC00D136AL) + + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Server Errors +// +// IdRange = 5000 - 5999 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_REDIRECT +// +// MessageText: +// +// The client redirected to another server.%0 +// +#define NS_E_REDIRECT _HRESULT_TYPEDEF_(0xC00D1388L) + +// +// MessageId: NS_E_STALE_PRESENTATION +// +// MessageText: +// +// The streaming media description is no longer current.%0 +// +#define NS_E_STALE_PRESENTATION _HRESULT_TYPEDEF_(0xC00D1389L) + + + // Namespace Errors + +// +// MessageId: NS_E_NAMESPACE_WRONG_PERSIST +// +// MessageText: +// +// It is not possible to create a persistent namespace node under a transient parent node.%0 +// +#define NS_E_NAMESPACE_WRONG_PERSIST _HRESULT_TYPEDEF_(0xC00D138AL) + +// +// MessageId: NS_E_NAMESPACE_WRONG_TYPE +// +// MessageText: +// +// It is not possible to store a value in a namespace node that has a different value type.%0 +// +#define NS_E_NAMESPACE_WRONG_TYPE _HRESULT_TYPEDEF_(0xC00D138BL) + +// +// MessageId: NS_E_NAMESPACE_NODE_CONFLICT +// +// MessageText: +// +// It is not possible to remove the root namespace node.%0 +// +#define NS_E_NAMESPACE_NODE_CONFLICT _HRESULT_TYPEDEF_(0xC00D138CL) + +// +// MessageId: NS_E_NAMESPACE_NODE_NOT_FOUND +// +// MessageText: +// +// The specified namespace node could not be found.%0 +// +#define NS_E_NAMESPACE_NODE_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D138DL) + +// +// MessageId: NS_E_NAMESPACE_BUFFER_TOO_SMALL +// +// MessageText: +// +// The buffer supplied to hold namespace node string is too small.%0 +// +#define NS_E_NAMESPACE_BUFFER_TOO_SMALL _HRESULT_TYPEDEF_(0xC00D138EL) + +// +// MessageId: NS_E_NAMESPACE_TOO_MANY_CALLBACKS +// +// MessageText: +// +// The callback list on a namespace node is at the maximum size.%0 +// +#define NS_E_NAMESPACE_TOO_MANY_CALLBACKS _HRESULT_TYPEDEF_(0xC00D138FL) + +// +// MessageId: NS_E_NAMESPACE_DUPLICATE_CALLBACK +// +// MessageText: +// +// It is not possible to register an already-registered callback on a namespace node.%0 +// +#define NS_E_NAMESPACE_DUPLICATE_CALLBACK _HRESULT_TYPEDEF_(0xC00D1390L) + +// +// MessageId: NS_E_NAMESPACE_CALLBACK_NOT_FOUND +// +// MessageText: +// +// Cannot find the callback in the namespace when attempting to remove the callback.%0 +// +#define NS_E_NAMESPACE_CALLBACK_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D1391L) + +// +// MessageId: NS_E_NAMESPACE_NAME_TOO_LONG +// +// MessageText: +// +// The namespace node name exceeds the allowed maximum length.%0 +// +#define NS_E_NAMESPACE_NAME_TOO_LONG _HRESULT_TYPEDEF_(0xC00D1392L) + +// +// MessageId: NS_E_NAMESPACE_DUPLICATE_NAME +// +// MessageText: +// +// Cannot create a namespace node that already exists.%0 +// +#define NS_E_NAMESPACE_DUPLICATE_NAME _HRESULT_TYPEDEF_(0xC00D1393L) + +// +// MessageId: NS_E_NAMESPACE_EMPTY_NAME +// +// MessageText: +// +// The namespace node name cannot be a null string.%0 +// +#define NS_E_NAMESPACE_EMPTY_NAME _HRESULT_TYPEDEF_(0xC00D1394L) + +// +// MessageId: NS_E_NAMESPACE_INDEX_TOO_LARGE +// +// MessageText: +// +// Finding a child namespace node by index failed because the index exceeded the number of children.%0 +// +#define NS_E_NAMESPACE_INDEX_TOO_LARGE _HRESULT_TYPEDEF_(0xC00D1395L) + +// +// MessageId: NS_E_NAMESPACE_BAD_NAME +// +// MessageText: +// +// The namespace node name is invalid.%0 +// +#define NS_E_NAMESPACE_BAD_NAME _HRESULT_TYPEDEF_(0xC00D1396L) + +// +// MessageId: NS_E_NAMESPACE_WRONG_SECURITY +// +// MessageText: +// +// It is not possible to store a value in a namespace node that has a different security type.%0 +// +#define NS_E_NAMESPACE_WRONG_SECURITY _HRESULT_TYPEDEF_(0xC00D1397L) + + + // Cache Errors 5100-5199 + +// +// MessageId: NS_E_CACHE_ARCHIVE_CONFLICT +// +// MessageText: +// +// The archive request conflicts with other requests in progress.%0 +// +#define NS_E_CACHE_ARCHIVE_CONFLICT _HRESULT_TYPEDEF_(0xC00D13ECL) + +// +// MessageId: NS_E_CACHE_ORIGIN_SERVER_NOT_FOUND +// +// MessageText: +// +// The specified origin server cannot be found.%0 +// +#define NS_E_CACHE_ORIGIN_SERVER_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D13EDL) + +// +// MessageId: NS_E_CACHE_ORIGIN_SERVER_TIMEOUT +// +// MessageText: +// +// The specified origin server is not responding.%0 +// +#define NS_E_CACHE_ORIGIN_SERVER_TIMEOUT _HRESULT_TYPEDEF_(0xC00D13EEL) + +// +// MessageId: NS_E_CACHE_NOT_BROADCAST +// +// MessageText: +// +// The internal code for HTTP status code 412 Precondition Failed due to not broadcast type.%0 +// +#define NS_E_CACHE_NOT_BROADCAST _HRESULT_TYPEDEF_(0xC00D13EFL) + +// +// MessageId: NS_E_CACHE_CANNOT_BE_CACHED +// +// MessageText: +// +// The internal code for HTTP status code 403 Forbidden due to not cacheable.%0 +// +#define NS_E_CACHE_CANNOT_BE_CACHED _HRESULT_TYPEDEF_(0xC00D13F0L) + +// +// MessageId: NS_E_CACHE_NOT_MODIFIED +// +// MessageText: +// +// The internal code for HTTP status code 304 Not Modified.%0 +// +#define NS_E_CACHE_NOT_MODIFIED _HRESULT_TYPEDEF_(0xC00D13F1L) + + +// Object Model Errors 5200-5299 + +// +// MessageId: NS_E_CANNOT_REMOVE_PUBLISHING_POINT +// +// MessageText: +// +// It is not possible to remove a cache or proxy publishing point.%0 +// +#define NS_E_CANNOT_REMOVE_PUBLISHING_POINT _HRESULT_TYPEDEF_(0xC00D1450L) + +// +// MessageId: NS_E_CANNOT_REMOVE_PLUGIN +// +// MessageText: +// +// It is not possible to remove the last instance of a type of plug-in.%0 +// +#define NS_E_CANNOT_REMOVE_PLUGIN _HRESULT_TYPEDEF_(0xC00D1451L) + +// +// MessageId: NS_E_WRONG_PUBLISHING_POINT_TYPE +// +// MessageText: +// +// Cache and proxy publishing points do not support this property or method.%0 +// +#define NS_E_WRONG_PUBLISHING_POINT_TYPE _HRESULT_TYPEDEF_(0xC00D1452L) + +// +// MessageId: NS_E_UNSUPPORTED_LOAD_TYPE +// +// MessageText: +// +// The plug-in does not support the specified load type.%0 +// +#define NS_E_UNSUPPORTED_LOAD_TYPE _HRESULT_TYPEDEF_(0xC00D1453L) + +// +// MessageId: NS_E_INVALID_PLUGIN_LOAD_TYPE_CONFIGURATION +// +// MessageText: +// +// The plug-in does not support any load types. The plug-in must support at least one load type.%0 +// +#define NS_E_INVALID_PLUGIN_LOAD_TYPE_CONFIGURATION _HRESULT_TYPEDEF_(0xC00D1454L) + +// +// MessageId: NS_E_INVALID_PUBLISHING_POINT_NAME +// +// MessageText: +// +// The publishing point name is invalid.%0 +// +#define NS_E_INVALID_PUBLISHING_POINT_NAME _HRESULT_TYPEDEF_(0xC00D1455L) + +// +// MessageId: NS_E_TOO_MANY_MULTICAST_SINKS +// +// MessageText: +// +// Only one multicast data writer plug-in can be enabled for a publishing point.%0 +// +#define NS_E_TOO_MANY_MULTICAST_SINKS _HRESULT_TYPEDEF_(0xC00D1456L) + +// +// MessageId: NS_E_PUBLISHING_POINT_INVALID_REQUEST_WHILE_STARTED +// +// MessageText: +// +// The requested operation cannot be completed while the publishing point is started.%0 +// +#define NS_E_PUBLISHING_POINT_INVALID_REQUEST_WHILE_STARTED _HRESULT_TYPEDEF_(0xC00D1457L) + +// +// MessageId: NS_E_MULTICAST_PLUGIN_NOT_ENABLED +// +// MessageText: +// +// A multicast data writer plug-in must be enabled in order for this operation to be completed.%0 +// +#define NS_E_MULTICAST_PLUGIN_NOT_ENABLED _HRESULT_TYPEDEF_(0xC00D1458L) + +// +// MessageId: NS_E_INVALID_OPERATING_SYSTEM_VERSION +// +// MessageText: +// +// This feature requires Windows .NET Enterprise Server.%0 +// +#define NS_E_INVALID_OPERATING_SYSTEM_VERSION _HRESULT_TYPEDEF_(0xC00D1459L) + +// +// MessageId: NS_E_PUBLISHING_POINT_REMOVED +// +// MessageText: +// +// The requested operation cannot be completed because the specified publishing point has been removed.%0 +// +#define NS_E_PUBLISHING_POINT_REMOVED _HRESULT_TYPEDEF_(0xC00D145AL) + +// +// MessageId: NS_E_INVALID_PUSH_PUBLISHING_POINT_START_REQUEST +// +// MessageText: +// +// Push publishing points are started when the encoder starts pushing the stream. This publishing point cannot be started by the server administrator.%0 +// +#define NS_E_INVALID_PUSH_PUBLISHING_POINT_START_REQUEST _HRESULT_TYPEDEF_(0xC00D145BL) + +// +// MessageId: NS_E_UNSUPPORTED_LANGUAGE +// +// MessageText: +// +// The specified language is not supported.%0 +// +#define NS_E_UNSUPPORTED_LANGUAGE _HRESULT_TYPEDEF_(0xC00D145CL) + +// +// MessageId: NS_E_WRONG_OS_VERSION +// +// MessageText: +// +// Windows Media Services will only run on Windows .NET Server and Windows .NET Enterprise Server.%0 +// +#define NS_E_WRONG_OS_VERSION _HRESULT_TYPEDEF_(0xC00D145DL) + +// +// MessageId: NS_E_PUBLISHING_POINT_STOPPED +// +// MessageText: +// +// The operation cannot be completed because the publishing point has been stopped.%0 +// +#define NS_E_PUBLISHING_POINT_STOPPED _HRESULT_TYPEDEF_(0xC00D145EL) + + +// Playlist Errors 5300-5399 + +// +// MessageId: NS_E_PLAYLIST_ENTRY_ALREADY_PLAYING +// +// MessageText: +// +// The playlist entry is already playing.%0 +// +#define NS_E_PLAYLIST_ENTRY_ALREADY_PLAYING _HRESULT_TYPEDEF_(0xC00D14B4L) + +// +// MessageId: NS_E_EMPTY_PLAYLIST +// +// MessageText: +// +// The playlist or directory you are requesting does not contain content.%0 +// +#define NS_E_EMPTY_PLAYLIST _HRESULT_TYPEDEF_(0xC00D14B5L) + +// +// MessageId: NS_E_PLAYLIST_PARSE_FAILURE +// +// MessageText: +// +// The server was unable to parse the requested playlist file.%0 +// +#define NS_E_PLAYLIST_PARSE_FAILURE _HRESULT_TYPEDEF_(0xC00D14B6L) + +// +// MessageId: NS_E_PLAYLIST_UNSUPPORTED_ENTRY +// +// MessageText: +// +// The requested operation is not supported for this type of playlist entry.%0 +// +#define NS_E_PLAYLIST_UNSUPPORTED_ENTRY _HRESULT_TYPEDEF_(0xC00D14B7L) + +// +// MessageId: NS_E_PLAYLIST_ENTRY_NOT_IN_PLAYLIST +// +// MessageText: +// +// Cannot jump to a playlist entry that is not inserted in the playlist.%0 +// +#define NS_E_PLAYLIST_ENTRY_NOT_IN_PLAYLIST _HRESULT_TYPEDEF_(0xC00D14B8L) + +// +// MessageId: NS_E_PLAYLIST_ENTRY_SEEK +// +// MessageText: +// +// Cannot seek to the desired playlist entry.%0 +// +#define NS_E_PLAYLIST_ENTRY_SEEK _HRESULT_TYPEDEF_(0xC00D14B9L) + +// +// MessageId: NS_E_PLAYLIST_RECURSIVE_PLAYLISTS +// +// MessageText: +// +// Cannot play recursive playlist.%0 +// +#define NS_E_PLAYLIST_RECURSIVE_PLAYLISTS _HRESULT_TYPEDEF_(0xC00D14BAL) + +// +// MessageId: NS_E_PLAYLIST_TOO_MANY_NESTED_PLAYLISTS +// +// MessageText: +// +// The number of nested playlists exceeded the limit the server can handle.%0 +// +#define NS_E_PLAYLIST_TOO_MANY_NESTED_PLAYLISTS _HRESULT_TYPEDEF_(0xC00D14BBL) + +// +// MessageId: NS_E_PLAYLIST_SHUTDOWN +// +// MessageText: +// +// Cannot execute the requested operation because the playlist has been shut down by the Media Server.%0 +// +#define NS_E_PLAYLIST_SHUTDOWN _HRESULT_TYPEDEF_(0xC00D14BCL) + +// +// MessageId: NS_E_PLAYLIST_END_RECEDING +// +// MessageText: +// +// The playlist has ended while receding.%0 +// +#define NS_E_PLAYLIST_END_RECEDING _HRESULT_TYPEDEF_(0xC00D14BDL) + +// +// MessageId: NS_I_PLAYLIST_CHANGE_RECEDING +// +// MessageText: +// +// The playlist change occurred while receding.%0 +// +#define NS_I_PLAYLIST_CHANGE_RECEDING _HRESULT_TYPEDEF_(0x400D14BEL) + + +// Datapath Errors -- 5400 - 5499 + +// +// MessageId: NS_E_DATAPATH_NO_SINK +// +// MessageText: +// +// The data path does not have an associated data writer plug-in.%0 +// +#define NS_E_DATAPATH_NO_SINK _HRESULT_TYPEDEF_(0xC00D1518L) + +// +// MessageId: NS_S_PUBLISHING_POINT_STARTED_WITH_FAILED_SINKS +// +// MessageText: +// +// The publishing point successfully started, but one or more of the requested data writer plug-ins failed.%0 +// +#define NS_S_PUBLISHING_POINT_STARTED_WITH_FAILED_SINKS _HRESULT_TYPEDEF_(0x000D1519L) + +// +// MessageId: NS_E_INVALID_PUSH_TEMPLATE +// +// MessageText: +// +// The specified push template is invalid.%0 +// +#define NS_E_INVALID_PUSH_TEMPLATE _HRESULT_TYPEDEF_(0xC00D151AL) + +// +// MessageId: NS_E_INVALID_PUSH_PUBLISHING_POINT +// +// MessageText: +// +// The specified push publishing point is invalid.%0 +// +#define NS_E_INVALID_PUSH_PUBLISHING_POINT _HRESULT_TYPEDEF_(0xC00D151BL) + +// +// MessageId: NS_E_CRITICAL_ERROR +// +// MessageText: +// +// The requested operation cannot be performed because the server or publishing point is in a critical error state.%0 +// +#define NS_E_CRITICAL_ERROR _HRESULT_TYPEDEF_(0xC00D151CL) + +// +// MessageId: NS_E_NO_NEW_CONNECTIONS +// +// MessageText: +// +// The content can not be played because the server is not currently accepting connections. Try connecting at a later time.%0 +// +#define NS_E_NO_NEW_CONNECTIONS _HRESULT_TYPEDEF_(0xC00D151DL) + +// +// MessageId: NS_E_WSX_INVALID_VERSION +// +// MessageText: +// +// The version of this playlist is not supported by the server.%0 +// +#define NS_E_WSX_INVALID_VERSION _HRESULT_TYPEDEF_(0xC00D151EL) + +// +// MessageId: NS_E_HEADER_MISMATCH +// +// MessageText: +// +// The command does not apply to the current media header user by a server component.%0 +// +#define NS_E_HEADER_MISMATCH _HRESULT_TYPEDEF_(0xC00D151FL) + +// +// MessageId: NS_E_PUSH_DUPLICATE_PUBLISHING_POINT_NAME +// +// MessageText: +// +// The specified publishing point name is already in use.%0 +// +#define NS_E_PUSH_DUPLICATE_PUBLISHING_POINT_NAME _HRESULT_TYPEDEF_(0xC00D1520L) + + +// Plugin Errors -- 5500 - 5599 + +// +// MessageId: NS_E_NO_SCRIPT_ENGINE +// +// MessageText: +// +// There is no script engine available for this file.%0 +// +#define NS_E_NO_SCRIPT_ENGINE _HRESULT_TYPEDEF_(0xC00D157CL) + +// +// MessageId: NS_E_PLUGIN_ERROR_REPORTED +// +// MessageText: +// +// The plug-in has reported an error. See the Troubleshooting tab or the NT Application Event Log for details.%0 +// +#define NS_E_PLUGIN_ERROR_REPORTED _HRESULT_TYPEDEF_(0xC00D157DL) + +// +// MessageId: NS_E_SOURCE_PLUGIN_NOT_FOUND +// +// MessageText: +// +// No enabled data source plug-in is available to access the requested content.%0 +// +#define NS_E_SOURCE_PLUGIN_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D157EL) + +// +// MessageId: NS_E_PLAYLIST_PLUGIN_NOT_FOUND +// +// MessageText: +// +// No enabled playlist parser plug-in is available to access the requested content.%0 +// +#define NS_E_PLAYLIST_PLUGIN_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D157FL) + +// +// MessageId: NS_E_DATA_SOURCE_ENUMERATION_NOT_SUPPORTED +// +// MessageText: +// +// The data source plug-in does not support enumeration.%0 +// +#define NS_E_DATA_SOURCE_ENUMERATION_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xC00D1580L) + +// +// MessageId: NS_E_MEDIA_PARSER_INVALID_FORMAT +// +// MessageText: +// +// The server cannot stream the selected file because it is either damaged or corrupt. Select a different file.%0 +// +#define NS_E_MEDIA_PARSER_INVALID_FORMAT _HRESULT_TYPEDEF_(0xC00D1581L) + +// +// MessageId: NS_E_SCRIPT_DEBUGGER_NOT_INSTALLED +// +// MessageText: +// +// The plug-in cannot be enabled because a compatible script debugger is not installed on this system. Install a script debugger, or disable the script debugger option on the general tab of the plug-in's properties page and try again.%0 +// +#define NS_E_SCRIPT_DEBUGGER_NOT_INSTALLED _HRESULT_TYPEDEF_(0xC00D1582L) + +// +// MessageId: NS_E_FEATURE_REQUIRES_ENTERPRISE_SERVER +// +// MessageText: +// +// The plug-in cannot be loaded because it requires Windows .NET Enterprise Server.%0 +// +#define NS_E_FEATURE_REQUIRES_ENTERPRISE_SERVER _HRESULT_TYPEDEF_(0xC00D1583L) + +// +// MessageId: NS_E_WIZARD_RUNNING +// +// MessageText: +// +// Another wizard is currently running. Please close the other wizard or wait until it finishes before attempting to run this wizard again.%0 +// +#define NS_E_WIZARD_RUNNING _HRESULT_TYPEDEF_(0xC00D1584L) + +// +// MessageId: NS_E_INVALID_LOG_URL +// +// MessageText: +// +// Invalid log URL. Multicast logging URL must look like "http://servername/isapibackend.dll" .%0 +// +#define NS_E_INVALID_LOG_URL _HRESULT_TYPEDEF_(0xC00D1585L) + +// +// MessageId: NS_E_INVALID_MTU_RANGE +// +// MessageText: +// +// Invalid MTU specified. The valid range for maximum packet size is between 36 and 65507 bytes .%0 +// +#define NS_E_INVALID_MTU_RANGE _HRESULT_TYPEDEF_(0xC00D1586L) + +// +// MessageId: NS_E_INVALID_PLAY_STATISTICS +// +// MessageText: +// +// Invalid play statistics for logging .%0 +// +#define NS_E_INVALID_PLAY_STATISTICS _HRESULT_TYPEDEF_(0xC00D1587L) + +// +// MessageId: NS_E_LOG_NEED_TO_BE_SKIPPED +// +// MessageText: +// +// The log needs to be skipped .%0 +// +#define NS_E_LOG_NEED_TO_BE_SKIPPED _HRESULT_TYPEDEF_(0xC00D1588L) + +// +// MessageId: NS_E_HTTP_TEXT_DATACONTAINER_SIZE_LIMIT_EXCEEDED +// +// MessageText: +// +// The size of the data exceeded the limit the WMS HTTP Download Data Source plugin can handle.%0 +// +#define NS_E_HTTP_TEXT_DATACONTAINER_SIZE_LIMIT_EXCEEDED _HRESULT_TYPEDEF_(0xC00D1589L) + +// +// MessageId: NS_E_PORT_IN_USE +// +// MessageText: +// +// One usage of each socket address (protocol/network address/port) is permitted. Verify that other services or applications are not attempting to use the same port and then try to enable the plug-in again.%0 +// +#define NS_E_PORT_IN_USE _HRESULT_TYPEDEF_(0xC00D158AL) + +// +// MessageId: NS_E_PORT_IN_USE_HTTP +// +// MessageText: +// +// One usage of each socket address (protocol/network address/port) is permitted. Verify that other services (such as IIS) or applications are not attempting to use the same port and then try to enable the plug-in again.%0 +// +#define NS_E_PORT_IN_USE_HTTP _HRESULT_TYPEDEF_(0xC00D158BL) + +// +// MessageId: NS_E_HTTP_TEXT_DATACONTAINER_INVALID_SERVER_RESPONSE +// +// MessageText: +// +// The WMS HTTP Download Data Source plugin was unable to receive the remote server's response.%0 +// +#define NS_E_HTTP_TEXT_DATACONTAINER_INVALID_SERVER_RESPONSE _HRESULT_TYPEDEF_(0xC00D158CL) + +// +// MessageId: NS_E_ARCHIVE_REACH_QUOTA +// +// MessageText: +// +// The archive plug-in has reached its quota.%0 +// +#define NS_E_ARCHIVE_REACH_QUOTA _HRESULT_TYPEDEF_(0xC00D158DL) + +// +// MessageId: NS_E_ARCHIVE_ABORT_DUE_TO_BCAST +// +// MessageText: +// +// The archive plug-in aborted because the source was from broadcast.%0 +// +#define NS_E_ARCHIVE_ABORT_DUE_TO_BCAST _HRESULT_TYPEDEF_(0xC00D158EL) + +// +// MessageId: NS_E_ARCHIVE_GAP_DETECTED +// +// MessageText: +// +// The archive plug-in detected an interrupt in the source.%0 +// +#define NS_E_ARCHIVE_GAP_DETECTED _HRESULT_TYPEDEF_(0xC00D158FL) + + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Tools Errors +// +// IdRange = 7000 - 7999 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_BAD_MARKIN +// +// MessageText: +// +// The mark-in time should be greater than 0 and less than the mark-out time.%0 +// +#define NS_E_BAD_MARKIN _HRESULT_TYPEDEF_(0xC00D1B58L) + +// +// MessageId: NS_E_BAD_MARKOUT +// +// MessageText: +// +// The mark-out time should be greater than the mark-in time and less than the file duration.%0 +// +#define NS_E_BAD_MARKOUT _HRESULT_TYPEDEF_(0xC00D1B59L) + +// +// MessageId: NS_E_NOMATCHING_MEDIASOURCE +// +// MessageText: +// +// No matching media type is found in the source %1.%0 +// +#define NS_E_NOMATCHING_MEDIASOURCE _HRESULT_TYPEDEF_(0xC00D1B5AL) + +// +// MessageId: NS_E_UNSUPPORTED_SOURCETYPE +// +// MessageText: +// +// The specified source type is not supported.%0 +// +#define NS_E_UNSUPPORTED_SOURCETYPE _HRESULT_TYPEDEF_(0xC00D1B5BL) + +// +// MessageId: NS_E_TOO_MANY_AUDIO +// +// MessageText: +// +// It is not possible to specify more than one audio input.%0 +// +#define NS_E_TOO_MANY_AUDIO _HRESULT_TYPEDEF_(0xC00D1B5CL) + +// +// MessageId: NS_E_TOO_MANY_VIDEO +// +// MessageText: +// +// It is not possible to specify more than two video inputs.%0 +// +#define NS_E_TOO_MANY_VIDEO _HRESULT_TYPEDEF_(0xC00D1B5DL) + +// +// MessageId: NS_E_NOMATCHING_ELEMENT +// +// MessageText: +// +// No matching element is found in the list.%0 +// +#define NS_E_NOMATCHING_ELEMENT _HRESULT_TYPEDEF_(0xC00D1B5EL) + +// +// MessageId: NS_E_MISMATCHED_MEDIACONTENT +// +// MessageText: +// +// The profile's media types must match the media types defined for the session.%0 +// +#define NS_E_MISMATCHED_MEDIACONTENT _HRESULT_TYPEDEF_(0xC00D1B5FL) + +// +// MessageId: NS_E_CANNOT_DELETE_ACTIVE_SOURCEGROUP +// +// MessageText: +// +// It is not possible to remove an active source while encoding.%0 +// +#define NS_E_CANNOT_DELETE_ACTIVE_SOURCEGROUP _HRESULT_TYPEDEF_(0xC00D1B60L) + +// +// MessageId: NS_E_AUDIODEVICE_BUSY +// +// MessageText: +// +// It is not possible to open the specified audio capture device because it is currently in use.%0 +// +#define NS_E_AUDIODEVICE_BUSY _HRESULT_TYPEDEF_(0xC00D1B61L) + +// +// MessageId: NS_E_AUDIODEVICE_UNEXPECTED +// +// MessageText: +// +// It is not possible to open the specified audio capture device because an unexpected error has occurred.%0 +// +#define NS_E_AUDIODEVICE_UNEXPECTED _HRESULT_TYPEDEF_(0xC00D1B62L) + +// +// MessageId: NS_E_AUDIODEVICE_BADFORMAT +// +// MessageText: +// +// The audio capture device does not support the specified audio format.%0 +// +#define NS_E_AUDIODEVICE_BADFORMAT _HRESULT_TYPEDEF_(0xC00D1B63L) + +// +// MessageId: NS_E_VIDEODEVICE_BUSY +// +// MessageText: +// +// It is not possible to open the specified video capture device because it is currently in use.%0 +// +#define NS_E_VIDEODEVICE_BUSY _HRESULT_TYPEDEF_(0xC00D1B64L) + +// +// MessageId: NS_E_VIDEODEVICE_UNEXPECTED +// +// MessageText: +// +// It is not possible to open the specified video capture device because an unexpected error has occurred.%0 +// +#define NS_E_VIDEODEVICE_UNEXPECTED _HRESULT_TYPEDEF_(0xC00D1B65L) + +// +// MessageId: NS_E_INVALIDCALL_WHILE_ENCODER_RUNNING +// +// MessageText: +// +// This operation is not allowed while encoding.%0 +// +#define NS_E_INVALIDCALL_WHILE_ENCODER_RUNNING _HRESULT_TYPEDEF_(0xC00D1B66L) + +// +// MessageId: NS_E_NO_PROFILE_IN_SOURCEGROUP +// +// MessageText: +// +// No profile is set for the source.%0 +// +#define NS_E_NO_PROFILE_IN_SOURCEGROUP _HRESULT_TYPEDEF_(0xC00D1B67L) + +// +// MessageId: NS_E_VIDEODRIVER_UNSTABLE +// +// MessageText: +// +// The video capture driver returned an unrecoverable error. It is now in an unstable state.%0 +// +#define NS_E_VIDEODRIVER_UNSTABLE _HRESULT_TYPEDEF_(0xC00D1B68L) + +// +// MessageId: NS_E_VIDCAPSTARTFAILED +// +// MessageText: +// +// It was not possible to start the video device.%0 +// +#define NS_E_VIDCAPSTARTFAILED _HRESULT_TYPEDEF_(0xC00D1B69L) + +// +// MessageId: NS_E_VIDSOURCECOMPRESSION +// +// MessageText: +// +// The video source does not support the requested output format or color depth.%0 +// +#define NS_E_VIDSOURCECOMPRESSION _HRESULT_TYPEDEF_(0xC00D1B6AL) + +// +// MessageId: NS_E_VIDSOURCESIZE +// +// MessageText: +// +// The video source does not support the requested capture size.%0 +// +#define NS_E_VIDSOURCESIZE _HRESULT_TYPEDEF_(0xC00D1B6BL) + +// +// MessageId: NS_E_ICMQUERYFORMAT +// +// MessageText: +// +// It was not possible to obtain output information from the video compressor.%0 +// +#define NS_E_ICMQUERYFORMAT _HRESULT_TYPEDEF_(0xC00D1B6CL) + +// +// MessageId: NS_E_VIDCAPCREATEWINDOW +// +// MessageText: +// +// It was not possible to create a video capture window.%0 +// +#define NS_E_VIDCAPCREATEWINDOW _HRESULT_TYPEDEF_(0xC00D1B6DL) + +// +// MessageId: NS_E_VIDCAPDRVINUSE +// +// MessageText: +// +// There is already a stream active on this video device.%0 +// +#define NS_E_VIDCAPDRVINUSE _HRESULT_TYPEDEF_(0xC00D1B6EL) + +// +// MessageId: NS_E_NO_MEDIAFORMAT_IN_SOURCE +// +// MessageText: +// +// No media format is set in source.%0 +// +#define NS_E_NO_MEDIAFORMAT_IN_SOURCE _HRESULT_TYPEDEF_(0xC00D1B6FL) + +// +// MessageId: NS_E_NO_VALID_OUTPUT_STREAM +// +// MessageText: +// +// Cannot find a valid output stream from the source.%0 +// +#define NS_E_NO_VALID_OUTPUT_STREAM _HRESULT_TYPEDEF_(0xC00D1B70L) + +// +// MessageId: NS_E_NO_VALID_SOURCE_PLUGIN +// +// MessageText: +// +// It was not possible to find a valid source plug-in for the specified source.%0 +// +#define NS_E_NO_VALID_SOURCE_PLUGIN _HRESULT_TYPEDEF_(0xC00D1B71L) + +// +// MessageId: NS_E_NO_ACTIVE_SOURCEGROUP +// +// MessageText: +// +// No source is currently active.%0 +// +#define NS_E_NO_ACTIVE_SOURCEGROUP _HRESULT_TYPEDEF_(0xC00D1B72L) + +// +// MessageId: NS_E_NO_SCRIPT_STREAM +// +// MessageText: +// +// No script stream is set in the current source.%0 +// +#define NS_E_NO_SCRIPT_STREAM _HRESULT_TYPEDEF_(0xC00D1B73L) + +// +// MessageId: NS_E_INVALIDCALL_WHILE_ARCHIVAL_RUNNING +// +// MessageText: +// +// This operation is not allowed while archiving.%0 +// +#define NS_E_INVALIDCALL_WHILE_ARCHIVAL_RUNNING _HRESULT_TYPEDEF_(0xC00D1B74L) + +// +// MessageId: NS_E_INVALIDPACKETSIZE +// +// MessageText: +// +// The setting for the maximum packet size is not valid.%0 +// +#define NS_E_INVALIDPACKETSIZE _HRESULT_TYPEDEF_(0xC00D1B75L) + +// +// MessageId: NS_E_PLUGIN_CLSID_INVALID +// +// MessageText: +// +// The plug-in CLSID specified is not valid.%0 +// +#define NS_E_PLUGIN_CLSID_INVALID _HRESULT_TYPEDEF_(0xC00D1B76L) + +// +// MessageId: NS_E_UNSUPPORTED_ARCHIVETYPE +// +// MessageText: +// +// This archive type is not supported.%0 +// +#define NS_E_UNSUPPORTED_ARCHIVETYPE _HRESULT_TYPEDEF_(0xC00D1B77L) + +// +// MessageId: NS_E_UNSUPPORTED_ARCHIVEOPERATION +// +// MessageText: +// +// This archive operation is not supported.%0 +// +#define NS_E_UNSUPPORTED_ARCHIVEOPERATION _HRESULT_TYPEDEF_(0xC00D1B78L) + +// +// MessageId: NS_E_ARCHIVE_FILENAME_NOTSET +// +// MessageText: +// +// The local archive file name was not set.%0 +// +#define NS_E_ARCHIVE_FILENAME_NOTSET _HRESULT_TYPEDEF_(0xC00D1B79L) + +// +// MessageId: NS_E_SOURCEGROUP_NOTPREPARED +// +// MessageText: +// +// The source is not yet prepared.%0 +// +#define NS_E_SOURCEGROUP_NOTPREPARED _HRESULT_TYPEDEF_(0xC00D1B7AL) + +// +// MessageId: NS_E_PROFILE_MISMATCH +// +// MessageText: +// +// Profiles on the sources do not match.%0 +// +#define NS_E_PROFILE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1B7BL) + +// +// MessageId: NS_E_INCORRECTCLIPSETTINGS +// +// MessageText: +// +// The specified crop values are not valid.%0 +// +#define NS_E_INCORRECTCLIPSETTINGS _HRESULT_TYPEDEF_(0xC00D1B7CL) + +// +// MessageId: NS_E_NOSTATSAVAILABLE +// +// MessageText: +// +// No statistics are available at this time.%0 +// +#define NS_E_NOSTATSAVAILABLE _HRESULT_TYPEDEF_(0xC00D1B7DL) + +// +// MessageId: NS_E_NOTARCHIVING +// +// MessageText: +// +// The encoder is not archiving.%0 +// +#define NS_E_NOTARCHIVING _HRESULT_TYPEDEF_(0xC00D1B7EL) + +// +// MessageId: NS_E_INVALIDCALL_WHILE_ENCODER_STOPPED +// +// MessageText: +// +// This operation is only allowed during encoding.%0 +// +#define NS_E_INVALIDCALL_WHILE_ENCODER_STOPPED _HRESULT_TYPEDEF_(0xC00D1B7FL) + +// +// MessageId: NS_E_NOSOURCEGROUPS +// +// MessageText: +// +// This SourceGroupCollection doesn't contain any SourceGroups.%0 +// +#define NS_E_NOSOURCEGROUPS _HRESULT_TYPEDEF_(0xC00D1B80L) + +// +// MessageId: NS_E_INVALIDINPUTFPS +// +// MessageText: +// +// This source does not have a frame rate of 30 fps. Therefore, it is not possible to apply the inverse telecine filter to the source.%0 +// +#define NS_E_INVALIDINPUTFPS _HRESULT_TYPEDEF_(0xC00D1B81L) + +// +// MessageId: NS_E_NO_DATAVIEW_SUPPORT +// +// MessageText: +// +// It is not possible to display your source or output video in the Video panel.%0 +// +#define NS_E_NO_DATAVIEW_SUPPORT _HRESULT_TYPEDEF_(0xC00D1B82L) + +// +// MessageId: NS_E_CODEC_UNAVAILABLE +// +// MessageText: +// +// One or more codecs required to open this content could not be found.%0 +// +#define NS_E_CODEC_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D1B83L) + +// +// MessageId: NS_E_ARCHIVE_SAME_AS_INPUT +// +// MessageText: +// +// The archive file has the same name as an input file. Change one of the names before continuing.%0 +// +#define NS_E_ARCHIVE_SAME_AS_INPUT _HRESULT_TYPEDEF_(0xC00D1B84L) + +// +// MessageId: NS_E_SOURCE_NOTSPECIFIED +// +// MessageText: +// +// The source has not been set up completely.%0 +// +#define NS_E_SOURCE_NOTSPECIFIED _HRESULT_TYPEDEF_(0xC00D1B85L) + +// +// MessageId: NS_E_NO_REALTIME_TIMECOMPRESSION +// +// MessageText: +// +// It is not possible to apply time compression to a broadcast session.%0 +// +#define NS_E_NO_REALTIME_TIMECOMPRESSION _HRESULT_TYPEDEF_(0xC00D1B86L) + +// +// MessageId: NS_E_UNSUPPORTED_ENCODER_DEVICE +// +// MessageText: +// +// It is not possible to open this device.%0 +// +#define NS_E_UNSUPPORTED_ENCODER_DEVICE _HRESULT_TYPEDEF_(0xC00D1B87L) + +// +// MessageId: NS_E_UNEXPECTED_DISPLAY_SETTINGS +// +// MessageText: +// +// It is not possible to start encoding because the display size or color has changed since the current session was defined. Restore the previous settings or create a new session.%0 +// +#define NS_E_UNEXPECTED_DISPLAY_SETTINGS _HRESULT_TYPEDEF_(0xC00D1B88L) + +// +// MessageId: NS_E_NO_AUDIODATA +// +// MessageText: +// +// No audio data has been received for several seconds. Check the audio source and restart the encoder.%0 +// +#define NS_E_NO_AUDIODATA _HRESULT_TYPEDEF_(0xC00D1B89L) + +// +// MessageId: NS_E_INPUTSOURCE_PROBLEM +// +// MessageText: +// +// One or all of the specified sources are not working properly. Check that the sources are configured correctly.%0 +// +#define NS_E_INPUTSOURCE_PROBLEM _HRESULT_TYPEDEF_(0xC00D1B8AL) + +// +// MessageId: NS_E_WME_VERSION_MISMATCH +// +// MessageText: +// +// The supplied configuration file is not supported by this version of the encoder.%0 +// +#define NS_E_WME_VERSION_MISMATCH _HRESULT_TYPEDEF_(0xC00D1B8BL) + +// +// MessageId: NS_E_NO_REALTIME_PREPROCESS +// +// MessageText: +// +// It is not possible to use image preprocessing with live encoding.%0 +// +#define NS_E_NO_REALTIME_PREPROCESS _HRESULT_TYPEDEF_(0xC00D1B8CL) + +// +// MessageId: NS_E_NO_REPEAT_PREPROCESS +// +// MessageText: +// +// It is not possible to use two-pass encoding when the source is set to loop.%0 +// +#define NS_E_NO_REPEAT_PREPROCESS _HRESULT_TYPEDEF_(0xC00D1B8DL) + +// +// MessageId: NS_E_CANNOT_PAUSE_LIVEBROADCAST +// +// MessageText: +// +// It is not possible to pause encoding during a broadcast.%0 +// +#define NS_E_CANNOT_PAUSE_LIVEBROADCAST _HRESULT_TYPEDEF_(0xC00D1B8EL) + +// +// MessageId: NS_E_DRM_PROFILE_NOT_SET +// +// MessageText: +// +// A DRM profile has not been set for the current session.%0 +// +#define NS_E_DRM_PROFILE_NOT_SET _HRESULT_TYPEDEF_(0xC00D1B8FL) + +// +// MessageId: NS_E_DUPLICATE_DRMPROFILE +// +// MessageText: +// +// The profile ID is already used by a DRM profile. Specify a different profile ID.%0 +// +#define NS_E_DUPLICATE_DRMPROFILE _HRESULT_TYPEDEF_(0xC00D1B90L) + +// +// MessageId: NS_E_INVALID_DEVICE +// +// MessageText: +// +// The setting of the selected device does not support control for playing back tapes.%0 +// +#define NS_E_INVALID_DEVICE _HRESULT_TYPEDEF_(0xC00D1B91L) + +// +// MessageId: NS_E_SPEECHEDL_ON_NON_MIXEDMODE +// +// MessageText: +// +// You must specify a mixed voice and audio mode in order to use an optimization definition file.%0 +// +#define NS_E_SPEECHEDL_ON_NON_MIXEDMODE _HRESULT_TYPEDEF_(0xC00D1B92L) + +// +// MessageId: NS_E_DRM_PASSWORD_TOO_LONG +// +// MessageText: +// +// The specified password is too long. Type a password with fewer than 8 characters.%0 +// +#define NS_E_DRM_PASSWORD_TOO_LONG _HRESULT_TYPEDEF_(0xC00D1B93L) + +// +// MessageId: NS_E_DEVCONTROL_FAILED_SEEK +// +// MessageText: +// +// It is not possible to seek to the specified mark-in point.%0 +// +#define NS_E_DEVCONTROL_FAILED_SEEK _HRESULT_TYPEDEF_(0xC00D1B94L) + +// +// MessageId: NS_E_INTERLACE_REQUIRE_SAMESIZE +// +// MessageText: +// +// When you choose to maintain the interlacing in your video, the output video size must match the input video size.%0 +// +#define NS_E_INTERLACE_REQUIRE_SAMESIZE _HRESULT_TYPEDEF_(0xC00D1B95L) + +// +// MessageId: NS_E_TOO_MANY_DEVICECONTROL +// +// MessageText: +// +// Only one device control plug-in can control a device.%0 +// +#define NS_E_TOO_MANY_DEVICECONTROL _HRESULT_TYPEDEF_(0xC00D1B96L) + +// +// MessageId: NS_E_NO_MULTIPASS_FOR_LIVEDEVICE +// +// MessageText: +// +// You must also enable storing content to hard disk temporarily in order to use two-pass encoding with the input device.%0 +// +#define NS_E_NO_MULTIPASS_FOR_LIVEDEVICE _HRESULT_TYPEDEF_(0xC00D1B97L) + +// +// MessageId: NS_E_MISSING_AUDIENCE +// +// MessageText: +// +// An audience is missing from the output stream configuration.%0 +// +#define NS_E_MISSING_AUDIENCE _HRESULT_TYPEDEF_(0xC00D1B98L) + +// +// MessageId: NS_E_AUDIENCE_CONTENTTYPE_MISMATCH +// +// MessageText: +// +// All audiences in the output tree must have the same content type.%0 +// +#define NS_E_AUDIENCE_CONTENTTYPE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1B99L) + +// +// MessageId: NS_E_MISSING_SOURCE_INDEX +// +// MessageText: +// +// A source index is missing from the output stream configuration.%0 +// +#define NS_E_MISSING_SOURCE_INDEX _HRESULT_TYPEDEF_(0xC00D1B9AL) + +// +// MessageId: NS_E_NUM_LANGUAGE_MISMATCH +// +// MessageText: +// +// The same source index in different audiences should have the same number of languages.%0 +// +#define NS_E_NUM_LANGUAGE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1B9BL) + +// +// MessageId: NS_E_LANGUAGE_MISMATCH +// +// MessageText: +// +// The same source index in different audiences should have the same languages.%0 +// +#define NS_E_LANGUAGE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1B9CL) + +// +// MessageId: NS_E_VBRMODE_MISMATCH +// +// MessageText: +// +// The same source index in different audiences should use the same VBR encoding mode.%0 +// +#define NS_E_VBRMODE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1B9DL) + +// +// MessageId: NS_E_INVALID_INPUT_AUDIENCE_INDEX +// +// MessageText: +// +// The bit rate index specified is not valid.%0 +// +#define NS_E_INVALID_INPUT_AUDIENCE_INDEX _HRESULT_TYPEDEF_(0xC00D1B9EL) + +// +// MessageId: NS_E_INVALID_INPUT_LANGUAGE +// +// MessageText: +// +// The specified language is not valid.%0 +// +#define NS_E_INVALID_INPUT_LANGUAGE _HRESULT_TYPEDEF_(0xC00D1B9FL) + +// +// MessageId: NS_E_INVALID_INPUT_STREAM +// +// MessageText: +// +// The specified source type is not valid.%0 +// +#define NS_E_INVALID_INPUT_STREAM _HRESULT_TYPEDEF_(0xC00D1BA0L) + +// +// MessageId: NS_E_EXPECT_MONO_WAV_INPUT +// +// MessageText: +// +// The source must be a mono channel .wav file.%0 +// +#define NS_E_EXPECT_MONO_WAV_INPUT _HRESULT_TYPEDEF_(0xC00D1BA1L) + +// +// MessageId: NS_E_INPUT_WAVFORMAT_MISMATCH +// +// MessageText: +// +// All the source .wav files must have the same format.%0 +// +#define NS_E_INPUT_WAVFORMAT_MISMATCH _HRESULT_TYPEDEF_(0xC00D1BA2L) + +// +// MessageId: NS_E_RECORDQ_DISK_FULL +// +// MessageText: +// +// The hard disk being used for temporary storage of content has reached the minimum allowed disk space. Create more space on the hard disk and restart encoding.%0 +// +#define NS_E_RECORDQ_DISK_FULL _HRESULT_TYPEDEF_(0xC00D1BA3L) + +// +// MessageId: NS_E_NO_PAL_INVERSE_TELECINE +// +// MessageText: +// +// It is not possible to apply the inverse telecine feature to PAL content.%0 +// +#define NS_E_NO_PAL_INVERSE_TELECINE _HRESULT_TYPEDEF_(0xC00D1BA4L) + +// +// MessageId: NS_E_ACTIVE_SG_DEVICE_DISCONNECTED +// +// MessageText: +// +// A capture device in the current active source is no longer available.%0 +// +#define NS_E_ACTIVE_SG_DEVICE_DISCONNECTED _HRESULT_TYPEDEF_(0xC00D1BA5L) + +// +// MessageId: NS_E_ACTIVE_SG_DEVICE_CONTROL_DISCONNECTED +// +// MessageText: +// +// A device used in the current active source for device control is no longer available.%0 +// +#define NS_E_ACTIVE_SG_DEVICE_CONTROL_DISCONNECTED _HRESULT_TYPEDEF_(0xC00D1BA6L) + +// +// MessageId: NS_E_NO_FRAMES_SUBMITTED_TO_ANALYZER +// +// MessageText: +// +// No frames have been submitted to the analyzer for analysis.%0 +// +#define NS_E_NO_FRAMES_SUBMITTED_TO_ANALYZER _HRESULT_TYPEDEF_(0xC00D1BA7L) + +// +// MessageId: NS_E_INPUT_DOESNOT_SUPPORT_SMPTE +// +// MessageText: +// +// The source video does not support time codes.%0 +// +#define NS_E_INPUT_DOESNOT_SUPPORT_SMPTE _HRESULT_TYPEDEF_(0xC00D1BA8L) + +// +// MessageId: NS_E_NO_SMPTE_WITH_MULTIPLE_SOURCEGROUPS +// +// MessageText: +// +// It is not possible to generate a time code when there are multiple sources in a session.%0 +// +#define NS_E_NO_SMPTE_WITH_MULTIPLE_SOURCEGROUPS _HRESULT_TYPEDEF_(0xC00D1BA9L) + +// +// MessageId: NS_E_BAD_CONTENTEDL +// +// MessageText: +// +// The voice codec optimization definition file can not be found or is corrupted.%0 +// +#define NS_E_BAD_CONTENTEDL _HRESULT_TYPEDEF_(0xC00D1BAAL) + +// +// MessageId: NS_E_INTERLACEMODE_MISMATCH +// +// MessageText: +// +// The same source index in different audiences should have the same interlace mode.%0 +// +#define NS_E_INTERLACEMODE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1BABL) + +// +// MessageId: NS_E_NONSQUAREPIXELMODE_MISMATCH +// +// MessageText: +// +// The same source index in different audiences should have the same nonsquare pixel mode.%0 +// +#define NS_E_NONSQUAREPIXELMODE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1BACL) + +// +// MessageId: NS_E_SMPTEMODE_MISMATCH +// +// MessageText: +// +// The same source index in different audiences should have the same time code mode.%0 +// +#define NS_E_SMPTEMODE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1BADL) + +// +// MessageId: NS_E_END_OF_TAPE +// +// MessageText: +// +// Either the end of the tape has been reached or there is no tape. Check the device and tape.%0 +// +#define NS_E_END_OF_TAPE _HRESULT_TYPEDEF_(0xC00D1BAEL) + +// +// MessageId: NS_E_NO_MEDIA_IN_AUDIENCE +// +// MessageText: +// +// No audio or video input has been specified.%0 +// +#define NS_E_NO_MEDIA_IN_AUDIENCE _HRESULT_TYPEDEF_(0xC00D1BAFL) + +// +// MessageId: NS_E_NO_AUDIENCES +// +// MessageText: +// +// The profile must contain a bit rate.%0 +// +#define NS_E_NO_AUDIENCES _HRESULT_TYPEDEF_(0xC00D1BB0L) + +// +// MessageId: NS_E_NO_AUDIO_COMPAT +// +// MessageText: +// +// You must specify at least one audio stream to be compatible with Windows Media Player 7.1.%0 +// +#define NS_E_NO_AUDIO_COMPAT _HRESULT_TYPEDEF_(0xC00D1BB1L) + +// +// MessageId: NS_E_INVALID_VBR_COMPAT +// +// MessageText: +// +// Using a VBR encoding mode is not compatible with Windows Media Player 7.1.%0 +// +#define NS_E_INVALID_VBR_COMPAT _HRESULT_TYPEDEF_(0xC00D1BB2L) + +// +// MessageId: NS_E_NO_PROFILE_NAME +// +// MessageText: +// +// You must specify a profile name.%0 +// +#define NS_E_NO_PROFILE_NAME _HRESULT_TYPEDEF_(0xC00D1BB3L) + +// +// MessageId: NS_E_INVALID_VBR_WITH_UNCOMP +// +// MessageText: +// +// It is not possible to use a VBR encoding mode with uncompressed audio or video.%0 +// +#define NS_E_INVALID_VBR_WITH_UNCOMP _HRESULT_TYPEDEF_(0xC00D1BB4L) + +// +// MessageId: NS_E_MULTIPLE_VBR_AUDIENCES +// +// MessageText: +// +// It is not possible to use MBR encoding with VBR encoding.%0 +// +#define NS_E_MULTIPLE_VBR_AUDIENCES _HRESULT_TYPEDEF_(0xC00D1BB5L) + +// +// MessageId: NS_E_UNCOMP_COMP_COMBINATION +// +// MessageText: +// +// It is not possible to mix uncompressed and compressed content in a session.%0 +// +#define NS_E_UNCOMP_COMP_COMBINATION _HRESULT_TYPEDEF_(0xC00D1BB6L) + +// +// MessageId: NS_E_MULTIPLE_AUDIO_CODECS +// +// MessageText: +// +// All audiences must use the same audio codec.%0 +// +#define NS_E_MULTIPLE_AUDIO_CODECS _HRESULT_TYPEDEF_(0xC00D1BB7L) + +// +// MessageId: NS_E_MULTIPLE_AUDIO_FORMATS +// +// MessageText: +// +// All audiences should use the same audio format to be compatible with Windows Media Player 7.1.%0 +// +#define NS_E_MULTIPLE_AUDIO_FORMATS _HRESULT_TYPEDEF_(0xC00D1BB8L) + +// +// MessageId: NS_E_AUDIO_BITRATE_STEPDOWN +// +// MessageText: +// +// The audio bit rate for an audience with a higher total bit rate must be greater than one with a lower total bit rate.%0 +// +#define NS_E_AUDIO_BITRATE_STEPDOWN _HRESULT_TYPEDEF_(0xC00D1BB9L) + +// +// MessageId: NS_E_INVALID_AUDIO_PEAKRATE +// +// MessageText: +// +// The audio peak bit rate setting is not valid.%0 +// +#define NS_E_INVALID_AUDIO_PEAKRATE _HRESULT_TYPEDEF_(0xC00D1BBAL) + +// +// MessageId: NS_E_INVALID_AUDIO_PEAKRATE_2 +// +// MessageText: +// +// The audio peak bit rate setting must be greater than the audio bit rate setting.%0 +// +#define NS_E_INVALID_AUDIO_PEAKRATE_2 _HRESULT_TYPEDEF_(0xC00D1BBBL) + +// +// MessageId: NS_E_INVALID_AUDIO_BUFFERMAX +// +// MessageText: +// +// The setting for the maximum buffer size for audio is not valid.%0 +// +#define NS_E_INVALID_AUDIO_BUFFERMAX _HRESULT_TYPEDEF_(0xC00D1BBCL) + +// +// MessageId: NS_E_MULTIPLE_VIDEO_CODECS +// +// MessageText: +// +// All audiences must use the same video codec.%0 +// +#define NS_E_MULTIPLE_VIDEO_CODECS _HRESULT_TYPEDEF_(0xC00D1BBDL) + +// +// MessageId: NS_E_MULTIPLE_VIDEO_SIZES +// +// MessageText: +// +// All audiences should use the same video size to be compatible with Windows Media Player 7.1.%0 +// +#define NS_E_MULTIPLE_VIDEO_SIZES _HRESULT_TYPEDEF_(0xC00D1BBEL) + +// +// MessageId: NS_E_INVALID_VIDEO_BITRATE +// +// MessageText: +// +// The video bit rate setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_BITRATE _HRESULT_TYPEDEF_(0xC00D1BBFL) + +// +// MessageId: NS_E_VIDEO_BITRATE_STEPDOWN +// +// MessageText: +// +// The video bit rate for an audience with a higher total bit rate must be greater than one with a lower total bit rate.%0 +// +#define NS_E_VIDEO_BITRATE_STEPDOWN _HRESULT_TYPEDEF_(0xC00D1BC0L) + +// +// MessageId: NS_E_INVALID_VIDEO_PEAKRATE +// +// MessageText: +// +// The video peak bit rate setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_PEAKRATE _HRESULT_TYPEDEF_(0xC00D1BC1L) + +// +// MessageId: NS_E_INVALID_VIDEO_PEAKRATE_2 +// +// MessageText: +// +// The video peak bit rate setting must be greater than the video bit rate setting.%0 +// +#define NS_E_INVALID_VIDEO_PEAKRATE_2 _HRESULT_TYPEDEF_(0xC00D1BC2L) + +// +// MessageId: NS_E_INVALID_VIDEO_WIDTH +// +// MessageText: +// +// The video width setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_WIDTH _HRESULT_TYPEDEF_(0xC00D1BC3L) + +// +// MessageId: NS_E_INVALID_VIDEO_HEIGHT +// +// MessageText: +// +// The video height setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_HEIGHT _HRESULT_TYPEDEF_(0xC00D1BC4L) + +// +// MessageId: NS_E_INVALID_VIDEO_FPS +// +// MessageText: +// +// The video frame rate setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_FPS _HRESULT_TYPEDEF_(0xC00D1BC5L) + +// +// MessageId: NS_E_INVALID_VIDEO_KEYFRAME +// +// MessageText: +// +// The video key frame setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_KEYFRAME _HRESULT_TYPEDEF_(0xC00D1BC6L) + +// +// MessageId: NS_E_INVALID_VIDEO_IQUALITY +// +// MessageText: +// +// The video image quality setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_IQUALITY _HRESULT_TYPEDEF_(0xC00D1BC7L) + +// +// MessageId: NS_E_INVALID_VIDEO_CQUALITY +// +// MessageText: +// +// The video codec quality setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_CQUALITY _HRESULT_TYPEDEF_(0xC00D1BC8L) + +// +// MessageId: NS_E_INVALID_VIDEO_BUFFER +// +// MessageText: +// +// The video buffer setting is not valid.%0 +// +#define NS_E_INVALID_VIDEO_BUFFER _HRESULT_TYPEDEF_(0xC00D1BC9L) + +// +// MessageId: NS_E_INVALID_VIDEO_BUFFERMAX +// +// MessageText: +// +// The setting for the maximum buffer size for video is not valid.%0 +// +#define NS_E_INVALID_VIDEO_BUFFERMAX _HRESULT_TYPEDEF_(0xC00D1BCAL) + +// +// MessageId: NS_E_INVALID_VIDEO_BUFFERMAX_2 +// +// MessageText: +// +// The value of the video maximum buffer size setting must be greater than the video buffer size setting.%0 +// +#define NS_E_INVALID_VIDEO_BUFFERMAX_2 _HRESULT_TYPEDEF_(0xC00D1BCBL) + +// +// MessageId: NS_E_INVALID_VIDEO_WIDTH_ALIGN +// +// MessageText: +// +// The alignment of the video width is not valid.%0 +// +#define NS_E_INVALID_VIDEO_WIDTH_ALIGN _HRESULT_TYPEDEF_(0xC00D1BCCL) + +// +// MessageId: NS_E_INVALID_VIDEO_HEIGHT_ALIGN +// +// MessageText: +// +// The alignment of the video height is not valid.%0 +// +#define NS_E_INVALID_VIDEO_HEIGHT_ALIGN _HRESULT_TYPEDEF_(0xC00D1BCDL) + +// +// MessageId: NS_E_MULTIPLE_SCRIPT_BITRATES +// +// MessageText: +// +// All bit rates must have the same script bit rate.%0 +// +#define NS_E_MULTIPLE_SCRIPT_BITRATES _HRESULT_TYPEDEF_(0xC00D1BCEL) + +// +// MessageId: NS_E_INVALID_SCRIPT_BITRATE +// +// MessageText: +// +// The script bit rate specified is not valid.%0 +// +#define NS_E_INVALID_SCRIPT_BITRATE _HRESULT_TYPEDEF_(0xC00D1BCFL) + +// +// MessageId: NS_E_MULTIPLE_FILE_BITRATES +// +// MessageText: +// +// All bit rates must have the same file transfer bit rate.%0 +// +#define NS_E_MULTIPLE_FILE_BITRATES _HRESULT_TYPEDEF_(0xC00D1BD0L) + +// +// MessageId: NS_E_INVALID_FILE_BITRATE +// +// MessageText: +// +// The file transfer bit rate is not valid.%0 +// +#define NS_E_INVALID_FILE_BITRATE _HRESULT_TYPEDEF_(0xC00D1BD1L) + +// +// MessageId: NS_E_SAME_AS_INPUT_COMBINATION +// +// MessageText: +// +// All audiences in a profile should either be same as input or have video width and height specified.%0 +// +#define NS_E_SAME_AS_INPUT_COMBINATION _HRESULT_TYPEDEF_(0xC00D1BD2L) + +// +// MessageId: NS_E_SOURCE_CANNOT_LOOP +// +// MessageText: +// +// This source type does not support looping.%0 +// +#define NS_E_SOURCE_CANNOT_LOOP _HRESULT_TYPEDEF_(0xC00D1BD3L) + +// +// MessageId: NS_E_INVALID_FOLDDOWN_COEFFICIENTS +// +// MessageText: +// +// The fold-down value needs to be between -144 and 0.%0 +// +#define NS_E_INVALID_FOLDDOWN_COEFFICIENTS _HRESULT_TYPEDEF_(0xC00D1BD4L) + +// +// MessageId: NS_E_DRMPROFILE_NOTFOUND +// +// MessageText: +// +// The specified DRM profile does not exist in the system.%0 +// +#define NS_E_DRMPROFILE_NOTFOUND _HRESULT_TYPEDEF_(0xC00D1BD5L) + +// +// MessageId: NS_E_INVALID_TIMECODE +// +// MessageText: +// +// The specified time code is not valid.%0 +// +#define NS_E_INVALID_TIMECODE _HRESULT_TYPEDEF_(0xC00D1BD6L) + +// +// MessageId: NS_E_NO_AUDIO_TIMECOMPRESSION +// +// MessageText: +// +// It is not possible to apply time compression to a video-only session.%0 +// +#define NS_E_NO_AUDIO_TIMECOMPRESSION _HRESULT_TYPEDEF_(0xC00D1BD7L) + +// +// MessageId: NS_E_NO_TWOPASS_TIMECOMPRESSION +// +// MessageText: +// +// It is not possible to apply time compression to a session that is using two-pass encoding.%0 +// +#define NS_E_NO_TWOPASS_TIMECOMPRESSION _HRESULT_TYPEDEF_(0xC00D1BD8L) + +// +// MessageId: NS_E_TIMECODE_REQUIRES_VIDEOSTREAM +// +// MessageText: +// +// It is not possible to generate a time code for an audio-only session.%0 +// +#define NS_E_TIMECODE_REQUIRES_VIDEOSTREAM _HRESULT_TYPEDEF_(0xC00D1BD9L) + +// +// MessageId: NS_E_NO_MBR_WITH_TIMECODE +// +// MessageText: +// +// It is not possible to generate a time code when you are encoding content at multiple bit rates.%0 +// +#define NS_E_NO_MBR_WITH_TIMECODE _HRESULT_TYPEDEF_(0xC00D1BDAL) + +// +// MessageId: NS_E_INVALID_INTERLACEMODE +// +// MessageText: +// +// The video codec selected does not support maintaining interlacing in video.%0 +// +#define NS_E_INVALID_INTERLACEMODE _HRESULT_TYPEDEF_(0xC00D1BDBL) + +// +// MessageId: NS_E_INVALID_INTERLACE_COMPAT +// +// MessageText: +// +// Maintaining interlacing in video is not compatible with Windows Media Player 7.1.%0 +// +#define NS_E_INVALID_INTERLACE_COMPAT _HRESULT_TYPEDEF_(0xC00D1BDCL) + +// +// MessageId: NS_E_INVALID_NONSQUAREPIXEL_COMPAT +// +// MessageText: +// +// Allowing nonsquare pixel output is not compatible with Windows Media Player 7.1.%0 +// +#define NS_E_INVALID_NONSQUAREPIXEL_COMPAT _HRESULT_TYPEDEF_(0xC00D1BDDL) + +// +// MessageId: NS_E_INVALID_SOURCE_WITH_DEVICE_CONTROL +// +// MessageText: +// +// Only capture devices can be used with device control.%0 +// +#define NS_E_INVALID_SOURCE_WITH_DEVICE_CONTROL _HRESULT_TYPEDEF_(0xC00D1BDEL) + +// +// MessageId: NS_E_CANNOT_GENERATE_BROADCAST_INFO_FOR_QUALITYVBR +// +// MessageText: +// +// It is not possible to generate the stream format file if you are using quality-based VBR encoding for the audio or video stream. Instead use the Windows Media file generated after encoding to create the announcement file.%0 +// +#define NS_E_CANNOT_GENERATE_BROADCAST_INFO_FOR_QUALITYVBR _HRESULT_TYPEDEF_(0xC00D1BDFL) + +// +// MessageId: NS_E_EXCEED_MAX_DRM_PROFILE_LIMIT +// +// MessageText: +// +// It is not possible to create a DRM profile because the maximum number of profiles has been reached. You must delete some DRM profiles before creating new ones.%0 +// +#define NS_E_EXCEED_MAX_DRM_PROFILE_LIMIT _HRESULT_TYPEDEF_(0xC00D1BE0L) + +// +// MessageId: NS_E_DEVICECONTROL_UNSTABLE +// +// MessageText: +// +// The device is in an unstable state. Check that the device is functioning properly and a tape is in place. +// +#define NS_E_DEVICECONTROL_UNSTABLE _HRESULT_TYPEDEF_(0xC00D1BE1L) + +// +// MessageId: NS_E_INVALID_PIXEL_ASPECT_RATIO +// +// MessageText: +// +// The pixel aspect ratio value must be between 1 and 255. +// +#define NS_E_INVALID_PIXEL_ASPECT_RATIO _HRESULT_TYPEDEF_(0xC00D1BE2L) + +// +// MessageId: NS_E_AUDIENCE__LANGUAGE_CONTENTTYPE_MISMATCH +// +// MessageText: +// +// All streams with different languages in the same audience must have same properties.%0 +// +#define NS_E_AUDIENCE__LANGUAGE_CONTENTTYPE_MISMATCH _HRESULT_TYPEDEF_(0xC00D1BE3L) + +// +// MessageId: NS_E_INVALID_PROFILE_CONTENTTYPE +// +// MessageText: +// +// The profile must contain at least one audio or video stream.%0 +// +#define NS_E_INVALID_PROFILE_CONTENTTYPE _HRESULT_TYPEDEF_(0xC00D1BE4L) + +// +// MessageId: NS_E_TRANSFORM_PLUGIN_NOT_FOUND +// +// MessageText: +// +// The transform plug-in could not be found.%0 +// +#define NS_E_TRANSFORM_PLUGIN_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D1BE5L) + +// +// MessageId: NS_E_TRANSFORM_PLUGIN_INVALID +// +// MessageText: +// +// The transform plug-in is not valid. It may be damaged or you may not have the required permissions to access the plug-in.%0 +// +#define NS_E_TRANSFORM_PLUGIN_INVALID _HRESULT_TYPEDEF_(0xC00D1BE6L) + +// +// MessageId: NS_E_EDL_REQUIRED_FOR_DEVICE_MULTIPASS +// +// MessageText: +// +// To use two-pass encoding, you must enable device control and setup an edit decision list (EDL) that has at least one entry.%0 +// +#define NS_E_EDL_REQUIRED_FOR_DEVICE_MULTIPASS _HRESULT_TYPEDEF_(0xC00D1BE7L) + +// +// MessageId: NS_E_INVALID_VIDEO_WIDTH_FOR_INTERLACED_ENCODING +// +// MessageText: +// +// When you choose to maintain the interlacing in your video, the output video size must be a multiple of 4.%0 +// +#define NS_E_INVALID_VIDEO_WIDTH_FOR_INTERLACED_ENCODING _HRESULT_TYPEDEF_(0xC00D1BE8L) + + +///////////////////////////////////////////////////////////////////////// +// +// DRM Specific Errors +// +// IdRange = 10000..10999 +///////////////////////////////////////////////////////////////////////// +// +// MessageId: NS_E_DRM_INVALID_APPLICATION +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact product support for this application.%0 +// +#define NS_E_DRM_INVALID_APPLICATION _HRESULT_TYPEDEF_(0xC00D2711L) + +// +// MessageId: NS_E_DRM_LICENSE_STORE_ERROR +// +// MessageText: +// +// License storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_LICENSE_STORE_ERROR _HRESULT_TYPEDEF_(0xC00D2712L) + +// +// MessageId: NS_E_DRM_SECURE_STORE_ERROR +// +// MessageText: +// +// Secure storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_SECURE_STORE_ERROR _HRESULT_TYPEDEF_(0xC00D2713L) + +// +// MessageId: NS_E_DRM_LICENSE_STORE_SAVE_ERROR +// +// MessageText: +// +// License acquisition did not work. Acquire a new license or contact the content provider for further assistance.%0 +// +#define NS_E_DRM_LICENSE_STORE_SAVE_ERROR _HRESULT_TYPEDEF_(0xC00D2714L) + +// +// MessageId: NS_E_DRM_SECURE_STORE_UNLOCK_ERROR +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0 +// +#define NS_E_DRM_SECURE_STORE_UNLOCK_ERROR _HRESULT_TYPEDEF_(0xC00D2715L) + +// +// MessageId: NS_E_DRM_INVALID_CONTENT +// +// MessageText: +// +// The media file is corrupted. Contact the content provider to get a new file.%0 +// +#define NS_E_DRM_INVALID_CONTENT _HRESULT_TYPEDEF_(0xC00D2716L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_OPEN_LICENSE +// +// MessageText: +// +// The license is corrupted. Acquire a new license.%0 +// +#define NS_E_DRM_UNABLE_TO_OPEN_LICENSE _HRESULT_TYPEDEF_(0xC00D2717L) + +// +// MessageId: NS_E_DRM_INVALID_LICENSE +// +// MessageText: +// +// The license is corrupted or invalid. Acquire a new license%0 +// +#define NS_E_DRM_INVALID_LICENSE _HRESULT_TYPEDEF_(0xC00D2718L) + +// +// MessageId: NS_E_DRM_INVALID_MACHINE +// +// MessageText: +// +// Licenses cannot be copied from one computer to another. Use License Management to transfer licenses, or get a new license for the media file.%0 +// +#define NS_E_DRM_INVALID_MACHINE _HRESULT_TYPEDEF_(0xC00D2719L) + +// +// MessageId: NS_E_DRM_ENUM_LICENSE_FAILED +// +// MessageText: +// +// License storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_ENUM_LICENSE_FAILED _HRESULT_TYPEDEF_(0xC00D271BL) + +// +// MessageId: NS_E_DRM_INVALID_LICENSE_REQUEST +// +// MessageText: +// +// The media file is corrupted. Contact the content provider to get a new file.%0 +// +#define NS_E_DRM_INVALID_LICENSE_REQUEST _HRESULT_TYPEDEF_(0xC00D271CL) + +// +// MessageId: NS_E_DRM_UNABLE_TO_INITIALIZE +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0 +// +#define NS_E_DRM_UNABLE_TO_INITIALIZE _HRESULT_TYPEDEF_(0xC00D271DL) + +// +// MessageId: NS_E_DRM_UNABLE_TO_ACQUIRE_LICENSE +// +// MessageText: +// +// The license could not be acquired. Try again later.%0 +// +#define NS_E_DRM_UNABLE_TO_ACQUIRE_LICENSE _HRESULT_TYPEDEF_(0xC00D271EL) + +// +// MessageId: NS_E_DRM_INVALID_LICENSE_ACQUIRED +// +// MessageText: +// +// License acquisition did not work. Acquire a new license or contact the content provider for further assistance.%0 +// +#define NS_E_DRM_INVALID_LICENSE_ACQUIRED _HRESULT_TYPEDEF_(0xC00D271FL) + +// +// MessageId: NS_E_DRM_NO_RIGHTS +// +// MessageText: +// +// The requested operation cannot be performed on this file.%0 +// +#define NS_E_DRM_NO_RIGHTS _HRESULT_TYPEDEF_(0xC00D2720L) + +// +// MessageId: NS_E_DRM_KEY_ERROR +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_KEY_ERROR _HRESULT_TYPEDEF_(0xC00D2721L) + +// +// MessageId: NS_E_DRM_ENCRYPT_ERROR +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0 +// +#define NS_E_DRM_ENCRYPT_ERROR _HRESULT_TYPEDEF_(0xC00D2722L) + +// +// MessageId: NS_E_DRM_DECRYPT_ERROR +// +// MessageText: +// +// The media file is corrupted. Contact the content provider to get a new file.%0 +// +#define NS_E_DRM_DECRYPT_ERROR _HRESULT_TYPEDEF_(0xC00D2723L) + +// +// MessageId: NS_E_DRM_LICENSE_INVALID_XML +// +// MessageText: +// +// The license is corrupted. Acquire a new license.%0 +// +#define NS_E_DRM_LICENSE_INVALID_XML _HRESULT_TYPEDEF_(0xC00D2725L) + +// +// MessageId: NS_S_DRM_LICENSE_ACQUIRED +// +// MessageText: +// +// Status message: The license was acquired.%0 +// +#define NS_S_DRM_LICENSE_ACQUIRED _HRESULT_TYPEDEF_(0x000D2726L) + +// +// MessageId: NS_S_DRM_INDIVIDUALIZED +// +// MessageText: +// +// Status message: The security upgrade has been completed.%0 +// +#define NS_S_DRM_INDIVIDUALIZED _HRESULT_TYPEDEF_(0x000D2727L) + +// +// MessageId: NS_E_DRM_NEEDS_INDIVIDUALIZATION +// +// MessageText: +// +// A security upgrade is required to perform the operation on this media file.%0 +// +#define NS_E_DRM_NEEDS_INDIVIDUALIZATION _HRESULT_TYPEDEF_(0xC00D2728L) + +// +// MessageId: NS_E_DRM_ALREADY_INDIVIDUALIZED +// +// MessageText: +// +// You already have the latest security components. No upgrade is necessary at this time.%0 +// +#define NS_E_DRM_ALREADY_INDIVIDUALIZED _HRESULT_TYPEDEF_(0xC00D2729L) + +// +// MessageId: NS_E_DRM_ACTION_NOT_QUERIED +// +// MessageText: +// +// The application cannot perform this action. Contact product support for this application.%0 +// +#define NS_E_DRM_ACTION_NOT_QUERIED _HRESULT_TYPEDEF_(0xC00D272AL) + +// +// MessageId: NS_E_DRM_ACQUIRING_LICENSE +// +// MessageText: +// +// You cannot begin a new license acquisition process until the current one has been completed.%0 +// +#define NS_E_DRM_ACQUIRING_LICENSE _HRESULT_TYPEDEF_(0xC00D272BL) + +// +// MessageId: NS_E_DRM_INDIVIDUALIZING +// +// MessageText: +// +// You cannot begin a new security upgrade until the current one has been completed.%0 +// +#define NS_E_DRM_INDIVIDUALIZING _HRESULT_TYPEDEF_(0xC00D272CL) + +// +// MessageId: NS_E_DRM_PARAMETERS_MISMATCHED +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_PARAMETERS_MISMATCHED _HRESULT_TYPEDEF_(0xC00D272FL) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_LICENSE_OBJECT +// +// MessageText: +// +// A license cannot be created for this media file. Reinstall the application.%0 +// +#define NS_E_DRM_UNABLE_TO_CREATE_LICENSE_OBJECT _HRESULT_TYPEDEF_(0xC00D2730L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_INDI_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_INDI_OBJECT _HRESULT_TYPEDEF_(0xC00D2731L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_ENCRYPT_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_ENCRYPT_OBJECT _HRESULT_TYPEDEF_(0xC00D2732L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_DECRYPT_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_DECRYPT_OBJECT _HRESULT_TYPEDEF_(0xC00D2733L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_PROPERTIES_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_PROPERTIES_OBJECT _HRESULT_TYPEDEF_(0xC00D2734L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_BACKUP_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_BACKUP_OBJECT _HRESULT_TYPEDEF_(0xC00D2735L) + +// +// MessageId: NS_E_DRM_INDIVIDUALIZE_ERROR +// +// MessageText: +// +// The security upgrade failed. Try again later.%0 +// +#define NS_E_DRM_INDIVIDUALIZE_ERROR _HRESULT_TYPEDEF_(0xC00D2736L) + +// +// MessageId: NS_E_DRM_LICENSE_OPEN_ERROR +// +// MessageText: +// +// License storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_LICENSE_OPEN_ERROR _HRESULT_TYPEDEF_(0xC00D2737L) + +// +// MessageId: NS_E_DRM_LICENSE_CLOSE_ERROR +// +// MessageText: +// +// License storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_LICENSE_CLOSE_ERROR _HRESULT_TYPEDEF_(0xC00D2738L) + +// +// MessageId: NS_E_DRM_GET_LICENSE_ERROR +// +// MessageText: +// +// License storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_GET_LICENSE_ERROR _HRESULT_TYPEDEF_(0xC00D2739L) + +// +// MessageId: NS_E_DRM_QUERY_ERROR +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_QUERY_ERROR _HRESULT_TYPEDEF_(0xC00D273AL) + +// +// MessageId: NS_E_DRM_REPORT_ERROR +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact product support for this application.%0 +// +#define NS_E_DRM_REPORT_ERROR _HRESULT_TYPEDEF_(0xC00D273BL) + +// +// MessageId: NS_E_DRM_GET_LICENSESTRING_ERROR +// +// MessageText: +// +// License storage is not working. Contact Microsoft product support.%0 +// +#define NS_E_DRM_GET_LICENSESTRING_ERROR _HRESULT_TYPEDEF_(0xC00D273CL) + +// +// MessageId: NS_E_DRM_GET_CONTENTSTRING_ERROR +// +// MessageText: +// +// The media file is corrupted. Contact the content provider to get a new file.%0 +// +#define NS_E_DRM_GET_CONTENTSTRING_ERROR _HRESULT_TYPEDEF_(0xC00D273DL) + +// +// MessageId: NS_E_DRM_MONITOR_ERROR +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Try again later.%0 +// +#define NS_E_DRM_MONITOR_ERROR _HRESULT_TYPEDEF_(0xC00D273EL) + +// +// MessageId: NS_E_DRM_UNABLE_TO_SET_PARAMETER +// +// MessageText: +// +// The application has made an invalid call to the Digital Rights Management component. Contact product support for this application.%0 +// +#define NS_E_DRM_UNABLE_TO_SET_PARAMETER _HRESULT_TYPEDEF_(0xC00D273FL) + +// +// MessageId: NS_E_DRM_INVALID_APPDATA +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_INVALID_APPDATA _HRESULT_TYPEDEF_(0xC00D2740L) + +// +// MessageId: NS_E_DRM_INVALID_APPDATA_VERSION +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact product support for this application.%0. +// +#define NS_E_DRM_INVALID_APPDATA_VERSION _HRESULT_TYPEDEF_(0xC00D2741L) + +// +// MessageId: NS_E_DRM_BACKUP_EXISTS +// +// MessageText: +// +// Licenses are already backed up in this location.%0 +// +#define NS_E_DRM_BACKUP_EXISTS _HRESULT_TYPEDEF_(0xC00D2742L) + +// +// MessageId: NS_E_DRM_BACKUP_CORRUPT +// +// MessageText: +// +// One or more backed-up licenses are missing or corrupt.%0 +// +#define NS_E_DRM_BACKUP_CORRUPT _HRESULT_TYPEDEF_(0xC00D2743L) + +// +// MessageId: NS_E_DRM_BACKUPRESTORE_BUSY +// +// MessageText: +// +// You cannot begin a new backup process until the current process has been completed.%0 +// +#define NS_E_DRM_BACKUPRESTORE_BUSY _HRESULT_TYPEDEF_(0xC00D2744L) + +// +// MessageId: NS_S_DRM_MONITOR_CANCELLED +// +// MessageText: +// +// Status message: License monitoring has been cancelled.%0 +// +#define NS_S_DRM_MONITOR_CANCELLED _HRESULT_TYPEDEF_(0x000D2746L) + +// +// MessageId: NS_S_DRM_ACQUIRE_CANCELLED +// +// MessageText: +// +// Status message: License acquisition has been cancelled.%0 +// +#define NS_S_DRM_ACQUIRE_CANCELLED _HRESULT_TYPEDEF_(0x000D2747L) + +// +// MessageId: NS_E_DRM_LICENSE_UNUSABLE +// +// MessageText: +// +// The license is invalid. Contact the content provider for further assistance.%0 +// +#define NS_E_DRM_LICENSE_UNUSABLE _HRESULT_TYPEDEF_(0xC00D2748L) + +// +// MessageId: NS_E_DRM_INVALID_PROPERTY +// +// MessageText: +// +// A required property was not set by the application. Contact product support for this application.%0. +// +#define NS_E_DRM_INVALID_PROPERTY _HRESULT_TYPEDEF_(0xC00D2749L) + +// +// MessageId: NS_E_DRM_SECURE_STORE_NOT_FOUND +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component of this application. Try to acquire a license again.%0 +// +#define NS_E_DRM_SECURE_STORE_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D274AL) + +// +// MessageId: NS_E_DRM_CACHED_CONTENT_ERROR +// +// MessageText: +// +// A license cannot be found for this media file. Use License Management to transfer a license for this file from the original computer, or acquire a new license.%0 +// +#define NS_E_DRM_CACHED_CONTENT_ERROR _HRESULT_TYPEDEF_(0xC00D274BL) + +// +// MessageId: NS_E_DRM_INDIVIDUALIZATION_INCOMPLETE +// +// MessageText: +// +// A problem occurred during the security upgrade. Try again later.%0 +// +#define NS_E_DRM_INDIVIDUALIZATION_INCOMPLETE _HRESULT_TYPEDEF_(0xC00D274CL) + +// +// MessageId: NS_E_DRM_DRIVER_AUTH_FAILURE +// +// MessageText: +// +// Certified driver components are required to play this media file. Contact Windows Update to see whether updated drivers are available for your hardware.%0 +// +#define NS_E_DRM_DRIVER_AUTH_FAILURE _HRESULT_TYPEDEF_(0xC00D274DL) + +// +// MessageId: NS_E_DRM_NEED_UPGRADE_MSSAP +// +// MessageText: +// +// One or more of the Secure Audio Path components were not found or an entry point in those components was not found.%0 +// +#define NS_E_DRM_NEED_UPGRADE_MSSAP _HRESULT_TYPEDEF_(0xC00D274EL) + +// +// MessageId: NS_E_DRM_REOPEN_CONTENT +// +// MessageText: +// +// Status message: Reopen the file.%0 +// +#define NS_E_DRM_REOPEN_CONTENT _HRESULT_TYPEDEF_(0xC00D274FL) + +// +// MessageId: NS_E_DRM_DRIVER_DIGIOUT_FAILURE +// +// MessageText: +// +// Certain driver functionality is required to play this media file. Contact Windows Update to see whether updated drivers are available for your hardware.%0 +// +#define NS_E_DRM_DRIVER_DIGIOUT_FAILURE _HRESULT_TYPEDEF_(0xC00D2750L) + +// +// MessageId: NS_E_DRM_INVALID_SECURESTORE_PASSWORD +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_INVALID_SECURESTORE_PASSWORD _HRESULT_TYPEDEF_(0xC00D2751L) + +// +// MessageId: NS_E_DRM_APPCERT_REVOKED +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_APPCERT_REVOKED _HRESULT_TYPEDEF_(0xC00D2752L) + +// +// MessageId: NS_E_DRM_RESTORE_FRAUD +// +// MessageText: +// +// You cannot restore your license(s).%0 +// +#define NS_E_DRM_RESTORE_FRAUD _HRESULT_TYPEDEF_(0xC00D2753L) + +// +// MessageId: NS_E_DRM_HARDWARE_INCONSISTENT +// +// MessageText: +// +// The licenses for your media files are corrupted. Contact Microsoft product support.%0 +// +#define NS_E_DRM_HARDWARE_INCONSISTENT _HRESULT_TYPEDEF_(0xC00D2754L) + +// +// MessageId: NS_E_DRM_SDMI_TRIGGER +// +// MessageText: +// +// To transfer this media file, you must upgrade the application.%0 +// +#define NS_E_DRM_SDMI_TRIGGER _HRESULT_TYPEDEF_(0xC00D2755L) + +// +// MessageId: NS_E_DRM_SDMI_NOMORECOPIES +// +// MessageText: +// +// You cannot make any more copies of this media file.%0 +// +#define NS_E_DRM_SDMI_NOMORECOPIES _HRESULT_TYPEDEF_(0xC00D2756L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_HEADER_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_HEADER_OBJECT _HRESULT_TYPEDEF_(0xC00D2757L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_KEYS_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_KEYS_OBJECT _HRESULT_TYPEDEF_(0xC00D2758L) + +;// This error is never shown to user but needed for program logic. +// +// MessageId: NS_E_DRM_LICENSE_NOTACQUIRED +// +// MessageText: +// +// Unable to obtain license.%0 +// +#define NS_E_DRM_LICENSE_NOTACQUIRED _HRESULT_TYPEDEF_(0xC00D2759L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_CODING_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_CODING_OBJECT _HRESULT_TYPEDEF_(0xC00D275AL) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_STATE_DATA_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_STATE_DATA_OBJECT _HRESULT_TYPEDEF_(0xC00D275BL) + +// +// MessageId: NS_E_DRM_BUFFER_TOO_SMALL +// +// MessageText: +// +// The buffer supplied is not sufficient.%0. +// +#define NS_E_DRM_BUFFER_TOO_SMALL _HRESULT_TYPEDEF_(0xC00D275CL) + +// +// MessageId: NS_E_DRM_UNSUPPORTED_PROPERTY +// +// MessageText: +// +// The property requested is not supported.%0. +// +#define NS_E_DRM_UNSUPPORTED_PROPERTY _HRESULT_TYPEDEF_(0xC00D275DL) + +// +// MessageId: NS_E_DRM_ERROR_BAD_NET_RESP +// +// MessageText: +// +// The specified server cannot perform the requested operation.%0. +// +#define NS_E_DRM_ERROR_BAD_NET_RESP _HRESULT_TYPEDEF_(0xC00D275EL) + +// +// MessageId: NS_E_DRM_STORE_NOTALLSTORED +// +// MessageText: +// +// Some of the licenses could not be stored.%0. +// +#define NS_E_DRM_STORE_NOTALLSTORED _HRESULT_TYPEDEF_(0xC00D275FL) + +// +// MessageId: NS_E_DRM_SECURITY_COMPONENT_SIGNATURE_INVALID +// +// MessageText: +// +// The Digital Rights Management security upgrade component could not be validated. Contact Microsoft product support.%0 +// +#define NS_E_DRM_SECURITY_COMPONENT_SIGNATURE_INVALID _HRESULT_TYPEDEF_(0xC00D2760L) + +// +// MessageId: NS_E_DRM_INVALID_DATA +// +// MessageText: +// +// Invalid or corrupt data was encountered.%0 +// +#define NS_E_DRM_INVALID_DATA _HRESULT_TYPEDEF_(0xC00D2761L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CONTACT_SERVER +// +// MessageText: +// +// Unable to contact the server for the requested operation.%0 +// +#define NS_E_DRM_UNABLE_TO_CONTACT_SERVER _HRESULT_TYPEDEF_(0xC00D2762L) + +// +// MessageId: NS_E_DRM_UNABLE_TO_CREATE_AUTHENTICATION_OBJECT +// +// MessageText: +// +// A problem has occurred in the Digital Rights Management component. Contact Microsoft product support.%0. +// +#define NS_E_DRM_UNABLE_TO_CREATE_AUTHENTICATION_OBJECT _HRESULT_TYPEDEF_(0xC00D2763L) + +// +// MessageId: NS_E_DRM_NOT_CONFIGURED +// +// MessageText: +// +// Not all of the necessary properties for DRM have been set.%0 +// +#define NS_E_DRM_NOT_CONFIGURED _HRESULT_TYPEDEF_(0xC00D2764L) + +// +// MessageId: NS_E_DRM_DEVICE_ACTIVATION_CANCELED +// +// MessageText: +// +// The portable device does not have the security required to copy protected files to it. To obtain the additional security, try to copy the file to your portable device again. When a message appears, click OK.%0 +// +#define NS_E_DRM_DEVICE_ACTIVATION_CANCELED _HRESULT_TYPEDEF_(0xC00D2765L) + + +// +// License Reasons Section +// Error Codes why a license is not usable. Reserve 10200..10300 for this purpose. +// 10200..10249 is for license reported reasons. 10250..10300 is for client detected reasons. +// + +// +// MessageId: NS_E_DRM_LICENSE_EXPIRED +// +// MessageText: +// +// The license for this file has expired and is no longer valid. Contact your content provider for further assistance.%0 +// +#define NS_E_DRM_LICENSE_EXPIRED _HRESULT_TYPEDEF_(0xC00D27D8L) + +// +// MessageId: NS_E_DRM_LICENSE_NOTENABLED +// +// MessageText: +// +// The license for this file is not valid yet, but will be at a future date.%0 +// +#define NS_E_DRM_LICENSE_NOTENABLED _HRESULT_TYPEDEF_(0xC00D27D9L) + +// +// MessageId: NS_E_DRM_LICENSE_APPSECLOW +// +// MessageText: +// +// The license for this file requires a higher level of security than the player you are currently using has. Try using a different player or download a newer version of your current player.%0 +// +#define NS_E_DRM_LICENSE_APPSECLOW _HRESULT_TYPEDEF_(0xC00D27DAL) + +// +// MessageId: NS_E_DRM_STORE_NEEDINDI +// +// MessageText: +// +// The license cannot be stored as it requires security upgrade of Digital Rights Management component.%0. +// +#define NS_E_DRM_STORE_NEEDINDI _HRESULT_TYPEDEF_(0xC00D27DBL) + +// +// MessageId: NS_E_DRM_STORE_NOTALLOWED +// +// MessageText: +// +// Your machine does not meet the requirements for storing the license.%0. +// +#define NS_E_DRM_STORE_NOTALLOWED _HRESULT_TYPEDEF_(0xC00D27DCL) + +// +// MessageId: NS_E_DRM_LICENSE_APP_NOTALLOWED +// +// MessageText: +// +// The license for this file requires an upgraded version of your player or a different player.%0. +// +#define NS_E_DRM_LICENSE_APP_NOTALLOWED _HRESULT_TYPEDEF_(0xC00D27DDL) + +// +// MessageId: NS_S_DRM_NEEDS_INDIVIDUALIZATION +// +// MessageText: +// +// A security upgrade is required to perform the operation on this media file.%0 +// +#define NS_S_DRM_NEEDS_INDIVIDUALIZATION _HRESULT_TYPEDEF_(0x000D27DEL) + +// +// MessageId: NS_E_DRM_LICENSE_CERT_EXPIRED +// +// MessageText: +// +// The license server's certificate expired. Make sure your system clock is set correctly. Contact your content provider for further assistance. %0. +// +#define NS_E_DRM_LICENSE_CERT_EXPIRED _HRESULT_TYPEDEF_(0xC00D27DFL) + +// +// MessageId: NS_E_DRM_LICENSE_SECLOW +// +// MessageText: +// +// The license for this file requires a higher level of security than the player you are currently using has. Try using a different player or download a newer version of your current player.%0 +// +#define NS_E_DRM_LICENSE_SECLOW _HRESULT_TYPEDEF_(0xC00D27E0L) + +// +// MessageId: NS_E_DRM_LICENSE_CONTENT_REVOKED +// +// MessageText: +// +// The content owner for the license you just acquired is no longer supporting their content. Contact the content owner for a newer version of the content.%0 +// +#define NS_E_DRM_LICENSE_CONTENT_REVOKED _HRESULT_TYPEDEF_(0xC00D27E1L) + +// +// MessageId: NS_E_DRM_LICENSE_NOSAP +// +// MessageText: +// +// The license for this file requires a feature that is not supported in your current player or operating system. You can try with newer version of your current player or contact your content provider for further assistance.%0 +// +#define NS_E_DRM_LICENSE_NOSAP _HRESULT_TYPEDEF_(0xC00D280AL) + +// +// MessageId: NS_E_DRM_LICENSE_NOSVP +// +// MessageText: +// +// The license for this file requires a feature that is not supported in your current player or operating system. You can try with newer version of your current player or contact your content provider for further assistance.%0 +// +#define NS_E_DRM_LICENSE_NOSVP _HRESULT_TYPEDEF_(0xC00D280BL) + +// +// MessageId: NS_E_DRM_LICENSE_NOWDM +// +// MessageText: +// +// The license for this file requires Windows Driver Model (WDM) audio drivers. Contact your sound card manufacturer for further assistance.%0 +// +#define NS_E_DRM_LICENSE_NOWDM _HRESULT_TYPEDEF_(0xC00D280CL) + +// +// MessageId: NS_E_DRM_LICENSE_NOTRUSTEDCODEC +// +// MessageText: +// +// The license for this file requires a higher level of security than the player you are currently using has. Try using a different player or download a newer version of your current player.%0 +// +#define NS_E_DRM_LICENSE_NOTRUSTEDCODEC _HRESULT_TYPEDEF_(0xC00D280DL) + + +// +// End of License Reasons Section +// + +// +// MessageId: NS_E_DRM_NEEDS_UPGRADE_TEMPFILE +// +// MessageText: +// +// An updated version of your media player is required to play the selected content.%0 +// +#define NS_E_DRM_NEEDS_UPGRADE_TEMPFILE _HRESULT_TYPEDEF_(0xC00D283DL) + +// +// MessageId: NS_E_DRM_NEED_UPGRADE_PD +// +// MessageText: +// +// A new version of the Digital Rights Management component is required. Contact product support for this application to get the latest version.%0 +// +#define NS_E_DRM_NEED_UPGRADE_PD _HRESULT_TYPEDEF_(0xC00D283EL) + +// +// MessageId: NS_E_DRM_SIGNATURE_FAILURE +// +// MessageText: +// +// Failed to either create or verify the content header.%0 +// +#define NS_E_DRM_SIGNATURE_FAILURE _HRESULT_TYPEDEF_(0xC00D283FL) + +// +// MessageId: NS_E_DRM_LICENSE_SERVER_INFO_MISSING +// +// MessageText: +// +// Could not read the necessary information from the system registry.%0 +// +#define NS_E_DRM_LICENSE_SERVER_INFO_MISSING _HRESULT_TYPEDEF_(0xC00D2840L) + +// +// MessageId: NS_E_DRM_BUSY +// +// MessageText: +// +// The DRM subsystem is currently locked by another application or user. Try again later.%0 +// +#define NS_E_DRM_BUSY _HRESULT_TYPEDEF_(0xC00D2841L) + +// +// MessageId: NS_E_DRM_PD_TOO_MANY_DEVICES +// +// MessageText: +// +// There are too many target devices registered on the portable media.%0 +// +#define NS_E_DRM_PD_TOO_MANY_DEVICES _HRESULT_TYPEDEF_(0xC00D2842L) + +// +// MessageId: NS_E_DRM_INDIV_FRAUD +// +// MessageText: +// +// The security upgrade cannot be completed because the allowed number of daily upgrades has been exceeded. Try again tomorrow.%0 +// +#define NS_E_DRM_INDIV_FRAUD _HRESULT_TYPEDEF_(0xC00D2843L) + +// +// MessageId: NS_E_DRM_INDIV_NO_CABS +// +// MessageText: +// +// The security upgrade cannot be completed because the server is unable to perform the operation. Try again later.%0 +// +#define NS_E_DRM_INDIV_NO_CABS _HRESULT_TYPEDEF_(0xC00D2844L) + +// +// MessageId: NS_E_DRM_INDIV_SERVICE_UNAVAILABLE +// +// MessageText: +// +// The security upgrade cannot be performed because the server is not available. Try again later.%0 +// +#define NS_E_DRM_INDIV_SERVICE_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D2845L) + +// +// MessageId: NS_E_DRM_RESTORE_SERVICE_UNAVAILABLE +// +// MessageText: +// +// Windows Media Player cannot restore your licenses because the server is not available. Try again later.%0 +// +#define NS_E_DRM_RESTORE_SERVICE_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D2846L) + + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Setup Specific Errors +// +// IdRange = 11000..11999 +///////////////////////////////////////////////////////////////////////// +// +// MessageId: NS_S_REBOOT_RECOMMENDED +// +// MessageText: +// +// The requested operation is successful. Some cleanup will not be complete until the system is rebooted.%0 +// +#define NS_S_REBOOT_RECOMMENDED _HRESULT_TYPEDEF_(0x000D2AF8L) + +// +// MessageId: NS_S_REBOOT_REQUIRED +// +// MessageText: +// +// The requested operation is successful. The system will not function correctly until the system is rebooted.%0 +// +#define NS_S_REBOOT_REQUIRED _HRESULT_TYPEDEF_(0x000D2AF9L) + +// +// MessageId: NS_E_REBOOT_RECOMMENDED +// +// MessageText: +// +// The requested operation failed. Some cleanup will not be complete until the system is rebooted.%0 +// +#define NS_E_REBOOT_RECOMMENDED _HRESULT_TYPEDEF_(0xC00D2AFAL) + +// +// MessageId: NS_E_REBOOT_REQUIRED +// +// MessageText: +// +// The requested operation failed. The system will not function correctly until the system is rebooted.%0 +// +#define NS_E_REBOOT_REQUIRED _HRESULT_TYPEDEF_(0xC00D2AFBL) + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Networking Errors +// +// IdRange = 12000..12999 +///////////////////////////////////////////////////////////////////////// +// +// MessageId: NS_E_UNKNOWN_PROTOCOL +// +// MessageText: +// +// The specified protocol is not supported.%0 +// +#define NS_E_UNKNOWN_PROTOCOL _HRESULT_TYPEDEF_(0xC00D2EE0L) + +// +// MessageId: NS_E_REDIRECT_TO_PROXY +// +// MessageText: +// +// The client is redirected to a proxy server.%0 +// +#define NS_E_REDIRECT_TO_PROXY _HRESULT_TYPEDEF_(0xC00D2EE1L) + +// +// MessageId: NS_E_INTERNAL_SERVER_ERROR +// +// MessageText: +// +// The server encountered an unexpected condition which prevented it from fulfilling the request.%0 +// +#define NS_E_INTERNAL_SERVER_ERROR _HRESULT_TYPEDEF_(0xC00D2EE2L) + +// +// MessageId: NS_E_BAD_REQUEST +// +// MessageText: +// +// The request could not be understood by the server.%0 +// +#define NS_E_BAD_REQUEST _HRESULT_TYPEDEF_(0xC00D2EE3L) + +// +// MessageId: NS_E_ERROR_FROM_PROXY +// +// MessageText: +// +// The proxy experienced an error while attempting to contact the media server.%0 +// +#define NS_E_ERROR_FROM_PROXY _HRESULT_TYPEDEF_(0xC00D2EE4L) + +// +// MessageId: NS_E_PROXY_TIMEOUT +// +// MessageText: +// +// The proxy did not receive a timely response while attempting to contact the media server.%0 +// +#define NS_E_PROXY_TIMEOUT _HRESULT_TYPEDEF_(0xC00D2EE5L) + +// +// MessageId: NS_E_SERVER_UNAVAILABLE +// +// MessageText: +// +// The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.%0 +// +#define NS_E_SERVER_UNAVAILABLE _HRESULT_TYPEDEF_(0xC00D2EE6L) + +// +// MessageId: NS_E_REFUSED_BY_SERVER +// +// MessageText: +// +// The server is refusing to fulfill the requested operation.%0 +// +#define NS_E_REFUSED_BY_SERVER _HRESULT_TYPEDEF_(0xC00D2EE7L) + +// +// MessageId: NS_E_INCOMPATIBLE_SERVER +// +// MessageText: +// +// The server is not a compatible streaming media server.%0 +// +#define NS_E_INCOMPATIBLE_SERVER _HRESULT_TYPEDEF_(0xC00D2EE8L) + +// +// MessageId: NS_E_MULTICAST_DISABLED +// +// MessageText: +// +// The content cannot be streamed because the Multicast protocol has been disabled.%0 +// +#define NS_E_MULTICAST_DISABLED _HRESULT_TYPEDEF_(0xC00D2EE9L) + +// +// MessageId: NS_E_INVALID_REDIRECT +// +// MessageText: +// +// The server redirected the player to an invalid location.%0 +// +#define NS_E_INVALID_REDIRECT _HRESULT_TYPEDEF_(0xC00D2EEAL) + +// +// MessageId: NS_E_ALL_PROTOCOLS_DISABLED +// +// MessageText: +// +// The content cannot be streamed because all protocols have been disabled.%0 +// +#define NS_E_ALL_PROTOCOLS_DISABLED _HRESULT_TYPEDEF_(0xC00D2EEBL) + +// +// MessageId: NS_E_MSBD_NO_LONGER_SUPPORTED +// +// MessageText: +// +// The MSBD protocol is no longer supported. Please use HTTP to connect to the Windows Media stream.%0 +// +#define NS_E_MSBD_NO_LONGER_SUPPORTED _HRESULT_TYPEDEF_(0xC00D2EECL) + +// +// MessageId: NS_E_PROXY_NOT_FOUND +// +// MessageText: +// +// The proxy server could not be located. Please check your proxy server configuration.%0 +// +#define NS_E_PROXY_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D2EEDL) + +// +// MessageId: NS_E_CANNOT_CONNECT_TO_PROXY +// +// MessageText: +// +// Unable to establish a connection to the proxy server. Please check your proxy server configuration.%0 +// +#define NS_E_CANNOT_CONNECT_TO_PROXY _HRESULT_TYPEDEF_(0xC00D2EEEL) + +// +// MessageId: NS_E_SERVER_DNS_TIMEOUT +// +// MessageText: +// +// Unable to locate the media server. The operation timed out.%0 +// +#define NS_E_SERVER_DNS_TIMEOUT _HRESULT_TYPEDEF_(0xC00D2EEFL) + +// +// MessageId: NS_E_PROXY_DNS_TIMEOUT +// +// MessageText: +// +// Unable to locate the proxy server. The operation timed out.%0 +// +#define NS_E_PROXY_DNS_TIMEOUT _HRESULT_TYPEDEF_(0xC00D2EF0L) + +// +// MessageId: NS_E_CLOSED_ON_SUSPEND +// +// MessageText: +// +// Media closed because Windows was shut down.%0 +// +#define NS_E_CLOSED_ON_SUSPEND _HRESULT_TYPEDEF_(0xC00D2EF1L) + +// +// MessageId: NS_E_CANNOT_READ_PLAYLIST_FROM_MEDIASERVER +// +// MessageText: +// +// Unable to read the contents of a playlist file from a media server.%0 +// +#define NS_E_CANNOT_READ_PLAYLIST_FROM_MEDIASERVER _HRESULT_TYPEDEF_(0xC00D2EF2L) + +// +// MessageId: NS_E_SESSION_NOT_FOUND +// +// MessageText: +// +// Session not found.%0 +// +#define NS_E_SESSION_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D2EF3L) + +// +// MessageId: NS_E_REQUIRE_STREAMING_CLIENT +// +// MessageText: +// +// Content requires a streaming media client.%0 +// +#define NS_E_REQUIRE_STREAMING_CLIENT _HRESULT_TYPEDEF_(0xC00D2EF4L) + +// +// MessageId: NS_E_PLAYLIST_ENTRY_HAS_CHANGED +// +// MessageText: +// +// A command applies to a previous playlist entry.%0 +// +#define NS_E_PLAYLIST_ENTRY_HAS_CHANGED _HRESULT_TYPEDEF_(0xC00D2EF5L) + +// +// MessageId: NS_E_PROXY_ACCESSDENIED +// +// MessageText: +// +// The proxy server is denying access. The username and/or password might be incorrect.%0 +// +#define NS_E_PROXY_ACCESSDENIED _HRESULT_TYPEDEF_(0xC00D2EF6L) + +// +// MessageId: NS_E_PROXY_SOURCE_ACCESSDENIED +// +// MessageText: +// +// The proxy could not provide valid authentication credentials to the media server.%0 +// +#define NS_E_PROXY_SOURCE_ACCESSDENIED _HRESULT_TYPEDEF_(0xC00D2EF7L) + +// +// MessageId: NS_E_NETWORK_SINK_WRITE +// +// MessageText: +// +// The network sink failed to write data to the network.%0 +// +#define NS_E_NETWORK_SINK_WRITE _HRESULT_TYPEDEF_(0xC00D2EF8L) + +// +// MessageId: NS_E_FIREWALL +// +// MessageText: +// +// Packets are not being received from the server. The packets might be blocked by a filtering device, such as a network firewall.%0 +// +#define NS_E_FIREWALL _HRESULT_TYPEDEF_(0xC00D2EF9L) + +// +// MessageId: NS_E_MMS_NOT_SUPPORTED +// +// MessageText: +// +// The MMS protocol is not supported. Please use HTTP or RTSP to connect to the Windows Media stream.%0 +// +#define NS_E_MMS_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xC00D2EFAL) + +// +// MessageId: NS_E_SERVER_ACCESSDENIED +// +// MessageText: +// +// The Windows Media server is denying access. The username and/or password might be incorrect.%0 +// +#define NS_E_SERVER_ACCESSDENIED _HRESULT_TYPEDEF_(0xC00D2EFBL) + +// +// MessageId: NS_E_RESOURCE_GONE +// +// MessageText: +// +// The Publishing Point or file on the Windows Media Server is no longer available.%0 +// +#define NS_E_RESOURCE_GONE _HRESULT_TYPEDEF_(0xC00D2EFCL) + +// +// MessageId: NS_E_NO_EXISTING_PACKETIZER +// +// MessageText: +// +// There is no existing packetizer plugin for a stream.%0 +// +#define NS_E_NO_EXISTING_PACKETIZER _HRESULT_TYPEDEF_(0xC00D2EFDL) + +// +// MessageId: NS_E_BAD_SYNTAX_IN_SERVER_RESPONSE +// +// MessageText: +// +// The response from the media server could not be understood. This might be caused by an incompatible proxy server or media server.%0 +// +#define NS_E_BAD_SYNTAX_IN_SERVER_RESPONSE _HRESULT_TYPEDEF_(0xC00D2EFEL) + +// +// MessageId: NS_I_RECONNECTED +// +// MessageText: +// +// The client is reconnected.%0 +// +#define NS_I_RECONNECTED _HRESULT_TYPEDEF_(0x400D2EFFL) + +// +// MessageId: NS_E_RESET_SOCKET_CONNECTION +// +// MessageText: +// +// The Windows Media Server reset the network connection.%0 +// +#define NS_E_RESET_SOCKET_CONNECTION _HRESULT_TYPEDEF_(0xC00D2F00L) + +// +// MessageId: NS_I_NOLOG_STOP +// +// MessageText: +// +// Forcing a switch to a pending header on start.%0 +// +#define NS_I_NOLOG_STOP _HRESULT_TYPEDEF_(0x400D2F01L) + +// +// MessageId: NS_E_TOO_MANY_HOPS +// +// MessageText: +// +// The request could not reach the media server (too many hops).%0 +// +#define NS_E_TOO_MANY_HOPS _HRESULT_TYPEDEF_(0xC00D2F02L) + +// +// MessageId: NS_I_EXISTING_PACKETIZER +// +// MessageText: +// +// There is already an existing packetizer plugin for the stream.%0 +// +#define NS_I_EXISTING_PACKETIZER _HRESULT_TYPEDEF_(0x400D2F03L) + +// +// MessageId: NS_I_MANUAL_PROXY +// +// MessageText: +// +// The proxy setting is manual.%0 +// +#define NS_I_MANUAL_PROXY _HRESULT_TYPEDEF_(0x400D2F04L) + +// +// MessageId: NS_E_TOO_MUCH_DATA_FROM_SERVER +// +// MessageText: +// +// The server is sending too much data. The connection has been terminated.%0 +// +#define NS_E_TOO_MUCH_DATA_FROM_SERVER _HRESULT_TYPEDEF_(0xC00D2F05L) + +// +// MessageId: NS_E_CONNECT_TIMEOUT +// +// MessageText: +// +// It was not possible to establish a connection to the media server in a timely manner. The media server may be down for maintenance, or it may be necessary to use a proxy server to access this media server.%0 +// +#define NS_E_CONNECT_TIMEOUT _HRESULT_TYPEDEF_(0xC00D2F06L) + +// +// MessageId: NS_E_PROXY_CONNECT_TIMEOUT +// +// MessageText: +// +// It was not possible to establish a connection to the proxy server in a timely manner. Please check your proxy server configuration.%0 +// +#define NS_E_PROXY_CONNECT_TIMEOUT _HRESULT_TYPEDEF_(0xC00D2F07L) + +// +// MessageId: NS_E_SESSION_INVALID +// +// MessageText: +// +// Session not found.%0 +// +#define NS_E_SESSION_INVALID _HRESULT_TYPEDEF_(0xC00D2F08L) + +// +// MessageId: NS_S_EOSRECEDING +// +// MessageText: +// +// EOS hit during rewinding.%0 +// +#define NS_S_EOSRECEDING _HRESULT_TYPEDEF_(0x000D2F09L) + +// +// MessageId: NS_E_PACKETSINK_UNKNOWN_FEC_STREAM +// +// MessageText: +// +// Unknown packet sink stream.%0 +// +#define NS_E_PACKETSINK_UNKNOWN_FEC_STREAM _HRESULT_TYPEDEF_(0xC00D2F0AL) + +// +// MessageId: NS_E_PUSH_CANNOTCONNECT +// +// MessageText: +// +// Unable to establish a connection to the server. Ensure Windows Media Services is started and the HTTP Server control protocol is properly enabled.%0 +// +#define NS_E_PUSH_CANNOTCONNECT _HRESULT_TYPEDEF_(0xC00D2F0BL) + +// +// MessageId: NS_E_INCOMPATIBLE_PUSH_SERVER +// +// MessageText: +// +// The Server service that received the HTTP push request is not a compatible version of Windows Media Services (WMS). This error may indicate the push request was received by IIS instead of WMS. Ensure WMS is started and has the HTTP Server control protocol properly enabled and try again.%0 +// +#define NS_E_INCOMPATIBLE_PUSH_SERVER _HRESULT_TYPEDEF_(0xC00D2F0CL) + +// +// MessageId: NS_S_CHANGENOTICE +// +// MessageText: +// +// Internal.%0 +// +#define NS_S_CHANGENOTICE _HRESULT_TYPEDEF_(0x000D2F0DL) + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Client Media Services +// +// IdRange = 13000..13999 (0x32C8-0x36AF) +///////////////////////////////////////////////////////////////////////// +// +// MessageId: NS_E_END_OF_PLAYLIST +// +// MessageText: +// +// The playlist has reached its end.%0 +// +#define NS_E_END_OF_PLAYLIST _HRESULT_TYPEDEF_(0xC00D32C8L) + +// +// MessageId: NS_E_USE_FILE_SOURCE +// +// MessageText: +// +// Use file source.%0 +// +#define NS_E_USE_FILE_SOURCE _HRESULT_TYPEDEF_(0xC00D32C9L) + +// +// MessageId: NS_E_PROPERTY_NOT_FOUND +// +// MessageText: +// +// The property was not found.%0 +// +#define NS_E_PROPERTY_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D32CAL) + +// +// MessageId: NS_E_PROPERTY_READ_ONLY +// +// MessageText: +// +// The property is read only.%0 +// +#define NS_E_PROPERTY_READ_ONLY _HRESULT_TYPEDEF_(0xC00D32CCL) + +// +// MessageId: NS_E_TABLE_KEY_NOT_FOUND +// +// MessageText: +// +// The table key was not found.%0 +// +#define NS_E_TABLE_KEY_NOT_FOUND _HRESULT_TYPEDEF_(0xC00D32CDL) + +// +// MessageId: NS_E_INVALID_QUERY_OPERATOR +// +// MessageText: +// +// Invalid query operator.%0 +// +#define NS_E_INVALID_QUERY_OPERATOR _HRESULT_TYPEDEF_(0xC00D32CFL) + +// +// MessageId: NS_E_INVALID_QUERY_PROPERTY +// +// MessageText: +// +// Invalid query property.%0 +// +#define NS_E_INVALID_QUERY_PROPERTY _HRESULT_TYPEDEF_(0xC00D32D0L) + +// +// MessageId: NS_E_PROPERTY_NOT_SUPPORTED +// +// MessageText: +// +// The property is not supported.%0 +// +#define NS_E_PROPERTY_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xC00D32D2L) + +// +// MessageId: NS_E_SCHEMA_CLASSIFY_FAILURE +// +// MessageText: +// +// Schema classification failure.%0 +// +#define NS_E_SCHEMA_CLASSIFY_FAILURE _HRESULT_TYPEDEF_(0xC00D32D4L) + +// +// MessageId: NS_E_METADATA_FORMAT_NOT_SUPPORTED +// +// MessageText: +// +// The metadata format is not supported.%0 +// +#define NS_E_METADATA_FORMAT_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xC00D32D5L) + +// +// MessageId: NS_E_METADATA_NO_EDITING_CAPABILITY +// +// MessageText: +// +// Cannot edit the metadata.%0 +// +#define NS_E_METADATA_NO_EDITING_CAPABILITY _HRESULT_TYPEDEF_(0xC00D32D6L) + +// +// MessageId: NS_E_METADATA_CANNOT_SET_LOCALE +// +// MessageText: +// +// Cannot set the locale id.%0 +// +#define NS_E_METADATA_CANNOT_SET_LOCALE _HRESULT_TYPEDEF_(0xC00D32D7L) + +// +// MessageId: NS_E_METADATA_LANGUAGE_NOT_SUPORTED +// +// MessageText: +// +// The language is not supported in the format.%0 +// +#define NS_E_METADATA_LANGUAGE_NOT_SUPORTED _HRESULT_TYPEDEF_(0xC00D32D8L) + +// +// MessageId: NS_E_METADATA_NO_RFC1766_NAME_FOR_LOCALE +// +// MessageText: +// +// There is no RFC1766 name translation for the supplied locale id.%0 +// +#define NS_E_METADATA_NO_RFC1766_NAME_FOR_LOCALE _HRESULT_TYPEDEF_(0xC00D32D9L) + +// +// MessageId: NS_E_METADATA_NOT_AVAILABLE +// +// MessageText: +// +// The metadata (or metadata item) is not available.%0 +// +#define NS_E_METADATA_NOT_AVAILABLE _HRESULT_TYPEDEF_(0xC00D32DAL) + +// +// MessageId: NS_E_METADATA_CACHE_DATA_NOT_AVAILABLE +// +// MessageText: +// +// The cached metadata (or metadata item) is not available.%0 +// +#define NS_E_METADATA_CACHE_DATA_NOT_AVAILABLE _HRESULT_TYPEDEF_(0xC00D32DBL) + +// +// MessageId: NS_E_METADATA_INVALID_DOCUMENT_TYPE +// +// MessageText: +// +// The metadata document is invalid.%0 +// +#define NS_E_METADATA_INVALID_DOCUMENT_TYPE _HRESULT_TYPEDEF_(0xC00D32DCL) + +// +// MessageId: NS_E_METADATA_IDENTIFIER_NOT_AVAILABLE +// +// MessageText: +// +// The metadata content identifier is not available.%0 +// +#define NS_E_METADATA_IDENTIFIER_NOT_AVAILABLE _HRESULT_TYPEDEF_(0xC00D32DDL) + +// +// MessageId: NS_E_METADATA_CANNOT_RETRIEVE_FROM_OFFLINE_CACHE +// +// MessageText: +// +// Cannot retrieve metadata from the offline metadata cache.%0 +// +#define NS_E_METADATA_CANNOT_RETRIEVE_FROM_OFFLINE_CACHE _HRESULT_TYPEDEF_(0xC00D32DEL) + + +#endif // _NSERROR_H + diff --git a/win32/lib/wmsdk/include/wmsbuffer.h b/win32/lib/wmsdk/include/wmsbuffer.h new file mode 100755 index 0000000..637a89b --- /dev/null +++ b/win32/lib/wmsdk/include/wmsbuffer.h @@ -0,0 +1,947 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 6.00.0361 */ +/* Compiler settings for wmsbuffer.idl: + Oicf, W1, Zp8, env=Win32 (32b run) + protocol : dce , ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + + +/* verify that the <rpcndr.h> version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 475 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of <rpcndr.h> +#endif // __RPCNDR_H_VERSION__ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __wmsbuffer_h__ +#define __wmsbuffer_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +/* Forward Declarations */ + +#ifndef __INSSBuffer_FWD_DEFINED__ +#define __INSSBuffer_FWD_DEFINED__ +typedef interface INSSBuffer INSSBuffer; +#endif /* __INSSBuffer_FWD_DEFINED__ */ + + +#ifndef __INSSBuffer2_FWD_DEFINED__ +#define __INSSBuffer2_FWD_DEFINED__ +typedef interface INSSBuffer2 INSSBuffer2; +#endif /* __INSSBuffer2_FWD_DEFINED__ */ + + +#ifndef __INSSBuffer3_FWD_DEFINED__ +#define __INSSBuffer3_FWD_DEFINED__ +typedef interface INSSBuffer3 INSSBuffer3; +#endif /* __INSSBuffer3_FWD_DEFINED__ */ + + +#ifndef __INSSBuffer4_FWD_DEFINED__ +#define __INSSBuffer4_FWD_DEFINED__ +typedef interface INSSBuffer4 INSSBuffer4; +#endif /* __INSSBuffer4_FWD_DEFINED__ */ + + +#ifndef __IWMSBufferAllocator_FWD_DEFINED__ +#define __IWMSBufferAllocator_FWD_DEFINED__ +typedef interface IWMSBufferAllocator IWMSBufferAllocator; +#endif /* __IWMSBufferAllocator_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "objidl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +void * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void * ); + +/* interface __MIDL_itf_wmsbuffer_0000 */ +/* [local] */ + +//========================================================================= +// +// Microsoft Windows Media Technologies +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//========================================================================= +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +EXTERN_GUID( IID_INSSBuffer, 0xE1CD3524,0x03D7,0x11d2,0x9E,0xED,0x00,0x60,0x97,0xD2,0xD7,0xCF ); +EXTERN_GUID( IID_IWMSBuffer, 0xE1CD3524,0x03D7,0x11d2,0x9E,0xED,0x00,0x60,0x97,0xD2,0xD7,0xCF ); +EXTERN_GUID( IID_INSSBuffer2,0x4f528693,0x1035,0x43fe,0xb4,0x28,0x75,0x75,0x61,0xad,0x3a,0x68 ); +EXTERN_GUID( IID_INSSBuffer3,0xc87ceaaf,0x75be,0x4bc4,0x84,0xeb,0xac,0x27,0x98,0x50,0x76,0x72 ); +EXTERN_GUID( IID_INSSBuffer4,0xb6b8fd5a,0x32e2,0x49d4,0xa9,0x10,0xc2,0x6c,0xc8,0x54,0x65,0xed ); +EXTERN_GUID( IID_IWMSBufferAllocator, 0x61103CA4,0x2033,0x11d2,0x9E,0xF1,0x00,0x60,0x97,0xD2,0xD7,0xCF ); +#define IWMSBuffer INSSBuffer + + + +extern RPC_IF_HANDLE __MIDL_itf_wmsbuffer_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_wmsbuffer_0000_v0_0_s_ifspec; + +#ifndef __INSSBuffer_INTERFACE_DEFINED__ +#define __INSSBuffer_INTERFACE_DEFINED__ + +/* interface INSSBuffer */ +/* [version][uuid][unique][object][local] */ + + +EXTERN_C const IID IID_INSSBuffer; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("E1CD3524-03D7-11d2-9EED-006097D2D7CF") + INSSBuffer : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetLength( + /* [out] */ DWORD *pdwLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetLength( + /* [in] */ DWORD dwLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxLength( + /* [out] */ DWORD *pdwLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBuffer( + /* [out] */ BYTE **ppdwBuffer) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBufferAndLength( + /* [out] */ BYTE **ppdwBuffer, + /* [out] */ DWORD *pdwLength) = 0; + + }; + +#else /* C style interface */ + + typedef struct INSSBufferVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + INSSBuffer * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + INSSBuffer * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + INSSBuffer * This); + + HRESULT ( STDMETHODCALLTYPE *GetLength )( + INSSBuffer * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *SetLength )( + INSSBuffer * This, + /* [in] */ DWORD dwLength); + + HRESULT ( STDMETHODCALLTYPE *GetMaxLength )( + INSSBuffer * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetBuffer )( + INSSBuffer * This, + /* [out] */ BYTE **ppdwBuffer); + + HRESULT ( STDMETHODCALLTYPE *GetBufferAndLength )( + INSSBuffer * This, + /* [out] */ BYTE **ppdwBuffer, + /* [out] */ DWORD *pdwLength); + + END_INTERFACE + } INSSBufferVtbl; + + interface INSSBuffer + { + CONST_VTBL struct INSSBufferVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define INSSBuffer_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define INSSBuffer_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define INSSBuffer_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define INSSBuffer_GetLength(This,pdwLength) \ + (This)->lpVtbl -> GetLength(This,pdwLength) + +#define INSSBuffer_SetLength(This,dwLength) \ + (This)->lpVtbl -> SetLength(This,dwLength) + +#define INSSBuffer_GetMaxLength(This,pdwLength) \ + (This)->lpVtbl -> GetMaxLength(This,pdwLength) + +#define INSSBuffer_GetBuffer(This,ppdwBuffer) \ + (This)->lpVtbl -> GetBuffer(This,ppdwBuffer) + +#define INSSBuffer_GetBufferAndLength(This,ppdwBuffer,pdwLength) \ + (This)->lpVtbl -> GetBufferAndLength(This,ppdwBuffer,pdwLength) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE INSSBuffer_GetLength_Proxy( + INSSBuffer * This, + /* [out] */ DWORD *pdwLength); + + +void __RPC_STUB INSSBuffer_GetLength_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer_SetLength_Proxy( + INSSBuffer * This, + /* [in] */ DWORD dwLength); + + +void __RPC_STUB INSSBuffer_SetLength_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer_GetMaxLength_Proxy( + INSSBuffer * This, + /* [out] */ DWORD *pdwLength); + + +void __RPC_STUB INSSBuffer_GetMaxLength_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer_GetBuffer_Proxy( + INSSBuffer * This, + /* [out] */ BYTE **ppdwBuffer); + + +void __RPC_STUB INSSBuffer_GetBuffer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer_GetBufferAndLength_Proxy( + INSSBuffer * This, + /* [out] */ BYTE **ppdwBuffer, + /* [out] */ DWORD *pdwLength); + + +void __RPC_STUB INSSBuffer_GetBufferAndLength_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __INSSBuffer_INTERFACE_DEFINED__ */ + + +#ifndef __INSSBuffer2_INTERFACE_DEFINED__ +#define __INSSBuffer2_INTERFACE_DEFINED__ + +/* interface INSSBuffer2 */ +/* [version][uuid][unique][object][local] */ + + +EXTERN_C const IID IID_INSSBuffer2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("4F528693-1035-43fe-B428-757561AD3A68") + INSSBuffer2 : public INSSBuffer + { + public: + virtual HRESULT STDMETHODCALLTYPE GetSampleProperties( + /* [in] */ DWORD cbProperties, + /* [out] */ BYTE *pbProperties) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetSampleProperties( + /* [in] */ DWORD cbProperties, + /* [in] */ BYTE *pbProperties) = 0; + + }; + +#else /* C style interface */ + + typedef struct INSSBuffer2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + INSSBuffer2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + INSSBuffer2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + INSSBuffer2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetLength )( + INSSBuffer2 * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *SetLength )( + INSSBuffer2 * This, + /* [in] */ DWORD dwLength); + + HRESULT ( STDMETHODCALLTYPE *GetMaxLength )( + INSSBuffer2 * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetBuffer )( + INSSBuffer2 * This, + /* [out] */ BYTE **ppdwBuffer); + + HRESULT ( STDMETHODCALLTYPE *GetBufferAndLength )( + INSSBuffer2 * This, + /* [out] */ BYTE **ppdwBuffer, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetSampleProperties )( + INSSBuffer2 * This, + /* [in] */ DWORD cbProperties, + /* [out] */ BYTE *pbProperties); + + HRESULT ( STDMETHODCALLTYPE *SetSampleProperties )( + INSSBuffer2 * This, + /* [in] */ DWORD cbProperties, + /* [in] */ BYTE *pbProperties); + + END_INTERFACE + } INSSBuffer2Vtbl; + + interface INSSBuffer2 + { + CONST_VTBL struct INSSBuffer2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define INSSBuffer2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define INSSBuffer2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define INSSBuffer2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define INSSBuffer2_GetLength(This,pdwLength) \ + (This)->lpVtbl -> GetLength(This,pdwLength) + +#define INSSBuffer2_SetLength(This,dwLength) \ + (This)->lpVtbl -> SetLength(This,dwLength) + +#define INSSBuffer2_GetMaxLength(This,pdwLength) \ + (This)->lpVtbl -> GetMaxLength(This,pdwLength) + +#define INSSBuffer2_GetBuffer(This,ppdwBuffer) \ + (This)->lpVtbl -> GetBuffer(This,ppdwBuffer) + +#define INSSBuffer2_GetBufferAndLength(This,ppdwBuffer,pdwLength) \ + (This)->lpVtbl -> GetBufferAndLength(This,ppdwBuffer,pdwLength) + + +#define INSSBuffer2_GetSampleProperties(This,cbProperties,pbProperties) \ + (This)->lpVtbl -> GetSampleProperties(This,cbProperties,pbProperties) + +#define INSSBuffer2_SetSampleProperties(This,cbProperties,pbProperties) \ + (This)->lpVtbl -> SetSampleProperties(This,cbProperties,pbProperties) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE INSSBuffer2_GetSampleProperties_Proxy( + INSSBuffer2 * This, + /* [in] */ DWORD cbProperties, + /* [out] */ BYTE *pbProperties); + + +void __RPC_STUB INSSBuffer2_GetSampleProperties_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer2_SetSampleProperties_Proxy( + INSSBuffer2 * This, + /* [in] */ DWORD cbProperties, + /* [in] */ BYTE *pbProperties); + + +void __RPC_STUB INSSBuffer2_SetSampleProperties_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __INSSBuffer2_INTERFACE_DEFINED__ */ + + +#ifndef __INSSBuffer3_INTERFACE_DEFINED__ +#define __INSSBuffer3_INTERFACE_DEFINED__ + +/* interface INSSBuffer3 */ +/* [version][uuid][unique][object][local] */ + + +EXTERN_C const IID IID_INSSBuffer3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("C87CEAAF-75BE-4bc4-84EB-AC2798507672") + INSSBuffer3 : public INSSBuffer2 + { + public: + virtual HRESULT STDMETHODCALLTYPE SetProperty( + /* [in] */ GUID guidBufferProperty, + /* [in] */ void *pvBufferProperty, + /* [in] */ DWORD dwBufferPropertySize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProperty( + /* [in] */ GUID guidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize) = 0; + + }; + +#else /* C style interface */ + + typedef struct INSSBuffer3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + INSSBuffer3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + INSSBuffer3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + INSSBuffer3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetLength )( + INSSBuffer3 * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *SetLength )( + INSSBuffer3 * This, + /* [in] */ DWORD dwLength); + + HRESULT ( STDMETHODCALLTYPE *GetMaxLength )( + INSSBuffer3 * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetBuffer )( + INSSBuffer3 * This, + /* [out] */ BYTE **ppdwBuffer); + + HRESULT ( STDMETHODCALLTYPE *GetBufferAndLength )( + INSSBuffer3 * This, + /* [out] */ BYTE **ppdwBuffer, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetSampleProperties )( + INSSBuffer3 * This, + /* [in] */ DWORD cbProperties, + /* [out] */ BYTE *pbProperties); + + HRESULT ( STDMETHODCALLTYPE *SetSampleProperties )( + INSSBuffer3 * This, + /* [in] */ DWORD cbProperties, + /* [in] */ BYTE *pbProperties); + + HRESULT ( STDMETHODCALLTYPE *SetProperty )( + INSSBuffer3 * This, + /* [in] */ GUID guidBufferProperty, + /* [in] */ void *pvBufferProperty, + /* [in] */ DWORD dwBufferPropertySize); + + HRESULT ( STDMETHODCALLTYPE *GetProperty )( + INSSBuffer3 * This, + /* [in] */ GUID guidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize); + + END_INTERFACE + } INSSBuffer3Vtbl; + + interface INSSBuffer3 + { + CONST_VTBL struct INSSBuffer3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define INSSBuffer3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define INSSBuffer3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define INSSBuffer3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define INSSBuffer3_GetLength(This,pdwLength) \ + (This)->lpVtbl -> GetLength(This,pdwLength) + +#define INSSBuffer3_SetLength(This,dwLength) \ + (This)->lpVtbl -> SetLength(This,dwLength) + +#define INSSBuffer3_GetMaxLength(This,pdwLength) \ + (This)->lpVtbl -> GetMaxLength(This,pdwLength) + +#define INSSBuffer3_GetBuffer(This,ppdwBuffer) \ + (This)->lpVtbl -> GetBuffer(This,ppdwBuffer) + +#define INSSBuffer3_GetBufferAndLength(This,ppdwBuffer,pdwLength) \ + (This)->lpVtbl -> GetBufferAndLength(This,ppdwBuffer,pdwLength) + + +#define INSSBuffer3_GetSampleProperties(This,cbProperties,pbProperties) \ + (This)->lpVtbl -> GetSampleProperties(This,cbProperties,pbProperties) + +#define INSSBuffer3_SetSampleProperties(This,cbProperties,pbProperties) \ + (This)->lpVtbl -> SetSampleProperties(This,cbProperties,pbProperties) + + +#define INSSBuffer3_SetProperty(This,guidBufferProperty,pvBufferProperty,dwBufferPropertySize) \ + (This)->lpVtbl -> SetProperty(This,guidBufferProperty,pvBufferProperty,dwBufferPropertySize) + +#define INSSBuffer3_GetProperty(This,guidBufferProperty,pvBufferProperty,pdwBufferPropertySize) \ + (This)->lpVtbl -> GetProperty(This,guidBufferProperty,pvBufferProperty,pdwBufferPropertySize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE INSSBuffer3_SetProperty_Proxy( + INSSBuffer3 * This, + /* [in] */ GUID guidBufferProperty, + /* [in] */ void *pvBufferProperty, + /* [in] */ DWORD dwBufferPropertySize); + + +void __RPC_STUB INSSBuffer3_SetProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer3_GetProperty_Proxy( + INSSBuffer3 * This, + /* [in] */ GUID guidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize); + + +void __RPC_STUB INSSBuffer3_GetProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __INSSBuffer3_INTERFACE_DEFINED__ */ + + +#ifndef __INSSBuffer4_INTERFACE_DEFINED__ +#define __INSSBuffer4_INTERFACE_DEFINED__ + +/* interface INSSBuffer4 */ +/* [version][uuid][unique][object][local] */ + + +EXTERN_C const IID IID_INSSBuffer4; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("B6B8FD5A-32E2-49d4-A910-C26CC85465ED") + INSSBuffer4 : public INSSBuffer3 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetPropertyCount( + /* [out] */ DWORD *pcBufferProperties) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyByIndex( + /* [in] */ DWORD dwBufferPropertyIndex, + /* [out] */ GUID *pguidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize) = 0; + + }; + +#else /* C style interface */ + + typedef struct INSSBuffer4Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + INSSBuffer4 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + INSSBuffer4 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + INSSBuffer4 * This); + + HRESULT ( STDMETHODCALLTYPE *GetLength )( + INSSBuffer4 * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *SetLength )( + INSSBuffer4 * This, + /* [in] */ DWORD dwLength); + + HRESULT ( STDMETHODCALLTYPE *GetMaxLength )( + INSSBuffer4 * This, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetBuffer )( + INSSBuffer4 * This, + /* [out] */ BYTE **ppdwBuffer); + + HRESULT ( STDMETHODCALLTYPE *GetBufferAndLength )( + INSSBuffer4 * This, + /* [out] */ BYTE **ppdwBuffer, + /* [out] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetSampleProperties )( + INSSBuffer4 * This, + /* [in] */ DWORD cbProperties, + /* [out] */ BYTE *pbProperties); + + HRESULT ( STDMETHODCALLTYPE *SetSampleProperties )( + INSSBuffer4 * This, + /* [in] */ DWORD cbProperties, + /* [in] */ BYTE *pbProperties); + + HRESULT ( STDMETHODCALLTYPE *SetProperty )( + INSSBuffer4 * This, + /* [in] */ GUID guidBufferProperty, + /* [in] */ void *pvBufferProperty, + /* [in] */ DWORD dwBufferPropertySize); + + HRESULT ( STDMETHODCALLTYPE *GetProperty )( + INSSBuffer4 * This, + /* [in] */ GUID guidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyCount )( + INSSBuffer4 * This, + /* [out] */ DWORD *pcBufferProperties); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyByIndex )( + INSSBuffer4 * This, + /* [in] */ DWORD dwBufferPropertyIndex, + /* [out] */ GUID *pguidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize); + + END_INTERFACE + } INSSBuffer4Vtbl; + + interface INSSBuffer4 + { + CONST_VTBL struct INSSBuffer4Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define INSSBuffer4_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define INSSBuffer4_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define INSSBuffer4_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define INSSBuffer4_GetLength(This,pdwLength) \ + (This)->lpVtbl -> GetLength(This,pdwLength) + +#define INSSBuffer4_SetLength(This,dwLength) \ + (This)->lpVtbl -> SetLength(This,dwLength) + +#define INSSBuffer4_GetMaxLength(This,pdwLength) \ + (This)->lpVtbl -> GetMaxLength(This,pdwLength) + +#define INSSBuffer4_GetBuffer(This,ppdwBuffer) \ + (This)->lpVtbl -> GetBuffer(This,ppdwBuffer) + +#define INSSBuffer4_GetBufferAndLength(This,ppdwBuffer,pdwLength) \ + (This)->lpVtbl -> GetBufferAndLength(This,ppdwBuffer,pdwLength) + + +#define INSSBuffer4_GetSampleProperties(This,cbProperties,pbProperties) \ + (This)->lpVtbl -> GetSampleProperties(This,cbProperties,pbProperties) + +#define INSSBuffer4_SetSampleProperties(This,cbProperties,pbProperties) \ + (This)->lpVtbl -> SetSampleProperties(This,cbProperties,pbProperties) + + +#define INSSBuffer4_SetProperty(This,guidBufferProperty,pvBufferProperty,dwBufferPropertySize) \ + (This)->lpVtbl -> SetProperty(This,guidBufferProperty,pvBufferProperty,dwBufferPropertySize) + +#define INSSBuffer4_GetProperty(This,guidBufferProperty,pvBufferProperty,pdwBufferPropertySize) \ + (This)->lpVtbl -> GetProperty(This,guidBufferProperty,pvBufferProperty,pdwBufferPropertySize) + + +#define INSSBuffer4_GetPropertyCount(This,pcBufferProperties) \ + (This)->lpVtbl -> GetPropertyCount(This,pcBufferProperties) + +#define INSSBuffer4_GetPropertyByIndex(This,dwBufferPropertyIndex,pguidBufferProperty,pvBufferProperty,pdwBufferPropertySize) \ + (This)->lpVtbl -> GetPropertyByIndex(This,dwBufferPropertyIndex,pguidBufferProperty,pvBufferProperty,pdwBufferPropertySize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE INSSBuffer4_GetPropertyCount_Proxy( + INSSBuffer4 * This, + /* [out] */ DWORD *pcBufferProperties); + + +void __RPC_STUB INSSBuffer4_GetPropertyCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE INSSBuffer4_GetPropertyByIndex_Proxy( + INSSBuffer4 * This, + /* [in] */ DWORD dwBufferPropertyIndex, + /* [out] */ GUID *pguidBufferProperty, + /* [out] */ void *pvBufferProperty, + /* [out][in] */ DWORD *pdwBufferPropertySize); + + +void __RPC_STUB INSSBuffer4_GetPropertyByIndex_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __INSSBuffer4_INTERFACE_DEFINED__ */ + + +#ifndef __IWMSBufferAllocator_INTERFACE_DEFINED__ +#define __IWMSBufferAllocator_INTERFACE_DEFINED__ + +/* interface IWMSBufferAllocator */ +/* [version][uuid][unique][object][local] */ + + +EXTERN_C const IID IID_IWMSBufferAllocator; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("61103CA4-2033-11d2-9EF1-006097D2D7CF") + IWMSBufferAllocator : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AllocateBuffer( + /* [in] */ DWORD dwMaxBufferSize, + /* [out] */ INSSBuffer **ppBuffer) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocatePageSizeBuffer( + /* [in] */ DWORD dwMaxBufferSize, + /* [out] */ INSSBuffer **ppBuffer) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMSBufferAllocatorVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMSBufferAllocator * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMSBufferAllocator * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMSBufferAllocator * This); + + HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )( + IWMSBufferAllocator * This, + /* [in] */ DWORD dwMaxBufferSize, + /* [out] */ INSSBuffer **ppBuffer); + + HRESULT ( STDMETHODCALLTYPE *AllocatePageSizeBuffer )( + IWMSBufferAllocator * This, + /* [in] */ DWORD dwMaxBufferSize, + /* [out] */ INSSBuffer **ppBuffer); + + END_INTERFACE + } IWMSBufferAllocatorVtbl; + + interface IWMSBufferAllocator + { + CONST_VTBL struct IWMSBufferAllocatorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMSBufferAllocator_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMSBufferAllocator_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMSBufferAllocator_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMSBufferAllocator_AllocateBuffer(This,dwMaxBufferSize,ppBuffer) \ + (This)->lpVtbl -> AllocateBuffer(This,dwMaxBufferSize,ppBuffer) + +#define IWMSBufferAllocator_AllocatePageSizeBuffer(This,dwMaxBufferSize,ppBuffer) \ + (This)->lpVtbl -> AllocatePageSizeBuffer(This,dwMaxBufferSize,ppBuffer) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMSBufferAllocator_AllocateBuffer_Proxy( + IWMSBufferAllocator * This, + /* [in] */ DWORD dwMaxBufferSize, + /* [out] */ INSSBuffer **ppBuffer); + + +void __RPC_STUB IWMSBufferAllocator_AllocateBuffer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSBufferAllocator_AllocatePageSizeBuffer_Proxy( + IWMSBufferAllocator * This, + /* [in] */ DWORD dwMaxBufferSize, + /* [out] */ INSSBuffer **ppBuffer); + + +void __RPC_STUB IWMSBufferAllocator_AllocatePageSizeBuffer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMSBufferAllocator_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/win32/lib/wmsdk/include/wmsdk.h b/win32/lib/wmsdk/include/wmsdk.h new file mode 100755 index 0000000..b967b62 --- /dev/null +++ b/win32/lib/wmsdk/include/wmsdk.h @@ -0,0 +1,12 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// +#pragma once +#include <windows.h> +#include "wmsdkidl.h" +#include "asferr.h" +#include "nserror.h" diff --git a/win32/lib/wmsdk/include/wmsdkidl.h b/win32/lib/wmsdk/include/wmsdkidl.h new file mode 100755 index 0000000..52ca73f --- /dev/null +++ b/win32/lib/wmsdk/include/wmsdkidl.h @@ -0,0 +1,19919 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 6.00.0361 */ +/* Compiler settings for wmsdkidl.idl: + Oicf, W1, Zp8, env=Win32 (32b run) + protocol : dce , ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + + +/* verify that the <rpcndr.h> version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 475 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of <rpcndr.h> +#endif // __RPCNDR_H_VERSION__ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __wmsdkidl_h__ +#define __wmsdkidl_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +/* Forward Declarations */ + +#ifndef __IWMMediaProps_FWD_DEFINED__ +#define __IWMMediaProps_FWD_DEFINED__ +typedef interface IWMMediaProps IWMMediaProps; +#endif /* __IWMMediaProps_FWD_DEFINED__ */ + + +#ifndef __IWMVideoMediaProps_FWD_DEFINED__ +#define __IWMVideoMediaProps_FWD_DEFINED__ +typedef interface IWMVideoMediaProps IWMVideoMediaProps; +#endif /* __IWMVideoMediaProps_FWD_DEFINED__ */ + + +#ifndef __IWMWriter_FWD_DEFINED__ +#define __IWMWriter_FWD_DEFINED__ +typedef interface IWMWriter IWMWriter; +#endif /* __IWMWriter_FWD_DEFINED__ */ + + +#ifndef __IWMDRMWriter_FWD_DEFINED__ +#define __IWMDRMWriter_FWD_DEFINED__ +typedef interface IWMDRMWriter IWMDRMWriter; +#endif /* __IWMDRMWriter_FWD_DEFINED__ */ + + +#ifndef __IWMInputMediaProps_FWD_DEFINED__ +#define __IWMInputMediaProps_FWD_DEFINED__ +typedef interface IWMInputMediaProps IWMInputMediaProps; +#endif /* __IWMInputMediaProps_FWD_DEFINED__ */ + + +#ifndef __IWMPropertyVault_FWD_DEFINED__ +#define __IWMPropertyVault_FWD_DEFINED__ +typedef interface IWMPropertyVault IWMPropertyVault; +#endif /* __IWMPropertyVault_FWD_DEFINED__ */ + + +#ifndef __IWMIStreamProps_FWD_DEFINED__ +#define __IWMIStreamProps_FWD_DEFINED__ +typedef interface IWMIStreamProps IWMIStreamProps; +#endif /* __IWMIStreamProps_FWD_DEFINED__ */ + + +#ifndef __IWMReader_FWD_DEFINED__ +#define __IWMReader_FWD_DEFINED__ +typedef interface IWMReader IWMReader; +#endif /* __IWMReader_FWD_DEFINED__ */ + + +#ifndef __IWMSyncReader_FWD_DEFINED__ +#define __IWMSyncReader_FWD_DEFINED__ +typedef interface IWMSyncReader IWMSyncReader; +#endif /* __IWMSyncReader_FWD_DEFINED__ */ + + +#ifndef __IWMSyncReader2_FWD_DEFINED__ +#define __IWMSyncReader2_FWD_DEFINED__ +typedef interface IWMSyncReader2 IWMSyncReader2; +#endif /* __IWMSyncReader2_FWD_DEFINED__ */ + + +#ifndef __IWMOutputMediaProps_FWD_DEFINED__ +#define __IWMOutputMediaProps_FWD_DEFINED__ +typedef interface IWMOutputMediaProps IWMOutputMediaProps; +#endif /* __IWMOutputMediaProps_FWD_DEFINED__ */ + + +#ifndef __IWMStatusCallback_FWD_DEFINED__ +#define __IWMStatusCallback_FWD_DEFINED__ +typedef interface IWMStatusCallback IWMStatusCallback; +#endif /* __IWMStatusCallback_FWD_DEFINED__ */ + + +#ifndef __IWMReaderCallback_FWD_DEFINED__ +#define __IWMReaderCallback_FWD_DEFINED__ +typedef interface IWMReaderCallback IWMReaderCallback; +#endif /* __IWMReaderCallback_FWD_DEFINED__ */ + + +#ifndef __IWMCredentialCallback_FWD_DEFINED__ +#define __IWMCredentialCallback_FWD_DEFINED__ +typedef interface IWMCredentialCallback IWMCredentialCallback; +#endif /* __IWMCredentialCallback_FWD_DEFINED__ */ + + +#ifndef __IWMMetadataEditor_FWD_DEFINED__ +#define __IWMMetadataEditor_FWD_DEFINED__ +typedef interface IWMMetadataEditor IWMMetadataEditor; +#endif /* __IWMMetadataEditor_FWD_DEFINED__ */ + + +#ifndef __IWMMetadataEditor2_FWD_DEFINED__ +#define __IWMMetadataEditor2_FWD_DEFINED__ +typedef interface IWMMetadataEditor2 IWMMetadataEditor2; +#endif /* __IWMMetadataEditor2_FWD_DEFINED__ */ + + +#ifndef __IWMDRMEditor_FWD_DEFINED__ +#define __IWMDRMEditor_FWD_DEFINED__ +typedef interface IWMDRMEditor IWMDRMEditor; +#endif /* __IWMDRMEditor_FWD_DEFINED__ */ + + +#ifndef __IWMHeaderInfo_FWD_DEFINED__ +#define __IWMHeaderInfo_FWD_DEFINED__ +typedef interface IWMHeaderInfo IWMHeaderInfo; +#endif /* __IWMHeaderInfo_FWD_DEFINED__ */ + + +#ifndef __IWMHeaderInfo2_FWD_DEFINED__ +#define __IWMHeaderInfo2_FWD_DEFINED__ +typedef interface IWMHeaderInfo2 IWMHeaderInfo2; +#endif /* __IWMHeaderInfo2_FWD_DEFINED__ */ + + +#ifndef __IWMHeaderInfo3_FWD_DEFINED__ +#define __IWMHeaderInfo3_FWD_DEFINED__ +typedef interface IWMHeaderInfo3 IWMHeaderInfo3; +#endif /* __IWMHeaderInfo3_FWD_DEFINED__ */ + + +#ifndef __IWMProfileManager_FWD_DEFINED__ +#define __IWMProfileManager_FWD_DEFINED__ +typedef interface IWMProfileManager IWMProfileManager; +#endif /* __IWMProfileManager_FWD_DEFINED__ */ + + +#ifndef __IWMProfileManager2_FWD_DEFINED__ +#define __IWMProfileManager2_FWD_DEFINED__ +typedef interface IWMProfileManager2 IWMProfileManager2; +#endif /* __IWMProfileManager2_FWD_DEFINED__ */ + + +#ifndef __IWMProfileManagerLanguage_FWD_DEFINED__ +#define __IWMProfileManagerLanguage_FWD_DEFINED__ +typedef interface IWMProfileManagerLanguage IWMProfileManagerLanguage; +#endif /* __IWMProfileManagerLanguage_FWD_DEFINED__ */ + + +#ifndef __IWMProfile_FWD_DEFINED__ +#define __IWMProfile_FWD_DEFINED__ +typedef interface IWMProfile IWMProfile; +#endif /* __IWMProfile_FWD_DEFINED__ */ + + +#ifndef __IWMProfile2_FWD_DEFINED__ +#define __IWMProfile2_FWD_DEFINED__ +typedef interface IWMProfile2 IWMProfile2; +#endif /* __IWMProfile2_FWD_DEFINED__ */ + + +#ifndef __IWMProfile3_FWD_DEFINED__ +#define __IWMProfile3_FWD_DEFINED__ +typedef interface IWMProfile3 IWMProfile3; +#endif /* __IWMProfile3_FWD_DEFINED__ */ + + +#ifndef __IWMStreamConfig_FWD_DEFINED__ +#define __IWMStreamConfig_FWD_DEFINED__ +typedef interface IWMStreamConfig IWMStreamConfig; +#endif /* __IWMStreamConfig_FWD_DEFINED__ */ + + +#ifndef __IWMStreamConfig2_FWD_DEFINED__ +#define __IWMStreamConfig2_FWD_DEFINED__ +typedef interface IWMStreamConfig2 IWMStreamConfig2; +#endif /* __IWMStreamConfig2_FWD_DEFINED__ */ + + +#ifndef __IWMStreamConfig3_FWD_DEFINED__ +#define __IWMStreamConfig3_FWD_DEFINED__ +typedef interface IWMStreamConfig3 IWMStreamConfig3; +#endif /* __IWMStreamConfig3_FWD_DEFINED__ */ + + +#ifndef __IWMPacketSize_FWD_DEFINED__ +#define __IWMPacketSize_FWD_DEFINED__ +typedef interface IWMPacketSize IWMPacketSize; +#endif /* __IWMPacketSize_FWD_DEFINED__ */ + + +#ifndef __IWMPacketSize2_FWD_DEFINED__ +#define __IWMPacketSize2_FWD_DEFINED__ +typedef interface IWMPacketSize2 IWMPacketSize2; +#endif /* __IWMPacketSize2_FWD_DEFINED__ */ + + +#ifndef __IWMStreamList_FWD_DEFINED__ +#define __IWMStreamList_FWD_DEFINED__ +typedef interface IWMStreamList IWMStreamList; +#endif /* __IWMStreamList_FWD_DEFINED__ */ + + +#ifndef __IWMMutualExclusion_FWD_DEFINED__ +#define __IWMMutualExclusion_FWD_DEFINED__ +typedef interface IWMMutualExclusion IWMMutualExclusion; +#endif /* __IWMMutualExclusion_FWD_DEFINED__ */ + + +#ifndef __IWMMutualExclusion2_FWD_DEFINED__ +#define __IWMMutualExclusion2_FWD_DEFINED__ +typedef interface IWMMutualExclusion2 IWMMutualExclusion2; +#endif /* __IWMMutualExclusion2_FWD_DEFINED__ */ + + +#ifndef __IWMBandwidthSharing_FWD_DEFINED__ +#define __IWMBandwidthSharing_FWD_DEFINED__ +typedef interface IWMBandwidthSharing IWMBandwidthSharing; +#endif /* __IWMBandwidthSharing_FWD_DEFINED__ */ + + +#ifndef __IWMStreamPrioritization_FWD_DEFINED__ +#define __IWMStreamPrioritization_FWD_DEFINED__ +typedef interface IWMStreamPrioritization IWMStreamPrioritization; +#endif /* __IWMStreamPrioritization_FWD_DEFINED__ */ + + +#ifndef __IWMWriterAdvanced_FWD_DEFINED__ +#define __IWMWriterAdvanced_FWD_DEFINED__ +typedef interface IWMWriterAdvanced IWMWriterAdvanced; +#endif /* __IWMWriterAdvanced_FWD_DEFINED__ */ + + +#ifndef __IWMWriterAdvanced2_FWD_DEFINED__ +#define __IWMWriterAdvanced2_FWD_DEFINED__ +typedef interface IWMWriterAdvanced2 IWMWriterAdvanced2; +#endif /* __IWMWriterAdvanced2_FWD_DEFINED__ */ + + +#ifndef __IWMWriterAdvanced3_FWD_DEFINED__ +#define __IWMWriterAdvanced3_FWD_DEFINED__ +typedef interface IWMWriterAdvanced3 IWMWriterAdvanced3; +#endif /* __IWMWriterAdvanced3_FWD_DEFINED__ */ + + +#ifndef __IWMWriterPreprocess_FWD_DEFINED__ +#define __IWMWriterPreprocess_FWD_DEFINED__ +typedef interface IWMWriterPreprocess IWMWriterPreprocess; +#endif /* __IWMWriterPreprocess_FWD_DEFINED__ */ + + +#ifndef __IWMWriterPostViewCallback_FWD_DEFINED__ +#define __IWMWriterPostViewCallback_FWD_DEFINED__ +typedef interface IWMWriterPostViewCallback IWMWriterPostViewCallback; +#endif /* __IWMWriterPostViewCallback_FWD_DEFINED__ */ + + +#ifndef __IWMWriterPostView_FWD_DEFINED__ +#define __IWMWriterPostView_FWD_DEFINED__ +typedef interface IWMWriterPostView IWMWriterPostView; +#endif /* __IWMWriterPostView_FWD_DEFINED__ */ + + +#ifndef __IWMWriterSink_FWD_DEFINED__ +#define __IWMWriterSink_FWD_DEFINED__ +typedef interface IWMWriterSink IWMWriterSink; +#endif /* __IWMWriterSink_FWD_DEFINED__ */ + + +#ifndef __IWMRegisterCallback_FWD_DEFINED__ +#define __IWMRegisterCallback_FWD_DEFINED__ +typedef interface IWMRegisterCallback IWMRegisterCallback; +#endif /* __IWMRegisterCallback_FWD_DEFINED__ */ + + +#ifndef __IWMWriterFileSink_FWD_DEFINED__ +#define __IWMWriterFileSink_FWD_DEFINED__ +typedef interface IWMWriterFileSink IWMWriterFileSink; +#endif /* __IWMWriterFileSink_FWD_DEFINED__ */ + + +#ifndef __IWMWriterFileSink2_FWD_DEFINED__ +#define __IWMWriterFileSink2_FWD_DEFINED__ +typedef interface IWMWriterFileSink2 IWMWriterFileSink2; +#endif /* __IWMWriterFileSink2_FWD_DEFINED__ */ + + +#ifndef __IWMWriterFileSink3_FWD_DEFINED__ +#define __IWMWriterFileSink3_FWD_DEFINED__ +typedef interface IWMWriterFileSink3 IWMWriterFileSink3; +#endif /* __IWMWriterFileSink3_FWD_DEFINED__ */ + + +#ifndef __IWMWriterNetworkSink_FWD_DEFINED__ +#define __IWMWriterNetworkSink_FWD_DEFINED__ +typedef interface IWMWriterNetworkSink IWMWriterNetworkSink; +#endif /* __IWMWriterNetworkSink_FWD_DEFINED__ */ + + +#ifndef __IWMClientConnections_FWD_DEFINED__ +#define __IWMClientConnections_FWD_DEFINED__ +typedef interface IWMClientConnections IWMClientConnections; +#endif /* __IWMClientConnections_FWD_DEFINED__ */ + + +#ifndef __IWMClientConnections2_FWD_DEFINED__ +#define __IWMClientConnections2_FWD_DEFINED__ +typedef interface IWMClientConnections2 IWMClientConnections2; +#endif /* __IWMClientConnections2_FWD_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced_FWD_DEFINED__ +#define __IWMReaderAdvanced_FWD_DEFINED__ +typedef interface IWMReaderAdvanced IWMReaderAdvanced; +#endif /* __IWMReaderAdvanced_FWD_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced2_FWD_DEFINED__ +#define __IWMReaderAdvanced2_FWD_DEFINED__ +typedef interface IWMReaderAdvanced2 IWMReaderAdvanced2; +#endif /* __IWMReaderAdvanced2_FWD_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced3_FWD_DEFINED__ +#define __IWMReaderAdvanced3_FWD_DEFINED__ +typedef interface IWMReaderAdvanced3 IWMReaderAdvanced3; +#endif /* __IWMReaderAdvanced3_FWD_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced4_FWD_DEFINED__ +#define __IWMReaderAdvanced4_FWD_DEFINED__ +typedef interface IWMReaderAdvanced4 IWMReaderAdvanced4; +#endif /* __IWMReaderAdvanced4_FWD_DEFINED__ */ + + +#ifndef __IWMReaderAllocatorEx_FWD_DEFINED__ +#define __IWMReaderAllocatorEx_FWD_DEFINED__ +typedef interface IWMReaderAllocatorEx IWMReaderAllocatorEx; +#endif /* __IWMReaderAllocatorEx_FWD_DEFINED__ */ + + +#ifndef __IWMReaderTypeNegotiation_FWD_DEFINED__ +#define __IWMReaderTypeNegotiation_FWD_DEFINED__ +typedef interface IWMReaderTypeNegotiation IWMReaderTypeNegotiation; +#endif /* __IWMReaderTypeNegotiation_FWD_DEFINED__ */ + + +#ifndef __IWMReaderCallbackAdvanced_FWD_DEFINED__ +#define __IWMReaderCallbackAdvanced_FWD_DEFINED__ +typedef interface IWMReaderCallbackAdvanced IWMReaderCallbackAdvanced; +#endif /* __IWMReaderCallbackAdvanced_FWD_DEFINED__ */ + + +#ifndef __IWMDRMReader_FWD_DEFINED__ +#define __IWMDRMReader_FWD_DEFINED__ +typedef interface IWMDRMReader IWMDRMReader; +#endif /* __IWMDRMReader_FWD_DEFINED__ */ + + +#ifndef __IWMReaderNetworkConfig_FWD_DEFINED__ +#define __IWMReaderNetworkConfig_FWD_DEFINED__ +typedef interface IWMReaderNetworkConfig IWMReaderNetworkConfig; +#endif /* __IWMReaderNetworkConfig_FWD_DEFINED__ */ + + +#ifndef __IWMReaderNetworkConfig2_FWD_DEFINED__ +#define __IWMReaderNetworkConfig2_FWD_DEFINED__ +typedef interface IWMReaderNetworkConfig2 IWMReaderNetworkConfig2; +#endif /* __IWMReaderNetworkConfig2_FWD_DEFINED__ */ + + +#ifndef __IWMReaderStreamClock_FWD_DEFINED__ +#define __IWMReaderStreamClock_FWD_DEFINED__ +typedef interface IWMReaderStreamClock IWMReaderStreamClock; +#endif /* __IWMReaderStreamClock_FWD_DEFINED__ */ + + +#ifndef __IWMIndexer_FWD_DEFINED__ +#define __IWMIndexer_FWD_DEFINED__ +typedef interface IWMIndexer IWMIndexer; +#endif /* __IWMIndexer_FWD_DEFINED__ */ + + +#ifndef __IWMIndexer2_FWD_DEFINED__ +#define __IWMIndexer2_FWD_DEFINED__ +typedef interface IWMIndexer2 IWMIndexer2; +#endif /* __IWMIndexer2_FWD_DEFINED__ */ + + +#ifndef __IWMLicenseBackup_FWD_DEFINED__ +#define __IWMLicenseBackup_FWD_DEFINED__ +typedef interface IWMLicenseBackup IWMLicenseBackup; +#endif /* __IWMLicenseBackup_FWD_DEFINED__ */ + + +#ifndef __IWMLicenseRestore_FWD_DEFINED__ +#define __IWMLicenseRestore_FWD_DEFINED__ +typedef interface IWMLicenseRestore IWMLicenseRestore; +#endif /* __IWMLicenseRestore_FWD_DEFINED__ */ + + +#ifndef __IWMBackupRestoreProps_FWD_DEFINED__ +#define __IWMBackupRestoreProps_FWD_DEFINED__ +typedef interface IWMBackupRestoreProps IWMBackupRestoreProps; +#endif /* __IWMBackupRestoreProps_FWD_DEFINED__ */ + + +#ifndef __IWMCodecInfo_FWD_DEFINED__ +#define __IWMCodecInfo_FWD_DEFINED__ +typedef interface IWMCodecInfo IWMCodecInfo; +#endif /* __IWMCodecInfo_FWD_DEFINED__ */ + + +#ifndef __IWMCodecInfo2_FWD_DEFINED__ +#define __IWMCodecInfo2_FWD_DEFINED__ +typedef interface IWMCodecInfo2 IWMCodecInfo2; +#endif /* __IWMCodecInfo2_FWD_DEFINED__ */ + + +#ifndef __IWMCodecInfo3_FWD_DEFINED__ +#define __IWMCodecInfo3_FWD_DEFINED__ +typedef interface IWMCodecInfo3 IWMCodecInfo3; +#endif /* __IWMCodecInfo3_FWD_DEFINED__ */ + + +#ifndef __IWMLanguageList_FWD_DEFINED__ +#define __IWMLanguageList_FWD_DEFINED__ +typedef interface IWMLanguageList IWMLanguageList; +#endif /* __IWMLanguageList_FWD_DEFINED__ */ + + +#ifndef __IWMWriterPushSink_FWD_DEFINED__ +#define __IWMWriterPushSink_FWD_DEFINED__ +typedef interface IWMWriterPushSink IWMWriterPushSink; +#endif /* __IWMWriterPushSink_FWD_DEFINED__ */ + + +#ifndef __IWMWatermarkInfo_FWD_DEFINED__ +#define __IWMWatermarkInfo_FWD_DEFINED__ +typedef interface IWMWatermarkInfo IWMWatermarkInfo; +#endif /* __IWMWatermarkInfo_FWD_DEFINED__ */ + + +#ifndef __IWMReaderAccelerator_FWD_DEFINED__ +#define __IWMReaderAccelerator_FWD_DEFINED__ +typedef interface IWMReaderAccelerator IWMReaderAccelerator; +#endif /* __IWMReaderAccelerator_FWD_DEFINED__ */ + + +#ifndef __IWMReaderTimecode_FWD_DEFINED__ +#define __IWMReaderTimecode_FWD_DEFINED__ +typedef interface IWMReaderTimecode IWMReaderTimecode; +#endif /* __IWMReaderTimecode_FWD_DEFINED__ */ + + +#ifndef __IWMAddressAccess_FWD_DEFINED__ +#define __IWMAddressAccess_FWD_DEFINED__ +typedef interface IWMAddressAccess IWMAddressAccess; +#endif /* __IWMAddressAccess_FWD_DEFINED__ */ + + +#ifndef __IWMAddressAccess2_FWD_DEFINED__ +#define __IWMAddressAccess2_FWD_DEFINED__ +typedef interface IWMAddressAccess2 IWMAddressAccess2; +#endif /* __IWMAddressAccess2_FWD_DEFINED__ */ + + +#ifndef __IWMImageInfo_FWD_DEFINED__ +#define __IWMImageInfo_FWD_DEFINED__ +typedef interface IWMImageInfo IWMImageInfo; +#endif /* __IWMImageInfo_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oaidl.h" +#include "wmsbuffer.h" +#include "drmexternals.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +void * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void * ); + +/* interface __MIDL_itf_wmsdkidl_0000 */ +/* [local] */ + +//========================================================================= +// +// Microsoft Windows Media Technologies +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//========================================================================= +typedef unsigned __int64 QWORD; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////// +// +// These are the special case attributes that give information +// about the Windows Media file. +// +static const DWORD g_dwWMSpecialAttributes = 20; +static const WCHAR g_wszWMDuration[] =L"Duration"; +static const WCHAR g_wszWMBitrate[] =L"Bitrate"; +static const WCHAR g_wszWMSeekable[] =L"Seekable"; +static const WCHAR g_wszWMStridable[] =L"Stridable"; +static const WCHAR g_wszWMBroadcast[] =L"Broadcast"; +static const WCHAR g_wszWMProtected[] =L"Is_Protected"; +static const WCHAR g_wszWMTrusted[] =L"Is_Trusted"; +static const WCHAR g_wszWMSignature_Name[] =L"Signature_Name"; +static const WCHAR g_wszWMHasAudio[] =L"HasAudio"; +static const WCHAR g_wszWMHasImage[] =L"HasImage"; +static const WCHAR g_wszWMHasScript[] =L"HasScript"; +static const WCHAR g_wszWMHasVideo[] =L"HasVideo"; +static const WCHAR g_wszWMCurrentBitrate[] =L"CurrentBitrate"; +static const WCHAR g_wszWMOptimalBitrate[] =L"OptimalBitrate"; +static const WCHAR g_wszWMHasAttachedImages[] =L"HasAttachedImages"; +static const WCHAR g_wszWMSkipBackward[] =L"Can_Skip_Backward"; +static const WCHAR g_wszWMSkipForward[] =L"Can_Skip_Forward"; +static const WCHAR g_wszWMNumberOfFrames[] =L"NumberOfFrames"; +static const WCHAR g_wszWMFileSize[] =L"FileSize"; +static const WCHAR g_wszWMHasArbitraryDataStream[] =L"HasArbitraryDataStream"; +static const WCHAR g_wszWMHasFileTransferStream[] =L"HasFileTransferStream"; +static const WCHAR g_wszWMContainerFormat[] =L"WM/ContainerFormat"; + +//////////////////////////////////////////////////////////////// +// +// The content description object supports 5 basic attributes. +// +static const DWORD g_dwWMContentAttributes = 5; +static const WCHAR g_wszWMTitle[] =L"Title"; +static const WCHAR g_wszWMAuthor[] =L"Author"; +static const WCHAR g_wszWMDescription[] =L"Description"; +static const WCHAR g_wszWMRating[] =L"Rating"; +static const WCHAR g_wszWMCopyright[] =L"Copyright"; + +//////////////////////////////////////////////////////////////// +// +// These attributes are used to configure and query DRM settings in the reader and writer. +// +static const WCHAR g_wszWMUse_DRM[] = L"Use_DRM"; +static const WCHAR g_wszWMDRM_Flags[] = L"DRM_Flags"; +static const WCHAR g_wszWMDRM_Level[] = L"DRM_Level"; +static const WCHAR g_wszWMUse_Advanced_DRM[] = L"Use_Advanced_DRM"; +static const WCHAR g_wszWMDRM_KeySeed[] = L"DRM_KeySeed"; +static const WCHAR g_wszWMDRM_KeyID[] = L"DRM_KeyID"; +static const WCHAR g_wszWMDRM_ContentID[] = L"DRM_ContentID"; +static const WCHAR g_wszWMDRM_IndividualizedVersion[] = L"DRM_IndividualizedVersion"; +static const WCHAR g_wszWMDRM_LicenseAcqURL[] = L"DRM_LicenseAcqURL"; +static const WCHAR g_wszWMDRM_V1LicenseAcqURL[] = L"DRM_V1LicenseAcqURL"; +static const WCHAR g_wszWMDRM_HeaderSignPrivKey[] = L"DRM_HeaderSignPrivKey"; +static const WCHAR g_wszWMDRM_LASignaturePrivKey[] = L"DRM_LASignaturePrivKey"; +static const WCHAR g_wszWMDRM_LASignatureCert[] = L"DRM_LASignatureCert"; +static const WCHAR g_wszWMDRM_LASignatureLicSrvCert[] = L"DRM_LASignatureLicSrvCert"; +static const WCHAR g_wszWMDRM_LASignatureRootCert[] = L"DRM_LASignatureRootCert"; + +//////////////////////////////////////////////////////////////// +// +// These are the additional attributes defined in the WM attribute +// namespace that give information about the content. +// +static const WCHAR g_wszWMAlbumTitle[] =L"WM/AlbumTitle"; +static const WCHAR g_wszWMTrack[] =L"WM/Track"; +static const WCHAR g_wszWMPromotionURL[] =L"WM/PromotionURL"; +static const WCHAR g_wszWMAlbumCoverURL[] =L"WM/AlbumCoverURL"; +static const WCHAR g_wszWMGenre[] =L"WM/Genre"; +static const WCHAR g_wszWMYear[] =L"WM/Year"; +static const WCHAR g_wszWMGenreID[] =L"WM/GenreID"; +static const WCHAR g_wszWMMCDI[] =L"WM/MCDI"; +static const WCHAR g_wszWMComposer[] =L"WM/Composer"; +static const WCHAR g_wszWMLyrics[] =L"WM/Lyrics"; +static const WCHAR g_wszWMTrackNumber[] =L"WM/TrackNumber"; +static const WCHAR g_wszWMToolName[] =L"WM/ToolName"; +static const WCHAR g_wszWMToolVersion[] =L"WM/ToolVersion"; +static const WCHAR g_wszWMIsVBR[] =L"IsVBR"; +static const WCHAR g_wszWMAlbumArtist[] =L"WM/AlbumArtist"; + +//////////////////////////////////////////////////////////////// +// +// These optional attributes may be used to give information +// about the branding of the content. +// +static const WCHAR g_wszWMBannerImageType[] =L"BannerImageType"; +static const WCHAR g_wszWMBannerImageData[] =L"BannerImageData"; +static const WCHAR g_wszWMBannerImageURL[] =L"BannerImageURL"; +static const WCHAR g_wszWMCopyrightURL[] =L"CopyrightURL"; +//////////////////////////////////////////////////////////////// +// +// Optional attributes, used to give information +// about video stream properties. +// +static const WCHAR g_wszWMAspectRatioX[] =L"AspectRatioX"; +static const WCHAR g_wszWMAspectRatioY[] =L"AspectRatioY"; +//////////////////////////////////////////////////////////////// +// +// Optional attributes, used to give information +// about the overall streaming properties of VBR files. +// This attribute takes the format: +// WORD wReserved (must be 0) +// WM_LEAKY_BUCKET_PAIR pair1 +// WM_LEAKY_BUCKET_PAIR pair2 +// ... +// +static const WCHAR g_wszASFLeakyBucketPairs[] =L"ASFLeakyBucketPairs"; +//////////////////////////////////////////////////////////////// +// +// The NSC file supports the following attributes. +// +static const DWORD g_dwWMNSCAttributes = 5; +static const WCHAR g_wszWMNSCName[] =L"NSC_Name"; +static const WCHAR g_wszWMNSCAddress[] =L"NSC_Address"; +static const WCHAR g_wszWMNSCPhone[] =L"NSC_Phone"; +static const WCHAR g_wszWMNSCEmail[] =L"NSC_Email"; +static const WCHAR g_wszWMNSCDescription[] =L"NSC_Description"; + +//////////////////////////////////////////////////////////////// +// +// Attributes introduced in V9 +// +static const WCHAR g_wszWMWriter[] =L"WM/Writer"; +static const WCHAR g_wszWMConductor[] =L"WM/Conductor"; +static const WCHAR g_wszWMProducer[] =L"WM/Producer"; +static const WCHAR g_wszWMDirector[] =L"WM/Director"; +static const WCHAR g_wszWMContentGroupDescription[] =L"WM/ContentGroupDescription"; +static const WCHAR g_wszWMSubTitle[] =L"WM/SubTitle"; +static const WCHAR g_wszWMPartOfSet[] =L"WM/PartOfSet"; +static const WCHAR g_wszWMProtectionType[] =L"WM/ProtectionType"; +static const WCHAR g_wszWMVideoHeight[] =L"WM/VideoHeight"; +static const WCHAR g_wszWMVideoWidth[] =L"WM/VideoWidth"; +static const WCHAR g_wszWMVideoFrameRate[] =L"WM/VideoFrameRate"; +static const WCHAR g_wszWMMediaClassPrimaryID[] =L"WM/MediaClassPrimaryID"; +static const WCHAR g_wszWMMediaClassSecondaryID[] =L"WM/MediaClassSecondaryID"; +static const WCHAR g_wszWMPeriod[] = L"WM/Period"; +static const WCHAR g_wszWMCategory[] = L"WM/Category"; +static const WCHAR g_wszWMPicture[] =L"WM/Picture"; +static const WCHAR g_wszWMLyrics_Synchronised[] =L"WM/Lyrics_Synchronised"; +static const WCHAR g_wszWMOriginalLyricist[] =L"WM/OriginalLyricist"; +static const WCHAR g_wszWMOriginalArtist[] =L"WM/OriginalArtist"; +static const WCHAR g_wszWMOriginalAlbumTitle[] =L"WM/OriginalAlbumTitle"; +static const WCHAR g_wszWMOriginalReleaseYear[] =L"WM/OriginalReleaseYear"; +static const WCHAR g_wszWMOriginalFilename[] =L"WM/OriginalFilename"; +static const WCHAR g_wszWMPublisher[] =L"WM/Publisher"; +static const WCHAR g_wszWMEncodedBy[] =L"WM/EncodedBy"; +static const WCHAR g_wszWMEncodingSettings[] =L"WM/EncodingSettings"; +static const WCHAR g_wszWMEncodingTime[] =L"WM/EncodingTime"; +static const WCHAR g_wszWMAuthorURL[] =L"WM/AuthorURL"; +static const WCHAR g_wszWMUserWebURL[] =L"WM/UserWebURL"; +static const WCHAR g_wszWMAudioFileURL[] =L"WM/AudioFileURL"; +static const WCHAR g_wszWMAudioSourceURL[] =L"WM/AudioSourceURL"; +static const WCHAR g_wszWMLanguage[] =L"WM/Language"; +static const WCHAR g_wszWMParentalRating[] =L"WM/ParentalRating"; +static const WCHAR g_wszWMBeatsPerMinute[] =L"WM/BeatsPerMinute"; +static const WCHAR g_wszWMInitialKey[] =L"WM/InitialKey"; +static const WCHAR g_wszWMMood[] =L"WM/Mood"; +static const WCHAR g_wszWMText[] =L"WM/Text"; +static const WCHAR g_wszWMDVDID[] =L"WM/DVDID"; +static const WCHAR g_wszWMWMContentID[] =L"WM/WMContentID"; +static const WCHAR g_wszWMWMCollectionID[] =L"WM/WMCollectionID"; +static const WCHAR g_wszWMWMCollectionGroupID[] =L"WM/WMCollectionGroupID"; +static const WCHAR g_wszWMUniqueFileIdentifier[] =L"WM/UniqueFileIdentifier"; +static const WCHAR g_wszWMModifiedBy[] =L"WM/ModifiedBy"; +static const WCHAR g_wszWMRadioStationName[] =L"WM/RadioStationName"; +static const WCHAR g_wszWMRadioStationOwner[] =L"WM/RadioStationOwner"; +static const WCHAR g_wszWMPlaylistDelay[] =L"WM/PlaylistDelay"; +static const WCHAR g_wszWMCodec[] =L"WM/Codec"; +static const WCHAR g_wszWMDRM[] =L"WM/DRM"; +static const WCHAR g_wszWMISRC[] =L"WM/ISRC"; +static const WCHAR g_wszWMProvider[] =L"WM/Provider"; +static const WCHAR g_wszWMProviderRating[] =L"WM/ProviderRating"; +static const WCHAR g_wszWMProviderStyle[] =L"WM/ProviderStyle"; +static const WCHAR g_wszWMContentDistributor[] =L"WM/ContentDistributor"; +static const WCHAR g_wszWMSubscriptionContentID[] =L"WM/SubscriptionContentID"; +static const WCHAR g_wszWMWMADRCPeakReference[] =L"WM/WMADRCPeakReference"; +static const WCHAR g_wszWMWMADRCPeakTarget[] =L"WM/WMADRCPeakTarget"; +static const WCHAR g_wszWMWMADRCAverageReference[] =L"WM/WMADRCAverageReference"; +static const WCHAR g_wszWMWMADRCAverageTarget[] =L"WM/WMADRCAverageTarget"; +//////////////////////////////////////////////////////////////// +// +// These are setting names for use in Get/SetOutputSetting +// +static const WCHAR g_wszEarlyDataDelivery[] =L"EarlyDataDelivery"; +static const WCHAR g_wszJustInTimeDecode[] =L"JustInTimeDecode"; +static const WCHAR g_wszSingleOutputBuffer[] =L"SingleOutputBuffer"; +static const WCHAR g_wszSoftwareScaling[] =L"SoftwareScaling"; +static const WCHAR g_wszDeliverOnReceive[] =L"DeliverOnReceive"; +static const WCHAR g_wszScrambledAudio[] =L"ScrambledAudio"; +static const WCHAR g_wszDedicatedDeliveryThread[] =L"DedicatedDeliveryThread"; +static const WCHAR g_wszEnableDiscreteOutput[] = L"EnableDiscreteOutput"; +static const WCHAR g_wszSpeakerConfig[] = L"SpeakerConfig"; +static const WCHAR g_wszDynamicRangeControl[] = L"DynamicRangeControl"; +static const WCHAR g_wszAllowInterlacedOutput[] = L"AllowInterlacedOutput"; +static const WCHAR g_wszVideoSampleDurations[] =L"VideoSampleDurations"; +static const WCHAR g_wszStreamLanguage[] =L"StreamLanguage"; + +//////////////////////////////////////////////////////////////// +// +// These are setting names for use in Get/SetInputSetting +// +static const WCHAR g_wszDeinterlaceMode[] =L"DeinterlaceMode"; +static const WCHAR g_wszInitialPatternForInverseTelecine[] =L"InitialPatternForInverseTelecine"; +static const WCHAR g_wszJPEGCompressionQuality[] =L"JPEGCompressionQuality"; +static const WCHAR g_wszWatermarkCLSID[] =L"WatermarkCLSID"; +static const WCHAR g_wszWatermarkConfig[] =L"WatermarkConfig"; +static const WCHAR g_wszInterlacedCoding[] =L"InterlacedCoding"; +static const WCHAR g_wszFixedFrameRate[] =L"FixedFrameRate"; + +//////////////////////////////////////////////////////////////// +// +// All known IWMPropertyVault property names +// +// g_wszOriginalSourceFormatTag is obsolete and has been superceded by g_wszOriginalWaveFormat +static const WCHAR g_wszOriginalSourceFormatTag[] =L"_SOURCEFORMATTAG"; +static const WCHAR g_wszOriginalWaveFormat[] =L"_ORIGINALWAVEFORMAT"; +static const WCHAR g_wszEDL[] =L"_EDL"; +static const WCHAR g_wszComplexity[] =L"_COMPLEXITYEX"; +static const WCHAR g_wszDecoderComplexityRequested[] =L"_DECODERCOMPLEXITYPROFILE"; + +//////////////////////////////////////////////////////////////// +// +// All known IWMIStreamProps property names +// +static const WCHAR g_wszReloadIndexOnSeek[] =L"ReloadIndexOnSeek"; +static const WCHAR g_wszStreamNumIndexObjects[] =L"StreamNumIndexObjects"; +static const WCHAR g_wszFailSeekOnError[] =L"FailSeekOnError"; +static const WCHAR g_wszPermitSeeksBeyondEndOfStream[] =L"PermitSeeksBeyondEndOfStream"; +static const WCHAR g_wszUsePacketAtSeekPoint[] =L"UsePacketAtSeekPoint"; +static const WCHAR g_wszSourceBufferTime[] =L"SourceBufferTime"; +static const WCHAR g_wszSourceMaxBytesAtOnce[] =L"SourceMaxBytesAtOnce"; + +//////////////////////////////////////////////////////////////// +// +// VBR encoding settings +// +static const WCHAR g_wszVBREnabled[] =L"_VBRENABLED"; +static const WCHAR g_wszVBRQuality[] =L"_VBRQUALITY"; +static const WCHAR g_wszVBRBitrateMax[] =L"_RMAX"; +static const WCHAR g_wszVBRBufferWindowMax[] =L"_BMAX"; + +//////////////////////////////////////////////////////////////// +// +// VBR Video settings +// +static const WCHAR g_wszVBRPeak[] = L"VBR Peak"; +static const WCHAR g_wszBufferAverage[] = L"Buffer Average"; + +//////////////////////////////////////////////////////////////// +// +// Codec encoding complexity settings +// +// g_wszComplexity should be used to set desired encoding complexity on the +// stream's IWMPropertyVault (see above for definition) +// The below settings can be queried from IWMCodecInfo3::GetCodecProp() +// +static const WCHAR g_wszComplexityMax[] =L"_COMPLEXITYEXMAX"; +static const WCHAR g_wszComplexityOffline[] =L"_COMPLEXITYEXOFFLINE"; +static const WCHAR g_wszComplexityLive[] =L"_COMPLEXITYEXLIVE"; +static const WCHAR g_wszIsVBRSupported[] =L"_ISVBRSUPPORTED"; +//////////////////////////////////////////////////////////////// +// +// Codec enumeration settings +// +// g_wszVBREnabled can be used as a codec enumeration setting (see above for definition) +static const WCHAR g_wszNumPasses[] = L"_PASSESUSED"; + +//////////////////////////////////////////////////////////////// +// +// These are WMA Voice V9 attribute names and values +// +static const WCHAR g_wszMusicSpeechClassMode[] = L"MusicSpeechClassMode"; +static const WCHAR g_wszMusicClassMode[] = L"MusicClassMode"; +static const WCHAR g_wszSpeechClassMode[] = L"SpeechClassMode"; +static const WCHAR g_wszMixedClassMode[] = L"MixedClassMode"; + +//////////////////////////////////////////////////////////////// +// +// The WMA Voice V9 supports the following format property. +// +static const WCHAR g_wszSpeechCaps[] = L"SpeechFormatCap"; + +//////////////////////////////////////////////////////////////// +// +// Multi-channel WMA properties +// +static const WCHAR g_wszPeakValue[] = L"PeakValue"; +static const WCHAR g_wszAverageLevel[] = L"AverageLevel"; +static const WCHAR g_wszFold6To2Channels3[] = L"Fold6To2Channels3"; +static const WCHAR g_wszFoldToChannelsTemplate[] = L"Fold%luTo%luChannels%lu"; + +//////////////////////////////////////////////////////////////// +// +// Complexity profile description strings +// +static const WCHAR g_wszDeviceConformanceTemplate[] = L"DeviceConformanceTemplate"; + +//////////////////////////////////////////////////////////////// +// +// Frame interpolation on video decode +// +static const WCHAR g_wszEnableFrameInterpolation[] =L"EnableFrameInterpolation"; + +//////////////////////////////////////////////////////////////// +// +// Needs previous sample for Delta frame on video decode +// +static const WCHAR g_wszNeedsPreviousSample[] =L"NeedsPreviousSample"; + +//////////////////////////////////////////////////////////////// +// +// Flags that can be passed into the Start method of IWMReader +// +#define WM_START_CURRENTPOSITION ( ( QWORD )-1 ) + +#define WM_BACKUP_OVERWRITE ((DWORD) 0x00000001) +#define WM_RESTORE_INDIVIDUALIZE ((DWORD) 0x00000002) +#define WAVE_FORMAT_DRM 0x0009 + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0001 + { WEBSTREAM_SAMPLE_TYPE_FILE = 0x1, + WEBSTREAM_SAMPLE_TYPE_RENDER = 0x2 + } ; + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0002 + { WM_SF_CLEANPOINT = 0x1, + WM_SF_DISCONTINUITY = 0x2, + WM_SF_DATALOSS = 0x4 + } ; + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0003 + { WM_SFEX_NOTASYNCPOINT = 0x2, + WM_SFEX_DATALOSS = 0x4 + } ; +typedef +enum WMT_STATUS + { WMT_ERROR = 0, + WMT_OPENED = 1, + WMT_BUFFERING_START = 2, + WMT_BUFFERING_STOP = 3, + WMT_EOF = 4, + WMT_END_OF_FILE = 4, + WMT_END_OF_SEGMENT = 5, + WMT_END_OF_STREAMING = 6, + WMT_LOCATING = 7, + WMT_CONNECTING = 8, + WMT_NO_RIGHTS = 9, + WMT_MISSING_CODEC = 10, + WMT_STARTED = 11, + WMT_STOPPED = 12, + WMT_CLOSED = 13, + WMT_STRIDING = 14, + WMT_TIMER = 15, + WMT_INDEX_PROGRESS = 16, + WMT_SAVEAS_START = 17, + WMT_SAVEAS_STOP = 18, + WMT_NEW_SOURCEFLAGS = 19, + WMT_NEW_METADATA = 20, + WMT_BACKUPRESTORE_BEGIN = 21, + WMT_SOURCE_SWITCH = 22, + WMT_ACQUIRE_LICENSE = 23, + WMT_INDIVIDUALIZE = 24, + WMT_NEEDS_INDIVIDUALIZATION = 25, + WMT_NO_RIGHTS_EX = 26, + WMT_BACKUPRESTORE_END = 27, + WMT_BACKUPRESTORE_CONNECTING = 28, + WMT_BACKUPRESTORE_DISCONNECTING = 29, + WMT_ERROR_WITHURL = 30, + WMT_RESTRICTED_LICENSE = 31, + WMT_CLIENT_CONNECT = 32, + WMT_CLIENT_DISCONNECT = 33, + WMT_NATIVE_OUTPUT_PROPS_CHANGED = 34, + WMT_RECONNECT_START = 35, + WMT_RECONNECT_END = 36, + WMT_CLIENT_CONNECT_EX = 37, + WMT_CLIENT_DISCONNECT_EX = 38, + WMT_SET_FEC_SPAN = 39, + WMT_PREROLL_READY = 40, + WMT_PREROLL_COMPLETE = 41, + WMT_CLIENT_PROPERTIES = 42, + WMT_LICENSEURL_SIGNATURE_STATE = 43 + } WMT_STATUS; + +typedef +enum WMT_RIGHTS + { WMT_RIGHT_PLAYBACK = 0x1, + WMT_RIGHT_COPY_TO_NON_SDMI_DEVICE = 0x2, + WMT_RIGHT_COPY_TO_CD = 0x8, + WMT_RIGHT_COPY_TO_SDMI_DEVICE = 0x10, + WMT_RIGHT_ONE_TIME = 0x20, + WMT_RIGHT_SAVE_STREAM_PROTECTED = 0x40, + WMT_RIGHT_SDMI_TRIGGER = 0x10000, + WMT_RIGHT_SDMI_NOMORECOPIES = 0x20000 + } WMT_RIGHTS; + +typedef +enum WMT_STREAM_SELECTION + { WMT_OFF = 0, + WMT_CLEANPOINT_ONLY = 1, + WMT_ON = 2 + } WMT_STREAM_SELECTION; + +typedef +enum WMT_IMAGE_TYPE + { WMT_IT_NONE = 0, + WMT_IT_BITMAP = 1, + WMT_IT_JPEG = 2, + WMT_IT_GIF = 3 + } WMT_IMAGE_TYPE; + +typedef +enum WMT_ATTR_DATATYPE + { WMT_TYPE_DWORD = 0, + WMT_TYPE_STRING = 1, + WMT_TYPE_BINARY = 2, + WMT_TYPE_BOOL = 3, + WMT_TYPE_QWORD = 4, + WMT_TYPE_WORD = 5, + WMT_TYPE_GUID = 6 + } WMT_ATTR_DATATYPE; + +typedef +enum WMT_ATTR_IMAGETYPE + { WMT_IMAGETYPE_BITMAP = 1, + WMT_IMAGETYPE_JPEG = 2, + WMT_IMAGETYPE_GIF = 3 + } WMT_ATTR_IMAGETYPE; + +typedef +enum WMT_VERSION + { WMT_VER_4_0 = 0x40000, + WMT_VER_7_0 = 0x70000, + WMT_VER_8_0 = 0x80000, + WMT_VER_9_0 = 0x90000 + } WMT_VERSION; + +typedef +enum tagWMT_STORAGE_FORMAT + { WMT_Storage_Format_MP3 = 0, + WMT_Storage_Format_V1 = WMT_Storage_Format_MP3 + 1 + } WMT_STORAGE_FORMAT; + +typedef +enum tagWMT_DRMLA_TRUST + { WMT_DRMLA_UNTRUSTED = 0, + WMT_DRMLA_TRUSTED = WMT_DRMLA_UNTRUSTED + 1, + WMT_DRMLA_TAMPERED = WMT_DRMLA_TRUSTED + 1 + } WMT_DRMLA_TRUST; + +typedef +enum tagWMT_TRANSPORT_TYPE + { WMT_Transport_Type_Unreliable = 0, + WMT_Transport_Type_Reliable = WMT_Transport_Type_Unreliable + 1 + } WMT_TRANSPORT_TYPE; + +typedef +enum WMT_NET_PROTOCOL + { WMT_PROTOCOL_HTTP = 0 + } WMT_NET_PROTOCOL; + +typedef +enum WMT_PLAY_MODE + { WMT_PLAY_MODE_AUTOSELECT = 0, + WMT_PLAY_MODE_LOCAL = 1, + WMT_PLAY_MODE_DOWNLOAD = 2, + WMT_PLAY_MODE_STREAMING = 3 + } WMT_PLAY_MODE; + +typedef +enum WMT_PROXY_SETTINGS + { WMT_PROXY_SETTING_NONE = 0, + WMT_PROXY_SETTING_MANUAL = 1, + WMT_PROXY_SETTING_AUTO = 2, + WMT_PROXY_SETTING_BROWSER = 3, + WMT_PROXY_SETTING_MAX = WMT_PROXY_SETTING_BROWSER + 1 + } WMT_PROXY_SETTINGS; + +typedef +enum WMT_CODEC_INFO_TYPE + { WMT_CODECINFO_AUDIO = 0, + WMT_CODECINFO_VIDEO = 1, + WMT_CODECINFO_UNKNOWN = 0xffffffff + } WMT_CODEC_INFO_TYPE; + + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0004 + { WM_DM_NOTINTERLACED = 0, + WM_DM_DEINTERLACE_NORMAL = 1, + WM_DM_DEINTERLACE_HALFSIZE = 2, + WM_DM_DEINTERLACE_HALFSIZEDOUBLERATE = 3, + WM_DM_DEINTERLACE_INVERSETELECINE = 4, + WM_DM_DEINTERLACE_VERTICALHALFSIZEDOUBLERATE = 5 + } ; + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0005 + { WM_DM_IT_DISABLE_COHERENT_MODE = 0, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_AA_TOP = 1, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_BB_TOP = 2, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_BC_TOP = 3, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_CD_TOP = 4, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_DD_TOP = 5, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_AA_BOTTOM = 6, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_BB_BOTTOM = 7, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_BC_BOTTOM = 8, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_CD_BOTTOM = 9, + WM_DM_IT_FIRST_FRAME_IN_CLIP_IS_DD_BOTTOM = 10 + } ; +typedef +enum tagWMT_OFFSET_FORMAT + { WMT_OFFSET_FORMAT_100NS = 0, + WMT_OFFSET_FORMAT_FRAME_NUMBERS = WMT_OFFSET_FORMAT_100NS + 1, + WMT_OFFSET_FORMAT_PLAYLIST_OFFSET = WMT_OFFSET_FORMAT_FRAME_NUMBERS + 1, + WMT_OFFSET_FORMAT_TIMECODE = WMT_OFFSET_FORMAT_PLAYLIST_OFFSET + 1 + } WMT_OFFSET_FORMAT; + +typedef +enum tagWMT_INDEXER_TYPE + { WMT_IT_PRESENTATION_TIME = 0, + WMT_IT_FRAME_NUMBERS = WMT_IT_PRESENTATION_TIME + 1, + WMT_IT_TIMECODE = WMT_IT_FRAME_NUMBERS + 1 + } WMT_INDEXER_TYPE; + +typedef +enum tagWMT_INDEX_TYPE + { WMT_IT_NEAREST_DATA_UNIT = 1, + WMT_IT_NEAREST_OBJECT = WMT_IT_NEAREST_DATA_UNIT + 1, + WMT_IT_NEAREST_CLEAN_POINT = WMT_IT_NEAREST_OBJECT + 1 + } WMT_INDEX_TYPE; + +typedef +enum tagWMT_FILESINK_MODE + { WMT_FM_SINGLE_BUFFERS = 0x1, + WMT_FM_FILESINK_DATA_UNITS = 0x2, + WMT_FM_FILESINK_UNBUFFERED = 0x4 + } WMT_FILESINK_MODE; + +typedef +enum tagWMT_MUSICSPEECH_CLASS_MODE + { WMT_MS_CLASS_MUSIC = 0, + WMT_MS_CLASS_SPEECH = 1, + WMT_MS_CLASS_MIXED = 2 + } WMT_MUSICSPEECH_CLASS_MODE; + +typedef +enum tagWMT_WATERMARK_ENTRY_TYPE + { WMT_WMETYPE_AUDIO = 1, + WMT_WMETYPE_VIDEO = 2 + } WMT_WATERMARK_ENTRY_TYPE; + + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0006 + { WM_PLAYBACK_DRC_HIGH = 0, + WM_PLAYBACK_DRC_MEDIUM = WM_PLAYBACK_DRC_HIGH + 1, + WM_PLAYBACK_DRC_LOW = WM_PLAYBACK_DRC_MEDIUM + 1 + } ; + +enum __MIDL___MIDL_itf_wmsdkidl_0000_0007 + { WMT_TIMECODE_FRAMERATE_30 = 0, + WMT_TIMECODE_FRAMERATE_30DROP = WMT_TIMECODE_FRAMERATE_30 + 1, + WMT_TIMECODE_FRAMERATE_25 = WMT_TIMECODE_FRAMERATE_30DROP + 1, + WMT_TIMECODE_FRAMERATE_24 = WMT_TIMECODE_FRAMERATE_25 + 1 + } ; +typedef +enum WMT_CREDENTIAL_FLAGS + { WMT_CREDENTIAL_SAVE = 0x1, + WMT_CREDENTIAL_DONT_CACHE = 0x2, + WMT_CREDENTIAL_CLEAR_TEXT = 0x4, + WMT_CREDENTIAL_PROXY = 0x8, + WMT_CREDENTIAL_ENCRYPT = 0x10 + } WMT_CREDENTIAL_FLAGS; + +typedef +enum WM_AETYPE + { WM_AETYPE_INCLUDE = 0x69, + WM_AETYPE_EXCLUDE = 0x65 + } WM_AETYPE; + + +#pragma pack(push) + +#pragma pack(2) +typedef struct _WMStreamPrioritizationRecord + { + WORD wStreamNumber; + BOOL fMandatory; + } WM_STREAM_PRIORITY_RECORD; + + +#pragma pack(pop) +typedef struct _WMWriterStatistics + { + QWORD qwSampleCount; + QWORD qwByteCount; + QWORD qwDroppedSampleCount; + QWORD qwDroppedByteCount; + DWORD dwCurrentBitrate; + DWORD dwAverageBitrate; + DWORD dwExpectedBitrate; + DWORD dwCurrentSampleRate; + DWORD dwAverageSampleRate; + DWORD dwExpectedSampleRate; + } WM_WRITER_STATISTICS; + +typedef struct _WMWriterStatisticsEx + { + DWORD dwBitratePlusOverhead; + DWORD dwCurrentSampleDropRateInQueue; + DWORD dwCurrentSampleDropRateInCodec; + DWORD dwCurrentSampleDropRateInMultiplexer; + DWORD dwTotalSampleDropsInQueue; + DWORD dwTotalSampleDropsInCodec; + DWORD dwTotalSampleDropsInMultiplexer; + } WM_WRITER_STATISTICS_EX; + +typedef struct _WMReaderStatistics + { + DWORD cbSize; + DWORD dwBandwidth; + DWORD cPacketsReceived; + DWORD cPacketsRecovered; + DWORD cPacketsLost; + WORD wQuality; + } WM_READER_STATISTICS; + +typedef struct _WMReaderClientInfo + { + DWORD cbSize; + WCHAR *wszLang; + WCHAR *wszBrowserUserAgent; + WCHAR *wszBrowserWebPage; + QWORD qwReserved; + LPARAM *pReserved; + WCHAR *wszHostExe; + QWORD qwHostVersion; + WCHAR *wszPlayerUserAgent; + } WM_READER_CLIENTINFO; + +typedef struct _WMClientProperties + { + DWORD dwIPAddress; + DWORD dwPort; + } WM_CLIENT_PROPERTIES; + +typedef struct _WMClientPropertiesEx + { + DWORD cbSize; + LPCWSTR pwszIPAddress; + LPCWSTR pwszPort; + LPCWSTR pwszDNSName; + } WM_CLIENT_PROPERTIES_EX; + +typedef struct _WMPortNumberRange + { + WORD wPortBegin; + WORD wPortEnd; + } WM_PORT_NUMBER_RANGE; + +typedef struct _WMT_BUFFER_SEGMENT + { + INSSBuffer *pBuffer; + DWORD cbOffset; + DWORD cbLength; + } WMT_BUFFER_SEGMENT; + +typedef struct _WMT_PAYLOAD_FRAGMENT + { + DWORD dwPayloadIndex; + WMT_BUFFER_SEGMENT segmentData; + } WMT_PAYLOAD_FRAGMENT; + +typedef struct _WMT_FILESINK_DATA_UNIT + { + WMT_BUFFER_SEGMENT packetHeaderBuffer; + DWORD cPayloads; + WMT_BUFFER_SEGMENT *pPayloadHeaderBuffers; + DWORD cPayloadDataFragments; + WMT_PAYLOAD_FRAGMENT *pPayloadDataFragments; + } WMT_FILESINK_DATA_UNIT; + +typedef struct _WMT_WEBSTREAM_FORMAT + { + WORD cbSize; + WORD cbSampleHeaderFixedData; + WORD wVersion; + WORD wReserved; + } WMT_WEBSTREAM_FORMAT; + +typedef struct _WMT_WEBSTREAM_SAMPLE_HEADER + { + WORD cbLength; + WORD wPart; + WORD cTotalParts; + WORD wSampleType; + WCHAR wszURL[ 1 ]; + } WMT_WEBSTREAM_SAMPLE_HEADER; + +typedef struct _WMAddressAccessEntry + { + DWORD dwIPAddress; + DWORD dwMask; + } WM_ADDRESS_ACCESSENTRY; + + +#pragma pack(push) + +#pragma pack(1) +typedef struct _WMPicture + { + LPWSTR pwszMIMEType; + BYTE bPictureType; + LPWSTR pwszDescription; + DWORD dwDataLen; + BYTE *pbData; + } WM_PICTURE; + +typedef struct _WMSynchronisedLyrics + { + BYTE bTimeStampFormat; + BYTE bContentType; + LPWSTR pwszContentDescriptor; + DWORD dwLyricsLen; + BYTE *pbLyrics; + } WM_SYNCHRONISED_LYRICS; + +typedef struct _WMUserWebURL + { + LPWSTR pwszDescription; + LPWSTR pwszURL; + } WM_USER_WEB_URL; + +typedef struct _WMUserText + { + LPWSTR pwszDescription; + LPWSTR pwszText; + } WM_USER_TEXT; + +typedef struct _WMLeakyBucketPair + { + DWORD dwBitrate; + DWORD msBufferWindow; + } WM_LEAKY_BUCKET_PAIR; + + +#pragma pack(pop) +typedef struct _WM_LICENSE_STATE_DATA + { + DWORD dwSize; + DWORD dwNumStates; + DRM_LICENSE_STATE_DATA stateData[ 1 ]; + } WM_LICENSE_STATE_DATA; + +typedef struct __WMT_WATERMARK_ENTRY + { + WMT_WATERMARK_ENTRY_TYPE wmetType; + CLSID clsid; + UINT cbDisplayName; + LPWSTR pwszDisplayName; + } WMT_WATERMARK_ENTRY; + +#define WMT_VIDEOIMAGE_SAMPLE_INPUT_FRAME 1 // sample has input frame +#define WMT_VIDEOIMAGE_SAMPLE_OUTPUT_FRAME 2 // sample produces output frame +#define WMT_VIDEOIMAGE_SAMPLE_USES_CURRENT_INPUT_FRAME 4 +#define WMT_VIDEOIMAGE_SAMPLE_USES_PREVIOUS_INPUT_FRAME 8 +#define WMT_VIDEOIMAGE_SAMPLE_MOTION 1 // acef used (includes resizing) +#define WMT_VIDEOIMAGE_SAMPLE_ROTATION 2 // bd also used (not valid without acef) +#define WMT_VIDEOIMAGE_SAMPLE_BLENDING 4 // BlendCoef1 used +#define WMT_VIDEOIMAGE_SAMPLE_ADV_BLENDING 8 // BlendCoef2 also used (not valid without BlendCoef1) +#define WMT_VIDEOIMAGE_INTEGER_DENOMINATOR 65536L +#define WMT_VIDEOIMAGE_MAGIC_NUMBER 0x1d4a45f2 +typedef struct __WMT_VIDEOIMAGE_SAMPLE + { + DWORD dwMagic; + ULONG cbStruct; + DWORD dwControlFlags; + DWORD dwInputFlagsCur; + LONG lCurMotionXtoX; + LONG lCurMotionYtoX; + LONG lCurMotionXoffset; + LONG lCurMotionXtoY; + LONG lCurMotionYtoY; + LONG lCurMotionYoffset; + LONG lCurBlendCoef1; + LONG lCurBlendCoef2; + DWORD dwInputFlagsPrev; + LONG lPrevMotionXtoX; + LONG lPrevMotionYtoX; + LONG lPrevMotionXoffset; + LONG lPrevMotionXtoY; + LONG lPrevMotionYtoY; + LONG lPrevMotionYoffset; + LONG lPrevBlendCoef1; + LONG lPrevBlendCoef2; + } WMT_VIDEOIMAGE_SAMPLE; + +typedef struct _WMMediaType + { + GUID majortype; + GUID subtype; + BOOL bFixedSizeSamples; + BOOL bTemporalCompression; + ULONG lSampleSize; + GUID formattype; + IUnknown *pUnk; + ULONG cbFormat; + /* [size_is] */ BYTE *pbFormat; + } WM_MEDIA_TYPE; + +typedef struct tagWMVIDEOINFOHEADER +{ + // + // The bit we really want to use. + // + RECT rcSource; + + // + // Where the video should go. + // + RECT rcTarget; + + // + // Approximate bit data rate. + // + DWORD dwBitRate; + + // + // Bit error rate for this stream. + // + DWORD dwBitErrorRate; + + // + // Average time per frame (100ns units). + // + LONGLONG AvgTimePerFrame; + + BITMAPINFOHEADER bmiHeader; +} WMVIDEOINFOHEADER; +typedef struct tagWMVIDEOINFOHEADER2 +{ + // + // The bit we really want to use. + // + RECT rcSource; + + // + // Where the video should go. + // + RECT rcTarget; + + // + // Approximate bit data rate. + // + DWORD dwBitRate; + + // + // Bit error rate for this stream. + // + DWORD dwBitErrorRate; + + // + // Average time per frame (100ns units). + // + LONGLONG AvgTimePerFrame; + + // + // Use AMINTERLACE_* defines. Reject connection if undefined bits are not 0. + // + DWORD dwInterlaceFlags; + + // + // use AMCOPYPROTECT_* defines. Reject connection if undefined bits are not 0. + // + DWORD dwCopyProtectFlags; + + // + // X dimension of picture aspect ratio, e.g. 16 for 16x9 display. + // + DWORD dwPictAspectRatioX; + + // + // Y dimension of picture aspect ratio, e.g. 9 for 16x9 display. + // + DWORD dwPictAspectRatioY; + + // + // Must be 0; reject connection otherwise. + // + DWORD dwReserved1; + + // + // Must be 0; reject connection otherwise. + // + DWORD dwReserved2; + + BITMAPINFOHEADER bmiHeader; +} WMVIDEOINFOHEADER2; +typedef struct tagWMMPEG2VIDEOINFO +{ + // + // Video info header2. + // + WMVIDEOINFOHEADER2 hdr; + + // + // Not used for DVD. + // + DWORD dwStartTimeCode; + + // + // Is 0 for DVD (no sequence header). + // + DWORD cbSequenceHeader; + + // + // Use enum MPEG2Profile. + // + DWORD dwProfile; + + // + // Use enum MPEG2Level. + // + DWORD dwLevel; + + // + // Use AMMPEG2_* defines. Reject connection if undefined bits are not 0. + // + DWORD dwFlags; + + // + // Sequence header. + // + DWORD dwSequenceHeader[1]; + +} WMMPEG2VIDEOINFO; +typedef struct tagWMSCRIPTFORMAT +{ + GUID scriptType; +} WMSCRIPTFORMAT; +// 00000000-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_Base +EXTERN_GUID(WMMEDIASUBTYPE_Base, +0x00000000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 73646976-0000-0010-8000-00AA00389B71 'vids' == WMMEDIATYPE_Video +EXTERN_GUID(WMMEDIATYPE_Video, +0x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// e436eb78-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB1 +EXTERN_GUID(WMMEDIASUBTYPE_RGB1, +0xe436eb78, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// e436eb79-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB4 +EXTERN_GUID(WMMEDIASUBTYPE_RGB4, +0xe436eb79, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// e436eb7a-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB8 +EXTERN_GUID(WMMEDIASUBTYPE_RGB8, +0xe436eb7a, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// e436eb7b-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB565 +EXTERN_GUID(WMMEDIASUBTYPE_RGB565, +0xe436eb7b, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// e436eb7c-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB555 +EXTERN_GUID(WMMEDIASUBTYPE_RGB555, +0xe436eb7c, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// e436eb7d-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB24 +EXTERN_GUID(WMMEDIASUBTYPE_RGB24, +0xe436eb7d, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// e436eb7e-524f-11ce-9f53-0020af0ba770 MEDIASUBTYPE_RGB32 +EXTERN_GUID(WMMEDIASUBTYPE_RGB32, +0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +// 30323449-0000-0010-8000-00AA00389B71 'YV12' == MEDIASUBTYPE_I420 +EXTERN_GUID(WMMEDIASUBTYPE_I420, +0x30323449, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 56555949-0000-0010-8000-00AA00389B71 'YV12' == MEDIASUBTYPE_IYUV +EXTERN_GUID(WMMEDIASUBTYPE_IYUV, +0x56555949, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 31313259-0000-0010-8000-00AA00389B71 'YV12' == MEDIASUBTYPE_YV12 +EXTERN_GUID(WMMEDIASUBTYPE_YV12, +0x32315659, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 32595559-0000-0010-8000-00AA00389B71 'YUY2' == MEDIASUBTYPE_YUY2 +EXTERN_GUID(WMMEDIASUBTYPE_YUY2, +0x32595559, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 59565955-0000-0010-8000-00AA00389B71 'UYVY' == MEDIASUBTYPE_UYVY +EXTERN_GUID(WMMEDIASUBTYPE_UYVY, +0x59565955, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 55595659-0000-0010-8000-00AA00389B71 'YVYU' == MEDIASUBTYPE_YVYU +EXTERN_GUID(WMMEDIASUBTYPE_YVYU, +0x55595659, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 39555659-0000-0010-8000-00AA00389B71 'YVU9' == MEDIASUBTYPE_YVU9 +EXTERN_GUID(WMMEDIASUBTYPE_YVU9, +0x39555659, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 1d4a45f2-e5f6-4b44-8388-f0ae5c0e0c37 MEDIASUBTYPE_VIDEOIMAGE +EXTERN_GUID(WMMEDIASUBTYPE_VIDEOIMAGE, +0x1d4a45f2, 0xe5f6, 0x4b44, 0x83, 0x88, 0xf0, 0xae, 0x5c, 0x0e, 0x0c, 0x37); +// 3334504D-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_MP43 +EXTERN_GUID(WMMEDIASUBTYPE_MP43, +0x3334504D, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 5334504D-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_MP4S +EXTERN_GUID(WMMEDIASUBTYPE_MP4S, +0x5334504D, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 31564D57-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMV1 +EXTERN_GUID(WMMEDIASUBTYPE_WMV1, +0x31564D57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 32564D57-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMV2 +EXTERN_GUID(WMMEDIASUBTYPE_WMV2, +0x32564D57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 3153534D-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_MSS1 +EXTERN_GUID(WMMEDIASUBTYPE_MSS1, +0x3153534D, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// e06d8026-db46-11cf-b4d1-00805f6cbbea WMMEDIASUBTYPE_MPEG2_VIDEO +EXTERN_GUID(WMMEDIASUBTYPE_MPEG2_VIDEO, +0xe06d8026, 0xdb46, 0x11cf, 0xb4, 0xd1, 0x00, 0x80, 0x5f, 0x6c, 0xbb, 0xea); +// 73647561-0000-0010-8000-00AA00389B71 'auds' == WMMEDIATYPE_Audio +EXTERN_GUID(WMMEDIATYPE_Audio, +0x73647561, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 00000001-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_PCM +EXTERN_GUID(WMMEDIASUBTYPE_PCM, +0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000009-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_DRM +EXTERN_GUID(WMMEDIASUBTYPE_DRM, +0x00000009, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000162-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMAudioV9 +EXTERN_GUID(WMMEDIASUBTYPE_WMAudioV9, +0x00000162, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000163-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMAudio_Lossless +EXTERN_GUID(WMMEDIASUBTYPE_WMAudio_Lossless, +0x00000163, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 3253534D-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_MSS2 +EXTERN_GUID(WMMEDIASUBTYPE_MSS2, +0x3253534D, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 0000000A-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMSP1 +EXTERN_GUID( WMMEDIASUBTYPE_WMSP1, +0x0000000A,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71); +// 33564D57-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMV3 +EXTERN_GUID(WMMEDIASUBTYPE_WMV3, +0x33564D57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 50564D57-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMVP +EXTERN_GUID(WMMEDIASUBTYPE_WMVP, +0x50564D57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000161-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMAudioV8 +EXTERN_GUID(WMMEDIASUBTYPE_WMAudioV8, +0x00000161, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000161-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMAudioV7 +EXTERN_GUID(WMMEDIASUBTYPE_WMAudioV7, +0x00000161, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000161-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_WMAudioV2 +EXTERN_GUID(WMMEDIASUBTYPE_WMAudioV2, +0x00000161, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000130-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_ACELPnet +EXTERN_GUID(WMMEDIASUBTYPE_ACELPnet, +0x00000130, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 00000050-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_MP3 +EXTERN_GUID(WMMEDIASUBTYPE_MP3, +0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); +// 776257d4-c627-41cb-8f81-7ac7ff1c40cc WMMEDIASUBTYPE_WebStream +EXTERN_GUID(WMMEDIASUBTYPE_WebStream, +0x776257d4, 0xc627, 0x41cb, 0x8f, 0x81, 0x7a, 0xc7, 0xff, 0x1c, 0x40, 0xcc); +// 73636d64-0000-0010-8000-00AA00389B71 'scmd' == WMMEDIATYPE_Script +EXTERN_GUID(WMMEDIATYPE_Script, +0x73636d64, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); +// 34A50FD8-8AA5-4386-81FE-A0EFE0488E31 'imag' == WMMEDIATYPE_Image +EXTERN_GUID(WMMEDIATYPE_Image, +0x34a50fd8, 0x8aa5, 0x4386, 0x81, 0xfe, 0xa0, 0xef, 0xe0, 0x48, 0x8e, 0x31); +// D9E47579-930E-4427-ADFC-AD80F290E470 'fxfr' == WMMEDIATYPE_FileTransfer +EXTERN_GUID(WMMEDIATYPE_FileTransfer, +0xd9e47579, 0x930e, 0x4427, 0xad, 0xfc, 0xad, 0x80, 0xf2, 0x90, 0xe4, 0x70); +// 9BBA1EA7-5AB2-4829-BA57-0940209BCF3E 'text' == WMMEDIATYPE_Text +EXTERN_GUID(WMMEDIATYPE_Text, +0x9bba1ea7, 0x5ab2, 0x4829, 0xba, 0x57, 0x9, 0x40, 0x20, 0x9b, 0xcf, 0x3e); +// 05589f80-c356-11ce-bf01-00aa0055595a WMFORMAT_VideoInfo +EXTERN_GUID(WMFORMAT_VideoInfo, +0x05589f80, 0xc356, 0x11ce, 0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a); +// e06d80e3-db46-11cf-b4d1-00805f6cbbea WMFORMAT_MPEG2Video +EXTERN_GUID(WMFORMAT_MPEG2Video, +0xe06d80e3, 0xdb46, 0x11cf, 0xb4, 0xd1, 0x00, 0x80, 0x05f, 0x6c, 0xbb, 0xea); +// 05589f81-c356-11ce-bf01-00aa0055595a WMFORMAT_WaveFormatEx +EXTERN_GUID(WMFORMAT_WaveFormatEx, +0x05589f81, 0xc356, 0x11ce, 0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a); +// 5C8510F2-DEBE-4ca7-BBA5-F07A104F8DFF WMFORMAT_Script +EXTERN_GUID(WMFORMAT_Script, +0x5c8510f2, 0xdebe, 0x4ca7, 0xbb, 0xa5, 0xf0, 0x7a, 0x10, 0x4f, 0x8d, 0xff); +// da1e6b13-8359-4050-b398-388e965bf00c WMFORMAT_WebStream +EXTERN_GUID(WMFORMAT_WebStream, +0xda1e6b13, 0x8359, 0x4050, 0xb3, 0x98, 0x38, 0x8e, 0x96, 0x5b, 0xf0, 0x0c); +// 82f38a70-c29f-11d1-97ad-00a0c95ea850 WMSCRIPTTYPE_TwoStrings +EXTERN_GUID( WMSCRIPTTYPE_TwoStrings, +0x82f38a70,0xc29f,0x11d1,0x97,0xad,0x00,0xa0,0xc9,0x5e,0xa8,0x50); +EXTERN_GUID( WM_SampleExtensionGUID_OutputCleanPoint, 0xf72a3c6f, 0x6eb4, 0x4ebc, 0xb1, 0x92, 0x9, 0xad, 0x97, 0x59, 0xe8, 0x28 ); +EXTERN_GUID( WM_SampleExtensionGUID_Timecode, 0x399595ec, 0x8667, 0x4e2d, 0x8f, 0xdb, 0x98, 0x81, 0x4c, 0xe7, 0x6c, 0x1e); +EXTERN_GUID( WM_SampleExtensionGUID_FileName, 0xe165ec0e, 0x19ed, 0x45d7, 0xb4, 0xa7, 0x25, 0xcb, 0xd1, 0xe2, 0x8e, 0x9b); +EXTERN_GUID( WM_SampleExtensionGUID_ContentType, 0xd590dc20, 0x07bc, 0x436c, 0x9c, 0xf7, 0xf3, 0xbb, 0xfb, 0xf1, 0xa4, 0xdc ); +EXTERN_GUID( WM_SampleExtensionGUID_PixelAspectRatio, 0x1b1ee554, 0xf9ea, 0x4bc8, 0x82, 0x1a, 0x37, 0x6b, 0x74, 0xe4, 0xc4, 0xb8 ); +EXTERN_GUID( WM_SampleExtensionGUID_SampleDuration, 0xc6bd9450, 0x867f, 0x4907, 0x83, 0xa3, 0xc7, 0x79, 0x21, 0xb7, 0x33, 0xad ); +#define WM_SampleExtension_ContentType_Size 1 +#define WM_SampleExtension_PixelAspectRatio_Size 2 +#define WM_SampleExtension_Timecode_Size 14 +#define WM_SampleExtension_SampleDuration_Size 2 +#define WM_CT_INTERLACED 128 +#define WM_CT_BOTTOM_FIELD_FIRST 32 +#define WM_CT_TOP_FIELD_FIRST 64 + +#pragma pack(push) + +#pragma pack(2) +typedef struct _WMT_TIMECODE_EXTENSION_DATA + { + WORD wRange; + DWORD dwTimecode; + DWORD dwUserbits; + DWORD dwAmFlags; + } WMT_TIMECODE_EXTENSION_DATA; + + +#pragma pack(pop) +EXTERN_GUID( IID_IWMMediaProps, 0x96406bce,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMVideoMediaProps, 0x96406bcf,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMWriter, 0x96406bd4,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMInputMediaProps, 0x96406bd5,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMReader, 0x96406bd6,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMSyncReader, 0x9397f121,0x7705,0x4dc9,0xb0,0x49,0x98,0xb6,0x98,0x18,0x84,0x14 ); +EXTERN_GUID( IID_IWMSyncReader2, 0xfaed3d21,0x1b6b,0x4af7,0x8c,0xb6,0x3e,0x18,0x9b,0xbc,0x18,0x7b ); +EXTERN_GUID( IID_IWMOutputMediaProps, 0x96406bd7,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMStatusCallback, 0x6d7cdc70,0x9888,0x11d3,0x8e,0xdc,0x00,0xc0,0x4f,0x61,0x09,0xcf ); +EXTERN_GUID( IID_IWMReaderCallback, 0x96406bd8,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMCredentialCallback, 0x342e0eb7,0xe651,0x450c,0x97,0x5b,0x2a,0xce,0x2c,0x90,0xc4,0x8e ); +EXTERN_GUID( IID_IWMMetadataEditor, 0x96406bd9,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMMetadataEditor2, 0x203cffe3,0x2e18,0x4fdf,0xb5,0x9d,0x6e,0x71,0x53,0x05,0x34,0xcf ); +EXTERN_GUID( IID_IWMDRMEditor, 0xFF130EBC,0xA6C3,0x42A6,0xB4,0x01,0xC3,0x38,0x2C,0x3E,0x08,0xB3 ); +EXTERN_GUID( IID_IWMHeaderInfo, 0x96406bda,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMHeaderInfo2, 0x15cf9781,0x454e,0x482e,0xb3,0x93,0x85,0xfa,0xe4,0x87,0xa8,0x10 ); +EXTERN_GUID( IID_IWMHeaderInfo3, 0x15CC68E3,0x27CC,0x4ecd,0xB2,0x22,0x3F,0x5D,0x02,0xD8,0x0B,0xD5 ); +EXTERN_GUID( IID_IWMProfileManager, 0xd16679f2,0x6ca0,0x472d,0x8d,0x31,0x2f,0x5d,0x55,0xae,0xe1,0x55 ); +EXTERN_GUID( IID_IWMProfileManager2, 0x7a924e51,0x73c1,0x494d,0x80,0x19,0x23,0xd3,0x7e,0xd9,0xb8,0x9a ); +EXTERN_GUID( IID_IWMProfileManagerLanguage, 0xba4dcc78,0x7ee0,0x4ab8,0xb2,0x7a,0xdb,0xce,0x8b,0xc5,0x14,0x54 ); +EXTERN_GUID( IID_IWMProfile, 0x96406bdb,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMProfile2, 0x07e72d33,0xd94e,0x4be7,0x88,0x43,0x60,0xae,0x5f,0xf7,0xe5,0xf5 ); +EXTERN_GUID( IID_IWMProfile3, 0x00ef96cc,0xa461,0x4546,0x8b,0xcd,0xc9,0xa2,0x8f,0x0e,0x06,0xf5 ); +EXTERN_GUID( IID_IWMStreamConfig, 0x96406bdc,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMStreamConfig2, 0x7688d8cb,0xfc0d,0x43bd,0x94,0x59,0x5a,0x8d,0xec,0x20,0x0c,0xfa ); +EXTERN_GUID( IID_IWMStreamConfig3, 0xcb164104,0x3aa9,0x45a7,0x9a,0xc9,0x4d,0xae,0xe1,0x31,0xd6,0xe1 ); +EXTERN_GUID( IID_IWMStreamList, 0x96406bdd,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMMutualExclusion, 0x96406bde,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMMutualExclusion2, 0x302b57d,0x89d1,0x4ba2,0x85,0xc9,0x16,0x6f,0x2c,0x53,0xeb,0x91 ); +EXTERN_GUID( IID_IWMBandwidthSharing, 0xad694af1,0xf8d9,0x42f8,0xbc,0x47,0x70,0x31,0x1b,0x0c,0x4f,0x9e ); +EXTERN_GUID( IID_IWMStreamPrioritization, 0x8c1c6090,0xf9a8,0x4748,0x8e,0xc3,0xdd,0x11,0x08,0xba,0x1e,0x77 ); +EXTERN_GUID( IID_IWMWriterAdvanced, 0x96406be3,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMWriterAdvanced2, 0x962dc1ec,0xc046,0x4db8,0x9c,0xc7,0x26,0xce,0xae,0x50,0x08,0x17 ); +EXTERN_GUID( IID_IWMWriterAdvanced3, 0x2cd6492d,0x7c37,0x4e76,0x9d,0x3b,0x59,0x26,0x11,0x83,0xa2,0x2e ); +EXTERN_GUID( IID_IWMWriterPreprocess, 0xfc54a285,0x38c4,0x45b5,0xaa,0x23,0x85,0xb9,0xf7,0xcb,0x42,0x4b ); +EXTERN_GUID( IID_IWMWriterSink, 0x96406be4,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMWriterFileSink, 0x96406be5,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMWriterFileSink2, 0x14282ba7,0x4aef,0x4205,0x8c,0xe5,0xc2,0x29,0x03,0x5a,0x05,0xbc ); +EXTERN_GUID( IID_IWMWriterFileSink3, 0x3fea4feb,0x2945,0x47a7,0xa1,0xdd,0xc5,0x3a,0x8f,0xc4,0xc4,0x5c ); +EXTERN_GUID( IID_IWMWriterNetworkSink, 0x96406be7,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMClientConnections, 0x73c66010,0xa299,0x41df,0xb1,0xf0,0xcc,0xf0,0x3b,0x09,0xc1,0xc6 ); +EXTERN_GUID( IID_IWMClientConnections2, 0x4091571e,0x4701,0x4593,0xbb,0x3d,0xd5,0xf5,0xf0,0xc7,0x42,0x46 ); +EXTERN_GUID( IID_IWMReaderAdvanced, 0x96406bea,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMReaderAdvanced2, 0xae14a945,0xb90c,0x4d0d,0x91,0x27,0x80,0xd6,0x65,0xf7,0xd7,0x3e ); +EXTERN_GUID( IID_IWMReaderAdvanced3, 0x5dc0674b,0xf04b,0x4a4e,0x9f,0x2a,0xb1,0xaf,0xde,0x2c,0x81,0x00 ); +EXTERN_GUID( IID_IWMReaderAdvanced4, 0x945a76a2,0x12ae,0x4d48,0xbd,0x3c,0xcd,0x1d,0x90,0x39,0x9b,0x85 ); +EXTERN_GUID( IID_IWMDRMReader, 0xd2827540,0x3ee7,0x432c,0xb1,0x4c,0xdc,0x17,0xf0,0x85,0xd3,0xb3 ); +EXTERN_GUID( IID_IWMReaderCallbackAdvanced, 0x96406beb,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMReaderNetworkConfig,0x96406bec,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMReaderStreamClock, 0x96406bed,0x2b2b,0x11d3,0xb3,0x6b,0x00,0xc0,0x4f,0x61,0x08,0xff ); +EXTERN_GUID( IID_IWMIndexer, 0x6d7cdc71,0x9888,0x11d3,0x8e,0xdc,0x00,0xc0,0x4f,0x61,0x09,0xcf ); +EXTERN_GUID( IID_IWMIndexer2, 0xb70f1e42,0x6255,0x4df0,0xa6,0xb9,0x02,0xb2,0x12,0xd9,0xe2,0xbb ); +EXTERN_GUID( IID_IWMReaderAllocatorEx, 0x9f762fa7,0xa22e,0x428d,0x93,0xc9,0xac,0x82,0xf3,0xaa,0xfe,0x5a ); +EXTERN_GUID( IID_IWMReaderTypeNegotiation, 0xfdbe5592,0x81a1,0x41ea,0x93,0xbd,0x73,0x5c,0xad,0x1a,0xdc,0x5 ); +EXTERN_GUID( IID_IWMLicenseBackup, 0x05E5AC9F,0x3FB6,0x4508,0xBB,0x43,0xA4,0x06,0x7B,0xA1,0xEB,0xE8); +EXTERN_GUID( IID_IWMLicenseRestore, 0xC70B6334,0xa22e,0x4efb,0xA2,0x45,0x15,0xE6,0x5A,0x00,0x4A,0x13); +EXTERN_GUID( IID_IWMBackupRestoreProps, 0x3C8E0DA6,0x996F,0x4ff3,0xA1,0xAF,0x48,0x38,0xF9,0x37,0x7e,0x2e); +EXTERN_GUID( IID_IWMPacketSize, 0xcdfb97ab,0x188f,0x40b3,0xb6,0x43,0x5b,0x79,0x03,0x97,0x5c,0x59); +EXTERN_GUID( IID_IWMPacketSize2, 0x8bfc2b9e,0xb646,0x4233,0xa8,0x77,0x1c,0x6a,0x7,0x96,0x69,0xdc); +EXTERN_GUID( IID_IWMRegisterCallback, 0xcf4b1f99,0x4de2,0x4e49,0xa3,0x63,0x25,0x27,0x40,0xd9,0x9b,0xc1); +EXTERN_GUID( IID_IWMWriterPostView, 0x81e20ce4,0x75ef,0x491a,0x80,0x04,0xfc,0x53,0xc4,0x5b,0xdc,0x3e); +EXTERN_GUID( IID_IWMWriterPostViewCallback, 0xd9d6549d,0xa193,0x4f24,0xb3,0x08,0x03,0x12,0x3d,0x9b,0x7f,0x8d); +EXTERN_GUID( IID_IWMCodecInfo, 0xa970f41e,0x34de,0x4a98,0xb3,0xba,0xe4,0xb3,0xca,0x75,0x28,0xf0); +EXTERN_GUID( IID_IWMCodecInfo2, 0xaa65e273,0xb686,0x4056,0x91,0xec,0xdd,0x76,0x8d,0x4d,0xf7,0x10); +EXTERN_GUID( IID_IWMCodecInfo3, 0x7e51f487,0x4d93,0x4f98,0x8a,0xb4,0x27,0xd0,0x56,0x5a,0xdc,0x51); +EXTERN_GUID( IID_IWMPropertyVault, 0x72995A79,0x5090,0x42a4,0x9C,0x8C,0xD9,0xD0,0xB6,0xD3,0x4B,0xE5 ); +EXTERN_GUID( IID_IWMIStreamProps, 0x6816dad3,0x2b4b,0x4c8e,0x81,0x49,0x87,0x4c,0x34,0x83,0xa7,0x53 ); +EXTERN_GUID( IID_IWMLanguageList, 0xdf683f00,0x2d49,0x4d8e,0x92,0xb7,0xfb,0x19,0xf6,0xa0,0xdc,0x57 ); +EXTERN_GUID( IID_IWMDRMWriter, 0xd6ea5dd0,0x12a0,0x43f4,0x90,0xab,0xa3,0xfd,0x45,0x1e,0x6a,0x07 ); +EXTERN_GUID( IID_IWMWriterPushSink, 0xdc10e6a5,0x072c,0x467d,0xbf,0x57,0x63,0x30,0xa9,0xdd,0xe1,0x2a ); +EXTERN_GUID( IID_IWMReaderNetworkConfig2,0xd979a853,0x042b,0x4050,0x83,0x87,0xc9,0x39,0xdb,0x22,0x01,0x3f ); +EXTERN_GUID( IID_IWMWatermarkInfo, 0x6f497062,0xf2e2,0x4624,0x8e,0xa7,0x9d,0xd4,0x0d,0x81,0xfc,0x8d ); +EXTERN_GUID( IID_IWMReaderAccelerator, 0xbddc4d08,0x944d,0x4d52,0xa6,0x12,0x46,0xc3,0xfd,0xa0,0x7d,0xd4 ); +EXTERN_GUID( IID_IWMReaderTimecode, 0xf369e2f0,0xe081,0x4fe6,0x84,0x50,0xb8,0x10,0xb2,0xf4,0x10,0xd1 ); +EXTERN_GUID( IID_IWMImageInfo, 0x9f0aa3b6,0x7267,0x4d89,0x88,0xf2,0xba,0x91,0x5a,0xa5,0xc4,0xc6); +EXTERN_GUID( IID_IWMAddressAccess, 0xBB3C6389,0x1633,0x4e92,0xAF,0x14,0x9F,0x31,0x73,0xBA,0x39,0xD0 ); +EXTERN_GUID( IID_IWMAddressAccess2, 0x65a83fc2,0x3e98,0x4d4d,0x81,0xb5,0x2a,0x74,0x28,0x86,0xb3,0x3d ); +EXTERN_GUID( CLSID_WMMUTEX_Language, 0xD6E22A00,0x35DA,0x11D1,0x90,0x34,0x00,0xA0,0xC9,0x03,0x49,0xBE ); +EXTERN_GUID( CLSID_WMMUTEX_Bitrate, 0xD6E22A01,0x35DA,0x11D1,0x90,0x34,0x00,0xA0,0xC9,0x03,0x49,0xBE ); +EXTERN_GUID( CLSID_WMMUTEX_Presentation, 0xD6E22A02,0x35DA,0x11D1,0x90,0x34,0x00,0xA0,0xC9,0x03,0x49,0xBE ); +EXTERN_GUID( CLSID_WMMUTEX_Unknown, 0xD6E22A03,0x35DA,0x11D1,0x90,0x34,0x00,0xA0,0xC9,0x03,0x49,0xBE ); +EXTERN_GUID( CLSID_WMBandwidthSharing_Exclusive, 0xaf6060aa,0x5197,0x11d2,0xb6,0xaf,0x00,0xc0,0x4f,0xd9,0x08,0xe9 ); +EXTERN_GUID( CLSID_WMBandwidthSharing_Partial, 0xaf6060ab,0x5197,0x11d2,0xb6,0xaf,0x00,0xc0,0x4f,0xd9,0x08,0xe9 ); +// {B42CDE2B-6178-4a2c-A375-89DD3FD7F497} +EXTERN_GUID( WMT_DMOCATEGORY_AUDIO_WATERMARK, 0x65221c5a, 0xfa75, 0x4b39, 0xb5, 0x0c, 0x06, 0xc3, 0x36, 0xb6, 0xa3, 0xef ); +// {E77797C6-18AF-4458-BBDD-492D3F78FC8F} +EXTERN_GUID( WMT_DMOCATEGORY_VIDEO_WATERMARK, 0x187cc922, 0x8efc, 0x4404, 0x9d, 0xaf, 0x63, 0xf4, 0x83, 0x0d, 0xf1, 0xbc ); +#define WM_MAX_VIDEO_STREAMS 0x3f +#define WM_MAX_STREAMS 0x3f +HRESULT STDMETHODCALLTYPE WMIsContentProtected( const WCHAR *pwszFileName, BOOL *pfIsProtected ); +HRESULT STDMETHODCALLTYPE WMCreateCertificate( IUnknown** pUnkCert ); +HRESULT STDMETHODCALLTYPE WMCreateWriter( IUnknown* pUnkCert, IWMWriter **ppWriter ); +HRESULT STDMETHODCALLTYPE WMCreateReader( IUnknown* pUnkCert, DWORD dwRights, IWMReader **ppReader ); +HRESULT STDMETHODCALLTYPE WMCreateSyncReader( IUnknown* pUnkCert, DWORD dwRights, IWMSyncReader **ppSyncReader ); +HRESULT STDMETHODCALLTYPE WMCreateEditor( IWMMetadataEditor **ppEditor ); +HRESULT STDMETHODCALLTYPE WMCreateIndexer( IWMIndexer **ppIndexer ); +HRESULT STDMETHODCALLTYPE WMCreateBackupRestorer( IUnknown *pCallback, IWMLicenseBackup **ppBackup ); +HRESULT STDMETHODCALLTYPE WMCreateProfileManager( IWMProfileManager **ppProfileManager ); +HRESULT STDMETHODCALLTYPE WMCreateWriterFileSink( IWMWriterFileSink **ppSink ); +HRESULT STDMETHODCALLTYPE WMCreateWriterNetworkSink( IWMWriterNetworkSink **ppSink ); +HRESULT STDMETHODCALLTYPE WMCreateWriterPushSink( IWMWriterPushSink **ppSink ); + + +extern RPC_IF_HANDLE __MIDL_itf_wmsdkidl_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_wmsdkidl_0000_v0_0_s_ifspec; + +#ifndef __IWMMediaProps_INTERFACE_DEFINED__ +#define __IWMMediaProps_INTERFACE_DEFINED__ + +/* interface IWMMediaProps */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMMediaProps; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BCE-2B2B-11d3-B36B-00C04F6108FF") + IWMMediaProps : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetType( + /* [out] */ GUID *pguidType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMediaType( + /* [out] */ WM_MEDIA_TYPE *pType, + /* [out][in] */ DWORD *pcbType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMediaType( + /* [in] */ WM_MEDIA_TYPE *pType) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMMediaPropsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMMediaProps * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMMediaProps * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMMediaProps * This); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMMediaProps * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *GetMediaType )( + IWMMediaProps * This, + /* [out] */ WM_MEDIA_TYPE *pType, + /* [out][in] */ DWORD *pcbType); + + HRESULT ( STDMETHODCALLTYPE *SetMediaType )( + IWMMediaProps * This, + /* [in] */ WM_MEDIA_TYPE *pType); + + END_INTERFACE + } IWMMediaPropsVtbl; + + interface IWMMediaProps + { + CONST_VTBL struct IWMMediaPropsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMMediaProps_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMMediaProps_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMMediaProps_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMMediaProps_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMMediaProps_GetMediaType(This,pType,pcbType) \ + (This)->lpVtbl -> GetMediaType(This,pType,pcbType) + +#define IWMMediaProps_SetMediaType(This,pType) \ + (This)->lpVtbl -> SetMediaType(This,pType) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMMediaProps_GetType_Proxy( + IWMMediaProps * This, + /* [out] */ GUID *pguidType); + + +void __RPC_STUB IWMMediaProps_GetType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMediaProps_GetMediaType_Proxy( + IWMMediaProps * This, + /* [out] */ WM_MEDIA_TYPE *pType, + /* [out][in] */ DWORD *pcbType); + + +void __RPC_STUB IWMMediaProps_GetMediaType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMediaProps_SetMediaType_Proxy( + IWMMediaProps * This, + /* [in] */ WM_MEDIA_TYPE *pType); + + +void __RPC_STUB IWMMediaProps_SetMediaType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMMediaProps_INTERFACE_DEFINED__ */ + + +#ifndef __IWMVideoMediaProps_INTERFACE_DEFINED__ +#define __IWMVideoMediaProps_INTERFACE_DEFINED__ + +/* interface IWMVideoMediaProps */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMVideoMediaProps; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BCF-2B2B-11d3-B36B-00C04F6108FF") + IWMVideoMediaProps : public IWMMediaProps + { + public: + virtual HRESULT STDMETHODCALLTYPE GetMaxKeyFrameSpacing( + /* [out] */ LONGLONG *pllTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMaxKeyFrameSpacing( + /* [in] */ LONGLONG llTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetQuality( + /* [out] */ DWORD *pdwQuality) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetQuality( + /* [in] */ DWORD dwQuality) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMVideoMediaPropsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMVideoMediaProps * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMVideoMediaProps * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMVideoMediaProps * This); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMVideoMediaProps * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *GetMediaType )( + IWMVideoMediaProps * This, + /* [out] */ WM_MEDIA_TYPE *pType, + /* [out][in] */ DWORD *pcbType); + + HRESULT ( STDMETHODCALLTYPE *SetMediaType )( + IWMVideoMediaProps * This, + /* [in] */ WM_MEDIA_TYPE *pType); + + HRESULT ( STDMETHODCALLTYPE *GetMaxKeyFrameSpacing )( + IWMVideoMediaProps * This, + /* [out] */ LONGLONG *pllTime); + + HRESULT ( STDMETHODCALLTYPE *SetMaxKeyFrameSpacing )( + IWMVideoMediaProps * This, + /* [in] */ LONGLONG llTime); + + HRESULT ( STDMETHODCALLTYPE *GetQuality )( + IWMVideoMediaProps * This, + /* [out] */ DWORD *pdwQuality); + + HRESULT ( STDMETHODCALLTYPE *SetQuality )( + IWMVideoMediaProps * This, + /* [in] */ DWORD dwQuality); + + END_INTERFACE + } IWMVideoMediaPropsVtbl; + + interface IWMVideoMediaProps + { + CONST_VTBL struct IWMVideoMediaPropsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMVideoMediaProps_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMVideoMediaProps_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMVideoMediaProps_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMVideoMediaProps_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMVideoMediaProps_GetMediaType(This,pType,pcbType) \ + (This)->lpVtbl -> GetMediaType(This,pType,pcbType) + +#define IWMVideoMediaProps_SetMediaType(This,pType) \ + (This)->lpVtbl -> SetMediaType(This,pType) + + +#define IWMVideoMediaProps_GetMaxKeyFrameSpacing(This,pllTime) \ + (This)->lpVtbl -> GetMaxKeyFrameSpacing(This,pllTime) + +#define IWMVideoMediaProps_SetMaxKeyFrameSpacing(This,llTime) \ + (This)->lpVtbl -> SetMaxKeyFrameSpacing(This,llTime) + +#define IWMVideoMediaProps_GetQuality(This,pdwQuality) \ + (This)->lpVtbl -> GetQuality(This,pdwQuality) + +#define IWMVideoMediaProps_SetQuality(This,dwQuality) \ + (This)->lpVtbl -> SetQuality(This,dwQuality) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMVideoMediaProps_GetMaxKeyFrameSpacing_Proxy( + IWMVideoMediaProps * This, + /* [out] */ LONGLONG *pllTime); + + +void __RPC_STUB IWMVideoMediaProps_GetMaxKeyFrameSpacing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMVideoMediaProps_SetMaxKeyFrameSpacing_Proxy( + IWMVideoMediaProps * This, + /* [in] */ LONGLONG llTime); + + +void __RPC_STUB IWMVideoMediaProps_SetMaxKeyFrameSpacing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMVideoMediaProps_GetQuality_Proxy( + IWMVideoMediaProps * This, + /* [out] */ DWORD *pdwQuality); + + +void __RPC_STUB IWMVideoMediaProps_GetQuality_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMVideoMediaProps_SetQuality_Proxy( + IWMVideoMediaProps * This, + /* [in] */ DWORD dwQuality); + + +void __RPC_STUB IWMVideoMediaProps_SetQuality_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMVideoMediaProps_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriter_INTERFACE_DEFINED__ +#define __IWMWriter_INTERFACE_DEFINED__ + +/* interface IWMWriter */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriter; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BD4-2B2B-11d3-B36B-00C04F6108FF") + IWMWriter : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetProfileByID( + /* [in] */ REFGUID guidProfile) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProfile( + /* [in] */ IWMProfile *pProfile) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputFilename( + /* [in] */ const WCHAR *pwszFilename) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputCount( + /* [out] */ DWORD *pcInputs) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputProps( + /* [in] */ DWORD dwInputNum, + /* [out] */ IWMInputMediaProps **ppInput) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetInputProps( + /* [in] */ DWORD dwInputNum, + /* [in] */ IWMInputMediaProps *pInput) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputFormatCount( + /* [in] */ DWORD dwInputNumber, + /* [out] */ DWORD *pcFormats) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputFormat( + /* [in] */ DWORD dwInputNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMInputMediaProps **pProps) = 0; + + virtual HRESULT STDMETHODCALLTYPE BeginWriting( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE EndWriting( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocateSample( + /* [in] */ DWORD dwSampleSize, + /* [out] */ INSSBuffer **ppSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteSample( + /* [in] */ DWORD dwInputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE Flush( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriter * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriter * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriter * This); + + HRESULT ( STDMETHODCALLTYPE *SetProfileByID )( + IWMWriter * This, + /* [in] */ REFGUID guidProfile); + + HRESULT ( STDMETHODCALLTYPE *SetProfile )( + IWMWriter * This, + /* [in] */ IWMProfile *pProfile); + + HRESULT ( STDMETHODCALLTYPE *SetOutputFilename )( + IWMWriter * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *GetInputCount )( + IWMWriter * This, + /* [out] */ DWORD *pcInputs); + + HRESULT ( STDMETHODCALLTYPE *GetInputProps )( + IWMWriter * This, + /* [in] */ DWORD dwInputNum, + /* [out] */ IWMInputMediaProps **ppInput); + + HRESULT ( STDMETHODCALLTYPE *SetInputProps )( + IWMWriter * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ IWMInputMediaProps *pInput); + + HRESULT ( STDMETHODCALLTYPE *GetInputFormatCount )( + IWMWriter * This, + /* [in] */ DWORD dwInputNumber, + /* [out] */ DWORD *pcFormats); + + HRESULT ( STDMETHODCALLTYPE *GetInputFormat )( + IWMWriter * This, + /* [in] */ DWORD dwInputNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMInputMediaProps **pProps); + + HRESULT ( STDMETHODCALLTYPE *BeginWriting )( + IWMWriter * This); + + HRESULT ( STDMETHODCALLTYPE *EndWriting )( + IWMWriter * This); + + HRESULT ( STDMETHODCALLTYPE *AllocateSample )( + IWMWriter * This, + /* [in] */ DWORD dwSampleSize, + /* [out] */ INSSBuffer **ppSample); + + HRESULT ( STDMETHODCALLTYPE *WriteSample )( + IWMWriter * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + HRESULT ( STDMETHODCALLTYPE *Flush )( + IWMWriter * This); + + END_INTERFACE + } IWMWriterVtbl; + + interface IWMWriter + { + CONST_VTBL struct IWMWriterVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriter_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriter_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriter_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriter_SetProfileByID(This,guidProfile) \ + (This)->lpVtbl -> SetProfileByID(This,guidProfile) + +#define IWMWriter_SetProfile(This,pProfile) \ + (This)->lpVtbl -> SetProfile(This,pProfile) + +#define IWMWriter_SetOutputFilename(This,pwszFilename) \ + (This)->lpVtbl -> SetOutputFilename(This,pwszFilename) + +#define IWMWriter_GetInputCount(This,pcInputs) \ + (This)->lpVtbl -> GetInputCount(This,pcInputs) + +#define IWMWriter_GetInputProps(This,dwInputNum,ppInput) \ + (This)->lpVtbl -> GetInputProps(This,dwInputNum,ppInput) + +#define IWMWriter_SetInputProps(This,dwInputNum,pInput) \ + (This)->lpVtbl -> SetInputProps(This,dwInputNum,pInput) + +#define IWMWriter_GetInputFormatCount(This,dwInputNumber,pcFormats) \ + (This)->lpVtbl -> GetInputFormatCount(This,dwInputNumber,pcFormats) + +#define IWMWriter_GetInputFormat(This,dwInputNumber,dwFormatNumber,pProps) \ + (This)->lpVtbl -> GetInputFormat(This,dwInputNumber,dwFormatNumber,pProps) + +#define IWMWriter_BeginWriting(This) \ + (This)->lpVtbl -> BeginWriting(This) + +#define IWMWriter_EndWriting(This) \ + (This)->lpVtbl -> EndWriting(This) + +#define IWMWriter_AllocateSample(This,dwSampleSize,ppSample) \ + (This)->lpVtbl -> AllocateSample(This,dwSampleSize,ppSample) + +#define IWMWriter_WriteSample(This,dwInputNum,cnsSampleTime,dwFlags,pSample) \ + (This)->lpVtbl -> WriteSample(This,dwInputNum,cnsSampleTime,dwFlags,pSample) + +#define IWMWriter_Flush(This) \ + (This)->lpVtbl -> Flush(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriter_SetProfileByID_Proxy( + IWMWriter * This, + /* [in] */ REFGUID guidProfile); + + +void __RPC_STUB IWMWriter_SetProfileByID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_SetProfile_Proxy( + IWMWriter * This, + /* [in] */ IWMProfile *pProfile); + + +void __RPC_STUB IWMWriter_SetProfile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_SetOutputFilename_Proxy( + IWMWriter * This, + /* [in] */ const WCHAR *pwszFilename); + + +void __RPC_STUB IWMWriter_SetOutputFilename_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_GetInputCount_Proxy( + IWMWriter * This, + /* [out] */ DWORD *pcInputs); + + +void __RPC_STUB IWMWriter_GetInputCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_GetInputProps_Proxy( + IWMWriter * This, + /* [in] */ DWORD dwInputNum, + /* [out] */ IWMInputMediaProps **ppInput); + + +void __RPC_STUB IWMWriter_GetInputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_SetInputProps_Proxy( + IWMWriter * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ IWMInputMediaProps *pInput); + + +void __RPC_STUB IWMWriter_SetInputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_GetInputFormatCount_Proxy( + IWMWriter * This, + /* [in] */ DWORD dwInputNumber, + /* [out] */ DWORD *pcFormats); + + +void __RPC_STUB IWMWriter_GetInputFormatCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_GetInputFormat_Proxy( + IWMWriter * This, + /* [in] */ DWORD dwInputNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMInputMediaProps **pProps); + + +void __RPC_STUB IWMWriter_GetInputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_BeginWriting_Proxy( + IWMWriter * This); + + +void __RPC_STUB IWMWriter_BeginWriting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_EndWriting_Proxy( + IWMWriter * This); + + +void __RPC_STUB IWMWriter_EndWriting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_AllocateSample_Proxy( + IWMWriter * This, + /* [in] */ DWORD dwSampleSize, + /* [out] */ INSSBuffer **ppSample); + + +void __RPC_STUB IWMWriter_AllocateSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_WriteSample_Proxy( + IWMWriter * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + +void __RPC_STUB IWMWriter_WriteSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriter_Flush_Proxy( + IWMWriter * This); + + +void __RPC_STUB IWMWriter_Flush_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriter_INTERFACE_DEFINED__ */ + + +#ifndef __IWMDRMWriter_INTERFACE_DEFINED__ +#define __IWMDRMWriter_INTERFACE_DEFINED__ + +/* interface IWMDRMWriter */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMDRMWriter; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("d6ea5dd0-12a0-43f4-90ab-a3fd451e6a07") + IWMDRMWriter : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GenerateKeySeed( + /* [size_is][out] */ WCHAR *pwszKeySeed, + /* [out][in] */ DWORD *pcwchLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GenerateKeyID( + /* [size_is][out] */ WCHAR *pwszKeyID, + /* [out][in] */ DWORD *pcwchLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GenerateSigningKeyPair( + /* [size_is][out] */ WCHAR *pwszPrivKey, + /* [out][in] */ DWORD *pcwchPrivKeyLength, + /* [size_is][out] */ WCHAR *pwszPubKey, + /* [out][in] */ DWORD *pcwchPubKeyLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetDRMAttribute( + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMDRMWriterVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMDRMWriter * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMDRMWriter * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMDRMWriter * This); + + HRESULT ( STDMETHODCALLTYPE *GenerateKeySeed )( + IWMDRMWriter * This, + /* [size_is][out] */ WCHAR *pwszKeySeed, + /* [out][in] */ DWORD *pcwchLength); + + HRESULT ( STDMETHODCALLTYPE *GenerateKeyID )( + IWMDRMWriter * This, + /* [size_is][out] */ WCHAR *pwszKeyID, + /* [out][in] */ DWORD *pcwchLength); + + HRESULT ( STDMETHODCALLTYPE *GenerateSigningKeyPair )( + IWMDRMWriter * This, + /* [size_is][out] */ WCHAR *pwszPrivKey, + /* [out][in] */ DWORD *pcwchPrivKeyLength, + /* [size_is][out] */ WCHAR *pwszPubKey, + /* [out][in] */ DWORD *pcwchPubKeyLength); + + HRESULT ( STDMETHODCALLTYPE *SetDRMAttribute )( + IWMDRMWriter * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + END_INTERFACE + } IWMDRMWriterVtbl; + + interface IWMDRMWriter + { + CONST_VTBL struct IWMDRMWriterVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMDRMWriter_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMDRMWriter_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMDRMWriter_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMDRMWriter_GenerateKeySeed(This,pwszKeySeed,pcwchLength) \ + (This)->lpVtbl -> GenerateKeySeed(This,pwszKeySeed,pcwchLength) + +#define IWMDRMWriter_GenerateKeyID(This,pwszKeyID,pcwchLength) \ + (This)->lpVtbl -> GenerateKeyID(This,pwszKeyID,pcwchLength) + +#define IWMDRMWriter_GenerateSigningKeyPair(This,pwszPrivKey,pcwchPrivKeyLength,pwszPubKey,pcwchPubKeyLength) \ + (This)->lpVtbl -> GenerateSigningKeyPair(This,pwszPrivKey,pcwchPrivKeyLength,pwszPubKey,pcwchPubKeyLength) + +#define IWMDRMWriter_SetDRMAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetDRMAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMDRMWriter_GenerateKeySeed_Proxy( + IWMDRMWriter * This, + /* [size_is][out] */ WCHAR *pwszKeySeed, + /* [out][in] */ DWORD *pcwchLength); + + +void __RPC_STUB IWMDRMWriter_GenerateKeySeed_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMWriter_GenerateKeyID_Proxy( + IWMDRMWriter * This, + /* [size_is][out] */ WCHAR *pwszKeyID, + /* [out][in] */ DWORD *pcwchLength); + + +void __RPC_STUB IWMDRMWriter_GenerateKeyID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMWriter_GenerateSigningKeyPair_Proxy( + IWMDRMWriter * This, + /* [size_is][out] */ WCHAR *pwszPrivKey, + /* [out][in] */ DWORD *pcwchPrivKeyLength, + /* [size_is][out] */ WCHAR *pwszPubKey, + /* [out][in] */ DWORD *pcwchPubKeyLength); + + +void __RPC_STUB IWMDRMWriter_GenerateSigningKeyPair_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMWriter_SetDRMAttribute_Proxy( + IWMDRMWriter * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMDRMWriter_SetDRMAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMDRMWriter_INTERFACE_DEFINED__ */ + + +#ifndef __IWMInputMediaProps_INTERFACE_DEFINED__ +#define __IWMInputMediaProps_INTERFACE_DEFINED__ + +/* interface IWMInputMediaProps */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMInputMediaProps; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BD5-2B2B-11d3-B36B-00C04F6108FF") + IWMInputMediaProps : public IWMMediaProps + { + public: + virtual HRESULT STDMETHODCALLTYPE GetConnectionName( + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGroupName( + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMInputMediaPropsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMInputMediaProps * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMInputMediaProps * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMInputMediaProps * This); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMInputMediaProps * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *GetMediaType )( + IWMInputMediaProps * This, + /* [out] */ WM_MEDIA_TYPE *pType, + /* [out][in] */ DWORD *pcbType); + + HRESULT ( STDMETHODCALLTYPE *SetMediaType )( + IWMInputMediaProps * This, + /* [in] */ WM_MEDIA_TYPE *pType); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionName )( + IWMInputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *GetGroupName )( + IWMInputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + END_INTERFACE + } IWMInputMediaPropsVtbl; + + interface IWMInputMediaProps + { + CONST_VTBL struct IWMInputMediaPropsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMInputMediaProps_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMInputMediaProps_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMInputMediaProps_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMInputMediaProps_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMInputMediaProps_GetMediaType(This,pType,pcbType) \ + (This)->lpVtbl -> GetMediaType(This,pType,pcbType) + +#define IWMInputMediaProps_SetMediaType(This,pType) \ + (This)->lpVtbl -> SetMediaType(This,pType) + + +#define IWMInputMediaProps_GetConnectionName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetConnectionName(This,pwszName,pcchName) + +#define IWMInputMediaProps_GetGroupName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetGroupName(This,pwszName,pcchName) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMInputMediaProps_GetConnectionName_Proxy( + IWMInputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + +void __RPC_STUB IWMInputMediaProps_GetConnectionName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMInputMediaProps_GetGroupName_Proxy( + IWMInputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + +void __RPC_STUB IWMInputMediaProps_GetGroupName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMInputMediaProps_INTERFACE_DEFINED__ */ + + +#ifndef __IWMPropertyVault_INTERFACE_DEFINED__ +#define __IWMPropertyVault_INTERFACE_DEFINED__ + +/* interface IWMPropertyVault */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMPropertyVault; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("72995A79-5090-42a4-9C8C-D9D0B6D34BE5") + IWMPropertyVault : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetPropertyCount( + /* [in] */ DWORD *pdwCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyByName( + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProperty( + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE pType, + /* [in] */ BYTE *pValue, + /* [in] */ DWORD dwSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyByIndex( + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pszName, + /* [out][in] */ DWORD *pdwNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE CopyPropertiesFrom( + /* [in] */ IWMPropertyVault *pIWMPropertyVault) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clear( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMPropertyVaultVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMPropertyVault * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMPropertyVault * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMPropertyVault * This); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyCount )( + IWMPropertyVault * This, + /* [in] */ DWORD *pdwCount); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyByName )( + IWMPropertyVault * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + HRESULT ( STDMETHODCALLTYPE *SetProperty )( + IWMPropertyVault * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE pType, + /* [in] */ BYTE *pValue, + /* [in] */ DWORD dwSize); + + HRESULT ( STDMETHODCALLTYPE *GetPropertyByIndex )( + IWMPropertyVault * This, + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pszName, + /* [out][in] */ DWORD *pdwNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + HRESULT ( STDMETHODCALLTYPE *CopyPropertiesFrom )( + IWMPropertyVault * This, + /* [in] */ IWMPropertyVault *pIWMPropertyVault); + + HRESULT ( STDMETHODCALLTYPE *Clear )( + IWMPropertyVault * This); + + END_INTERFACE + } IWMPropertyVaultVtbl; + + interface IWMPropertyVault + { + CONST_VTBL struct IWMPropertyVaultVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMPropertyVault_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMPropertyVault_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMPropertyVault_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMPropertyVault_GetPropertyCount(This,pdwCount) \ + (This)->lpVtbl -> GetPropertyCount(This,pdwCount) + +#define IWMPropertyVault_GetPropertyByName(This,pszName,pType,pValue,pdwSize) \ + (This)->lpVtbl -> GetPropertyByName(This,pszName,pType,pValue,pdwSize) + +#define IWMPropertyVault_SetProperty(This,pszName,pType,pValue,dwSize) \ + (This)->lpVtbl -> SetProperty(This,pszName,pType,pValue,dwSize) + +#define IWMPropertyVault_GetPropertyByIndex(This,dwIndex,pszName,pdwNameLen,pType,pValue,pdwSize) \ + (This)->lpVtbl -> GetPropertyByIndex(This,dwIndex,pszName,pdwNameLen,pType,pValue,pdwSize) + +#define IWMPropertyVault_CopyPropertiesFrom(This,pIWMPropertyVault) \ + (This)->lpVtbl -> CopyPropertiesFrom(This,pIWMPropertyVault) + +#define IWMPropertyVault_Clear(This) \ + (This)->lpVtbl -> Clear(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMPropertyVault_GetPropertyCount_Proxy( + IWMPropertyVault * This, + /* [in] */ DWORD *pdwCount); + + +void __RPC_STUB IWMPropertyVault_GetPropertyCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPropertyVault_GetPropertyByName_Proxy( + IWMPropertyVault * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + +void __RPC_STUB IWMPropertyVault_GetPropertyByName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPropertyVault_SetProperty_Proxy( + IWMPropertyVault * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE pType, + /* [in] */ BYTE *pValue, + /* [in] */ DWORD dwSize); + + +void __RPC_STUB IWMPropertyVault_SetProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPropertyVault_GetPropertyByIndex_Proxy( + IWMPropertyVault * This, + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pszName, + /* [out][in] */ DWORD *pdwNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + +void __RPC_STUB IWMPropertyVault_GetPropertyByIndex_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPropertyVault_CopyPropertiesFrom_Proxy( + IWMPropertyVault * This, + /* [in] */ IWMPropertyVault *pIWMPropertyVault); + + +void __RPC_STUB IWMPropertyVault_CopyPropertiesFrom_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPropertyVault_Clear_Proxy( + IWMPropertyVault * This); + + +void __RPC_STUB IWMPropertyVault_Clear_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMPropertyVault_INTERFACE_DEFINED__ */ + + +#ifndef __IWMIStreamProps_INTERFACE_DEFINED__ +#define __IWMIStreamProps_INTERFACE_DEFINED__ + +/* interface IWMIStreamProps */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMIStreamProps; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("6816dad3-2b4b-4c8e-8149-874c3483a753") + IWMIStreamProps : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetProperty( + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMIStreamPropsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMIStreamProps * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMIStreamProps * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMIStreamProps * This); + + HRESULT ( STDMETHODCALLTYPE *GetProperty )( + IWMIStreamProps * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + END_INTERFACE + } IWMIStreamPropsVtbl; + + interface IWMIStreamProps + { + CONST_VTBL struct IWMIStreamPropsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMIStreamProps_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMIStreamProps_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMIStreamProps_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMIStreamProps_GetProperty(This,pszName,pType,pValue,pdwSize) \ + (This)->lpVtbl -> GetProperty(This,pszName,pType,pValue,pdwSize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMIStreamProps_GetProperty_Proxy( + IWMIStreamProps * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + +void __RPC_STUB IWMIStreamProps_GetProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMIStreamProps_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReader_INTERFACE_DEFINED__ +#define __IWMReader_INTERFACE_DEFINED__ + +/* interface IWMReader */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReader; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BD6-2B2B-11d3-B36B-00C04F6108FF") + IWMReader : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputCount( + /* [out] */ DWORD *pcOutputs) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputProps( + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputProps( + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputFormatCount( + /* [in] */ DWORD dwOutputNumber, + /* [out] */ DWORD *pcFormats) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputFormat( + /* [in] */ DWORD dwOutputNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMOutputMediaProps **ppProps) = 0; + + virtual HRESULT STDMETHODCALLTYPE Start( + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Pause( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Resume( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReader * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReader * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReader * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMReader * This, + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMReader * This); + + HRESULT ( STDMETHODCALLTYPE *GetOutputCount )( + IWMReader * This, + /* [out] */ DWORD *pcOutputs); + + HRESULT ( STDMETHODCALLTYPE *GetOutputProps )( + IWMReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput); + + HRESULT ( STDMETHODCALLTYPE *SetOutputProps )( + IWMReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + HRESULT ( STDMETHODCALLTYPE *GetOutputFormatCount )( + IWMReader * This, + /* [in] */ DWORD dwOutputNumber, + /* [out] */ DWORD *pcFormats); + + HRESULT ( STDMETHODCALLTYPE *GetOutputFormat )( + IWMReader * This, + /* [in] */ DWORD dwOutputNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMOutputMediaProps **ppProps); + + HRESULT ( STDMETHODCALLTYPE *Start )( + IWMReader * This, + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + IWMReader * This); + + HRESULT ( STDMETHODCALLTYPE *Pause )( + IWMReader * This); + + HRESULT ( STDMETHODCALLTYPE *Resume )( + IWMReader * This); + + END_INTERFACE + } IWMReaderVtbl; + + interface IWMReader + { + CONST_VTBL struct IWMReaderVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReader_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReader_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReader_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReader_Open(This,pwszURL,pCallback,pvContext) \ + (This)->lpVtbl -> Open(This,pwszURL,pCallback,pvContext) + +#define IWMReader_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMReader_GetOutputCount(This,pcOutputs) \ + (This)->lpVtbl -> GetOutputCount(This,pcOutputs) + +#define IWMReader_GetOutputProps(This,dwOutputNum,ppOutput) \ + (This)->lpVtbl -> GetOutputProps(This,dwOutputNum,ppOutput) + +#define IWMReader_SetOutputProps(This,dwOutputNum,pOutput) \ + (This)->lpVtbl -> SetOutputProps(This,dwOutputNum,pOutput) + +#define IWMReader_GetOutputFormatCount(This,dwOutputNumber,pcFormats) \ + (This)->lpVtbl -> GetOutputFormatCount(This,dwOutputNumber,pcFormats) + +#define IWMReader_GetOutputFormat(This,dwOutputNumber,dwFormatNumber,ppProps) \ + (This)->lpVtbl -> GetOutputFormat(This,dwOutputNumber,dwFormatNumber,ppProps) + +#define IWMReader_Start(This,cnsStart,cnsDuration,fRate,pvContext) \ + (This)->lpVtbl -> Start(This,cnsStart,cnsDuration,fRate,pvContext) + +#define IWMReader_Stop(This) \ + (This)->lpVtbl -> Stop(This) + +#define IWMReader_Pause(This) \ + (This)->lpVtbl -> Pause(This) + +#define IWMReader_Resume(This) \ + (This)->lpVtbl -> Resume(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReader_Open_Proxy( + IWMReader * This, + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReader_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_Close_Proxy( + IWMReader * This); + + +void __RPC_STUB IWMReader_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_GetOutputCount_Proxy( + IWMReader * This, + /* [out] */ DWORD *pcOutputs); + + +void __RPC_STUB IWMReader_GetOutputCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_GetOutputProps_Proxy( + IWMReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput); + + +void __RPC_STUB IWMReader_GetOutputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_SetOutputProps_Proxy( + IWMReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + +void __RPC_STUB IWMReader_SetOutputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_GetOutputFormatCount_Proxy( + IWMReader * This, + /* [in] */ DWORD dwOutputNumber, + /* [out] */ DWORD *pcFormats); + + +void __RPC_STUB IWMReader_GetOutputFormatCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_GetOutputFormat_Proxy( + IWMReader * This, + /* [in] */ DWORD dwOutputNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMOutputMediaProps **ppProps); + + +void __RPC_STUB IWMReader_GetOutputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_Start_Proxy( + IWMReader * This, + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReader_Start_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_Stop_Proxy( + IWMReader * This); + + +void __RPC_STUB IWMReader_Stop_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_Pause_Proxy( + IWMReader * This); + + +void __RPC_STUB IWMReader_Pause_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReader_Resume_Proxy( + IWMReader * This); + + +void __RPC_STUB IWMReader_Resume_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReader_INTERFACE_DEFINED__ */ + + +#ifndef __IWMSyncReader_INTERFACE_DEFINED__ +#define __IWMSyncReader_INTERFACE_DEFINED__ + +/* interface IWMSyncReader */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMSyncReader; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9397F121-7705-4dc9-B049-98B698188414") + IWMSyncReader : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ const WCHAR *pwszFilename) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetRange( + /* [in] */ QWORD cnsStartTime, + /* [in] */ LONGLONG cnsDuration) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetRangeByFrame( + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetNextSample( + /* [in] */ WORD wStreamNum, + /* [out] */ INSSBuffer **ppSample, + /* [out] */ QWORD *pcnsSampleTime, + /* [out] */ QWORD *pcnsDuration, + /* [out] */ DWORD *pdwFlags, + /* [out] */ DWORD *pdwOutputNum, + /* [out] */ WORD *pwStreamNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetStreamsSelected( + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamSelected( + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetReadStreamSamples( + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fCompressed) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetReadStreamSamples( + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfCompressed) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputSetting( + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputSetting( + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputCount( + /* [out] */ DWORD *pcOutputs) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputProps( + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputProps( + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputFormatCount( + /* [in] */ DWORD dwOutputNum, + /* [out] */ DWORD *pcFormats) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputFormat( + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD dwFormatNum, + /* [out] */ IWMOutputMediaProps **ppProps) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputNumberForStream( + /* [in] */ WORD wStreamNum, + /* [out] */ DWORD *pdwOutputNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamNumberForOutput( + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwStreamNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxOutputSampleSize( + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxStreamSampleSize( + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax) = 0; + + virtual HRESULT STDMETHODCALLTYPE OpenStream( + /* [in] */ IStream *pStream) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMSyncReaderVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMSyncReader * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMSyncReader * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMSyncReader * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMSyncReader * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMSyncReader * This); + + HRESULT ( STDMETHODCALLTYPE *SetRange )( + IWMSyncReader * This, + /* [in] */ QWORD cnsStartTime, + /* [in] */ LONGLONG cnsDuration); + + HRESULT ( STDMETHODCALLTYPE *SetRangeByFrame )( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead); + + HRESULT ( STDMETHODCALLTYPE *GetNextSample )( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ INSSBuffer **ppSample, + /* [out] */ QWORD *pcnsSampleTime, + /* [out] */ QWORD *pcnsDuration, + /* [out] */ DWORD *pdwFlags, + /* [out] */ DWORD *pdwOutputNum, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *SetStreamsSelected )( + IWMSyncReader * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + HRESULT ( STDMETHODCALLTYPE *GetStreamSelected )( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + HRESULT ( STDMETHODCALLTYPE *SetReadStreamSamples )( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fCompressed); + + HRESULT ( STDMETHODCALLTYPE *GetReadStreamSamples )( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfCompressed); + + HRESULT ( STDMETHODCALLTYPE *GetOutputSetting )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetOutputSetting )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetOutputCount )( + IWMSyncReader * This, + /* [out] */ DWORD *pcOutputs); + + HRESULT ( STDMETHODCALLTYPE *GetOutputProps )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput); + + HRESULT ( STDMETHODCALLTYPE *SetOutputProps )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + HRESULT ( STDMETHODCALLTYPE *GetOutputFormatCount )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ DWORD *pcFormats); + + HRESULT ( STDMETHODCALLTYPE *GetOutputFormat )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD dwFormatNum, + /* [out] */ IWMOutputMediaProps **ppProps); + + HRESULT ( STDMETHODCALLTYPE *GetOutputNumberForStream )( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ DWORD *pdwOutputNum); + + HRESULT ( STDMETHODCALLTYPE *GetStreamNumberForOutput )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetMaxOutputSampleSize )( + IWMSyncReader * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *GetMaxStreamSampleSize )( + IWMSyncReader * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *OpenStream )( + IWMSyncReader * This, + /* [in] */ IStream *pStream); + + END_INTERFACE + } IWMSyncReaderVtbl; + + interface IWMSyncReader + { + CONST_VTBL struct IWMSyncReaderVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMSyncReader_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMSyncReader_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMSyncReader_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMSyncReader_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + +#define IWMSyncReader_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMSyncReader_SetRange(This,cnsStartTime,cnsDuration) \ + (This)->lpVtbl -> SetRange(This,cnsStartTime,cnsDuration) + +#define IWMSyncReader_SetRangeByFrame(This,wStreamNum,qwFrameNumber,cFramesToRead) \ + (This)->lpVtbl -> SetRangeByFrame(This,wStreamNum,qwFrameNumber,cFramesToRead) + +#define IWMSyncReader_GetNextSample(This,wStreamNum,ppSample,pcnsSampleTime,pcnsDuration,pdwFlags,pdwOutputNum,pwStreamNum) \ + (This)->lpVtbl -> GetNextSample(This,wStreamNum,ppSample,pcnsSampleTime,pcnsDuration,pdwFlags,pdwOutputNum,pwStreamNum) + +#define IWMSyncReader_SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) \ + (This)->lpVtbl -> SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) + +#define IWMSyncReader_GetStreamSelected(This,wStreamNum,pSelection) \ + (This)->lpVtbl -> GetStreamSelected(This,wStreamNum,pSelection) + +#define IWMSyncReader_SetReadStreamSamples(This,wStreamNum,fCompressed) \ + (This)->lpVtbl -> SetReadStreamSamples(This,wStreamNum,fCompressed) + +#define IWMSyncReader_GetReadStreamSamples(This,wStreamNum,pfCompressed) \ + (This)->lpVtbl -> GetReadStreamSamples(This,wStreamNum,pfCompressed) + +#define IWMSyncReader_GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) + +#define IWMSyncReader_SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) + +#define IWMSyncReader_GetOutputCount(This,pcOutputs) \ + (This)->lpVtbl -> GetOutputCount(This,pcOutputs) + +#define IWMSyncReader_GetOutputProps(This,dwOutputNum,ppOutput) \ + (This)->lpVtbl -> GetOutputProps(This,dwOutputNum,ppOutput) + +#define IWMSyncReader_SetOutputProps(This,dwOutputNum,pOutput) \ + (This)->lpVtbl -> SetOutputProps(This,dwOutputNum,pOutput) + +#define IWMSyncReader_GetOutputFormatCount(This,dwOutputNum,pcFormats) \ + (This)->lpVtbl -> GetOutputFormatCount(This,dwOutputNum,pcFormats) + +#define IWMSyncReader_GetOutputFormat(This,dwOutputNum,dwFormatNum,ppProps) \ + (This)->lpVtbl -> GetOutputFormat(This,dwOutputNum,dwFormatNum,ppProps) + +#define IWMSyncReader_GetOutputNumberForStream(This,wStreamNum,pdwOutputNum) \ + (This)->lpVtbl -> GetOutputNumberForStream(This,wStreamNum,pdwOutputNum) + +#define IWMSyncReader_GetStreamNumberForOutput(This,dwOutputNum,pwStreamNum) \ + (This)->lpVtbl -> GetStreamNumberForOutput(This,dwOutputNum,pwStreamNum) + +#define IWMSyncReader_GetMaxOutputSampleSize(This,dwOutput,pcbMax) \ + (This)->lpVtbl -> GetMaxOutputSampleSize(This,dwOutput,pcbMax) + +#define IWMSyncReader_GetMaxStreamSampleSize(This,wStream,pcbMax) \ + (This)->lpVtbl -> GetMaxStreamSampleSize(This,wStream,pcbMax) + +#define IWMSyncReader_OpenStream(This,pStream) \ + (This)->lpVtbl -> OpenStream(This,pStream) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_Open_Proxy( + IWMSyncReader * This, + /* [in] */ const WCHAR *pwszFilename); + + +void __RPC_STUB IWMSyncReader_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_Close_Proxy( + IWMSyncReader * This); + + +void __RPC_STUB IWMSyncReader_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_SetRange_Proxy( + IWMSyncReader * This, + /* [in] */ QWORD cnsStartTime, + /* [in] */ LONGLONG cnsDuration); + + +void __RPC_STUB IWMSyncReader_SetRange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_SetRangeByFrame_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead); + + +void __RPC_STUB IWMSyncReader_SetRangeByFrame_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetNextSample_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ INSSBuffer **ppSample, + /* [out] */ QWORD *pcnsSampleTime, + /* [out] */ QWORD *pcnsDuration, + /* [out] */ DWORD *pdwFlags, + /* [out] */ DWORD *pdwOutputNum, + /* [out] */ WORD *pwStreamNum); + + +void __RPC_STUB IWMSyncReader_GetNextSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_SetStreamsSelected_Proxy( + IWMSyncReader * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + +void __RPC_STUB IWMSyncReader_SetStreamsSelected_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetStreamSelected_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + +void __RPC_STUB IWMSyncReader_GetStreamSelected_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_SetReadStreamSamples_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fCompressed); + + +void __RPC_STUB IWMSyncReader_SetReadStreamSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetReadStreamSamples_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfCompressed); + + +void __RPC_STUB IWMSyncReader_GetReadStreamSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetOutputSetting_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMSyncReader_GetOutputSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_SetOutputSetting_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMSyncReader_SetOutputSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetOutputCount_Proxy( + IWMSyncReader * This, + /* [out] */ DWORD *pcOutputs); + + +void __RPC_STUB IWMSyncReader_GetOutputCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetOutputProps_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput); + + +void __RPC_STUB IWMSyncReader_GetOutputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_SetOutputProps_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + +void __RPC_STUB IWMSyncReader_SetOutputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetOutputFormatCount_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ DWORD *pcFormats); + + +void __RPC_STUB IWMSyncReader_GetOutputFormatCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetOutputFormat_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD dwFormatNum, + /* [out] */ IWMOutputMediaProps **ppProps); + + +void __RPC_STUB IWMSyncReader_GetOutputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetOutputNumberForStream_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStreamNum, + /* [out] */ DWORD *pdwOutputNum); + + +void __RPC_STUB IWMSyncReader_GetOutputNumberForStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetStreamNumberForOutput_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwStreamNum); + + +void __RPC_STUB IWMSyncReader_GetStreamNumberForOutput_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetMaxOutputSampleSize_Proxy( + IWMSyncReader * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + +void __RPC_STUB IWMSyncReader_GetMaxOutputSampleSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_GetMaxStreamSampleSize_Proxy( + IWMSyncReader * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + +void __RPC_STUB IWMSyncReader_GetMaxStreamSampleSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader_OpenStream_Proxy( + IWMSyncReader * This, + /* [in] */ IStream *pStream); + + +void __RPC_STUB IWMSyncReader_OpenStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMSyncReader_INTERFACE_DEFINED__ */ + + +#ifndef __IWMSyncReader2_INTERFACE_DEFINED__ +#define __IWMSyncReader2_INTERFACE_DEFINED__ + +/* interface IWMSyncReader2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMSyncReader2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("faed3d21-1b6b-4af7-8cb6-3e189bbc187b") + IWMSyncReader2 : public IWMSyncReader + { + public: + virtual HRESULT STDMETHODCALLTYPE SetRangeByTimecode( + /* [in] */ WORD wStreamNum, + /* [in] */ WMT_TIMECODE_EXTENSION_DATA *pStart, + /* [in] */ WMT_TIMECODE_EXTENSION_DATA *pEnd) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetRangeByFrameEx( + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead, + /* [out] */ QWORD *pcnsStartTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAllocateForOutput( + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMReaderAllocatorEx *pAllocator) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAllocateForOutput( + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMReaderAllocatorEx **ppAllocator) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAllocateForStream( + /* [in] */ WORD wStreamNum, + /* [in] */ IWMReaderAllocatorEx *pAllocator) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAllocateForStream( + /* [in] */ WORD dwSreamNum, + /* [out] */ IWMReaderAllocatorEx **ppAllocator) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMSyncReader2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMSyncReader2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMSyncReader2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMSyncReader2 * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMSyncReader2 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMSyncReader2 * This); + + HRESULT ( STDMETHODCALLTYPE *SetRange )( + IWMSyncReader2 * This, + /* [in] */ QWORD cnsStartTime, + /* [in] */ LONGLONG cnsDuration); + + HRESULT ( STDMETHODCALLTYPE *SetRangeByFrame )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead); + + HRESULT ( STDMETHODCALLTYPE *GetNextSample )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ INSSBuffer **ppSample, + /* [out] */ QWORD *pcnsSampleTime, + /* [out] */ QWORD *pcnsDuration, + /* [out] */ DWORD *pdwFlags, + /* [out] */ DWORD *pdwOutputNum, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *SetStreamsSelected )( + IWMSyncReader2 * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + HRESULT ( STDMETHODCALLTYPE *GetStreamSelected )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + HRESULT ( STDMETHODCALLTYPE *SetReadStreamSamples )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fCompressed); + + HRESULT ( STDMETHODCALLTYPE *GetReadStreamSamples )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfCompressed); + + HRESULT ( STDMETHODCALLTYPE *GetOutputSetting )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetOutputSetting )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetOutputCount )( + IWMSyncReader2 * This, + /* [out] */ DWORD *pcOutputs); + + HRESULT ( STDMETHODCALLTYPE *GetOutputProps )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMOutputMediaProps **ppOutput); + + HRESULT ( STDMETHODCALLTYPE *SetOutputProps )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + HRESULT ( STDMETHODCALLTYPE *GetOutputFormatCount )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ DWORD *pcFormats); + + HRESULT ( STDMETHODCALLTYPE *GetOutputFormat )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD dwFormatNum, + /* [out] */ IWMOutputMediaProps **ppProps); + + HRESULT ( STDMETHODCALLTYPE *GetOutputNumberForStream )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ DWORD *pdwOutputNum); + + HRESULT ( STDMETHODCALLTYPE *GetStreamNumberForOutput )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetMaxOutputSampleSize )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *GetMaxStreamSampleSize )( + IWMSyncReader2 * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *OpenStream )( + IWMSyncReader2 * This, + /* [in] */ IStream *pStream); + + HRESULT ( STDMETHODCALLTYPE *SetRangeByTimecode )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WMT_TIMECODE_EXTENSION_DATA *pStart, + /* [in] */ WMT_TIMECODE_EXTENSION_DATA *pEnd); + + HRESULT ( STDMETHODCALLTYPE *SetRangeByFrameEx )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead, + /* [out] */ QWORD *pcnsStartTime); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForOutput )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMReaderAllocatorEx *pAllocator); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForOutput )( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMReaderAllocatorEx **ppAllocator); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForStream )( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ IWMReaderAllocatorEx *pAllocator); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForStream )( + IWMSyncReader2 * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ IWMReaderAllocatorEx **ppAllocator); + + END_INTERFACE + } IWMSyncReader2Vtbl; + + interface IWMSyncReader2 + { + CONST_VTBL struct IWMSyncReader2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMSyncReader2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMSyncReader2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMSyncReader2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMSyncReader2_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + +#define IWMSyncReader2_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMSyncReader2_SetRange(This,cnsStartTime,cnsDuration) \ + (This)->lpVtbl -> SetRange(This,cnsStartTime,cnsDuration) + +#define IWMSyncReader2_SetRangeByFrame(This,wStreamNum,qwFrameNumber,cFramesToRead) \ + (This)->lpVtbl -> SetRangeByFrame(This,wStreamNum,qwFrameNumber,cFramesToRead) + +#define IWMSyncReader2_GetNextSample(This,wStreamNum,ppSample,pcnsSampleTime,pcnsDuration,pdwFlags,pdwOutputNum,pwStreamNum) \ + (This)->lpVtbl -> GetNextSample(This,wStreamNum,ppSample,pcnsSampleTime,pcnsDuration,pdwFlags,pdwOutputNum,pwStreamNum) + +#define IWMSyncReader2_SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) \ + (This)->lpVtbl -> SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) + +#define IWMSyncReader2_GetStreamSelected(This,wStreamNum,pSelection) \ + (This)->lpVtbl -> GetStreamSelected(This,wStreamNum,pSelection) + +#define IWMSyncReader2_SetReadStreamSamples(This,wStreamNum,fCompressed) \ + (This)->lpVtbl -> SetReadStreamSamples(This,wStreamNum,fCompressed) + +#define IWMSyncReader2_GetReadStreamSamples(This,wStreamNum,pfCompressed) \ + (This)->lpVtbl -> GetReadStreamSamples(This,wStreamNum,pfCompressed) + +#define IWMSyncReader2_GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) + +#define IWMSyncReader2_SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) + +#define IWMSyncReader2_GetOutputCount(This,pcOutputs) \ + (This)->lpVtbl -> GetOutputCount(This,pcOutputs) + +#define IWMSyncReader2_GetOutputProps(This,dwOutputNum,ppOutput) \ + (This)->lpVtbl -> GetOutputProps(This,dwOutputNum,ppOutput) + +#define IWMSyncReader2_SetOutputProps(This,dwOutputNum,pOutput) \ + (This)->lpVtbl -> SetOutputProps(This,dwOutputNum,pOutput) + +#define IWMSyncReader2_GetOutputFormatCount(This,dwOutputNum,pcFormats) \ + (This)->lpVtbl -> GetOutputFormatCount(This,dwOutputNum,pcFormats) + +#define IWMSyncReader2_GetOutputFormat(This,dwOutputNum,dwFormatNum,ppProps) \ + (This)->lpVtbl -> GetOutputFormat(This,dwOutputNum,dwFormatNum,ppProps) + +#define IWMSyncReader2_GetOutputNumberForStream(This,wStreamNum,pdwOutputNum) \ + (This)->lpVtbl -> GetOutputNumberForStream(This,wStreamNum,pdwOutputNum) + +#define IWMSyncReader2_GetStreamNumberForOutput(This,dwOutputNum,pwStreamNum) \ + (This)->lpVtbl -> GetStreamNumberForOutput(This,dwOutputNum,pwStreamNum) + +#define IWMSyncReader2_GetMaxOutputSampleSize(This,dwOutput,pcbMax) \ + (This)->lpVtbl -> GetMaxOutputSampleSize(This,dwOutput,pcbMax) + +#define IWMSyncReader2_GetMaxStreamSampleSize(This,wStream,pcbMax) \ + (This)->lpVtbl -> GetMaxStreamSampleSize(This,wStream,pcbMax) + +#define IWMSyncReader2_OpenStream(This,pStream) \ + (This)->lpVtbl -> OpenStream(This,pStream) + + +#define IWMSyncReader2_SetRangeByTimecode(This,wStreamNum,pStart,pEnd) \ + (This)->lpVtbl -> SetRangeByTimecode(This,wStreamNum,pStart,pEnd) + +#define IWMSyncReader2_SetRangeByFrameEx(This,wStreamNum,qwFrameNumber,cFramesToRead,pcnsStartTime) \ + (This)->lpVtbl -> SetRangeByFrameEx(This,wStreamNum,qwFrameNumber,cFramesToRead,pcnsStartTime) + +#define IWMSyncReader2_SetAllocateForOutput(This,dwOutputNum,pAllocator) \ + (This)->lpVtbl -> SetAllocateForOutput(This,dwOutputNum,pAllocator) + +#define IWMSyncReader2_GetAllocateForOutput(This,dwOutputNum,ppAllocator) \ + (This)->lpVtbl -> GetAllocateForOutput(This,dwOutputNum,ppAllocator) + +#define IWMSyncReader2_SetAllocateForStream(This,wStreamNum,pAllocator) \ + (This)->lpVtbl -> SetAllocateForStream(This,wStreamNum,pAllocator) + +#define IWMSyncReader2_GetAllocateForStream(This,dwSreamNum,ppAllocator) \ + (This)->lpVtbl -> GetAllocateForStream(This,dwSreamNum,ppAllocator) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMSyncReader2_SetRangeByTimecode_Proxy( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WMT_TIMECODE_EXTENSION_DATA *pStart, + /* [in] */ WMT_TIMECODE_EXTENSION_DATA *pEnd); + + +void __RPC_STUB IWMSyncReader2_SetRangeByTimecode_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader2_SetRangeByFrameEx_Proxy( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD qwFrameNumber, + /* [in] */ LONGLONG cFramesToRead, + /* [out] */ QWORD *pcnsStartTime); + + +void __RPC_STUB IWMSyncReader2_SetRangeByFrameEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader2_SetAllocateForOutput_Proxy( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMReaderAllocatorEx *pAllocator); + + +void __RPC_STUB IWMSyncReader2_SetAllocateForOutput_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader2_GetAllocateForOutput_Proxy( + IWMSyncReader2 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ IWMReaderAllocatorEx **ppAllocator); + + +void __RPC_STUB IWMSyncReader2_GetAllocateForOutput_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader2_SetAllocateForStream_Proxy( + IWMSyncReader2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ IWMReaderAllocatorEx *pAllocator); + + +void __RPC_STUB IWMSyncReader2_SetAllocateForStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMSyncReader2_GetAllocateForStream_Proxy( + IWMSyncReader2 * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ IWMReaderAllocatorEx **ppAllocator); + + +void __RPC_STUB IWMSyncReader2_GetAllocateForStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMSyncReader2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMOutputMediaProps_INTERFACE_DEFINED__ +#define __IWMOutputMediaProps_INTERFACE_DEFINED__ + +/* interface IWMOutputMediaProps */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMOutputMediaProps; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BD7-2B2B-11d3-B36B-00C04F6108FF") + IWMOutputMediaProps : public IWMMediaProps + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStreamGroupName( + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectionName( + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMOutputMediaPropsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMOutputMediaProps * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMOutputMediaProps * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMOutputMediaProps * This); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMOutputMediaProps * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *GetMediaType )( + IWMOutputMediaProps * This, + /* [out] */ WM_MEDIA_TYPE *pType, + /* [out][in] */ DWORD *pcbType); + + HRESULT ( STDMETHODCALLTYPE *SetMediaType )( + IWMOutputMediaProps * This, + /* [in] */ WM_MEDIA_TYPE *pType); + + HRESULT ( STDMETHODCALLTYPE *GetStreamGroupName )( + IWMOutputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionName )( + IWMOutputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + END_INTERFACE + } IWMOutputMediaPropsVtbl; + + interface IWMOutputMediaProps + { + CONST_VTBL struct IWMOutputMediaPropsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMOutputMediaProps_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMOutputMediaProps_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMOutputMediaProps_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMOutputMediaProps_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMOutputMediaProps_GetMediaType(This,pType,pcbType) \ + (This)->lpVtbl -> GetMediaType(This,pType,pcbType) + +#define IWMOutputMediaProps_SetMediaType(This,pType) \ + (This)->lpVtbl -> SetMediaType(This,pType) + + +#define IWMOutputMediaProps_GetStreamGroupName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetStreamGroupName(This,pwszName,pcchName) + +#define IWMOutputMediaProps_GetConnectionName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetConnectionName(This,pwszName,pcchName) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMOutputMediaProps_GetStreamGroupName_Proxy( + IWMOutputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + +void __RPC_STUB IWMOutputMediaProps_GetStreamGroupName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMOutputMediaProps_GetConnectionName_Proxy( + IWMOutputMediaProps * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + +void __RPC_STUB IWMOutputMediaProps_GetConnectionName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMOutputMediaProps_INTERFACE_DEFINED__ */ + + +#ifndef __IWMStatusCallback_INTERFACE_DEFINED__ +#define __IWMStatusCallback_INTERFACE_DEFINED__ + +/* interface IWMStatusCallback */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMStatusCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("6d7cdc70-9888-11d3-8edc-00c04f6109cf") + IWMStatusCallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnStatus( + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [in] */ BYTE *pValue, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMStatusCallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMStatusCallback * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMStatusCallback * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMStatusCallback * This); + + HRESULT ( STDMETHODCALLTYPE *OnStatus )( + IWMStatusCallback * This, + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [in] */ BYTE *pValue, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMStatusCallbackVtbl; + + interface IWMStatusCallback + { + CONST_VTBL struct IWMStatusCallbackVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMStatusCallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMStatusCallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMStatusCallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMStatusCallback_OnStatus(This,Status,hr,dwType,pValue,pvContext) \ + (This)->lpVtbl -> OnStatus(This,Status,hr,dwType,pValue,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMStatusCallback_OnStatus_Proxy( + IWMStatusCallback * This, + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [in] */ BYTE *pValue, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMStatusCallback_OnStatus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMStatusCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderCallback_INTERFACE_DEFINED__ +#define __IWMReaderCallback_INTERFACE_DEFINED__ + +/* interface IWMReaderCallback */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BD8-2B2B-11d3-B36B-00C04F6108FF") + IWMReaderCallback : public IWMStatusCallback + { + public: + virtual HRESULT STDMETHODCALLTYPE OnSample( + /* [in] */ DWORD dwOutputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderCallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderCallback * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderCallback * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderCallback * This); + + HRESULT ( STDMETHODCALLTYPE *OnStatus )( + IWMReaderCallback * This, + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [in] */ BYTE *pValue, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *OnSample )( + IWMReaderCallback * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMReaderCallbackVtbl; + + interface IWMReaderCallback + { + CONST_VTBL struct IWMReaderCallbackVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderCallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderCallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderCallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderCallback_OnStatus(This,Status,hr,dwType,pValue,pvContext) \ + (This)->lpVtbl -> OnStatus(This,Status,hr,dwType,pValue,pvContext) + + +#define IWMReaderCallback_OnSample(This,dwOutputNum,cnsSampleTime,cnsSampleDuration,dwFlags,pSample,pvContext) \ + (This)->lpVtbl -> OnSample(This,dwOutputNum,cnsSampleTime,cnsSampleDuration,dwFlags,pSample,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderCallback_OnSample_Proxy( + IWMReaderCallback * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallback_OnSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IWMCredentialCallback_INTERFACE_DEFINED__ +#define __IWMCredentialCallback_INTERFACE_DEFINED__ + +/* interface IWMCredentialCallback */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMCredentialCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("342e0eb7-e651-450c-975b-2ace2c90c48e") + IWMCredentialCallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AcquireCredentials( + /* [in] */ WCHAR *pwszRealm, + /* [in] */ WCHAR *pwszSite, + /* [size_is][out] */ WCHAR *pwszUser, + /* [in] */ DWORD cchUser, + /* [size_is][out] */ WCHAR *pwszPassword, + /* [in] */ DWORD cchPassword, + /* [in] */ HRESULT hrStatus, + /* [out] */ DWORD *pdwFlags) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMCredentialCallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMCredentialCallback * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMCredentialCallback * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMCredentialCallback * This); + + HRESULT ( STDMETHODCALLTYPE *AcquireCredentials )( + IWMCredentialCallback * This, + /* [in] */ WCHAR *pwszRealm, + /* [in] */ WCHAR *pwszSite, + /* [size_is][out] */ WCHAR *pwszUser, + /* [in] */ DWORD cchUser, + /* [size_is][out] */ WCHAR *pwszPassword, + /* [in] */ DWORD cchPassword, + /* [in] */ HRESULT hrStatus, + /* [out] */ DWORD *pdwFlags); + + END_INTERFACE + } IWMCredentialCallbackVtbl; + + interface IWMCredentialCallback + { + CONST_VTBL struct IWMCredentialCallbackVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMCredentialCallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMCredentialCallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMCredentialCallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMCredentialCallback_AcquireCredentials(This,pwszRealm,pwszSite,pwszUser,cchUser,pwszPassword,cchPassword,hrStatus,pdwFlags) \ + (This)->lpVtbl -> AcquireCredentials(This,pwszRealm,pwszSite,pwszUser,cchUser,pwszPassword,cchPassword,hrStatus,pdwFlags) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMCredentialCallback_AcquireCredentials_Proxy( + IWMCredentialCallback * This, + /* [in] */ WCHAR *pwszRealm, + /* [in] */ WCHAR *pwszSite, + /* [size_is][out] */ WCHAR *pwszUser, + /* [in] */ DWORD cchUser, + /* [size_is][out] */ WCHAR *pwszPassword, + /* [in] */ DWORD cchPassword, + /* [in] */ HRESULT hrStatus, + /* [out] */ DWORD *pdwFlags); + + +void __RPC_STUB IWMCredentialCallback_AcquireCredentials_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMCredentialCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IWMMetadataEditor_INTERFACE_DEFINED__ +#define __IWMMetadataEditor_INTERFACE_DEFINED__ + +/* interface IWMMetadataEditor */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMMetadataEditor; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BD9-2B2B-11d3-B36B-00C04F6108FF") + IWMMetadataEditor : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ const WCHAR *pwszFilename) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Flush( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMMetadataEditorVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMMetadataEditor * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMMetadataEditor * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMMetadataEditor * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMMetadataEditor * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMMetadataEditor * This); + + HRESULT ( STDMETHODCALLTYPE *Flush )( + IWMMetadataEditor * This); + + END_INTERFACE + } IWMMetadataEditorVtbl; + + interface IWMMetadataEditor + { + CONST_VTBL struct IWMMetadataEditorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMMetadataEditor_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMMetadataEditor_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMMetadataEditor_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMMetadataEditor_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + +#define IWMMetadataEditor_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMMetadataEditor_Flush(This) \ + (This)->lpVtbl -> Flush(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMMetadataEditor_Open_Proxy( + IWMMetadataEditor * This, + /* [in] */ const WCHAR *pwszFilename); + + +void __RPC_STUB IWMMetadataEditor_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMetadataEditor_Close_Proxy( + IWMMetadataEditor * This); + + +void __RPC_STUB IWMMetadataEditor_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMetadataEditor_Flush_Proxy( + IWMMetadataEditor * This); + + +void __RPC_STUB IWMMetadataEditor_Flush_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMMetadataEditor_INTERFACE_DEFINED__ */ + + +#ifndef __IWMMetadataEditor2_INTERFACE_DEFINED__ +#define __IWMMetadataEditor2_INTERFACE_DEFINED__ + +/* interface IWMMetadataEditor2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMMetadataEditor2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("203CFFE3-2E18-4fdf-B59D-6E71530534CF") + IWMMetadataEditor2 : public IWMMetadataEditor + { + public: + virtual HRESULT STDMETHODCALLTYPE OpenEx( + /* [in] */ const WCHAR *pwszFilename, + /* [in] */ DWORD dwDesiredAccess, + /* [in] */ DWORD dwShareMode) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMMetadataEditor2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMMetadataEditor2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMMetadataEditor2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMMetadataEditor2 * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMMetadataEditor2 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMMetadataEditor2 * This); + + HRESULT ( STDMETHODCALLTYPE *Flush )( + IWMMetadataEditor2 * This); + + HRESULT ( STDMETHODCALLTYPE *OpenEx )( + IWMMetadataEditor2 * This, + /* [in] */ const WCHAR *pwszFilename, + /* [in] */ DWORD dwDesiredAccess, + /* [in] */ DWORD dwShareMode); + + END_INTERFACE + } IWMMetadataEditor2Vtbl; + + interface IWMMetadataEditor2 + { + CONST_VTBL struct IWMMetadataEditor2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMMetadataEditor2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMMetadataEditor2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMMetadataEditor2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMMetadataEditor2_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + +#define IWMMetadataEditor2_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMMetadataEditor2_Flush(This) \ + (This)->lpVtbl -> Flush(This) + + +#define IWMMetadataEditor2_OpenEx(This,pwszFilename,dwDesiredAccess,dwShareMode) \ + (This)->lpVtbl -> OpenEx(This,pwszFilename,dwDesiredAccess,dwShareMode) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMMetadataEditor2_OpenEx_Proxy( + IWMMetadataEditor2 * This, + /* [in] */ const WCHAR *pwszFilename, + /* [in] */ DWORD dwDesiredAccess, + /* [in] */ DWORD dwShareMode); + + +void __RPC_STUB IWMMetadataEditor2_OpenEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMMetadataEditor2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMDRMEditor_INTERFACE_DEFINED__ +#define __IWMDRMEditor_INTERFACE_DEFINED__ + +/* interface IWMDRMEditor */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMDRMEditor; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("FF130EBC-A6C3-42A6-B401-C3382C3E08B3") + IWMDRMEditor : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetDRMProperty( + /* [in] */ LPCWSTR pwstrName, + /* [out] */ WMT_ATTR_DATATYPE *pdwType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMDRMEditorVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMDRMEditor * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMDRMEditor * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMDRMEditor * This); + + HRESULT ( STDMETHODCALLTYPE *GetDRMProperty )( + IWMDRMEditor * This, + /* [in] */ LPCWSTR pwstrName, + /* [out] */ WMT_ATTR_DATATYPE *pdwType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + END_INTERFACE + } IWMDRMEditorVtbl; + + interface IWMDRMEditor + { + CONST_VTBL struct IWMDRMEditorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMDRMEditor_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMDRMEditor_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMDRMEditor_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMDRMEditor_GetDRMProperty(This,pwstrName,pdwType,pValue,pcbLength) \ + (This)->lpVtbl -> GetDRMProperty(This,pwstrName,pdwType,pValue,pcbLength) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMDRMEditor_GetDRMProperty_Proxy( + IWMDRMEditor * This, + /* [in] */ LPCWSTR pwstrName, + /* [out] */ WMT_ATTR_DATATYPE *pdwType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMDRMEditor_GetDRMProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMDRMEditor_INTERFACE_DEFINED__ */ + + +#ifndef __IWMHeaderInfo_INTERFACE_DEFINED__ +#define __IWMHeaderInfo_INTERFACE_DEFINED__ + +/* interface IWMHeaderInfo */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMHeaderInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BDA-2B2B-11d3-B36B-00C04F6108FF") + IWMHeaderInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetAttributeCount( + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeByIndex( + /* [in] */ WORD wIndex, + /* [out][in] */ WORD *pwStreamNum, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeByName( + /* [out][in] */ WORD *pwStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAttribute( + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMarkerCount( + /* [out] */ WORD *pcMarkers) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMarker( + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszMarkerName, + /* [out][in] */ WORD *pcchMarkerNameLen, + /* [out] */ QWORD *pcnsMarkerTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddMarker( + /* [in] */ WCHAR *pwszMarkerName, + /* [in] */ QWORD cnsMarkerTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveMarker( + /* [in] */ WORD wIndex) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetScriptCount( + /* [out] */ WORD *pcScripts) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetScript( + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszType, + /* [out][in] */ WORD *pcchTypeLen, + /* [size_is][out] */ WCHAR *pwszCommand, + /* [out][in] */ WORD *pcchCommandLen, + /* [out] */ QWORD *pcnsScriptTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddScript( + /* [in] */ WCHAR *pwszType, + /* [in] */ WCHAR *pwszCommand, + /* [in] */ QWORD cnsScriptTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveScript( + /* [in] */ WORD wIndex) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMHeaderInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMHeaderInfo * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMHeaderInfo * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMHeaderInfo * This); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeCount )( + IWMHeaderInfo * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByIndex )( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex, + /* [out][in] */ WORD *pwStreamNum, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByName )( + IWMHeaderInfo * This, + /* [out][in] */ WORD *pwStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetAttribute )( + IWMHeaderInfo * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetMarkerCount )( + IWMHeaderInfo * This, + /* [out] */ WORD *pcMarkers); + + HRESULT ( STDMETHODCALLTYPE *GetMarker )( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszMarkerName, + /* [out][in] */ WORD *pcchMarkerNameLen, + /* [out] */ QWORD *pcnsMarkerTime); + + HRESULT ( STDMETHODCALLTYPE *AddMarker )( + IWMHeaderInfo * This, + /* [in] */ WCHAR *pwszMarkerName, + /* [in] */ QWORD cnsMarkerTime); + + HRESULT ( STDMETHODCALLTYPE *RemoveMarker )( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex); + + HRESULT ( STDMETHODCALLTYPE *GetScriptCount )( + IWMHeaderInfo * This, + /* [out] */ WORD *pcScripts); + + HRESULT ( STDMETHODCALLTYPE *GetScript )( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszType, + /* [out][in] */ WORD *pcchTypeLen, + /* [size_is][out] */ WCHAR *pwszCommand, + /* [out][in] */ WORD *pcchCommandLen, + /* [out] */ QWORD *pcnsScriptTime); + + HRESULT ( STDMETHODCALLTYPE *AddScript )( + IWMHeaderInfo * This, + /* [in] */ WCHAR *pwszType, + /* [in] */ WCHAR *pwszCommand, + /* [in] */ QWORD cnsScriptTime); + + HRESULT ( STDMETHODCALLTYPE *RemoveScript )( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex); + + END_INTERFACE + } IWMHeaderInfoVtbl; + + interface IWMHeaderInfo + { + CONST_VTBL struct IWMHeaderInfoVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMHeaderInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMHeaderInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMHeaderInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMHeaderInfo_GetAttributeCount(This,wStreamNum,pcAttributes) \ + (This)->lpVtbl -> GetAttributeCount(This,wStreamNum,pcAttributes) + +#define IWMHeaderInfo_GetAttributeByIndex(This,wIndex,pwStreamNum,pwszName,pcchNameLen,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByIndex(This,wIndex,pwStreamNum,pwszName,pcchNameLen,pType,pValue,pcbLength) + +#define IWMHeaderInfo_GetAttributeByName(This,pwStreamNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByName(This,pwStreamNum,pszName,pType,pValue,pcbLength) + +#define IWMHeaderInfo_SetAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) + +#define IWMHeaderInfo_GetMarkerCount(This,pcMarkers) \ + (This)->lpVtbl -> GetMarkerCount(This,pcMarkers) + +#define IWMHeaderInfo_GetMarker(This,wIndex,pwszMarkerName,pcchMarkerNameLen,pcnsMarkerTime) \ + (This)->lpVtbl -> GetMarker(This,wIndex,pwszMarkerName,pcchMarkerNameLen,pcnsMarkerTime) + +#define IWMHeaderInfo_AddMarker(This,pwszMarkerName,cnsMarkerTime) \ + (This)->lpVtbl -> AddMarker(This,pwszMarkerName,cnsMarkerTime) + +#define IWMHeaderInfo_RemoveMarker(This,wIndex) \ + (This)->lpVtbl -> RemoveMarker(This,wIndex) + +#define IWMHeaderInfo_GetScriptCount(This,pcScripts) \ + (This)->lpVtbl -> GetScriptCount(This,pcScripts) + +#define IWMHeaderInfo_GetScript(This,wIndex,pwszType,pcchTypeLen,pwszCommand,pcchCommandLen,pcnsScriptTime) \ + (This)->lpVtbl -> GetScript(This,wIndex,pwszType,pcchTypeLen,pwszCommand,pcchCommandLen,pcnsScriptTime) + +#define IWMHeaderInfo_AddScript(This,pwszType,pwszCommand,cnsScriptTime) \ + (This)->lpVtbl -> AddScript(This,pwszType,pwszCommand,cnsScriptTime) + +#define IWMHeaderInfo_RemoveScript(This,wIndex) \ + (This)->lpVtbl -> RemoveScript(This,wIndex) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetAttributeCount_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes); + + +void __RPC_STUB IWMHeaderInfo_GetAttributeCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetAttributeByIndex_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex, + /* [out][in] */ WORD *pwStreamNum, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMHeaderInfo_GetAttributeByIndex_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetAttributeByName_Proxy( + IWMHeaderInfo * This, + /* [out][in] */ WORD *pwStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMHeaderInfo_GetAttributeByName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_SetAttribute_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMHeaderInfo_SetAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetMarkerCount_Proxy( + IWMHeaderInfo * This, + /* [out] */ WORD *pcMarkers); + + +void __RPC_STUB IWMHeaderInfo_GetMarkerCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetMarker_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszMarkerName, + /* [out][in] */ WORD *pcchMarkerNameLen, + /* [out] */ QWORD *pcnsMarkerTime); + + +void __RPC_STUB IWMHeaderInfo_GetMarker_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_AddMarker_Proxy( + IWMHeaderInfo * This, + /* [in] */ WCHAR *pwszMarkerName, + /* [in] */ QWORD cnsMarkerTime); + + +void __RPC_STUB IWMHeaderInfo_AddMarker_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_RemoveMarker_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex); + + +void __RPC_STUB IWMHeaderInfo_RemoveMarker_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetScriptCount_Proxy( + IWMHeaderInfo * This, + /* [out] */ WORD *pcScripts); + + +void __RPC_STUB IWMHeaderInfo_GetScriptCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_GetScript_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszType, + /* [out][in] */ WORD *pcchTypeLen, + /* [size_is][out] */ WCHAR *pwszCommand, + /* [out][in] */ WORD *pcchCommandLen, + /* [out] */ QWORD *pcnsScriptTime); + + +void __RPC_STUB IWMHeaderInfo_GetScript_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_AddScript_Proxy( + IWMHeaderInfo * This, + /* [in] */ WCHAR *pwszType, + /* [in] */ WCHAR *pwszCommand, + /* [in] */ QWORD cnsScriptTime); + + +void __RPC_STUB IWMHeaderInfo_AddScript_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo_RemoveScript_Proxy( + IWMHeaderInfo * This, + /* [in] */ WORD wIndex); + + +void __RPC_STUB IWMHeaderInfo_RemoveScript_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMHeaderInfo_INTERFACE_DEFINED__ */ + + +#ifndef __IWMHeaderInfo2_INTERFACE_DEFINED__ +#define __IWMHeaderInfo2_INTERFACE_DEFINED__ + +/* interface IWMHeaderInfo2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMHeaderInfo2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("15CF9781-454E-482e-B393-85FAE487A810") + IWMHeaderInfo2 : public IWMHeaderInfo + { + public: + virtual HRESULT STDMETHODCALLTYPE GetCodecInfoCount( + /* [out] */ DWORD *pcCodecInfos) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCodecInfo( + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchName, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WMT_CODEC_INFO_TYPE *pCodecType, + /* [out][in] */ WORD *pcbCodecInfo, + /* [size_is][out] */ BYTE *pbCodecInfo) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMHeaderInfo2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMHeaderInfo2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMHeaderInfo2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMHeaderInfo2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeCount )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByIndex )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wIndex, + /* [out][in] */ WORD *pwStreamNum, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByName )( + IWMHeaderInfo2 * This, + /* [out][in] */ WORD *pwStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetAttribute )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetMarkerCount )( + IWMHeaderInfo2 * This, + /* [out] */ WORD *pcMarkers); + + HRESULT ( STDMETHODCALLTYPE *GetMarker )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszMarkerName, + /* [out][in] */ WORD *pcchMarkerNameLen, + /* [out] */ QWORD *pcnsMarkerTime); + + HRESULT ( STDMETHODCALLTYPE *AddMarker )( + IWMHeaderInfo2 * This, + /* [in] */ WCHAR *pwszMarkerName, + /* [in] */ QWORD cnsMarkerTime); + + HRESULT ( STDMETHODCALLTYPE *RemoveMarker )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wIndex); + + HRESULT ( STDMETHODCALLTYPE *GetScriptCount )( + IWMHeaderInfo2 * This, + /* [out] */ WORD *pcScripts); + + HRESULT ( STDMETHODCALLTYPE *GetScript )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszType, + /* [out][in] */ WORD *pcchTypeLen, + /* [size_is][out] */ WCHAR *pwszCommand, + /* [out][in] */ WORD *pcchCommandLen, + /* [out] */ QWORD *pcnsScriptTime); + + HRESULT ( STDMETHODCALLTYPE *AddScript )( + IWMHeaderInfo2 * This, + /* [in] */ WCHAR *pwszType, + /* [in] */ WCHAR *pwszCommand, + /* [in] */ QWORD cnsScriptTime); + + HRESULT ( STDMETHODCALLTYPE *RemoveScript )( + IWMHeaderInfo2 * This, + /* [in] */ WORD wIndex); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfoCount )( + IWMHeaderInfo2 * This, + /* [out] */ DWORD *pcCodecInfos); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfo )( + IWMHeaderInfo2 * This, + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchName, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WMT_CODEC_INFO_TYPE *pCodecType, + /* [out][in] */ WORD *pcbCodecInfo, + /* [size_is][out] */ BYTE *pbCodecInfo); + + END_INTERFACE + } IWMHeaderInfo2Vtbl; + + interface IWMHeaderInfo2 + { + CONST_VTBL struct IWMHeaderInfo2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMHeaderInfo2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMHeaderInfo2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMHeaderInfo2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMHeaderInfo2_GetAttributeCount(This,wStreamNum,pcAttributes) \ + (This)->lpVtbl -> GetAttributeCount(This,wStreamNum,pcAttributes) + +#define IWMHeaderInfo2_GetAttributeByIndex(This,wIndex,pwStreamNum,pwszName,pcchNameLen,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByIndex(This,wIndex,pwStreamNum,pwszName,pcchNameLen,pType,pValue,pcbLength) + +#define IWMHeaderInfo2_GetAttributeByName(This,pwStreamNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByName(This,pwStreamNum,pszName,pType,pValue,pcbLength) + +#define IWMHeaderInfo2_SetAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) + +#define IWMHeaderInfo2_GetMarkerCount(This,pcMarkers) \ + (This)->lpVtbl -> GetMarkerCount(This,pcMarkers) + +#define IWMHeaderInfo2_GetMarker(This,wIndex,pwszMarkerName,pcchMarkerNameLen,pcnsMarkerTime) \ + (This)->lpVtbl -> GetMarker(This,wIndex,pwszMarkerName,pcchMarkerNameLen,pcnsMarkerTime) + +#define IWMHeaderInfo2_AddMarker(This,pwszMarkerName,cnsMarkerTime) \ + (This)->lpVtbl -> AddMarker(This,pwszMarkerName,cnsMarkerTime) + +#define IWMHeaderInfo2_RemoveMarker(This,wIndex) \ + (This)->lpVtbl -> RemoveMarker(This,wIndex) + +#define IWMHeaderInfo2_GetScriptCount(This,pcScripts) \ + (This)->lpVtbl -> GetScriptCount(This,pcScripts) + +#define IWMHeaderInfo2_GetScript(This,wIndex,pwszType,pcchTypeLen,pwszCommand,pcchCommandLen,pcnsScriptTime) \ + (This)->lpVtbl -> GetScript(This,wIndex,pwszType,pcchTypeLen,pwszCommand,pcchCommandLen,pcnsScriptTime) + +#define IWMHeaderInfo2_AddScript(This,pwszType,pwszCommand,cnsScriptTime) \ + (This)->lpVtbl -> AddScript(This,pwszType,pwszCommand,cnsScriptTime) + +#define IWMHeaderInfo2_RemoveScript(This,wIndex) \ + (This)->lpVtbl -> RemoveScript(This,wIndex) + + +#define IWMHeaderInfo2_GetCodecInfoCount(This,pcCodecInfos) \ + (This)->lpVtbl -> GetCodecInfoCount(This,pcCodecInfos) + +#define IWMHeaderInfo2_GetCodecInfo(This,wIndex,pcchName,pwszName,pcchDescription,pwszDescription,pCodecType,pcbCodecInfo,pbCodecInfo) \ + (This)->lpVtbl -> GetCodecInfo(This,wIndex,pcchName,pwszName,pcchDescription,pwszDescription,pCodecType,pcbCodecInfo,pbCodecInfo) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo2_GetCodecInfoCount_Proxy( + IWMHeaderInfo2 * This, + /* [out] */ DWORD *pcCodecInfos); + + +void __RPC_STUB IWMHeaderInfo2_GetCodecInfoCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo2_GetCodecInfo_Proxy( + IWMHeaderInfo2 * This, + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchName, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WMT_CODEC_INFO_TYPE *pCodecType, + /* [out][in] */ WORD *pcbCodecInfo, + /* [size_is][out] */ BYTE *pbCodecInfo); + + +void __RPC_STUB IWMHeaderInfo2_GetCodecInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMHeaderInfo2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMHeaderInfo3_INTERFACE_DEFINED__ +#define __IWMHeaderInfo3_INTERFACE_DEFINED__ + +/* interface IWMHeaderInfo3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMHeaderInfo3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("15CC68E3-27CC-4ecd-B222-3F5D02D80BD5") + IWMHeaderInfo3 : public IWMHeaderInfo2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetAttributeCountEx( + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeIndices( + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pwszName, + /* [in] */ WORD *pwLangIndex, + /* [size_is][out] */ WORD *pwIndices, + /* [out][in] */ WORD *pwCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeByIndexEx( + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex, + /* [size_is][out] */ LPWSTR pwszName, + /* [out][in] */ WORD *pwNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [out] */ WORD *pwLangIndex, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwDataLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE ModifyAttribute( + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ WORD wLangIndex, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddAttribute( + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WORD *pwIndex, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ WORD wLangIndex, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteAttribute( + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddCodecInfo( + /* [in] */ WCHAR *pwszName, + /* [in] */ WCHAR *pwszDescription, + /* [in] */ WMT_CODEC_INFO_TYPE codecType, + /* [in] */ WORD cbCodecInfo, + /* [size_is][in] */ BYTE *pbCodecInfo) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMHeaderInfo3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMHeaderInfo3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMHeaderInfo3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMHeaderInfo3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeCount )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByIndex )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wIndex, + /* [out][in] */ WORD *pwStreamNum, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByName )( + IWMHeaderInfo3 * This, + /* [out][in] */ WORD *pwStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetAttribute )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetMarkerCount )( + IWMHeaderInfo3 * This, + /* [out] */ WORD *pcMarkers); + + HRESULT ( STDMETHODCALLTYPE *GetMarker )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszMarkerName, + /* [out][in] */ WORD *pcchMarkerNameLen, + /* [out] */ QWORD *pcnsMarkerTime); + + HRESULT ( STDMETHODCALLTYPE *AddMarker )( + IWMHeaderInfo3 * This, + /* [in] */ WCHAR *pwszMarkerName, + /* [in] */ QWORD cnsMarkerTime); + + HRESULT ( STDMETHODCALLTYPE *RemoveMarker )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wIndex); + + HRESULT ( STDMETHODCALLTYPE *GetScriptCount )( + IWMHeaderInfo3 * This, + /* [out] */ WORD *pcScripts); + + HRESULT ( STDMETHODCALLTYPE *GetScript )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszType, + /* [out][in] */ WORD *pcchTypeLen, + /* [size_is][out] */ WCHAR *pwszCommand, + /* [out][in] */ WORD *pcchCommandLen, + /* [out] */ QWORD *pcnsScriptTime); + + HRESULT ( STDMETHODCALLTYPE *AddScript )( + IWMHeaderInfo3 * This, + /* [in] */ WCHAR *pwszType, + /* [in] */ WCHAR *pwszCommand, + /* [in] */ QWORD cnsScriptTime); + + HRESULT ( STDMETHODCALLTYPE *RemoveScript )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wIndex); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfoCount )( + IWMHeaderInfo3 * This, + /* [out] */ DWORD *pcCodecInfos); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfo )( + IWMHeaderInfo3 * This, + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchName, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WMT_CODEC_INFO_TYPE *pCodecType, + /* [out][in] */ WORD *pcbCodecInfo, + /* [size_is][out] */ BYTE *pbCodecInfo); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeCountEx )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeIndices )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pwszName, + /* [in] */ WORD *pwLangIndex, + /* [size_is][out] */ WORD *pwIndices, + /* [out][in] */ WORD *pwCount); + + HRESULT ( STDMETHODCALLTYPE *GetAttributeByIndexEx )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex, + /* [size_is][out] */ LPWSTR pwszName, + /* [out][in] */ WORD *pwNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [out] */ WORD *pwLangIndex, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwDataLength); + + HRESULT ( STDMETHODCALLTYPE *ModifyAttribute )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ WORD wLangIndex, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwLength); + + HRESULT ( STDMETHODCALLTYPE *AddAttribute )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WORD *pwIndex, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ WORD wLangIndex, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwLength); + + HRESULT ( STDMETHODCALLTYPE *DeleteAttribute )( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex); + + HRESULT ( STDMETHODCALLTYPE *AddCodecInfo )( + IWMHeaderInfo3 * This, + /* [in] */ WCHAR *pwszName, + /* [in] */ WCHAR *pwszDescription, + /* [in] */ WMT_CODEC_INFO_TYPE codecType, + /* [in] */ WORD cbCodecInfo, + /* [size_is][in] */ BYTE *pbCodecInfo); + + END_INTERFACE + } IWMHeaderInfo3Vtbl; + + interface IWMHeaderInfo3 + { + CONST_VTBL struct IWMHeaderInfo3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMHeaderInfo3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMHeaderInfo3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMHeaderInfo3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMHeaderInfo3_GetAttributeCount(This,wStreamNum,pcAttributes) \ + (This)->lpVtbl -> GetAttributeCount(This,wStreamNum,pcAttributes) + +#define IWMHeaderInfo3_GetAttributeByIndex(This,wIndex,pwStreamNum,pwszName,pcchNameLen,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByIndex(This,wIndex,pwStreamNum,pwszName,pcchNameLen,pType,pValue,pcbLength) + +#define IWMHeaderInfo3_GetAttributeByName(This,pwStreamNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByName(This,pwStreamNum,pszName,pType,pValue,pcbLength) + +#define IWMHeaderInfo3_SetAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetAttribute(This,wStreamNum,pszName,Type,pValue,cbLength) + +#define IWMHeaderInfo3_GetMarkerCount(This,pcMarkers) \ + (This)->lpVtbl -> GetMarkerCount(This,pcMarkers) + +#define IWMHeaderInfo3_GetMarker(This,wIndex,pwszMarkerName,pcchMarkerNameLen,pcnsMarkerTime) \ + (This)->lpVtbl -> GetMarker(This,wIndex,pwszMarkerName,pcchMarkerNameLen,pcnsMarkerTime) + +#define IWMHeaderInfo3_AddMarker(This,pwszMarkerName,cnsMarkerTime) \ + (This)->lpVtbl -> AddMarker(This,pwszMarkerName,cnsMarkerTime) + +#define IWMHeaderInfo3_RemoveMarker(This,wIndex) \ + (This)->lpVtbl -> RemoveMarker(This,wIndex) + +#define IWMHeaderInfo3_GetScriptCount(This,pcScripts) \ + (This)->lpVtbl -> GetScriptCount(This,pcScripts) + +#define IWMHeaderInfo3_GetScript(This,wIndex,pwszType,pcchTypeLen,pwszCommand,pcchCommandLen,pcnsScriptTime) \ + (This)->lpVtbl -> GetScript(This,wIndex,pwszType,pcchTypeLen,pwszCommand,pcchCommandLen,pcnsScriptTime) + +#define IWMHeaderInfo3_AddScript(This,pwszType,pwszCommand,cnsScriptTime) \ + (This)->lpVtbl -> AddScript(This,pwszType,pwszCommand,cnsScriptTime) + +#define IWMHeaderInfo3_RemoveScript(This,wIndex) \ + (This)->lpVtbl -> RemoveScript(This,wIndex) + + +#define IWMHeaderInfo3_GetCodecInfoCount(This,pcCodecInfos) \ + (This)->lpVtbl -> GetCodecInfoCount(This,pcCodecInfos) + +#define IWMHeaderInfo3_GetCodecInfo(This,wIndex,pcchName,pwszName,pcchDescription,pwszDescription,pCodecType,pcbCodecInfo,pbCodecInfo) \ + (This)->lpVtbl -> GetCodecInfo(This,wIndex,pcchName,pwszName,pcchDescription,pwszDescription,pCodecType,pcbCodecInfo,pbCodecInfo) + + +#define IWMHeaderInfo3_GetAttributeCountEx(This,wStreamNum,pcAttributes) \ + (This)->lpVtbl -> GetAttributeCountEx(This,wStreamNum,pcAttributes) + +#define IWMHeaderInfo3_GetAttributeIndices(This,wStreamNum,pwszName,pwLangIndex,pwIndices,pwCount) \ + (This)->lpVtbl -> GetAttributeIndices(This,wStreamNum,pwszName,pwLangIndex,pwIndices,pwCount) + +#define IWMHeaderInfo3_GetAttributeByIndexEx(This,wStreamNum,wIndex,pwszName,pwNameLen,pType,pwLangIndex,pValue,pdwDataLength) \ + (This)->lpVtbl -> GetAttributeByIndexEx(This,wStreamNum,wIndex,pwszName,pwNameLen,pType,pwLangIndex,pValue,pdwDataLength) + +#define IWMHeaderInfo3_ModifyAttribute(This,wStreamNum,wIndex,Type,wLangIndex,pValue,dwLength) \ + (This)->lpVtbl -> ModifyAttribute(This,wStreamNum,wIndex,Type,wLangIndex,pValue,dwLength) + +#define IWMHeaderInfo3_AddAttribute(This,wStreamNum,pszName,pwIndex,Type,wLangIndex,pValue,dwLength) \ + (This)->lpVtbl -> AddAttribute(This,wStreamNum,pszName,pwIndex,Type,wLangIndex,pValue,dwLength) + +#define IWMHeaderInfo3_DeleteAttribute(This,wStreamNum,wIndex) \ + (This)->lpVtbl -> DeleteAttribute(This,wStreamNum,wIndex) + +#define IWMHeaderInfo3_AddCodecInfo(This,pwszName,pwszDescription,codecType,cbCodecInfo,pbCodecInfo) \ + (This)->lpVtbl -> AddCodecInfo(This,pwszName,pwszDescription,codecType,cbCodecInfo,pbCodecInfo) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_GetAttributeCountEx_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pcAttributes); + + +void __RPC_STUB IWMHeaderInfo3_GetAttributeCountEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_GetAttributeIndices_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pwszName, + /* [in] */ WORD *pwLangIndex, + /* [size_is][out] */ WORD *pwIndices, + /* [out][in] */ WORD *pwCount); + + +void __RPC_STUB IWMHeaderInfo3_GetAttributeIndices_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_GetAttributeByIndexEx_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex, + /* [size_is][out] */ LPWSTR pwszName, + /* [out][in] */ WORD *pwNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [out] */ WORD *pwLangIndex, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwDataLength); + + +void __RPC_STUB IWMHeaderInfo3_GetAttributeByIndexEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_ModifyAttribute_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ WORD wLangIndex, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwLength); + + +void __RPC_STUB IWMHeaderInfo3_ModifyAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_AddAttribute_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WORD *pwIndex, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ WORD wLangIndex, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwLength); + + +void __RPC_STUB IWMHeaderInfo3_AddAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_DeleteAttribute_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wIndex); + + +void __RPC_STUB IWMHeaderInfo3_DeleteAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMHeaderInfo3_AddCodecInfo_Proxy( + IWMHeaderInfo3 * This, + /* [in] */ WCHAR *pwszName, + /* [in] */ WCHAR *pwszDescription, + /* [in] */ WMT_CODEC_INFO_TYPE codecType, + /* [in] */ WORD cbCodecInfo, + /* [size_is][in] */ BYTE *pbCodecInfo); + + +void __RPC_STUB IWMHeaderInfo3_AddCodecInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMHeaderInfo3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMProfileManager_INTERFACE_DEFINED__ +#define __IWMProfileManager_INTERFACE_DEFINED__ + +/* interface IWMProfileManager */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMProfileManager; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("d16679f2-6ca0-472d-8d31-2f5d55aee155") + IWMProfileManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE CreateEmptyProfile( + /* [in] */ WMT_VERSION dwVersion, + /* [out] */ IWMProfile **ppProfile) = 0; + + virtual HRESULT STDMETHODCALLTYPE LoadProfileByID( + /* [in] */ REFGUID guidProfile, + /* [out] */ IWMProfile **ppProfile) = 0; + + virtual HRESULT STDMETHODCALLTYPE LoadProfileByData( + /* [in] */ const WCHAR *pwszProfile, + /* [out] */ IWMProfile **ppProfile) = 0; + + virtual HRESULT STDMETHODCALLTYPE SaveProfile( + /* [in] */ IWMProfile *pIWMProfile, + /* [in] */ WCHAR *pwszProfile, + /* [out][in] */ DWORD *pdwLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSystemProfileCount( + /* [out] */ DWORD *pcProfiles) = 0; + + virtual HRESULT STDMETHODCALLTYPE LoadSystemProfile( + /* [in] */ DWORD dwProfileIndex, + /* [out] */ IWMProfile **ppProfile) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMProfileManagerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMProfileManager * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMProfileManager * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMProfileManager * This); + + HRESULT ( STDMETHODCALLTYPE *CreateEmptyProfile )( + IWMProfileManager * This, + /* [in] */ WMT_VERSION dwVersion, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *LoadProfileByID )( + IWMProfileManager * This, + /* [in] */ REFGUID guidProfile, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *LoadProfileByData )( + IWMProfileManager * This, + /* [in] */ const WCHAR *pwszProfile, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *SaveProfile )( + IWMProfileManager * This, + /* [in] */ IWMProfile *pIWMProfile, + /* [in] */ WCHAR *pwszProfile, + /* [out][in] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetSystemProfileCount )( + IWMProfileManager * This, + /* [out] */ DWORD *pcProfiles); + + HRESULT ( STDMETHODCALLTYPE *LoadSystemProfile )( + IWMProfileManager * This, + /* [in] */ DWORD dwProfileIndex, + /* [out] */ IWMProfile **ppProfile); + + END_INTERFACE + } IWMProfileManagerVtbl; + + interface IWMProfileManager + { + CONST_VTBL struct IWMProfileManagerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMProfileManager_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMProfileManager_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMProfileManager_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMProfileManager_CreateEmptyProfile(This,dwVersion,ppProfile) \ + (This)->lpVtbl -> CreateEmptyProfile(This,dwVersion,ppProfile) + +#define IWMProfileManager_LoadProfileByID(This,guidProfile,ppProfile) \ + (This)->lpVtbl -> LoadProfileByID(This,guidProfile,ppProfile) + +#define IWMProfileManager_LoadProfileByData(This,pwszProfile,ppProfile) \ + (This)->lpVtbl -> LoadProfileByData(This,pwszProfile,ppProfile) + +#define IWMProfileManager_SaveProfile(This,pIWMProfile,pwszProfile,pdwLength) \ + (This)->lpVtbl -> SaveProfile(This,pIWMProfile,pwszProfile,pdwLength) + +#define IWMProfileManager_GetSystemProfileCount(This,pcProfiles) \ + (This)->lpVtbl -> GetSystemProfileCount(This,pcProfiles) + +#define IWMProfileManager_LoadSystemProfile(This,dwProfileIndex,ppProfile) \ + (This)->lpVtbl -> LoadSystemProfile(This,dwProfileIndex,ppProfile) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMProfileManager_CreateEmptyProfile_Proxy( + IWMProfileManager * This, + /* [in] */ WMT_VERSION dwVersion, + /* [out] */ IWMProfile **ppProfile); + + +void __RPC_STUB IWMProfileManager_CreateEmptyProfile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManager_LoadProfileByID_Proxy( + IWMProfileManager * This, + /* [in] */ REFGUID guidProfile, + /* [out] */ IWMProfile **ppProfile); + + +void __RPC_STUB IWMProfileManager_LoadProfileByID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManager_LoadProfileByData_Proxy( + IWMProfileManager * This, + /* [in] */ const WCHAR *pwszProfile, + /* [out] */ IWMProfile **ppProfile); + + +void __RPC_STUB IWMProfileManager_LoadProfileByData_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManager_SaveProfile_Proxy( + IWMProfileManager * This, + /* [in] */ IWMProfile *pIWMProfile, + /* [in] */ WCHAR *pwszProfile, + /* [out][in] */ DWORD *pdwLength); + + +void __RPC_STUB IWMProfileManager_SaveProfile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManager_GetSystemProfileCount_Proxy( + IWMProfileManager * This, + /* [out] */ DWORD *pcProfiles); + + +void __RPC_STUB IWMProfileManager_GetSystemProfileCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManager_LoadSystemProfile_Proxy( + IWMProfileManager * This, + /* [in] */ DWORD dwProfileIndex, + /* [out] */ IWMProfile **ppProfile); + + +void __RPC_STUB IWMProfileManager_LoadSystemProfile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMProfileManager_INTERFACE_DEFINED__ */ + + +#ifndef __IWMProfileManager2_INTERFACE_DEFINED__ +#define __IWMProfileManager2_INTERFACE_DEFINED__ + +/* interface IWMProfileManager2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMProfileManager2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("7A924E51-73C1-494d-8019-23D37ED9B89A") + IWMProfileManager2 : public IWMProfileManager + { + public: + virtual HRESULT STDMETHODCALLTYPE GetSystemProfileVersion( + WMT_VERSION *pdwVersion) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetSystemProfileVersion( + WMT_VERSION dwVersion) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMProfileManager2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMProfileManager2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMProfileManager2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMProfileManager2 * This); + + HRESULT ( STDMETHODCALLTYPE *CreateEmptyProfile )( + IWMProfileManager2 * This, + /* [in] */ WMT_VERSION dwVersion, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *LoadProfileByID )( + IWMProfileManager2 * This, + /* [in] */ REFGUID guidProfile, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *LoadProfileByData )( + IWMProfileManager2 * This, + /* [in] */ const WCHAR *pwszProfile, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *SaveProfile )( + IWMProfileManager2 * This, + /* [in] */ IWMProfile *pIWMProfile, + /* [in] */ WCHAR *pwszProfile, + /* [out][in] */ DWORD *pdwLength); + + HRESULT ( STDMETHODCALLTYPE *GetSystemProfileCount )( + IWMProfileManager2 * This, + /* [out] */ DWORD *pcProfiles); + + HRESULT ( STDMETHODCALLTYPE *LoadSystemProfile )( + IWMProfileManager2 * This, + /* [in] */ DWORD dwProfileIndex, + /* [out] */ IWMProfile **ppProfile); + + HRESULT ( STDMETHODCALLTYPE *GetSystemProfileVersion )( + IWMProfileManager2 * This, + WMT_VERSION *pdwVersion); + + HRESULT ( STDMETHODCALLTYPE *SetSystemProfileVersion )( + IWMProfileManager2 * This, + WMT_VERSION dwVersion); + + END_INTERFACE + } IWMProfileManager2Vtbl; + + interface IWMProfileManager2 + { + CONST_VTBL struct IWMProfileManager2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMProfileManager2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMProfileManager2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMProfileManager2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMProfileManager2_CreateEmptyProfile(This,dwVersion,ppProfile) \ + (This)->lpVtbl -> CreateEmptyProfile(This,dwVersion,ppProfile) + +#define IWMProfileManager2_LoadProfileByID(This,guidProfile,ppProfile) \ + (This)->lpVtbl -> LoadProfileByID(This,guidProfile,ppProfile) + +#define IWMProfileManager2_LoadProfileByData(This,pwszProfile,ppProfile) \ + (This)->lpVtbl -> LoadProfileByData(This,pwszProfile,ppProfile) + +#define IWMProfileManager2_SaveProfile(This,pIWMProfile,pwszProfile,pdwLength) \ + (This)->lpVtbl -> SaveProfile(This,pIWMProfile,pwszProfile,pdwLength) + +#define IWMProfileManager2_GetSystemProfileCount(This,pcProfiles) \ + (This)->lpVtbl -> GetSystemProfileCount(This,pcProfiles) + +#define IWMProfileManager2_LoadSystemProfile(This,dwProfileIndex,ppProfile) \ + (This)->lpVtbl -> LoadSystemProfile(This,dwProfileIndex,ppProfile) + + +#define IWMProfileManager2_GetSystemProfileVersion(This,pdwVersion) \ + (This)->lpVtbl -> GetSystemProfileVersion(This,pdwVersion) + +#define IWMProfileManager2_SetSystemProfileVersion(This,dwVersion) \ + (This)->lpVtbl -> SetSystemProfileVersion(This,dwVersion) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMProfileManager2_GetSystemProfileVersion_Proxy( + IWMProfileManager2 * This, + WMT_VERSION *pdwVersion); + + +void __RPC_STUB IWMProfileManager2_GetSystemProfileVersion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManager2_SetSystemProfileVersion_Proxy( + IWMProfileManager2 * This, + WMT_VERSION dwVersion); + + +void __RPC_STUB IWMProfileManager2_SetSystemProfileVersion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMProfileManager2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMProfileManagerLanguage_INTERFACE_DEFINED__ +#define __IWMProfileManagerLanguage_INTERFACE_DEFINED__ + +/* interface IWMProfileManagerLanguage */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMProfileManagerLanguage; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("BA4DCC78-7EE0-4ab8-B27A-DBCE8BC51454") + IWMProfileManagerLanguage : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetUserLanguageID( + WORD *wLangID) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetUserLanguageID( + WORD wLangID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMProfileManagerLanguageVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMProfileManagerLanguage * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMProfileManagerLanguage * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMProfileManagerLanguage * This); + + HRESULT ( STDMETHODCALLTYPE *GetUserLanguageID )( + IWMProfileManagerLanguage * This, + WORD *wLangID); + + HRESULT ( STDMETHODCALLTYPE *SetUserLanguageID )( + IWMProfileManagerLanguage * This, + WORD wLangID); + + END_INTERFACE + } IWMProfileManagerLanguageVtbl; + + interface IWMProfileManagerLanguage + { + CONST_VTBL struct IWMProfileManagerLanguageVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMProfileManagerLanguage_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMProfileManagerLanguage_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMProfileManagerLanguage_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMProfileManagerLanguage_GetUserLanguageID(This,wLangID) \ + (This)->lpVtbl -> GetUserLanguageID(This,wLangID) + +#define IWMProfileManagerLanguage_SetUserLanguageID(This,wLangID) \ + (This)->lpVtbl -> SetUserLanguageID(This,wLangID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMProfileManagerLanguage_GetUserLanguageID_Proxy( + IWMProfileManagerLanguage * This, + WORD *wLangID); + + +void __RPC_STUB IWMProfileManagerLanguage_GetUserLanguageID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfileManagerLanguage_SetUserLanguageID_Proxy( + IWMProfileManagerLanguage * This, + WORD wLangID); + + +void __RPC_STUB IWMProfileManagerLanguage_SetUserLanguageID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMProfileManagerLanguage_INTERFACE_DEFINED__ */ + + +#ifndef __IWMProfile_INTERFACE_DEFINED__ +#define __IWMProfile_INTERFACE_DEFINED__ + +/* interface IWMProfile */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMProfile; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BDB-2B2B-11d3-B36B-00C04F6108FF") + IWMProfile : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetVersion( + /* [out] */ WMT_VERSION *pdwVersion) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ DWORD *pcchName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetName( + /* [in] */ const WCHAR *pwszName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDescription( + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out][in] */ DWORD *pcchDescription) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetDescription( + /* [in] */ const WCHAR *pwszDescription) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamCount( + /* [out] */ DWORD *pcStreams) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStream( + /* [in] */ DWORD dwStreamIndex, + /* [out] */ IWMStreamConfig **ppConfig) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamByNumber( + /* [in] */ WORD wStreamNum, + /* [out] */ IWMStreamConfig **ppConfig) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveStream( + /* [in] */ IWMStreamConfig *pConfig) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveStreamByNumber( + /* [in] */ WORD wStreamNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddStream( + /* [in] */ IWMStreamConfig *pConfig) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReconfigStream( + /* [in] */ IWMStreamConfig *pConfig) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateNewStream( + /* [in] */ REFGUID guidStreamType, + /* [out] */ IWMStreamConfig **ppConfig) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMutualExclusionCount( + /* [out] */ DWORD *pcME) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMutualExclusion( + /* [in] */ DWORD dwMEIndex, + /* [out] */ IWMMutualExclusion **ppME) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveMutualExclusion( + /* [in] */ IWMMutualExclusion *pME) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddMutualExclusion( + /* [in] */ IWMMutualExclusion *pME) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateNewMutualExclusion( + /* [out] */ IWMMutualExclusion **ppME) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMProfileVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMProfile * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMProfile * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMProfile * This); + + HRESULT ( STDMETHODCALLTYPE *GetVersion )( + IWMProfile * This, + /* [out] */ WMT_VERSION *pdwVersion); + + HRESULT ( STDMETHODCALLTYPE *GetName )( + IWMProfile * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ DWORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *SetName )( + IWMProfile * This, + /* [in] */ const WCHAR *pwszName); + + HRESULT ( STDMETHODCALLTYPE *GetDescription )( + IWMProfile * This, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out][in] */ DWORD *pcchDescription); + + HRESULT ( STDMETHODCALLTYPE *SetDescription )( + IWMProfile * This, + /* [in] */ const WCHAR *pwszDescription); + + HRESULT ( STDMETHODCALLTYPE *GetStreamCount )( + IWMProfile * This, + /* [out] */ DWORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *GetStream )( + IWMProfile * This, + /* [in] */ DWORD dwStreamIndex, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *GetStreamByNumber )( + IWMProfile * This, + /* [in] */ WORD wStreamNum, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMProfile * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *RemoveStreamByNumber )( + IWMProfile * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMProfile * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *ReconfigStream )( + IWMProfile * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *CreateNewStream )( + IWMProfile * This, + /* [in] */ REFGUID guidStreamType, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *GetMutualExclusionCount )( + IWMProfile * This, + /* [out] */ DWORD *pcME); + + HRESULT ( STDMETHODCALLTYPE *GetMutualExclusion )( + IWMProfile * This, + /* [in] */ DWORD dwMEIndex, + /* [out] */ IWMMutualExclusion **ppME); + + HRESULT ( STDMETHODCALLTYPE *RemoveMutualExclusion )( + IWMProfile * This, + /* [in] */ IWMMutualExclusion *pME); + + HRESULT ( STDMETHODCALLTYPE *AddMutualExclusion )( + IWMProfile * This, + /* [in] */ IWMMutualExclusion *pME); + + HRESULT ( STDMETHODCALLTYPE *CreateNewMutualExclusion )( + IWMProfile * This, + /* [out] */ IWMMutualExclusion **ppME); + + END_INTERFACE + } IWMProfileVtbl; + + interface IWMProfile + { + CONST_VTBL struct IWMProfileVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMProfile_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMProfile_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMProfile_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMProfile_GetVersion(This,pdwVersion) \ + (This)->lpVtbl -> GetVersion(This,pdwVersion) + +#define IWMProfile_GetName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetName(This,pwszName,pcchName) + +#define IWMProfile_SetName(This,pwszName) \ + (This)->lpVtbl -> SetName(This,pwszName) + +#define IWMProfile_GetDescription(This,pwszDescription,pcchDescription) \ + (This)->lpVtbl -> GetDescription(This,pwszDescription,pcchDescription) + +#define IWMProfile_SetDescription(This,pwszDescription) \ + (This)->lpVtbl -> SetDescription(This,pwszDescription) + +#define IWMProfile_GetStreamCount(This,pcStreams) \ + (This)->lpVtbl -> GetStreamCount(This,pcStreams) + +#define IWMProfile_GetStream(This,dwStreamIndex,ppConfig) \ + (This)->lpVtbl -> GetStream(This,dwStreamIndex,ppConfig) + +#define IWMProfile_GetStreamByNumber(This,wStreamNum,ppConfig) \ + (This)->lpVtbl -> GetStreamByNumber(This,wStreamNum,ppConfig) + +#define IWMProfile_RemoveStream(This,pConfig) \ + (This)->lpVtbl -> RemoveStream(This,pConfig) + +#define IWMProfile_RemoveStreamByNumber(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStreamByNumber(This,wStreamNum) + +#define IWMProfile_AddStream(This,pConfig) \ + (This)->lpVtbl -> AddStream(This,pConfig) + +#define IWMProfile_ReconfigStream(This,pConfig) \ + (This)->lpVtbl -> ReconfigStream(This,pConfig) + +#define IWMProfile_CreateNewStream(This,guidStreamType,ppConfig) \ + (This)->lpVtbl -> CreateNewStream(This,guidStreamType,ppConfig) + +#define IWMProfile_GetMutualExclusionCount(This,pcME) \ + (This)->lpVtbl -> GetMutualExclusionCount(This,pcME) + +#define IWMProfile_GetMutualExclusion(This,dwMEIndex,ppME) \ + (This)->lpVtbl -> GetMutualExclusion(This,dwMEIndex,ppME) + +#define IWMProfile_RemoveMutualExclusion(This,pME) \ + (This)->lpVtbl -> RemoveMutualExclusion(This,pME) + +#define IWMProfile_AddMutualExclusion(This,pME) \ + (This)->lpVtbl -> AddMutualExclusion(This,pME) + +#define IWMProfile_CreateNewMutualExclusion(This,ppME) \ + (This)->lpVtbl -> CreateNewMutualExclusion(This,ppME) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetVersion_Proxy( + IWMProfile * This, + /* [out] */ WMT_VERSION *pdwVersion); + + +void __RPC_STUB IWMProfile_GetVersion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetName_Proxy( + IWMProfile * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ DWORD *pcchName); + + +void __RPC_STUB IWMProfile_GetName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_SetName_Proxy( + IWMProfile * This, + /* [in] */ const WCHAR *pwszName); + + +void __RPC_STUB IWMProfile_SetName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetDescription_Proxy( + IWMProfile * This, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out][in] */ DWORD *pcchDescription); + + +void __RPC_STUB IWMProfile_GetDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_SetDescription_Proxy( + IWMProfile * This, + /* [in] */ const WCHAR *pwszDescription); + + +void __RPC_STUB IWMProfile_SetDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetStreamCount_Proxy( + IWMProfile * This, + /* [out] */ DWORD *pcStreams); + + +void __RPC_STUB IWMProfile_GetStreamCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetStream_Proxy( + IWMProfile * This, + /* [in] */ DWORD dwStreamIndex, + /* [out] */ IWMStreamConfig **ppConfig); + + +void __RPC_STUB IWMProfile_GetStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetStreamByNumber_Proxy( + IWMProfile * This, + /* [in] */ WORD wStreamNum, + /* [out] */ IWMStreamConfig **ppConfig); + + +void __RPC_STUB IWMProfile_GetStreamByNumber_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_RemoveStream_Proxy( + IWMProfile * This, + /* [in] */ IWMStreamConfig *pConfig); + + +void __RPC_STUB IWMProfile_RemoveStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_RemoveStreamByNumber_Proxy( + IWMProfile * This, + /* [in] */ WORD wStreamNum); + + +void __RPC_STUB IWMProfile_RemoveStreamByNumber_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_AddStream_Proxy( + IWMProfile * This, + /* [in] */ IWMStreamConfig *pConfig); + + +void __RPC_STUB IWMProfile_AddStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_ReconfigStream_Proxy( + IWMProfile * This, + /* [in] */ IWMStreamConfig *pConfig); + + +void __RPC_STUB IWMProfile_ReconfigStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_CreateNewStream_Proxy( + IWMProfile * This, + /* [in] */ REFGUID guidStreamType, + /* [out] */ IWMStreamConfig **ppConfig); + + +void __RPC_STUB IWMProfile_CreateNewStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetMutualExclusionCount_Proxy( + IWMProfile * This, + /* [out] */ DWORD *pcME); + + +void __RPC_STUB IWMProfile_GetMutualExclusionCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_GetMutualExclusion_Proxy( + IWMProfile * This, + /* [in] */ DWORD dwMEIndex, + /* [out] */ IWMMutualExclusion **ppME); + + +void __RPC_STUB IWMProfile_GetMutualExclusion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_RemoveMutualExclusion_Proxy( + IWMProfile * This, + /* [in] */ IWMMutualExclusion *pME); + + +void __RPC_STUB IWMProfile_RemoveMutualExclusion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_AddMutualExclusion_Proxy( + IWMProfile * This, + /* [in] */ IWMMutualExclusion *pME); + + +void __RPC_STUB IWMProfile_AddMutualExclusion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile_CreateNewMutualExclusion_Proxy( + IWMProfile * This, + /* [out] */ IWMMutualExclusion **ppME); + + +void __RPC_STUB IWMProfile_CreateNewMutualExclusion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMProfile_INTERFACE_DEFINED__ */ + + +#ifndef __IWMProfile2_INTERFACE_DEFINED__ +#define __IWMProfile2_INTERFACE_DEFINED__ + +/* interface IWMProfile2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMProfile2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("07E72D33-D94E-4be7-8843-60AE5FF7E5F5") + IWMProfile2 : public IWMProfile + { + public: + virtual HRESULT STDMETHODCALLTYPE GetProfileID( + /* [out] */ GUID *pguidID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMProfile2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMProfile2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMProfile2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMProfile2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetVersion )( + IWMProfile2 * This, + /* [out] */ WMT_VERSION *pdwVersion); + + HRESULT ( STDMETHODCALLTYPE *GetName )( + IWMProfile2 * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ DWORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *SetName )( + IWMProfile2 * This, + /* [in] */ const WCHAR *pwszName); + + HRESULT ( STDMETHODCALLTYPE *GetDescription )( + IWMProfile2 * This, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out][in] */ DWORD *pcchDescription); + + HRESULT ( STDMETHODCALLTYPE *SetDescription )( + IWMProfile2 * This, + /* [in] */ const WCHAR *pwszDescription); + + HRESULT ( STDMETHODCALLTYPE *GetStreamCount )( + IWMProfile2 * This, + /* [out] */ DWORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *GetStream )( + IWMProfile2 * This, + /* [in] */ DWORD dwStreamIndex, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *GetStreamByNumber )( + IWMProfile2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMProfile2 * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *RemoveStreamByNumber )( + IWMProfile2 * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMProfile2 * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *ReconfigStream )( + IWMProfile2 * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *CreateNewStream )( + IWMProfile2 * This, + /* [in] */ REFGUID guidStreamType, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *GetMutualExclusionCount )( + IWMProfile2 * This, + /* [out] */ DWORD *pcME); + + HRESULT ( STDMETHODCALLTYPE *GetMutualExclusion )( + IWMProfile2 * This, + /* [in] */ DWORD dwMEIndex, + /* [out] */ IWMMutualExclusion **ppME); + + HRESULT ( STDMETHODCALLTYPE *RemoveMutualExclusion )( + IWMProfile2 * This, + /* [in] */ IWMMutualExclusion *pME); + + HRESULT ( STDMETHODCALLTYPE *AddMutualExclusion )( + IWMProfile2 * This, + /* [in] */ IWMMutualExclusion *pME); + + HRESULT ( STDMETHODCALLTYPE *CreateNewMutualExclusion )( + IWMProfile2 * This, + /* [out] */ IWMMutualExclusion **ppME); + + HRESULT ( STDMETHODCALLTYPE *GetProfileID )( + IWMProfile2 * This, + /* [out] */ GUID *pguidID); + + END_INTERFACE + } IWMProfile2Vtbl; + + interface IWMProfile2 + { + CONST_VTBL struct IWMProfile2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMProfile2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMProfile2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMProfile2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMProfile2_GetVersion(This,pdwVersion) \ + (This)->lpVtbl -> GetVersion(This,pdwVersion) + +#define IWMProfile2_GetName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetName(This,pwszName,pcchName) + +#define IWMProfile2_SetName(This,pwszName) \ + (This)->lpVtbl -> SetName(This,pwszName) + +#define IWMProfile2_GetDescription(This,pwszDescription,pcchDescription) \ + (This)->lpVtbl -> GetDescription(This,pwszDescription,pcchDescription) + +#define IWMProfile2_SetDescription(This,pwszDescription) \ + (This)->lpVtbl -> SetDescription(This,pwszDescription) + +#define IWMProfile2_GetStreamCount(This,pcStreams) \ + (This)->lpVtbl -> GetStreamCount(This,pcStreams) + +#define IWMProfile2_GetStream(This,dwStreamIndex,ppConfig) \ + (This)->lpVtbl -> GetStream(This,dwStreamIndex,ppConfig) + +#define IWMProfile2_GetStreamByNumber(This,wStreamNum,ppConfig) \ + (This)->lpVtbl -> GetStreamByNumber(This,wStreamNum,ppConfig) + +#define IWMProfile2_RemoveStream(This,pConfig) \ + (This)->lpVtbl -> RemoveStream(This,pConfig) + +#define IWMProfile2_RemoveStreamByNumber(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStreamByNumber(This,wStreamNum) + +#define IWMProfile2_AddStream(This,pConfig) \ + (This)->lpVtbl -> AddStream(This,pConfig) + +#define IWMProfile2_ReconfigStream(This,pConfig) \ + (This)->lpVtbl -> ReconfigStream(This,pConfig) + +#define IWMProfile2_CreateNewStream(This,guidStreamType,ppConfig) \ + (This)->lpVtbl -> CreateNewStream(This,guidStreamType,ppConfig) + +#define IWMProfile2_GetMutualExclusionCount(This,pcME) \ + (This)->lpVtbl -> GetMutualExclusionCount(This,pcME) + +#define IWMProfile2_GetMutualExclusion(This,dwMEIndex,ppME) \ + (This)->lpVtbl -> GetMutualExclusion(This,dwMEIndex,ppME) + +#define IWMProfile2_RemoveMutualExclusion(This,pME) \ + (This)->lpVtbl -> RemoveMutualExclusion(This,pME) + +#define IWMProfile2_AddMutualExclusion(This,pME) \ + (This)->lpVtbl -> AddMutualExclusion(This,pME) + +#define IWMProfile2_CreateNewMutualExclusion(This,ppME) \ + (This)->lpVtbl -> CreateNewMutualExclusion(This,ppME) + + +#define IWMProfile2_GetProfileID(This,pguidID) \ + (This)->lpVtbl -> GetProfileID(This,pguidID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMProfile2_GetProfileID_Proxy( + IWMProfile2 * This, + /* [out] */ GUID *pguidID); + + +void __RPC_STUB IWMProfile2_GetProfileID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMProfile2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMProfile3_INTERFACE_DEFINED__ +#define __IWMProfile3_INTERFACE_DEFINED__ + +/* interface IWMProfile3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMProfile3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("00EF96CC-A461-4546-8BCD-C9A28F0E06F5") + IWMProfile3 : public IWMProfile2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStorageFormat( + /* [out] */ WMT_STORAGE_FORMAT *pnStorageFormat) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetStorageFormat( + /* [in] */ WMT_STORAGE_FORMAT nStorageFormat) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBandwidthSharingCount( + /* [out] */ DWORD *pcBS) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBandwidthSharing( + /* [in] */ DWORD dwBSIndex, + /* [out] */ IWMBandwidthSharing **ppBS) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveBandwidthSharing( + /* [in] */ IWMBandwidthSharing *pBS) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddBandwidthSharing( + /* [in] */ IWMBandwidthSharing *pBS) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateNewBandwidthSharing( + /* [out] */ IWMBandwidthSharing **ppBS) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamPrioritization( + /* [out] */ IWMStreamPrioritization **ppSP) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetStreamPrioritization( + /* [in] */ IWMStreamPrioritization *pSP) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveStreamPrioritization( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateNewStreamPrioritization( + /* [out] */ IWMStreamPrioritization **ppSP) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetExpectedPacketCount( + /* [in] */ QWORD msDuration, + /* [out] */ QWORD *pcPackets) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMProfile3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMProfile3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMProfile3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMProfile3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetVersion )( + IWMProfile3 * This, + /* [out] */ WMT_VERSION *pdwVersion); + + HRESULT ( STDMETHODCALLTYPE *GetName )( + IWMProfile3 * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ DWORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *SetName )( + IWMProfile3 * This, + /* [in] */ const WCHAR *pwszName); + + HRESULT ( STDMETHODCALLTYPE *GetDescription )( + IWMProfile3 * This, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out][in] */ DWORD *pcchDescription); + + HRESULT ( STDMETHODCALLTYPE *SetDescription )( + IWMProfile3 * This, + /* [in] */ const WCHAR *pwszDescription); + + HRESULT ( STDMETHODCALLTYPE *GetStreamCount )( + IWMProfile3 * This, + /* [out] */ DWORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *GetStream )( + IWMProfile3 * This, + /* [in] */ DWORD dwStreamIndex, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *GetStreamByNumber )( + IWMProfile3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMProfile3 * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *RemoveStreamByNumber )( + IWMProfile3 * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMProfile3 * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *ReconfigStream )( + IWMProfile3 * This, + /* [in] */ IWMStreamConfig *pConfig); + + HRESULT ( STDMETHODCALLTYPE *CreateNewStream )( + IWMProfile3 * This, + /* [in] */ REFGUID guidStreamType, + /* [out] */ IWMStreamConfig **ppConfig); + + HRESULT ( STDMETHODCALLTYPE *GetMutualExclusionCount )( + IWMProfile3 * This, + /* [out] */ DWORD *pcME); + + HRESULT ( STDMETHODCALLTYPE *GetMutualExclusion )( + IWMProfile3 * This, + /* [in] */ DWORD dwMEIndex, + /* [out] */ IWMMutualExclusion **ppME); + + HRESULT ( STDMETHODCALLTYPE *RemoveMutualExclusion )( + IWMProfile3 * This, + /* [in] */ IWMMutualExclusion *pME); + + HRESULT ( STDMETHODCALLTYPE *AddMutualExclusion )( + IWMProfile3 * This, + /* [in] */ IWMMutualExclusion *pME); + + HRESULT ( STDMETHODCALLTYPE *CreateNewMutualExclusion )( + IWMProfile3 * This, + /* [out] */ IWMMutualExclusion **ppME); + + HRESULT ( STDMETHODCALLTYPE *GetProfileID )( + IWMProfile3 * This, + /* [out] */ GUID *pguidID); + + HRESULT ( STDMETHODCALLTYPE *GetStorageFormat )( + IWMProfile3 * This, + /* [out] */ WMT_STORAGE_FORMAT *pnStorageFormat); + + HRESULT ( STDMETHODCALLTYPE *SetStorageFormat )( + IWMProfile3 * This, + /* [in] */ WMT_STORAGE_FORMAT nStorageFormat); + + HRESULT ( STDMETHODCALLTYPE *GetBandwidthSharingCount )( + IWMProfile3 * This, + /* [out] */ DWORD *pcBS); + + HRESULT ( STDMETHODCALLTYPE *GetBandwidthSharing )( + IWMProfile3 * This, + /* [in] */ DWORD dwBSIndex, + /* [out] */ IWMBandwidthSharing **ppBS); + + HRESULT ( STDMETHODCALLTYPE *RemoveBandwidthSharing )( + IWMProfile3 * This, + /* [in] */ IWMBandwidthSharing *pBS); + + HRESULT ( STDMETHODCALLTYPE *AddBandwidthSharing )( + IWMProfile3 * This, + /* [in] */ IWMBandwidthSharing *pBS); + + HRESULT ( STDMETHODCALLTYPE *CreateNewBandwidthSharing )( + IWMProfile3 * This, + /* [out] */ IWMBandwidthSharing **ppBS); + + HRESULT ( STDMETHODCALLTYPE *GetStreamPrioritization )( + IWMProfile3 * This, + /* [out] */ IWMStreamPrioritization **ppSP); + + HRESULT ( STDMETHODCALLTYPE *SetStreamPrioritization )( + IWMProfile3 * This, + /* [in] */ IWMStreamPrioritization *pSP); + + HRESULT ( STDMETHODCALLTYPE *RemoveStreamPrioritization )( + IWMProfile3 * This); + + HRESULT ( STDMETHODCALLTYPE *CreateNewStreamPrioritization )( + IWMProfile3 * This, + /* [out] */ IWMStreamPrioritization **ppSP); + + HRESULT ( STDMETHODCALLTYPE *GetExpectedPacketCount )( + IWMProfile3 * This, + /* [in] */ QWORD msDuration, + /* [out] */ QWORD *pcPackets); + + END_INTERFACE + } IWMProfile3Vtbl; + + interface IWMProfile3 + { + CONST_VTBL struct IWMProfile3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMProfile3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMProfile3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMProfile3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMProfile3_GetVersion(This,pdwVersion) \ + (This)->lpVtbl -> GetVersion(This,pdwVersion) + +#define IWMProfile3_GetName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetName(This,pwszName,pcchName) + +#define IWMProfile3_SetName(This,pwszName) \ + (This)->lpVtbl -> SetName(This,pwszName) + +#define IWMProfile3_GetDescription(This,pwszDescription,pcchDescription) \ + (This)->lpVtbl -> GetDescription(This,pwszDescription,pcchDescription) + +#define IWMProfile3_SetDescription(This,pwszDescription) \ + (This)->lpVtbl -> SetDescription(This,pwszDescription) + +#define IWMProfile3_GetStreamCount(This,pcStreams) \ + (This)->lpVtbl -> GetStreamCount(This,pcStreams) + +#define IWMProfile3_GetStream(This,dwStreamIndex,ppConfig) \ + (This)->lpVtbl -> GetStream(This,dwStreamIndex,ppConfig) + +#define IWMProfile3_GetStreamByNumber(This,wStreamNum,ppConfig) \ + (This)->lpVtbl -> GetStreamByNumber(This,wStreamNum,ppConfig) + +#define IWMProfile3_RemoveStream(This,pConfig) \ + (This)->lpVtbl -> RemoveStream(This,pConfig) + +#define IWMProfile3_RemoveStreamByNumber(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStreamByNumber(This,wStreamNum) + +#define IWMProfile3_AddStream(This,pConfig) \ + (This)->lpVtbl -> AddStream(This,pConfig) + +#define IWMProfile3_ReconfigStream(This,pConfig) \ + (This)->lpVtbl -> ReconfigStream(This,pConfig) + +#define IWMProfile3_CreateNewStream(This,guidStreamType,ppConfig) \ + (This)->lpVtbl -> CreateNewStream(This,guidStreamType,ppConfig) + +#define IWMProfile3_GetMutualExclusionCount(This,pcME) \ + (This)->lpVtbl -> GetMutualExclusionCount(This,pcME) + +#define IWMProfile3_GetMutualExclusion(This,dwMEIndex,ppME) \ + (This)->lpVtbl -> GetMutualExclusion(This,dwMEIndex,ppME) + +#define IWMProfile3_RemoveMutualExclusion(This,pME) \ + (This)->lpVtbl -> RemoveMutualExclusion(This,pME) + +#define IWMProfile3_AddMutualExclusion(This,pME) \ + (This)->lpVtbl -> AddMutualExclusion(This,pME) + +#define IWMProfile3_CreateNewMutualExclusion(This,ppME) \ + (This)->lpVtbl -> CreateNewMutualExclusion(This,ppME) + + +#define IWMProfile3_GetProfileID(This,pguidID) \ + (This)->lpVtbl -> GetProfileID(This,pguidID) + + +#define IWMProfile3_GetStorageFormat(This,pnStorageFormat) \ + (This)->lpVtbl -> GetStorageFormat(This,pnStorageFormat) + +#define IWMProfile3_SetStorageFormat(This,nStorageFormat) \ + (This)->lpVtbl -> SetStorageFormat(This,nStorageFormat) + +#define IWMProfile3_GetBandwidthSharingCount(This,pcBS) \ + (This)->lpVtbl -> GetBandwidthSharingCount(This,pcBS) + +#define IWMProfile3_GetBandwidthSharing(This,dwBSIndex,ppBS) \ + (This)->lpVtbl -> GetBandwidthSharing(This,dwBSIndex,ppBS) + +#define IWMProfile3_RemoveBandwidthSharing(This,pBS) \ + (This)->lpVtbl -> RemoveBandwidthSharing(This,pBS) + +#define IWMProfile3_AddBandwidthSharing(This,pBS) \ + (This)->lpVtbl -> AddBandwidthSharing(This,pBS) + +#define IWMProfile3_CreateNewBandwidthSharing(This,ppBS) \ + (This)->lpVtbl -> CreateNewBandwidthSharing(This,ppBS) + +#define IWMProfile3_GetStreamPrioritization(This,ppSP) \ + (This)->lpVtbl -> GetStreamPrioritization(This,ppSP) + +#define IWMProfile3_SetStreamPrioritization(This,pSP) \ + (This)->lpVtbl -> SetStreamPrioritization(This,pSP) + +#define IWMProfile3_RemoveStreamPrioritization(This) \ + (This)->lpVtbl -> RemoveStreamPrioritization(This) + +#define IWMProfile3_CreateNewStreamPrioritization(This,ppSP) \ + (This)->lpVtbl -> CreateNewStreamPrioritization(This,ppSP) + +#define IWMProfile3_GetExpectedPacketCount(This,msDuration,pcPackets) \ + (This)->lpVtbl -> GetExpectedPacketCount(This,msDuration,pcPackets) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMProfile3_GetStorageFormat_Proxy( + IWMProfile3 * This, + /* [out] */ WMT_STORAGE_FORMAT *pnStorageFormat); + + +void __RPC_STUB IWMProfile3_GetStorageFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_SetStorageFormat_Proxy( + IWMProfile3 * This, + /* [in] */ WMT_STORAGE_FORMAT nStorageFormat); + + +void __RPC_STUB IWMProfile3_SetStorageFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_GetBandwidthSharingCount_Proxy( + IWMProfile3 * This, + /* [out] */ DWORD *pcBS); + + +void __RPC_STUB IWMProfile3_GetBandwidthSharingCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_GetBandwidthSharing_Proxy( + IWMProfile3 * This, + /* [in] */ DWORD dwBSIndex, + /* [out] */ IWMBandwidthSharing **ppBS); + + +void __RPC_STUB IWMProfile3_GetBandwidthSharing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_RemoveBandwidthSharing_Proxy( + IWMProfile3 * This, + /* [in] */ IWMBandwidthSharing *pBS); + + +void __RPC_STUB IWMProfile3_RemoveBandwidthSharing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_AddBandwidthSharing_Proxy( + IWMProfile3 * This, + /* [in] */ IWMBandwidthSharing *pBS); + + +void __RPC_STUB IWMProfile3_AddBandwidthSharing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_CreateNewBandwidthSharing_Proxy( + IWMProfile3 * This, + /* [out] */ IWMBandwidthSharing **ppBS); + + +void __RPC_STUB IWMProfile3_CreateNewBandwidthSharing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_GetStreamPrioritization_Proxy( + IWMProfile3 * This, + /* [out] */ IWMStreamPrioritization **ppSP); + + +void __RPC_STUB IWMProfile3_GetStreamPrioritization_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_SetStreamPrioritization_Proxy( + IWMProfile3 * This, + /* [in] */ IWMStreamPrioritization *pSP); + + +void __RPC_STUB IWMProfile3_SetStreamPrioritization_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_RemoveStreamPrioritization_Proxy( + IWMProfile3 * This); + + +void __RPC_STUB IWMProfile3_RemoveStreamPrioritization_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_CreateNewStreamPrioritization_Proxy( + IWMProfile3 * This, + /* [out] */ IWMStreamPrioritization **ppSP); + + +void __RPC_STUB IWMProfile3_CreateNewStreamPrioritization_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMProfile3_GetExpectedPacketCount_Proxy( + IWMProfile3 * This, + /* [in] */ QWORD msDuration, + /* [out] */ QWORD *pcPackets); + + +void __RPC_STUB IWMProfile3_GetExpectedPacketCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMProfile3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMStreamConfig_INTERFACE_DEFINED__ +#define __IWMStreamConfig_INTERFACE_DEFINED__ + +/* interface IWMStreamConfig */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMStreamConfig; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BDC-2B2B-11d3-B36B-00C04F6108FF") + IWMStreamConfig : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStreamType( + /* [out] */ GUID *pguidStreamType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamNumber( + /* [out] */ WORD *pwStreamNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetStreamNumber( + /* [in] */ WORD wStreamNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamName( + /* [size_is][out] */ WCHAR *pwszStreamName, + /* [out][in] */ WORD *pcchStreamName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetStreamName( + /* [in] */ WCHAR *pwszStreamName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectionName( + /* [size_is][out] */ WCHAR *pwszInputName, + /* [out][in] */ WORD *pcchInputName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetConnectionName( + /* [in] */ WCHAR *pwszInputName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBitrate( + /* [out] */ DWORD *pdwBitrate) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBitrate( + /* [in] */ DWORD pdwBitrate) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBufferWindow( + /* [out] */ DWORD *pmsBufferWindow) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBufferWindow( + /* [in] */ DWORD msBufferWindow) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMStreamConfigVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMStreamConfig * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMStreamConfig * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMStreamConfig * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreamType )( + IWMStreamConfig * This, + /* [out] */ GUID *pguidStreamType); + + HRESULT ( STDMETHODCALLTYPE *GetStreamNumber )( + IWMStreamConfig * This, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *SetStreamNumber )( + IWMStreamConfig * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetStreamName )( + IWMStreamConfig * This, + /* [size_is][out] */ WCHAR *pwszStreamName, + /* [out][in] */ WORD *pcchStreamName); + + HRESULT ( STDMETHODCALLTYPE *SetStreamName )( + IWMStreamConfig * This, + /* [in] */ WCHAR *pwszStreamName); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionName )( + IWMStreamConfig * This, + /* [size_is][out] */ WCHAR *pwszInputName, + /* [out][in] */ WORD *pcchInputName); + + HRESULT ( STDMETHODCALLTYPE *SetConnectionName )( + IWMStreamConfig * This, + /* [in] */ WCHAR *pwszInputName); + + HRESULT ( STDMETHODCALLTYPE *GetBitrate )( + IWMStreamConfig * This, + /* [out] */ DWORD *pdwBitrate); + + HRESULT ( STDMETHODCALLTYPE *SetBitrate )( + IWMStreamConfig * This, + /* [in] */ DWORD pdwBitrate); + + HRESULT ( STDMETHODCALLTYPE *GetBufferWindow )( + IWMStreamConfig * This, + /* [out] */ DWORD *pmsBufferWindow); + + HRESULT ( STDMETHODCALLTYPE *SetBufferWindow )( + IWMStreamConfig * This, + /* [in] */ DWORD msBufferWindow); + + END_INTERFACE + } IWMStreamConfigVtbl; + + interface IWMStreamConfig + { + CONST_VTBL struct IWMStreamConfigVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMStreamConfig_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMStreamConfig_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMStreamConfig_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMStreamConfig_GetStreamType(This,pguidStreamType) \ + (This)->lpVtbl -> GetStreamType(This,pguidStreamType) + +#define IWMStreamConfig_GetStreamNumber(This,pwStreamNum) \ + (This)->lpVtbl -> GetStreamNumber(This,pwStreamNum) + +#define IWMStreamConfig_SetStreamNumber(This,wStreamNum) \ + (This)->lpVtbl -> SetStreamNumber(This,wStreamNum) + +#define IWMStreamConfig_GetStreamName(This,pwszStreamName,pcchStreamName) \ + (This)->lpVtbl -> GetStreamName(This,pwszStreamName,pcchStreamName) + +#define IWMStreamConfig_SetStreamName(This,pwszStreamName) \ + (This)->lpVtbl -> SetStreamName(This,pwszStreamName) + +#define IWMStreamConfig_GetConnectionName(This,pwszInputName,pcchInputName) \ + (This)->lpVtbl -> GetConnectionName(This,pwszInputName,pcchInputName) + +#define IWMStreamConfig_SetConnectionName(This,pwszInputName) \ + (This)->lpVtbl -> SetConnectionName(This,pwszInputName) + +#define IWMStreamConfig_GetBitrate(This,pdwBitrate) \ + (This)->lpVtbl -> GetBitrate(This,pdwBitrate) + +#define IWMStreamConfig_SetBitrate(This,pdwBitrate) \ + (This)->lpVtbl -> SetBitrate(This,pdwBitrate) + +#define IWMStreamConfig_GetBufferWindow(This,pmsBufferWindow) \ + (This)->lpVtbl -> GetBufferWindow(This,pmsBufferWindow) + +#define IWMStreamConfig_SetBufferWindow(This,msBufferWindow) \ + (This)->lpVtbl -> SetBufferWindow(This,msBufferWindow) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_GetStreamType_Proxy( + IWMStreamConfig * This, + /* [out] */ GUID *pguidStreamType); + + +void __RPC_STUB IWMStreamConfig_GetStreamType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_GetStreamNumber_Proxy( + IWMStreamConfig * This, + /* [out] */ WORD *pwStreamNum); + + +void __RPC_STUB IWMStreamConfig_GetStreamNumber_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_SetStreamNumber_Proxy( + IWMStreamConfig * This, + /* [in] */ WORD wStreamNum); + + +void __RPC_STUB IWMStreamConfig_SetStreamNumber_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_GetStreamName_Proxy( + IWMStreamConfig * This, + /* [size_is][out] */ WCHAR *pwszStreamName, + /* [out][in] */ WORD *pcchStreamName); + + +void __RPC_STUB IWMStreamConfig_GetStreamName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_SetStreamName_Proxy( + IWMStreamConfig * This, + /* [in] */ WCHAR *pwszStreamName); + + +void __RPC_STUB IWMStreamConfig_SetStreamName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_GetConnectionName_Proxy( + IWMStreamConfig * This, + /* [size_is][out] */ WCHAR *pwszInputName, + /* [out][in] */ WORD *pcchInputName); + + +void __RPC_STUB IWMStreamConfig_GetConnectionName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_SetConnectionName_Proxy( + IWMStreamConfig * This, + /* [in] */ WCHAR *pwszInputName); + + +void __RPC_STUB IWMStreamConfig_SetConnectionName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_GetBitrate_Proxy( + IWMStreamConfig * This, + /* [out] */ DWORD *pdwBitrate); + + +void __RPC_STUB IWMStreamConfig_GetBitrate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_SetBitrate_Proxy( + IWMStreamConfig * This, + /* [in] */ DWORD pdwBitrate); + + +void __RPC_STUB IWMStreamConfig_SetBitrate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_GetBufferWindow_Proxy( + IWMStreamConfig * This, + /* [out] */ DWORD *pmsBufferWindow); + + +void __RPC_STUB IWMStreamConfig_GetBufferWindow_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig_SetBufferWindow_Proxy( + IWMStreamConfig * This, + /* [in] */ DWORD msBufferWindow); + + +void __RPC_STUB IWMStreamConfig_SetBufferWindow_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMStreamConfig_INTERFACE_DEFINED__ */ + + +#ifndef __IWMStreamConfig2_INTERFACE_DEFINED__ +#define __IWMStreamConfig2_INTERFACE_DEFINED__ + +/* interface IWMStreamConfig2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMStreamConfig2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("7688D8CB-FC0D-43BD-9459-5A8DEC200CFA") + IWMStreamConfig2 : public IWMStreamConfig + { + public: + virtual HRESULT STDMETHODCALLTYPE GetTransportType( + /* [out] */ WMT_TRANSPORT_TYPE *pnTransportType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetTransportType( + /* [in] */ WMT_TRANSPORT_TYPE nTransportType) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddDataUnitExtension( + /* [in] */ GUID guidExtensionSystemID, + /* [in] */ WORD cbExtensionDataSize, + /* [size_is][in] */ BYTE *pbExtensionSystemInfo, + /* [in] */ DWORD cbExtensionSystemInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDataUnitExtensionCount( + /* [out] */ WORD *pcDataUnitExtensions) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDataUnitExtension( + /* [in] */ WORD wDataUnitExtensionNumber, + /* [out] */ GUID *pguidExtensionSystemID, + /* [out] */ WORD *pcbExtensionDataSize, + /* [size_is][out] */ BYTE *pbExtensionSystemInfo, + /* [out][in] */ DWORD *pcbExtensionSystemInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveAllDataUnitExtensions( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMStreamConfig2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMStreamConfig2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMStreamConfig2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMStreamConfig2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreamType )( + IWMStreamConfig2 * This, + /* [out] */ GUID *pguidStreamType); + + HRESULT ( STDMETHODCALLTYPE *GetStreamNumber )( + IWMStreamConfig2 * This, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *SetStreamNumber )( + IWMStreamConfig2 * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetStreamName )( + IWMStreamConfig2 * This, + /* [size_is][out] */ WCHAR *pwszStreamName, + /* [out][in] */ WORD *pcchStreamName); + + HRESULT ( STDMETHODCALLTYPE *SetStreamName )( + IWMStreamConfig2 * This, + /* [in] */ WCHAR *pwszStreamName); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionName )( + IWMStreamConfig2 * This, + /* [size_is][out] */ WCHAR *pwszInputName, + /* [out][in] */ WORD *pcchInputName); + + HRESULT ( STDMETHODCALLTYPE *SetConnectionName )( + IWMStreamConfig2 * This, + /* [in] */ WCHAR *pwszInputName); + + HRESULT ( STDMETHODCALLTYPE *GetBitrate )( + IWMStreamConfig2 * This, + /* [out] */ DWORD *pdwBitrate); + + HRESULT ( STDMETHODCALLTYPE *SetBitrate )( + IWMStreamConfig2 * This, + /* [in] */ DWORD pdwBitrate); + + HRESULT ( STDMETHODCALLTYPE *GetBufferWindow )( + IWMStreamConfig2 * This, + /* [out] */ DWORD *pmsBufferWindow); + + HRESULT ( STDMETHODCALLTYPE *SetBufferWindow )( + IWMStreamConfig2 * This, + /* [in] */ DWORD msBufferWindow); + + HRESULT ( STDMETHODCALLTYPE *GetTransportType )( + IWMStreamConfig2 * This, + /* [out] */ WMT_TRANSPORT_TYPE *pnTransportType); + + HRESULT ( STDMETHODCALLTYPE *SetTransportType )( + IWMStreamConfig2 * This, + /* [in] */ WMT_TRANSPORT_TYPE nTransportType); + + HRESULT ( STDMETHODCALLTYPE *AddDataUnitExtension )( + IWMStreamConfig2 * This, + /* [in] */ GUID guidExtensionSystemID, + /* [in] */ WORD cbExtensionDataSize, + /* [size_is][in] */ BYTE *pbExtensionSystemInfo, + /* [in] */ DWORD cbExtensionSystemInfo); + + HRESULT ( STDMETHODCALLTYPE *GetDataUnitExtensionCount )( + IWMStreamConfig2 * This, + /* [out] */ WORD *pcDataUnitExtensions); + + HRESULT ( STDMETHODCALLTYPE *GetDataUnitExtension )( + IWMStreamConfig2 * This, + /* [in] */ WORD wDataUnitExtensionNumber, + /* [out] */ GUID *pguidExtensionSystemID, + /* [out] */ WORD *pcbExtensionDataSize, + /* [size_is][out] */ BYTE *pbExtensionSystemInfo, + /* [out][in] */ DWORD *pcbExtensionSystemInfo); + + HRESULT ( STDMETHODCALLTYPE *RemoveAllDataUnitExtensions )( + IWMStreamConfig2 * This); + + END_INTERFACE + } IWMStreamConfig2Vtbl; + + interface IWMStreamConfig2 + { + CONST_VTBL struct IWMStreamConfig2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMStreamConfig2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMStreamConfig2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMStreamConfig2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMStreamConfig2_GetStreamType(This,pguidStreamType) \ + (This)->lpVtbl -> GetStreamType(This,pguidStreamType) + +#define IWMStreamConfig2_GetStreamNumber(This,pwStreamNum) \ + (This)->lpVtbl -> GetStreamNumber(This,pwStreamNum) + +#define IWMStreamConfig2_SetStreamNumber(This,wStreamNum) \ + (This)->lpVtbl -> SetStreamNumber(This,wStreamNum) + +#define IWMStreamConfig2_GetStreamName(This,pwszStreamName,pcchStreamName) \ + (This)->lpVtbl -> GetStreamName(This,pwszStreamName,pcchStreamName) + +#define IWMStreamConfig2_SetStreamName(This,pwszStreamName) \ + (This)->lpVtbl -> SetStreamName(This,pwszStreamName) + +#define IWMStreamConfig2_GetConnectionName(This,pwszInputName,pcchInputName) \ + (This)->lpVtbl -> GetConnectionName(This,pwszInputName,pcchInputName) + +#define IWMStreamConfig2_SetConnectionName(This,pwszInputName) \ + (This)->lpVtbl -> SetConnectionName(This,pwszInputName) + +#define IWMStreamConfig2_GetBitrate(This,pdwBitrate) \ + (This)->lpVtbl -> GetBitrate(This,pdwBitrate) + +#define IWMStreamConfig2_SetBitrate(This,pdwBitrate) \ + (This)->lpVtbl -> SetBitrate(This,pdwBitrate) + +#define IWMStreamConfig2_GetBufferWindow(This,pmsBufferWindow) \ + (This)->lpVtbl -> GetBufferWindow(This,pmsBufferWindow) + +#define IWMStreamConfig2_SetBufferWindow(This,msBufferWindow) \ + (This)->lpVtbl -> SetBufferWindow(This,msBufferWindow) + + +#define IWMStreamConfig2_GetTransportType(This,pnTransportType) \ + (This)->lpVtbl -> GetTransportType(This,pnTransportType) + +#define IWMStreamConfig2_SetTransportType(This,nTransportType) \ + (This)->lpVtbl -> SetTransportType(This,nTransportType) + +#define IWMStreamConfig2_AddDataUnitExtension(This,guidExtensionSystemID,cbExtensionDataSize,pbExtensionSystemInfo,cbExtensionSystemInfo) \ + (This)->lpVtbl -> AddDataUnitExtension(This,guidExtensionSystemID,cbExtensionDataSize,pbExtensionSystemInfo,cbExtensionSystemInfo) + +#define IWMStreamConfig2_GetDataUnitExtensionCount(This,pcDataUnitExtensions) \ + (This)->lpVtbl -> GetDataUnitExtensionCount(This,pcDataUnitExtensions) + +#define IWMStreamConfig2_GetDataUnitExtension(This,wDataUnitExtensionNumber,pguidExtensionSystemID,pcbExtensionDataSize,pbExtensionSystemInfo,pcbExtensionSystemInfo) \ + (This)->lpVtbl -> GetDataUnitExtension(This,wDataUnitExtensionNumber,pguidExtensionSystemID,pcbExtensionDataSize,pbExtensionSystemInfo,pcbExtensionSystemInfo) + +#define IWMStreamConfig2_RemoveAllDataUnitExtensions(This) \ + (This)->lpVtbl -> RemoveAllDataUnitExtensions(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig2_GetTransportType_Proxy( + IWMStreamConfig2 * This, + /* [out] */ WMT_TRANSPORT_TYPE *pnTransportType); + + +void __RPC_STUB IWMStreamConfig2_GetTransportType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig2_SetTransportType_Proxy( + IWMStreamConfig2 * This, + /* [in] */ WMT_TRANSPORT_TYPE nTransportType); + + +void __RPC_STUB IWMStreamConfig2_SetTransportType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig2_AddDataUnitExtension_Proxy( + IWMStreamConfig2 * This, + /* [in] */ GUID guidExtensionSystemID, + /* [in] */ WORD cbExtensionDataSize, + /* [size_is][in] */ BYTE *pbExtensionSystemInfo, + /* [in] */ DWORD cbExtensionSystemInfo); + + +void __RPC_STUB IWMStreamConfig2_AddDataUnitExtension_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig2_GetDataUnitExtensionCount_Proxy( + IWMStreamConfig2 * This, + /* [out] */ WORD *pcDataUnitExtensions); + + +void __RPC_STUB IWMStreamConfig2_GetDataUnitExtensionCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig2_GetDataUnitExtension_Proxy( + IWMStreamConfig2 * This, + /* [in] */ WORD wDataUnitExtensionNumber, + /* [out] */ GUID *pguidExtensionSystemID, + /* [out] */ WORD *pcbExtensionDataSize, + /* [size_is][out] */ BYTE *pbExtensionSystemInfo, + /* [out][in] */ DWORD *pcbExtensionSystemInfo); + + +void __RPC_STUB IWMStreamConfig2_GetDataUnitExtension_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig2_RemoveAllDataUnitExtensions_Proxy( + IWMStreamConfig2 * This); + + +void __RPC_STUB IWMStreamConfig2_RemoveAllDataUnitExtensions_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMStreamConfig2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMStreamConfig3_INTERFACE_DEFINED__ +#define __IWMStreamConfig3_INTERFACE_DEFINED__ + +/* interface IWMStreamConfig3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMStreamConfig3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("CB164104-3AA9-45a7-9AC9-4DAEE131D6E1") + IWMStreamConfig3 : public IWMStreamConfig2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetLanguage( + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetLanguage( + /* [in] */ LPWSTR pwszLanguageString) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMStreamConfig3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMStreamConfig3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMStreamConfig3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMStreamConfig3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreamType )( + IWMStreamConfig3 * This, + /* [out] */ GUID *pguidStreamType); + + HRESULT ( STDMETHODCALLTYPE *GetStreamNumber )( + IWMStreamConfig3 * This, + /* [out] */ WORD *pwStreamNum); + + HRESULT ( STDMETHODCALLTYPE *SetStreamNumber )( + IWMStreamConfig3 * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetStreamName )( + IWMStreamConfig3 * This, + /* [size_is][out] */ WCHAR *pwszStreamName, + /* [out][in] */ WORD *pcchStreamName); + + HRESULT ( STDMETHODCALLTYPE *SetStreamName )( + IWMStreamConfig3 * This, + /* [in] */ WCHAR *pwszStreamName); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionName )( + IWMStreamConfig3 * This, + /* [size_is][out] */ WCHAR *pwszInputName, + /* [out][in] */ WORD *pcchInputName); + + HRESULT ( STDMETHODCALLTYPE *SetConnectionName )( + IWMStreamConfig3 * This, + /* [in] */ WCHAR *pwszInputName); + + HRESULT ( STDMETHODCALLTYPE *GetBitrate )( + IWMStreamConfig3 * This, + /* [out] */ DWORD *pdwBitrate); + + HRESULT ( STDMETHODCALLTYPE *SetBitrate )( + IWMStreamConfig3 * This, + /* [in] */ DWORD pdwBitrate); + + HRESULT ( STDMETHODCALLTYPE *GetBufferWindow )( + IWMStreamConfig3 * This, + /* [out] */ DWORD *pmsBufferWindow); + + HRESULT ( STDMETHODCALLTYPE *SetBufferWindow )( + IWMStreamConfig3 * This, + /* [in] */ DWORD msBufferWindow); + + HRESULT ( STDMETHODCALLTYPE *GetTransportType )( + IWMStreamConfig3 * This, + /* [out] */ WMT_TRANSPORT_TYPE *pnTransportType); + + HRESULT ( STDMETHODCALLTYPE *SetTransportType )( + IWMStreamConfig3 * This, + /* [in] */ WMT_TRANSPORT_TYPE nTransportType); + + HRESULT ( STDMETHODCALLTYPE *AddDataUnitExtension )( + IWMStreamConfig3 * This, + /* [in] */ GUID guidExtensionSystemID, + /* [in] */ WORD cbExtensionDataSize, + /* [size_is][in] */ BYTE *pbExtensionSystemInfo, + /* [in] */ DWORD cbExtensionSystemInfo); + + HRESULT ( STDMETHODCALLTYPE *GetDataUnitExtensionCount )( + IWMStreamConfig3 * This, + /* [out] */ WORD *pcDataUnitExtensions); + + HRESULT ( STDMETHODCALLTYPE *GetDataUnitExtension )( + IWMStreamConfig3 * This, + /* [in] */ WORD wDataUnitExtensionNumber, + /* [out] */ GUID *pguidExtensionSystemID, + /* [out] */ WORD *pcbExtensionDataSize, + /* [size_is][out] */ BYTE *pbExtensionSystemInfo, + /* [out][in] */ DWORD *pcbExtensionSystemInfo); + + HRESULT ( STDMETHODCALLTYPE *RemoveAllDataUnitExtensions )( + IWMStreamConfig3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetLanguage )( + IWMStreamConfig3 * This, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength); + + HRESULT ( STDMETHODCALLTYPE *SetLanguage )( + IWMStreamConfig3 * This, + /* [in] */ LPWSTR pwszLanguageString); + + END_INTERFACE + } IWMStreamConfig3Vtbl; + + interface IWMStreamConfig3 + { + CONST_VTBL struct IWMStreamConfig3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMStreamConfig3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMStreamConfig3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMStreamConfig3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMStreamConfig3_GetStreamType(This,pguidStreamType) \ + (This)->lpVtbl -> GetStreamType(This,pguidStreamType) + +#define IWMStreamConfig3_GetStreamNumber(This,pwStreamNum) \ + (This)->lpVtbl -> GetStreamNumber(This,pwStreamNum) + +#define IWMStreamConfig3_SetStreamNumber(This,wStreamNum) \ + (This)->lpVtbl -> SetStreamNumber(This,wStreamNum) + +#define IWMStreamConfig3_GetStreamName(This,pwszStreamName,pcchStreamName) \ + (This)->lpVtbl -> GetStreamName(This,pwszStreamName,pcchStreamName) + +#define IWMStreamConfig3_SetStreamName(This,pwszStreamName) \ + (This)->lpVtbl -> SetStreamName(This,pwszStreamName) + +#define IWMStreamConfig3_GetConnectionName(This,pwszInputName,pcchInputName) \ + (This)->lpVtbl -> GetConnectionName(This,pwszInputName,pcchInputName) + +#define IWMStreamConfig3_SetConnectionName(This,pwszInputName) \ + (This)->lpVtbl -> SetConnectionName(This,pwszInputName) + +#define IWMStreamConfig3_GetBitrate(This,pdwBitrate) \ + (This)->lpVtbl -> GetBitrate(This,pdwBitrate) + +#define IWMStreamConfig3_SetBitrate(This,pdwBitrate) \ + (This)->lpVtbl -> SetBitrate(This,pdwBitrate) + +#define IWMStreamConfig3_GetBufferWindow(This,pmsBufferWindow) \ + (This)->lpVtbl -> GetBufferWindow(This,pmsBufferWindow) + +#define IWMStreamConfig3_SetBufferWindow(This,msBufferWindow) \ + (This)->lpVtbl -> SetBufferWindow(This,msBufferWindow) + + +#define IWMStreamConfig3_GetTransportType(This,pnTransportType) \ + (This)->lpVtbl -> GetTransportType(This,pnTransportType) + +#define IWMStreamConfig3_SetTransportType(This,nTransportType) \ + (This)->lpVtbl -> SetTransportType(This,nTransportType) + +#define IWMStreamConfig3_AddDataUnitExtension(This,guidExtensionSystemID,cbExtensionDataSize,pbExtensionSystemInfo,cbExtensionSystemInfo) \ + (This)->lpVtbl -> AddDataUnitExtension(This,guidExtensionSystemID,cbExtensionDataSize,pbExtensionSystemInfo,cbExtensionSystemInfo) + +#define IWMStreamConfig3_GetDataUnitExtensionCount(This,pcDataUnitExtensions) \ + (This)->lpVtbl -> GetDataUnitExtensionCount(This,pcDataUnitExtensions) + +#define IWMStreamConfig3_GetDataUnitExtension(This,wDataUnitExtensionNumber,pguidExtensionSystemID,pcbExtensionDataSize,pbExtensionSystemInfo,pcbExtensionSystemInfo) \ + (This)->lpVtbl -> GetDataUnitExtension(This,wDataUnitExtensionNumber,pguidExtensionSystemID,pcbExtensionDataSize,pbExtensionSystemInfo,pcbExtensionSystemInfo) + +#define IWMStreamConfig3_RemoveAllDataUnitExtensions(This) \ + (This)->lpVtbl -> RemoveAllDataUnitExtensions(This) + + +#define IWMStreamConfig3_GetLanguage(This,pwszLanguageString,pcchLanguageStringLength) \ + (This)->lpVtbl -> GetLanguage(This,pwszLanguageString,pcchLanguageStringLength) + +#define IWMStreamConfig3_SetLanguage(This,pwszLanguageString) \ + (This)->lpVtbl -> SetLanguage(This,pwszLanguageString) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig3_GetLanguage_Proxy( + IWMStreamConfig3 * This, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength); + + +void __RPC_STUB IWMStreamConfig3_GetLanguage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamConfig3_SetLanguage_Proxy( + IWMStreamConfig3 * This, + /* [in] */ LPWSTR pwszLanguageString); + + +void __RPC_STUB IWMStreamConfig3_SetLanguage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMStreamConfig3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMPacketSize_INTERFACE_DEFINED__ +#define __IWMPacketSize_INTERFACE_DEFINED__ + +/* interface IWMPacketSize */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMPacketSize; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("CDFB97AB-188F-40b3-B643-5B7903975C59") + IWMPacketSize : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetMaxPacketSize( + /* [out] */ DWORD *pdwMaxPacketSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMaxPacketSize( + /* [in] */ DWORD dwMaxPacketSize) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMPacketSizeVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMPacketSize * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMPacketSize * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMPacketSize * This); + + HRESULT ( STDMETHODCALLTYPE *GetMaxPacketSize )( + IWMPacketSize * This, + /* [out] */ DWORD *pdwMaxPacketSize); + + HRESULT ( STDMETHODCALLTYPE *SetMaxPacketSize )( + IWMPacketSize * This, + /* [in] */ DWORD dwMaxPacketSize); + + END_INTERFACE + } IWMPacketSizeVtbl; + + interface IWMPacketSize + { + CONST_VTBL struct IWMPacketSizeVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMPacketSize_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMPacketSize_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMPacketSize_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMPacketSize_GetMaxPacketSize(This,pdwMaxPacketSize) \ + (This)->lpVtbl -> GetMaxPacketSize(This,pdwMaxPacketSize) + +#define IWMPacketSize_SetMaxPacketSize(This,dwMaxPacketSize) \ + (This)->lpVtbl -> SetMaxPacketSize(This,dwMaxPacketSize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMPacketSize_GetMaxPacketSize_Proxy( + IWMPacketSize * This, + /* [out] */ DWORD *pdwMaxPacketSize); + + +void __RPC_STUB IWMPacketSize_GetMaxPacketSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPacketSize_SetMaxPacketSize_Proxy( + IWMPacketSize * This, + /* [in] */ DWORD dwMaxPacketSize); + + +void __RPC_STUB IWMPacketSize_SetMaxPacketSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMPacketSize_INTERFACE_DEFINED__ */ + + +#ifndef __IWMPacketSize2_INTERFACE_DEFINED__ +#define __IWMPacketSize2_INTERFACE_DEFINED__ + +/* interface IWMPacketSize2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMPacketSize2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("8BFC2B9E-B646-4233-A877-1C6A079669DC") + IWMPacketSize2 : public IWMPacketSize + { + public: + virtual HRESULT STDMETHODCALLTYPE GetMinPacketSize( + /* [out] */ DWORD *pdwMinPacketSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMinPacketSize( + /* [in] */ DWORD dwMinPacketSize) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMPacketSize2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMPacketSize2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMPacketSize2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMPacketSize2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetMaxPacketSize )( + IWMPacketSize2 * This, + /* [out] */ DWORD *pdwMaxPacketSize); + + HRESULT ( STDMETHODCALLTYPE *SetMaxPacketSize )( + IWMPacketSize2 * This, + /* [in] */ DWORD dwMaxPacketSize); + + HRESULT ( STDMETHODCALLTYPE *GetMinPacketSize )( + IWMPacketSize2 * This, + /* [out] */ DWORD *pdwMinPacketSize); + + HRESULT ( STDMETHODCALLTYPE *SetMinPacketSize )( + IWMPacketSize2 * This, + /* [in] */ DWORD dwMinPacketSize); + + END_INTERFACE + } IWMPacketSize2Vtbl; + + interface IWMPacketSize2 + { + CONST_VTBL struct IWMPacketSize2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMPacketSize2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMPacketSize2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMPacketSize2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMPacketSize2_GetMaxPacketSize(This,pdwMaxPacketSize) \ + (This)->lpVtbl -> GetMaxPacketSize(This,pdwMaxPacketSize) + +#define IWMPacketSize2_SetMaxPacketSize(This,dwMaxPacketSize) \ + (This)->lpVtbl -> SetMaxPacketSize(This,dwMaxPacketSize) + + +#define IWMPacketSize2_GetMinPacketSize(This,pdwMinPacketSize) \ + (This)->lpVtbl -> GetMinPacketSize(This,pdwMinPacketSize) + +#define IWMPacketSize2_SetMinPacketSize(This,dwMinPacketSize) \ + (This)->lpVtbl -> SetMinPacketSize(This,dwMinPacketSize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMPacketSize2_GetMinPacketSize_Proxy( + IWMPacketSize2 * This, + /* [out] */ DWORD *pdwMinPacketSize); + + +void __RPC_STUB IWMPacketSize2_GetMinPacketSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMPacketSize2_SetMinPacketSize_Proxy( + IWMPacketSize2 * This, + /* [in] */ DWORD dwMinPacketSize); + + +void __RPC_STUB IWMPacketSize2_SetMinPacketSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMPacketSize2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMStreamList_INTERFACE_DEFINED__ +#define __IWMStreamList_INTERFACE_DEFINED__ + +/* interface IWMStreamList */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMStreamList; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BDD-2B2B-11d3-B36B-00C04F6108FF") + IWMStreamList : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStreams( + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddStream( + /* [in] */ WORD wStreamNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveStream( + /* [in] */ WORD wStreamNum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMStreamListVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMStreamList * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMStreamList * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMStreamList * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreams )( + IWMStreamList * This, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMStreamList * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMStreamList * This, + /* [in] */ WORD wStreamNum); + + END_INTERFACE + } IWMStreamListVtbl; + + interface IWMStreamList + { + CONST_VTBL struct IWMStreamListVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMStreamList_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMStreamList_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMStreamList_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMStreamList_GetStreams(This,pwStreamNumArray,pcStreams) \ + (This)->lpVtbl -> GetStreams(This,pwStreamNumArray,pcStreams) + +#define IWMStreamList_AddStream(This,wStreamNum) \ + (This)->lpVtbl -> AddStream(This,wStreamNum) + +#define IWMStreamList_RemoveStream(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStream(This,wStreamNum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMStreamList_GetStreams_Proxy( + IWMStreamList * This, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + +void __RPC_STUB IWMStreamList_GetStreams_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamList_AddStream_Proxy( + IWMStreamList * This, + /* [in] */ WORD wStreamNum); + + +void __RPC_STUB IWMStreamList_AddStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamList_RemoveStream_Proxy( + IWMStreamList * This, + /* [in] */ WORD wStreamNum); + + +void __RPC_STUB IWMStreamList_RemoveStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMStreamList_INTERFACE_DEFINED__ */ + + +#ifndef __IWMMutualExclusion_INTERFACE_DEFINED__ +#define __IWMMutualExclusion_INTERFACE_DEFINED__ + +/* interface IWMMutualExclusion */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMMutualExclusion; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BDE-2B2B-11d3-B36B-00C04F6108FF") + IWMMutualExclusion : public IWMStreamList + { + public: + virtual HRESULT STDMETHODCALLTYPE GetType( + /* [out] */ GUID *pguidType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetType( + /* [in] */ REFGUID guidType) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMMutualExclusionVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMMutualExclusion * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMMutualExclusion * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMMutualExclusion * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreams )( + IWMMutualExclusion * This, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMMutualExclusion * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMMutualExclusion * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMMutualExclusion * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *SetType )( + IWMMutualExclusion * This, + /* [in] */ REFGUID guidType); + + END_INTERFACE + } IWMMutualExclusionVtbl; + + interface IWMMutualExclusion + { + CONST_VTBL struct IWMMutualExclusionVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMMutualExclusion_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMMutualExclusion_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMMutualExclusion_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMMutualExclusion_GetStreams(This,pwStreamNumArray,pcStreams) \ + (This)->lpVtbl -> GetStreams(This,pwStreamNumArray,pcStreams) + +#define IWMMutualExclusion_AddStream(This,wStreamNum) \ + (This)->lpVtbl -> AddStream(This,wStreamNum) + +#define IWMMutualExclusion_RemoveStream(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStream(This,wStreamNum) + + +#define IWMMutualExclusion_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMMutualExclusion_SetType(This,guidType) \ + (This)->lpVtbl -> SetType(This,guidType) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion_GetType_Proxy( + IWMMutualExclusion * This, + /* [out] */ GUID *pguidType); + + +void __RPC_STUB IWMMutualExclusion_GetType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion_SetType_Proxy( + IWMMutualExclusion * This, + /* [in] */ REFGUID guidType); + + +void __RPC_STUB IWMMutualExclusion_SetType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMMutualExclusion_INTERFACE_DEFINED__ */ + + +#ifndef __IWMMutualExclusion2_INTERFACE_DEFINED__ +#define __IWMMutualExclusion2_INTERFACE_DEFINED__ + +/* interface IWMMutualExclusion2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMMutualExclusion2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("0302B57D-89D1-4ba2-85C9-166F2C53EB91") + IWMMutualExclusion2 : public IWMMutualExclusion + { + public: + virtual HRESULT STDMETHODCALLTYPE GetName( + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetName( + /* [in] */ WCHAR *pwszName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetRecordCount( + /* [out] */ WORD *pwRecordCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddRecord( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveRecord( + /* [in] */ WORD wRecordNumber) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetRecordName( + /* [in] */ WORD wRecordNumber, + /* [size_is][out] */ WCHAR *pwszRecordName, + /* [out][in] */ WORD *pcchRecordName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetRecordName( + /* [in] */ WORD wRecordNumber, + /* [in] */ WCHAR *pwszRecordName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamsForRecord( + /* [in] */ WORD wRecordNumber, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddStreamForRecord( + /* [in] */ WORD wRecordNumber, + /* [in] */ WORD wStreamNumber) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveStreamForRecord( + /* [in] */ WORD wRecordNumber, + /* [in] */ WORD wStreamNumber) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMMutualExclusion2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMMutualExclusion2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMMutualExclusion2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMMutualExclusion2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreams )( + IWMMutualExclusion2 * This, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMMutualExclusion2 * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *SetType )( + IWMMutualExclusion2 * This, + /* [in] */ REFGUID guidType); + + HRESULT ( STDMETHODCALLTYPE *GetName )( + IWMMutualExclusion2 * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *SetName )( + IWMMutualExclusion2 * This, + /* [in] */ WCHAR *pwszName); + + HRESULT ( STDMETHODCALLTYPE *GetRecordCount )( + IWMMutualExclusion2 * This, + /* [out] */ WORD *pwRecordCount); + + HRESULT ( STDMETHODCALLTYPE *AddRecord )( + IWMMutualExclusion2 * This); + + HRESULT ( STDMETHODCALLTYPE *RemoveRecord )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber); + + HRESULT ( STDMETHODCALLTYPE *GetRecordName )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [size_is][out] */ WCHAR *pwszRecordName, + /* [out][in] */ WORD *pcchRecordName); + + HRESULT ( STDMETHODCALLTYPE *SetRecordName )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [in] */ WCHAR *pwszRecordName); + + HRESULT ( STDMETHODCALLTYPE *GetStreamsForRecord )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *AddStreamForRecord )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [in] */ WORD wStreamNumber); + + HRESULT ( STDMETHODCALLTYPE *RemoveStreamForRecord )( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [in] */ WORD wStreamNumber); + + END_INTERFACE + } IWMMutualExclusion2Vtbl; + + interface IWMMutualExclusion2 + { + CONST_VTBL struct IWMMutualExclusion2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMMutualExclusion2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMMutualExclusion2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMMutualExclusion2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMMutualExclusion2_GetStreams(This,pwStreamNumArray,pcStreams) \ + (This)->lpVtbl -> GetStreams(This,pwStreamNumArray,pcStreams) + +#define IWMMutualExclusion2_AddStream(This,wStreamNum) \ + (This)->lpVtbl -> AddStream(This,wStreamNum) + +#define IWMMutualExclusion2_RemoveStream(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStream(This,wStreamNum) + + +#define IWMMutualExclusion2_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMMutualExclusion2_SetType(This,guidType) \ + (This)->lpVtbl -> SetType(This,guidType) + + +#define IWMMutualExclusion2_GetName(This,pwszName,pcchName) \ + (This)->lpVtbl -> GetName(This,pwszName,pcchName) + +#define IWMMutualExclusion2_SetName(This,pwszName) \ + (This)->lpVtbl -> SetName(This,pwszName) + +#define IWMMutualExclusion2_GetRecordCount(This,pwRecordCount) \ + (This)->lpVtbl -> GetRecordCount(This,pwRecordCount) + +#define IWMMutualExclusion2_AddRecord(This) \ + (This)->lpVtbl -> AddRecord(This) + +#define IWMMutualExclusion2_RemoveRecord(This,wRecordNumber) \ + (This)->lpVtbl -> RemoveRecord(This,wRecordNumber) + +#define IWMMutualExclusion2_GetRecordName(This,wRecordNumber,pwszRecordName,pcchRecordName) \ + (This)->lpVtbl -> GetRecordName(This,wRecordNumber,pwszRecordName,pcchRecordName) + +#define IWMMutualExclusion2_SetRecordName(This,wRecordNumber,pwszRecordName) \ + (This)->lpVtbl -> SetRecordName(This,wRecordNumber,pwszRecordName) + +#define IWMMutualExclusion2_GetStreamsForRecord(This,wRecordNumber,pwStreamNumArray,pcStreams) \ + (This)->lpVtbl -> GetStreamsForRecord(This,wRecordNumber,pwStreamNumArray,pcStreams) + +#define IWMMutualExclusion2_AddStreamForRecord(This,wRecordNumber,wStreamNumber) \ + (This)->lpVtbl -> AddStreamForRecord(This,wRecordNumber,wStreamNumber) + +#define IWMMutualExclusion2_RemoveStreamForRecord(This,wRecordNumber,wStreamNumber) \ + (This)->lpVtbl -> RemoveStreamForRecord(This,wRecordNumber,wStreamNumber) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_GetName_Proxy( + IWMMutualExclusion2 * This, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchName); + + +void __RPC_STUB IWMMutualExclusion2_GetName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_SetName_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WCHAR *pwszName); + + +void __RPC_STUB IWMMutualExclusion2_SetName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_GetRecordCount_Proxy( + IWMMutualExclusion2 * This, + /* [out] */ WORD *pwRecordCount); + + +void __RPC_STUB IWMMutualExclusion2_GetRecordCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_AddRecord_Proxy( + IWMMutualExclusion2 * This); + + +void __RPC_STUB IWMMutualExclusion2_AddRecord_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_RemoveRecord_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber); + + +void __RPC_STUB IWMMutualExclusion2_RemoveRecord_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_GetRecordName_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [size_is][out] */ WCHAR *pwszRecordName, + /* [out][in] */ WORD *pcchRecordName); + + +void __RPC_STUB IWMMutualExclusion2_GetRecordName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_SetRecordName_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [in] */ WCHAR *pwszRecordName); + + +void __RPC_STUB IWMMutualExclusion2_SetRecordName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_GetStreamsForRecord_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + +void __RPC_STUB IWMMutualExclusion2_GetStreamsForRecord_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_AddStreamForRecord_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [in] */ WORD wStreamNumber); + + +void __RPC_STUB IWMMutualExclusion2_AddStreamForRecord_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMMutualExclusion2_RemoveStreamForRecord_Proxy( + IWMMutualExclusion2 * This, + /* [in] */ WORD wRecordNumber, + /* [in] */ WORD wStreamNumber); + + +void __RPC_STUB IWMMutualExclusion2_RemoveStreamForRecord_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMMutualExclusion2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMBandwidthSharing_INTERFACE_DEFINED__ +#define __IWMBandwidthSharing_INTERFACE_DEFINED__ + +/* interface IWMBandwidthSharing */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMBandwidthSharing; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("AD694AF1-F8D9-42F8-BC47-70311B0C4F9E") + IWMBandwidthSharing : public IWMStreamList + { + public: + virtual HRESULT STDMETHODCALLTYPE GetType( + /* [out] */ GUID *pguidType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetType( + /* [in] */ REFGUID guidType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBandwidth( + /* [out] */ DWORD *pdwBitrate, + /* [out] */ DWORD *pmsBufferWindow) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBandwidth( + /* [in] */ DWORD dwBitrate, + /* [in] */ DWORD msBufferWindow) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMBandwidthSharingVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMBandwidthSharing * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMBandwidthSharing * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMBandwidthSharing * This); + + HRESULT ( STDMETHODCALLTYPE *GetStreams )( + IWMBandwidthSharing * This, + /* [size_is][out] */ WORD *pwStreamNumArray, + /* [out][in] */ WORD *pcStreams); + + HRESULT ( STDMETHODCALLTYPE *AddStream )( + IWMBandwidthSharing * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *RemoveStream )( + IWMBandwidthSharing * This, + /* [in] */ WORD wStreamNum); + + HRESULT ( STDMETHODCALLTYPE *GetType )( + IWMBandwidthSharing * This, + /* [out] */ GUID *pguidType); + + HRESULT ( STDMETHODCALLTYPE *SetType )( + IWMBandwidthSharing * This, + /* [in] */ REFGUID guidType); + + HRESULT ( STDMETHODCALLTYPE *GetBandwidth )( + IWMBandwidthSharing * This, + /* [out] */ DWORD *pdwBitrate, + /* [out] */ DWORD *pmsBufferWindow); + + HRESULT ( STDMETHODCALLTYPE *SetBandwidth )( + IWMBandwidthSharing * This, + /* [in] */ DWORD dwBitrate, + /* [in] */ DWORD msBufferWindow); + + END_INTERFACE + } IWMBandwidthSharingVtbl; + + interface IWMBandwidthSharing + { + CONST_VTBL struct IWMBandwidthSharingVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMBandwidthSharing_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMBandwidthSharing_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMBandwidthSharing_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMBandwidthSharing_GetStreams(This,pwStreamNumArray,pcStreams) \ + (This)->lpVtbl -> GetStreams(This,pwStreamNumArray,pcStreams) + +#define IWMBandwidthSharing_AddStream(This,wStreamNum) \ + (This)->lpVtbl -> AddStream(This,wStreamNum) + +#define IWMBandwidthSharing_RemoveStream(This,wStreamNum) \ + (This)->lpVtbl -> RemoveStream(This,wStreamNum) + + +#define IWMBandwidthSharing_GetType(This,pguidType) \ + (This)->lpVtbl -> GetType(This,pguidType) + +#define IWMBandwidthSharing_SetType(This,guidType) \ + (This)->lpVtbl -> SetType(This,guidType) + +#define IWMBandwidthSharing_GetBandwidth(This,pdwBitrate,pmsBufferWindow) \ + (This)->lpVtbl -> GetBandwidth(This,pdwBitrate,pmsBufferWindow) + +#define IWMBandwidthSharing_SetBandwidth(This,dwBitrate,msBufferWindow) \ + (This)->lpVtbl -> SetBandwidth(This,dwBitrate,msBufferWindow) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMBandwidthSharing_GetType_Proxy( + IWMBandwidthSharing * This, + /* [out] */ GUID *pguidType); + + +void __RPC_STUB IWMBandwidthSharing_GetType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBandwidthSharing_SetType_Proxy( + IWMBandwidthSharing * This, + /* [in] */ REFGUID guidType); + + +void __RPC_STUB IWMBandwidthSharing_SetType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBandwidthSharing_GetBandwidth_Proxy( + IWMBandwidthSharing * This, + /* [out] */ DWORD *pdwBitrate, + /* [out] */ DWORD *pmsBufferWindow); + + +void __RPC_STUB IWMBandwidthSharing_GetBandwidth_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBandwidthSharing_SetBandwidth_Proxy( + IWMBandwidthSharing * This, + /* [in] */ DWORD dwBitrate, + /* [in] */ DWORD msBufferWindow); + + +void __RPC_STUB IWMBandwidthSharing_SetBandwidth_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMBandwidthSharing_INTERFACE_DEFINED__ */ + + +#ifndef __IWMStreamPrioritization_INTERFACE_DEFINED__ +#define __IWMStreamPrioritization_INTERFACE_DEFINED__ + +/* interface IWMStreamPrioritization */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMStreamPrioritization; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("8C1C6090-F9A8-4748-8EC3-DD1108BA1E77") + IWMStreamPrioritization : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetPriorityRecords( + /* [size_is][out] */ WM_STREAM_PRIORITY_RECORD *pRecordArray, + /* [out][in] */ WORD *pcRecords) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetPriorityRecords( + /* [in] */ WM_STREAM_PRIORITY_RECORD *pRecordArray, + /* [in] */ WORD cRecords) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMStreamPrioritizationVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMStreamPrioritization * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMStreamPrioritization * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMStreamPrioritization * This); + + HRESULT ( STDMETHODCALLTYPE *GetPriorityRecords )( + IWMStreamPrioritization * This, + /* [size_is][out] */ WM_STREAM_PRIORITY_RECORD *pRecordArray, + /* [out][in] */ WORD *pcRecords); + + HRESULT ( STDMETHODCALLTYPE *SetPriorityRecords )( + IWMStreamPrioritization * This, + /* [in] */ WM_STREAM_PRIORITY_RECORD *pRecordArray, + /* [in] */ WORD cRecords); + + END_INTERFACE + } IWMStreamPrioritizationVtbl; + + interface IWMStreamPrioritization + { + CONST_VTBL struct IWMStreamPrioritizationVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMStreamPrioritization_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMStreamPrioritization_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMStreamPrioritization_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMStreamPrioritization_GetPriorityRecords(This,pRecordArray,pcRecords) \ + (This)->lpVtbl -> GetPriorityRecords(This,pRecordArray,pcRecords) + +#define IWMStreamPrioritization_SetPriorityRecords(This,pRecordArray,cRecords) \ + (This)->lpVtbl -> SetPriorityRecords(This,pRecordArray,cRecords) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMStreamPrioritization_GetPriorityRecords_Proxy( + IWMStreamPrioritization * This, + /* [size_is][out] */ WM_STREAM_PRIORITY_RECORD *pRecordArray, + /* [out][in] */ WORD *pcRecords); + + +void __RPC_STUB IWMStreamPrioritization_GetPriorityRecords_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMStreamPrioritization_SetPriorityRecords_Proxy( + IWMStreamPrioritization * This, + /* [in] */ WM_STREAM_PRIORITY_RECORD *pRecordArray, + /* [in] */ WORD cRecords); + + +void __RPC_STUB IWMStreamPrioritization_SetPriorityRecords_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMStreamPrioritization_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterAdvanced_INTERFACE_DEFINED__ +#define __IWMWriterAdvanced_INTERFACE_DEFINED__ + +/* interface IWMWriterAdvanced */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterAdvanced; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BE3-2B2B-11d3-B36B-00C04F6108FF") + IWMWriterAdvanced : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetSinkCount( + /* [out] */ DWORD *pcSinks) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSink( + /* [in] */ DWORD dwSinkNum, + /* [out] */ IWMWriterSink **ppSink) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddSink( + /* [in] */ IWMWriterSink *pSink) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveSink( + /* [in] */ IWMWriterSink *pSink) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteStreamSample( + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD msSampleSendTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetLiveSource( + BOOL fIsLiveSource) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsRealTime( + /* [out] */ BOOL *pfRealTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetWriterTime( + /* [out] */ QWORD *pcnsCurrentTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStatistics( + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS *pStats) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetSyncTolerance( + /* [in] */ DWORD msWindow) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSyncTolerance( + /* [out] */ DWORD *pmsWindow) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterAdvancedVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterAdvanced * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterAdvanced * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterAdvanced * This); + + HRESULT ( STDMETHODCALLTYPE *GetSinkCount )( + IWMWriterAdvanced * This, + /* [out] */ DWORD *pcSinks); + + HRESULT ( STDMETHODCALLTYPE *GetSink )( + IWMWriterAdvanced * This, + /* [in] */ DWORD dwSinkNum, + /* [out] */ IWMWriterSink **ppSink); + + HRESULT ( STDMETHODCALLTYPE *AddSink )( + IWMWriterAdvanced * This, + /* [in] */ IWMWriterSink *pSink); + + HRESULT ( STDMETHODCALLTYPE *RemoveSink )( + IWMWriterAdvanced * This, + /* [in] */ IWMWriterSink *pSink); + + HRESULT ( STDMETHODCALLTYPE *WriteStreamSample )( + IWMWriterAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD msSampleSendTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + HRESULT ( STDMETHODCALLTYPE *SetLiveSource )( + IWMWriterAdvanced * This, + BOOL fIsLiveSource); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterAdvanced * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *GetWriterTime )( + IWMWriterAdvanced * This, + /* [out] */ QWORD *pcnsCurrentTime); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMWriterAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS *pStats); + + HRESULT ( STDMETHODCALLTYPE *SetSyncTolerance )( + IWMWriterAdvanced * This, + /* [in] */ DWORD msWindow); + + HRESULT ( STDMETHODCALLTYPE *GetSyncTolerance )( + IWMWriterAdvanced * This, + /* [out] */ DWORD *pmsWindow); + + END_INTERFACE + } IWMWriterAdvancedVtbl; + + interface IWMWriterAdvanced + { + CONST_VTBL struct IWMWriterAdvancedVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterAdvanced_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterAdvanced_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterAdvanced_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterAdvanced_GetSinkCount(This,pcSinks) \ + (This)->lpVtbl -> GetSinkCount(This,pcSinks) + +#define IWMWriterAdvanced_GetSink(This,dwSinkNum,ppSink) \ + (This)->lpVtbl -> GetSink(This,dwSinkNum,ppSink) + +#define IWMWriterAdvanced_AddSink(This,pSink) \ + (This)->lpVtbl -> AddSink(This,pSink) + +#define IWMWriterAdvanced_RemoveSink(This,pSink) \ + (This)->lpVtbl -> RemoveSink(This,pSink) + +#define IWMWriterAdvanced_WriteStreamSample(This,wStreamNum,cnsSampleTime,msSampleSendTime,cnsSampleDuration,dwFlags,pSample) \ + (This)->lpVtbl -> WriteStreamSample(This,wStreamNum,cnsSampleTime,msSampleSendTime,cnsSampleDuration,dwFlags,pSample) + +#define IWMWriterAdvanced_SetLiveSource(This,fIsLiveSource) \ + (This)->lpVtbl -> SetLiveSource(This,fIsLiveSource) + +#define IWMWriterAdvanced_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterAdvanced_GetWriterTime(This,pcnsCurrentTime) \ + (This)->lpVtbl -> GetWriterTime(This,pcnsCurrentTime) + +#define IWMWriterAdvanced_GetStatistics(This,wStreamNum,pStats) \ + (This)->lpVtbl -> GetStatistics(This,wStreamNum,pStats) + +#define IWMWriterAdvanced_SetSyncTolerance(This,msWindow) \ + (This)->lpVtbl -> SetSyncTolerance(This,msWindow) + +#define IWMWriterAdvanced_GetSyncTolerance(This,pmsWindow) \ + (This)->lpVtbl -> GetSyncTolerance(This,pmsWindow) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_GetSinkCount_Proxy( + IWMWriterAdvanced * This, + /* [out] */ DWORD *pcSinks); + + +void __RPC_STUB IWMWriterAdvanced_GetSinkCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_GetSink_Proxy( + IWMWriterAdvanced * This, + /* [in] */ DWORD dwSinkNum, + /* [out] */ IWMWriterSink **ppSink); + + +void __RPC_STUB IWMWriterAdvanced_GetSink_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_AddSink_Proxy( + IWMWriterAdvanced * This, + /* [in] */ IWMWriterSink *pSink); + + +void __RPC_STUB IWMWriterAdvanced_AddSink_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_RemoveSink_Proxy( + IWMWriterAdvanced * This, + /* [in] */ IWMWriterSink *pSink); + + +void __RPC_STUB IWMWriterAdvanced_RemoveSink_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_WriteStreamSample_Proxy( + IWMWriterAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD msSampleSendTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + +void __RPC_STUB IWMWriterAdvanced_WriteStreamSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_SetLiveSource_Proxy( + IWMWriterAdvanced * This, + BOOL fIsLiveSource); + + +void __RPC_STUB IWMWriterAdvanced_SetLiveSource_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_IsRealTime_Proxy( + IWMWriterAdvanced * This, + /* [out] */ BOOL *pfRealTime); + + +void __RPC_STUB IWMWriterAdvanced_IsRealTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_GetWriterTime_Proxy( + IWMWriterAdvanced * This, + /* [out] */ QWORD *pcnsCurrentTime); + + +void __RPC_STUB IWMWriterAdvanced_GetWriterTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_GetStatistics_Proxy( + IWMWriterAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS *pStats); + + +void __RPC_STUB IWMWriterAdvanced_GetStatistics_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_SetSyncTolerance_Proxy( + IWMWriterAdvanced * This, + /* [in] */ DWORD msWindow); + + +void __RPC_STUB IWMWriterAdvanced_SetSyncTolerance_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced_GetSyncTolerance_Proxy( + IWMWriterAdvanced * This, + /* [out] */ DWORD *pmsWindow); + + +void __RPC_STUB IWMWriterAdvanced_GetSyncTolerance_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterAdvanced_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterAdvanced2_INTERFACE_DEFINED__ +#define __IWMWriterAdvanced2_INTERFACE_DEFINED__ + +/* interface IWMWriterAdvanced2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterAdvanced2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("962dc1ec-c046-4db8-9cc7-26ceae500817") + IWMWriterAdvanced2 : public IWMWriterAdvanced + { + public: + virtual HRESULT STDMETHODCALLTYPE GetInputSetting( + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetInputSetting( + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterAdvanced2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterAdvanced2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterAdvanced2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterAdvanced2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetSinkCount )( + IWMWriterAdvanced2 * This, + /* [out] */ DWORD *pcSinks); + + HRESULT ( STDMETHODCALLTYPE *GetSink )( + IWMWriterAdvanced2 * This, + /* [in] */ DWORD dwSinkNum, + /* [out] */ IWMWriterSink **ppSink); + + HRESULT ( STDMETHODCALLTYPE *AddSink )( + IWMWriterAdvanced2 * This, + /* [in] */ IWMWriterSink *pSink); + + HRESULT ( STDMETHODCALLTYPE *RemoveSink )( + IWMWriterAdvanced2 * This, + /* [in] */ IWMWriterSink *pSink); + + HRESULT ( STDMETHODCALLTYPE *WriteStreamSample )( + IWMWriterAdvanced2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD msSampleSendTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + HRESULT ( STDMETHODCALLTYPE *SetLiveSource )( + IWMWriterAdvanced2 * This, + BOOL fIsLiveSource); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterAdvanced2 * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *GetWriterTime )( + IWMWriterAdvanced2 * This, + /* [out] */ QWORD *pcnsCurrentTime); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMWriterAdvanced2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS *pStats); + + HRESULT ( STDMETHODCALLTYPE *SetSyncTolerance )( + IWMWriterAdvanced2 * This, + /* [in] */ DWORD msWindow); + + HRESULT ( STDMETHODCALLTYPE *GetSyncTolerance )( + IWMWriterAdvanced2 * This, + /* [out] */ DWORD *pmsWindow); + + HRESULT ( STDMETHODCALLTYPE *GetInputSetting )( + IWMWriterAdvanced2 * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetInputSetting )( + IWMWriterAdvanced2 * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + END_INTERFACE + } IWMWriterAdvanced2Vtbl; + + interface IWMWriterAdvanced2 + { + CONST_VTBL struct IWMWriterAdvanced2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterAdvanced2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterAdvanced2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterAdvanced2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterAdvanced2_GetSinkCount(This,pcSinks) \ + (This)->lpVtbl -> GetSinkCount(This,pcSinks) + +#define IWMWriterAdvanced2_GetSink(This,dwSinkNum,ppSink) \ + (This)->lpVtbl -> GetSink(This,dwSinkNum,ppSink) + +#define IWMWriterAdvanced2_AddSink(This,pSink) \ + (This)->lpVtbl -> AddSink(This,pSink) + +#define IWMWriterAdvanced2_RemoveSink(This,pSink) \ + (This)->lpVtbl -> RemoveSink(This,pSink) + +#define IWMWriterAdvanced2_WriteStreamSample(This,wStreamNum,cnsSampleTime,msSampleSendTime,cnsSampleDuration,dwFlags,pSample) \ + (This)->lpVtbl -> WriteStreamSample(This,wStreamNum,cnsSampleTime,msSampleSendTime,cnsSampleDuration,dwFlags,pSample) + +#define IWMWriterAdvanced2_SetLiveSource(This,fIsLiveSource) \ + (This)->lpVtbl -> SetLiveSource(This,fIsLiveSource) + +#define IWMWriterAdvanced2_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterAdvanced2_GetWriterTime(This,pcnsCurrentTime) \ + (This)->lpVtbl -> GetWriterTime(This,pcnsCurrentTime) + +#define IWMWriterAdvanced2_GetStatistics(This,wStreamNum,pStats) \ + (This)->lpVtbl -> GetStatistics(This,wStreamNum,pStats) + +#define IWMWriterAdvanced2_SetSyncTolerance(This,msWindow) \ + (This)->lpVtbl -> SetSyncTolerance(This,msWindow) + +#define IWMWriterAdvanced2_GetSyncTolerance(This,pmsWindow) \ + (This)->lpVtbl -> GetSyncTolerance(This,pmsWindow) + + +#define IWMWriterAdvanced2_GetInputSetting(This,dwInputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetInputSetting(This,dwInputNum,pszName,pType,pValue,pcbLength) + +#define IWMWriterAdvanced2_SetInputSetting(This,dwInputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetInputSetting(This,dwInputNum,pszName,Type,pValue,cbLength) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced2_GetInputSetting_Proxy( + IWMWriterAdvanced2 * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMWriterAdvanced2_GetInputSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced2_SetInputSetting_Proxy( + IWMWriterAdvanced2 * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMWriterAdvanced2_SetInputSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterAdvanced2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterAdvanced3_INTERFACE_DEFINED__ +#define __IWMWriterAdvanced3_INTERFACE_DEFINED__ + +/* interface IWMWriterAdvanced3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterAdvanced3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("2cd6492d-7c37-4e76-9d3b-59261183a22e") + IWMWriterAdvanced3 : public IWMWriterAdvanced2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStatisticsEx( + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS_EX *pStats) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetNonBlocking( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterAdvanced3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterAdvanced3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterAdvanced3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterAdvanced3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetSinkCount )( + IWMWriterAdvanced3 * This, + /* [out] */ DWORD *pcSinks); + + HRESULT ( STDMETHODCALLTYPE *GetSink )( + IWMWriterAdvanced3 * This, + /* [in] */ DWORD dwSinkNum, + /* [out] */ IWMWriterSink **ppSink); + + HRESULT ( STDMETHODCALLTYPE *AddSink )( + IWMWriterAdvanced3 * This, + /* [in] */ IWMWriterSink *pSink); + + HRESULT ( STDMETHODCALLTYPE *RemoveSink )( + IWMWriterAdvanced3 * This, + /* [in] */ IWMWriterSink *pSink); + + HRESULT ( STDMETHODCALLTYPE *WriteStreamSample )( + IWMWriterAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD msSampleSendTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + HRESULT ( STDMETHODCALLTYPE *SetLiveSource )( + IWMWriterAdvanced3 * This, + BOOL fIsLiveSource); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterAdvanced3 * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *GetWriterTime )( + IWMWriterAdvanced3 * This, + /* [out] */ QWORD *pcnsCurrentTime); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMWriterAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS *pStats); + + HRESULT ( STDMETHODCALLTYPE *SetSyncTolerance )( + IWMWriterAdvanced3 * This, + /* [in] */ DWORD msWindow); + + HRESULT ( STDMETHODCALLTYPE *GetSyncTolerance )( + IWMWriterAdvanced3 * This, + /* [out] */ DWORD *pmsWindow); + + HRESULT ( STDMETHODCALLTYPE *GetInputSetting )( + IWMWriterAdvanced3 * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetInputSetting )( + IWMWriterAdvanced3 * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetStatisticsEx )( + IWMWriterAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS_EX *pStats); + + HRESULT ( STDMETHODCALLTYPE *SetNonBlocking )( + IWMWriterAdvanced3 * This); + + END_INTERFACE + } IWMWriterAdvanced3Vtbl; + + interface IWMWriterAdvanced3 + { + CONST_VTBL struct IWMWriterAdvanced3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterAdvanced3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterAdvanced3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterAdvanced3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterAdvanced3_GetSinkCount(This,pcSinks) \ + (This)->lpVtbl -> GetSinkCount(This,pcSinks) + +#define IWMWriterAdvanced3_GetSink(This,dwSinkNum,ppSink) \ + (This)->lpVtbl -> GetSink(This,dwSinkNum,ppSink) + +#define IWMWriterAdvanced3_AddSink(This,pSink) \ + (This)->lpVtbl -> AddSink(This,pSink) + +#define IWMWriterAdvanced3_RemoveSink(This,pSink) \ + (This)->lpVtbl -> RemoveSink(This,pSink) + +#define IWMWriterAdvanced3_WriteStreamSample(This,wStreamNum,cnsSampleTime,msSampleSendTime,cnsSampleDuration,dwFlags,pSample) \ + (This)->lpVtbl -> WriteStreamSample(This,wStreamNum,cnsSampleTime,msSampleSendTime,cnsSampleDuration,dwFlags,pSample) + +#define IWMWriterAdvanced3_SetLiveSource(This,fIsLiveSource) \ + (This)->lpVtbl -> SetLiveSource(This,fIsLiveSource) + +#define IWMWriterAdvanced3_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterAdvanced3_GetWriterTime(This,pcnsCurrentTime) \ + (This)->lpVtbl -> GetWriterTime(This,pcnsCurrentTime) + +#define IWMWriterAdvanced3_GetStatistics(This,wStreamNum,pStats) \ + (This)->lpVtbl -> GetStatistics(This,wStreamNum,pStats) + +#define IWMWriterAdvanced3_SetSyncTolerance(This,msWindow) \ + (This)->lpVtbl -> SetSyncTolerance(This,msWindow) + +#define IWMWriterAdvanced3_GetSyncTolerance(This,pmsWindow) \ + (This)->lpVtbl -> GetSyncTolerance(This,pmsWindow) + + +#define IWMWriterAdvanced3_GetInputSetting(This,dwInputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetInputSetting(This,dwInputNum,pszName,pType,pValue,pcbLength) + +#define IWMWriterAdvanced3_SetInputSetting(This,dwInputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetInputSetting(This,dwInputNum,pszName,Type,pValue,cbLength) + + +#define IWMWriterAdvanced3_GetStatisticsEx(This,wStreamNum,pStats) \ + (This)->lpVtbl -> GetStatisticsEx(This,wStreamNum,pStats) + +#define IWMWriterAdvanced3_SetNonBlocking(This) \ + (This)->lpVtbl -> SetNonBlocking(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced3_GetStatisticsEx_Proxy( + IWMWriterAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WM_WRITER_STATISTICS_EX *pStats); + + +void __RPC_STUB IWMWriterAdvanced3_GetStatisticsEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterAdvanced3_SetNonBlocking_Proxy( + IWMWriterAdvanced3 * This); + + +void __RPC_STUB IWMWriterAdvanced3_SetNonBlocking_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterAdvanced3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterPreprocess_INTERFACE_DEFINED__ +#define __IWMWriterPreprocess_INTERFACE_DEFINED__ + +/* interface IWMWriterPreprocess */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterPreprocess; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("fc54a285-38c4-45b5-aa23-85b9f7cb424b") + IWMWriterPreprocess : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetMaxPreprocessingPasses( + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags, + /* [out] */ DWORD *pdwMaxNumPasses) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetNumPreprocessingPasses( + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags, + /* [in] */ DWORD dwNumPasses) = 0; + + virtual HRESULT STDMETHODCALLTYPE BeginPreprocessingPass( + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE PreprocessSample( + /* [in] */ DWORD dwInputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE EndPreprocessingPass( + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterPreprocessVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterPreprocess * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterPreprocess * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterPreprocess * This); + + HRESULT ( STDMETHODCALLTYPE *GetMaxPreprocessingPasses )( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags, + /* [out] */ DWORD *pdwMaxNumPasses); + + HRESULT ( STDMETHODCALLTYPE *SetNumPreprocessingPasses )( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags, + /* [in] */ DWORD dwNumPasses); + + HRESULT ( STDMETHODCALLTYPE *BeginPreprocessingPass )( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE *PreprocessSample )( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + HRESULT ( STDMETHODCALLTYPE *EndPreprocessingPass )( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags); + + END_INTERFACE + } IWMWriterPreprocessVtbl; + + interface IWMWriterPreprocess + { + CONST_VTBL struct IWMWriterPreprocessVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterPreprocess_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterPreprocess_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterPreprocess_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterPreprocess_GetMaxPreprocessingPasses(This,dwInputNum,dwFlags,pdwMaxNumPasses) \ + (This)->lpVtbl -> GetMaxPreprocessingPasses(This,dwInputNum,dwFlags,pdwMaxNumPasses) + +#define IWMWriterPreprocess_SetNumPreprocessingPasses(This,dwInputNum,dwFlags,dwNumPasses) \ + (This)->lpVtbl -> SetNumPreprocessingPasses(This,dwInputNum,dwFlags,dwNumPasses) + +#define IWMWriterPreprocess_BeginPreprocessingPass(This,dwInputNum,dwFlags) \ + (This)->lpVtbl -> BeginPreprocessingPass(This,dwInputNum,dwFlags) + +#define IWMWriterPreprocess_PreprocessSample(This,dwInputNum,cnsSampleTime,dwFlags,pSample) \ + (This)->lpVtbl -> PreprocessSample(This,dwInputNum,cnsSampleTime,dwFlags,pSample) + +#define IWMWriterPreprocess_EndPreprocessingPass(This,dwInputNum,dwFlags) \ + (This)->lpVtbl -> EndPreprocessingPass(This,dwInputNum,dwFlags) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterPreprocess_GetMaxPreprocessingPasses_Proxy( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags, + /* [out] */ DWORD *pdwMaxNumPasses); + + +void __RPC_STUB IWMWriterPreprocess_GetMaxPreprocessingPasses_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPreprocess_SetNumPreprocessingPasses_Proxy( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags, + /* [in] */ DWORD dwNumPasses); + + +void __RPC_STUB IWMWriterPreprocess_SetNumPreprocessingPasses_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPreprocess_BeginPreprocessingPass_Proxy( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IWMWriterPreprocess_BeginPreprocessingPass_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPreprocess_PreprocessSample_Proxy( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample); + + +void __RPC_STUB IWMWriterPreprocess_PreprocessSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPreprocess_EndPreprocessingPass_Proxy( + IWMWriterPreprocess * This, + /* [in] */ DWORD dwInputNum, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IWMWriterPreprocess_EndPreprocessingPass_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterPreprocess_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterPostViewCallback_INTERFACE_DEFINED__ +#define __IWMWriterPostViewCallback_INTERFACE_DEFINED__ + +/* interface IWMWriterPostViewCallback */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterPostViewCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("D9D6549D-A193-4f24-B308-03123D9B7F8D") + IWMWriterPostViewCallback : public IWMStatusCallback + { + public: + virtual HRESULT STDMETHODCALLTYPE OnPostViewSample( + /* [in] */ WORD wStreamNumber, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocateForPostView( + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterPostViewCallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterPostViewCallback * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterPostViewCallback * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterPostViewCallback * This); + + HRESULT ( STDMETHODCALLTYPE *OnStatus )( + IWMWriterPostViewCallback * This, + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [in] */ BYTE *pValue, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *OnPostViewSample )( + IWMWriterPostViewCallback * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *AllocateForPostView )( + IWMWriterPostViewCallback * This, + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMWriterPostViewCallbackVtbl; + + interface IWMWriterPostViewCallback + { + CONST_VTBL struct IWMWriterPostViewCallbackVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterPostViewCallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterPostViewCallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterPostViewCallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterPostViewCallback_OnStatus(This,Status,hr,dwType,pValue,pvContext) \ + (This)->lpVtbl -> OnStatus(This,Status,hr,dwType,pValue,pvContext) + + +#define IWMWriterPostViewCallback_OnPostViewSample(This,wStreamNumber,cnsSampleTime,cnsSampleDuration,dwFlags,pSample,pvContext) \ + (This)->lpVtbl -> OnPostViewSample(This,wStreamNumber,cnsSampleTime,cnsSampleDuration,dwFlags,pSample,pvContext) + +#define IWMWriterPostViewCallback_AllocateForPostView(This,wStreamNum,cbBuffer,ppBuffer,pvContext) \ + (This)->lpVtbl -> AllocateForPostView(This,wStreamNum,cbBuffer,ppBuffer,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterPostViewCallback_OnPostViewSample_Proxy( + IWMWriterPostViewCallback * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMWriterPostViewCallback_OnPostViewSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostViewCallback_AllocateForPostView_Proxy( + IWMWriterPostViewCallback * This, + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMWriterPostViewCallback_AllocateForPostView_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterPostViewCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterPostView_INTERFACE_DEFINED__ +#define __IWMWriterPostView_INTERFACE_DEFINED__ + +/* interface IWMWriterPostView */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterPostView; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("81E20CE4-75EF-491a-8004-FC53C45BDC3E") + IWMWriterPostView : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetPostViewCallback( + IWMWriterPostViewCallback *pCallback, + void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetReceivePostViewSamples( + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceivePostViewSamples) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetReceivePostViewSamples( + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceivePostViewSamples) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPostViewProps( + /* [in] */ WORD wStreamNumber, + /* [out] */ IWMMediaProps **ppOutput) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetPostViewProps( + /* [in] */ WORD wStreamNumber, + /* [in] */ IWMMediaProps *pOutput) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPostViewFormatCount( + /* [in] */ WORD wStreamNumber, + /* [out] */ DWORD *pcFormats) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPostViewFormat( + /* [in] */ WORD wStreamNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMMediaProps **ppProps) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAllocateForPostView( + /* [in] */ WORD wStreamNumber, + /* [in] */ BOOL fAllocate) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAllocateForPostView( + /* [in] */ WORD wStreamNumber, + /* [out] */ BOOL *pfAllocate) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterPostViewVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterPostView * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterPostView * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterPostView * This); + + HRESULT ( STDMETHODCALLTYPE *SetPostViewCallback )( + IWMWriterPostView * This, + IWMWriterPostViewCallback *pCallback, + void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *SetReceivePostViewSamples )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceivePostViewSamples); + + HRESULT ( STDMETHODCALLTYPE *GetReceivePostViewSamples )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceivePostViewSamples); + + HRESULT ( STDMETHODCALLTYPE *GetPostViewProps )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [out] */ IWMMediaProps **ppOutput); + + HRESULT ( STDMETHODCALLTYPE *SetPostViewProps )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ IWMMediaProps *pOutput); + + HRESULT ( STDMETHODCALLTYPE *GetPostViewFormatCount )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [out] */ DWORD *pcFormats); + + HRESULT ( STDMETHODCALLTYPE *GetPostViewFormat )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMMediaProps **ppProps); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForPostView )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForPostView )( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [out] */ BOOL *pfAllocate); + + END_INTERFACE + } IWMWriterPostViewVtbl; + + interface IWMWriterPostView + { + CONST_VTBL struct IWMWriterPostViewVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterPostView_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterPostView_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterPostView_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterPostView_SetPostViewCallback(This,pCallback,pvContext) \ + (This)->lpVtbl -> SetPostViewCallback(This,pCallback,pvContext) + +#define IWMWriterPostView_SetReceivePostViewSamples(This,wStreamNum,fReceivePostViewSamples) \ + (This)->lpVtbl -> SetReceivePostViewSamples(This,wStreamNum,fReceivePostViewSamples) + +#define IWMWriterPostView_GetReceivePostViewSamples(This,wStreamNum,pfReceivePostViewSamples) \ + (This)->lpVtbl -> GetReceivePostViewSamples(This,wStreamNum,pfReceivePostViewSamples) + +#define IWMWriterPostView_GetPostViewProps(This,wStreamNumber,ppOutput) \ + (This)->lpVtbl -> GetPostViewProps(This,wStreamNumber,ppOutput) + +#define IWMWriterPostView_SetPostViewProps(This,wStreamNumber,pOutput) \ + (This)->lpVtbl -> SetPostViewProps(This,wStreamNumber,pOutput) + +#define IWMWriterPostView_GetPostViewFormatCount(This,wStreamNumber,pcFormats) \ + (This)->lpVtbl -> GetPostViewFormatCount(This,wStreamNumber,pcFormats) + +#define IWMWriterPostView_GetPostViewFormat(This,wStreamNumber,dwFormatNumber,ppProps) \ + (This)->lpVtbl -> GetPostViewFormat(This,wStreamNumber,dwFormatNumber,ppProps) + +#define IWMWriterPostView_SetAllocateForPostView(This,wStreamNumber,fAllocate) \ + (This)->lpVtbl -> SetAllocateForPostView(This,wStreamNumber,fAllocate) + +#define IWMWriterPostView_GetAllocateForPostView(This,wStreamNumber,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForPostView(This,wStreamNumber,pfAllocate) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_SetPostViewCallback_Proxy( + IWMWriterPostView * This, + IWMWriterPostViewCallback *pCallback, + void *pvContext); + + +void __RPC_STUB IWMWriterPostView_SetPostViewCallback_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_SetReceivePostViewSamples_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceivePostViewSamples); + + +void __RPC_STUB IWMWriterPostView_SetReceivePostViewSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_GetReceivePostViewSamples_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceivePostViewSamples); + + +void __RPC_STUB IWMWriterPostView_GetReceivePostViewSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_GetPostViewProps_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [out] */ IWMMediaProps **ppOutput); + + +void __RPC_STUB IWMWriterPostView_GetPostViewProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_SetPostViewProps_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ IWMMediaProps *pOutput); + + +void __RPC_STUB IWMWriterPostView_SetPostViewProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_GetPostViewFormatCount_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [out] */ DWORD *pcFormats); + + +void __RPC_STUB IWMWriterPostView_GetPostViewFormatCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_GetPostViewFormat_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ DWORD dwFormatNumber, + /* [out] */ IWMMediaProps **ppProps); + + +void __RPC_STUB IWMWriterPostView_GetPostViewFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_SetAllocateForPostView_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ BOOL fAllocate); + + +void __RPC_STUB IWMWriterPostView_SetAllocateForPostView_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPostView_GetAllocateForPostView_Proxy( + IWMWriterPostView * This, + /* [in] */ WORD wStreamNumber, + /* [out] */ BOOL *pfAllocate); + + +void __RPC_STUB IWMWriterPostView_GetAllocateForPostView_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterPostView_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterSink_INTERFACE_DEFINED__ +#define __IWMWriterSink_INTERFACE_DEFINED__ + +/* interface IWMWriterSink */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterSink; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BE4-2B2B-11d3-B36B-00C04F6108FF") + IWMWriterSink : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnHeader( + /* [in] */ INSSBuffer *pHeader) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsRealTime( + /* [out] */ BOOL *pfRealTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocateDataUnit( + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnDataUnit( + /* [in] */ INSSBuffer *pDataUnit) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnEndWriting( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterSinkVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterSink * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterSink * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterSink * This); + + HRESULT ( STDMETHODCALLTYPE *OnHeader )( + IWMWriterSink * This, + /* [in] */ INSSBuffer *pHeader); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterSink * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *AllocateDataUnit )( + IWMWriterSink * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnit )( + IWMWriterSink * This, + /* [in] */ INSSBuffer *pDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnEndWriting )( + IWMWriterSink * This); + + END_INTERFACE + } IWMWriterSinkVtbl; + + interface IWMWriterSink + { + CONST_VTBL struct IWMWriterSinkVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterSink_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterSink_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterSink_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterSink_OnHeader(This,pHeader) \ + (This)->lpVtbl -> OnHeader(This,pHeader) + +#define IWMWriterSink_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterSink_AllocateDataUnit(This,cbDataUnit,ppDataUnit) \ + (This)->lpVtbl -> AllocateDataUnit(This,cbDataUnit,ppDataUnit) + +#define IWMWriterSink_OnDataUnit(This,pDataUnit) \ + (This)->lpVtbl -> OnDataUnit(This,pDataUnit) + +#define IWMWriterSink_OnEndWriting(This) \ + (This)->lpVtbl -> OnEndWriting(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterSink_OnHeader_Proxy( + IWMWriterSink * This, + /* [in] */ INSSBuffer *pHeader); + + +void __RPC_STUB IWMWriterSink_OnHeader_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterSink_IsRealTime_Proxy( + IWMWriterSink * This, + /* [out] */ BOOL *pfRealTime); + + +void __RPC_STUB IWMWriterSink_IsRealTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterSink_AllocateDataUnit_Proxy( + IWMWriterSink * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + +void __RPC_STUB IWMWriterSink_AllocateDataUnit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterSink_OnDataUnit_Proxy( + IWMWriterSink * This, + /* [in] */ INSSBuffer *pDataUnit); + + +void __RPC_STUB IWMWriterSink_OnDataUnit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterSink_OnEndWriting_Proxy( + IWMWriterSink * This); + + +void __RPC_STUB IWMWriterSink_OnEndWriting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterSink_INTERFACE_DEFINED__ */ + + +#ifndef __IWMRegisterCallback_INTERFACE_DEFINED__ +#define __IWMRegisterCallback_INTERFACE_DEFINED__ + +/* interface IWMRegisterCallback */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMRegisterCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("CF4B1F99-4DE2-4e49-A363-252740D99BC1") + IWMRegisterCallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Advise( + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE Unadvise( + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMRegisterCallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMRegisterCallback * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMRegisterCallback * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMRegisterCallback * This); + + HRESULT ( STDMETHODCALLTYPE *Advise )( + IWMRegisterCallback * This, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *Unadvise )( + IWMRegisterCallback * This, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMRegisterCallbackVtbl; + + interface IWMRegisterCallback + { + CONST_VTBL struct IWMRegisterCallbackVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMRegisterCallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMRegisterCallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMRegisterCallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMRegisterCallback_Advise(This,pCallback,pvContext) \ + (This)->lpVtbl -> Advise(This,pCallback,pvContext) + +#define IWMRegisterCallback_Unadvise(This,pCallback,pvContext) \ + (This)->lpVtbl -> Unadvise(This,pCallback,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMRegisterCallback_Advise_Proxy( + IWMRegisterCallback * This, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMRegisterCallback_Advise_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMRegisterCallback_Unadvise_Proxy( + IWMRegisterCallback * This, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMRegisterCallback_Unadvise_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMRegisterCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterFileSink_INTERFACE_DEFINED__ +#define __IWMWriterFileSink_INTERFACE_DEFINED__ + +/* interface IWMWriterFileSink */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterFileSink; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BE5-2B2B-11d3-B36B-00C04F6108FF") + IWMWriterFileSink : public IWMWriterSink + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ const WCHAR *pwszFilename) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterFileSinkVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterFileSink * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterFileSink * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterFileSink * This); + + HRESULT ( STDMETHODCALLTYPE *OnHeader )( + IWMWriterFileSink * This, + /* [in] */ INSSBuffer *pHeader); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterFileSink * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *AllocateDataUnit )( + IWMWriterFileSink * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnit )( + IWMWriterFileSink * This, + /* [in] */ INSSBuffer *pDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnEndWriting )( + IWMWriterFileSink * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMWriterFileSink * This, + /* [in] */ const WCHAR *pwszFilename); + + END_INTERFACE + } IWMWriterFileSinkVtbl; + + interface IWMWriterFileSink + { + CONST_VTBL struct IWMWriterFileSinkVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterFileSink_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterFileSink_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterFileSink_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterFileSink_OnHeader(This,pHeader) \ + (This)->lpVtbl -> OnHeader(This,pHeader) + +#define IWMWriterFileSink_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterFileSink_AllocateDataUnit(This,cbDataUnit,ppDataUnit) \ + (This)->lpVtbl -> AllocateDataUnit(This,cbDataUnit,ppDataUnit) + +#define IWMWriterFileSink_OnDataUnit(This,pDataUnit) \ + (This)->lpVtbl -> OnDataUnit(This,pDataUnit) + +#define IWMWriterFileSink_OnEndWriting(This) \ + (This)->lpVtbl -> OnEndWriting(This) + + +#define IWMWriterFileSink_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink_Open_Proxy( + IWMWriterFileSink * This, + /* [in] */ const WCHAR *pwszFilename); + + +void __RPC_STUB IWMWriterFileSink_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterFileSink_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterFileSink2_INTERFACE_DEFINED__ +#define __IWMWriterFileSink2_INTERFACE_DEFINED__ + +/* interface IWMWriterFileSink2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterFileSink2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("14282BA7-4AEF-4205-8CE5-C229035A05BC") + IWMWriterFileSink2 : public IWMWriterFileSink + { + public: + virtual HRESULT STDMETHODCALLTYPE Start( + /* [in] */ QWORD cnsStartTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE Stop( + /* [in] */ QWORD cnsStopTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsStopped( + /* [out] */ BOOL *pfStopped) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFileDuration( + /* [out] */ QWORD *pcnsDuration) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFileSize( + /* [out] */ QWORD *pcbFile) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsClosed( + /* [out] */ BOOL *pfClosed) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterFileSink2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterFileSink2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterFileSink2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterFileSink2 * This); + + HRESULT ( STDMETHODCALLTYPE *OnHeader )( + IWMWriterFileSink2 * This, + /* [in] */ INSSBuffer *pHeader); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterFileSink2 * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *AllocateDataUnit )( + IWMWriterFileSink2 * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnit )( + IWMWriterFileSink2 * This, + /* [in] */ INSSBuffer *pDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnEndWriting )( + IWMWriterFileSink2 * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMWriterFileSink2 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *Start )( + IWMWriterFileSink2 * This, + /* [in] */ QWORD cnsStartTime); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + IWMWriterFileSink2 * This, + /* [in] */ QWORD cnsStopTime); + + HRESULT ( STDMETHODCALLTYPE *IsStopped )( + IWMWriterFileSink2 * This, + /* [out] */ BOOL *pfStopped); + + HRESULT ( STDMETHODCALLTYPE *GetFileDuration )( + IWMWriterFileSink2 * This, + /* [out] */ QWORD *pcnsDuration); + + HRESULT ( STDMETHODCALLTYPE *GetFileSize )( + IWMWriterFileSink2 * This, + /* [out] */ QWORD *pcbFile); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMWriterFileSink2 * This); + + HRESULT ( STDMETHODCALLTYPE *IsClosed )( + IWMWriterFileSink2 * This, + /* [out] */ BOOL *pfClosed); + + END_INTERFACE + } IWMWriterFileSink2Vtbl; + + interface IWMWriterFileSink2 + { + CONST_VTBL struct IWMWriterFileSink2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterFileSink2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterFileSink2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterFileSink2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterFileSink2_OnHeader(This,pHeader) \ + (This)->lpVtbl -> OnHeader(This,pHeader) + +#define IWMWriterFileSink2_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterFileSink2_AllocateDataUnit(This,cbDataUnit,ppDataUnit) \ + (This)->lpVtbl -> AllocateDataUnit(This,cbDataUnit,ppDataUnit) + +#define IWMWriterFileSink2_OnDataUnit(This,pDataUnit) \ + (This)->lpVtbl -> OnDataUnit(This,pDataUnit) + +#define IWMWriterFileSink2_OnEndWriting(This) \ + (This)->lpVtbl -> OnEndWriting(This) + + +#define IWMWriterFileSink2_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + + +#define IWMWriterFileSink2_Start(This,cnsStartTime) \ + (This)->lpVtbl -> Start(This,cnsStartTime) + +#define IWMWriterFileSink2_Stop(This,cnsStopTime) \ + (This)->lpVtbl -> Stop(This,cnsStopTime) + +#define IWMWriterFileSink2_IsStopped(This,pfStopped) \ + (This)->lpVtbl -> IsStopped(This,pfStopped) + +#define IWMWriterFileSink2_GetFileDuration(This,pcnsDuration) \ + (This)->lpVtbl -> GetFileDuration(This,pcnsDuration) + +#define IWMWriterFileSink2_GetFileSize(This,pcbFile) \ + (This)->lpVtbl -> GetFileSize(This,pcbFile) + +#define IWMWriterFileSink2_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMWriterFileSink2_IsClosed(This,pfClosed) \ + (This)->lpVtbl -> IsClosed(This,pfClosed) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_Start_Proxy( + IWMWriterFileSink2 * This, + /* [in] */ QWORD cnsStartTime); + + +void __RPC_STUB IWMWriterFileSink2_Start_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_Stop_Proxy( + IWMWriterFileSink2 * This, + /* [in] */ QWORD cnsStopTime); + + +void __RPC_STUB IWMWriterFileSink2_Stop_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_IsStopped_Proxy( + IWMWriterFileSink2 * This, + /* [out] */ BOOL *pfStopped); + + +void __RPC_STUB IWMWriterFileSink2_IsStopped_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_GetFileDuration_Proxy( + IWMWriterFileSink2 * This, + /* [out] */ QWORD *pcnsDuration); + + +void __RPC_STUB IWMWriterFileSink2_GetFileDuration_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_GetFileSize_Proxy( + IWMWriterFileSink2 * This, + /* [out] */ QWORD *pcbFile); + + +void __RPC_STUB IWMWriterFileSink2_GetFileSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_Close_Proxy( + IWMWriterFileSink2 * This); + + +void __RPC_STUB IWMWriterFileSink2_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink2_IsClosed_Proxy( + IWMWriterFileSink2 * This, + /* [out] */ BOOL *pfClosed); + + +void __RPC_STUB IWMWriterFileSink2_IsClosed_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterFileSink2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterFileSink3_INTERFACE_DEFINED__ +#define __IWMWriterFileSink3_INTERFACE_DEFINED__ + +/* interface IWMWriterFileSink3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterFileSink3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("3FEA4FEB-2945-47A7-A1DD-C53A8FC4C45C") + IWMWriterFileSink3 : public IWMWriterFileSink2 + { + public: + virtual HRESULT STDMETHODCALLTYPE SetAutoIndexing( + /* [in] */ BOOL fDoAutoIndexing) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAutoIndexing( + /* [out] */ BOOL *pfAutoIndexing) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetControlStream( + /* [in] */ WORD wStreamNumber, + /* [in] */ BOOL fShouldControlStartAndStop) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMode( + /* [out] */ DWORD *pdwFileSinkMode) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnDataUnitEx( + /* [in] */ WMT_FILESINK_DATA_UNIT *pFileSinkDataUnit) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetUnbufferedIO( + /* [in] */ BOOL fUnbufferedIO, + /* [in] */ BOOL fRestrictMemUsage) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetUnbufferedIO( + /* [out] */ BOOL *pfUnbufferedIO) = 0; + + virtual HRESULT STDMETHODCALLTYPE CompleteOperations( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterFileSink3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterFileSink3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterFileSink3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterFileSink3 * This); + + HRESULT ( STDMETHODCALLTYPE *OnHeader )( + IWMWriterFileSink3 * This, + /* [in] */ INSSBuffer *pHeader); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *AllocateDataUnit )( + IWMWriterFileSink3 * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnit )( + IWMWriterFileSink3 * This, + /* [in] */ INSSBuffer *pDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnEndWriting )( + IWMWriterFileSink3 * This); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMWriterFileSink3 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *Start )( + IWMWriterFileSink3 * This, + /* [in] */ QWORD cnsStartTime); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + IWMWriterFileSink3 * This, + /* [in] */ QWORD cnsStopTime); + + HRESULT ( STDMETHODCALLTYPE *IsStopped )( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfStopped); + + HRESULT ( STDMETHODCALLTYPE *GetFileDuration )( + IWMWriterFileSink3 * This, + /* [out] */ QWORD *pcnsDuration); + + HRESULT ( STDMETHODCALLTYPE *GetFileSize )( + IWMWriterFileSink3 * This, + /* [out] */ QWORD *pcbFile); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMWriterFileSink3 * This); + + HRESULT ( STDMETHODCALLTYPE *IsClosed )( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfClosed); + + HRESULT ( STDMETHODCALLTYPE *SetAutoIndexing )( + IWMWriterFileSink3 * This, + /* [in] */ BOOL fDoAutoIndexing); + + HRESULT ( STDMETHODCALLTYPE *GetAutoIndexing )( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfAutoIndexing); + + HRESULT ( STDMETHODCALLTYPE *SetControlStream )( + IWMWriterFileSink3 * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ BOOL fShouldControlStartAndStop); + + HRESULT ( STDMETHODCALLTYPE *GetMode )( + IWMWriterFileSink3 * This, + /* [out] */ DWORD *pdwFileSinkMode); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnitEx )( + IWMWriterFileSink3 * This, + /* [in] */ WMT_FILESINK_DATA_UNIT *pFileSinkDataUnit); + + HRESULT ( STDMETHODCALLTYPE *SetUnbufferedIO )( + IWMWriterFileSink3 * This, + /* [in] */ BOOL fUnbufferedIO, + /* [in] */ BOOL fRestrictMemUsage); + + HRESULT ( STDMETHODCALLTYPE *GetUnbufferedIO )( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfUnbufferedIO); + + HRESULT ( STDMETHODCALLTYPE *CompleteOperations )( + IWMWriterFileSink3 * This); + + END_INTERFACE + } IWMWriterFileSink3Vtbl; + + interface IWMWriterFileSink3 + { + CONST_VTBL struct IWMWriterFileSink3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterFileSink3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterFileSink3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterFileSink3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterFileSink3_OnHeader(This,pHeader) \ + (This)->lpVtbl -> OnHeader(This,pHeader) + +#define IWMWriterFileSink3_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterFileSink3_AllocateDataUnit(This,cbDataUnit,ppDataUnit) \ + (This)->lpVtbl -> AllocateDataUnit(This,cbDataUnit,ppDataUnit) + +#define IWMWriterFileSink3_OnDataUnit(This,pDataUnit) \ + (This)->lpVtbl -> OnDataUnit(This,pDataUnit) + +#define IWMWriterFileSink3_OnEndWriting(This) \ + (This)->lpVtbl -> OnEndWriting(This) + + +#define IWMWriterFileSink3_Open(This,pwszFilename) \ + (This)->lpVtbl -> Open(This,pwszFilename) + + +#define IWMWriterFileSink3_Start(This,cnsStartTime) \ + (This)->lpVtbl -> Start(This,cnsStartTime) + +#define IWMWriterFileSink3_Stop(This,cnsStopTime) \ + (This)->lpVtbl -> Stop(This,cnsStopTime) + +#define IWMWriterFileSink3_IsStopped(This,pfStopped) \ + (This)->lpVtbl -> IsStopped(This,pfStopped) + +#define IWMWriterFileSink3_GetFileDuration(This,pcnsDuration) \ + (This)->lpVtbl -> GetFileDuration(This,pcnsDuration) + +#define IWMWriterFileSink3_GetFileSize(This,pcbFile) \ + (This)->lpVtbl -> GetFileSize(This,pcbFile) + +#define IWMWriterFileSink3_Close(This) \ + (This)->lpVtbl -> Close(This) + +#define IWMWriterFileSink3_IsClosed(This,pfClosed) \ + (This)->lpVtbl -> IsClosed(This,pfClosed) + + +#define IWMWriterFileSink3_SetAutoIndexing(This,fDoAutoIndexing) \ + (This)->lpVtbl -> SetAutoIndexing(This,fDoAutoIndexing) + +#define IWMWriterFileSink3_GetAutoIndexing(This,pfAutoIndexing) \ + (This)->lpVtbl -> GetAutoIndexing(This,pfAutoIndexing) + +#define IWMWriterFileSink3_SetControlStream(This,wStreamNumber,fShouldControlStartAndStop) \ + (This)->lpVtbl -> SetControlStream(This,wStreamNumber,fShouldControlStartAndStop) + +#define IWMWriterFileSink3_GetMode(This,pdwFileSinkMode) \ + (This)->lpVtbl -> GetMode(This,pdwFileSinkMode) + +#define IWMWriterFileSink3_OnDataUnitEx(This,pFileSinkDataUnit) \ + (This)->lpVtbl -> OnDataUnitEx(This,pFileSinkDataUnit) + +#define IWMWriterFileSink3_SetUnbufferedIO(This,fUnbufferedIO,fRestrictMemUsage) \ + (This)->lpVtbl -> SetUnbufferedIO(This,fUnbufferedIO,fRestrictMemUsage) + +#define IWMWriterFileSink3_GetUnbufferedIO(This,pfUnbufferedIO) \ + (This)->lpVtbl -> GetUnbufferedIO(This,pfUnbufferedIO) + +#define IWMWriterFileSink3_CompleteOperations(This) \ + (This)->lpVtbl -> CompleteOperations(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_SetAutoIndexing_Proxy( + IWMWriterFileSink3 * This, + /* [in] */ BOOL fDoAutoIndexing); + + +void __RPC_STUB IWMWriterFileSink3_SetAutoIndexing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_GetAutoIndexing_Proxy( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfAutoIndexing); + + +void __RPC_STUB IWMWriterFileSink3_GetAutoIndexing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_SetControlStream_Proxy( + IWMWriterFileSink3 * This, + /* [in] */ WORD wStreamNumber, + /* [in] */ BOOL fShouldControlStartAndStop); + + +void __RPC_STUB IWMWriterFileSink3_SetControlStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_GetMode_Proxy( + IWMWriterFileSink3 * This, + /* [out] */ DWORD *pdwFileSinkMode); + + +void __RPC_STUB IWMWriterFileSink3_GetMode_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_OnDataUnitEx_Proxy( + IWMWriterFileSink3 * This, + /* [in] */ WMT_FILESINK_DATA_UNIT *pFileSinkDataUnit); + + +void __RPC_STUB IWMWriterFileSink3_OnDataUnitEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_SetUnbufferedIO_Proxy( + IWMWriterFileSink3 * This, + /* [in] */ BOOL fUnbufferedIO, + /* [in] */ BOOL fRestrictMemUsage); + + +void __RPC_STUB IWMWriterFileSink3_SetUnbufferedIO_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_GetUnbufferedIO_Proxy( + IWMWriterFileSink3 * This, + /* [out] */ BOOL *pfUnbufferedIO); + + +void __RPC_STUB IWMWriterFileSink3_GetUnbufferedIO_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterFileSink3_CompleteOperations_Proxy( + IWMWriterFileSink3 * This); + + +void __RPC_STUB IWMWriterFileSink3_CompleteOperations_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterFileSink3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterNetworkSink_INTERFACE_DEFINED__ +#define __IWMWriterNetworkSink_INTERFACE_DEFINED__ + +/* interface IWMWriterNetworkSink */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterNetworkSink; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BE7-2B2B-11d3-B36B-00C04F6108FF") + IWMWriterNetworkSink : public IWMWriterSink + { + public: + virtual HRESULT STDMETHODCALLTYPE SetMaximumClients( + /* [in] */ DWORD dwMaxClients) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaximumClients( + /* [out] */ DWORD *pdwMaxClients) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetNetworkProtocol( + /* [in] */ WMT_NET_PROTOCOL protocol) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetNetworkProtocol( + /* [out] */ WMT_NET_PROTOCOL *pProtocol) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetHostURL( + /* [out] */ WCHAR *pwszURL, + /* [out][in] */ DWORD *pcchURL) = 0; + + virtual HRESULT STDMETHODCALLTYPE Open( + /* [out][in] */ DWORD *pdwPortNum) = 0; + + virtual HRESULT STDMETHODCALLTYPE Disconnect( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterNetworkSinkVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterNetworkSink * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterNetworkSink * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterNetworkSink * This); + + HRESULT ( STDMETHODCALLTYPE *OnHeader )( + IWMWriterNetworkSink * This, + /* [in] */ INSSBuffer *pHeader); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterNetworkSink * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *AllocateDataUnit )( + IWMWriterNetworkSink * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnit )( + IWMWriterNetworkSink * This, + /* [in] */ INSSBuffer *pDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnEndWriting )( + IWMWriterNetworkSink * This); + + HRESULT ( STDMETHODCALLTYPE *SetMaximumClients )( + IWMWriterNetworkSink * This, + /* [in] */ DWORD dwMaxClients); + + HRESULT ( STDMETHODCALLTYPE *GetMaximumClients )( + IWMWriterNetworkSink * This, + /* [out] */ DWORD *pdwMaxClients); + + HRESULT ( STDMETHODCALLTYPE *SetNetworkProtocol )( + IWMWriterNetworkSink * This, + /* [in] */ WMT_NET_PROTOCOL protocol); + + HRESULT ( STDMETHODCALLTYPE *GetNetworkProtocol )( + IWMWriterNetworkSink * This, + /* [out] */ WMT_NET_PROTOCOL *pProtocol); + + HRESULT ( STDMETHODCALLTYPE *GetHostURL )( + IWMWriterNetworkSink * This, + /* [out] */ WCHAR *pwszURL, + /* [out][in] */ DWORD *pcchURL); + + HRESULT ( STDMETHODCALLTYPE *Open )( + IWMWriterNetworkSink * This, + /* [out][in] */ DWORD *pdwPortNum); + + HRESULT ( STDMETHODCALLTYPE *Disconnect )( + IWMWriterNetworkSink * This); + + HRESULT ( STDMETHODCALLTYPE *Close )( + IWMWriterNetworkSink * This); + + END_INTERFACE + } IWMWriterNetworkSinkVtbl; + + interface IWMWriterNetworkSink + { + CONST_VTBL struct IWMWriterNetworkSinkVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterNetworkSink_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterNetworkSink_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterNetworkSink_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterNetworkSink_OnHeader(This,pHeader) \ + (This)->lpVtbl -> OnHeader(This,pHeader) + +#define IWMWriterNetworkSink_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterNetworkSink_AllocateDataUnit(This,cbDataUnit,ppDataUnit) \ + (This)->lpVtbl -> AllocateDataUnit(This,cbDataUnit,ppDataUnit) + +#define IWMWriterNetworkSink_OnDataUnit(This,pDataUnit) \ + (This)->lpVtbl -> OnDataUnit(This,pDataUnit) + +#define IWMWriterNetworkSink_OnEndWriting(This) \ + (This)->lpVtbl -> OnEndWriting(This) + + +#define IWMWriterNetworkSink_SetMaximumClients(This,dwMaxClients) \ + (This)->lpVtbl -> SetMaximumClients(This,dwMaxClients) + +#define IWMWriterNetworkSink_GetMaximumClients(This,pdwMaxClients) \ + (This)->lpVtbl -> GetMaximumClients(This,pdwMaxClients) + +#define IWMWriterNetworkSink_SetNetworkProtocol(This,protocol) \ + (This)->lpVtbl -> SetNetworkProtocol(This,protocol) + +#define IWMWriterNetworkSink_GetNetworkProtocol(This,pProtocol) \ + (This)->lpVtbl -> GetNetworkProtocol(This,pProtocol) + +#define IWMWriterNetworkSink_GetHostURL(This,pwszURL,pcchURL) \ + (This)->lpVtbl -> GetHostURL(This,pwszURL,pcchURL) + +#define IWMWriterNetworkSink_Open(This,pdwPortNum) \ + (This)->lpVtbl -> Open(This,pdwPortNum) + +#define IWMWriterNetworkSink_Disconnect(This) \ + (This)->lpVtbl -> Disconnect(This) + +#define IWMWriterNetworkSink_Close(This) \ + (This)->lpVtbl -> Close(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_SetMaximumClients_Proxy( + IWMWriterNetworkSink * This, + /* [in] */ DWORD dwMaxClients); + + +void __RPC_STUB IWMWriterNetworkSink_SetMaximumClients_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_GetMaximumClients_Proxy( + IWMWriterNetworkSink * This, + /* [out] */ DWORD *pdwMaxClients); + + +void __RPC_STUB IWMWriterNetworkSink_GetMaximumClients_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_SetNetworkProtocol_Proxy( + IWMWriterNetworkSink * This, + /* [in] */ WMT_NET_PROTOCOL protocol); + + +void __RPC_STUB IWMWriterNetworkSink_SetNetworkProtocol_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_GetNetworkProtocol_Proxy( + IWMWriterNetworkSink * This, + /* [out] */ WMT_NET_PROTOCOL *pProtocol); + + +void __RPC_STUB IWMWriterNetworkSink_GetNetworkProtocol_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_GetHostURL_Proxy( + IWMWriterNetworkSink * This, + /* [out] */ WCHAR *pwszURL, + /* [out][in] */ DWORD *pcchURL); + + +void __RPC_STUB IWMWriterNetworkSink_GetHostURL_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_Open_Proxy( + IWMWriterNetworkSink * This, + /* [out][in] */ DWORD *pdwPortNum); + + +void __RPC_STUB IWMWriterNetworkSink_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_Disconnect_Proxy( + IWMWriterNetworkSink * This); + + +void __RPC_STUB IWMWriterNetworkSink_Disconnect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterNetworkSink_Close_Proxy( + IWMWriterNetworkSink * This); + + +void __RPC_STUB IWMWriterNetworkSink_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterNetworkSink_INTERFACE_DEFINED__ */ + + +#ifndef __IWMClientConnections_INTERFACE_DEFINED__ +#define __IWMClientConnections_INTERFACE_DEFINED__ + +/* interface IWMClientConnections */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMClientConnections; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("73C66010-A299-41df-B1F0-CCF03B09C1C6") + IWMClientConnections : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetClientCount( + /* [out] */ DWORD *pcClients) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetClientProperties( + /* [in] */ DWORD dwClientNum, + /* [out] */ WM_CLIENT_PROPERTIES *pClientProperties) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMClientConnectionsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMClientConnections * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMClientConnections * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMClientConnections * This); + + HRESULT ( STDMETHODCALLTYPE *GetClientCount )( + IWMClientConnections * This, + /* [out] */ DWORD *pcClients); + + HRESULT ( STDMETHODCALLTYPE *GetClientProperties )( + IWMClientConnections * This, + /* [in] */ DWORD dwClientNum, + /* [out] */ WM_CLIENT_PROPERTIES *pClientProperties); + + END_INTERFACE + } IWMClientConnectionsVtbl; + + interface IWMClientConnections + { + CONST_VTBL struct IWMClientConnectionsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMClientConnections_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMClientConnections_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMClientConnections_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMClientConnections_GetClientCount(This,pcClients) \ + (This)->lpVtbl -> GetClientCount(This,pcClients) + +#define IWMClientConnections_GetClientProperties(This,dwClientNum,pClientProperties) \ + (This)->lpVtbl -> GetClientProperties(This,dwClientNum,pClientProperties) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMClientConnections_GetClientCount_Proxy( + IWMClientConnections * This, + /* [out] */ DWORD *pcClients); + + +void __RPC_STUB IWMClientConnections_GetClientCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMClientConnections_GetClientProperties_Proxy( + IWMClientConnections * This, + /* [in] */ DWORD dwClientNum, + /* [out] */ WM_CLIENT_PROPERTIES *pClientProperties); + + +void __RPC_STUB IWMClientConnections_GetClientProperties_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMClientConnections_INTERFACE_DEFINED__ */ + + +#ifndef __IWMClientConnections2_INTERFACE_DEFINED__ +#define __IWMClientConnections2_INTERFACE_DEFINED__ + +/* interface IWMClientConnections2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMClientConnections2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("4091571e-4701-4593-bb3d-d5f5f0c74246") + IWMClientConnections2 : public IWMClientConnections + { + public: + virtual HRESULT STDMETHODCALLTYPE GetClientInfo( + /* [in] */ DWORD dwClientNum, + /* [size_is][out] */ WCHAR *pwszNetworkAddress, + /* [out][in] */ DWORD *pcchNetworkAddress, + /* [size_is][out] */ WCHAR *pwszPort, + /* [out][in] */ DWORD *pcchPort, + /* [size_is][out] */ WCHAR *pwszDNSName, + /* [out][in] */ DWORD *pcchDNSName) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMClientConnections2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMClientConnections2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMClientConnections2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMClientConnections2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetClientCount )( + IWMClientConnections2 * This, + /* [out] */ DWORD *pcClients); + + HRESULT ( STDMETHODCALLTYPE *GetClientProperties )( + IWMClientConnections2 * This, + /* [in] */ DWORD dwClientNum, + /* [out] */ WM_CLIENT_PROPERTIES *pClientProperties); + + HRESULT ( STDMETHODCALLTYPE *GetClientInfo )( + IWMClientConnections2 * This, + /* [in] */ DWORD dwClientNum, + /* [size_is][out] */ WCHAR *pwszNetworkAddress, + /* [out][in] */ DWORD *pcchNetworkAddress, + /* [size_is][out] */ WCHAR *pwszPort, + /* [out][in] */ DWORD *pcchPort, + /* [size_is][out] */ WCHAR *pwszDNSName, + /* [out][in] */ DWORD *pcchDNSName); + + END_INTERFACE + } IWMClientConnections2Vtbl; + + interface IWMClientConnections2 + { + CONST_VTBL struct IWMClientConnections2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMClientConnections2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMClientConnections2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMClientConnections2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMClientConnections2_GetClientCount(This,pcClients) \ + (This)->lpVtbl -> GetClientCount(This,pcClients) + +#define IWMClientConnections2_GetClientProperties(This,dwClientNum,pClientProperties) \ + (This)->lpVtbl -> GetClientProperties(This,dwClientNum,pClientProperties) + + +#define IWMClientConnections2_GetClientInfo(This,dwClientNum,pwszNetworkAddress,pcchNetworkAddress,pwszPort,pcchPort,pwszDNSName,pcchDNSName) \ + (This)->lpVtbl -> GetClientInfo(This,dwClientNum,pwszNetworkAddress,pcchNetworkAddress,pwszPort,pcchPort,pwszDNSName,pcchDNSName) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMClientConnections2_GetClientInfo_Proxy( + IWMClientConnections2 * This, + /* [in] */ DWORD dwClientNum, + /* [size_is][out] */ WCHAR *pwszNetworkAddress, + /* [out][in] */ DWORD *pcchNetworkAddress, + /* [size_is][out] */ WCHAR *pwszPort, + /* [out][in] */ DWORD *pcchPort, + /* [size_is][out] */ WCHAR *pwszDNSName, + /* [out][in] */ DWORD *pcchDNSName); + + +void __RPC_STUB IWMClientConnections2_GetClientInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMClientConnections2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced_INTERFACE_DEFINED__ +#define __IWMReaderAdvanced_INTERFACE_DEFINED__ + +/* interface IWMReaderAdvanced */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderAdvanced; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BEA-2B2B-11d3-B36B-00C04F6108FF") + IWMReaderAdvanced : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetUserProvidedClock( + /* [in] */ BOOL fUserClock) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetUserProvidedClock( + /* [out] */ BOOL *pfUserClock) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeliverTime( + /* [in] */ QWORD cnsTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetManualStreamSelection( + /* [in] */ BOOL fSelection) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetManualStreamSelection( + /* [out] */ BOOL *pfSelection) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetStreamsSelected( + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamSelected( + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetReceiveSelectionCallbacks( + /* [in] */ BOOL fGetCallbacks) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetReceiveSelectionCallbacks( + /* [out] */ BOOL *pfGetCallbacks) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetReceiveStreamSamples( + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceiveStreamSamples) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetReceiveStreamSamples( + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceiveStreamSamples) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAllocateForOutput( + /* [in] */ DWORD dwOutputNum, + /* [in] */ BOOL fAllocate) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAllocateForOutput( + /* [in] */ DWORD dwOutputNum, + /* [out] */ BOOL *pfAllocate) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAllocateForStream( + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fAllocate) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAllocateForStream( + /* [in] */ WORD dwSreamNum, + /* [out] */ BOOL *pfAllocate) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStatistics( + /* [out][in] */ WM_READER_STATISTICS *pStatistics) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetClientInfo( + /* [in] */ WM_READER_CLIENTINFO *pClientInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxOutputSampleSize( + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxStreamSampleSize( + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax) = 0; + + virtual HRESULT STDMETHODCALLTYPE NotifyLateDelivery( + QWORD cnsLateness) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderAdvancedVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderAdvanced * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderAdvanced * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderAdvanced * This); + + HRESULT ( STDMETHODCALLTYPE *SetUserProvidedClock )( + IWMReaderAdvanced * This, + /* [in] */ BOOL fUserClock); + + HRESULT ( STDMETHODCALLTYPE *GetUserProvidedClock )( + IWMReaderAdvanced * This, + /* [out] */ BOOL *pfUserClock); + + HRESULT ( STDMETHODCALLTYPE *DeliverTime )( + IWMReaderAdvanced * This, + /* [in] */ QWORD cnsTime); + + HRESULT ( STDMETHODCALLTYPE *SetManualStreamSelection )( + IWMReaderAdvanced * This, + /* [in] */ BOOL fSelection); + + HRESULT ( STDMETHODCALLTYPE *GetManualStreamSelection )( + IWMReaderAdvanced * This, + /* [out] */ BOOL *pfSelection); + + HRESULT ( STDMETHODCALLTYPE *SetStreamsSelected )( + IWMReaderAdvanced * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + HRESULT ( STDMETHODCALLTYPE *GetStreamSelected )( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveSelectionCallbacks )( + IWMReaderAdvanced * This, + /* [in] */ BOOL fGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveSelectionCallbacks )( + IWMReaderAdvanced * This, + /* [out] */ BOOL *pfGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveStreamSamples )( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveStreamSamples )( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForOutput )( + IWMReaderAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForOutput )( + IWMReaderAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForStream )( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForStream )( + IWMReaderAdvanced * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMReaderAdvanced * This, + /* [out][in] */ WM_READER_STATISTICS *pStatistics); + + HRESULT ( STDMETHODCALLTYPE *SetClientInfo )( + IWMReaderAdvanced * This, + /* [in] */ WM_READER_CLIENTINFO *pClientInfo); + + HRESULT ( STDMETHODCALLTYPE *GetMaxOutputSampleSize )( + IWMReaderAdvanced * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *GetMaxStreamSampleSize )( + IWMReaderAdvanced * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *NotifyLateDelivery )( + IWMReaderAdvanced * This, + QWORD cnsLateness); + + END_INTERFACE + } IWMReaderAdvancedVtbl; + + interface IWMReaderAdvanced + { + CONST_VTBL struct IWMReaderAdvancedVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderAdvanced_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderAdvanced_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderAdvanced_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderAdvanced_SetUserProvidedClock(This,fUserClock) \ + (This)->lpVtbl -> SetUserProvidedClock(This,fUserClock) + +#define IWMReaderAdvanced_GetUserProvidedClock(This,pfUserClock) \ + (This)->lpVtbl -> GetUserProvidedClock(This,pfUserClock) + +#define IWMReaderAdvanced_DeliverTime(This,cnsTime) \ + (This)->lpVtbl -> DeliverTime(This,cnsTime) + +#define IWMReaderAdvanced_SetManualStreamSelection(This,fSelection) \ + (This)->lpVtbl -> SetManualStreamSelection(This,fSelection) + +#define IWMReaderAdvanced_GetManualStreamSelection(This,pfSelection) \ + (This)->lpVtbl -> GetManualStreamSelection(This,pfSelection) + +#define IWMReaderAdvanced_SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) \ + (This)->lpVtbl -> SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) + +#define IWMReaderAdvanced_GetStreamSelected(This,wStreamNum,pSelection) \ + (This)->lpVtbl -> GetStreamSelected(This,wStreamNum,pSelection) + +#define IWMReaderAdvanced_SetReceiveSelectionCallbacks(This,fGetCallbacks) \ + (This)->lpVtbl -> SetReceiveSelectionCallbacks(This,fGetCallbacks) + +#define IWMReaderAdvanced_GetReceiveSelectionCallbacks(This,pfGetCallbacks) \ + (This)->lpVtbl -> GetReceiveSelectionCallbacks(This,pfGetCallbacks) + +#define IWMReaderAdvanced_SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) \ + (This)->lpVtbl -> SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) + +#define IWMReaderAdvanced_GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) \ + (This)->lpVtbl -> GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) + +#define IWMReaderAdvanced_SetAllocateForOutput(This,dwOutputNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForOutput(This,dwOutputNum,fAllocate) + +#define IWMReaderAdvanced_GetAllocateForOutput(This,dwOutputNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForOutput(This,dwOutputNum,pfAllocate) + +#define IWMReaderAdvanced_SetAllocateForStream(This,wStreamNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForStream(This,wStreamNum,fAllocate) + +#define IWMReaderAdvanced_GetAllocateForStream(This,dwSreamNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForStream(This,dwSreamNum,pfAllocate) + +#define IWMReaderAdvanced_GetStatistics(This,pStatistics) \ + (This)->lpVtbl -> GetStatistics(This,pStatistics) + +#define IWMReaderAdvanced_SetClientInfo(This,pClientInfo) \ + (This)->lpVtbl -> SetClientInfo(This,pClientInfo) + +#define IWMReaderAdvanced_GetMaxOutputSampleSize(This,dwOutput,pcbMax) \ + (This)->lpVtbl -> GetMaxOutputSampleSize(This,dwOutput,pcbMax) + +#define IWMReaderAdvanced_GetMaxStreamSampleSize(This,wStream,pcbMax) \ + (This)->lpVtbl -> GetMaxStreamSampleSize(This,wStream,pcbMax) + +#define IWMReaderAdvanced_NotifyLateDelivery(This,cnsLateness) \ + (This)->lpVtbl -> NotifyLateDelivery(This,cnsLateness) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetUserProvidedClock_Proxy( + IWMReaderAdvanced * This, + /* [in] */ BOOL fUserClock); + + +void __RPC_STUB IWMReaderAdvanced_SetUserProvidedClock_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetUserProvidedClock_Proxy( + IWMReaderAdvanced * This, + /* [out] */ BOOL *pfUserClock); + + +void __RPC_STUB IWMReaderAdvanced_GetUserProvidedClock_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_DeliverTime_Proxy( + IWMReaderAdvanced * This, + /* [in] */ QWORD cnsTime); + + +void __RPC_STUB IWMReaderAdvanced_DeliverTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetManualStreamSelection_Proxy( + IWMReaderAdvanced * This, + /* [in] */ BOOL fSelection); + + +void __RPC_STUB IWMReaderAdvanced_SetManualStreamSelection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetManualStreamSelection_Proxy( + IWMReaderAdvanced * This, + /* [out] */ BOOL *pfSelection); + + +void __RPC_STUB IWMReaderAdvanced_GetManualStreamSelection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetStreamsSelected_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + +void __RPC_STUB IWMReaderAdvanced_SetStreamsSelected_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetStreamSelected_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + +void __RPC_STUB IWMReaderAdvanced_GetStreamSelected_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetReceiveSelectionCallbacks_Proxy( + IWMReaderAdvanced * This, + /* [in] */ BOOL fGetCallbacks); + + +void __RPC_STUB IWMReaderAdvanced_SetReceiveSelectionCallbacks_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetReceiveSelectionCallbacks_Proxy( + IWMReaderAdvanced * This, + /* [out] */ BOOL *pfGetCallbacks); + + +void __RPC_STUB IWMReaderAdvanced_GetReceiveSelectionCallbacks_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetReceiveStreamSamples_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceiveStreamSamples); + + +void __RPC_STUB IWMReaderAdvanced_SetReceiveStreamSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetReceiveStreamSamples_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceiveStreamSamples); + + +void __RPC_STUB IWMReaderAdvanced_GetReceiveStreamSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetAllocateForOutput_Proxy( + IWMReaderAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ BOOL fAllocate); + + +void __RPC_STUB IWMReaderAdvanced_SetAllocateForOutput_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetAllocateForOutput_Proxy( + IWMReaderAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ BOOL *pfAllocate); + + +void __RPC_STUB IWMReaderAdvanced_GetAllocateForOutput_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetAllocateForStream_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fAllocate); + + +void __RPC_STUB IWMReaderAdvanced_SetAllocateForStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetAllocateForStream_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ BOOL *pfAllocate); + + +void __RPC_STUB IWMReaderAdvanced_GetAllocateForStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetStatistics_Proxy( + IWMReaderAdvanced * This, + /* [out][in] */ WM_READER_STATISTICS *pStatistics); + + +void __RPC_STUB IWMReaderAdvanced_GetStatistics_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_SetClientInfo_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WM_READER_CLIENTINFO *pClientInfo); + + +void __RPC_STUB IWMReaderAdvanced_SetClientInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetMaxOutputSampleSize_Proxy( + IWMReaderAdvanced * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + +void __RPC_STUB IWMReaderAdvanced_GetMaxOutputSampleSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_GetMaxStreamSampleSize_Proxy( + IWMReaderAdvanced * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + +void __RPC_STUB IWMReaderAdvanced_GetMaxStreamSampleSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced_NotifyLateDelivery_Proxy( + IWMReaderAdvanced * This, + QWORD cnsLateness); + + +void __RPC_STUB IWMReaderAdvanced_NotifyLateDelivery_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderAdvanced_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced2_INTERFACE_DEFINED__ +#define __IWMReaderAdvanced2_INTERFACE_DEFINED__ + +/* interface IWMReaderAdvanced2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderAdvanced2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("ae14a945-b90c-4d0d-9127-80d665f7d73e") + IWMReaderAdvanced2 : public IWMReaderAdvanced + { + public: + virtual HRESULT STDMETHODCALLTYPE SetPlayMode( + /* [in] */ WMT_PLAY_MODE Mode) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPlayMode( + /* [out] */ WMT_PLAY_MODE *pMode) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBufferProgress( + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pcnsBuffering) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDownloadProgress( + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pqwBytesDownloaded, + /* [out] */ QWORD *pcnsDownload) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSaveAsProgress( + /* [out] */ DWORD *pdwPercent) = 0; + + virtual HRESULT STDMETHODCALLTYPE SaveFileAs( + /* [in] */ const WCHAR *pwszFilename) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProtocolName( + /* [size_is][out] */ WCHAR *pwszProtocol, + /* [out][in] */ DWORD *pcchProtocol) = 0; + + virtual HRESULT STDMETHODCALLTYPE StartAtMarker( + /* [in] */ WORD wMarkerIndex, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputSetting( + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputSetting( + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE Preroll( + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetLogClientID( + /* [in] */ BOOL fLogClientID) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLogClientID( + /* [out] */ BOOL *pfLogClientID) = 0; + + virtual HRESULT STDMETHODCALLTYPE StopBuffering( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE OpenStream( + /* [in] */ IStream *pStream, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderAdvanced2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderAdvanced2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderAdvanced2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderAdvanced2 * This); + + HRESULT ( STDMETHODCALLTYPE *SetUserProvidedClock )( + IWMReaderAdvanced2 * This, + /* [in] */ BOOL fUserClock); + + HRESULT ( STDMETHODCALLTYPE *GetUserProvidedClock )( + IWMReaderAdvanced2 * This, + /* [out] */ BOOL *pfUserClock); + + HRESULT ( STDMETHODCALLTYPE *DeliverTime )( + IWMReaderAdvanced2 * This, + /* [in] */ QWORD cnsTime); + + HRESULT ( STDMETHODCALLTYPE *SetManualStreamSelection )( + IWMReaderAdvanced2 * This, + /* [in] */ BOOL fSelection); + + HRESULT ( STDMETHODCALLTYPE *GetManualStreamSelection )( + IWMReaderAdvanced2 * This, + /* [out] */ BOOL *pfSelection); + + HRESULT ( STDMETHODCALLTYPE *SetStreamsSelected )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + HRESULT ( STDMETHODCALLTYPE *GetStreamSelected )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveSelectionCallbacks )( + IWMReaderAdvanced2 * This, + /* [in] */ BOOL fGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveSelectionCallbacks )( + IWMReaderAdvanced2 * This, + /* [out] */ BOOL *pfGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveStreamSamples )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveStreamSamples )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForOutput )( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForOutput )( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForStream )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForStream )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMReaderAdvanced2 * This, + /* [out][in] */ WM_READER_STATISTICS *pStatistics); + + HRESULT ( STDMETHODCALLTYPE *SetClientInfo )( + IWMReaderAdvanced2 * This, + /* [in] */ WM_READER_CLIENTINFO *pClientInfo); + + HRESULT ( STDMETHODCALLTYPE *GetMaxOutputSampleSize )( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *GetMaxStreamSampleSize )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *NotifyLateDelivery )( + IWMReaderAdvanced2 * This, + QWORD cnsLateness); + + HRESULT ( STDMETHODCALLTYPE *SetPlayMode )( + IWMReaderAdvanced2 * This, + /* [in] */ WMT_PLAY_MODE Mode); + + HRESULT ( STDMETHODCALLTYPE *GetPlayMode )( + IWMReaderAdvanced2 * This, + /* [out] */ WMT_PLAY_MODE *pMode); + + HRESULT ( STDMETHODCALLTYPE *GetBufferProgress )( + IWMReaderAdvanced2 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pcnsBuffering); + + HRESULT ( STDMETHODCALLTYPE *GetDownloadProgress )( + IWMReaderAdvanced2 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pqwBytesDownloaded, + /* [out] */ QWORD *pcnsDownload); + + HRESULT ( STDMETHODCALLTYPE *GetSaveAsProgress )( + IWMReaderAdvanced2 * This, + /* [out] */ DWORD *pdwPercent); + + HRESULT ( STDMETHODCALLTYPE *SaveFileAs )( + IWMReaderAdvanced2 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *GetProtocolName )( + IWMReaderAdvanced2 * This, + /* [size_is][out] */ WCHAR *pwszProtocol, + /* [out][in] */ DWORD *pcchProtocol); + + HRESULT ( STDMETHODCALLTYPE *StartAtMarker )( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wMarkerIndex, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *GetOutputSetting )( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetOutputSetting )( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *Preroll )( + IWMReaderAdvanced2 * This, + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate); + + HRESULT ( STDMETHODCALLTYPE *SetLogClientID )( + IWMReaderAdvanced2 * This, + /* [in] */ BOOL fLogClientID); + + HRESULT ( STDMETHODCALLTYPE *GetLogClientID )( + IWMReaderAdvanced2 * This, + /* [out] */ BOOL *pfLogClientID); + + HRESULT ( STDMETHODCALLTYPE *StopBuffering )( + IWMReaderAdvanced2 * This); + + HRESULT ( STDMETHODCALLTYPE *OpenStream )( + IWMReaderAdvanced2 * This, + /* [in] */ IStream *pStream, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMReaderAdvanced2Vtbl; + + interface IWMReaderAdvanced2 + { + CONST_VTBL struct IWMReaderAdvanced2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderAdvanced2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderAdvanced2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderAdvanced2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderAdvanced2_SetUserProvidedClock(This,fUserClock) \ + (This)->lpVtbl -> SetUserProvidedClock(This,fUserClock) + +#define IWMReaderAdvanced2_GetUserProvidedClock(This,pfUserClock) \ + (This)->lpVtbl -> GetUserProvidedClock(This,pfUserClock) + +#define IWMReaderAdvanced2_DeliverTime(This,cnsTime) \ + (This)->lpVtbl -> DeliverTime(This,cnsTime) + +#define IWMReaderAdvanced2_SetManualStreamSelection(This,fSelection) \ + (This)->lpVtbl -> SetManualStreamSelection(This,fSelection) + +#define IWMReaderAdvanced2_GetManualStreamSelection(This,pfSelection) \ + (This)->lpVtbl -> GetManualStreamSelection(This,pfSelection) + +#define IWMReaderAdvanced2_SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) \ + (This)->lpVtbl -> SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) + +#define IWMReaderAdvanced2_GetStreamSelected(This,wStreamNum,pSelection) \ + (This)->lpVtbl -> GetStreamSelected(This,wStreamNum,pSelection) + +#define IWMReaderAdvanced2_SetReceiveSelectionCallbacks(This,fGetCallbacks) \ + (This)->lpVtbl -> SetReceiveSelectionCallbacks(This,fGetCallbacks) + +#define IWMReaderAdvanced2_GetReceiveSelectionCallbacks(This,pfGetCallbacks) \ + (This)->lpVtbl -> GetReceiveSelectionCallbacks(This,pfGetCallbacks) + +#define IWMReaderAdvanced2_SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) \ + (This)->lpVtbl -> SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) + +#define IWMReaderAdvanced2_GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) \ + (This)->lpVtbl -> GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) + +#define IWMReaderAdvanced2_SetAllocateForOutput(This,dwOutputNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForOutput(This,dwOutputNum,fAllocate) + +#define IWMReaderAdvanced2_GetAllocateForOutput(This,dwOutputNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForOutput(This,dwOutputNum,pfAllocate) + +#define IWMReaderAdvanced2_SetAllocateForStream(This,wStreamNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForStream(This,wStreamNum,fAllocate) + +#define IWMReaderAdvanced2_GetAllocateForStream(This,dwSreamNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForStream(This,dwSreamNum,pfAllocate) + +#define IWMReaderAdvanced2_GetStatistics(This,pStatistics) \ + (This)->lpVtbl -> GetStatistics(This,pStatistics) + +#define IWMReaderAdvanced2_SetClientInfo(This,pClientInfo) \ + (This)->lpVtbl -> SetClientInfo(This,pClientInfo) + +#define IWMReaderAdvanced2_GetMaxOutputSampleSize(This,dwOutput,pcbMax) \ + (This)->lpVtbl -> GetMaxOutputSampleSize(This,dwOutput,pcbMax) + +#define IWMReaderAdvanced2_GetMaxStreamSampleSize(This,wStream,pcbMax) \ + (This)->lpVtbl -> GetMaxStreamSampleSize(This,wStream,pcbMax) + +#define IWMReaderAdvanced2_NotifyLateDelivery(This,cnsLateness) \ + (This)->lpVtbl -> NotifyLateDelivery(This,cnsLateness) + + +#define IWMReaderAdvanced2_SetPlayMode(This,Mode) \ + (This)->lpVtbl -> SetPlayMode(This,Mode) + +#define IWMReaderAdvanced2_GetPlayMode(This,pMode) \ + (This)->lpVtbl -> GetPlayMode(This,pMode) + +#define IWMReaderAdvanced2_GetBufferProgress(This,pdwPercent,pcnsBuffering) \ + (This)->lpVtbl -> GetBufferProgress(This,pdwPercent,pcnsBuffering) + +#define IWMReaderAdvanced2_GetDownloadProgress(This,pdwPercent,pqwBytesDownloaded,pcnsDownload) \ + (This)->lpVtbl -> GetDownloadProgress(This,pdwPercent,pqwBytesDownloaded,pcnsDownload) + +#define IWMReaderAdvanced2_GetSaveAsProgress(This,pdwPercent) \ + (This)->lpVtbl -> GetSaveAsProgress(This,pdwPercent) + +#define IWMReaderAdvanced2_SaveFileAs(This,pwszFilename) \ + (This)->lpVtbl -> SaveFileAs(This,pwszFilename) + +#define IWMReaderAdvanced2_GetProtocolName(This,pwszProtocol,pcchProtocol) \ + (This)->lpVtbl -> GetProtocolName(This,pwszProtocol,pcchProtocol) + +#define IWMReaderAdvanced2_StartAtMarker(This,wMarkerIndex,cnsDuration,fRate,pvContext) \ + (This)->lpVtbl -> StartAtMarker(This,wMarkerIndex,cnsDuration,fRate,pvContext) + +#define IWMReaderAdvanced2_GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) + +#define IWMReaderAdvanced2_SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) + +#define IWMReaderAdvanced2_Preroll(This,cnsStart,cnsDuration,fRate) \ + (This)->lpVtbl -> Preroll(This,cnsStart,cnsDuration,fRate) + +#define IWMReaderAdvanced2_SetLogClientID(This,fLogClientID) \ + (This)->lpVtbl -> SetLogClientID(This,fLogClientID) + +#define IWMReaderAdvanced2_GetLogClientID(This,pfLogClientID) \ + (This)->lpVtbl -> GetLogClientID(This,pfLogClientID) + +#define IWMReaderAdvanced2_StopBuffering(This) \ + (This)->lpVtbl -> StopBuffering(This) + +#define IWMReaderAdvanced2_OpenStream(This,pStream,pCallback,pvContext) \ + (This)->lpVtbl -> OpenStream(This,pStream,pCallback,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_SetPlayMode_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ WMT_PLAY_MODE Mode); + + +void __RPC_STUB IWMReaderAdvanced2_SetPlayMode_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetPlayMode_Proxy( + IWMReaderAdvanced2 * This, + /* [out] */ WMT_PLAY_MODE *pMode); + + +void __RPC_STUB IWMReaderAdvanced2_GetPlayMode_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetBufferProgress_Proxy( + IWMReaderAdvanced2 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pcnsBuffering); + + +void __RPC_STUB IWMReaderAdvanced2_GetBufferProgress_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetDownloadProgress_Proxy( + IWMReaderAdvanced2 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pqwBytesDownloaded, + /* [out] */ QWORD *pcnsDownload); + + +void __RPC_STUB IWMReaderAdvanced2_GetDownloadProgress_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetSaveAsProgress_Proxy( + IWMReaderAdvanced2 * This, + /* [out] */ DWORD *pdwPercent); + + +void __RPC_STUB IWMReaderAdvanced2_GetSaveAsProgress_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_SaveFileAs_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ const WCHAR *pwszFilename); + + +void __RPC_STUB IWMReaderAdvanced2_SaveFileAs_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetProtocolName_Proxy( + IWMReaderAdvanced2 * This, + /* [size_is][out] */ WCHAR *pwszProtocol, + /* [out][in] */ DWORD *pcchProtocol); + + +void __RPC_STUB IWMReaderAdvanced2_GetProtocolName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_StartAtMarker_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ WORD wMarkerIndex, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderAdvanced2_StartAtMarker_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetOutputSetting_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMReaderAdvanced2_GetOutputSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_SetOutputSetting_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMReaderAdvanced2_SetOutputSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_Preroll_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate); + + +void __RPC_STUB IWMReaderAdvanced2_Preroll_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_SetLogClientID_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ BOOL fLogClientID); + + +void __RPC_STUB IWMReaderAdvanced2_SetLogClientID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_GetLogClientID_Proxy( + IWMReaderAdvanced2 * This, + /* [out] */ BOOL *pfLogClientID); + + +void __RPC_STUB IWMReaderAdvanced2_GetLogClientID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_StopBuffering_Proxy( + IWMReaderAdvanced2 * This); + + +void __RPC_STUB IWMReaderAdvanced2_StopBuffering_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced2_OpenStream_Proxy( + IWMReaderAdvanced2 * This, + /* [in] */ IStream *pStream, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderAdvanced2_OpenStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderAdvanced2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced3_INTERFACE_DEFINED__ +#define __IWMReaderAdvanced3_INTERFACE_DEFINED__ + +/* interface IWMReaderAdvanced3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderAdvanced3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("5DC0674B-F04B-4a4e-9F2A-B1AFDE2C8100") + IWMReaderAdvanced3 : public IWMReaderAdvanced2 + { + public: + virtual HRESULT STDMETHODCALLTYPE StopNetStreaming( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE StartAtPosition( + /* [in] */ WORD wStreamNum, + /* [in] */ void *pvOffsetStart, + /* [in] */ void *pvDuration, + /* [in] */ WMT_OFFSET_FORMAT dwOffsetFormat, + /* [in] */ float fRate, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderAdvanced3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderAdvanced3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderAdvanced3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderAdvanced3 * This); + + HRESULT ( STDMETHODCALLTYPE *SetUserProvidedClock )( + IWMReaderAdvanced3 * This, + /* [in] */ BOOL fUserClock); + + HRESULT ( STDMETHODCALLTYPE *GetUserProvidedClock )( + IWMReaderAdvanced3 * This, + /* [out] */ BOOL *pfUserClock); + + HRESULT ( STDMETHODCALLTYPE *DeliverTime )( + IWMReaderAdvanced3 * This, + /* [in] */ QWORD cnsTime); + + HRESULT ( STDMETHODCALLTYPE *SetManualStreamSelection )( + IWMReaderAdvanced3 * This, + /* [in] */ BOOL fSelection); + + HRESULT ( STDMETHODCALLTYPE *GetManualStreamSelection )( + IWMReaderAdvanced3 * This, + /* [out] */ BOOL *pfSelection); + + HRESULT ( STDMETHODCALLTYPE *SetStreamsSelected )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + HRESULT ( STDMETHODCALLTYPE *GetStreamSelected )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveSelectionCallbacks )( + IWMReaderAdvanced3 * This, + /* [in] */ BOOL fGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveSelectionCallbacks )( + IWMReaderAdvanced3 * This, + /* [out] */ BOOL *pfGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveStreamSamples )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveStreamSamples )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForOutput )( + IWMReaderAdvanced3 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForOutput )( + IWMReaderAdvanced3 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForStream )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForStream )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMReaderAdvanced3 * This, + /* [out][in] */ WM_READER_STATISTICS *pStatistics); + + HRESULT ( STDMETHODCALLTYPE *SetClientInfo )( + IWMReaderAdvanced3 * This, + /* [in] */ WM_READER_CLIENTINFO *pClientInfo); + + HRESULT ( STDMETHODCALLTYPE *GetMaxOutputSampleSize )( + IWMReaderAdvanced3 * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *GetMaxStreamSampleSize )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *NotifyLateDelivery )( + IWMReaderAdvanced3 * This, + QWORD cnsLateness); + + HRESULT ( STDMETHODCALLTYPE *SetPlayMode )( + IWMReaderAdvanced3 * This, + /* [in] */ WMT_PLAY_MODE Mode); + + HRESULT ( STDMETHODCALLTYPE *GetPlayMode )( + IWMReaderAdvanced3 * This, + /* [out] */ WMT_PLAY_MODE *pMode); + + HRESULT ( STDMETHODCALLTYPE *GetBufferProgress )( + IWMReaderAdvanced3 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pcnsBuffering); + + HRESULT ( STDMETHODCALLTYPE *GetDownloadProgress )( + IWMReaderAdvanced3 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pqwBytesDownloaded, + /* [out] */ QWORD *pcnsDownload); + + HRESULT ( STDMETHODCALLTYPE *GetSaveAsProgress )( + IWMReaderAdvanced3 * This, + /* [out] */ DWORD *pdwPercent); + + HRESULT ( STDMETHODCALLTYPE *SaveFileAs )( + IWMReaderAdvanced3 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *GetProtocolName )( + IWMReaderAdvanced3 * This, + /* [size_is][out] */ WCHAR *pwszProtocol, + /* [out][in] */ DWORD *pcchProtocol); + + HRESULT ( STDMETHODCALLTYPE *StartAtMarker )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wMarkerIndex, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *GetOutputSetting )( + IWMReaderAdvanced3 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetOutputSetting )( + IWMReaderAdvanced3 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *Preroll )( + IWMReaderAdvanced3 * This, + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate); + + HRESULT ( STDMETHODCALLTYPE *SetLogClientID )( + IWMReaderAdvanced3 * This, + /* [in] */ BOOL fLogClientID); + + HRESULT ( STDMETHODCALLTYPE *GetLogClientID )( + IWMReaderAdvanced3 * This, + /* [out] */ BOOL *pfLogClientID); + + HRESULT ( STDMETHODCALLTYPE *StopBuffering )( + IWMReaderAdvanced3 * This); + + HRESULT ( STDMETHODCALLTYPE *OpenStream )( + IWMReaderAdvanced3 * This, + /* [in] */ IStream *pStream, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *StopNetStreaming )( + IWMReaderAdvanced3 * This); + + HRESULT ( STDMETHODCALLTYPE *StartAtPosition )( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ void *pvOffsetStart, + /* [in] */ void *pvDuration, + /* [in] */ WMT_OFFSET_FORMAT dwOffsetFormat, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMReaderAdvanced3Vtbl; + + interface IWMReaderAdvanced3 + { + CONST_VTBL struct IWMReaderAdvanced3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderAdvanced3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderAdvanced3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderAdvanced3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderAdvanced3_SetUserProvidedClock(This,fUserClock) \ + (This)->lpVtbl -> SetUserProvidedClock(This,fUserClock) + +#define IWMReaderAdvanced3_GetUserProvidedClock(This,pfUserClock) \ + (This)->lpVtbl -> GetUserProvidedClock(This,pfUserClock) + +#define IWMReaderAdvanced3_DeliverTime(This,cnsTime) \ + (This)->lpVtbl -> DeliverTime(This,cnsTime) + +#define IWMReaderAdvanced3_SetManualStreamSelection(This,fSelection) \ + (This)->lpVtbl -> SetManualStreamSelection(This,fSelection) + +#define IWMReaderAdvanced3_GetManualStreamSelection(This,pfSelection) \ + (This)->lpVtbl -> GetManualStreamSelection(This,pfSelection) + +#define IWMReaderAdvanced3_SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) \ + (This)->lpVtbl -> SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) + +#define IWMReaderAdvanced3_GetStreamSelected(This,wStreamNum,pSelection) \ + (This)->lpVtbl -> GetStreamSelected(This,wStreamNum,pSelection) + +#define IWMReaderAdvanced3_SetReceiveSelectionCallbacks(This,fGetCallbacks) \ + (This)->lpVtbl -> SetReceiveSelectionCallbacks(This,fGetCallbacks) + +#define IWMReaderAdvanced3_GetReceiveSelectionCallbacks(This,pfGetCallbacks) \ + (This)->lpVtbl -> GetReceiveSelectionCallbacks(This,pfGetCallbacks) + +#define IWMReaderAdvanced3_SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) \ + (This)->lpVtbl -> SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) + +#define IWMReaderAdvanced3_GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) \ + (This)->lpVtbl -> GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) + +#define IWMReaderAdvanced3_SetAllocateForOutput(This,dwOutputNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForOutput(This,dwOutputNum,fAllocate) + +#define IWMReaderAdvanced3_GetAllocateForOutput(This,dwOutputNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForOutput(This,dwOutputNum,pfAllocate) + +#define IWMReaderAdvanced3_SetAllocateForStream(This,wStreamNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForStream(This,wStreamNum,fAllocate) + +#define IWMReaderAdvanced3_GetAllocateForStream(This,dwSreamNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForStream(This,dwSreamNum,pfAllocate) + +#define IWMReaderAdvanced3_GetStatistics(This,pStatistics) \ + (This)->lpVtbl -> GetStatistics(This,pStatistics) + +#define IWMReaderAdvanced3_SetClientInfo(This,pClientInfo) \ + (This)->lpVtbl -> SetClientInfo(This,pClientInfo) + +#define IWMReaderAdvanced3_GetMaxOutputSampleSize(This,dwOutput,pcbMax) \ + (This)->lpVtbl -> GetMaxOutputSampleSize(This,dwOutput,pcbMax) + +#define IWMReaderAdvanced3_GetMaxStreamSampleSize(This,wStream,pcbMax) \ + (This)->lpVtbl -> GetMaxStreamSampleSize(This,wStream,pcbMax) + +#define IWMReaderAdvanced3_NotifyLateDelivery(This,cnsLateness) \ + (This)->lpVtbl -> NotifyLateDelivery(This,cnsLateness) + + +#define IWMReaderAdvanced3_SetPlayMode(This,Mode) \ + (This)->lpVtbl -> SetPlayMode(This,Mode) + +#define IWMReaderAdvanced3_GetPlayMode(This,pMode) \ + (This)->lpVtbl -> GetPlayMode(This,pMode) + +#define IWMReaderAdvanced3_GetBufferProgress(This,pdwPercent,pcnsBuffering) \ + (This)->lpVtbl -> GetBufferProgress(This,pdwPercent,pcnsBuffering) + +#define IWMReaderAdvanced3_GetDownloadProgress(This,pdwPercent,pqwBytesDownloaded,pcnsDownload) \ + (This)->lpVtbl -> GetDownloadProgress(This,pdwPercent,pqwBytesDownloaded,pcnsDownload) + +#define IWMReaderAdvanced3_GetSaveAsProgress(This,pdwPercent) \ + (This)->lpVtbl -> GetSaveAsProgress(This,pdwPercent) + +#define IWMReaderAdvanced3_SaveFileAs(This,pwszFilename) \ + (This)->lpVtbl -> SaveFileAs(This,pwszFilename) + +#define IWMReaderAdvanced3_GetProtocolName(This,pwszProtocol,pcchProtocol) \ + (This)->lpVtbl -> GetProtocolName(This,pwszProtocol,pcchProtocol) + +#define IWMReaderAdvanced3_StartAtMarker(This,wMarkerIndex,cnsDuration,fRate,pvContext) \ + (This)->lpVtbl -> StartAtMarker(This,wMarkerIndex,cnsDuration,fRate,pvContext) + +#define IWMReaderAdvanced3_GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) + +#define IWMReaderAdvanced3_SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) + +#define IWMReaderAdvanced3_Preroll(This,cnsStart,cnsDuration,fRate) \ + (This)->lpVtbl -> Preroll(This,cnsStart,cnsDuration,fRate) + +#define IWMReaderAdvanced3_SetLogClientID(This,fLogClientID) \ + (This)->lpVtbl -> SetLogClientID(This,fLogClientID) + +#define IWMReaderAdvanced3_GetLogClientID(This,pfLogClientID) \ + (This)->lpVtbl -> GetLogClientID(This,pfLogClientID) + +#define IWMReaderAdvanced3_StopBuffering(This) \ + (This)->lpVtbl -> StopBuffering(This) + +#define IWMReaderAdvanced3_OpenStream(This,pStream,pCallback,pvContext) \ + (This)->lpVtbl -> OpenStream(This,pStream,pCallback,pvContext) + + +#define IWMReaderAdvanced3_StopNetStreaming(This) \ + (This)->lpVtbl -> StopNetStreaming(This) + +#define IWMReaderAdvanced3_StartAtPosition(This,wStreamNum,pvOffsetStart,pvDuration,dwOffsetFormat,fRate,pvContext) \ + (This)->lpVtbl -> StartAtPosition(This,wStreamNum,pvOffsetStart,pvDuration,dwOffsetFormat,fRate,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced3_StopNetStreaming_Proxy( + IWMReaderAdvanced3 * This); + + +void __RPC_STUB IWMReaderAdvanced3_StopNetStreaming_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced3_StartAtPosition_Proxy( + IWMReaderAdvanced3 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ void *pvOffsetStart, + /* [in] */ void *pvDuration, + /* [in] */ WMT_OFFSET_FORMAT dwOffsetFormat, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderAdvanced3_StartAtPosition_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderAdvanced3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderAdvanced4_INTERFACE_DEFINED__ +#define __IWMReaderAdvanced4_INTERFACE_DEFINED__ + +/* interface IWMReaderAdvanced4 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderAdvanced4; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("945A76A2-12AE-4d48-BD3C-CD1D90399B85") + IWMReaderAdvanced4 : public IWMReaderAdvanced3 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetLanguageCount( + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwLanguageCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLanguage( + /* [in] */ DWORD dwOutputNum, + /* [in] */ WORD wLanguage, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxSpeedFactor( + /* [out] */ double *pdblFactor) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsUsingFastCache( + /* [out] */ BOOL *pfUsingFastCache) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddLogParam( + /* [in] */ LPCWSTR wszNameSpace, + /* [in] */ LPCWSTR wszName, + /* [in] */ LPCWSTR wszValue) = 0; + + virtual HRESULT STDMETHODCALLTYPE SendLogParams( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE CanSaveFileAs( + /* [out] */ BOOL *pfCanSave) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelSaveFileAs( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetURL( + /* [size_is][out] */ WCHAR *pwszURL, + /* [out][in] */ DWORD *pcchURL) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderAdvanced4Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderAdvanced4 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderAdvanced4 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderAdvanced4 * This); + + HRESULT ( STDMETHODCALLTYPE *SetUserProvidedClock )( + IWMReaderAdvanced4 * This, + /* [in] */ BOOL fUserClock); + + HRESULT ( STDMETHODCALLTYPE *GetUserProvidedClock )( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfUserClock); + + HRESULT ( STDMETHODCALLTYPE *DeliverTime )( + IWMReaderAdvanced4 * This, + /* [in] */ QWORD cnsTime); + + HRESULT ( STDMETHODCALLTYPE *SetManualStreamSelection )( + IWMReaderAdvanced4 * This, + /* [in] */ BOOL fSelection); + + HRESULT ( STDMETHODCALLTYPE *GetManualStreamSelection )( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfSelection); + + HRESULT ( STDMETHODCALLTYPE *SetStreamsSelected )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD cStreamCount, + /* [in] */ WORD *pwStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections); + + HRESULT ( STDMETHODCALLTYPE *GetStreamSelected )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WMT_STREAM_SELECTION *pSelection); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveSelectionCallbacks )( + IWMReaderAdvanced4 * This, + /* [in] */ BOOL fGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveSelectionCallbacks )( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfGetCallbacks); + + HRESULT ( STDMETHODCALLTYPE *SetReceiveStreamSamples )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *GetReceiveStreamSamples )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wStreamNum, + /* [out] */ BOOL *pfReceiveStreamSamples); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForOutput )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForOutput )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *SetAllocateForStream )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ BOOL fAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetAllocateForStream )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD dwSreamNum, + /* [out] */ BOOL *pfAllocate); + + HRESULT ( STDMETHODCALLTYPE *GetStatistics )( + IWMReaderAdvanced4 * This, + /* [out][in] */ WM_READER_STATISTICS *pStatistics); + + HRESULT ( STDMETHODCALLTYPE *SetClientInfo )( + IWMReaderAdvanced4 * This, + /* [in] */ WM_READER_CLIENTINFO *pClientInfo); + + HRESULT ( STDMETHODCALLTYPE *GetMaxOutputSampleSize )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutput, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *GetMaxStreamSampleSize )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wStream, + /* [out] */ DWORD *pcbMax); + + HRESULT ( STDMETHODCALLTYPE *NotifyLateDelivery )( + IWMReaderAdvanced4 * This, + QWORD cnsLateness); + + HRESULT ( STDMETHODCALLTYPE *SetPlayMode )( + IWMReaderAdvanced4 * This, + /* [in] */ WMT_PLAY_MODE Mode); + + HRESULT ( STDMETHODCALLTYPE *GetPlayMode )( + IWMReaderAdvanced4 * This, + /* [out] */ WMT_PLAY_MODE *pMode); + + HRESULT ( STDMETHODCALLTYPE *GetBufferProgress )( + IWMReaderAdvanced4 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pcnsBuffering); + + HRESULT ( STDMETHODCALLTYPE *GetDownloadProgress )( + IWMReaderAdvanced4 * This, + /* [out] */ DWORD *pdwPercent, + /* [out] */ QWORD *pqwBytesDownloaded, + /* [out] */ QWORD *pcnsDownload); + + HRESULT ( STDMETHODCALLTYPE *GetSaveAsProgress )( + IWMReaderAdvanced4 * This, + /* [out] */ DWORD *pdwPercent); + + HRESULT ( STDMETHODCALLTYPE *SaveFileAs )( + IWMReaderAdvanced4 * This, + /* [in] */ const WCHAR *pwszFilename); + + HRESULT ( STDMETHODCALLTYPE *GetProtocolName )( + IWMReaderAdvanced4 * This, + /* [size_is][out] */ WCHAR *pwszProtocol, + /* [out][in] */ DWORD *pcchProtocol); + + HRESULT ( STDMETHODCALLTYPE *StartAtMarker )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wMarkerIndex, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *GetOutputSetting )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetOutputSetting )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *Preroll )( + IWMReaderAdvanced4 * This, + /* [in] */ QWORD cnsStart, + /* [in] */ QWORD cnsDuration, + /* [in] */ float fRate); + + HRESULT ( STDMETHODCALLTYPE *SetLogClientID )( + IWMReaderAdvanced4 * This, + /* [in] */ BOOL fLogClientID); + + HRESULT ( STDMETHODCALLTYPE *GetLogClientID )( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfLogClientID); + + HRESULT ( STDMETHODCALLTYPE *StopBuffering )( + IWMReaderAdvanced4 * This); + + HRESULT ( STDMETHODCALLTYPE *OpenStream )( + IWMReaderAdvanced4 * This, + /* [in] */ IStream *pStream, + /* [in] */ IWMReaderCallback *pCallback, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *StopNetStreaming )( + IWMReaderAdvanced4 * This); + + HRESULT ( STDMETHODCALLTYPE *StartAtPosition )( + IWMReaderAdvanced4 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ void *pvOffsetStart, + /* [in] */ void *pvDuration, + /* [in] */ WMT_OFFSET_FORMAT dwOffsetFormat, + /* [in] */ float fRate, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *GetLanguageCount )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwLanguageCount); + + HRESULT ( STDMETHODCALLTYPE *GetLanguage )( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ WORD wLanguage, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength); + + HRESULT ( STDMETHODCALLTYPE *GetMaxSpeedFactor )( + IWMReaderAdvanced4 * This, + /* [out] */ double *pdblFactor); + + HRESULT ( STDMETHODCALLTYPE *IsUsingFastCache )( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfUsingFastCache); + + HRESULT ( STDMETHODCALLTYPE *AddLogParam )( + IWMReaderAdvanced4 * This, + /* [in] */ LPCWSTR wszNameSpace, + /* [in] */ LPCWSTR wszName, + /* [in] */ LPCWSTR wszValue); + + HRESULT ( STDMETHODCALLTYPE *SendLogParams )( + IWMReaderAdvanced4 * This); + + HRESULT ( STDMETHODCALLTYPE *CanSaveFileAs )( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfCanSave); + + HRESULT ( STDMETHODCALLTYPE *CancelSaveFileAs )( + IWMReaderAdvanced4 * This); + + HRESULT ( STDMETHODCALLTYPE *GetURL )( + IWMReaderAdvanced4 * This, + /* [size_is][out] */ WCHAR *pwszURL, + /* [out][in] */ DWORD *pcchURL); + + END_INTERFACE + } IWMReaderAdvanced4Vtbl; + + interface IWMReaderAdvanced4 + { + CONST_VTBL struct IWMReaderAdvanced4Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderAdvanced4_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderAdvanced4_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderAdvanced4_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderAdvanced4_SetUserProvidedClock(This,fUserClock) \ + (This)->lpVtbl -> SetUserProvidedClock(This,fUserClock) + +#define IWMReaderAdvanced4_GetUserProvidedClock(This,pfUserClock) \ + (This)->lpVtbl -> GetUserProvidedClock(This,pfUserClock) + +#define IWMReaderAdvanced4_DeliverTime(This,cnsTime) \ + (This)->lpVtbl -> DeliverTime(This,cnsTime) + +#define IWMReaderAdvanced4_SetManualStreamSelection(This,fSelection) \ + (This)->lpVtbl -> SetManualStreamSelection(This,fSelection) + +#define IWMReaderAdvanced4_GetManualStreamSelection(This,pfSelection) \ + (This)->lpVtbl -> GetManualStreamSelection(This,pfSelection) + +#define IWMReaderAdvanced4_SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) \ + (This)->lpVtbl -> SetStreamsSelected(This,cStreamCount,pwStreamNumbers,pSelections) + +#define IWMReaderAdvanced4_GetStreamSelected(This,wStreamNum,pSelection) \ + (This)->lpVtbl -> GetStreamSelected(This,wStreamNum,pSelection) + +#define IWMReaderAdvanced4_SetReceiveSelectionCallbacks(This,fGetCallbacks) \ + (This)->lpVtbl -> SetReceiveSelectionCallbacks(This,fGetCallbacks) + +#define IWMReaderAdvanced4_GetReceiveSelectionCallbacks(This,pfGetCallbacks) \ + (This)->lpVtbl -> GetReceiveSelectionCallbacks(This,pfGetCallbacks) + +#define IWMReaderAdvanced4_SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) \ + (This)->lpVtbl -> SetReceiveStreamSamples(This,wStreamNum,fReceiveStreamSamples) + +#define IWMReaderAdvanced4_GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) \ + (This)->lpVtbl -> GetReceiveStreamSamples(This,wStreamNum,pfReceiveStreamSamples) + +#define IWMReaderAdvanced4_SetAllocateForOutput(This,dwOutputNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForOutput(This,dwOutputNum,fAllocate) + +#define IWMReaderAdvanced4_GetAllocateForOutput(This,dwOutputNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForOutput(This,dwOutputNum,pfAllocate) + +#define IWMReaderAdvanced4_SetAllocateForStream(This,wStreamNum,fAllocate) \ + (This)->lpVtbl -> SetAllocateForStream(This,wStreamNum,fAllocate) + +#define IWMReaderAdvanced4_GetAllocateForStream(This,dwSreamNum,pfAllocate) \ + (This)->lpVtbl -> GetAllocateForStream(This,dwSreamNum,pfAllocate) + +#define IWMReaderAdvanced4_GetStatistics(This,pStatistics) \ + (This)->lpVtbl -> GetStatistics(This,pStatistics) + +#define IWMReaderAdvanced4_SetClientInfo(This,pClientInfo) \ + (This)->lpVtbl -> SetClientInfo(This,pClientInfo) + +#define IWMReaderAdvanced4_GetMaxOutputSampleSize(This,dwOutput,pcbMax) \ + (This)->lpVtbl -> GetMaxOutputSampleSize(This,dwOutput,pcbMax) + +#define IWMReaderAdvanced4_GetMaxStreamSampleSize(This,wStream,pcbMax) \ + (This)->lpVtbl -> GetMaxStreamSampleSize(This,wStream,pcbMax) + +#define IWMReaderAdvanced4_NotifyLateDelivery(This,cnsLateness) \ + (This)->lpVtbl -> NotifyLateDelivery(This,cnsLateness) + + +#define IWMReaderAdvanced4_SetPlayMode(This,Mode) \ + (This)->lpVtbl -> SetPlayMode(This,Mode) + +#define IWMReaderAdvanced4_GetPlayMode(This,pMode) \ + (This)->lpVtbl -> GetPlayMode(This,pMode) + +#define IWMReaderAdvanced4_GetBufferProgress(This,pdwPercent,pcnsBuffering) \ + (This)->lpVtbl -> GetBufferProgress(This,pdwPercent,pcnsBuffering) + +#define IWMReaderAdvanced4_GetDownloadProgress(This,pdwPercent,pqwBytesDownloaded,pcnsDownload) \ + (This)->lpVtbl -> GetDownloadProgress(This,pdwPercent,pqwBytesDownloaded,pcnsDownload) + +#define IWMReaderAdvanced4_GetSaveAsProgress(This,pdwPercent) \ + (This)->lpVtbl -> GetSaveAsProgress(This,pdwPercent) + +#define IWMReaderAdvanced4_SaveFileAs(This,pwszFilename) \ + (This)->lpVtbl -> SaveFileAs(This,pwszFilename) + +#define IWMReaderAdvanced4_GetProtocolName(This,pwszProtocol,pcchProtocol) \ + (This)->lpVtbl -> GetProtocolName(This,pwszProtocol,pcchProtocol) + +#define IWMReaderAdvanced4_StartAtMarker(This,wMarkerIndex,cnsDuration,fRate,pvContext) \ + (This)->lpVtbl -> StartAtMarker(This,wMarkerIndex,cnsDuration,fRate,pvContext) + +#define IWMReaderAdvanced4_GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetOutputSetting(This,dwOutputNum,pszName,pType,pValue,pcbLength) + +#define IWMReaderAdvanced4_SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetOutputSetting(This,dwOutputNum,pszName,Type,pValue,cbLength) + +#define IWMReaderAdvanced4_Preroll(This,cnsStart,cnsDuration,fRate) \ + (This)->lpVtbl -> Preroll(This,cnsStart,cnsDuration,fRate) + +#define IWMReaderAdvanced4_SetLogClientID(This,fLogClientID) \ + (This)->lpVtbl -> SetLogClientID(This,fLogClientID) + +#define IWMReaderAdvanced4_GetLogClientID(This,pfLogClientID) \ + (This)->lpVtbl -> GetLogClientID(This,pfLogClientID) + +#define IWMReaderAdvanced4_StopBuffering(This) \ + (This)->lpVtbl -> StopBuffering(This) + +#define IWMReaderAdvanced4_OpenStream(This,pStream,pCallback,pvContext) \ + (This)->lpVtbl -> OpenStream(This,pStream,pCallback,pvContext) + + +#define IWMReaderAdvanced4_StopNetStreaming(This) \ + (This)->lpVtbl -> StopNetStreaming(This) + +#define IWMReaderAdvanced4_StartAtPosition(This,wStreamNum,pvOffsetStart,pvDuration,dwOffsetFormat,fRate,pvContext) \ + (This)->lpVtbl -> StartAtPosition(This,wStreamNum,pvOffsetStart,pvDuration,dwOffsetFormat,fRate,pvContext) + + +#define IWMReaderAdvanced4_GetLanguageCount(This,dwOutputNum,pwLanguageCount) \ + (This)->lpVtbl -> GetLanguageCount(This,dwOutputNum,pwLanguageCount) + +#define IWMReaderAdvanced4_GetLanguage(This,dwOutputNum,wLanguage,pwszLanguageString,pcchLanguageStringLength) \ + (This)->lpVtbl -> GetLanguage(This,dwOutputNum,wLanguage,pwszLanguageString,pcchLanguageStringLength) + +#define IWMReaderAdvanced4_GetMaxSpeedFactor(This,pdblFactor) \ + (This)->lpVtbl -> GetMaxSpeedFactor(This,pdblFactor) + +#define IWMReaderAdvanced4_IsUsingFastCache(This,pfUsingFastCache) \ + (This)->lpVtbl -> IsUsingFastCache(This,pfUsingFastCache) + +#define IWMReaderAdvanced4_AddLogParam(This,wszNameSpace,wszName,wszValue) \ + (This)->lpVtbl -> AddLogParam(This,wszNameSpace,wszName,wszValue) + +#define IWMReaderAdvanced4_SendLogParams(This) \ + (This)->lpVtbl -> SendLogParams(This) + +#define IWMReaderAdvanced4_CanSaveFileAs(This,pfCanSave) \ + (This)->lpVtbl -> CanSaveFileAs(This,pfCanSave) + +#define IWMReaderAdvanced4_CancelSaveFileAs(This) \ + (This)->lpVtbl -> CancelSaveFileAs(This) + +#define IWMReaderAdvanced4_GetURL(This,pwszURL,pcchURL) \ + (This)->lpVtbl -> GetURL(This,pwszURL,pcchURL) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_GetLanguageCount_Proxy( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [out] */ WORD *pwLanguageCount); + + +void __RPC_STUB IWMReaderAdvanced4_GetLanguageCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_GetLanguage_Proxy( + IWMReaderAdvanced4 * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ WORD wLanguage, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength); + + +void __RPC_STUB IWMReaderAdvanced4_GetLanguage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_GetMaxSpeedFactor_Proxy( + IWMReaderAdvanced4 * This, + /* [out] */ double *pdblFactor); + + +void __RPC_STUB IWMReaderAdvanced4_GetMaxSpeedFactor_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_IsUsingFastCache_Proxy( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfUsingFastCache); + + +void __RPC_STUB IWMReaderAdvanced4_IsUsingFastCache_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_AddLogParam_Proxy( + IWMReaderAdvanced4 * This, + /* [in] */ LPCWSTR wszNameSpace, + /* [in] */ LPCWSTR wszName, + /* [in] */ LPCWSTR wszValue); + + +void __RPC_STUB IWMReaderAdvanced4_AddLogParam_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_SendLogParams_Proxy( + IWMReaderAdvanced4 * This); + + +void __RPC_STUB IWMReaderAdvanced4_SendLogParams_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_CanSaveFileAs_Proxy( + IWMReaderAdvanced4 * This, + /* [out] */ BOOL *pfCanSave); + + +void __RPC_STUB IWMReaderAdvanced4_CanSaveFileAs_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_CancelSaveFileAs_Proxy( + IWMReaderAdvanced4 * This); + + +void __RPC_STUB IWMReaderAdvanced4_CancelSaveFileAs_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAdvanced4_GetURL_Proxy( + IWMReaderAdvanced4 * This, + /* [size_is][out] */ WCHAR *pwszURL, + /* [out][in] */ DWORD *pcchURL); + + +void __RPC_STUB IWMReaderAdvanced4_GetURL_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderAdvanced4_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderAllocatorEx_INTERFACE_DEFINED__ +#define __IWMReaderAllocatorEx_INTERFACE_DEFINED__ + +/* interface IWMReaderAllocatorEx */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderAllocatorEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9F762FA7-A22E-428d-93C9-AC82F3AAFE5A") + IWMReaderAllocatorEx : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AllocateForStreamEx( + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ DWORD dwFlags, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocateForOutputEx( + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ DWORD dwFlags, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderAllocatorExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderAllocatorEx * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderAllocatorEx * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderAllocatorEx * This); + + HRESULT ( STDMETHODCALLTYPE *AllocateForStreamEx )( + IWMReaderAllocatorEx * This, + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ DWORD dwFlags, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *AllocateForOutputEx )( + IWMReaderAllocatorEx * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ DWORD dwFlags, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMReaderAllocatorExVtbl; + + interface IWMReaderAllocatorEx + { + CONST_VTBL struct IWMReaderAllocatorExVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderAllocatorEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderAllocatorEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderAllocatorEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderAllocatorEx_AllocateForStreamEx(This,wStreamNum,cbBuffer,ppBuffer,dwFlags,cnsSampleTime,cnsSampleDuration,pvContext) \ + (This)->lpVtbl -> AllocateForStreamEx(This,wStreamNum,cbBuffer,ppBuffer,dwFlags,cnsSampleTime,cnsSampleDuration,pvContext) + +#define IWMReaderAllocatorEx_AllocateForOutputEx(This,dwOutputNum,cbBuffer,ppBuffer,dwFlags,cnsSampleTime,cnsSampleDuration,pvContext) \ + (This)->lpVtbl -> AllocateForOutputEx(This,dwOutputNum,cbBuffer,ppBuffer,dwFlags,cnsSampleTime,cnsSampleDuration,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderAllocatorEx_AllocateForStreamEx_Proxy( + IWMReaderAllocatorEx * This, + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ DWORD dwFlags, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderAllocatorEx_AllocateForStreamEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAllocatorEx_AllocateForOutputEx_Proxy( + IWMReaderAllocatorEx * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ DWORD dwFlags, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderAllocatorEx_AllocateForOutputEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderAllocatorEx_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderTypeNegotiation_INTERFACE_DEFINED__ +#define __IWMReaderTypeNegotiation_INTERFACE_DEFINED__ + +/* interface IWMReaderTypeNegotiation */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderTypeNegotiation; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("FDBE5592-81A1-41ea-93BD-735CAD1ADC05") + IWMReaderTypeNegotiation : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE TryOutputProps( + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderTypeNegotiationVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderTypeNegotiation * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderTypeNegotiation * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderTypeNegotiation * This); + + HRESULT ( STDMETHODCALLTYPE *TryOutputProps )( + IWMReaderTypeNegotiation * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + END_INTERFACE + } IWMReaderTypeNegotiationVtbl; + + interface IWMReaderTypeNegotiation + { + CONST_VTBL struct IWMReaderTypeNegotiationVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderTypeNegotiation_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderTypeNegotiation_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderTypeNegotiation_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderTypeNegotiation_TryOutputProps(This,dwOutputNum,pOutput) \ + (This)->lpVtbl -> TryOutputProps(This,dwOutputNum,pOutput) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderTypeNegotiation_TryOutputProps_Proxy( + IWMReaderTypeNegotiation * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ IWMOutputMediaProps *pOutput); + + +void __RPC_STUB IWMReaderTypeNegotiation_TryOutputProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderTypeNegotiation_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderCallbackAdvanced_INTERFACE_DEFINED__ +#define __IWMReaderCallbackAdvanced_INTERFACE_DEFINED__ + +/* interface IWMReaderCallbackAdvanced */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderCallbackAdvanced; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BEB-2B2B-11d3-B36B-00C04F6108FF") + IWMReaderCallbackAdvanced : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnStreamSample( + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnTime( + /* [in] */ QWORD cnsCurrentTime, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnStreamSelection( + /* [in] */ WORD wStreamCount, + /* [in] */ WORD *pStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnOutputPropsChanged( + /* [in] */ DWORD dwOutputNum, + /* [in] */ WM_MEDIA_TYPE *pMediaType, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocateForStream( + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocateForOutput( + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderCallbackAdvancedVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderCallbackAdvanced * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderCallbackAdvanced * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderCallbackAdvanced * This); + + HRESULT ( STDMETHODCALLTYPE *OnStreamSample )( + IWMReaderCallbackAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *OnTime )( + IWMReaderCallbackAdvanced * This, + /* [in] */ QWORD cnsCurrentTime, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *OnStreamSelection )( + IWMReaderCallbackAdvanced * This, + /* [in] */ WORD wStreamCount, + /* [in] */ WORD *pStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *OnOutputPropsChanged )( + IWMReaderCallbackAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ WM_MEDIA_TYPE *pMediaType, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *AllocateForStream )( + IWMReaderCallbackAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *AllocateForOutput )( + IWMReaderCallbackAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext); + + END_INTERFACE + } IWMReaderCallbackAdvancedVtbl; + + interface IWMReaderCallbackAdvanced + { + CONST_VTBL struct IWMReaderCallbackAdvancedVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderCallbackAdvanced_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderCallbackAdvanced_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderCallbackAdvanced_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderCallbackAdvanced_OnStreamSample(This,wStreamNum,cnsSampleTime,cnsSampleDuration,dwFlags,pSample,pvContext) \ + (This)->lpVtbl -> OnStreamSample(This,wStreamNum,cnsSampleTime,cnsSampleDuration,dwFlags,pSample,pvContext) + +#define IWMReaderCallbackAdvanced_OnTime(This,cnsCurrentTime,pvContext) \ + (This)->lpVtbl -> OnTime(This,cnsCurrentTime,pvContext) + +#define IWMReaderCallbackAdvanced_OnStreamSelection(This,wStreamCount,pStreamNumbers,pSelections,pvContext) \ + (This)->lpVtbl -> OnStreamSelection(This,wStreamCount,pStreamNumbers,pSelections,pvContext) + +#define IWMReaderCallbackAdvanced_OnOutputPropsChanged(This,dwOutputNum,pMediaType,pvContext) \ + (This)->lpVtbl -> OnOutputPropsChanged(This,dwOutputNum,pMediaType,pvContext) + +#define IWMReaderCallbackAdvanced_AllocateForStream(This,wStreamNum,cbBuffer,ppBuffer,pvContext) \ + (This)->lpVtbl -> AllocateForStream(This,wStreamNum,cbBuffer,ppBuffer,pvContext) + +#define IWMReaderCallbackAdvanced_AllocateForOutput(This,dwOutputNum,cbBuffer,ppBuffer,pvContext) \ + (This)->lpVtbl -> AllocateForOutput(This,dwOutputNum,cbBuffer,ppBuffer,pvContext) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderCallbackAdvanced_OnStreamSample_Proxy( + IWMReaderCallbackAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ QWORD cnsSampleTime, + /* [in] */ QWORD cnsSampleDuration, + /* [in] */ DWORD dwFlags, + /* [in] */ INSSBuffer *pSample, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallbackAdvanced_OnStreamSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderCallbackAdvanced_OnTime_Proxy( + IWMReaderCallbackAdvanced * This, + /* [in] */ QWORD cnsCurrentTime, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallbackAdvanced_OnTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderCallbackAdvanced_OnStreamSelection_Proxy( + IWMReaderCallbackAdvanced * This, + /* [in] */ WORD wStreamCount, + /* [in] */ WORD *pStreamNumbers, + /* [in] */ WMT_STREAM_SELECTION *pSelections, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallbackAdvanced_OnStreamSelection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderCallbackAdvanced_OnOutputPropsChanged_Proxy( + IWMReaderCallbackAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ WM_MEDIA_TYPE *pMediaType, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallbackAdvanced_OnOutputPropsChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderCallbackAdvanced_AllocateForStream_Proxy( + IWMReaderCallbackAdvanced * This, + /* [in] */ WORD wStreamNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallbackAdvanced_AllocateForStream_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderCallbackAdvanced_AllocateForOutput_Proxy( + IWMReaderCallbackAdvanced * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ DWORD cbBuffer, + /* [out] */ INSSBuffer **ppBuffer, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMReaderCallbackAdvanced_AllocateForOutput_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderCallbackAdvanced_INTERFACE_DEFINED__ */ + + +#ifndef __IWMDRMReader_INTERFACE_DEFINED__ +#define __IWMDRMReader_INTERFACE_DEFINED__ + +/* interface IWMDRMReader */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMDRMReader; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("D2827540-3EE7-432c-B14C-DC17F085D3B3") + IWMDRMReader : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AcquireLicense( + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelLicenseAcquisition( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Individualize( + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelIndividualization( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE MonitorLicenseAcquisition( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelMonitorLicenseAcquisition( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetDRMProperty( + /* [in] */ LPCWSTR pwstrName, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDRMProperty( + /* [in] */ LPCWSTR pwstrName, + /* [out] */ WMT_ATTR_DATATYPE *pdwType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMDRMReaderVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMDRMReader * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMDRMReader * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMDRMReader * This); + + HRESULT ( STDMETHODCALLTYPE *AcquireLicense )( + IWMDRMReader * This, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE *CancelLicenseAcquisition )( + IWMDRMReader * This); + + HRESULT ( STDMETHODCALLTYPE *Individualize )( + IWMDRMReader * This, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE *CancelIndividualization )( + IWMDRMReader * This); + + HRESULT ( STDMETHODCALLTYPE *MonitorLicenseAcquisition )( + IWMDRMReader * This); + + HRESULT ( STDMETHODCALLTYPE *CancelMonitorLicenseAcquisition )( + IWMDRMReader * This); + + HRESULT ( STDMETHODCALLTYPE *SetDRMProperty )( + IWMDRMReader * This, + /* [in] */ LPCWSTR pwstrName, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *GetDRMProperty )( + IWMDRMReader * This, + /* [in] */ LPCWSTR pwstrName, + /* [out] */ WMT_ATTR_DATATYPE *pdwType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + END_INTERFACE + } IWMDRMReaderVtbl; + + interface IWMDRMReader + { + CONST_VTBL struct IWMDRMReaderVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMDRMReader_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMDRMReader_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMDRMReader_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMDRMReader_AcquireLicense(This,dwFlags) \ + (This)->lpVtbl -> AcquireLicense(This,dwFlags) + +#define IWMDRMReader_CancelLicenseAcquisition(This) \ + (This)->lpVtbl -> CancelLicenseAcquisition(This) + +#define IWMDRMReader_Individualize(This,dwFlags) \ + (This)->lpVtbl -> Individualize(This,dwFlags) + +#define IWMDRMReader_CancelIndividualization(This) \ + (This)->lpVtbl -> CancelIndividualization(This) + +#define IWMDRMReader_MonitorLicenseAcquisition(This) \ + (This)->lpVtbl -> MonitorLicenseAcquisition(This) + +#define IWMDRMReader_CancelMonitorLicenseAcquisition(This) \ + (This)->lpVtbl -> CancelMonitorLicenseAcquisition(This) + +#define IWMDRMReader_SetDRMProperty(This,pwstrName,dwType,pValue,cbLength) \ + (This)->lpVtbl -> SetDRMProperty(This,pwstrName,dwType,pValue,cbLength) + +#define IWMDRMReader_GetDRMProperty(This,pwstrName,pdwType,pValue,pcbLength) \ + (This)->lpVtbl -> GetDRMProperty(This,pwstrName,pdwType,pValue,pcbLength) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_AcquireLicense_Proxy( + IWMDRMReader * This, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IWMDRMReader_AcquireLicense_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_CancelLicenseAcquisition_Proxy( + IWMDRMReader * This); + + +void __RPC_STUB IWMDRMReader_CancelLicenseAcquisition_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_Individualize_Proxy( + IWMDRMReader * This, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IWMDRMReader_Individualize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_CancelIndividualization_Proxy( + IWMDRMReader * This); + + +void __RPC_STUB IWMDRMReader_CancelIndividualization_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_MonitorLicenseAcquisition_Proxy( + IWMDRMReader * This); + + +void __RPC_STUB IWMDRMReader_MonitorLicenseAcquisition_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_CancelMonitorLicenseAcquisition_Proxy( + IWMDRMReader * This); + + +void __RPC_STUB IWMDRMReader_CancelMonitorLicenseAcquisition_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_SetDRMProperty_Proxy( + IWMDRMReader * This, + /* [in] */ LPCWSTR pwstrName, + /* [in] */ WMT_ATTR_DATATYPE dwType, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMDRMReader_SetDRMProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMDRMReader_GetDRMProperty_Proxy( + IWMDRMReader * This, + /* [in] */ LPCWSTR pwstrName, + /* [out] */ WMT_ATTR_DATATYPE *pdwType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMDRMReader_GetDRMProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMDRMReader_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderNetworkConfig_INTERFACE_DEFINED__ +#define __IWMReaderNetworkConfig_INTERFACE_DEFINED__ + +/* interface IWMReaderNetworkConfig */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderNetworkConfig; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BEC-2B2B-11d3-B36B-00C04F6108FF") + IWMReaderNetworkConfig : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetBufferingTime( + /* [out] */ QWORD *pcnsBufferingTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBufferingTime( + /* [in] */ QWORD cnsBufferingTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetUDPPortRanges( + /* [size_is][out] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [out][in] */ DWORD *pcRanges) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetUDPPortRanges( + /* [size_is][in] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [in] */ DWORD cRanges) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProxySettings( + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ WMT_PROXY_SETTINGS *pProxySetting) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProxySettings( + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ WMT_PROXY_SETTINGS ProxySetting) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProxyHostName( + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszHostName, + /* [out][in] */ DWORD *pcchHostName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProxyHostName( + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszHostName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProxyPort( + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ DWORD *pdwPort) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProxyPort( + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ DWORD dwPort) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProxyExceptionList( + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszExceptionList, + /* [out][in] */ DWORD *pcchExceptionList) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProxyExceptionList( + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszExceptionList) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetProxyBypassForLocal( + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ BOOL *pfBypassForLocal) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProxyBypassForLocal( + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ BOOL fBypassForLocal) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetForceRerunAutoProxyDetection( + /* [out] */ BOOL *pfForceRerunDetection) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetForceRerunAutoProxyDetection( + /* [in] */ BOOL fForceRerunDetection) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableMulticast( + /* [out] */ BOOL *pfEnableMulticast) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableMulticast( + /* [in] */ BOOL fEnableMulticast) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableHTTP( + /* [out] */ BOOL *pfEnableHTTP) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableHTTP( + /* [in] */ BOOL fEnableHTTP) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableUDP( + /* [out] */ BOOL *pfEnableUDP) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableUDP( + /* [in] */ BOOL fEnableUDP) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableTCP( + /* [out] */ BOOL *pfEnableTCP) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableTCP( + /* [in] */ BOOL fEnableTCP) = 0; + + virtual HRESULT STDMETHODCALLTYPE ResetProtocolRollover( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectionBandwidth( + /* [out] */ DWORD *pdwConnectionBandwidth) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetConnectionBandwidth( + /* [in] */ DWORD dwConnectionBandwidth) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetNumProtocolsSupported( + /* [out] */ DWORD *pcProtocols) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSupportedProtocolName( + /* [in] */ DWORD dwProtocolNum, + /* [size_is][out] */ WCHAR *pwszProtocolName, + /* [out][in] */ DWORD *pcchProtocolName) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddLoggingUrl( + /* [in] */ LPCWSTR pwszUrl) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLoggingUrl( + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pwszUrl, + /* [out][in] */ DWORD *pcchUrl) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLoggingUrlCount( + /* [out] */ DWORD *pdwUrlCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE ResetLoggingUrlList( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderNetworkConfigVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderNetworkConfig * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderNetworkConfig * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderNetworkConfig * This); + + HRESULT ( STDMETHODCALLTYPE *GetBufferingTime )( + IWMReaderNetworkConfig * This, + /* [out] */ QWORD *pcnsBufferingTime); + + HRESULT ( STDMETHODCALLTYPE *SetBufferingTime )( + IWMReaderNetworkConfig * This, + /* [in] */ QWORD cnsBufferingTime); + + HRESULT ( STDMETHODCALLTYPE *GetUDPPortRanges )( + IWMReaderNetworkConfig * This, + /* [size_is][out] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [out][in] */ DWORD *pcRanges); + + HRESULT ( STDMETHODCALLTYPE *SetUDPPortRanges )( + IWMReaderNetworkConfig * This, + /* [size_is][in] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [in] */ DWORD cRanges); + + HRESULT ( STDMETHODCALLTYPE *GetProxySettings )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ WMT_PROXY_SETTINGS *pProxySetting); + + HRESULT ( STDMETHODCALLTYPE *SetProxySettings )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ WMT_PROXY_SETTINGS ProxySetting); + + HRESULT ( STDMETHODCALLTYPE *GetProxyHostName )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszHostName, + /* [out][in] */ DWORD *pcchHostName); + + HRESULT ( STDMETHODCALLTYPE *SetProxyHostName )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszHostName); + + HRESULT ( STDMETHODCALLTYPE *GetProxyPort )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ DWORD *pdwPort); + + HRESULT ( STDMETHODCALLTYPE *SetProxyPort )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ DWORD dwPort); + + HRESULT ( STDMETHODCALLTYPE *GetProxyExceptionList )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszExceptionList, + /* [out][in] */ DWORD *pcchExceptionList); + + HRESULT ( STDMETHODCALLTYPE *SetProxyExceptionList )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszExceptionList); + + HRESULT ( STDMETHODCALLTYPE *GetProxyBypassForLocal )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ BOOL *pfBypassForLocal); + + HRESULT ( STDMETHODCALLTYPE *SetProxyBypassForLocal )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ BOOL fBypassForLocal); + + HRESULT ( STDMETHODCALLTYPE *GetForceRerunAutoProxyDetection )( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfForceRerunDetection); + + HRESULT ( STDMETHODCALLTYPE *SetForceRerunAutoProxyDetection )( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fForceRerunDetection); + + HRESULT ( STDMETHODCALLTYPE *GetEnableMulticast )( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableMulticast); + + HRESULT ( STDMETHODCALLTYPE *SetEnableMulticast )( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableMulticast); + + HRESULT ( STDMETHODCALLTYPE *GetEnableHTTP )( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableHTTP); + + HRESULT ( STDMETHODCALLTYPE *SetEnableHTTP )( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableHTTP); + + HRESULT ( STDMETHODCALLTYPE *GetEnableUDP )( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableUDP); + + HRESULT ( STDMETHODCALLTYPE *SetEnableUDP )( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableUDP); + + HRESULT ( STDMETHODCALLTYPE *GetEnableTCP )( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableTCP); + + HRESULT ( STDMETHODCALLTYPE *SetEnableTCP )( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableTCP); + + HRESULT ( STDMETHODCALLTYPE *ResetProtocolRollover )( + IWMReaderNetworkConfig * This); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionBandwidth )( + IWMReaderNetworkConfig * This, + /* [out] */ DWORD *pdwConnectionBandwidth); + + HRESULT ( STDMETHODCALLTYPE *SetConnectionBandwidth )( + IWMReaderNetworkConfig * This, + /* [in] */ DWORD dwConnectionBandwidth); + + HRESULT ( STDMETHODCALLTYPE *GetNumProtocolsSupported )( + IWMReaderNetworkConfig * This, + /* [out] */ DWORD *pcProtocols); + + HRESULT ( STDMETHODCALLTYPE *GetSupportedProtocolName )( + IWMReaderNetworkConfig * This, + /* [in] */ DWORD dwProtocolNum, + /* [size_is][out] */ WCHAR *pwszProtocolName, + /* [out][in] */ DWORD *pcchProtocolName); + + HRESULT ( STDMETHODCALLTYPE *AddLoggingUrl )( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszUrl); + + HRESULT ( STDMETHODCALLTYPE *GetLoggingUrl )( + IWMReaderNetworkConfig * This, + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pwszUrl, + /* [out][in] */ DWORD *pcchUrl); + + HRESULT ( STDMETHODCALLTYPE *GetLoggingUrlCount )( + IWMReaderNetworkConfig * This, + /* [out] */ DWORD *pdwUrlCount); + + HRESULT ( STDMETHODCALLTYPE *ResetLoggingUrlList )( + IWMReaderNetworkConfig * This); + + END_INTERFACE + } IWMReaderNetworkConfigVtbl; + + interface IWMReaderNetworkConfig + { + CONST_VTBL struct IWMReaderNetworkConfigVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderNetworkConfig_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderNetworkConfig_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderNetworkConfig_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderNetworkConfig_GetBufferingTime(This,pcnsBufferingTime) \ + (This)->lpVtbl -> GetBufferingTime(This,pcnsBufferingTime) + +#define IWMReaderNetworkConfig_SetBufferingTime(This,cnsBufferingTime) \ + (This)->lpVtbl -> SetBufferingTime(This,cnsBufferingTime) + +#define IWMReaderNetworkConfig_GetUDPPortRanges(This,pRangeArray,pcRanges) \ + (This)->lpVtbl -> GetUDPPortRanges(This,pRangeArray,pcRanges) + +#define IWMReaderNetworkConfig_SetUDPPortRanges(This,pRangeArray,cRanges) \ + (This)->lpVtbl -> SetUDPPortRanges(This,pRangeArray,cRanges) + +#define IWMReaderNetworkConfig_GetProxySettings(This,pwszProtocol,pProxySetting) \ + (This)->lpVtbl -> GetProxySettings(This,pwszProtocol,pProxySetting) + +#define IWMReaderNetworkConfig_SetProxySettings(This,pwszProtocol,ProxySetting) \ + (This)->lpVtbl -> SetProxySettings(This,pwszProtocol,ProxySetting) + +#define IWMReaderNetworkConfig_GetProxyHostName(This,pwszProtocol,pwszHostName,pcchHostName) \ + (This)->lpVtbl -> GetProxyHostName(This,pwszProtocol,pwszHostName,pcchHostName) + +#define IWMReaderNetworkConfig_SetProxyHostName(This,pwszProtocol,pwszHostName) \ + (This)->lpVtbl -> SetProxyHostName(This,pwszProtocol,pwszHostName) + +#define IWMReaderNetworkConfig_GetProxyPort(This,pwszProtocol,pdwPort) \ + (This)->lpVtbl -> GetProxyPort(This,pwszProtocol,pdwPort) + +#define IWMReaderNetworkConfig_SetProxyPort(This,pwszProtocol,dwPort) \ + (This)->lpVtbl -> SetProxyPort(This,pwszProtocol,dwPort) + +#define IWMReaderNetworkConfig_GetProxyExceptionList(This,pwszProtocol,pwszExceptionList,pcchExceptionList) \ + (This)->lpVtbl -> GetProxyExceptionList(This,pwszProtocol,pwszExceptionList,pcchExceptionList) + +#define IWMReaderNetworkConfig_SetProxyExceptionList(This,pwszProtocol,pwszExceptionList) \ + (This)->lpVtbl -> SetProxyExceptionList(This,pwszProtocol,pwszExceptionList) + +#define IWMReaderNetworkConfig_GetProxyBypassForLocal(This,pwszProtocol,pfBypassForLocal) \ + (This)->lpVtbl -> GetProxyBypassForLocal(This,pwszProtocol,pfBypassForLocal) + +#define IWMReaderNetworkConfig_SetProxyBypassForLocal(This,pwszProtocol,fBypassForLocal) \ + (This)->lpVtbl -> SetProxyBypassForLocal(This,pwszProtocol,fBypassForLocal) + +#define IWMReaderNetworkConfig_GetForceRerunAutoProxyDetection(This,pfForceRerunDetection) \ + (This)->lpVtbl -> GetForceRerunAutoProxyDetection(This,pfForceRerunDetection) + +#define IWMReaderNetworkConfig_SetForceRerunAutoProxyDetection(This,fForceRerunDetection) \ + (This)->lpVtbl -> SetForceRerunAutoProxyDetection(This,fForceRerunDetection) + +#define IWMReaderNetworkConfig_GetEnableMulticast(This,pfEnableMulticast) \ + (This)->lpVtbl -> GetEnableMulticast(This,pfEnableMulticast) + +#define IWMReaderNetworkConfig_SetEnableMulticast(This,fEnableMulticast) \ + (This)->lpVtbl -> SetEnableMulticast(This,fEnableMulticast) + +#define IWMReaderNetworkConfig_GetEnableHTTP(This,pfEnableHTTP) \ + (This)->lpVtbl -> GetEnableHTTP(This,pfEnableHTTP) + +#define IWMReaderNetworkConfig_SetEnableHTTP(This,fEnableHTTP) \ + (This)->lpVtbl -> SetEnableHTTP(This,fEnableHTTP) + +#define IWMReaderNetworkConfig_GetEnableUDP(This,pfEnableUDP) \ + (This)->lpVtbl -> GetEnableUDP(This,pfEnableUDP) + +#define IWMReaderNetworkConfig_SetEnableUDP(This,fEnableUDP) \ + (This)->lpVtbl -> SetEnableUDP(This,fEnableUDP) + +#define IWMReaderNetworkConfig_GetEnableTCP(This,pfEnableTCP) \ + (This)->lpVtbl -> GetEnableTCP(This,pfEnableTCP) + +#define IWMReaderNetworkConfig_SetEnableTCP(This,fEnableTCP) \ + (This)->lpVtbl -> SetEnableTCP(This,fEnableTCP) + +#define IWMReaderNetworkConfig_ResetProtocolRollover(This) \ + (This)->lpVtbl -> ResetProtocolRollover(This) + +#define IWMReaderNetworkConfig_GetConnectionBandwidth(This,pdwConnectionBandwidth) \ + (This)->lpVtbl -> GetConnectionBandwidth(This,pdwConnectionBandwidth) + +#define IWMReaderNetworkConfig_SetConnectionBandwidth(This,dwConnectionBandwidth) \ + (This)->lpVtbl -> SetConnectionBandwidth(This,dwConnectionBandwidth) + +#define IWMReaderNetworkConfig_GetNumProtocolsSupported(This,pcProtocols) \ + (This)->lpVtbl -> GetNumProtocolsSupported(This,pcProtocols) + +#define IWMReaderNetworkConfig_GetSupportedProtocolName(This,dwProtocolNum,pwszProtocolName,pcchProtocolName) \ + (This)->lpVtbl -> GetSupportedProtocolName(This,dwProtocolNum,pwszProtocolName,pcchProtocolName) + +#define IWMReaderNetworkConfig_AddLoggingUrl(This,pwszUrl) \ + (This)->lpVtbl -> AddLoggingUrl(This,pwszUrl) + +#define IWMReaderNetworkConfig_GetLoggingUrl(This,dwIndex,pwszUrl,pcchUrl) \ + (This)->lpVtbl -> GetLoggingUrl(This,dwIndex,pwszUrl,pcchUrl) + +#define IWMReaderNetworkConfig_GetLoggingUrlCount(This,pdwUrlCount) \ + (This)->lpVtbl -> GetLoggingUrlCount(This,pdwUrlCount) + +#define IWMReaderNetworkConfig_ResetLoggingUrlList(This) \ + (This)->lpVtbl -> ResetLoggingUrlList(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetBufferingTime_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ QWORD *pcnsBufferingTime); + + +void __RPC_STUB IWMReaderNetworkConfig_GetBufferingTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetBufferingTime_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ QWORD cnsBufferingTime); + + +void __RPC_STUB IWMReaderNetworkConfig_SetBufferingTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetUDPPortRanges_Proxy( + IWMReaderNetworkConfig * This, + /* [size_is][out] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [out][in] */ DWORD *pcRanges); + + +void __RPC_STUB IWMReaderNetworkConfig_GetUDPPortRanges_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetUDPPortRanges_Proxy( + IWMReaderNetworkConfig * This, + /* [size_is][in] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [in] */ DWORD cRanges); + + +void __RPC_STUB IWMReaderNetworkConfig_SetUDPPortRanges_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetProxySettings_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ WMT_PROXY_SETTINGS *pProxySetting); + + +void __RPC_STUB IWMReaderNetworkConfig_GetProxySettings_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetProxySettings_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ WMT_PROXY_SETTINGS ProxySetting); + + +void __RPC_STUB IWMReaderNetworkConfig_SetProxySettings_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetProxyHostName_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszHostName, + /* [out][in] */ DWORD *pcchHostName); + + +void __RPC_STUB IWMReaderNetworkConfig_GetProxyHostName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetProxyHostName_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszHostName); + + +void __RPC_STUB IWMReaderNetworkConfig_SetProxyHostName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetProxyPort_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ DWORD *pdwPort); + + +void __RPC_STUB IWMReaderNetworkConfig_GetProxyPort_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetProxyPort_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ DWORD dwPort); + + +void __RPC_STUB IWMReaderNetworkConfig_SetProxyPort_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetProxyExceptionList_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszExceptionList, + /* [out][in] */ DWORD *pcchExceptionList); + + +void __RPC_STUB IWMReaderNetworkConfig_GetProxyExceptionList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetProxyExceptionList_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszExceptionList); + + +void __RPC_STUB IWMReaderNetworkConfig_SetProxyExceptionList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetProxyBypassForLocal_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ BOOL *pfBypassForLocal); + + +void __RPC_STUB IWMReaderNetworkConfig_GetProxyBypassForLocal_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetProxyBypassForLocal_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ BOOL fBypassForLocal); + + +void __RPC_STUB IWMReaderNetworkConfig_SetProxyBypassForLocal_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetForceRerunAutoProxyDetection_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfForceRerunDetection); + + +void __RPC_STUB IWMReaderNetworkConfig_GetForceRerunAutoProxyDetection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetForceRerunAutoProxyDetection_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fForceRerunDetection); + + +void __RPC_STUB IWMReaderNetworkConfig_SetForceRerunAutoProxyDetection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetEnableMulticast_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableMulticast); + + +void __RPC_STUB IWMReaderNetworkConfig_GetEnableMulticast_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetEnableMulticast_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableMulticast); + + +void __RPC_STUB IWMReaderNetworkConfig_SetEnableMulticast_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetEnableHTTP_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableHTTP); + + +void __RPC_STUB IWMReaderNetworkConfig_GetEnableHTTP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetEnableHTTP_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableHTTP); + + +void __RPC_STUB IWMReaderNetworkConfig_SetEnableHTTP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetEnableUDP_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableUDP); + + +void __RPC_STUB IWMReaderNetworkConfig_GetEnableUDP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetEnableUDP_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableUDP); + + +void __RPC_STUB IWMReaderNetworkConfig_SetEnableUDP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetEnableTCP_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ BOOL *pfEnableTCP); + + +void __RPC_STUB IWMReaderNetworkConfig_GetEnableTCP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetEnableTCP_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ BOOL fEnableTCP); + + +void __RPC_STUB IWMReaderNetworkConfig_SetEnableTCP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_ResetProtocolRollover_Proxy( + IWMReaderNetworkConfig * This); + + +void __RPC_STUB IWMReaderNetworkConfig_ResetProtocolRollover_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetConnectionBandwidth_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ DWORD *pdwConnectionBandwidth); + + +void __RPC_STUB IWMReaderNetworkConfig_GetConnectionBandwidth_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_SetConnectionBandwidth_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ DWORD dwConnectionBandwidth); + + +void __RPC_STUB IWMReaderNetworkConfig_SetConnectionBandwidth_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetNumProtocolsSupported_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ DWORD *pcProtocols); + + +void __RPC_STUB IWMReaderNetworkConfig_GetNumProtocolsSupported_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetSupportedProtocolName_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ DWORD dwProtocolNum, + /* [size_is][out] */ WCHAR *pwszProtocolName, + /* [out][in] */ DWORD *pcchProtocolName); + + +void __RPC_STUB IWMReaderNetworkConfig_GetSupportedProtocolName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_AddLoggingUrl_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ LPCWSTR pwszUrl); + + +void __RPC_STUB IWMReaderNetworkConfig_AddLoggingUrl_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetLoggingUrl_Proxy( + IWMReaderNetworkConfig * This, + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pwszUrl, + /* [out][in] */ DWORD *pcchUrl); + + +void __RPC_STUB IWMReaderNetworkConfig_GetLoggingUrl_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_GetLoggingUrlCount_Proxy( + IWMReaderNetworkConfig * This, + /* [out] */ DWORD *pdwUrlCount); + + +void __RPC_STUB IWMReaderNetworkConfig_GetLoggingUrlCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig_ResetLoggingUrlList_Proxy( + IWMReaderNetworkConfig * This); + + +void __RPC_STUB IWMReaderNetworkConfig_ResetLoggingUrlList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderNetworkConfig_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderNetworkConfig2_INTERFACE_DEFINED__ +#define __IWMReaderNetworkConfig2_INTERFACE_DEFINED__ + +/* interface IWMReaderNetworkConfig2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderNetworkConfig2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("d979a853-042b-4050-8387-c939db22013f") + IWMReaderNetworkConfig2 : public IWMReaderNetworkConfig + { + public: + virtual HRESULT STDMETHODCALLTYPE GetEnableContentCaching( + /* [out] */ BOOL *pfEnableContentCaching) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableContentCaching( + /* [in] */ BOOL fEnableContentCaching) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableFastCache( + /* [out] */ BOOL *pfEnableFastCache) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableFastCache( + /* [in] */ BOOL fEnableFastCache) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAcceleratedStreamingDuration( + /* [out] */ QWORD *pcnsAccelDuration) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAcceleratedStreamingDuration( + /* [in] */ QWORD cnsAccelDuration) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAutoReconnectLimit( + /* [out] */ DWORD *pdwAutoReconnectLimit) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAutoReconnectLimit( + /* [in] */ DWORD dwAutoReconnectLimit) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableResends( + /* [out] */ BOOL *pfEnableResends) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableResends( + /* [in] */ BOOL fEnableResends) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetEnableThinning( + /* [out] */ BOOL *pfEnableThinning) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetEnableThinning( + /* [in] */ BOOL fEnableThinning) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMaxNetPacketSize( + /* [out] */ DWORD *pdwMaxNetPacketSize) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderNetworkConfig2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderNetworkConfig2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderNetworkConfig2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderNetworkConfig2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetBufferingTime )( + IWMReaderNetworkConfig2 * This, + /* [out] */ QWORD *pcnsBufferingTime); + + HRESULT ( STDMETHODCALLTYPE *SetBufferingTime )( + IWMReaderNetworkConfig2 * This, + /* [in] */ QWORD cnsBufferingTime); + + HRESULT ( STDMETHODCALLTYPE *GetUDPPortRanges )( + IWMReaderNetworkConfig2 * This, + /* [size_is][out] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [out][in] */ DWORD *pcRanges); + + HRESULT ( STDMETHODCALLTYPE *SetUDPPortRanges )( + IWMReaderNetworkConfig2 * This, + /* [size_is][in] */ WM_PORT_NUMBER_RANGE *pRangeArray, + /* [in] */ DWORD cRanges); + + HRESULT ( STDMETHODCALLTYPE *GetProxySettings )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ WMT_PROXY_SETTINGS *pProxySetting); + + HRESULT ( STDMETHODCALLTYPE *SetProxySettings )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ WMT_PROXY_SETTINGS ProxySetting); + + HRESULT ( STDMETHODCALLTYPE *GetProxyHostName )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszHostName, + /* [out][in] */ DWORD *pcchHostName); + + HRESULT ( STDMETHODCALLTYPE *SetProxyHostName )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszHostName); + + HRESULT ( STDMETHODCALLTYPE *GetProxyPort )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ DWORD *pdwPort); + + HRESULT ( STDMETHODCALLTYPE *SetProxyPort )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ DWORD dwPort); + + HRESULT ( STDMETHODCALLTYPE *GetProxyExceptionList )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [size_is][out] */ WCHAR *pwszExceptionList, + /* [out][in] */ DWORD *pcchExceptionList); + + HRESULT ( STDMETHODCALLTYPE *SetProxyExceptionList )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ LPCWSTR pwszExceptionList); + + HRESULT ( STDMETHODCALLTYPE *GetProxyBypassForLocal )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [out] */ BOOL *pfBypassForLocal); + + HRESULT ( STDMETHODCALLTYPE *SetProxyBypassForLocal )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszProtocol, + /* [in] */ BOOL fBypassForLocal); + + HRESULT ( STDMETHODCALLTYPE *GetForceRerunAutoProxyDetection )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfForceRerunDetection); + + HRESULT ( STDMETHODCALLTYPE *SetForceRerunAutoProxyDetection )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fForceRerunDetection); + + HRESULT ( STDMETHODCALLTYPE *GetEnableMulticast )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableMulticast); + + HRESULT ( STDMETHODCALLTYPE *SetEnableMulticast )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableMulticast); + + HRESULT ( STDMETHODCALLTYPE *GetEnableHTTP )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableHTTP); + + HRESULT ( STDMETHODCALLTYPE *SetEnableHTTP )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableHTTP); + + HRESULT ( STDMETHODCALLTYPE *GetEnableUDP )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableUDP); + + HRESULT ( STDMETHODCALLTYPE *SetEnableUDP )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableUDP); + + HRESULT ( STDMETHODCALLTYPE *GetEnableTCP )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableTCP); + + HRESULT ( STDMETHODCALLTYPE *SetEnableTCP )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableTCP); + + HRESULT ( STDMETHODCALLTYPE *ResetProtocolRollover )( + IWMReaderNetworkConfig2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetConnectionBandwidth )( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pdwConnectionBandwidth); + + HRESULT ( STDMETHODCALLTYPE *SetConnectionBandwidth )( + IWMReaderNetworkConfig2 * This, + /* [in] */ DWORD dwConnectionBandwidth); + + HRESULT ( STDMETHODCALLTYPE *GetNumProtocolsSupported )( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pcProtocols); + + HRESULT ( STDMETHODCALLTYPE *GetSupportedProtocolName )( + IWMReaderNetworkConfig2 * This, + /* [in] */ DWORD dwProtocolNum, + /* [size_is][out] */ WCHAR *pwszProtocolName, + /* [out][in] */ DWORD *pcchProtocolName); + + HRESULT ( STDMETHODCALLTYPE *AddLoggingUrl )( + IWMReaderNetworkConfig2 * This, + /* [in] */ LPCWSTR pwszUrl); + + HRESULT ( STDMETHODCALLTYPE *GetLoggingUrl )( + IWMReaderNetworkConfig2 * This, + /* [in] */ DWORD dwIndex, + /* [size_is][out] */ LPWSTR pwszUrl, + /* [out][in] */ DWORD *pcchUrl); + + HRESULT ( STDMETHODCALLTYPE *GetLoggingUrlCount )( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pdwUrlCount); + + HRESULT ( STDMETHODCALLTYPE *ResetLoggingUrlList )( + IWMReaderNetworkConfig2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetEnableContentCaching )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableContentCaching); + + HRESULT ( STDMETHODCALLTYPE *SetEnableContentCaching )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableContentCaching); + + HRESULT ( STDMETHODCALLTYPE *GetEnableFastCache )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableFastCache); + + HRESULT ( STDMETHODCALLTYPE *SetEnableFastCache )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableFastCache); + + HRESULT ( STDMETHODCALLTYPE *GetAcceleratedStreamingDuration )( + IWMReaderNetworkConfig2 * This, + /* [out] */ QWORD *pcnsAccelDuration); + + HRESULT ( STDMETHODCALLTYPE *SetAcceleratedStreamingDuration )( + IWMReaderNetworkConfig2 * This, + /* [in] */ QWORD cnsAccelDuration); + + HRESULT ( STDMETHODCALLTYPE *GetAutoReconnectLimit )( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pdwAutoReconnectLimit); + + HRESULT ( STDMETHODCALLTYPE *SetAutoReconnectLimit )( + IWMReaderNetworkConfig2 * This, + /* [in] */ DWORD dwAutoReconnectLimit); + + HRESULT ( STDMETHODCALLTYPE *GetEnableResends )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableResends); + + HRESULT ( STDMETHODCALLTYPE *SetEnableResends )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableResends); + + HRESULT ( STDMETHODCALLTYPE *GetEnableThinning )( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableThinning); + + HRESULT ( STDMETHODCALLTYPE *SetEnableThinning )( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableThinning); + + HRESULT ( STDMETHODCALLTYPE *GetMaxNetPacketSize )( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pdwMaxNetPacketSize); + + END_INTERFACE + } IWMReaderNetworkConfig2Vtbl; + + interface IWMReaderNetworkConfig2 + { + CONST_VTBL struct IWMReaderNetworkConfig2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderNetworkConfig2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderNetworkConfig2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderNetworkConfig2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderNetworkConfig2_GetBufferingTime(This,pcnsBufferingTime) \ + (This)->lpVtbl -> GetBufferingTime(This,pcnsBufferingTime) + +#define IWMReaderNetworkConfig2_SetBufferingTime(This,cnsBufferingTime) \ + (This)->lpVtbl -> SetBufferingTime(This,cnsBufferingTime) + +#define IWMReaderNetworkConfig2_GetUDPPortRanges(This,pRangeArray,pcRanges) \ + (This)->lpVtbl -> GetUDPPortRanges(This,pRangeArray,pcRanges) + +#define IWMReaderNetworkConfig2_SetUDPPortRanges(This,pRangeArray,cRanges) \ + (This)->lpVtbl -> SetUDPPortRanges(This,pRangeArray,cRanges) + +#define IWMReaderNetworkConfig2_GetProxySettings(This,pwszProtocol,pProxySetting) \ + (This)->lpVtbl -> GetProxySettings(This,pwszProtocol,pProxySetting) + +#define IWMReaderNetworkConfig2_SetProxySettings(This,pwszProtocol,ProxySetting) \ + (This)->lpVtbl -> SetProxySettings(This,pwszProtocol,ProxySetting) + +#define IWMReaderNetworkConfig2_GetProxyHostName(This,pwszProtocol,pwszHostName,pcchHostName) \ + (This)->lpVtbl -> GetProxyHostName(This,pwszProtocol,pwszHostName,pcchHostName) + +#define IWMReaderNetworkConfig2_SetProxyHostName(This,pwszProtocol,pwszHostName) \ + (This)->lpVtbl -> SetProxyHostName(This,pwszProtocol,pwszHostName) + +#define IWMReaderNetworkConfig2_GetProxyPort(This,pwszProtocol,pdwPort) \ + (This)->lpVtbl -> GetProxyPort(This,pwszProtocol,pdwPort) + +#define IWMReaderNetworkConfig2_SetProxyPort(This,pwszProtocol,dwPort) \ + (This)->lpVtbl -> SetProxyPort(This,pwszProtocol,dwPort) + +#define IWMReaderNetworkConfig2_GetProxyExceptionList(This,pwszProtocol,pwszExceptionList,pcchExceptionList) \ + (This)->lpVtbl -> GetProxyExceptionList(This,pwszProtocol,pwszExceptionList,pcchExceptionList) + +#define IWMReaderNetworkConfig2_SetProxyExceptionList(This,pwszProtocol,pwszExceptionList) \ + (This)->lpVtbl -> SetProxyExceptionList(This,pwszProtocol,pwszExceptionList) + +#define IWMReaderNetworkConfig2_GetProxyBypassForLocal(This,pwszProtocol,pfBypassForLocal) \ + (This)->lpVtbl -> GetProxyBypassForLocal(This,pwszProtocol,pfBypassForLocal) + +#define IWMReaderNetworkConfig2_SetProxyBypassForLocal(This,pwszProtocol,fBypassForLocal) \ + (This)->lpVtbl -> SetProxyBypassForLocal(This,pwszProtocol,fBypassForLocal) + +#define IWMReaderNetworkConfig2_GetForceRerunAutoProxyDetection(This,pfForceRerunDetection) \ + (This)->lpVtbl -> GetForceRerunAutoProxyDetection(This,pfForceRerunDetection) + +#define IWMReaderNetworkConfig2_SetForceRerunAutoProxyDetection(This,fForceRerunDetection) \ + (This)->lpVtbl -> SetForceRerunAutoProxyDetection(This,fForceRerunDetection) + +#define IWMReaderNetworkConfig2_GetEnableMulticast(This,pfEnableMulticast) \ + (This)->lpVtbl -> GetEnableMulticast(This,pfEnableMulticast) + +#define IWMReaderNetworkConfig2_SetEnableMulticast(This,fEnableMulticast) \ + (This)->lpVtbl -> SetEnableMulticast(This,fEnableMulticast) + +#define IWMReaderNetworkConfig2_GetEnableHTTP(This,pfEnableHTTP) \ + (This)->lpVtbl -> GetEnableHTTP(This,pfEnableHTTP) + +#define IWMReaderNetworkConfig2_SetEnableHTTP(This,fEnableHTTP) \ + (This)->lpVtbl -> SetEnableHTTP(This,fEnableHTTP) + +#define IWMReaderNetworkConfig2_GetEnableUDP(This,pfEnableUDP) \ + (This)->lpVtbl -> GetEnableUDP(This,pfEnableUDP) + +#define IWMReaderNetworkConfig2_SetEnableUDP(This,fEnableUDP) \ + (This)->lpVtbl -> SetEnableUDP(This,fEnableUDP) + +#define IWMReaderNetworkConfig2_GetEnableTCP(This,pfEnableTCP) \ + (This)->lpVtbl -> GetEnableTCP(This,pfEnableTCP) + +#define IWMReaderNetworkConfig2_SetEnableTCP(This,fEnableTCP) \ + (This)->lpVtbl -> SetEnableTCP(This,fEnableTCP) + +#define IWMReaderNetworkConfig2_ResetProtocolRollover(This) \ + (This)->lpVtbl -> ResetProtocolRollover(This) + +#define IWMReaderNetworkConfig2_GetConnectionBandwidth(This,pdwConnectionBandwidth) \ + (This)->lpVtbl -> GetConnectionBandwidth(This,pdwConnectionBandwidth) + +#define IWMReaderNetworkConfig2_SetConnectionBandwidth(This,dwConnectionBandwidth) \ + (This)->lpVtbl -> SetConnectionBandwidth(This,dwConnectionBandwidth) + +#define IWMReaderNetworkConfig2_GetNumProtocolsSupported(This,pcProtocols) \ + (This)->lpVtbl -> GetNumProtocolsSupported(This,pcProtocols) + +#define IWMReaderNetworkConfig2_GetSupportedProtocolName(This,dwProtocolNum,pwszProtocolName,pcchProtocolName) \ + (This)->lpVtbl -> GetSupportedProtocolName(This,dwProtocolNum,pwszProtocolName,pcchProtocolName) + +#define IWMReaderNetworkConfig2_AddLoggingUrl(This,pwszUrl) \ + (This)->lpVtbl -> AddLoggingUrl(This,pwszUrl) + +#define IWMReaderNetworkConfig2_GetLoggingUrl(This,dwIndex,pwszUrl,pcchUrl) \ + (This)->lpVtbl -> GetLoggingUrl(This,dwIndex,pwszUrl,pcchUrl) + +#define IWMReaderNetworkConfig2_GetLoggingUrlCount(This,pdwUrlCount) \ + (This)->lpVtbl -> GetLoggingUrlCount(This,pdwUrlCount) + +#define IWMReaderNetworkConfig2_ResetLoggingUrlList(This) \ + (This)->lpVtbl -> ResetLoggingUrlList(This) + + +#define IWMReaderNetworkConfig2_GetEnableContentCaching(This,pfEnableContentCaching) \ + (This)->lpVtbl -> GetEnableContentCaching(This,pfEnableContentCaching) + +#define IWMReaderNetworkConfig2_SetEnableContentCaching(This,fEnableContentCaching) \ + (This)->lpVtbl -> SetEnableContentCaching(This,fEnableContentCaching) + +#define IWMReaderNetworkConfig2_GetEnableFastCache(This,pfEnableFastCache) \ + (This)->lpVtbl -> GetEnableFastCache(This,pfEnableFastCache) + +#define IWMReaderNetworkConfig2_SetEnableFastCache(This,fEnableFastCache) \ + (This)->lpVtbl -> SetEnableFastCache(This,fEnableFastCache) + +#define IWMReaderNetworkConfig2_GetAcceleratedStreamingDuration(This,pcnsAccelDuration) \ + (This)->lpVtbl -> GetAcceleratedStreamingDuration(This,pcnsAccelDuration) + +#define IWMReaderNetworkConfig2_SetAcceleratedStreamingDuration(This,cnsAccelDuration) \ + (This)->lpVtbl -> SetAcceleratedStreamingDuration(This,cnsAccelDuration) + +#define IWMReaderNetworkConfig2_GetAutoReconnectLimit(This,pdwAutoReconnectLimit) \ + (This)->lpVtbl -> GetAutoReconnectLimit(This,pdwAutoReconnectLimit) + +#define IWMReaderNetworkConfig2_SetAutoReconnectLimit(This,dwAutoReconnectLimit) \ + (This)->lpVtbl -> SetAutoReconnectLimit(This,dwAutoReconnectLimit) + +#define IWMReaderNetworkConfig2_GetEnableResends(This,pfEnableResends) \ + (This)->lpVtbl -> GetEnableResends(This,pfEnableResends) + +#define IWMReaderNetworkConfig2_SetEnableResends(This,fEnableResends) \ + (This)->lpVtbl -> SetEnableResends(This,fEnableResends) + +#define IWMReaderNetworkConfig2_GetEnableThinning(This,pfEnableThinning) \ + (This)->lpVtbl -> GetEnableThinning(This,pfEnableThinning) + +#define IWMReaderNetworkConfig2_SetEnableThinning(This,fEnableThinning) \ + (This)->lpVtbl -> SetEnableThinning(This,fEnableThinning) + +#define IWMReaderNetworkConfig2_GetMaxNetPacketSize(This,pdwMaxNetPacketSize) \ + (This)->lpVtbl -> GetMaxNetPacketSize(This,pdwMaxNetPacketSize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetEnableContentCaching_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableContentCaching); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetEnableContentCaching_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_SetEnableContentCaching_Proxy( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableContentCaching); + + +void __RPC_STUB IWMReaderNetworkConfig2_SetEnableContentCaching_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetEnableFastCache_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableFastCache); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetEnableFastCache_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_SetEnableFastCache_Proxy( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableFastCache); + + +void __RPC_STUB IWMReaderNetworkConfig2_SetEnableFastCache_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetAcceleratedStreamingDuration_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ QWORD *pcnsAccelDuration); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetAcceleratedStreamingDuration_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_SetAcceleratedStreamingDuration_Proxy( + IWMReaderNetworkConfig2 * This, + /* [in] */ QWORD cnsAccelDuration); + + +void __RPC_STUB IWMReaderNetworkConfig2_SetAcceleratedStreamingDuration_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetAutoReconnectLimit_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pdwAutoReconnectLimit); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetAutoReconnectLimit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_SetAutoReconnectLimit_Proxy( + IWMReaderNetworkConfig2 * This, + /* [in] */ DWORD dwAutoReconnectLimit); + + +void __RPC_STUB IWMReaderNetworkConfig2_SetAutoReconnectLimit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetEnableResends_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableResends); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetEnableResends_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_SetEnableResends_Proxy( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableResends); + + +void __RPC_STUB IWMReaderNetworkConfig2_SetEnableResends_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetEnableThinning_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ BOOL *pfEnableThinning); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetEnableThinning_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_SetEnableThinning_Proxy( + IWMReaderNetworkConfig2 * This, + /* [in] */ BOOL fEnableThinning); + + +void __RPC_STUB IWMReaderNetworkConfig2_SetEnableThinning_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderNetworkConfig2_GetMaxNetPacketSize_Proxy( + IWMReaderNetworkConfig2 * This, + /* [out] */ DWORD *pdwMaxNetPacketSize); + + +void __RPC_STUB IWMReaderNetworkConfig2_GetMaxNetPacketSize_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderNetworkConfig2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderStreamClock_INTERFACE_DEFINED__ +#define __IWMReaderStreamClock_INTERFACE_DEFINED__ + +/* interface IWMReaderStreamClock */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderStreamClock; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96406BED-2B2B-11d3-B36B-00C04F6108FF") + IWMReaderStreamClock : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetTime( + /* [in] */ QWORD *pcnsNow) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetTimer( + /* [in] */ QWORD cnsWhen, + /* [in] */ void *pvParam, + /* [out] */ DWORD *pdwTimerId) = 0; + + virtual HRESULT STDMETHODCALLTYPE KillTimer( + /* [in] */ DWORD dwTimerId) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderStreamClockVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderStreamClock * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderStreamClock * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderStreamClock * This); + + HRESULT ( STDMETHODCALLTYPE *GetTime )( + IWMReaderStreamClock * This, + /* [in] */ QWORD *pcnsNow); + + HRESULT ( STDMETHODCALLTYPE *SetTimer )( + IWMReaderStreamClock * This, + /* [in] */ QWORD cnsWhen, + /* [in] */ void *pvParam, + /* [out] */ DWORD *pdwTimerId); + + HRESULT ( STDMETHODCALLTYPE *KillTimer )( + IWMReaderStreamClock * This, + /* [in] */ DWORD dwTimerId); + + END_INTERFACE + } IWMReaderStreamClockVtbl; + + interface IWMReaderStreamClock + { + CONST_VTBL struct IWMReaderStreamClockVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderStreamClock_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderStreamClock_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderStreamClock_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderStreamClock_GetTime(This,pcnsNow) \ + (This)->lpVtbl -> GetTime(This,pcnsNow) + +#define IWMReaderStreamClock_SetTimer(This,cnsWhen,pvParam,pdwTimerId) \ + (This)->lpVtbl -> SetTimer(This,cnsWhen,pvParam,pdwTimerId) + +#define IWMReaderStreamClock_KillTimer(This,dwTimerId) \ + (This)->lpVtbl -> KillTimer(This,dwTimerId) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderStreamClock_GetTime_Proxy( + IWMReaderStreamClock * This, + /* [in] */ QWORD *pcnsNow); + + +void __RPC_STUB IWMReaderStreamClock_GetTime_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderStreamClock_SetTimer_Proxy( + IWMReaderStreamClock * This, + /* [in] */ QWORD cnsWhen, + /* [in] */ void *pvParam, + /* [out] */ DWORD *pdwTimerId); + + +void __RPC_STUB IWMReaderStreamClock_SetTimer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderStreamClock_KillTimer_Proxy( + IWMReaderStreamClock * This, + /* [in] */ DWORD dwTimerId); + + +void __RPC_STUB IWMReaderStreamClock_KillTimer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderStreamClock_INTERFACE_DEFINED__ */ + + +#ifndef __IWMIndexer_INTERFACE_DEFINED__ +#define __IWMIndexer_INTERFACE_DEFINED__ + +/* interface IWMIndexer */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMIndexer; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("6d7cdc71-9888-11d3-8edc-00c04f6109cf") + IWMIndexer : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE StartIndexing( + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE Cancel( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMIndexerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMIndexer * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMIndexer * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMIndexer * This); + + HRESULT ( STDMETHODCALLTYPE *StartIndexing )( + IWMIndexer * This, + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *Cancel )( + IWMIndexer * This); + + END_INTERFACE + } IWMIndexerVtbl; + + interface IWMIndexer + { + CONST_VTBL struct IWMIndexerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMIndexer_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMIndexer_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMIndexer_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMIndexer_StartIndexing(This,pwszURL,pCallback,pvContext) \ + (This)->lpVtbl -> StartIndexing(This,pwszURL,pCallback,pvContext) + +#define IWMIndexer_Cancel(This) \ + (This)->lpVtbl -> Cancel(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMIndexer_StartIndexing_Proxy( + IWMIndexer * This, + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + +void __RPC_STUB IWMIndexer_StartIndexing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMIndexer_Cancel_Proxy( + IWMIndexer * This); + + +void __RPC_STUB IWMIndexer_Cancel_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMIndexer_INTERFACE_DEFINED__ */ + + +#ifndef __IWMIndexer2_INTERFACE_DEFINED__ +#define __IWMIndexer2_INTERFACE_DEFINED__ + +/* interface IWMIndexer2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMIndexer2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("B70F1E42-6255-4df0-A6B9-02B212D9E2BB") + IWMIndexer2 : public IWMIndexer + { + public: + virtual HRESULT STDMETHODCALLTYPE Configure( + /* [in] */ WORD wStreamNum, + /* [in] */ WMT_INDEXER_TYPE nIndexerType, + /* [in] */ void *pvInterval, + /* [in] */ void *pvIndexType) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMIndexer2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMIndexer2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMIndexer2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMIndexer2 * This); + + HRESULT ( STDMETHODCALLTYPE *StartIndexing )( + IWMIndexer2 * This, + /* [in] */ const WCHAR *pwszURL, + /* [in] */ IWMStatusCallback *pCallback, + /* [in] */ void *pvContext); + + HRESULT ( STDMETHODCALLTYPE *Cancel )( + IWMIndexer2 * This); + + HRESULT ( STDMETHODCALLTYPE *Configure )( + IWMIndexer2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WMT_INDEXER_TYPE nIndexerType, + /* [in] */ void *pvInterval, + /* [in] */ void *pvIndexType); + + END_INTERFACE + } IWMIndexer2Vtbl; + + interface IWMIndexer2 + { + CONST_VTBL struct IWMIndexer2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMIndexer2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMIndexer2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMIndexer2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMIndexer2_StartIndexing(This,pwszURL,pCallback,pvContext) \ + (This)->lpVtbl -> StartIndexing(This,pwszURL,pCallback,pvContext) + +#define IWMIndexer2_Cancel(This) \ + (This)->lpVtbl -> Cancel(This) + + +#define IWMIndexer2_Configure(This,wStreamNum,nIndexerType,pvInterval,pvIndexType) \ + (This)->lpVtbl -> Configure(This,wStreamNum,nIndexerType,pvInterval,pvIndexType) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMIndexer2_Configure_Proxy( + IWMIndexer2 * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WMT_INDEXER_TYPE nIndexerType, + /* [in] */ void *pvInterval, + /* [in] */ void *pvIndexType); + + +void __RPC_STUB IWMIndexer2_Configure_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMIndexer2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMLicenseBackup_INTERFACE_DEFINED__ +#define __IWMLicenseBackup_INTERFACE_DEFINED__ + +/* interface IWMLicenseBackup */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMLicenseBackup; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("05E5AC9F-3FB6-4508-BB43-A4067BA1EBE8") + IWMLicenseBackup : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE BackupLicenses( + /* [in] */ DWORD dwFlags, + /* [in] */ IWMStatusCallback *pCallback) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelLicenseBackup( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMLicenseBackupVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMLicenseBackup * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMLicenseBackup * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMLicenseBackup * This); + + HRESULT ( STDMETHODCALLTYPE *BackupLicenses )( + IWMLicenseBackup * This, + /* [in] */ DWORD dwFlags, + /* [in] */ IWMStatusCallback *pCallback); + + HRESULT ( STDMETHODCALLTYPE *CancelLicenseBackup )( + IWMLicenseBackup * This); + + END_INTERFACE + } IWMLicenseBackupVtbl; + + interface IWMLicenseBackup + { + CONST_VTBL struct IWMLicenseBackupVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMLicenseBackup_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMLicenseBackup_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMLicenseBackup_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMLicenseBackup_BackupLicenses(This,dwFlags,pCallback) \ + (This)->lpVtbl -> BackupLicenses(This,dwFlags,pCallback) + +#define IWMLicenseBackup_CancelLicenseBackup(This) \ + (This)->lpVtbl -> CancelLicenseBackup(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMLicenseBackup_BackupLicenses_Proxy( + IWMLicenseBackup * This, + /* [in] */ DWORD dwFlags, + /* [in] */ IWMStatusCallback *pCallback); + + +void __RPC_STUB IWMLicenseBackup_BackupLicenses_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMLicenseBackup_CancelLicenseBackup_Proxy( + IWMLicenseBackup * This); + + +void __RPC_STUB IWMLicenseBackup_CancelLicenseBackup_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMLicenseBackup_INTERFACE_DEFINED__ */ + + +#ifndef __IWMLicenseRestore_INTERFACE_DEFINED__ +#define __IWMLicenseRestore_INTERFACE_DEFINED__ + +/* interface IWMLicenseRestore */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMLicenseRestore; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("C70B6334-A22E-4efb-A245-15E65A004A13") + IWMLicenseRestore : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE RestoreLicenses( + /* [in] */ DWORD dwFlags, + /* [in] */ IWMStatusCallback *pCallback) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelLicenseRestore( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMLicenseRestoreVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMLicenseRestore * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMLicenseRestore * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMLicenseRestore * This); + + HRESULT ( STDMETHODCALLTYPE *RestoreLicenses )( + IWMLicenseRestore * This, + /* [in] */ DWORD dwFlags, + /* [in] */ IWMStatusCallback *pCallback); + + HRESULT ( STDMETHODCALLTYPE *CancelLicenseRestore )( + IWMLicenseRestore * This); + + END_INTERFACE + } IWMLicenseRestoreVtbl; + + interface IWMLicenseRestore + { + CONST_VTBL struct IWMLicenseRestoreVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMLicenseRestore_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMLicenseRestore_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMLicenseRestore_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMLicenseRestore_RestoreLicenses(This,dwFlags,pCallback) \ + (This)->lpVtbl -> RestoreLicenses(This,dwFlags,pCallback) + +#define IWMLicenseRestore_CancelLicenseRestore(This) \ + (This)->lpVtbl -> CancelLicenseRestore(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMLicenseRestore_RestoreLicenses_Proxy( + IWMLicenseRestore * This, + /* [in] */ DWORD dwFlags, + /* [in] */ IWMStatusCallback *pCallback); + + +void __RPC_STUB IWMLicenseRestore_RestoreLicenses_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMLicenseRestore_CancelLicenseRestore_Proxy( + IWMLicenseRestore * This); + + +void __RPC_STUB IWMLicenseRestore_CancelLicenseRestore_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMLicenseRestore_INTERFACE_DEFINED__ */ + + +#ifndef __IWMBackupRestoreProps_INTERFACE_DEFINED__ +#define __IWMBackupRestoreProps_INTERFACE_DEFINED__ + +/* interface IWMBackupRestoreProps */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMBackupRestoreProps; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("3C8E0DA6-996F-4ff3-A1AF-4838F9377E2E") + IWMBackupRestoreProps : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetPropCount( + /* [out] */ WORD *pcProps) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropByIndex( + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropByName( + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetProp( + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveProp( + /* [in] */ LPCWSTR pcwszName) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveAllProps( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMBackupRestorePropsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMBackupRestoreProps * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMBackupRestoreProps * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMBackupRestoreProps * This); + + HRESULT ( STDMETHODCALLTYPE *GetPropCount )( + IWMBackupRestoreProps * This, + /* [out] */ WORD *pcProps); + + HRESULT ( STDMETHODCALLTYPE *GetPropByIndex )( + IWMBackupRestoreProps * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *GetPropByName )( + IWMBackupRestoreProps * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + HRESULT ( STDMETHODCALLTYPE *SetProp )( + IWMBackupRestoreProps * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE *RemoveProp )( + IWMBackupRestoreProps * This, + /* [in] */ LPCWSTR pcwszName); + + HRESULT ( STDMETHODCALLTYPE *RemoveAllProps )( + IWMBackupRestoreProps * This); + + END_INTERFACE + } IWMBackupRestorePropsVtbl; + + interface IWMBackupRestoreProps + { + CONST_VTBL struct IWMBackupRestorePropsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMBackupRestoreProps_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMBackupRestoreProps_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMBackupRestoreProps_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMBackupRestoreProps_GetPropCount(This,pcProps) \ + (This)->lpVtbl -> GetPropCount(This,pcProps) + +#define IWMBackupRestoreProps_GetPropByIndex(This,wIndex,pwszName,pcchNameLen,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetPropByIndex(This,wIndex,pwszName,pcchNameLen,pType,pValue,pcbLength) + +#define IWMBackupRestoreProps_GetPropByName(This,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetPropByName(This,pszName,pType,pValue,pcbLength) + +#define IWMBackupRestoreProps_SetProp(This,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetProp(This,pszName,Type,pValue,cbLength) + +#define IWMBackupRestoreProps_RemoveProp(This,pcwszName) \ + (This)->lpVtbl -> RemoveProp(This,pcwszName) + +#define IWMBackupRestoreProps_RemoveAllProps(This) \ + (This)->lpVtbl -> RemoveAllProps(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMBackupRestoreProps_GetPropCount_Proxy( + IWMBackupRestoreProps * This, + /* [out] */ WORD *pcProps); + + +void __RPC_STUB IWMBackupRestoreProps_GetPropCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBackupRestoreProps_GetPropByIndex_Proxy( + IWMBackupRestoreProps * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszName, + /* [out][in] */ WORD *pcchNameLen, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMBackupRestoreProps_GetPropByIndex_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBackupRestoreProps_GetPropByName_Proxy( + IWMBackupRestoreProps * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ WORD *pcbLength); + + +void __RPC_STUB IWMBackupRestoreProps_GetPropByName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBackupRestoreProps_SetProp_Proxy( + IWMBackupRestoreProps * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMBackupRestoreProps_SetProp_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBackupRestoreProps_RemoveProp_Proxy( + IWMBackupRestoreProps * This, + /* [in] */ LPCWSTR pcwszName); + + +void __RPC_STUB IWMBackupRestoreProps_RemoveProp_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMBackupRestoreProps_RemoveAllProps_Proxy( + IWMBackupRestoreProps * This); + + +void __RPC_STUB IWMBackupRestoreProps_RemoveAllProps_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMBackupRestoreProps_INTERFACE_DEFINED__ */ + + +#ifndef __IWMCodecInfo_INTERFACE_DEFINED__ +#define __IWMCodecInfo_INTERFACE_DEFINED__ + +/* interface IWMCodecInfo */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMCodecInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("A970F41E-34DE-4a98-B3BA-E4B3CA7528F0") + IWMCodecInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetCodecInfoCount( + /* [in] */ REFGUID guidType, + /* [out] */ DWORD *pcCodecs) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCodecFormatCount( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [out] */ DWORD *pcFormat) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCodecFormat( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMCodecInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMCodecInfo * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMCodecInfo * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMCodecInfo * This); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfoCount )( + IWMCodecInfo * This, + /* [in] */ REFGUID guidType, + /* [out] */ DWORD *pcCodecs); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormatCount )( + IWMCodecInfo * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [out] */ DWORD *pcFormat); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormat )( + IWMCodecInfo * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig); + + END_INTERFACE + } IWMCodecInfoVtbl; + + interface IWMCodecInfo + { + CONST_VTBL struct IWMCodecInfoVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMCodecInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMCodecInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMCodecInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMCodecInfo_GetCodecInfoCount(This,guidType,pcCodecs) \ + (This)->lpVtbl -> GetCodecInfoCount(This,guidType,pcCodecs) + +#define IWMCodecInfo_GetCodecFormatCount(This,guidType,dwCodecIndex,pcFormat) \ + (This)->lpVtbl -> GetCodecFormatCount(This,guidType,dwCodecIndex,pcFormat) + +#define IWMCodecInfo_GetCodecFormat(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig) \ + (This)->lpVtbl -> GetCodecFormat(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo_GetCodecInfoCount_Proxy( + IWMCodecInfo * This, + /* [in] */ REFGUID guidType, + /* [out] */ DWORD *pcCodecs); + + +void __RPC_STUB IWMCodecInfo_GetCodecInfoCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo_GetCodecFormatCount_Proxy( + IWMCodecInfo * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [out] */ DWORD *pcFormat); + + +void __RPC_STUB IWMCodecInfo_GetCodecFormatCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo_GetCodecFormat_Proxy( + IWMCodecInfo * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig); + + +void __RPC_STUB IWMCodecInfo_GetCodecFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMCodecInfo_INTERFACE_DEFINED__ */ + + +#ifndef __IWMCodecInfo2_INTERFACE_DEFINED__ +#define __IWMCodecInfo2_INTERFACE_DEFINED__ + +/* interface IWMCodecInfo2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMCodecInfo2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("AA65E273-B686-4056-91EC-DD768D4DF710") + IWMCodecInfo2 : public IWMCodecInfo + { + public: + virtual HRESULT STDMETHODCALLTYPE GetCodecName( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [size_is][out] */ WCHAR *wszName, + /* [out][in] */ DWORD *pcchName) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCodecFormatDesc( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig, + /* [size_is][out] */ WCHAR *wszDesc, + /* [out][in] */ DWORD *pcchDesc) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMCodecInfo2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMCodecInfo2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMCodecInfo2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMCodecInfo2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfoCount )( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [out] */ DWORD *pcCodecs); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormatCount )( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [out] */ DWORD *pcFormat); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormat )( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig); + + HRESULT ( STDMETHODCALLTYPE *GetCodecName )( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [size_is][out] */ WCHAR *wszName, + /* [out][in] */ DWORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormatDesc )( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig, + /* [size_is][out] */ WCHAR *wszDesc, + /* [out][in] */ DWORD *pcchDesc); + + END_INTERFACE + } IWMCodecInfo2Vtbl; + + interface IWMCodecInfo2 + { + CONST_VTBL struct IWMCodecInfo2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMCodecInfo2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMCodecInfo2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMCodecInfo2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMCodecInfo2_GetCodecInfoCount(This,guidType,pcCodecs) \ + (This)->lpVtbl -> GetCodecInfoCount(This,guidType,pcCodecs) + +#define IWMCodecInfo2_GetCodecFormatCount(This,guidType,dwCodecIndex,pcFormat) \ + (This)->lpVtbl -> GetCodecFormatCount(This,guidType,dwCodecIndex,pcFormat) + +#define IWMCodecInfo2_GetCodecFormat(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig) \ + (This)->lpVtbl -> GetCodecFormat(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig) + + +#define IWMCodecInfo2_GetCodecName(This,guidType,dwCodecIndex,wszName,pcchName) \ + (This)->lpVtbl -> GetCodecName(This,guidType,dwCodecIndex,wszName,pcchName) + +#define IWMCodecInfo2_GetCodecFormatDesc(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig,wszDesc,pcchDesc) \ + (This)->lpVtbl -> GetCodecFormatDesc(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig,wszDesc,pcchDesc) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo2_GetCodecName_Proxy( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [size_is][out] */ WCHAR *wszName, + /* [out][in] */ DWORD *pcchName); + + +void __RPC_STUB IWMCodecInfo2_GetCodecName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo2_GetCodecFormatDesc_Proxy( + IWMCodecInfo2 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig, + /* [size_is][out] */ WCHAR *wszDesc, + /* [out][in] */ DWORD *pcchDesc); + + +void __RPC_STUB IWMCodecInfo2_GetCodecFormatDesc_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMCodecInfo2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMCodecInfo3_INTERFACE_DEFINED__ +#define __IWMCodecInfo3_INTERFACE_DEFINED__ + +/* interface IWMCodecInfo3 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMCodecInfo3; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("7e51f487-4d93-4f98-8ab4-27d0565adc51") + IWMCodecInfo3 : public IWMCodecInfo2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetCodecFormatProp( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCodecProp( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetCodecEnumerationSetting( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCodecEnumerationSetting( + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMCodecInfo3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMCodecInfo3 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMCodecInfo3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMCodecInfo3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInfoCount )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [out] */ DWORD *pcCodecs); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormatCount )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [out] */ DWORD *pcFormat); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormat )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig); + + HRESULT ( STDMETHODCALLTYPE *GetCodecName )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [size_is][out] */ WCHAR *wszName, + /* [out][in] */ DWORD *pcchName); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormatDesc )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [out] */ IWMStreamConfig **ppIStreamConfig, + /* [size_is][out] */ WCHAR *wszDesc, + /* [out][in] */ DWORD *pcchDesc); + + HRESULT ( STDMETHODCALLTYPE *GetCodecFormatProp )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + HRESULT ( STDMETHODCALLTYPE *GetCodecProp )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + HRESULT ( STDMETHODCALLTYPE *SetCodecEnumerationSetting )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwSize); + + HRESULT ( STDMETHODCALLTYPE *GetCodecEnumerationSetting )( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + END_INTERFACE + } IWMCodecInfo3Vtbl; + + interface IWMCodecInfo3 + { + CONST_VTBL struct IWMCodecInfo3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMCodecInfo3_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMCodecInfo3_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMCodecInfo3_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMCodecInfo3_GetCodecInfoCount(This,guidType,pcCodecs) \ + (This)->lpVtbl -> GetCodecInfoCount(This,guidType,pcCodecs) + +#define IWMCodecInfo3_GetCodecFormatCount(This,guidType,dwCodecIndex,pcFormat) \ + (This)->lpVtbl -> GetCodecFormatCount(This,guidType,dwCodecIndex,pcFormat) + +#define IWMCodecInfo3_GetCodecFormat(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig) \ + (This)->lpVtbl -> GetCodecFormat(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig) + + +#define IWMCodecInfo3_GetCodecName(This,guidType,dwCodecIndex,wszName,pcchName) \ + (This)->lpVtbl -> GetCodecName(This,guidType,dwCodecIndex,wszName,pcchName) + +#define IWMCodecInfo3_GetCodecFormatDesc(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig,wszDesc,pcchDesc) \ + (This)->lpVtbl -> GetCodecFormatDesc(This,guidType,dwCodecIndex,dwFormatIndex,ppIStreamConfig,wszDesc,pcchDesc) + + +#define IWMCodecInfo3_GetCodecFormatProp(This,guidType,dwCodecIndex,dwFormatIndex,pszName,pType,pValue,pdwSize) \ + (This)->lpVtbl -> GetCodecFormatProp(This,guidType,dwCodecIndex,dwFormatIndex,pszName,pType,pValue,pdwSize) + +#define IWMCodecInfo3_GetCodecProp(This,guidType,dwCodecIndex,pszName,pType,pValue,pdwSize) \ + (This)->lpVtbl -> GetCodecProp(This,guidType,dwCodecIndex,pszName,pType,pValue,pdwSize) + +#define IWMCodecInfo3_SetCodecEnumerationSetting(This,guidType,dwCodecIndex,pszName,Type,pValue,dwSize) \ + (This)->lpVtbl -> SetCodecEnumerationSetting(This,guidType,dwCodecIndex,pszName,Type,pValue,dwSize) + +#define IWMCodecInfo3_GetCodecEnumerationSetting(This,guidType,dwCodecIndex,pszName,pType,pValue,pdwSize) \ + (This)->lpVtbl -> GetCodecEnumerationSetting(This,guidType,dwCodecIndex,pszName,pType,pValue,pdwSize) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo3_GetCodecFormatProp_Proxy( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ DWORD dwFormatIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + +void __RPC_STUB IWMCodecInfo3_GetCodecFormatProp_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo3_GetCodecProp_Proxy( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + +void __RPC_STUB IWMCodecInfo3_GetCodecProp_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo3_SetCodecEnumerationSetting_Proxy( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [size_is][in] */ const BYTE *pValue, + /* [in] */ DWORD dwSize); + + +void __RPC_STUB IWMCodecInfo3_SetCodecEnumerationSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMCodecInfo3_GetCodecEnumerationSetting_Proxy( + IWMCodecInfo3 * This, + /* [in] */ REFGUID guidType, + /* [in] */ DWORD dwCodecIndex, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE *pType, + /* [size_is][out] */ BYTE *pValue, + /* [out][in] */ DWORD *pdwSize); + + +void __RPC_STUB IWMCodecInfo3_GetCodecEnumerationSetting_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMCodecInfo3_INTERFACE_DEFINED__ */ + + +#ifndef __IWMLanguageList_INTERFACE_DEFINED__ +#define __IWMLanguageList_INTERFACE_DEFINED__ + +/* interface IWMLanguageList */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMLanguageList; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("DF683F00-2D49-4d8e-92B7-FB19F6A0DC57") + IWMLanguageList : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetLanguageCount( + /* [out] */ WORD *pwCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLanguageDetails( + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddLanguageByRFC1766String( + /* [in] */ LPWSTR pwszLanguageString, + /* [out] */ WORD *pwIndex) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMLanguageListVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMLanguageList * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMLanguageList * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMLanguageList * This); + + HRESULT ( STDMETHODCALLTYPE *GetLanguageCount )( + IWMLanguageList * This, + /* [out] */ WORD *pwCount); + + HRESULT ( STDMETHODCALLTYPE *GetLanguageDetails )( + IWMLanguageList * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength); + + HRESULT ( STDMETHODCALLTYPE *AddLanguageByRFC1766String )( + IWMLanguageList * This, + /* [in] */ LPWSTR pwszLanguageString, + /* [out] */ WORD *pwIndex); + + END_INTERFACE + } IWMLanguageListVtbl; + + interface IWMLanguageList + { + CONST_VTBL struct IWMLanguageListVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMLanguageList_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMLanguageList_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMLanguageList_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMLanguageList_GetLanguageCount(This,pwCount) \ + (This)->lpVtbl -> GetLanguageCount(This,pwCount) + +#define IWMLanguageList_GetLanguageDetails(This,wIndex,pwszLanguageString,pcchLanguageStringLength) \ + (This)->lpVtbl -> GetLanguageDetails(This,wIndex,pwszLanguageString,pcchLanguageStringLength) + +#define IWMLanguageList_AddLanguageByRFC1766String(This,pwszLanguageString,pwIndex) \ + (This)->lpVtbl -> AddLanguageByRFC1766String(This,pwszLanguageString,pwIndex) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMLanguageList_GetLanguageCount_Proxy( + IWMLanguageList * This, + /* [out] */ WORD *pwCount); + + +void __RPC_STUB IWMLanguageList_GetLanguageCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMLanguageList_GetLanguageDetails_Proxy( + IWMLanguageList * This, + /* [in] */ WORD wIndex, + /* [size_is][out] */ WCHAR *pwszLanguageString, + /* [out][in] */ WORD *pcchLanguageStringLength); + + +void __RPC_STUB IWMLanguageList_GetLanguageDetails_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMLanguageList_AddLanguageByRFC1766String_Proxy( + IWMLanguageList * This, + /* [in] */ LPWSTR pwszLanguageString, + /* [out] */ WORD *pwIndex); + + +void __RPC_STUB IWMLanguageList_AddLanguageByRFC1766String_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMLanguageList_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWriterPushSink_INTERFACE_DEFINED__ +#define __IWMWriterPushSink_INTERFACE_DEFINED__ + +/* interface IWMWriterPushSink */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWriterPushSink; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("dc10e6a5-072c-467d-bf57-6330a9dde12a") + IWMWriterPushSink : public IWMWriterSink + { + public: + virtual HRESULT STDMETHODCALLTYPE Connect( + /* [in] */ LPCWSTR pwszURL, + /* [in] */ LPCWSTR pwszTemplateURL, + /* [in] */ BOOL fAutoDestroy) = 0; + + virtual HRESULT STDMETHODCALLTYPE Disconnect( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE EndSession( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWriterPushSinkVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWriterPushSink * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWriterPushSink * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWriterPushSink * This); + + HRESULT ( STDMETHODCALLTYPE *OnHeader )( + IWMWriterPushSink * This, + /* [in] */ INSSBuffer *pHeader); + + HRESULT ( STDMETHODCALLTYPE *IsRealTime )( + IWMWriterPushSink * This, + /* [out] */ BOOL *pfRealTime); + + HRESULT ( STDMETHODCALLTYPE *AllocateDataUnit )( + IWMWriterPushSink * This, + /* [in] */ DWORD cbDataUnit, + /* [out] */ INSSBuffer **ppDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnDataUnit )( + IWMWriterPushSink * This, + /* [in] */ INSSBuffer *pDataUnit); + + HRESULT ( STDMETHODCALLTYPE *OnEndWriting )( + IWMWriterPushSink * This); + + HRESULT ( STDMETHODCALLTYPE *Connect )( + IWMWriterPushSink * This, + /* [in] */ LPCWSTR pwszURL, + /* [in] */ LPCWSTR pwszTemplateURL, + /* [in] */ BOOL fAutoDestroy); + + HRESULT ( STDMETHODCALLTYPE *Disconnect )( + IWMWriterPushSink * This); + + HRESULT ( STDMETHODCALLTYPE *EndSession )( + IWMWriterPushSink * This); + + END_INTERFACE + } IWMWriterPushSinkVtbl; + + interface IWMWriterPushSink + { + CONST_VTBL struct IWMWriterPushSinkVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWriterPushSink_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWriterPushSink_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWriterPushSink_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWriterPushSink_OnHeader(This,pHeader) \ + (This)->lpVtbl -> OnHeader(This,pHeader) + +#define IWMWriterPushSink_IsRealTime(This,pfRealTime) \ + (This)->lpVtbl -> IsRealTime(This,pfRealTime) + +#define IWMWriterPushSink_AllocateDataUnit(This,cbDataUnit,ppDataUnit) \ + (This)->lpVtbl -> AllocateDataUnit(This,cbDataUnit,ppDataUnit) + +#define IWMWriterPushSink_OnDataUnit(This,pDataUnit) \ + (This)->lpVtbl -> OnDataUnit(This,pDataUnit) + +#define IWMWriterPushSink_OnEndWriting(This) \ + (This)->lpVtbl -> OnEndWriting(This) + + +#define IWMWriterPushSink_Connect(This,pwszURL,pwszTemplateURL,fAutoDestroy) \ + (This)->lpVtbl -> Connect(This,pwszURL,pwszTemplateURL,fAutoDestroy) + +#define IWMWriterPushSink_Disconnect(This) \ + (This)->lpVtbl -> Disconnect(This) + +#define IWMWriterPushSink_EndSession(This) \ + (This)->lpVtbl -> EndSession(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWriterPushSink_Connect_Proxy( + IWMWriterPushSink * This, + /* [in] */ LPCWSTR pwszURL, + /* [in] */ LPCWSTR pwszTemplateURL, + /* [in] */ BOOL fAutoDestroy); + + +void __RPC_STUB IWMWriterPushSink_Connect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPushSink_Disconnect_Proxy( + IWMWriterPushSink * This); + + +void __RPC_STUB IWMWriterPushSink_Disconnect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWriterPushSink_EndSession_Proxy( + IWMWriterPushSink * This); + + +void __RPC_STUB IWMWriterPushSink_EndSession_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWriterPushSink_INTERFACE_DEFINED__ */ + + +#ifndef __IWMWatermarkInfo_INTERFACE_DEFINED__ +#define __IWMWatermarkInfo_INTERFACE_DEFINED__ + +/* interface IWMWatermarkInfo */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMWatermarkInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("6f497062-f2e2-4624-8ea7-9dd40d81fc8d") + IWMWatermarkInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetWatermarkEntryCount( + /* [in] */ WMT_WATERMARK_ENTRY_TYPE wmetType, + /* [out] */ DWORD *pdwCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetWatermarkEntry( + /* [in] */ WMT_WATERMARK_ENTRY_TYPE wmetType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WMT_WATERMARK_ENTRY *pEntry) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMWatermarkInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMWatermarkInfo * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMWatermarkInfo * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMWatermarkInfo * This); + + HRESULT ( STDMETHODCALLTYPE *GetWatermarkEntryCount )( + IWMWatermarkInfo * This, + /* [in] */ WMT_WATERMARK_ENTRY_TYPE wmetType, + /* [out] */ DWORD *pdwCount); + + HRESULT ( STDMETHODCALLTYPE *GetWatermarkEntry )( + IWMWatermarkInfo * This, + /* [in] */ WMT_WATERMARK_ENTRY_TYPE wmetType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WMT_WATERMARK_ENTRY *pEntry); + + END_INTERFACE + } IWMWatermarkInfoVtbl; + + interface IWMWatermarkInfo + { + CONST_VTBL struct IWMWatermarkInfoVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMWatermarkInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMWatermarkInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMWatermarkInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMWatermarkInfo_GetWatermarkEntryCount(This,wmetType,pdwCount) \ + (This)->lpVtbl -> GetWatermarkEntryCount(This,wmetType,pdwCount) + +#define IWMWatermarkInfo_GetWatermarkEntry(This,wmetType,dwEntryNum,pEntry) \ + (This)->lpVtbl -> GetWatermarkEntry(This,wmetType,dwEntryNum,pEntry) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMWatermarkInfo_GetWatermarkEntryCount_Proxy( + IWMWatermarkInfo * This, + /* [in] */ WMT_WATERMARK_ENTRY_TYPE wmetType, + /* [out] */ DWORD *pdwCount); + + +void __RPC_STUB IWMWatermarkInfo_GetWatermarkEntryCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMWatermarkInfo_GetWatermarkEntry_Proxy( + IWMWatermarkInfo * This, + /* [in] */ WMT_WATERMARK_ENTRY_TYPE wmetType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WMT_WATERMARK_ENTRY *pEntry); + + +void __RPC_STUB IWMWatermarkInfo_GetWatermarkEntry_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMWatermarkInfo_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderAccelerator_INTERFACE_DEFINED__ +#define __IWMReaderAccelerator_INTERFACE_DEFINED__ + +/* interface IWMReaderAccelerator */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderAccelerator; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("BDDC4D08-944D-4d52-A612-46C3FDA07DD4") + IWMReaderAccelerator : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetCodecInterface( + /* [in] */ DWORD dwOutputNum, + /* [in] */ REFIID riid, + /* [out] */ void **ppvCodecInterface) = 0; + + virtual HRESULT STDMETHODCALLTYPE Notify( + /* [in] */ DWORD dwOutputNum, + /* [in] */ WM_MEDIA_TYPE *pSubtype) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderAcceleratorVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderAccelerator * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderAccelerator * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderAccelerator * This); + + HRESULT ( STDMETHODCALLTYPE *GetCodecInterface )( + IWMReaderAccelerator * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ REFIID riid, + /* [out] */ void **ppvCodecInterface); + + HRESULT ( STDMETHODCALLTYPE *Notify )( + IWMReaderAccelerator * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ WM_MEDIA_TYPE *pSubtype); + + END_INTERFACE + } IWMReaderAcceleratorVtbl; + + interface IWMReaderAccelerator + { + CONST_VTBL struct IWMReaderAcceleratorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderAccelerator_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderAccelerator_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderAccelerator_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderAccelerator_GetCodecInterface(This,dwOutputNum,riid,ppvCodecInterface) \ + (This)->lpVtbl -> GetCodecInterface(This,dwOutputNum,riid,ppvCodecInterface) + +#define IWMReaderAccelerator_Notify(This,dwOutputNum,pSubtype) \ + (This)->lpVtbl -> Notify(This,dwOutputNum,pSubtype) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderAccelerator_GetCodecInterface_Proxy( + IWMReaderAccelerator * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ REFIID riid, + /* [out] */ void **ppvCodecInterface); + + +void __RPC_STUB IWMReaderAccelerator_GetCodecInterface_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderAccelerator_Notify_Proxy( + IWMReaderAccelerator * This, + /* [in] */ DWORD dwOutputNum, + /* [in] */ WM_MEDIA_TYPE *pSubtype); + + +void __RPC_STUB IWMReaderAccelerator_Notify_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderAccelerator_INTERFACE_DEFINED__ */ + + +#ifndef __IWMReaderTimecode_INTERFACE_DEFINED__ +#define __IWMReaderTimecode_INTERFACE_DEFINED__ + +/* interface IWMReaderTimecode */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMReaderTimecode; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("f369e2f0-e081-4fe6-8450-b810b2f410d1") + IWMReaderTimecode : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetTimecodeRangeCount( + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pwRangeCount) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetTimecodeRangeBounds( + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wRangeNum, + /* [out] */ DWORD *pStartTimecode, + /* [out] */ DWORD *pEndTimecode) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMReaderTimecodeVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMReaderTimecode * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMReaderTimecode * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMReaderTimecode * This); + + HRESULT ( STDMETHODCALLTYPE *GetTimecodeRangeCount )( + IWMReaderTimecode * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pwRangeCount); + + HRESULT ( STDMETHODCALLTYPE *GetTimecodeRangeBounds )( + IWMReaderTimecode * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wRangeNum, + /* [out] */ DWORD *pStartTimecode, + /* [out] */ DWORD *pEndTimecode); + + END_INTERFACE + } IWMReaderTimecodeVtbl; + + interface IWMReaderTimecode + { + CONST_VTBL struct IWMReaderTimecodeVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMReaderTimecode_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMReaderTimecode_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMReaderTimecode_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMReaderTimecode_GetTimecodeRangeCount(This,wStreamNum,pwRangeCount) \ + (This)->lpVtbl -> GetTimecodeRangeCount(This,wStreamNum,pwRangeCount) + +#define IWMReaderTimecode_GetTimecodeRangeBounds(This,wStreamNum,wRangeNum,pStartTimecode,pEndTimecode) \ + (This)->lpVtbl -> GetTimecodeRangeBounds(This,wStreamNum,wRangeNum,pStartTimecode,pEndTimecode) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMReaderTimecode_GetTimecodeRangeCount_Proxy( + IWMReaderTimecode * This, + /* [in] */ WORD wStreamNum, + /* [out] */ WORD *pwRangeCount); + + +void __RPC_STUB IWMReaderTimecode_GetTimecodeRangeCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMReaderTimecode_GetTimecodeRangeBounds_Proxy( + IWMReaderTimecode * This, + /* [in] */ WORD wStreamNum, + /* [in] */ WORD wRangeNum, + /* [out] */ DWORD *pStartTimecode, + /* [out] */ DWORD *pEndTimecode); + + +void __RPC_STUB IWMReaderTimecode_GetTimecodeRangeBounds_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMReaderTimecode_INTERFACE_DEFINED__ */ + + +#ifndef __IWMAddressAccess_INTERFACE_DEFINED__ +#define __IWMAddressAccess_INTERFACE_DEFINED__ + +/* interface IWMAddressAccess */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMAddressAccess; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("BB3C6389-1633-4e92-AF14-9F3173BA39D0") + IWMAddressAccess : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetAccessEntryCount( + /* [in] */ WM_AETYPE aeType, + /* [out] */ DWORD *pcEntries) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAccessEntry( + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddAccessEntry( + /* [in] */ WM_AETYPE aeType, + /* [in] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveAccessEntry( + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMAddressAccessVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMAddressAccess * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMAddressAccess * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMAddressAccess * This); + + HRESULT ( STDMETHODCALLTYPE *GetAccessEntryCount )( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [out] */ DWORD *pcEntries); + + HRESULT ( STDMETHODCALLTYPE *GetAccessEntry )( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry); + + HRESULT ( STDMETHODCALLTYPE *AddAccessEntry )( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry); + + HRESULT ( STDMETHODCALLTYPE *RemoveAccessEntry )( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum); + + END_INTERFACE + } IWMAddressAccessVtbl; + + interface IWMAddressAccess + { + CONST_VTBL struct IWMAddressAccessVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMAddressAccess_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMAddressAccess_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMAddressAccess_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMAddressAccess_GetAccessEntryCount(This,aeType,pcEntries) \ + (This)->lpVtbl -> GetAccessEntryCount(This,aeType,pcEntries) + +#define IWMAddressAccess_GetAccessEntry(This,aeType,dwEntryNum,pAddrAccessEntry) \ + (This)->lpVtbl -> GetAccessEntry(This,aeType,dwEntryNum,pAddrAccessEntry) + +#define IWMAddressAccess_AddAccessEntry(This,aeType,pAddrAccessEntry) \ + (This)->lpVtbl -> AddAccessEntry(This,aeType,pAddrAccessEntry) + +#define IWMAddressAccess_RemoveAccessEntry(This,aeType,dwEntryNum) \ + (This)->lpVtbl -> RemoveAccessEntry(This,aeType,dwEntryNum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMAddressAccess_GetAccessEntryCount_Proxy( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [out] */ DWORD *pcEntries); + + +void __RPC_STUB IWMAddressAccess_GetAccessEntryCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAddressAccess_GetAccessEntry_Proxy( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry); + + +void __RPC_STUB IWMAddressAccess_GetAccessEntry_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAddressAccess_AddAccessEntry_Proxy( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry); + + +void __RPC_STUB IWMAddressAccess_AddAccessEntry_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAddressAccess_RemoveAccessEntry_Proxy( + IWMAddressAccess * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum); + + +void __RPC_STUB IWMAddressAccess_RemoveAccessEntry_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMAddressAccess_INTERFACE_DEFINED__ */ + + +#ifndef __IWMAddressAccess2_INTERFACE_DEFINED__ +#define __IWMAddressAccess2_INTERFACE_DEFINED__ + +/* interface IWMAddressAccess2 */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMAddressAccess2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("65a83fc2-3e98-4d4d-81b5-2a742886b33d") + IWMAddressAccess2 : public IWMAddressAccess + { + public: + virtual HRESULT STDMETHODCALLTYPE GetAccessEntryEx( + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ BSTR *pbstrAddress, + /* [out] */ BSTR *pbstrMask) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddAccessEntryEx( + /* [in] */ WM_AETYPE aeType, + /* [in] */ BSTR bstrAddress, + /* [in] */ BSTR bstrMask) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMAddressAccess2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMAddressAccess2 * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMAddressAccess2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMAddressAccess2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetAccessEntryCount )( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [out] */ DWORD *pcEntries); + + HRESULT ( STDMETHODCALLTYPE *GetAccessEntry )( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry); + + HRESULT ( STDMETHODCALLTYPE *AddAccessEntry )( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ WM_ADDRESS_ACCESSENTRY *pAddrAccessEntry); + + HRESULT ( STDMETHODCALLTYPE *RemoveAccessEntry )( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum); + + HRESULT ( STDMETHODCALLTYPE *GetAccessEntryEx )( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ BSTR *pbstrAddress, + /* [out] */ BSTR *pbstrMask); + + HRESULT ( STDMETHODCALLTYPE *AddAccessEntryEx )( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ BSTR bstrAddress, + /* [in] */ BSTR bstrMask); + + END_INTERFACE + } IWMAddressAccess2Vtbl; + + interface IWMAddressAccess2 + { + CONST_VTBL struct IWMAddressAccess2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMAddressAccess2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMAddressAccess2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMAddressAccess2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMAddressAccess2_GetAccessEntryCount(This,aeType,pcEntries) \ + (This)->lpVtbl -> GetAccessEntryCount(This,aeType,pcEntries) + +#define IWMAddressAccess2_GetAccessEntry(This,aeType,dwEntryNum,pAddrAccessEntry) \ + (This)->lpVtbl -> GetAccessEntry(This,aeType,dwEntryNum,pAddrAccessEntry) + +#define IWMAddressAccess2_AddAccessEntry(This,aeType,pAddrAccessEntry) \ + (This)->lpVtbl -> AddAccessEntry(This,aeType,pAddrAccessEntry) + +#define IWMAddressAccess2_RemoveAccessEntry(This,aeType,dwEntryNum) \ + (This)->lpVtbl -> RemoveAccessEntry(This,aeType,dwEntryNum) + + +#define IWMAddressAccess2_GetAccessEntryEx(This,aeType,dwEntryNum,pbstrAddress,pbstrMask) \ + (This)->lpVtbl -> GetAccessEntryEx(This,aeType,dwEntryNum,pbstrAddress,pbstrMask) + +#define IWMAddressAccess2_AddAccessEntryEx(This,aeType,bstrAddress,bstrMask) \ + (This)->lpVtbl -> AddAccessEntryEx(This,aeType,bstrAddress,bstrMask) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMAddressAccess2_GetAccessEntryEx_Proxy( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ DWORD dwEntryNum, + /* [out] */ BSTR *pbstrAddress, + /* [out] */ BSTR *pbstrMask); + + +void __RPC_STUB IWMAddressAccess2_GetAccessEntryEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAddressAccess2_AddAccessEntryEx_Proxy( + IWMAddressAccess2 * This, + /* [in] */ WM_AETYPE aeType, + /* [in] */ BSTR bstrAddress, + /* [in] */ BSTR bstrMask); + + +void __RPC_STUB IWMAddressAccess2_AddAccessEntryEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMAddressAccess2_INTERFACE_DEFINED__ */ + + +#ifndef __IWMImageInfo_INTERFACE_DEFINED__ +#define __IWMImageInfo_INTERFACE_DEFINED__ + +/* interface IWMImageInfo */ +/* [local][unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMImageInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9F0AA3B6-7267-4d89-88F2-BA915AA5C4C6") + IWMImageInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetImageCount( + /* [out] */ DWORD *pcImages) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetImage( + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchMIMEType, + /* [size_is][out] */ WCHAR *pwszMIMEType, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WORD *pImageType, + /* [out][in] */ DWORD *pcbImageData, + /* [size_is][out] */ BYTE *pbImageData) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMImageInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMImageInfo * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMImageInfo * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMImageInfo * This); + + HRESULT ( STDMETHODCALLTYPE *GetImageCount )( + IWMImageInfo * This, + /* [out] */ DWORD *pcImages); + + HRESULT ( STDMETHODCALLTYPE *GetImage )( + IWMImageInfo * This, + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchMIMEType, + /* [size_is][out] */ WCHAR *pwszMIMEType, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WORD *pImageType, + /* [out][in] */ DWORD *pcbImageData, + /* [size_is][out] */ BYTE *pbImageData); + + END_INTERFACE + } IWMImageInfoVtbl; + + interface IWMImageInfo + { + CONST_VTBL struct IWMImageInfoVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMImageInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMImageInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMImageInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMImageInfo_GetImageCount(This,pcImages) \ + (This)->lpVtbl -> GetImageCount(This,pcImages) + +#define IWMImageInfo_GetImage(This,wIndex,pcchMIMEType,pwszMIMEType,pcchDescription,pwszDescription,pImageType,pcbImageData,pbImageData) \ + (This)->lpVtbl -> GetImage(This,wIndex,pcchMIMEType,pwszMIMEType,pcchDescription,pwszDescription,pImageType,pcbImageData,pbImageData) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMImageInfo_GetImageCount_Proxy( + IWMImageInfo * This, + /* [out] */ DWORD *pcImages); + + +void __RPC_STUB IWMImageInfo_GetImageCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMImageInfo_GetImage_Proxy( + IWMImageInfo * This, + /* [in] */ DWORD wIndex, + /* [out][in] */ WORD *pcchMIMEType, + /* [size_is][out] */ WCHAR *pwszMIMEType, + /* [out][in] */ WORD *pcchDescription, + /* [size_is][out] */ WCHAR *pwszDescription, + /* [out] */ WORD *pImageType, + /* [out][in] */ DWORD *pcbImageData, + /* [size_is][out] */ BYTE *pbImageData); + + +void __RPC_STUB IWMImageInfo_GetImage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMImageInfo_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/win32/src/AFXRES.H b/win32/src/AFXRES.H new file mode 100755 index 0000000..6abc354 --- /dev/null +++ b/win32/src/AFXRES.H @@ -0,0 +1,774 @@ +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) 1992-1998 Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#ifndef __AFXRES_H__ +#define __AFXRES_H__ + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS +#define _INC_WINDOWS + #include "winres.h" // extract from windows header +#endif +#endif + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, off) +#endif + +#ifdef APSTUDIO_INVOKED +#define APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// MFC resource types (see Technical note TN024 for implementation details) + +#ifndef RC_INVOKED +#define RT_DLGINIT MAKEINTRESOURCE(240) +#define RT_TOOLBAR MAKEINTRESOURCE(241) +#endif + +///////////////////////////////////////////////////////////////////////////// + +#ifdef APSTUDIO_INVOKED +#undef APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// General style bits etc + +// ControlBar styles +#define CBRS_ALIGN_LEFT 0x1000L +#define CBRS_ALIGN_TOP 0x2000L +#define CBRS_ALIGN_RIGHT 0x4000L +#define CBRS_ALIGN_BOTTOM 0x8000L +#define CBRS_ALIGN_ANY 0xF000L + +#define CBRS_BORDER_LEFT 0x0100L +#define CBRS_BORDER_TOP 0x0200L +#define CBRS_BORDER_RIGHT 0x0400L +#define CBRS_BORDER_BOTTOM 0x0800L +#define CBRS_BORDER_ANY 0x0F00L + +#define CBRS_TOOLTIPS 0x0010L +#define CBRS_FLYBY 0x0020L +#define CBRS_FLOAT_MULTI 0x0040L +#define CBRS_BORDER_3D 0x0080L +#define CBRS_HIDE_INPLACE 0x0008L +#define CBRS_SIZE_DYNAMIC 0x0004L +#define CBRS_SIZE_FIXED 0x0002L +#define CBRS_FLOATING 0x0001L + +#define CBRS_GRIPPER 0x00400000L + +#define CBRS_ORIENT_HORZ (CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM) +#define CBRS_ORIENT_VERT (CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT) +#define CBRS_ORIENT_ANY (CBRS_ORIENT_HORZ|CBRS_ORIENT_VERT) + +#define CBRS_ALL 0x0040FFFFL + +// the CBRS_ style is made up of an alignment style and a draw border style +// the alignment styles are mutually exclusive +// the draw border styles may be combined +#define CBRS_NOALIGN 0x00000000L +#define CBRS_LEFT (CBRS_ALIGN_LEFT|CBRS_BORDER_RIGHT) +#define CBRS_TOP (CBRS_ALIGN_TOP|CBRS_BORDER_BOTTOM) +#define CBRS_RIGHT (CBRS_ALIGN_RIGHT|CBRS_BORDER_LEFT) +#define CBRS_BOTTOM (CBRS_ALIGN_BOTTOM|CBRS_BORDER_TOP) + +///////////////////////////////////////////////////////////////////////////// +// Standard window components + +// Mode indicators in status bar - these are routed like commands +#define ID_INDICATOR_EXT 0xE700 // extended selection indicator +#define ID_INDICATOR_CAPS 0xE701 // cap lock indicator +#define ID_INDICATOR_NUM 0xE702 // num lock indicator +#define ID_INDICATOR_SCRL 0xE703 // scroll lock indicator +#define ID_INDICATOR_OVR 0xE704 // overtype mode indicator +#define ID_INDICATOR_REC 0xE705 // record mode indicator +#define ID_INDICATOR_KANA 0xE706 // kana lock indicator + +#define ID_SEPARATOR 0 // special separator value + +#ifndef RC_INVOKED // code only +// Standard control bars (IDW = window ID) +#define AFX_IDW_CONTROLBAR_FIRST 0xE800 +#define AFX_IDW_CONTROLBAR_LAST 0xE8FF + +#define AFX_IDW_TOOLBAR 0xE800 // main Toolbar for window +#define AFX_IDW_STATUS_BAR 0xE801 // Status bar window +#define AFX_IDW_PREVIEW_BAR 0xE802 // PrintPreview Dialog Bar +#define AFX_IDW_RESIZE_BAR 0xE803 // OLE in-place resize bar +#define AFX_IDW_REBAR 0xE804 // COMCTL32 "rebar" Bar +#define AFX_IDW_DIALOGBAR 0xE805 // CDialogBar + +// Note: If your application supports docking toolbars, you should +// not use the following IDs for your own toolbars. The IDs chosen +// are at the top of the first 32 such that the bars will be hidden +// while in print preview mode, and are not likely to conflict with +// IDs your application may have used succesfully in the past. + +#define AFX_IDW_DOCKBAR_TOP 0xE81B +#define AFX_IDW_DOCKBAR_LEFT 0xE81C +#define AFX_IDW_DOCKBAR_RIGHT 0xE81D +#define AFX_IDW_DOCKBAR_BOTTOM 0xE81E +#define AFX_IDW_DOCKBAR_FLOAT 0xE81F + +// Macro for mapping standard control bars to bitmask (limit of 32) +#define AFX_CONTROLBAR_MASK(nIDC) (1L << (nIDC - AFX_IDW_CONTROLBAR_FIRST)) + +// parts of Main Frame +#define AFX_IDW_PANE_FIRST 0xE900 // first pane (256 max) +#define AFX_IDW_PANE_LAST 0xE9ff +#define AFX_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) +#define AFX_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) + +#define AFX_IDW_SIZE_BOX 0xEA20 // size box for splitters +#define AFX_IDW_PANE_SAVE 0xEA21 // to shift AFX_IDW_PANE_FIRST +#endif //!RC_INVOKED + +#ifndef APSTUDIO_INVOKED + +// common style for form views +#define AFX_WS_DEFAULT_VIEW (WS_CHILD | WS_VISIBLE | WS_BORDER) + +#endif //!APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// Standard app configurable strings + +// for application title (defaults to EXE name or name in constructor) +#define AFX_IDS_APP_TITLE 0xE000 +// idle message bar line +#define AFX_IDS_IDLEMESSAGE 0xE001 +// message bar line when in shift-F1 help mode +#define AFX_IDS_HELPMODEMESSAGE 0xE002 +// document title when editing OLE embedding +#define AFX_IDS_APP_TITLE_EMBEDDING 0xE003 +// company name +#define AFX_IDS_COMPANY_NAME 0xE004 +// object name when server is inplace +#define AFX_IDS_OBJ_TITLE_INPLACE 0xE005 + +///////////////////////////////////////////////////////////////////////////// +// Standard Commands + +// File commands +#define ID_FILE_NEW 0xE100 +#define ID_FILE_OPEN 0xE101 +#define ID_FILE_CLOSE 0xE102 +#define ID_FILE_SAVE 0xE103 +#define ID_FILE_SAVE_AS 0xE104 +#define ID_FILE_PAGE_SETUP 0xE105 +#define ID_FILE_PRINT_SETUP 0xE106 +#define ID_FILE_PRINT 0xE107 +#define ID_FILE_PRINT_DIRECT 0xE108 +#define ID_FILE_PRINT_PREVIEW 0xE109 +#define ID_FILE_UPDATE 0xE10A +#define ID_FILE_SAVE_COPY_AS 0xE10B +#define ID_FILE_SEND_MAIL 0xE10C + +#define ID_FILE_MRU_FIRST 0xE110 +#define ID_FILE_MRU_FILE1 0xE110 // range - 16 max +#define ID_FILE_MRU_FILE2 0xE111 +#define ID_FILE_MRU_FILE3 0xE112 +#define ID_FILE_MRU_FILE4 0xE113 +#define ID_FILE_MRU_FILE5 0xE114 +#define ID_FILE_MRU_FILE6 0xE115 +#define ID_FILE_MRU_FILE7 0xE116 +#define ID_FILE_MRU_FILE8 0xE117 +#define ID_FILE_MRU_FILE9 0xE118 +#define ID_FILE_MRU_FILE10 0xE119 +#define ID_FILE_MRU_FILE11 0xE11A +#define ID_FILE_MRU_FILE12 0xE11B +#define ID_FILE_MRU_FILE13 0xE11C +#define ID_FILE_MRU_FILE14 0xE11D +#define ID_FILE_MRU_FILE15 0xE11E +#define ID_FILE_MRU_FILE16 0xE11F +#define ID_FILE_MRU_LAST 0xE11F + +// Edit commands +#define ID_EDIT_CLEAR 0xE120 +#define ID_EDIT_CLEAR_ALL 0xE121 +#define ID_EDIT_COPY 0xE122 +#define ID_EDIT_CUT 0xE123 +#define ID_EDIT_FIND 0xE124 +#define ID_EDIT_PASTE 0xE125 +#define ID_EDIT_PASTE_LINK 0xE126 +#define ID_EDIT_PASTE_SPECIAL 0xE127 +#define ID_EDIT_REPEAT 0xE128 +#define ID_EDIT_REPLACE 0xE129 +#define ID_EDIT_SELECT_ALL 0xE12A +#define ID_EDIT_UNDO 0xE12B +#define ID_EDIT_REDO 0xE12C + +// Window commands +#define ID_WINDOW_NEW 0xE130 +#define ID_WINDOW_ARRANGE 0xE131 +#define ID_WINDOW_CASCADE 0xE132 +#define ID_WINDOW_TILE_HORZ 0xE133 +#define ID_WINDOW_TILE_VERT 0xE134 +#define ID_WINDOW_SPLIT 0xE135 +#ifndef RC_INVOKED // code only +#define AFX_IDM_WINDOW_FIRST 0xE130 +#define AFX_IDM_WINDOW_LAST 0xE13F +#define AFX_IDM_FIRST_MDICHILD 0xFF00 // window list starts here +#endif //!RC_INVOKED + +// Help and App commands +#define ID_APP_ABOUT 0xE140 +#define ID_APP_EXIT 0xE141 +#define ID_HELP_INDEX 0xE142 +#define ID_HELP_FINDER 0xE143 +#define ID_HELP_USING 0xE144 +#define ID_CONTEXT_HELP 0xE145 // shift-F1 +// special commands for processing help +#define ID_HELP 0xE146 // first attempt for F1 +#define ID_DEFAULT_HELP 0xE147 // last attempt + +// Misc +#define ID_NEXT_PANE 0xE150 +#define ID_PREV_PANE 0xE151 + +// Format +#define ID_FORMAT_FONT 0xE160 + +// OLE commands +#define ID_OLE_INSERT_NEW 0xE200 +#define ID_OLE_EDIT_LINKS 0xE201 +#define ID_OLE_EDIT_CONVERT 0xE202 +#define ID_OLE_EDIT_CHANGE_ICON 0xE203 +#define ID_OLE_EDIT_PROPERTIES 0xE204 +#define ID_OLE_VERB_FIRST 0xE210 // range - 16 max +#ifndef RC_INVOKED // code only +#define ID_OLE_VERB_LAST 0xE21F +#endif //!RC_INVOKED + +// for print preview dialog bar +#define AFX_ID_PREVIEW_CLOSE 0xE300 +#define AFX_ID_PREVIEW_NUMPAGE 0xE301 // One/Two Page button +#define AFX_ID_PREVIEW_NEXT 0xE302 +#define AFX_ID_PREVIEW_PREV 0xE303 +#define AFX_ID_PREVIEW_PRINT 0xE304 +#define AFX_ID_PREVIEW_ZOOMIN 0xE305 +#define AFX_ID_PREVIEW_ZOOMOUT 0xE306 + +// View commands (same number used as IDW used for control bar) +#define ID_VIEW_TOOLBAR 0xE800 +#define ID_VIEW_STATUS_BAR 0xE801 +#define ID_VIEW_REBAR 0xE804 +#define ID_VIEW_AUTOARRANGE 0xE805 + // E810 -> E81F must be kept in order for RANGE macros +#define ID_VIEW_SMALLICON 0xE810 +#define ID_VIEW_LARGEICON 0xE811 +#define ID_VIEW_LIST 0xE812 +#define ID_VIEW_DETAILS 0xE813 +#define ID_VIEW_LINEUP 0xE814 +#define ID_VIEW_BYNAME 0xE815 +#define AFX_ID_VIEW_MINIMUM ID_VIEW_SMALLICON +#define AFX_ID_VIEW_MAXIMUM ID_VIEW_BYNAME + // E800 -> E8FF reserved for other control bar commands + +// RecordForm commands +#define ID_RECORD_FIRST 0xE900 +#define ID_RECORD_LAST 0xE901 +#define ID_RECORD_NEXT 0xE902 +#define ID_RECORD_PREV 0xE903 + +///////////////////////////////////////////////////////////////////////////// +// Standard control IDs + +#ifdef IDC_STATIC +#undef IDC_STATIC +#endif +#define IDC_STATIC (-1) // all static controls + +///////////////////////////////////////////////////////////////////////////// +// Standard string error/warnings + +#ifndef RC_INVOKED // code only +#define AFX_IDS_SCFIRST 0xEF00 +#endif //!RC_INVOKED + +#define AFX_IDS_SCSIZE 0xEF00 +#define AFX_IDS_SCMOVE 0xEF01 +#define AFX_IDS_SCMINIMIZE 0xEF02 +#define AFX_IDS_SCMAXIMIZE 0xEF03 +#define AFX_IDS_SCNEXTWINDOW 0xEF04 +#define AFX_IDS_SCPREVWINDOW 0xEF05 +#define AFX_IDS_SCCLOSE 0xEF06 +#define AFX_IDS_SCRESTORE 0xEF12 +#define AFX_IDS_SCTASKLIST 0xEF13 + +#define AFX_IDS_MDICHILD 0xEF1F + +#define AFX_IDS_DESKACCESSORY 0xEFDA + +// General strings +#define AFX_IDS_OPENFILE 0xF000 +#define AFX_IDS_SAVEFILE 0xF001 +#define AFX_IDS_ALLFILTER 0xF002 +#define AFX_IDS_UNTITLED 0xF003 +#define AFX_IDS_SAVEFILECOPY 0xF004 +#define AFX_IDS_PREVIEW_CLOSE 0xF005 +#define AFX_IDS_UNNAMED_FILE 0xF006 +#define AFX_IDS_HIDE 0xF011 + +// MFC Standard Exception Error messages +#define AFX_IDP_NO_ERROR_AVAILABLE 0xF020 +#define AFX_IDS_NOT_SUPPORTED_EXCEPTION 0xF021 +#define AFX_IDS_RESOURCE_EXCEPTION 0xF022 +#define AFX_IDS_MEMORY_EXCEPTION 0xF023 +#define AFX_IDS_USER_EXCEPTION 0xF024 + +// Printing and print preview strings +#define AFX_IDS_PRINTONPORT 0xF040 +#define AFX_IDS_ONEPAGE 0xF041 +#define AFX_IDS_TWOPAGE 0xF042 +#define AFX_IDS_PRINTPAGENUM 0xF043 +#define AFX_IDS_PREVIEWPAGEDESC 0xF044 +#define AFX_IDS_PRINTDEFAULTEXT 0xF045 +#define AFX_IDS_PRINTDEFAULT 0xF046 +#define AFX_IDS_PRINTFILTER 0xF047 +#define AFX_IDS_PRINTCAPTION 0xF048 +#define AFX_IDS_PRINTTOFILE 0xF049 + + +// OLE strings +#define AFX_IDS_OBJECT_MENUITEM 0xF080 +#define AFX_IDS_EDIT_VERB 0xF081 +#define AFX_IDS_ACTIVATE_VERB 0xF082 +#define AFX_IDS_CHANGE_LINK 0xF083 +#define AFX_IDS_AUTO 0xF084 +#define AFX_IDS_MANUAL 0xF085 +#define AFX_IDS_FROZEN 0xF086 +#define AFX_IDS_ALL_FILES 0xF087 +// dynamically changing menu items +#define AFX_IDS_SAVE_MENU 0xF088 +#define AFX_IDS_UPDATE_MENU 0xF089 +#define AFX_IDS_SAVE_AS_MENU 0xF08A +#define AFX_IDS_SAVE_COPY_AS_MENU 0xF08B +#define AFX_IDS_EXIT_MENU 0xF08C +#define AFX_IDS_UPDATING_ITEMS 0xF08D +// COlePasteSpecialDialog defines +#define AFX_IDS_METAFILE_FORMAT 0xF08E +#define AFX_IDS_DIB_FORMAT 0xF08F +#define AFX_IDS_BITMAP_FORMAT 0xF090 +#define AFX_IDS_LINKSOURCE_FORMAT 0xF091 +#define AFX_IDS_EMBED_FORMAT 0xF092 +// other OLE utility strings +#define AFX_IDS_PASTELINKEDTYPE 0xF094 +#define AFX_IDS_UNKNOWNTYPE 0xF095 +#define AFX_IDS_RTF_FORMAT 0xF096 +#define AFX_IDS_TEXT_FORMAT 0xF097 +// OLE datatype format error strings +#define AFX_IDS_INVALID_CURRENCY 0xF098 +#define AFX_IDS_INVALID_DATETIME 0xF099 +#define AFX_IDS_INVALID_DATETIMESPAN 0xF09A + +// General error / prompt strings +#define AFX_IDP_INVALID_FILENAME 0xF100 +#define AFX_IDP_FAILED_TO_OPEN_DOC 0xF101 +#define AFX_IDP_FAILED_TO_SAVE_DOC 0xF102 +#define AFX_IDP_ASK_TO_SAVE 0xF103 +#define AFX_IDP_FAILED_TO_CREATE_DOC 0xF104 +#define AFX_IDP_FILE_TOO_LARGE 0xF105 +#define AFX_IDP_FAILED_TO_START_PRINT 0xF106 +#define AFX_IDP_FAILED_TO_LAUNCH_HELP 0xF107 +#define AFX_IDP_INTERNAL_FAILURE 0xF108 // general failure +#define AFX_IDP_COMMAND_FAILURE 0xF109 // command failure +#define AFX_IDP_FAILED_MEMORY_ALLOC 0xF10A +#define AFX_IDP_UNREG_DONE 0xF10B +#define AFX_IDP_UNREG_FAILURE 0xF10C +#define AFX_IDP_DLL_LOAD_FAILED 0xF10D +#define AFX_IDP_DLL_BAD_VERSION 0xF10E + +// DDV parse errors +#define AFX_IDP_PARSE_INT 0xF110 +#define AFX_IDP_PARSE_REAL 0xF111 +#define AFX_IDP_PARSE_INT_RANGE 0xF112 +#define AFX_IDP_PARSE_REAL_RANGE 0xF113 +#define AFX_IDP_PARSE_STRING_SIZE 0xF114 +#define AFX_IDP_PARSE_RADIO_BUTTON 0xF115 +#define AFX_IDP_PARSE_BYTE 0xF116 +#define AFX_IDP_PARSE_UINT 0xF117 +#define AFX_IDP_PARSE_DATETIME 0xF118 +#define AFX_IDP_PARSE_CURRENCY 0xF119 + +// CFile/CArchive error strings for user failure +#define AFX_IDP_FAILED_INVALID_FORMAT 0xF120 +#define AFX_IDP_FAILED_INVALID_PATH 0xF121 +#define AFX_IDP_FAILED_DISK_FULL 0xF122 +#define AFX_IDP_FAILED_ACCESS_READ 0xF123 +#define AFX_IDP_FAILED_ACCESS_WRITE 0xF124 +#define AFX_IDP_FAILED_IO_ERROR_READ 0xF125 +#define AFX_IDP_FAILED_IO_ERROR_WRITE 0xF126 + +// OLE errors / prompt strings +#define AFX_IDP_STATIC_OBJECT 0xF180 +#define AFX_IDP_FAILED_TO_CONNECT 0xF181 +#define AFX_IDP_SERVER_BUSY 0xF182 +#define AFX_IDP_BAD_VERB 0xF183 +#define AFX_IDS_NOT_DOCOBJECT 0xF184 +#define AFX_IDP_FAILED_TO_NOTIFY 0xF185 +#define AFX_IDP_FAILED_TO_LAUNCH 0xF186 +#define AFX_IDP_ASK_TO_UPDATE 0xF187 +#define AFX_IDP_FAILED_TO_UPDATE 0xF188 +#define AFX_IDP_FAILED_TO_REGISTER 0xF189 +#define AFX_IDP_FAILED_TO_AUTO_REGISTER 0xF18A +#define AFX_IDP_FAILED_TO_CONVERT 0xF18B +#define AFX_IDP_GET_NOT_SUPPORTED 0xF18C +#define AFX_IDP_SET_NOT_SUPPORTED 0xF18D +#define AFX_IDP_ASK_TO_DISCARD 0xF18E +#define AFX_IDP_FAILED_TO_CREATE 0xF18F + +// MAPI errors / prompt strings +#define AFX_IDP_FAILED_MAPI_LOAD 0xF190 +#define AFX_IDP_INVALID_MAPI_DLL 0xF191 +#define AFX_IDP_FAILED_MAPI_SEND 0xF192 + +#define AFX_IDP_FILE_NONE 0xF1A0 +#define AFX_IDP_FILE_GENERIC 0xF1A1 +#define AFX_IDP_FILE_NOT_FOUND 0xF1A2 +#define AFX_IDP_FILE_BAD_PATH 0xF1A3 +#define AFX_IDP_FILE_TOO_MANY_OPEN 0xF1A4 +#define AFX_IDP_FILE_ACCESS_DENIED 0xF1A5 +#define AFX_IDP_FILE_INVALID_FILE 0xF1A6 +#define AFX_IDP_FILE_REMOVE_CURRENT 0xF1A7 +#define AFX_IDP_FILE_DIR_FULL 0xF1A8 +#define AFX_IDP_FILE_BAD_SEEK 0xF1A9 +#define AFX_IDP_FILE_HARD_IO 0xF1AA +#define AFX_IDP_FILE_SHARING 0xF1AB +#define AFX_IDP_FILE_LOCKING 0xF1AC +#define AFX_IDP_FILE_DISKFULL 0xF1AD +#define AFX_IDP_FILE_EOF 0xF1AE + +#define AFX_IDP_ARCH_NONE 0xF1B0 +#define AFX_IDP_ARCH_GENERIC 0xF1B1 +#define AFX_IDP_ARCH_READONLY 0xF1B2 +#define AFX_IDP_ARCH_ENDOFFILE 0xF1B3 +#define AFX_IDP_ARCH_WRITEONLY 0xF1B4 +#define AFX_IDP_ARCH_BADINDEX 0xF1B5 +#define AFX_IDP_ARCH_BADCLASS 0xF1B6 +#define AFX_IDP_ARCH_BADSCHEMA 0xF1B7 + +#define AFX_IDS_OCC_SCALEUNITS_PIXELS 0xF1C0 + +// 0xf200-0xf20f reserved + +// font names and point sizes +#define AFX_IDS_STATUS_FONT 0xF230 +#define AFX_IDS_TOOLTIP_FONT 0xF231 +#define AFX_IDS_UNICODE_FONT 0xF232 +#define AFX_IDS_MINI_FONT 0xF233 + +// ODBC Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_SQL_FIRST 0xF280 +#endif //!RC_INVOKED +#define AFX_IDP_SQL_CONNECT_FAIL 0xF281 +#define AFX_IDP_SQL_RECORDSET_FORWARD_ONLY 0xF282 +#define AFX_IDP_SQL_EMPTY_COLUMN_LIST 0xF283 +#define AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH 0xF284 +#define AFX_IDP_SQL_ILLEGAL_MODE 0xF285 +#define AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED 0xF286 +#define AFX_IDP_SQL_NO_CURRENT_RECORD 0xF287 +#define AFX_IDP_SQL_NO_ROWS_AFFECTED 0xF288 +#define AFX_IDP_SQL_RECORDSET_READONLY 0xF289 +#define AFX_IDP_SQL_SQL_NO_TOTAL 0xF28A +#define AFX_IDP_SQL_ODBC_LOAD_FAILED 0xF28B +#define AFX_IDP_SQL_DYNASET_NOT_SUPPORTED 0xF28C +#define AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED 0xF28D +#define AFX_IDP_SQL_API_CONFORMANCE 0xF28E +#define AFX_IDP_SQL_SQL_CONFORMANCE 0xF28F +#define AFX_IDP_SQL_NO_DATA_FOUND 0xF290 +#define AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED 0xF291 +#define AFX_IDP_SQL_ODBC_V2_REQUIRED 0xF292 +#define AFX_IDP_SQL_NO_POSITIONED_UPDATES 0xF293 +#define AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED 0xF294 +#define AFX_IDP_SQL_DATA_TRUNCATED 0xF295 +#define AFX_IDP_SQL_ROW_FETCH 0xF296 +#define AFX_IDP_SQL_INCORRECT_ODBC 0xF297 +#define AFX_IDP_SQL_UPDATE_DELETE_FAILED 0xF298 +#define AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED 0xF299 +#define AFX_IDP_SQL_FIELD_NOT_FOUND 0xF29A +#define AFX_IDP_SQL_BOOKMARKS_NOT_SUPPORTED 0xF29B +#define AFX_IDP_SQL_BOOKMARKS_NOT_ENABLED 0xF29C + +// ODBC Database strings +#define AFX_IDS_DELETED 0xF29D + +// DAO Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_DAO_FIRST 0xF2B0 +#endif //!RC_INVOKED +#define AFX_IDP_DAO_ENGINE_INITIALIZATION 0xF2B0 +#define AFX_IDP_DAO_DFX_BIND 0xF2B1 +#define AFX_IDP_DAO_OBJECT_NOT_OPEN 0xF2B2 + +// ICDAORecordset::GetRows Errors +// These are not placed in DAO Errors collection +// and must be handled directly by MFC. +#define AFX_IDP_DAO_ROWTOOSHORT 0xF2B3 +#define AFX_IDP_DAO_BADBINDINFO 0xF2B4 +#define AFX_IDP_DAO_COLUMNUNAVAILABLE 0xF2B5 + +///////////////////////////////////////////////////////////////////////////// +// Strings for ISAPI support + +#define AFX_IDS_HTTP_TITLE 0xF2D1 +#define AFX_IDS_HTTP_NO_TEXT 0xF2D2 +#define AFX_IDS_HTTP_BAD_REQUEST 0xF2D3 +#define AFX_IDS_HTTP_AUTH_REQUIRED 0xF2D4 +#define AFX_IDS_HTTP_FORBIDDEN 0xF2D5 +#define AFX_IDS_HTTP_NOT_FOUND 0xF2D6 +#define AFX_IDS_HTTP_SERVER_ERROR 0xF2D7 +#define AFX_IDS_HTTP_NOT_IMPLEMENTED 0xF2D8 + +///////////////////////////////////////////////////////////////////////////// +// AFX implementation - control IDs (AFX_IDC) + +// Parts of dialogs +#define AFX_IDC_LISTBOX 100 +#define AFX_IDC_CHANGE 101 + +// for print dialog +#define AFX_IDC_PRINT_DOCNAME 201 +#define AFX_IDC_PRINT_PRINTERNAME 202 +#define AFX_IDC_PRINT_PORTNAME 203 +#define AFX_IDC_PRINT_PAGENUM 204 + +// Property Sheet control id's (determined with Spy++) +#define ID_APPLY_NOW 0x3021 +#define ID_WIZBACK 0x3023 +#define ID_WIZNEXT 0x3024 +#define ID_WIZFINISH 0x3025 +#define AFX_IDC_TAB_CONTROL 0x3020 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for standard components + +#ifndef RC_INVOKED // code only +// These are really COMMDLG dialogs, so there usually isn't a resource +// for them, but these IDs are used as help IDs. +#define AFX_IDD_FILEOPEN 28676 +#define AFX_IDD_FILESAVE 28677 +#define AFX_IDD_FONT 28678 +#define AFX_IDD_COLOR 28679 +#define AFX_IDD_PRINT 28680 +#define AFX_IDD_PRINTSETUP 28681 +#define AFX_IDD_FIND 28682 +#define AFX_IDD_REPLACE 28683 +#endif //!RC_INVOKED + +// Standard dialogs app should leave alone (0x7801->) +#define AFX_IDD_NEWTYPEDLG 30721 +#define AFX_IDD_PRINTDLG 30722 +#define AFX_IDD_PREVIEW_TOOLBAR 30723 + +// Dialogs defined for OLE2UI library +#define AFX_IDD_INSERTOBJECT 30724 +#define AFX_IDD_CHANGEICON 30725 +#define AFX_IDD_CONVERT 30726 +#define AFX_IDD_PASTESPECIAL 30727 +#define AFX_IDD_EDITLINKS 30728 +#define AFX_IDD_FILEBROWSE 30729 +#define AFX_IDD_BUSY 30730 + +#define AFX_IDD_OBJECTPROPERTIES 30732 +#define AFX_IDD_CHANGESOURCE 30733 + +// Standard cursors (0x7901->) + // AFX_IDC = Cursor resources +#define AFX_IDC_CONTEXTHELP 30977 // context sensitive help +#define AFX_IDC_MAGNIFY 30978 // print preview zoom +#define AFX_IDC_SMALLARROWS 30979 // splitter +#define AFX_IDC_HSPLITBAR 30980 // splitter +#define AFX_IDC_VSPLITBAR 30981 // splitter +#define AFX_IDC_NODROPCRSR 30982 // No Drop Cursor +#define AFX_IDC_TRACKNWSE 30983 // tracker +#define AFX_IDC_TRACKNESW 30984 // tracker +#define AFX_IDC_TRACKNS 30985 // tracker +#define AFX_IDC_TRACKWE 30986 // tracker +#define AFX_IDC_TRACK4WAY 30987 // tracker +#define AFX_IDC_MOVE4WAY 30988 // resize bar (server only) + +// Mini frame window bitmap ID +#define AFX_IDB_MINIFRAME_MENU 30994 + +// CheckListBox checks bitmap ID +#define AFX_IDB_CHECKLISTBOX_NT 30995 +#define AFX_IDB_CHECKLISTBOX_95 30996 + +// AFX standard accelerator resources +#define AFX_IDR_PREVIEW_ACCEL 30997 + +// AFX standard ICON IDs (for MFC V1 apps) (0x7A01->) +#define AFX_IDI_STD_MDIFRAME 31233 +#define AFX_IDI_STD_FRAME 31234 + +///////////////////////////////////////////////////////////////////////////// +// AFX OLE control implementation - control IDs (AFX_IDC) + +// Font property page +#define AFX_IDC_FONTPROP 1000 +#define AFX_IDC_FONTNAMES 1001 +#define AFX_IDC_FONTSTYLES 1002 +#define AFX_IDC_FONTSIZES 1003 +#define AFX_IDC_STRIKEOUT 1004 +#define AFX_IDC_UNDERLINE 1005 +#define AFX_IDC_SAMPLEBOX 1006 + +// Color property page +#define AFX_IDC_COLOR_BLACK 1100 +#define AFX_IDC_COLOR_WHITE 1101 +#define AFX_IDC_COLOR_RED 1102 +#define AFX_IDC_COLOR_GREEN 1103 +#define AFX_IDC_COLOR_BLUE 1104 +#define AFX_IDC_COLOR_YELLOW 1105 +#define AFX_IDC_COLOR_MAGENTA 1106 +#define AFX_IDC_COLOR_CYAN 1107 +#define AFX_IDC_COLOR_GRAY 1108 +#define AFX_IDC_COLOR_LIGHTGRAY 1109 +#define AFX_IDC_COLOR_DARKRED 1110 +#define AFX_IDC_COLOR_DARKGREEN 1111 +#define AFX_IDC_COLOR_DARKBLUE 1112 +#define AFX_IDC_COLOR_LIGHTBROWN 1113 +#define AFX_IDC_COLOR_DARKMAGENTA 1114 +#define AFX_IDC_COLOR_DARKCYAN 1115 +#define AFX_IDC_COLORPROP 1116 +#define AFX_IDC_SYSTEMCOLORS 1117 + +// Picture porperty page +#define AFX_IDC_PROPNAME 1201 +#define AFX_IDC_PICTURE 1202 +#define AFX_IDC_BROWSE 1203 +#define AFX_IDC_CLEAR 1204 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for OLE control standard components + +// Standard propery page dialogs app should leave alone (0x7E01->) +#define AFX_IDD_PROPPAGE_COLOR 32257 +#define AFX_IDD_PROPPAGE_FONT 32258 +#define AFX_IDD_PROPPAGE_PICTURE 32259 + +#define AFX_IDB_TRUETYPE 32384 + +///////////////////////////////////////////////////////////////////////////// +// Standard OLE control strings + +// OLE Control page strings +#define AFX_IDS_PROPPAGE_UNKNOWN 0xFE01 +#define AFX_IDS_COLOR_DESKTOP 0xFE04 +#define AFX_IDS_COLOR_APPWORKSPACE 0xFE05 +#define AFX_IDS_COLOR_WNDBACKGND 0xFE06 +#define AFX_IDS_COLOR_WNDTEXT 0xFE07 +#define AFX_IDS_COLOR_MENUBAR 0xFE08 +#define AFX_IDS_COLOR_MENUTEXT 0xFE09 +#define AFX_IDS_COLOR_ACTIVEBAR 0xFE0A +#define AFX_IDS_COLOR_INACTIVEBAR 0xFE0B +#define AFX_IDS_COLOR_ACTIVETEXT 0xFE0C +#define AFX_IDS_COLOR_INACTIVETEXT 0xFE0D +#define AFX_IDS_COLOR_ACTIVEBORDER 0xFE0E +#define AFX_IDS_COLOR_INACTIVEBORDER 0xFE0F +#define AFX_IDS_COLOR_WNDFRAME 0xFE10 +#define AFX_IDS_COLOR_SCROLLBARS 0xFE11 +#define AFX_IDS_COLOR_BTNFACE 0xFE12 +#define AFX_IDS_COLOR_BTNSHADOW 0xFE13 +#define AFX_IDS_COLOR_BTNTEXT 0xFE14 +#define AFX_IDS_COLOR_BTNHIGHLIGHT 0xFE15 +#define AFX_IDS_COLOR_DISABLEDTEXT 0xFE16 +#define AFX_IDS_COLOR_HIGHLIGHT 0xFE17 +#define AFX_IDS_COLOR_HIGHLIGHTTEXT 0xFE18 +#define AFX_IDS_REGULAR 0xFE19 +#define AFX_IDS_BOLD 0xFE1A +#define AFX_IDS_ITALIC 0xFE1B +#define AFX_IDS_BOLDITALIC 0xFE1C +#define AFX_IDS_SAMPLETEXT 0xFE1D +#define AFX_IDS_DISPLAYSTRING_FONT 0xFE1E +#define AFX_IDS_DISPLAYSTRING_COLOR 0xFE1F +#define AFX_IDS_DISPLAYSTRING_PICTURE 0xFE20 +#define AFX_IDS_PICTUREFILTER 0xFE21 +#define AFX_IDS_PICTYPE_UNKNOWN 0xFE22 +#define AFX_IDS_PICTYPE_NONE 0xFE23 +#define AFX_IDS_PICTYPE_BITMAP 0xFE24 +#define AFX_IDS_PICTYPE_METAFILE 0xFE25 +#define AFX_IDS_PICTYPE_ICON 0xFE26 +#define AFX_IDS_COLOR_PPG 0xFE28 +#define AFX_IDS_COLOR_PPG_CAPTION 0xFE29 +#define AFX_IDS_FONT_PPG 0xFE2A +#define AFX_IDS_FONT_PPG_CAPTION 0xFE2B +#define AFX_IDS_PICTURE_PPG 0xFE2C +#define AFX_IDS_PICTURE_PPG_CAPTION 0xFE2D +#define AFX_IDS_PICTUREBROWSETITLE 0xFE30 +#define AFX_IDS_BORDERSTYLE_0 0xFE31 +#define AFX_IDS_BORDERSTYLE_1 0xFE32 + +// OLE Control verb names +#define AFX_IDS_VERB_EDIT 0xFE40 +#define AFX_IDS_VERB_PROPERTIES 0xFE41 + +// OLE Control internal error messages +#define AFX_IDP_PICTURECANTOPEN 0xFE83 +#define AFX_IDP_PICTURECANTLOAD 0xFE84 +#define AFX_IDP_PICTURETOOLARGE 0xFE85 +#define AFX_IDP_PICTUREREADFAILED 0xFE86 + +// Standard OLE Control error strings +#define AFX_IDP_E_ILLEGALFUNCTIONCALL 0xFEA0 +#define AFX_IDP_E_OVERFLOW 0xFEA1 +#define AFX_IDP_E_OUTOFMEMORY 0xFEA2 +#define AFX_IDP_E_DIVISIONBYZERO 0xFEA3 +#define AFX_IDP_E_OUTOFSTRINGSPACE 0xFEA4 +#define AFX_IDP_E_OUTOFSTACKSPACE 0xFEA5 +#define AFX_IDP_E_BADFILENAMEORNUMBER 0xFEA6 +#define AFX_IDP_E_FILENOTFOUND 0xFEA7 +#define AFX_IDP_E_BADFILEMODE 0xFEA8 +#define AFX_IDP_E_FILEALREADYOPEN 0xFEA9 +#define AFX_IDP_E_DEVICEIOERROR 0xFEAA +#define AFX_IDP_E_FILEALREADYEXISTS 0xFEAB +#define AFX_IDP_E_BADRECORDLENGTH 0xFEAC +#define AFX_IDP_E_DISKFULL 0xFEAD +#define AFX_IDP_E_BADRECORDNUMBER 0xFEAE +#define AFX_IDP_E_BADFILENAME 0xFEAF +#define AFX_IDP_E_TOOMANYFILES 0xFEB0 +#define AFX_IDP_E_DEVICEUNAVAILABLE 0xFEB1 +#define AFX_IDP_E_PERMISSIONDENIED 0xFEB2 +#define AFX_IDP_E_DISKNOTREADY 0xFEB3 +#define AFX_IDP_E_PATHFILEACCESSERROR 0xFEB4 +#define AFX_IDP_E_PATHNOTFOUND 0xFEB5 +#define AFX_IDP_E_INVALIDPATTERNSTRING 0xFEB6 +#define AFX_IDP_E_INVALIDUSEOFNULL 0xFEB7 +#define AFX_IDP_E_INVALIDFILEFORMAT 0xFEB8 +#define AFX_IDP_E_INVALIDPROPERTYVALUE 0xFEB9 +#define AFX_IDP_E_INVALIDPROPERTYARRAYINDEX 0xFEBA +#define AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME 0xFEBB +#define AFX_IDP_E_SETNOTSUPPORTED 0xFEBC +#define AFX_IDP_E_NEEDPROPERTYARRAYINDEX 0xFEBD +#define AFX_IDP_E_SETNOTPERMITTED 0xFEBE +#define AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME 0xFEBF +#define AFX_IDP_E_GETNOTSUPPORTED 0xFEC0 +#define AFX_IDP_E_PROPERTYNOTFOUND 0xFEC1 +#define AFX_IDP_E_INVALIDCLIPBOARDFORMAT 0xFEC2 +#define AFX_IDP_E_INVALIDPICTURE 0xFEC3 +#define AFX_IDP_E_PRINTERERROR 0xFEC4 +#define AFX_IDP_E_CANTSAVEFILETOTEMP 0xFEC5 +#define AFX_IDP_E_SEARCHTEXTNOTFOUND 0xFEC6 +#define AFX_IDP_E_REPLACEMENTSTOOLONG 0xFEC7 + +///////////////////////////////////////////////////////////////////////////// + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, on) +#endif + +#endif //__AFXRES_H__ + +///////////////////////////////////////////////////////////////////////////// diff --git a/win32/src/MeteredSection.cpp b/win32/src/MeteredSection.cpp new file mode 100755 index 0000000..d8fea51 --- /dev/null +++ b/win32/src/MeteredSection.cpp @@ -0,0 +1,329 @@ +/************************************************************ + Module Name: MeteredSection.c + Author: Dan Chou + Description: Implements the metered section synchronization object +************************************************************/ + +#include <windows.h> +#include <tchar.h> +#include "MeteredSection.h" +#include "../../src/fmod_memory.h" + +// Internal function declarations +BOOL InitMeteredSection(LPMETERED_SECTION lpMetSect, LONG lInitialCount, + LONG lMaximumCount, LPCTSTR lpName, BOOL bOpenOnly); +BOOL CreateMetSectEvent(LPMETERED_SECTION lpMetSect, LPCTSTR lpName, BOOL + bOpenOnly); +BOOL CreateMetSectFileView(LPMETERED_SECTION lpMetSect, LONG lInitialCount, + LONG lMaximumCount, LPCTSTR lpName, BOOL bOpenOnly); +void GetMeteredSectionLock(LPMETERED_SECTION lpMetSect); +void ReleaseMeteredSectionLock(LPMETERED_SECTION lpMetSect); + +/* + * CreateMeteredSection + */ +LPMETERED_SECTION CreateMeteredSection(LONG lInitialCount, + LONG lMaximumCount, LPCTSTR lpName) +{ + LPMETERED_SECTION lpMetSect; + + // Verify the parameters + if ((lMaximumCount < 1) || + (lInitialCount > lMaximumCount) || + (lInitialCount < 0) || + ((lpName) && (_tcslen(lpName) > MAX_METSECT_NAMELEN))) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + // Allocate memory for the metered section + lpMetSect = (LPMETERED_SECTION)FMOD_Memory_Alloc(sizeof(METERED_SECTION)); + + // If the memory for the metered section was allocated okay, + // initialize it + if (lpMetSect) + { + if (!InitMeteredSection(lpMetSect, lInitialCount, + lMaximumCount, lpName, FALSE)) + { + CloseMeteredSection(lpMetSect); + lpMetSect = NULL; + } + } + return lpMetSect; +} + +/* + * OpenMeteredSection + */ +#ifndef _WIN32_WCE +LPMETERED_SECTION OpenMeteredSection(LPCTSTR lpName) +{ + LPMETERED_SECTION lpMetSect = NULL; + + if (lpName) + { + lpMetSect = (LPMETERED_SECTION)FMOD_Memory_Alloc(sizeof(METERED_SECTION)); + + // If the memory for the metered section was allocated okay + if (lpMetSect) + { + if (!InitMeteredSection(lpMetSect, 0, 0, lpName, TRUE)) + { + // Metered section failed to initialize + CloseMeteredSection(lpMetSect); + lpMetSect = NULL; + } + } + } + return lpMetSect; +} +#endif + +/* + * EnterMeteredSection + */ +DWORD EnterMeteredSection(LPMETERED_SECTION lpMetSect, + DWORD dwMilliseconds) +{ + while (TRUE) + { + GetMeteredSectionLock(lpMetSect); + + // We have access to the metered section, everything we + // do now will be atomic + if (lpMetSect->lpSharedInfo->lAvailableCount >= 1) + { + lpMetSect->lpSharedInfo->lAvailableCount--; + ReleaseMeteredSectionLock(lpMetSect); + return WAIT_OBJECT_0; + } + + // Couldn't get in. Wait on the event object + lpMetSect->lpSharedInfo->lThreadsWaiting++; + ResetEvent(lpMetSect->hEvent); + ReleaseMeteredSectionLock(lpMetSect); + if (WaitForSingleObject(lpMetSect->hEvent, + dwMilliseconds) == WAIT_TIMEOUT) + { + return WAIT_TIMEOUT; + } + } +} + +/* + * LeaveMeteredSection + */ +BOOL LeaveMeteredSection(LPMETERED_SECTION lpMetSect, LONG lReleaseCount, + LPLONG lpPreviousCount) +{ + int iCount; + GetMeteredSectionLock(lpMetSect); + + // Save the old value if they want it + if (lpPreviousCount) + { + *lpPreviousCount = lpMetSect->lpSharedInfo->lAvailableCount; + } + + // We have access to the metered section, + // everything we do now will be atomic + if ((lReleaseCount < 0) || + (lpMetSect->lpSharedInfo->lAvailableCount+lReleaseCount > + lpMetSect->lpSharedInfo->lMaximumCount)) + { + ReleaseMeteredSectionLock(lpMetSect); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + lpMetSect->lpSharedInfo->lAvailableCount += lReleaseCount; + + // Set the event the appropriate number of times + lReleaseCount = + min(lReleaseCount,lpMetSect->lpSharedInfo->lThreadsWaiting); + if (lpMetSect->lpSharedInfo->lThreadsWaiting) + { + for (iCount=0; iCount < lReleaseCount ; iCount++) + { + lpMetSect->lpSharedInfo->lThreadsWaiting--; + SetEvent(lpMetSect->hEvent); + } + } + ReleaseMeteredSectionLock(lpMetSect); + return TRUE; +} + +/* + * CloseMeteredSection + */ +void CloseMeteredSection(LPMETERED_SECTION lpMetSect) +{ + if (lpMetSect) + { + // Clean up + #ifdef FILEMAPPING + if (lpMetSect->lpSharedInfo) UnmapViewOfFile(lpMetSect->lpSharedInfo); + if (lpMetSect->hFileMap) CloseHandle(lpMetSect->hFileMap); + #else + DeleteCriticalSection(&lpMetSect->hCrit); + #endif + + if (lpMetSect->hEvent) CloseHandle(lpMetSect->hEvent); + FMOD_Memory_Free(lpMetSect); + } +} + +/* + * InitMeteredSection + */ +BOOL InitMeteredSection(LPMETERED_SECTION lpMetSect, + LONG lInitialCount, LONG lMaximumCount, + LPCTSTR lpName, BOOL bOpenOnly) +{ + // Try to create the event object + if (CreateMetSectEvent(lpMetSect, lpName, bOpenOnly)) + { +#ifdef FILEMAPPING + // Try to create the memory mapped file + if (CreateMetSectFileView(lpMetSect, lInitialCount, lMaximumCount, lpName, bOpenOnly)) +#else + lpMetSect->lpSharedInfo = &lpMetSect->SharedInfo; + lpMetSect->lpSharedInfo->lThreadsWaiting = 0; + lpMetSect->lpSharedInfo->lAvailableCount = lInitialCount; + lpMetSect->lpSharedInfo->lMaximumCount = lMaximumCount; + InitializeCriticalSection(&lpMetSect->hCrit); + InterlockedExchange((long *)&(lpMetSect->lpSharedInfo->fInitialized), TRUE); +#endif + { + return TRUE; + } + } + + // Error occured, return FALSE so the caller knows to clean up + return FALSE; +} + +/* + * CreateMetSectEvent + */ +BOOL CreateMetSectEvent(LPMETERED_SECTION lpMetSect, + LPCTSTR lpName, BOOL bOpenOnly) +{ + TCHAR sz[MAX_PATH]; + if (lpName) + { + //wsprintf(sz, _TEXT("DKC_MSECT_EVT_%s"), lpName); + +#ifndef _WIN32_WCE + if (bOpenOnly) + { + lpMetSect->hEvent = OpenEvent(0, FALSE, sz); + } + else + { +#endif + // Create an auto-reset named event object + lpMetSect->hEvent = CreateEvent(NULL, FALSE, FALSE, sz); +#ifndef _WIN32_WCE + } +#endif + } + else + { + // Create an auto-reset unnamed event object + lpMetSect->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + } + return (lpMetSect->hEvent ? TRUE : FALSE); +} + +#ifdef FILEMAPPING +/* + * CreateMetSectFileView + */ +BOOL CreateMetSectFileView(LPMETERED_SECTION lpMetSect, + LONG lInitialCount, LONG lMaximumCount, + LPCTSTR lpName, BOOL bOpenOnly) +{ + TCHAR sz[MAX_PATH]; + DWORD dwLastError; + + if (lpName) + { + //wsprintf(sz, _TEXT("DKC_MSECT_MMF_%s"), lpName); + +#ifndef _WIN32_WCE + if (bOpenOnly) + { + lpMetSect->hFileMap = OpenFileMapping(0, FALSE, sz); + } + else + { +#endif + // Create a named file mapping + lpMetSect->hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, PAGE_READWRITE, 0, sizeof(METSECT_SHARED_INFO), sz); +#ifndef _WIN32_WCE + } +#endif + } + else + { + // Create an unnamed file mapping + lpMetSect->hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, PAGE_READWRITE, 0, sizeof(METSECT_SHARED_INFO), NULL); + } + + // Map a view of the file + if (lpMetSect->hFileMap) + { + dwLastError = GetLastError(); + lpMetSect->lpSharedInfo = (LPMETSECT_SHARED_INFO) + MapViewOfFile(lpMetSect->hFileMap, FILE_MAP_WRITE, 0, 0, 0); + if (lpMetSect->lpSharedInfo) + { + if (dwLastError != ERROR_ALREADY_EXISTS) + { + lpMetSect->lpSharedInfo->lSpinLock = 0; + lpMetSect->lpSharedInfo->lThreadsWaiting = 0; + lpMetSect->lpSharedInfo->lAvailableCount = lInitialCount; + lpMetSect->lpSharedInfo->lMaximumCount = lMaximumCount; + InterlockedExchange((long *)&(lpMetSect->lpSharedInfo->fInitialized), TRUE); + } + else + { // Already exists; wait for it to be initialized by the creator + while (!lpMetSect->lpSharedInfo->fInitialized) Sleep(0); + } + return TRUE; + } + } + + return FALSE; +} +#endif + +/* + * GetMeteredSectionLock + */ +void GetMeteredSectionLock(LPMETERED_SECTION lpMetSect) +{ +#if FILEMAPPING + // Spin and get access to the metered section lock + while (InterlockedExchange(&(lpMetSect->lpSharedInfo->lSpinLock), 1) != 0) + Sleep(1); +#else + EnterCriticalSection(&lpMetSect->hCrit); +#endif +} + +/* + * ReleaseMeteredSectionLock + */ +void ReleaseMeteredSectionLock(LPMETERED_SECTION lpMetSect) +{ +#if FILEMAPPING + InterlockedExchange(&(lpMetSect->lpSharedInfo->lSpinLock), 0); +#else + LeaveCriticalSection(&lpMetSect->hCrit); +#endif +} \ No newline at end of file diff --git a/win32/src/MeteredSection.h b/win32/src/MeteredSection.h new file mode 100755 index 0000000..e453517 --- /dev/null +++ b/win32/src/MeteredSection.h @@ -0,0 +1,62 @@ +/************************************************************ + Module Name: MeteredSection.h + Author: Dan Chou + Description: Defines the metered section synchronization object +************************************************************/ + +#include <windows.h> + +#ifndef _METERED_SECTION_H_ +#define _METERED_SECTION_H_ + +#define MAX_METSECT_NAMELEN 128 + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +//#define FILEMAPPING + +// Shared info needed for metered section +typedef struct _METSECT_SHARED_INFO { + BOOL fInitialized; // Is the metered section initialized? +#ifdef FILEMAPPING + LONG lSpinLock; // Used to gain access to this structure +#endif + LONG lThreadsWaiting; // Count of threads waiting + LONG lAvailableCount; // Available resource count + LONG lMaximumCount; // Maximum resource count +} METSECT_SHARED_INFO, *LPMETSECT_SHARED_INFO; + +// The opaque Metered Section data structure +typedef struct _METERED_SECTION { + HANDLE hEvent; // Handle to a kernel event object +#ifdef FILEMAPPING + HANDLE hFileMap; // Handle to memory mapped file +#else + METSECT_SHARED_INFO SharedInfo; + CRITICAL_SECTION hCrit; +#endif + LPMETSECT_SHARED_INFO lpSharedInfo; +} METERED_SECTION, *LPMETERED_SECTION; + +// Interface functions +LPMETERED_SECTION +CreateMeteredSection(LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName); + +#ifndef _WIN32_WCE +LPMETERED_SECTION OpenMeteredSection(LPCTSTR lpName); +#endif + +DWORD EnterMeteredSection(LPMETERED_SECTION lpMetSect, + DWORD dwMilliseconds); +BOOL LeaveMeteredSection(LPMETERED_SECTION lpMetSect, + LONG lReleaseCount, LPLONG lpPreviousCount); +void CloseMeteredSection(LPMETERED_SECTION lpMetSect); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // _METERED_SECTION_H_ diff --git a/win32/src/asio/asio.cpp b/win32/src/asio/asio.cpp new file mode 100755 index 0000000..eba4bd0 --- /dev/null +++ b/win32/src/asio/asio.cpp @@ -0,0 +1,259 @@ +/* + Steinberg Audio Stream I/O API + (c) 1996, Steinberg Soft- und Hardware GmbH + + asio.cpp + + asio functions entries which translate the + asio interface to the asiodrvr class methods +*/ + +#include "asiosys.h" // platform definition +#include "asio.h" + +#if MAC +#include "asiodrvr.h" + +#pragma export on + +AsioDriver *theAsioDriver = 0; + +extern "C" +{ + +long main() +{ + return 'ASIO'; +} + +#elif WINDOWS + +#include "windows.h" +#include "iasiodrv.h" +#include "asiodrivers.h" + +IASIO *theAsioDriver = 0; +extern AsioDrivers *asioDrivers; + +#elif SGI || SUN || BEOS || LINUX +#include "asiodrvr.h" +static AsioDriver *theAsioDriver = 0; +#endif + +//----------------------------------------------------------------------------------------------------- +ASIOError ASIOInit(ASIODriverInfo *info) +{ +#if MAC || SGI || SUN || BEOS || LINUX + if(theAsioDriver) + { + delete theAsioDriver; + theAsioDriver = 0; + } + info->driverVersion = 0; + strcpy(info->name, "No ASIO Driver"); + theAsioDriver = getDriver(); + if(!theAsioDriver) + { + strcpy(info->errorMessage, "Not enough memory for the ASIO driver!"); + return ASE_NotPresent; + } + if(!theAsioDriver->init(info->sysRef)) + { + theAsioDriver->getErrorMessage(info->errorMessage); + delete theAsioDriver; + theAsioDriver = 0; + return ASE_NotPresent; + } + strcpy(info->errorMessage, "No ASIO Driver Error"); + theAsioDriver->getDriverName(info->name); + info->driverVersion = theAsioDriver->getDriverVersion(); + return ASE_OK; + +#else + + info->driverVersion = 0; + strcpy(info->name, "No ASIO Driver"); + if(theAsioDriver) // must be loaded! + { + if(!theAsioDriver->init(info->sysRef)) + { + theAsioDriver->getErrorMessage(info->errorMessage); + theAsioDriver = 0; + return ASE_NotPresent; + } + + strcpy(info->errorMessage, "No ASIO Driver Error"); + theAsioDriver->getDriverName(info->name); + info->driverVersion = theAsioDriver->getDriverVersion(); + return ASE_OK; + } + return ASE_NotPresent; + +#endif // !MAC +} + +ASIOError ASIOExit(void) +{ + if(theAsioDriver) + { +#if WINDOWS + if (asioDrivers) + { + asioDrivers->removeCurrentDriver(); + } +#else + delete theAsioDriver; +#endif + } + theAsioDriver = 0; + return ASE_OK; +} + +ASIOError ASIOStart(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->start(); +} + +ASIOError ASIOStop(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->stop(); +} + +ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels) +{ + if(!theAsioDriver) + { + *numInputChannels = *numOutputChannels = 0; + return ASE_NotPresent; + } + return theAsioDriver->getChannels(numInputChannels, numOutputChannels); +} + +ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency) +{ + if(!theAsioDriver) + { + *inputLatency = *outputLatency = 0; + return ASE_NotPresent; + } + return theAsioDriver->getLatencies(inputLatency, outputLatency); +} + +ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) +{ + if(!theAsioDriver) + { + *minSize = *maxSize = *preferredSize = *granularity = 0; + return ASE_NotPresent; + } + return theAsioDriver->getBufferSize(minSize, maxSize, preferredSize, granularity); +} + +ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->canSampleRate(sampleRate); +} + +ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->getSampleRate(currentRate); +} + +ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->setSampleRate(sampleRate); +} + +ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources) +{ + if(!theAsioDriver) + { + *numSources = 0; + return ASE_NotPresent; + } + return theAsioDriver->getClockSources(clocks, numSources); +} + +ASIOError ASIOSetClockSource(long reference) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->setClockSource(reference); +} + +ASIOError ASIOGetSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->getSamplePosition(sPos, tStamp); +} + +ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info) +{ + if(!theAsioDriver) + { + info->channelGroup = -1; + info->type = ASIOSTInt16MSB; + strcpy(info->name, "None"); + return ASE_NotPresent; + } + return theAsioDriver->getChannelInfo(info); +} + +ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks) +{ + if(!theAsioDriver) + { + ASIOBufferInfo *info = bufferInfos; + for(long i = 0; i < numChannels; i++, info++) + info->buffers[0] = info->buffers[1] = 0; + return ASE_NotPresent; + } + return theAsioDriver->createBuffers(bufferInfos, numChannels, bufferSize, callbacks); +} + +ASIOError ASIODisposeBuffers(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->disposeBuffers(); +} + +ASIOError ASIOControlPanel(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->controlPanel(); +} + +ASIOError ASIOFuture(long selector, void *opt) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->future(selector, opt); +} + +ASIOError ASIOOutputReady(void) +{ + if(!theAsioDriver) + return ASE_NotPresent; + return theAsioDriver->outputReady(); +} + +#if MAC +} // extern "C" +#pragma export off +#endif + + diff --git a/win32/src/asio/asio.h b/win32/src/asio/asio.h new file mode 100755 index 0000000..242e34c --- /dev/null +++ b/win32/src/asio/asio.h @@ -0,0 +1,963 @@ +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +/* + Steinberg Audio Stream I/O API + (c) 1997 - 1999, Steinberg Soft- und Hardware GmbH + + ASIO Interface Specification v 2.0 + + basic concept is an i/o synchronous double-buffer scheme: + + on bufferSwitch(index == 0), host will read/write: + + after ASIOStart(), the + read first input buffer A (index 0) + | will be invalid (empty) + * ------------------------ + |------------------------|-----------------------| + | | | + | Input Buffer A (0) | Input Buffer B (1) | + | | | + |------------------------|-----------------------| + | | | + | Output Buffer A (0) | Output Buffer B (1) | + | | | + |------------------------|-----------------------| + * ------------------------- + | before calling ASIOStart(), + write host will have filled output + buffer B (index 1) already + + *please* take special care of proper statement of input + and output latencies (see ASIOGetLatencies()), these + control sequencer sync accuracy + +*/ + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +/* + +prototypes summary: + +ASIOError ASIOInit(ASIODriverInfo *info); +ASIOError ASIOExit(void); +ASIOError ASIOStart(void); +ASIOError ASIOStop(void); +ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels); +ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency); +ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); +ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate); +ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate); +ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate); +ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources); +ASIOError ASIOSetClockSource(long reference); +ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); +ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info); +ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks); +ASIOError ASIODisposeBuffers(void); +ASIOError ASIOControlPanel(void); +void *ASIOFuture(long selector, void *params); +ASIOError ASIOOutputReady(void); + +*/ + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +#ifndef __ASIO_H +#define __ASIO_H + +// force 4 byte alignment +#if defined(_MSC_VER) && !defined(__MWERKS__) +#pragma pack(push,4) +#elif PRAGMA_ALIGN_SUPPORTED +#pragma options align = native +#endif + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Type definitions +//- - - - - - - - - - - - - - - - - - - - - - - - - + +// number of samples data type is 64 bit integer +#if NATIVE_INT64 + typedef long long int ASIOSamples; +#else + typedef struct ASIOSamples { + unsigned long hi; + unsigned long lo; + } ASIOSamples; +#endif + +// Timestamp data type is 64 bit integer, +// Time format is Nanoseconds. +#if NATIVE_INT64 + typedef long long int ASIOTimeStamp ; +#else + typedef struct ASIOTimeStamp { + unsigned long hi; + unsigned long lo; + } ASIOTimeStamp; +#endif + +// Samplerates are expressed in IEEE 754 64 bit double float, +// native format as host computer +#if IEEE754_64FLOAT + typedef double ASIOSampleRate; +#else + typedef struct ASIOSampleRate { + char ieee[8]; + } ASIOSampleRate; +#endif + +// Boolean values are expressed as long +typedef long ASIOBool; +enum { + ASIOFalse = 0, + ASIOTrue = 1 +}; + +// Sample Types are expressed as long +typedef long ASIOSampleType; +enum { + ASIOSTInt16MSB = 0, + ASIOSTInt24MSB = 1, // used for 20 bits as well + ASIOSTInt32MSB = 2, + ASIOSTFloat32MSB = 3, // IEEE 754 32 bit float + ASIOSTFloat64MSB = 4, // IEEE 754 64 bit double float + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can be more easily used with these + ASIOSTInt32MSB16 = 8, // 32 bit data with 18 bit alignment + ASIOSTInt32MSB18 = 9, // 32 bit data with 18 bit alignment + ASIOSTInt32MSB20 = 10, // 32 bit data with 20 bit alignment + ASIOSTInt32MSB24 = 11, // 32 bit data with 24 bit alignment + + ASIOSTInt16LSB = 16, + ASIOSTInt24LSB = 17, // used for 20 bits as well + ASIOSTInt32LSB = 18, + ASIOSTFloat32LSB = 19, // IEEE 754 32 bit float, as found on Intel x86 architecture + ASIOSTFloat64LSB = 20, // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + ASIOSTInt32LSB16 = 24, // 32 bit data with 18 bit alignment + ASIOSTInt32LSB18 = 25, // 32 bit data with 18 bit alignment + ASIOSTInt32LSB20 = 26, // 32 bit data with 20 bit alignment + ASIOSTInt32LSB24 = 27 // 32 bit data with 24 bit alignment +}; + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Error codes +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef long ASIOError; +enum { + ASE_OK = 0, // This value will be returned whenever the call succeeded + ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls + ASE_NotPresent = -1000, // hardware input or output is not present or available + ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function) + ASE_InvalidParameter, // input parameter invalid + ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode + ASE_SPNotAdvancing, // hardware is not running when sample position is inquired + ASE_NoClock, // sample clock or rate cannot be determined or is not present + ASE_NoMemory // not enough memory for completing the request +}; + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Time Info support +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIOTimeCode +{ + double speed; // speed relation (fraction of nominal speed) + // optional; set to 0. or 1. if not supported + ASIOSamples timeCodeSamples; // time in samples + unsigned long flags; // some information flags (see below) + char future[64]; +} ASIOTimeCode; + +typedef enum ASIOTimeCodeFlags +{ + kTcValid = 1, + kTcRunning = 1 << 1, + kTcReverse = 1 << 2, + kTcOnspeed = 1 << 3, + kTcStill = 1 << 4, + + kTcSpeedValid = 1 << 8 +} ASIOTimeCodeFlags; + +typedef struct AsioTimeInfo +{ + double speed; // absolute speed (1. = nominal) + ASIOTimeStamp systemTime; // system time related to samplePosition, in nanoseconds + // on mac, must be derived from Microseconds() (not UpTime()!) + // on windows, must be derived from timeGetTime() + ASIOSamples samplePosition; + ASIOSampleRate sampleRate; // current rate + unsigned long flags; // (see below) + char reserved[12]; +} AsioTimeInfo; + +typedef enum AsioTimeInfoFlags +{ + kSystemTimeValid = 1, // must always be valid + kSamplePositionValid = 1 << 1, // must always be valid + kSampleRateValid = 1 << 2, + kSpeedValid = 1 << 3, + + kSampleRateChanged = 1 << 4, + kClockSourceChanged = 1 << 5 +} AsioTimeInfoFlags; + +typedef struct ASIOTime // both input/output +{ + long reserved[4]; // must be 0 + struct AsioTimeInfo timeInfo; // required + struct ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid) +} ASIOTime; + +#ifdef __cplusplus + extern "C" { +#endif + +/* + +using time info: +it is recommended to use the new method with time info even if the asio +device does not support timecode; continuous calls to ASIOGetSamplePosition +and ASIOGetSampleRate are avoided, and there is a more defined relationship +between callback time and the time info. + +see the example below. +to initiate time info mode, after you have received the callbacks pointer in +ASIOCreateBuffers, you will call the asioMessage callback with kAsioSupportsTimeInfo +as the argument. if this returns 1, host has accepted time info mode. +now host expects the new callback bufferSwitchTimeInfo to be used instead +of the old bufferSwitch method. the ASIOTime structure is assumed to be valid +and accessible until the callback returns. + +using time code: +if the device supports reading time code, it will call host's asioMessage callback +with kAsioSupportsTimeCode as the selector. it may then fill the according +fields and set the kTcValid flag. +host will call the future method with the kAsioEnableTimeCodeRead selector when +it wants to enable or disable tc reading by the device. you should also support +the kAsioCanTimeInfo and kAsioCanTimeCode selectors in ASIOFuture (see example). + +note: +the AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions. +as a matter of convention, the relationship between the sample +position counter and the time code at buffer switch time is +(ignoring offset between tc and sample pos when tc is running): + +on input: sample 0 -> input buffer sample 0 -> time code 0 +on output: sample 0 -> output buffer sample 0 -> time code 0 + +this means that for 'real' calculations, one has to take into account +the according latencies. + +example: + +ASIOTime asioTime; + +in createBuffers() +{ + FMOD_memset(&asioTime, 0, sizeof(ASIOTime)); + AsioTimeInfo* ti = &asioTime.timeInfo; + ti->sampleRate = theSampleRate; + ASIOTimeCode* tc = &asioTime.timeCode; + tc->speed = 1.; + timeInfoMode = false; + canTimeCode = false; + if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1) + { + timeInfoMode = true; +#if kCanTimeCode + if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1) + canTimeCode = true; +#endif + } +} + +void switchBuffers(long doubleBufferIndex, bool processNow) +{ + if(timeInfoMode) + { + AsioTimeInfo* ti = &asioTime.timeInfo; + ti->flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; + ti->systemTime = theNanoSeconds; + ti->samplePosition = theSamplePosition; + if(ti->sampleRate != theSampleRate) + ti->flags |= kSampleRateChanged; + ti->sampleRate = theSampleRate; + +#if kCanTimeCode + if(canTimeCode && timeCodeEnabled) + { + ASIOTimeCode* tc = &asioTime.timeCode; + tc->timeCodeSamples = tcSamples; // tc in samples + tc->flags = kTcValid | kTcRunning | kTcOnspeed; // if so... + } + ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse); +#else + callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse); +#endif + } + else + callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse); +} + +ASIOError ASIOFuture(long selector, void *params) +{ + switch(selector) + { + case kAsioEnableTimeCodeRead: + timeCodeEnabled = true; + return ASE_SUCCESS; + case kAsioDisableTimeCodeRead: + timeCodeEnabled = false; + return ASE_SUCCESS; + case kAsioCanTimeInfo: + return ASE_SUCCESS; + #if kCanTimeCode + case kAsioCanTimeCode: + return ASE_SUCCESS; + #endif + } + return ASE_NotPresent; +}; + +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// application's audio stream handler callbacks +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIOCallbacks +{ + void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess); + // bufferSwitch indicates that both input and output are to be processed. + // the current buffer half index (0 for A, 1 for B) determines + // - the output buffer that the host should start to fill. the other buffer + // will be passed to output hardware regardless of whether it got filled + // in time or not. + // - the input buffer that is now filled with incoming data. Note that + // because of the synchronicity of i/o, the input always has at + // least one buffer latency in relation to the output. + // directProcess suggests to the host whether it should immedeately + // start processing (directProcess == ASIOTrue), or whether its process + // should be deferred because the call comes from a very low level + // (for instance, a high level priority interrupt), and direct processing + // would cause timing instabilities for the rest of the system. If in doubt, + // directProcess should be set to ASIOFalse. + // Note: bufferSwitch may be called at interrupt time for highest efficiency. + + void (*sampleRateDidChange) (ASIOSampleRate sRate); + // gets called when the AudioStreamIO detects a sample rate change + // If sample rate is unknown, 0 is passed (for instance, clock loss + // when externally synchronized). + + long (*asioMessage) (long selector, long value, void* message, double* opt); + // generic callback for various purposes, see selectors below. + // note this is only present if the asio version is 2 or higher + + ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess); + // new callback with time info. makes ASIOGetSamplePosition() and various + // calls to ASIOGetSampleRate obsolete, + // and allows for timecode sync etc. to be preferred; will be used if + // the driver calls asioMessage with selector kAsioSupportsTimeInfo. +} ASIOCallbacks; + +// asioMessage selectors +enum +{ + kAsioSelectorSupported = 1, // selector in <value>, returns 1L if supported, + // 0 otherwise + kAsioEngineVersion, // returns engine (host) asio implementation version, + // 2 or higher + kAsioResetRequest, // request driver reset. if accepted, this + // will close the driver (ASIO_Exit() ) and + // re-open it again (ASIO_Init() etc). some + // drivers need to reconfigure for instance + // when the sample rate changes, or some basic + // changes have been made in ASIO_ControlPanel(). + // returns 1L; note the request is merely passed + // to the application, there is no way to determine + // if it gets accepted at this time (but it usually + // will be). + kAsioBufferSizeChange, // not yet supported, will currently always return 0L. + // for now, use kAsioResetRequest instead. + // once implemented, the new buffer size is expected + // in <value>, and on success returns 1L + kAsioResyncRequest, // the driver went out of sync, such that + // the timestamp is no longer valid. this + // is a request to re-start the engine and + // slave devices (sequencer). returns 1 for ok, + // 0 if not supported. + kAsioLatenciesChanged, // the drivers latencies have changed. The engine + // will refetch the latencies. + kAsioSupportsTimeInfo, // if host returns true here, it will expect the + // callback bufferSwitchTimeInfo to be called instead + // of bufferSwitch + kAsioSupportsTimeCode, // supports time code reading/writing + + kAsioSupportsInputMonitor, // supports input monitoring + + kAsioNumMessageSelectors +}; + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// (De-)Construction +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIODriverInfo +{ + long asioVersion; // currently, 2 + long driverVersion; // driver specific + char name[32]; + char errorMessage[124]; + void *sysRef; // on input: system reference + // (Windows: application main window handle, Mac & SGI: 0) +} ASIODriverInfo; + +ASIOError ASIOInit(ASIODriverInfo *info); +/* Purpose: + Initialize the AudioStreamIO. + Parameter: + info: pointer to an ASIODriver structure: + - asioVersion: + - on input, the host version. *** Note *** this is 0 for earlier asio + implementations, and the asioMessage callback is implemeted + only if asioVersion is 2 or greater. sorry but due to a design fault + the driver doesn't have access to the host version in ASIOInit :-( + added selector for host (engine) version in the asioMessage callback + so we're ok from now on. + - on return, asio implementation version. + older versions are 1 + if you support this version (namely, ASIO_outputReady() ) + this should be 2 or higher. also see the note in + ASIO_getTimeStamp() ! + - version: on return, the driver version (format is driver specific) + - name: on return, a null-terminated string containing the driver's name + - error message: on return, should contain a user message describing + the type of error that occured during ASIOInit(), if any. + - sysRef: platform specific + Returns: + If neither input nor output is present ASE_NotPresent + will be returned. + ASE_NoMemory, ASE_HWMalfunction are other possible error conditions +*/ + +ASIOError ASIOExit(void); +/* Purpose: + Terminates the AudioStreamIO. + Parameter: + None. + Returns: + If neither input nor output is present ASE_NotPresent + will be returned. + Notes: this implies ASIOStop() and ASIODisposeBuffers(), + meaning that no host callbacks must be accessed after ASIOExit(). +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Start/Stop +//- - - - - - - - - - - - - - - - - - - - - - - - - + +ASIOError ASIOStart(void); +/* Purpose: + Start input and output processing synchronously. + This will + - reset the sample counter to zero + - start the hardware (both input and output) + The first call to the hosts' bufferSwitch(index == 0) then tells + the host to read from input buffer A (index 0), and start + processing to output buffer A while output buffer B (which + has been filled by the host prior to calling ASIOStart()) + is possibly sounding (see also ASIOGetLatencies()) + Parameter: + None. + Returns: + If neither input nor output is present, ASE_NotPresent + will be returned. + If the hardware fails to start, ASE_HWMalfunction will be returned. + Notes: + There is no restriction on the time that ASIOStart() takes + to perform (that is, it is not considered a realtime trigger). +*/ + +ASIOError ASIOStop(void); +/* Purpose: + Stops input and output processing altogether. + Parameter: + None. + Returns: + If neither input nor output is present ASE_NotPresent + will be returned. + Notes: + On return from ASIOStop(), the driver must in no + case call the hosts' bufferSwitch() routine. +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Inquiry methods and sample rate +//- - - - - - - - - - - - - - - - - - - - - - - - - + +ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels); +/* Purpose: + Returns number of individual input/output channels. + Parameter: + numInputChannels will hold the number of available input channels + numOutputChannels will hold the number of available output channels + Returns: + If no input/output is present ASE_NotPresent will be returned. + If only inputs, or only outputs are available, the according + other parameter will be zero, and ASE_OK is returned. +*/ + +ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency); +/* Purpose: + Returns the input and output latencies. This includes + device specific delays, like FIFOs etc. + Parameter: + inputLatency will hold the 'age' of the first sample frame + in the input buffer when the hosts reads it in bufferSwitch() + (this is theoretical, meaning it does not include the overhead + and delay between the actual physical switch, and the time + when bufferSitch() enters). + This will usually be the size of one block in sample frames, plus + device specific latencies. + + outputLatency will specify the time between the buffer switch, + and the time when the next play buffer will start to sound. + The next play buffer is defined as the one the host starts + processing after (or at) bufferSwitch(), indicated by the + index parameter (0 for buffer A, 1 for buffer B). + It will usually be either one block, if the host writes directly + to a dma buffer, or two or more blocks if the buffer is 'latched' by + the driver. As an example, on ASIOStart(), the host will have filled + the play buffer at index 1 already; when it gets the callback (with + the parameter index == 0), this tells it to read from the input + buffer 0, and start to fill the play buffer 0 (assuming that now + play buffer 1 is already sounding). In this case, the output + latency is one block. If the driver decides to copy buffer 1 + at that time, and pass it to the hardware at the next slot (which + is most commonly done, but should be avoided), the output latency + becomes two blocks instead, resulting in a total i/o latency of at least + 3 blocks. As memory access is the main bottleneck in native dsp processing, + and to acheive less latency, it is highly recommended to try to avoid + copying (this is also why the driver is the owner of the buffers). To + summarize, the minimum i/o latency can be acheived if the input buffer + is processed by the host into the output buffer which will physically + start to sound on the next time slice. Also note that the host expects + the bufferSwitch() callback to be accessed for each time slice in order + to retain sync, possibly recursively; if it fails to process a block in + time, it will suspend its operation for some time in order to recover. + Returns: + If no input/output is present ASE_NotPresent will be returned. +*/ + +ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); +/* Purpose: + Returns min, max, and preferred buffer sizes for input/output + Parameter: + minSize will hold the minimum buffer size + maxSize will hold the maxium possible buffer size + preferredSize will hold the preferred buffer size (a size which + best fits performance and hardware requirements) + granularity will hold the granularity at which buffer sizes + may differ. Usually, the buffer size will be a power of 2; + in this case, granularity will hold -1 on return, signalling + possible buffer sizes starting from minSize, increased in + powers of 2 up to maxSize. + Returns: + If no input/output is present ASE_NotPresent will be returned. + Notes: + When minimum and maximum buffer size are equal, + the preferred buffer size has to be the same value as well; granularity + should be 0 in this case. +*/ + +ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate); +/* Purpose: + Inquires the hardware for the available sample rates. + Parameter: + sampleRate is the rate in question. + Returns: + If the inquired sample rate is not supported, ASE_NoClock will be returned. + If no input/output is present ASE_NotPresent will be returned. +*/ +ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate); +/* Purpose: + Get the current sample Rate. + Parameter: + currentRate will hold the current sample rate on return. + Returns: + If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned. + If no input/output is present ASE_NotPresent will be returned. + Notes: +*/ + +ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate); +/* Purpose: + Set the hardware to the requested sample Rate. If sampleRate == 0, + enable external sync. + Parameter: + sampleRate: on input, the requested rate + Returns: + If sampleRate is unknown ASE_NoClock will be returned. + If the current clock is external, and sampleRate is != 0, + ASE_InvalidMode will be returned + If no input/output is present ASE_NotPresent will be returned. + Notes: +*/ + +typedef struct ASIOClockSource +{ + long index; // as used for ASIOSetClockSource() + long associatedChannel; // for instance, S/PDIF or AES/EBU + long associatedGroup; // see channel groups (ASIOGetChannelInfo()) + ASIOBool isCurrentSource; // ASIOTrue if this is the current clock source + char name[32]; // for user selection +} ASIOClockSource; + +ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources); +/* Purpose: + Get the available external audio clock sources + Parameter: + clocks points to an array of ASIOClockSource structures: + - index: this is used to identify the clock source + when ASIOSetClockSource() is accessed, should be + an index counting from zero + - associatedInputChannel: the first channel of an associated + input group, if any. + - associatedGroup: the group index of that channel. + groups of channels are defined to seperate for + instance analog, S/PDIF, AES/EBU, ADAT connectors etc, + when present simultaniously. Note that associated channel + is enumerated according to numInputs/numOutputs, means it + is independant from a group (see also ASIOGetChannelInfo()) + inputs are associated to a clock if the physical connection + transfers both data and clock (like S/PDIF, AES/EBU, or + ADAT inputs). if there is no input channel associated with + the clock source (like Word Clock, or internal oscillator), both + associatedChannel and associatedGroup should be set to -1. + - isCurrentSource: on exit, ASIOTrue if this is the current clock + source, ASIOFalse else + - name: a null-terminated string for user selection of the available sources. + numSources: + on input: the number of allocated array members + on output: the number of available clock sources, at least + 1 (internal clock generator). + Returns: + If no input/output is present ASE_NotPresent will be returned. + Notes: +*/ + +ASIOError ASIOSetClockSource(long index); +/* Purpose: + Set the audio clock source + Parameter: + index as obtained from an inquiry to ASIOGetClockSources() + Returns: + If no input/output is present ASE_NotPresent will be returned. + If the clock can not be selected because an input channel which + carries the current clock source is active, ASE_InvalidMode + *may* be returned (this depends on the properties of the driver + and/or hardware). + Notes: + Should *not* return ASE_NoClock if there is no clock signal present + at the selected source; this will be inquired via ASIOGetSampleRate(). + It should call the host callback procedure sampleRateHasChanged(), + if the switch causes a sample rate change, or if no external clock + is present at the selected source. +*/ + +ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); +/* Purpose: + Inquires the sample position/time stamp pair. + Parameter: + sPos will hold the sample position on return. The sample + position is reset to zero when ASIOStart() gets called. + tStamp will hold the system time when the sample position + was latched. + Returns: + If no input/output is present, ASE_NotPresent will be returned. + If there is no clock, ASE_SPNotAdvancing will be returned. + Notes: + + in order to be able to synchronise properly, + the sample position / time stamp pair must refer to the current block, + that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch() + callback and expect the time for the current block. thus, when requested + in the very first bufferSwitch after ASIO_Start(), the sample position + should be zero, and the time stamp should refer to the very time where + the stream was started. it also means that the sample position must be + block aligned. the driver must ensure proper interpolation if the system + time can not be determined for the block position. the driver is responsible + for precise time stamps as it usually has most direct access to lower + level resources. proper behaviour of ASIO_GetSamplePosition() and ASIO_GetLatencies() + are essential for precise media synchronization! +*/ + +typedef struct ASIOChannelInfo +{ + long channel; // on input, channel index + ASIOBool isInput; // on input + ASIOBool isActive; // on exit + long channelGroup; // dto + ASIOSampleType type; // dto + char name[32]; // dto +} ASIOChannelInfo; + +ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info); +/* Purpose: + retreive information about the nature of a channel + Parameter: + info: pointer to a ASIOChannelInfo structure with + - channel: on input, the channel index of the channel in question. + - isInput: on input, ASIOTrue if info for an input channel is + requested, else output + - channelGroup: on return, the channel group that the channel + belongs to. For drivers which support different types of + channels, like analog, S/PDIF, AES/EBU, ADAT etc interfaces, + there should be a reasonable grouping of these types. Groups + are always independant form a channel index, that is, a channel + index always counts from 0 to numInputs/numOutputs regardless + of the group it may belong to. + There will always be at least one group (group 0). Please + also note that by default, the host may decide to activate + channels 0 and 1; thus, these should belong to the most + useful type (analog i/o, if present). + - type: on return, contains the sample type of the channel + - isActive: on return, ASIOTrue if channel is active as it was + installed by ASIOCreateBuffers(), ASIOFalse else + - name: describing the type of channel in question. Used to allow + for user selection, and enabling of specific channels. examples: + "Analog In", "SPDIF Out" etc + Returns: + If no input/output is present ASE_NotPresent will be returned. + Notes: + If possible, the string should be organised such that the first + characters are most significantly describing the nature of the + port, to allow for identification even if the view showing the + port name is too small to display more than 8 characters, for + instance. +*/ + +//- - - - - - - - - - - - - - - - - - - - - - - - - +// Buffer preparation +//- - - - - - - - - - - - - - - - - - - - - - - - - + +typedef struct ASIOBufferInfo +{ + ASIOBool isInput; // on input: ASIOTrue: input, else output + long channelNum; // on input: channel index + void *buffers[2]; // on output: double buffer addresses +} ASIOBufferInfo; + +ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks); + +/* Purpose: + Allocates input/output buffers for all input and output channels to be activated. + Parameter: + bufferInfos is a pointer to an array of ASIOBufferInfo structures: + - isInput: on input, ASIOTrue if the buffer is to be allocated + for an input, output buffer else + - channelNum: on input, the index of the channel in question + (counting from 0) + - buffers: on exit, 2 pointers to the halves of the channels' double-buffer. + the size of the buffer(s) of course depend on both the ASIOSampleType + as obtained from ASIOGetChannelInfo(), and bufferSize + numChannels is the sum of all input and output channels to be created; + thus bufferInfos is a pointer to an array of numChannels ASIOBufferInfo + structures. + bufferSize selects one of the possible buffer sizes as obtained from + ASIOGetBufferSizes(). + callbacks is a pointer to an ASIOCallbacks structure. + Returns: + If not enough memory is available ASE_NoMemory will be returned. + If no input/output is present ASE_NotPresent will be returned. + If bufferSize is not supported, or one or more of the bufferInfos elements + contain invalid settings, ASE_InvalidMode will be returned. + Notes: + If individual channel selection is not possible but requested, + the driver has to handle this. namely, bufferSwitch() will only + have filled buffers of enabled outputs. If possible, processing + and buss activities overhead should be avoided for channels which + were not enabled here. +*/ + +ASIOError ASIODisposeBuffers(void); +/* Purpose: + Releases all buffers for the device. + Parameter: + None. + Returns: + If no buffer were ever prepared, ASE_InvalidMode will be returned. + If no input/output is present ASE_NotPresent will be returned. + Notes: + This implies ASIOStop(). +*/ + +ASIOError ASIOControlPanel(void); +/* Purpose: + request the driver to start a control panel component + for device specific user settings. This will not be + accessed on some platforms (where the component is accessed + instead). + Parameter: + None. + Returns: + If no panel is available ASE_NotPresent will be returned. + Actually, the return code is ignored. + Notes: + if the user applied settings which require a re-configuration + of parts or all of the enigine and/or driver (such as a change of + the block size), the asioMessage callback can be used (see + ASIO_Callbacks). +*/ + +ASIOError ASIOFuture(long selector, void *params); +/* Purpose: + various + Parameter: + selector: operation Code as to be defined. zero is reserved for + testing purposes. + params: depends on the selector; usually pointer to a structure + for passing and retreiving any type and amount of parameters. + Returns: + the return value is also selector dependant. if the selector + is unknown, ASE_InvalidParameter should be returned to prevent + further calls with this selector. on success, ASE_SUCCESS + must be returned (note: ASE_OK is *not* sufficient!) + Notes: + see selectors defined below. +*/ + +enum +{ + kAsioEnableTimeCodeRead = 1, // no arguments + kAsioDisableTimeCodeRead, // no arguments + kAsioSetInputMonitor, // ASIOInputMonitor* in params + kAsioTransport, // ASIOTransportParameters* in params + kAsioSetInputGain, // ASIOChannelControls* in params, apply gain + kAsioGetInputMeter, // ASIOChannelControls* in params, fill meter + kAsioSetOutputGain, // ASIOChannelControls* in params, apply gain + kAsioGetOutputMeter, // ASIOChannelControls* in params, fill meter + kAsioCanInputMonitor, // no arguments for kAsioCanXXX selectors + kAsioCanTimeInfo, + kAsioCanTimeCode, + kAsioCanTransport, + kAsioCanInputGain, + kAsioCanInputMeter, + kAsioCanOutputGain, + kAsioCanOutputMeter +}; + +typedef struct ASIOInputMonitor +{ + long input; // this input was set to monitor (or off), -1: all + long output; // suggested output for monitoring the input (if so) + long gain; // suggested gain, ranging 0 - 0x7fffffffL (-inf to +12 dB) + ASIOBool state; // ASIOTrue => on, ASIOFalse => off + long pan; // suggested pan, 0 => all left, 0x7fffffff => right +} ASIOInputMonitor; + +typedef struct ASIOChannelControls +{ + long channel; // on input, channel index + ASIOBool isInput; // on input + long gain; // on input, ranges 0 thru 0x7fffffff + long meter; // on return, ranges 0 thru 0x7fffffff + char future[32]; +} ASIOChannelControls; + +typedef struct ASIOTransportParameters +{ + long command; // see enum below + ASIOSamples samplePosition; + long track; + long trackSwitches[16]; // 512 tracks on/off + char future[64]; +} ASIOTransportParameters; + +enum +{ + kTransStart = 1, + kTransStop, + kTransLocate, // to samplePosition + kTransPunchIn, + kTransPunchOut, + kTransArmOn, // track + kTransArmOff, // track + kTransMonitorOn, // track + kTransMonitorOff, // track + kTransArm, // trackSwitches + kTransMonitor // trackSwitches +}; + +ASIOError ASIOOutputReady(void); +/* Purpose: + this tells the driver that the host has completed processing + the output buffers. if the data format required by the hardware + differs from the supported asio formats, but the hardware + buffers are DMA buffers, the driver will have to convert + the audio stream data; as the bufferSwitch callback is + usually issued at dma block switch time, the driver will + have to convert the *previous* host buffer, which increases + the output latency by one block. + when the host finds out that ASIOOutputReady() returns + true, it will issue this call whenever it completed + output processing. then the driver can convert the + host data directly to the dma buffer to be played next, + reducing output latency by one block. + another way to look at it is, that the buffer switch is called + in order to pass the *input* stream to the host, so that it can + process the input into the output, and the output stream is passed + to the driver when the host has completed its process. + Parameter: + None + Returns: + only if the above mentioned scenario is given, and a reduction + of output latency can be acheived by this mechanism, should + ASE_OK be returned. otherwise (and usually), ASE_NotPresent + should be returned in order to prevent further calls to this + function. note that the host may want to determine if it is + to use this when the system is not yet fully initialized, so + ASE_OK should always be returned if the mechanism makes sense. + Notes: + please remeber to adjust ASIOGetLatencies() according to + whether ASIOOutputReady() was ever called or not, if your + driver supports this scenario. + also note that the engine may fail to call ASIO_OutputReady() + in time in overload cases. as already mentioned, bufferSwitch + should be called for every block regardless of whether a block + could be processed in time. +*/ + +#ifdef __cplusplus + } +#endif + +// restore old alignment +#if defined(_MSC_VER) && !defined(__MWERKS__) +#pragma pack(pop) +#elif PRAGMA_ALIGN_SUPPORTED +#pragma options align = reset +#endif + +#endif + diff --git a/win32/src/asio/asiodrivers.cpp b/win32/src/asio/asiodrivers.cpp new file mode 100755 index 0000000..d524217 --- /dev/null +++ b/win32/src/asio/asiodrivers.cpp @@ -0,0 +1,190 @@ +#include "asiodrivers.h" + +AsioDrivers* asioDrivers = 0; + +bool loadAsioDriver(char *name); + +bool loadAsioDriver(char *name) +{ + if(!asioDrivers) + asioDrivers = new AsioDrivers(); + if(asioDrivers) + return asioDrivers->loadDriver(name); + return false; +} + +//------------------------------------------------------------------------------------ + +#if MAC + +bool resolveASIO(unsigned long aconnID); + +AsioDrivers::AsioDrivers() : CodeFragments("ASIO Drivers", 'AsDr', 'Asio') +{ + connID = -1; + curIndex = -1; +} + +bool AsioDrivers::close() +{ + removeCurrentDriver(); + + return true; +} + +bool AsioDrivers::getCurrentDriverName(char *name) +{ + if(curIndex >= 0) + return getName(curIndex, name); + return false; +} + +long AsioDrivers::getDriverNames(char **names, long maxDrivers) +{ + for(long i = 0; i < getNumFragments() && i < maxDrivers; i++) + getName(i, names[i]); + return getNumFragments() < maxDrivers ? getNumFragments() : maxDrivers; +} + +bool AsioDrivers::loadDriver(char *name) +{ + char dname[64]; + unsigned long newID; + + for(long i = 0; i < getNumFragments(); i++) + { + if(getName(i, dname) && !strcmp(name, dname)) + { + if(newInstance(i, &newID)) + { + if(resolveASIO(newID)) + { + if(connID != -1) + removeInstance(curIndex, connID); + curIndex = i; + connID = newID; + return true; + } + } + break; + } + } + return false; +} + +void AsioDrivers::removeCurrentDriver() +{ + if(connID != -1) + removeInstance(curIndex, connID); + connID = -1; + curIndex = -1; +} + +//------------------------------------------------------------------------------------ + +#elif WINDOWS + +#include "iasiodrv.h" + +extern IASIO* theAsioDriver; + +AsioDrivers::AsioDrivers() : AsioDriverList() +{ + curIndex = -1; +} + +bool AsioDrivers::close() +{ + AsioDriverList::close(); + return true; +} + +bool AsioDrivers::getCurrentDriverName(char *name) +{ + if(curIndex >= 0) + return asioGetDriverName(curIndex, name, 32) == 0 ? true : false; + name[0] = 0; + return false; +} + +long AsioDrivers::getDriverNames(char **names, long maxDrivers) +{ + for(long i = 0; i < asioGetNumDev() && i < maxDrivers; i++) + asioGetDriverName(i, names[i], 32); + return asioGetNumDev() < maxDrivers ? asioGetNumDev() : maxDrivers; +} + +bool AsioDrivers::loadDriver(char *name) +{ + char dname[64]; + char curName[64]; + + for(long i = 0; i < asioGetNumDev(); i++) + { + if(!asioGetDriverName(i, dname, 32) && !strcmp(name, dname)) + { + curName[0] = 0; + getCurrentDriverName(curName); // in case we fail... + removeCurrentDriver(); + + if(!asioOpenDriver(i, (void **)&theAsioDriver)) + { + curIndex = i; + return true; + } + else + { + theAsioDriver = 0; + if(curName[0] && strcmp(dname, curName)) + loadDriver(curName); // try restore + } + break; + } + } + return false; +} + +void AsioDrivers::removeCurrentDriver() +{ + if(curIndex != -1) + asioCloseDriver(curIndex); + curIndex = -1; +} + +#elif SGI || BEOS + +#include "asiolist.h" + +AsioDrivers::AsioDrivers() + : AsioDriverList() +{ + curIndex = -1; +} + +bool AsioDrivers::close() +{ + return true; +} + +bool AsioDrivers::getCurrentDriverName(char *name) +{ + return false; +} + +long AsioDrivers::getDriverNames(char **names, long maxDrivers) +{ + return 0; +} + +bool AsioDrivers::loadDriver(char *name) +{ + return false; +} + +void AsioDrivers::removeCurrentDriver() +{ +} + +#else +#error implement me +#endif diff --git a/win32/src/asio/asiodrivers.h b/win32/src/asio/asiodrivers.h new file mode 100755 index 0000000..636e64a --- /dev/null +++ b/win32/src/asio/asiodrivers.h @@ -0,0 +1,41 @@ +#ifndef __AsioDrivers__ +#define __AsioDrivers__ + +#include "ginclude.h" + +#if MAC +#include "CodeFragments.hpp" + +class AsioDrivers : public CodeFragments + +#elif WINDOWS +#include <windows.h> +#include "asiolist.h" + +class AsioDrivers : public AsioDriverList + +#elif SGI || BEOS +#include "asiolist.h" + +class AsioDrivers : public AsioDriverList + +#else +#error implement me +#endif + +{ +public: + AsioDrivers(); + bool close(); + + bool getCurrentDriverName(char *name); + long getDriverNames(char **names, long maxDrivers); + bool loadDriver(char *name); + void removeCurrentDriver(); + long getCurrentDriverIndex() {return curIndex;} +protected: + unsigned long connID; + long curIndex; +}; + +#endif diff --git a/win32/src/asio/asiolist.cpp b/win32/src/asio/asiolist.cpp new file mode 100755 index 0000000..f8aac50 --- /dev/null +++ b/win32/src/asio/asiolist.cpp @@ -0,0 +1,273 @@ +#include <windows.h> +#include "iasiodrv.h" +#include "asiolist.h" + +#include "fmod_memory.h" + +#define ASIODRV_DESC "description" +#define INPROC_SERVER "InprocServer32" +#define ASIO_PATH "software\\asio" +#define COM_CLSID "clsid" + +// ****************************************************************** +// Local Functions +// ****************************************************************** +static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize) +{ + HKEY hkEnum,hksub,hkpath; + char databuf[512]; + LONG cr,rc = -1; + DWORD datatype,datasize; + DWORD index; + OFSTRUCT ofs; + HFILE hfile; + BOOL found = FALSE; + + CharLowerBuff(clsidstr,(DWORD)strlen(clsidstr)); + if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) { + + index = 0; + while (cr == ERROR_SUCCESS && !found) { + cr = RegEnumKey(hkEnum,index++,(LPTSTR)databuf,512); + if (cr == ERROR_SUCCESS) { + CharLowerBuff(databuf,(DWORD)strlen(databuf)); + if (!(strcmp(databuf,clsidstr))) { + if ((cr = RegOpenKeyEx(hkEnum,(LPCTSTR)databuf,0,KEY_READ,&hksub)) == ERROR_SUCCESS) { + if ((cr = RegOpenKeyEx(hksub,(LPCTSTR)INPROC_SERVER,0,KEY_READ,&hkpath)) == ERROR_SUCCESS) { + datatype = REG_SZ; datasize = (DWORD)dllpathsize; + cr = RegQueryValueEx(hkpath,0,0,&datatype,(LPBYTE)dllpath,&datasize); + if (cr == ERROR_SUCCESS) { + FMOD_memset(&ofs,0,sizeof(OFSTRUCT)); + ofs.cBytes = sizeof(OFSTRUCT); + hfile = OpenFile(dllpath,&ofs,OF_EXIST); + if (hfile) rc = 0; + } + RegCloseKey(hkpath); + } + RegCloseKey(hksub); + } + found = TRUE; // break out + } + } + } + RegCloseKey(hkEnum); + } + return rc; +} + + +static LPASIODRVSTRUCT newDrvStruct (HKEY hkey,char *keyname,int drvID,LPASIODRVSTRUCT lpdrv) +{ + HKEY hksub; + char databuf[256]; + char dllpath[MAXPATHLEN]; + WORD wData[100]; + CLSID clsid; + DWORD datatype,datasize; + LONG cr,rc; + + if (!lpdrv) { + if ((cr = RegOpenKeyEx(hkey,(LPCTSTR)keyname,0,KEY_READ,&hksub)) == ERROR_SUCCESS) { + + datatype = REG_SZ; datasize = 256; + cr = RegQueryValueEx(hksub,COM_CLSID,0,&datatype,(LPBYTE)databuf,&datasize); + if (cr == ERROR_SUCCESS) { + rc = findDrvPath (databuf,dllpath,MAXPATHLEN); + if (rc == 0) { + lpdrv = FMOD_Object_Calloc(ASIODRVSTRUCT); + if (lpdrv) { + FMOD_memset(lpdrv,0,sizeof(ASIODRVSTRUCT)); + lpdrv->drvID = drvID; + MultiByteToWideChar(CP_ACP,0,(LPCSTR)databuf,-1,(LPWSTR)wData,100); + if ((cr = CLSIDFromString((LPOLESTR)wData,(LPCLSID)&clsid)) == S_OK) { + FMOD_memcpy(&lpdrv->clsid,&clsid,sizeof(CLSID)); + } + + datatype = REG_SZ; datasize = 256; + cr = RegQueryValueEx(hksub,ASIODRV_DESC,0,&datatype,(LPBYTE)databuf,&datasize); + if (cr == ERROR_SUCCESS) { + strcpy(lpdrv->drvname,databuf); + } + else strcpy(lpdrv->drvname,keyname); + } + } + } + RegCloseKey(hksub); + } + } + else lpdrv->next = newDrvStruct(hkey,keyname,drvID+1,lpdrv->next); + + return lpdrv; +} + +static void deleteDrvStruct (LPASIODRVSTRUCT lpdrv) +{ + IASIO *iasio; + + if (lpdrv != 0) { + deleteDrvStruct(lpdrv->next); + if (lpdrv->asiodrv) { + iasio = (IASIO *)lpdrv->asiodrv; + iasio->Release(); + } + FMOD_Memory_Free(lpdrv); + } +} + + +static LPASIODRVSTRUCT getDrvStruct (int drvID,LPASIODRVSTRUCT lpdrv) +{ + while (lpdrv) { + if (lpdrv->drvID == drvID) return lpdrv; + lpdrv = lpdrv->next; + } + return 0; +} +// ****************************************************************** + + +// ****************************************************************** +// AsioDriverList +// ****************************************************************** +AsioDriverList::AsioDriverList () +{ + HKEY hkEnum = 0; + char keyname[MAXDRVNAMELEN]; + LPASIODRVSTRUCT pdl; + LONG cr; + DWORD index = 0; + BOOL fin = FALSE; + + numdrv = 0; + lpdrvlist = 0; + + cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,&hkEnum); + while (cr == ERROR_SUCCESS) { + if ((cr = RegEnumKey(hkEnum,index++,(LPTSTR)keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) { + lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist); + } + else fin = TRUE; + } + if (hkEnum) RegCloseKey(hkEnum); + + pdl = lpdrvlist; + while (pdl) { + numdrv++; + pdl = pdl->next; + } + + if (numdrv) CoInitialize(0); // initialize COM +} + + +bool AsioDriverList::close() +{ + if (numdrv) { + deleteDrvStruct(lpdrvlist); + CoUninitialize(); + } + + return true; +} + + +LONG AsioDriverList::asioGetNumDev (VOID) +{ + return (LONG)numdrv; +} + + +LONG AsioDriverList::asioOpenDriver (int drvID,LPVOID *asiodrv) +{ + LPASIODRVSTRUCT lpdrv = 0; + long rc; + + if (!asiodrv) return DRVERR_INVALID_PARAM; + + if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) { + if (!lpdrv->asiodrv) { + rc = CoCreateInstance(lpdrv->clsid,0,CLSCTX_INPROC_SERVER,lpdrv->clsid,asiodrv); + if (rc == S_OK) { + lpdrv->asiodrv = *asiodrv; + return 0; + } + // else if (rc == REGDB_E_CLASSNOTREG) + // strcpy (info->messageText, "Driver not registered in the Registration Database!"); + } + else rc = DRVERR_DEVICE_ALREADY_OPEN; + } + else rc = DRVERR_DEVICE_NOT_FOUND; + + return rc; +} + + +LONG AsioDriverList::asioCloseDriver (int drvID) +{ + LPASIODRVSTRUCT lpdrv = 0; + IASIO *iasio; + + if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) { + if (lpdrv->asiodrv) { + iasio = (IASIO *)lpdrv->asiodrv; + iasio->Release(); + lpdrv->asiodrv = 0; + } + } + + return 0; +} + +LONG AsioDriverList::asioGetDriverName (int drvID,char *drvname,int drvnamesize) +{ + LPASIODRVSTRUCT lpdrv = 0; + + if (!drvname) return DRVERR_INVALID_PARAM; + + if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) { + if (strlen(lpdrv->drvname) < (unsigned int)drvnamesize) { + strcpy(drvname,lpdrv->drvname); + } + else { + FMOD_memcpy(drvname,lpdrv->drvname,drvnamesize-4); + drvname[drvnamesize-4] = '.'; + drvname[drvnamesize-3] = '.'; + drvname[drvnamesize-2] = '.'; + drvname[drvnamesize-1] = 0; + } + return 0; + } + return DRVERR_DEVICE_NOT_FOUND; +} + +LONG AsioDriverList::asioGetDriverPath (int drvID,char *dllpath,int dllpathsize) +{ + LPASIODRVSTRUCT lpdrv = 0; + + if (!dllpath) return DRVERR_INVALID_PARAM; + + if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) { + if (strlen(lpdrv->dllpath) < (unsigned int)dllpathsize) { + strcpy(dllpath,lpdrv->dllpath); + return 0; + } + dllpath[0] = 0; + return DRVERR_INVALID_PARAM; + } + return DRVERR_DEVICE_NOT_FOUND; +} + +LONG AsioDriverList::asioGetDriverCLSID (int drvID,CLSID *clsid) +{ + LPASIODRVSTRUCT lpdrv = 0; + + if (!clsid) return DRVERR_INVALID_PARAM; + + if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) { + FMOD_memcpy(clsid,&lpdrv->clsid,sizeof(CLSID)); + return 0; + } + return DRVERR_DEVICE_NOT_FOUND; +} + + diff --git a/win32/src/asio/asiolist.h b/win32/src/asio/asiolist.h new file mode 100755 index 0000000..c781c52 --- /dev/null +++ b/win32/src/asio/asiolist.h @@ -0,0 +1,46 @@ +#ifndef __asiolist__ +#define __asiolist__ + +#define DRVERR -5000 +#define DRVERR_INVALID_PARAM DRVERR-1 +#define DRVERR_DEVICE_ALREADY_OPEN DRVERR-2 +#define DRVERR_DEVICE_NOT_FOUND DRVERR-3 + +#define MAXPATHLEN 512 +#define MAXDRVNAMELEN 128 + +struct asiodrvstruct +{ + int drvID; + CLSID clsid; + char dllpath[MAXPATHLEN]; + char drvname[MAXDRVNAMELEN]; + LPVOID asiodrv; + struct asiodrvstruct *next; +}; + +typedef struct asiodrvstruct ASIODRVSTRUCT; +typedef ASIODRVSTRUCT *LPASIODRVSTRUCT; + +class AsioDriverList { +public: + AsioDriverList(); + bool close(); + + LONG asioOpenDriver (int,VOID **); + LONG asioCloseDriver (int); + + // nice to have + LONG asioGetNumDev (VOID); + LONG asioGetDriverName (int,char *,int); + LONG asioGetDriverPath (int,char *,int); + LONG asioGetDriverCLSID (int,CLSID *); + + // or use directly access + LPASIODRVSTRUCT lpdrvlist; + int numdrv; +}; + +typedef class AsioDriverList *LPASIODRIVERLIST; + +#endif diff --git a/win32/src/asio/asiosys.h b/win32/src/asio/asiosys.h new file mode 100755 index 0000000..a61c233 --- /dev/null +++ b/win32/src/asio/asiosys.h @@ -0,0 +1,82 @@ +#ifndef __asiosys__ + #define __asiosys__ + + #ifdef WIN32 + #undef MAC + #define PPC 0 + #define WINDOWS 1 + #define SGI 0 + #define SUN 0 + #define LINUX 0 + #define BEOS 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #elif BEOS + #define MAC 0 + #define PPC 0 + #define WINDOWS 0 + #define PC 0 + #define SGI 0 + #define SUN 0 + #define LINUX 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #ifndef DEBUG + #define DEBUG 0 + #if DEBUG + void DEBUGGERMESSAGE(char *string); + #else + #define DEBUGGERMESSAGE(a) + #endif + #endif + + #elif SGI + #define MAC 0 + #define PPC 0 + #define WINDOWS 0 + #define PC 0 + #define SUN 0 + #define LINUX 0 + #define BEOS 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #ifndef DEBUG + #define DEBUG 0 + #if DEBUG + void DEBUGGERMESSAGE(char *string); + #else + #define DEBUGGERMESSAGE(a) + #endif + #endif + + #else // MAC + + #define MAC 1 + #define PPC 1 + #define WINDOWS 0 + #define PC 0 + #define SGI 0 + #define SUN 0 + #define LINUX 0 + #define BEOS 0 + + #define NATIVE_INT64 0 + #define IEEE754_64FLOAT 1 + + #ifndef DEBUG + #define DEBUG 0 + #if DEBUG + void DEBUGGERMESSAGE(char *string); + #else + #define DEBUGGERMESSAGE(a) + #endif + #endif + #endif + +#endif diff --git a/win32/src/asio/ginclude.h b/win32/src/asio/ginclude.h new file mode 100755 index 0000000..5603fac --- /dev/null +++ b/win32/src/asio/ginclude.h @@ -0,0 +1,38 @@ +#ifndef __gInclude__ +#define __gInclude__ + +#if SGI + #undef BEOS + #undef MAC + #undef WINDOWS + // + #define ASIO_BIG_ENDIAN 1 + #define ASIO_CPU_MIPS 1 +#elif defined WIN32 + #undef BEOS + #undef MAC + #undef SGI + #define WINDOWS 1 + #define ASIO_LITTLE_ENDIAN 1 + #define ASIO_CPU_X86 1 +#elif BEOS + #undef MAC + #undef SGI + #undef WINDOWS + #define ASIO_LITTLE_ENDIAN 1 + #define ASIO_CPU_X86 1 + // +#else + #define MAC 1 + #undef BEOS + #undef WINDOWS + #undef SGI + #define ASIO_BIG_ENDIAN 1 + #define ASIO_CPU_PPC 1 +#endif + +// always +#define NATIVE_INT64 0 +#define IEEE754_64FLOAT 1 + +#endif // __gInclude__ diff --git a/win32/src/asio/iasiodrv.h b/win32/src/asio/iasiodrv.h new file mode 100755 index 0000000..1496f0f --- /dev/null +++ b/win32/src/asio/iasiodrv.h @@ -0,0 +1,37 @@ +#include "asiosys.h" +#include "asio.h" + +/* Forward Declarations */ + +#ifndef __ASIODRIVER_FWD_DEFINED__ +#define __ASIODRIVER_FWD_DEFINED__ +typedef interface IASIO IASIO; +#endif /* __ASIODRIVER_FWD_DEFINED__ */ + +interface IASIO : public IUnknown +{ + + virtual ASIOBool init(void *sysHandle) = 0; + virtual void getDriverName(char *name) = 0; + virtual long getDriverVersion() = 0; + virtual void getErrorMessage(char *string) = 0; + virtual ASIOError start() = 0; + virtual ASIOError stop() = 0; + virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0; + virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0; + virtual ASIOError getBufferSize(long *minSize, long *maxSize, + long *preferredSize, long *granularity) = 0; + virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0; + virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0; + virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0; + virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0; + virtual ASIOError setClockSource(long reference) = 0; + virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; + virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0; + virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, + long bufferSize, ASIOCallbacks *callbacks) = 0; + virtual ASIOError disposeBuffers() = 0; + virtual ASIOError controlPanel() = 0; + virtual ASIOError future(long selector,void *opt) = 0; + virtual ASIOError outputReady() = 0; +}; diff --git a/win32/src/c32.mac b/win32/src/c32.mac new file mode 100755 index 0000000..1ff9951 --- /dev/null +++ b/win32/src/c32.mac @@ -0,0 +1,59 @@ +; NASM macro set to make interfacing to 32-bit programs easier -*- nasm -*- + + + +%imacro proc 1 ; begin a procedure definition + +%push proc + +%ifdef PLATFORM_LINUX + global _%1:function + global %1:function +%else + global _%1 + global %1 +%endif + +_%1: +%1: push ebp + + mov ebp,esp + +%assign %$arg 8 + +%define %$procname %1 + +%endmacro + + + +%imacro arg 0-1 4 ; used with the argument name as a label + +%00 equ %$arg + +%assign %$arg %1+%$arg + +%endmacro + + + +%imacro endproc 0 + +%ifnctx proc + +%error Mismatched `endproc'/`proc' + +%else + + leave + + ret + +__end_%$procname: ; useful for calculating function size +___end_%$procname: + +%pop + +%endif + +%endmacro diff --git a/win32/src/fmod_3dl2.h b/win32/src/fmod_3dl2.h new file mode 100755 index 0000000..4bfb948 --- /dev/null +++ b/win32/src/fmod_3dl2.h @@ -0,0 +1,211 @@ +// 3DL2.H +// + +#ifndef _3DL2_H_INCLUDED +#define _3DL2_H_INCLUDED + +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif +#include <dsound.h> + +#define FLOAT float +#define LONG long + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#pragma pack(push, 4) + +// I3DL2 listener property set {DA0F0520-300A-11D3-8A2B-0060970DB011} +DEFINE_GUID(DSPROPSETID_I3DL2_ListenerProperties, + 0xDA0F0520, + 0x300A, + 0x11D3, + 0x8A, 0x2B, 0x00, 0x60, 0x97, 0x0D, 0xB0, 0x11); + +typedef enum +{ + DSPROPERTY_I3DL2LISTENER_ALL, // sets all I3DL2 listener properties + DSPROPERTY_I3DL2LISTENER_ROOM, // room effect level at low frequencies + DSPROPERTY_I3DL2LISTENER_ROOMHF, // room effect high-frequency level re. low frequency level + DSPROPERTY_I3DL2LISTENER_ROOMROLLOFFFACTOR, // like DS3D flRolloffFactor but for room effect + DSPROPERTY_I3DL2LISTENER_DECAYTIME, // reverberation decay time at low-frequencies + DSPROPERTY_I3DL2LISTENER_DECAYHFRATIO, // high-frequency to low-frequency decay time ratio + DSPROPERTY_I3DL2LISTENER_REFLECTIONS, // early reflections level relative to room effect + DSPROPERTY_I3DL2LISTENER_REFLECTIONSDELAY, // delay time of first reflection + DSPROPERTY_I3DL2LISTENER_REVERB, // late reverberation level relative to room effect + DSPROPERTY_I3DL2LISTENER_REVERBDELAY, // late reverberation delay time relative to first reflection + DSPROPERTY_I3DL2LISTENER_DIFFUSION, // reverberation diffusion (echo density) + DSPROPERTY_I3DL2LISTENER_DENSITY, // reverberation density (modal density) + DSPROPERTY_I3DL2LISTENER_HFREFERENCE // reference high frequency +} DSPROPERTY_I3DL2_LISTENERPROPERTY; + +// use this structure for DSPROPERTY_I3DL2LISTENER_ALL +// - all levels are hundredths of decibels (mB) +// - all times are in seconds (s) +typedef struct _I3DL2_LISTENERPROPERTIES +{ + LONG lRoom; // [-10000, 0] default: -10000 mB + LONG lRoomHF; // [-10000, 0] default: 0 mB + FLOAT flRoomRolloffFactor; // [0.0, 10.0] default: 0.0 + FLOAT flDecayTime; // [0.1, 20.0] default: 1.0 s + FLOAT flDecayHFRatio; // [0.1, 2.0] default: 0.5 + LONG lReflections; // [-10000, 1000] default: -10000 mB + FLOAT flReflectionsDelay; // [0.0, 0.3] default: 0.02 s + LONG lReverb; // [-10000, 2000] default: -10000 mB + FLOAT flReverbDelay; // [0.0, 0.1] default: 0.04 s + FLOAT flDiffusion ; // [0.0, 100.0] default: 100.0 % + FLOAT flDensity; // [0.0, 100.0] default: 100.0 % + FLOAT flHFReference; // [20.0, 20000.0] default: 5000.0 Hz +} I3DL2_LISTENERPROPERTIES, *LPI3DL2_LISTENERPROPERTIES; + +// property ranges and defaults: + +#define I3DL2LISTENER_MINROOM -10000 +#define I3DL2LISTENER_MAXROOM 0 +#define I3DL2LISTENER_DEFAULTROOM -10000 + +#define I3DL2LISTENER_MINROOMHF -10000 +#define I3DL2LISTENER_MAXROOMHF 0 +#define I3DL2LISTENER_DEFAULTROOMHF 0 + +#define I3DL2LISTENER_MINROOMROLLOFFFACTOR 0.0f +#define I3DL2LISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define I3DL2LISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define I3DL2LISTENER_MINDECAYTIME 0.1f +#define I3DL2LISTENER_MAXDECAYTIME 20.0f +#define I3DL2LISTENER_DEFAULTDECAYTIME 1.0f + +#define I3DL2LISTENER_MINDECAYHFRATIO 0.1f +#define I3DL2LISTENER_MAXDECAYHFRATIO 2.0f +#define I3DL2LISTENER_DEFAULTDECAYHFRATIO 0.5f + +#define I3DL2LISTENER_MINREFLECTIONS -10000 +#define I3DL2LISTENER_MAXREFLECTIONS 1000 +#define I3DL2LISTENER_DEFAULTREFLECTIONS -10000 + +#define I3DL2LISTENER_MINREFLECTIONSDELAY 0.0f +#define I3DL2LISTENER_MAXREFLECTIONSDELAY 0.3f +#define I3DL2LISTENER_DEFAULTREFLECTIONSDELAY 0.02f + +#define I3DL2LISTENER_MINREVERB -10000 +#define I3DL2LISTENER_MAXREVERB 2000 +#define I3DL2LISTENER_DEFAULTREVERB -10000 + +#define I3DL2LISTENER_MINREVERBDELAY 0.0f +#define I3DL2LISTENER_MAXREVERBDELAY 0.1f +#define I3DL2LISTENER_DEFAULTREVERBDELAY 0.04f + +#define I3DL2LISTENER_MINDIFFUSION 0.0f +#define I3DL2LISTENER_MAXDIFFUSION 100.0f +#define I3DL2LISTENER_DEFAULTDIFFUSION 100.0f + +#define I3DL2LISTENER_MINDENSITY 0.0f +#define I3DL2LISTENER_MAXDENSITY 100.0f +#define I3DL2LISTENER_DEFAULTDENSITY 100.0f + +#define I3DL2LISTENER_MINHFREFERENCE 20.0f +#define I3DL2LISTENER_MAXHFREFERENCE 20000.0f +#define I3DL2LISTENER_DEFAULTHFREFERENCE 5000.0f + +// I3DL2 buffer property set {DA0F0521-300A-11D3-8A2B-0060970DB011} +DEFINE_GUID(DSPROPSETID_I3DL2_BufferProperties, + 0xDA0F0521, + 0x300A, + 0x11D3, + 0x8A, 0x2B, 0x00, 0x60, 0x97, 0x0D, 0xB0, 0x11); + +typedef enum +{ + DSPROPERTY_I3DL2BUFFER_ALL, // sets all I3DL2 buffer properties + DSPROPERTY_I3DL2BUFFER_OBSTRUCTIONALL, // sets both obstruction properties + DSPROPERTY_I3DL2BUFFER_OCCLUSIONALL, // sets both occlusion properties + DSPROPERTY_I3DL2BUFFER_DIRECT, // additional direct path level correction + DSPROPERTY_I3DL2BUFFER_DIRECTHF, // additional direct path high-frequency re. low-frequency level correction + DSPROPERTY_I3DL2BUFFER_ROOM, // additional room effect level correction + DSPROPERTY_I3DL2BUFFER_ROOMHF, // additional room effect high-frequency re. low-frequency level correction + DSPROPERTY_I3DL2BUFFER_ROOMROLLOFFFACTOR, // like DS3D flRolloffFactor but for room effect + DSPROPERTY_I3DL2BUFFER_OBSTRUCTION, // main obstruction control (attenuation at high frequencies) + DSPROPERTY_I3DL2BUFFER_OBSTRUCTIONLFRATIO, // obstruction low-frequency re. high-frequency ratio + DSPROPERTY_I3DL2BUFFER_OCCLUSION, // main occlusion control (attenuation at high frequencies) + DSPROPERTY_I3DL2BUFFER_OCCLUSIONLFRATIO // occlusion low-frequency re. high-frequency ratio +} DSPROPERTY_I3DL2_BUFFERPROPERTY; + +// use this structure for DSPROPERTY_I3DL2BUFFER_OBSTRUCTIONALL +// - all levels are hundredths of decibels (mB) +typedef struct _I3DL2_OBSTRUCTIONPROPERTIES +{ + LONG lHFLevel; // [-10000, 0] default: 0 mB + FLOAT flLFRatio; // [0.0, 1.0] default: 0.0 +} I3DL2_OBSTRUCTIONPROPERTIES, *LPI3DL2_OBSTRUCTIONPROPERTIES; + +// use this structure for DSPROPERTY_I3DL2BUFFER_OCCLUSIONALL +// - all levels are hundredths of decibels (mB) +typedef struct _I3DL2_OCCLUSIONPROPERTIES +{ + LONG lHFLevel; // [-10000, 0] default: 0 mB + FLOAT flLFRatio; // [0.0, 1.0] default: 0.25 +} I3DL2_OCCLUSIONPROPERTIES, *LPI3DL2_OCCLUSIONPROPERTIES; + +// use this structure for DSPROPERTY_I3DL2BUFFER_ALL +// - all levels are hundredths of decibels (mB) +typedef struct _I3DL2_BUFFERPROPERTIES +{ + LONG lDirect; // [-10000, 1000] default: 0 mB + LONG lDirectHF; // [-10000, 0] default: 0 mB + LONG lRoom; // [-10000, 1000] default: 0 mB + LONG lRoomHF; // [-10000, 0] default: 0 mB + FLOAT flRoomRolloffFactor; // [0.0, 10.0] default: 0.0 + I3DL2_OBSTRUCTIONPROPERTIES Obstruction; + I3DL2_OCCLUSIONPROPERTIES Occlusion; +} I3DL2_BUFFERPROPERTIES, *LPI3DL2_BUFFERPROPERTIES; + +// property ranges and defaults: + +#define I3DL2BUFFER_MINDIRECT -10000 +#define I3DL2BUFFER_MAXDIRECT 1000 +#define I3DL2BUFFER_DEFAULTDIRECT 0 + +#define I3DL2BUFFER_MINDIRECTHF -10000 +#define I3DL2BUFFER_MAXDIRECTHF 0 +#define I3DL2BUFFER_DEFAULTDIRECTHF 0 + +#define I3DL2BUFFER_MINROOM -10000 +#define I3DL2BUFFER_MAXROOM 1000 +#define I3DL2BUFFER_DEFAULTROOM 0 + +#define I3DL2BUFFER_MINROOMHF -10000 +#define I3DL2BUFFER_MAXROOMHF 0 +#define I3DL2BUFFER_DEFAULTROOMHF 0 + +#define I3DL2BUFFER_MINROOMROLLOFFFACTOR 0.0f +#define I3DL2BUFFER_MAXROOMROLLOFFFACTOR 10.f +#define I3DL2BUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define I3DL2BUFFER_MINOBSTRUCTION -10000 +#define I3DL2BUFFER_MAXOBSTRUCTION 0 +#define I3DL2BUFFER_DEFAULTOBSTRUCTION 0 + +#define I3DL2BUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define I3DL2BUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define I3DL2BUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define I3DL2BUFFER_MINOCCLUSION -10000 +#define I3DL2BUFFER_MAXOCCLUSION 0 +#define I3DL2BUFFER_DEFAULTOCCLUSION 0 + +#define I3DL2BUFFER_MINOCCLUSIONLFRATIO 0.0f +#define I3DL2BUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define I3DL2BUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/win32/src/fmod_channel_dsound.cpp b/win32/src/fmod_channel_dsound.cpp new file mode 100755 index 0000000..d6d64f9 --- /dev/null +++ b/win32/src/fmod_channel_dsound.cpp @@ -0,0 +1,1280 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSOUND + +#include "fmod_channel_dsound.h" +#include "fmod_debug.h" +#include "fmod_output_dsound.h" +#include "fmod_sample_dsound.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" +#include "fmod_3d.h" + +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif +#include <dsound.h> +#include <stdio.h> + +namespace FMOD +{ + +static const FMOD_GUID IID_IDirectSound3DBuffer = { 0x279AFA86, 0x4981, 0x11CE, { 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60} }; +static const FMOD_GUID IID_KSPROPSETID_Audio = { 0x45FFAAA0, 0x6E1B, 0x11D0, { 0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }; + +typedef enum { + KSPROPERTY_AUDIO_LATENCY = 1, + KSPROPERTY_AUDIO_COPY_PROTECTION, + KSPROPERTY_AUDIO_CHANNEL_CONFIG, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_AUDIO_POSITION, + KSPROPERTY_AUDIO_DYNAMIC_RANGE, + KSPROPERTY_AUDIO_QUALITY, + KSPROPERTY_AUDIO_SAMPLING_RATE, + KSPROPERTY_AUDIO_DYNAMIC_SAMPLING_RATE, + KSPROPERTY_AUDIO_MIX_LEVEL_TABLE, + KSPROPERTY_AUDIO_MIX_LEVEL_CAPS, + KSPROPERTY_AUDIO_MUX_SOURCE, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_AUDIO_BASS, + KSPROPERTY_AUDIO_MID, + KSPROPERTY_AUDIO_TREBLE, + KSPROPERTY_AUDIO_BASS_BOOST, + KSPROPERTY_AUDIO_EQ_LEVEL, + KSPROPERTY_AUDIO_NUM_EQ_BANDS, + KSPROPERTY_AUDIO_EQ_BANDS, + KSPROPERTY_AUDIO_AGC, + KSPROPERTY_AUDIO_DELAY, + KSPROPERTY_AUDIO_LOUDNESS, + KSPROPERTY_AUDIO_WIDE_MODE, + KSPROPERTY_AUDIO_WIDENESS, + KSPROPERTY_AUDIO_REVERB_LEVEL, + KSPROPERTY_AUDIO_CHORUS_LEVEL, + KSPROPERTY_AUDIO_DEV_SPECIFIC, + KSPROPERTY_AUDIO_DEMUX_DEST, + KSPROPERTY_AUDIO_STEREO_ENHANCE, + KSPROPERTY_AUDIO_MANUFACTURE_GUID, + KSPROPERTY_AUDIO_PRODUCT_GUID, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_AUDIO_STEREO_SPEAKER_GEOMETRY, + KSPROPERTY_AUDIO_SURROUND_ENCODE, + KSPROPERTY_AUDIO_3D_INTERFACE, + KSPROPERTY_AUDIO_PEAKMETER, + KSPROPERTY_AUDIO_ALGORITHM_INSTANCE, + KSPROPERTY_AUDIO_FILTER_STATE, + KSPROPERTY_AUDIO_PREFERRED_STATUS +} KSPROPERTY_AUDIO; + +// Audio quality constants +#define KSAUDIO_QUALITY_WORST 0x0 +#define KSAUDIO_QUALITY_PC 0x1 +#define KSAUDIO_QUALITY_BASIC 0x2 +#define KSAUDIO_QUALITY_ADVANCED 0x3 + +//#define FMOD_CHANNEL_DSOUND_QUALITY KSAUDIO_QUALITY_PC + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::init(int index, SystemI *system, Output *output, DSPI *dspmixtarget) +{ + FMOD_RESULT result; + + result = ChannelReal::init(index, system, output, dspmixtarget); + if (result != FMOD_OK) + { + return result; + } + + mBuffer = 0; + mBuffer3D = 0; + mOutputDSound = (OutputDSound *)output; + mMinFrequency = DSBFREQUENCY_MIN; + mMaxFrequency = DSBFREQUENCY_MAX; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::alloc() +{ + FMOD_MODE mode; + HRESULT hr; + SampleDSound *sampledsound; + + sampledsound = SAFE_CAST(SampleDSound, mSound); + if (!sampledsound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!sampledsound->mBuffer) + { + return FMOD_ERR_INVALID_PARAM; + } + + mode = sampledsound->mMode; + + if (mode & FMOD_UNIQUE) + { + mBuffer = sampledsound->mBuffer; + } + else + { + hr = mOutputDSound->mDirectSound->DuplicateSoundBuffer(sampledsound->mBuffer, (IDirectSoundBuffer **)&mBuffer); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + mLOCSoftware = sampledsound->mLOCSoftware; + + if (!mLOCSoftware) + { + mMinFrequency = mOutputDSound->mMinFrequency; + mMaxFrequency = mOutputDSound->mMaxFrequency; + } + + if (mOutputDSound->mChannelPool3D && mode & FMOD_3D) + { + /* + OBTAIN DS3D INTERFACE TO BUFFER + */ + if (mode & FMOD_UNIQUE) + { + mBuffer3D = sampledsound->mBuffer3D; + } + else + { + GUID guid; + + FMOD_memcpy(&guid, &IID_IDirectSound3DBuffer, sizeof(GUID)); + + if (mBuffer->QueryInterface(guid, (LPVOID *)&mBuffer3D) != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + /* + SET DS3D PROPERTIES FOR THIS BUFFER + */ + + if (mBuffer3D) + { + DS3DBUFFER buffparams; + + FMOD_memset(&buffparams, 0, sizeof(DS3DBUFFER)); + buffparams.dwSize = sizeof(DS3DBUFFER); + + mBuffer3D->GetAllParameters(&buffparams); + + buffparams.flMinDistance = mParent->mMinDistance; + buffparams.flMaxDistance = mParent->mMaxDistance; + buffparams.dwInsideConeAngle = (DWORD)mParent->mConeInsideAngle; + buffparams.dwOutsideConeAngle = (DWORD)mParent->mConeOutsideAngle; + if (mParent->mConeOutsideVolume < 0.00001f) + { + buffparams.lConeOutsideVolume = DSBVOLUME_MIN; + } + else if (mParent->mConeOutsideVolume > 0.9999f) + { + buffparams.lConeOutsideVolume = DSBVOLUME_MAX; + } + else + { + buffparams.lConeOutsideVolume = (int)(100.0f * 20.0f * FMOD_LOG10(mParent->mConeOutsideVolume)); + } + buffparams.vConeOrientation.x = mParent->mConeOrientation.x; + buffparams.vConeOrientation.y = mParent->mConeOrientation.y; + buffparams.vConeOrientation.z = mParent->mConeOrientation.z; + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + buffparams.vConeOrientation.z = -buffparams.vConeOrientation.z; + } + buffparams.dwMode = (mode & FMOD_3D_HEADRELATIVE) ? DS3DMODE_HEADRELATIVE : DS3DMODE_NORMAL; + + mBuffer3D->SetAllParameters(&buffparams, DS3D_IMMEDIATE); + } + } + + /* + Set the quality for this buffer. (Default quality is horribly clicky. + */ +#ifdef FMOD_CHANNEL_DSOUND_QUALITY + if (mLOCSoftware) + { + GUID guid; + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + HRESULT hr = mBuffer->QueryInterface(guid, (void**)&mBufferQuality); + } + else + { + mBufferQuality = 0; + } +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::start() +{ + SampleDSound *sampledsound; + HRESULT hr; + + sampledsound = SAFE_CAST(SampleDSound, mSound); + if (!sampledsound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!sampledsound->mBuffer || !mBuffer) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "ChannelDSound::start", "Not a hardware channel or sample\n")); + + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + + /* + START BUFFER PLAYING + */ + if (!(mFlags & CHANNELREAL_FLAG_PAUSED)) + { + FMOD_MODE smode; + DWORD playmode = 0; + + smode = sampledsound->mMode; + + if (!(smode & FMOD_UNIQUE)) + { + if (mLOCSoftware) + { + playmode |= DSBPLAY_LOCSOFTWARE; + } + else + { + playmode |= DSBPLAY_LOCHARDWARE; + } + } + + if (mMode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI) && mLoopCount) + { + playmode |= DSBPLAY_LOOPING; + } + + hr = mBuffer->Play(0,0, playmode); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "ChannelDSound::start", "IDirectSoundBuffer::Play returned errcode %08X\n", hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + +#ifdef FMOD_CHANNEL_DSOUND_QUALITY + if (mBufferQuality) + { + GUID guid; + DWORD dwQuality = FMOD_CHANNEL_DSOUND_QUALITY; + + FMOD_memcpy(&guid, &IID_KSPROPSETID_Audio, sizeof(GUID)); + + mBufferQuality->Set(guid, KSPROPERTY_AUDIO_QUALITY, PVOID(&dwQuality), sizeof(dwQuality), PVOID(&dwQuality), sizeof(dwQuality)); + } +#endif + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::stop() +{ + bool releasebuffers = true; + + if (mBuffer) + { + mBuffer->Stop(); + mBuffer->SetCurrentPosition(0); + } + + if (mBufferReverb) + { + mBufferReverb->Release(); + mBufferReverb = 0; + } + + if (mSound && mSound->mMode & FMOD_UNIQUE) + { + releasebuffers = false; + } + + if (releasebuffers) + { + if (mBuffer3D) + { + mBuffer3D->Release(); + } + + if (mBuffer) + { + mBuffer->Release(); + } + + #ifdef FMOD_CHANNEL_DSOUND_QUALITY + if (mBufferQuality) + { + mBufferQuality->Release(); + } + #endif + } + + mBuffer = 0; + mBuffer3D = 0; + mBufferQuality = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::setPaused(bool paused) +{ + FMOD_RESULT result = FMOD_OK; + + if (!mBuffer || !mSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (paused) + { + result = ChannelReal::setPaused(paused); + + mBuffer->Stop(); + + return result; + } + else + { + if (mFlags & CHANNELREAL_FLAG_PAUSED) + { + DWORD locflags = 0; + HRESULT hr; + + if (!(mMode & FMOD_UNIQUE)) + { + if (mLOCSoftware) + { + locflags |= DSBPLAY_LOCSOFTWARE; + } + else + { + locflags |= DSBPLAY_LOCHARDWARE; + } + } + hr = mBuffer->Play(0,0,locflags | (mMode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI) && mLoopCount ? DSBPLAY_LOOPING : 0)); + if (hr != DS_OK) + { + switch (hr) + { + case DSERR_BUFFERLOST: + hr = hr; + break; + case DSERR_INVALIDCALL: + hr = hr; + break; + case DSERR_INVALIDPARAM: + hr = hr; + break; + case DSERR_PRIOLEVELNEEDED: + hr = hr; + break; + }; + + if (hr == E_OUTOFMEMORY) + { + result = FMOD_ERR_MEMORY; + } + else + { + result = FMOD_ERR_OUTPUT_DRIVERCALL; + } + } +#ifdef FMOD_CHANNEL_DSOUND_QUALITY + else if (mBufferQuality) + { + GUID guid; + DWORD dwQuality = FMOD_CHANNEL_DSOUND_QUALITY; + + FMOD_memcpy(&guid, &IID_KSPROPSETID_Audio, sizeof(GUID)); + + mBufferQuality->Set(guid, KSPROPERTY_AUDIO_QUALITY, PVOID(&dwQuality), sizeof(dwQuality), PVOID(&dwQuality), sizeof(dwQuality)); + } +#endif + } + } + + if (result != FMOD_OK) + { + return result; + } + + return ChannelReal::setPaused(paused); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::setVolume(float volume) +{ + if (mBuffer) + { + HRESULT hr; + + if (mParent->mFlags & CHANNELI_FLAG_REALMUTE) + { + volume = 0; + } + + if (mOutputDSound->mReverbVersion == REVERB_VERSION_NONE) + { + volume *= (1.0f - mParent->mDirectOcclusion) * + (1.0f - mParent->mUserDirectOcclusion) * + mParent->mChannelGroup->mRealDirectOcclusionVolume; + } + + volume *= mParent->mChannelGroup->mRealVolume; + if (mMode & FMOD_3D) + { + if (mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback) + { + volume *= mParent->mVolume3D; + } + + volume *= mParent->mConeVolume3D; + } + if (mSound && mSound->mSoundGroup) + { + volume *= mSound->mSoundGroup->mVolume; + } + volume *= mParent->mFadeVolume; + + if (volume == 1.0f) + { + volume = 0; + } + else if (volume == 0) + { + if (mSound && mSound->mFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + volume = -8000; /* DirectSound bug workaround. Silence on a float directsound buffer apparently means a horrible buzzing noise. This is as close to silence as we can get before it buzzes. */ + } + else + { + volume = -10000; + } + } + else + { + if (volume < 0.00001f) + { + volume = 0.00001f; + } + + /* + Convert from linear amplitude to hundredths of decibels + */ + volume = 100.0f * 20.0f * FMOD_LOG10(volume); + } + + hr = mBuffer->SetVolume((int)volume); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::setFrequency(float frequency) +{ + if (mSound && !mOutputDSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mBuffer) + { + HRESULT hr; + + frequency *= mParent->mChannelGroup->mRealPitch; + + hr = mBuffer->SetFrequency((int)frequency); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::setPan(float pan, float fbpan) +{ + int hwpan; + + if (mBuffer3D) + { + return FMOD_ERR_FORMAT; + } + + if (mBuffer) + { + float gain; + + pan = (pan + 1.0f) / 2.0f; // convert from -1 to 1, to 0 to 1. + + if (pan < 0.5f) + { + gain = pan * 2.0f; + if (gain < 0.00001f) + { + gain = 0.00001f; + } + + /* + Convert from linear amplitude to hundredths of decibels + */ + hwpan = (int)(100.0f * 20.0f * FMOD_LOG10(gain)); + } + else + { + gain = (1.0f-pan) * 2.0f; + if (gain < 0.00001f) + { + gain = 0.00001f; + } + + /* + Convert from linear amplitude to hundredths of decibels + */ + hwpan = -(int)(100.0f * 20.0f * FMOD_LOG10(gain)); + } + + mBuffer->SetPan(hwpan); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + HRESULT hr; + unsigned int pcmbytes; + + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } + + if (!mBuffer || !mSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (postype == FMOD_TIMEUNIT_PCM) + { + SoundI::getBytesFromSamples(position, &pcmbytes, mSound->mChannels, mSound->mFormat); + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + pcmbytes = position; + } + else if (postype == FMOD_TIMEUNIT_MS) + { + pcmbytes = (unsigned int)((float)position / 1000.0f * mSound->mDefaultFrequency); + SoundI::getBytesFromSamples(pcmbytes, &pcmbytes, mSound->mChannels, mSound->mFormat); + } + + hr = mBuffer->SetCurrentPosition(pcmbytes); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + DWORD dwPlay, dwWrite; + HRESULT hr; + bool playing; + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + *position = 0; + + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } + + if (!mBuffer || !mSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + isPlaying(&playing); + + hr = mBuffer->GetCurrentPosition(&dwPlay, &dwWrite); + if (hr != DS_OK) + { + *position = 0; + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (postype == FMOD_TIMEUNIT_PCM) + { + SoundI::getSamplesFromBytes(dwPlay, position, mSound->mChannels, mSound->mFormat); + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + *position = dwPlay; + } + else if (postype == FMOD_TIMEUNIT_MS) + { + SoundI::getSamplesFromBytes(dwPlay, position, mSound->mChannels, mSound->mFormat); + *position = (unsigned int)((float)*position / mSound->mDefaultFrequency * 1000.0f); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::set3DAttributes() +{ + FMOD_RESULT result; + int numlisteners; + DS3DBUFFER buffparams; + int deferred = DS3D_IMMEDIATE; + + if (!mBuffer3D) + { + return FMOD_OK; + } + + if (!mOutputDSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mSystem->get3DNumListeners(&numlisteners); + if (result != FMOD_OK) + { + return result; + } + + FMOD_memset(&buffparams, 0, sizeof(DS3DBUFFER)); + buffparams.dwSize = sizeof(DS3DBUFFER); + mBuffer3D->GetAllParameters(&buffparams); + + if (numlisteners == 1) + { + buffparams.vPosition.x = mParent->mPosition3D.x; + buffparams.vPosition.y = mParent->mPosition3D.y; + buffparams.vPosition.z = mParent->mPosition3D.z; + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + buffparams.vPosition.z = -buffparams.vPosition.z; + } + } + else + { + buffparams.vPosition.x = 0; + buffparams.vPosition.y = 0; + buffparams.vPosition.z = mParent->mDistance; + } + + if (numlisteners == 1) + { + if (mParent->m3DDopplerLevel != 1.0f) + { + buffparams.vVelocity.x = (mParent->mVelocity3D.x * mParent->m3DDopplerLevel) + (mSystem->mListener[0].mVelocity.x * (1.0f - mParent->m3DDopplerLevel)); + buffparams.vVelocity.y = (mParent->mVelocity3D.y * mParent->m3DDopplerLevel) + (mSystem->mListener[0].mVelocity.y * (1.0f - mParent->m3DDopplerLevel)); + buffparams.vVelocity.z = (mParent->mVelocity3D.z * mParent->m3DDopplerLevel) + (mSystem->mListener[0].mVelocity.z * (1.0f - mParent->m3DDopplerLevel)); + } + else + { + buffparams.vVelocity.x = mParent->mVelocity3D.x; + buffparams.vVelocity.y = mParent->mVelocity3D.y; + buffparams.vVelocity.z = mParent->mVelocity3D.z; + } + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + buffparams.vVelocity.z = -buffparams.vVelocity.z; + } + } + else + { + buffparams.vVelocity.x = 0; + buffparams.vVelocity.y = 0; + buffparams.vVelocity.z = 0; + } + + if (mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback) + { + FMOD_VECTOR diff; + float distance; + FMOD_VECTOR pos, listenerpos; + + pos.x = mParent->mPosition3D.x; + pos.y = mParent->mPosition3D.y; + pos.z = mParent->mPosition3D.z; + + listenerpos = mSystem->mListener[0].mPosition; + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + pos.z = -pos.z; + listenerpos.z = -listenerpos.z; + } + + buffparams.flMinDistance = 1.0f; + buffparams.flMaxDistance = 1.0f; + + FMOD_Vector_Subtract(&pos, &listenerpos, &diff); + + distance = FMOD_Vector_GetLengthFast(&diff); + + if (distance > 0.0f) + { + FMOD_Vector_Scale(&diff, (1.0f / distance) * mSystem->mDistanceScale, &diff); + } + else + { + FMOD_Vector_Set(&diff, 0, 0, 0); + } + + FMOD_Vector_Add(&listenerpos, &diff, (FMOD_VECTOR *)(&buffparams.vPosition)); + + setVolume(mParent->mVolume); + } + else + { + buffparams.flMinDistance = mParent->mMinDistance; + buffparams.flMaxDistance = mParent->mMaxDistance; + + if (mParent->mConeOutsideAngle < 360.0f || mParent->mConeInsideAngle < 360.0f) /* Because the first part of this if statement calls setvolume all the time, we'll just call it here if cones are used. */ + { + setVolume(mParent->mVolume); + } + } + + // if (mOutputDSound->mInitFlags & FMOD_INIT_DSOUND_DEFERRED) + { + if (mFlags & CHANNELREAL_FLAG_ALLOCATED || mFlags & CHANNELREAL_FLAG_PAUSED) + { + deferred = DS3D_IMMEDIATE; + } + else + { + deferred = DS3D_DEFERRED; + mOutputDSound->mNeedToCommit = true; + } + } + + if (mBuffer3D->SetAllParameters(&buffparams, deferred) != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::set3DMinMaxDistance() +{ + int deferred = DS3D_IMMEDIATE; + + if (!mBuffer3D) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputDSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback) + { + return FMOD_OK; + } + else + { + //if (mOutputDSound->mInitFlags & FMOD_INIT_DSOUND_DEFERRED) + { + if (mFlags & CHANNELREAL_FLAG_ALLOCATED || mFlags & CHANNELREAL_FLAG_PAUSED) + { + deferred = DS3D_IMMEDIATE; + } + else + { + deferred = DS3D_DEFERRED; + mOutputDSound->mNeedToCommit = true; + } + } + + if (mBuffer3D->SetMinDistance(mParent->mMinDistance, deferred) != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (mBuffer3D->SetMaxDistance(mParent->mMaxDistance, deferred) != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::set3DOcclusion(float directOcclusion, float reverbOcclusion) +{ + if (mOutputDSound->mReverbVersion != REVERB_VERSION_NONE) + { + float directTransmission = (1.0f - directOcclusion) * + mParent->mChannelGroup->mRealDirectOcclusionVolume; + + if (directTransmission < 0.00001f) + { + directTransmission = 0.00001f; + } + + int directGain = int(100.0f * 20.0f * FMOD_LOG10(directTransmission)); + + float reverbTransmission = (1.0f - reverbOcclusion) * + mParent->mChannelGroup->mRealReverbOcclusionVolume; + if (reverbTransmission < 0.00001f) + { + reverbTransmission = 0.00001f; + } + int reverbGain = int(100.0f * 20.0f * FMOD_LOG10(reverbTransmission)); + + FMOD_REVERB_CHANNELPROPERTIES prop; + + getReverbProperties(&prop); + + prop.Direct = directGain >> 2; // quarter occlusion for low frequencies + prop.DirectHF = directGain; // full occlusion for high frequencies + prop.Room = reverbGain; + + return setReverbProperties(&prop); + } + else + { + return setVolume(mParent->mVolume); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::isPlaying(bool *isplaying, bool includethreadlatency) +{ + if (mFlags & CHANNELREAL_FLAG_ALLOCATED || mFlags & CHANNELREAL_FLAG_PAUSED) + { + if (isplaying) + { + *isplaying = true; + } + + return FMOD_OK; + } + + if (mBuffer) + { + DWORD flags; + + if (mBuffer->GetStatus(&flags) != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + *isplaying = (flags & DSBSTATUS_PLAYING); + } + else + { + *isplaying = false; + } + + if (!*isplaying) + { + mFlags &= (CHANNELREAL_FLAG_ALLOCATED | CHANNELREAL_FLAG_PLAYING); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::setMode(FMOD_MODE mode) +{ + int deferred = DS3D_IMMEDIATE; + FMOD_RESULT result; + + result = ChannelReal::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + + if (mBuffer3D) + { + HRESULT hr; + DWORD d3dmode; + + if (!mOutputDSound) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Turn head-relative on/off + */ + if (mMode & FMOD_3D_HEADRELATIVE) + { + d3dmode = DS3DMODE_HEADRELATIVE; + } + else + { + d3dmode = DS3DMODE_NORMAL; + } + + //if (mOutputDSound->mInitFlags & FMOD_INIT_DSOUND_DEFERRED) + { + if (mFlags & CHANNELREAL_FLAG_ALLOCATED || mFlags & CHANNELREAL_FLAG_PAUSED) + { + deferred = DS3D_IMMEDIATE; + } + else + { + deferred = DS3D_DEFERRED; + mOutputDSound->mNeedToCommit = true; + } + } + + hr = mBuffer3D->SetMode(d3dmode, deferred); + if (FAILED(hr)) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSound::getBuffer3D(IDirectSound3DBuffer **buffer3d) +{ + if (!buffer3d) + { + return FMOD_ERR_INVALID_PARAM; + } + + *buffer3d = mBuffer3D; + + return FMOD_OK; +} + + +FMOD_RESULT ChannelDSound::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + return FMOD_ERR_NEEDSSOFTWARE; +} + + +FMOD_RESULT ChannelDSound::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + return FMOD_ERR_NEEDSSOFTWARE; +} + + +} + +#endif /* FMOD_SUPPORT_DSOUND */ diff --git a/win32/src/fmod_channel_dsound.h b/win32/src/fmod_channel_dsound.h new file mode 100755 index 0000000..de7b6b0 --- /dev/null +++ b/win32/src/fmod_channel_dsound.h @@ -0,0 +1,67 @@ +#ifndef _FMOD_CHANNEL_DSOUND_H +#define _FMOD_CHANNEL_DSOUND_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSOUND + +#include "fmod_channel_real.h" + +#ifdef PLATFORM_32BIT + typedef unsigned long DWORD; + typedef DWORD DWORD_PTR; +#endif + +#include <dsound.h> + +namespace FMOD +{ + class Output; + + class ChannelDSound : public ChannelReal + { + friend class OutputDSound; + + private: + + bool mLOCSoftware; + + protected: + + OutputDSound *mOutputDSound; + IDirectSoundBuffer8 *mBuffer; + IDirectSound3DBuffer *mBuffer3D; + IKsPropertySet *mBufferReverb; + IKsPropertySet *mBufferQuality; + + public: + + ChannelDSound() {} + + FMOD_RESULT init (int index, SystemI *system, Output *output, DSPI *dspmixtarget); + FMOD_RESULT alloc (); + FMOD_RESULT start (); + + FMOD_RESULT stop (); + FMOD_RESULT setVolume (float volume); + FMOD_RESULT setFrequency (float frequency); + FMOD_RESULT setPan (float pan, float fbpan = 1); + FMOD_RESULT setSpeakerLevels (int speaker, float *levels, int numlevels); + FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + FMOD_RESULT setPaused (bool paused); + FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT set3DAttributes (); + FMOD_RESULT set3DMinMaxDistance (); + FMOD_RESULT set3DOcclusion (float directOcclusion, float reverbOcclusion); + + FMOD_RESULT isPlaying (bool *isplaying, bool includethreadlatency = false); + FMOD_RESULT setMode (FMOD_MODE mode); + + FMOD_RESULT getBuffer3D (IDirectSound3DBuffer **buffer3d); + }; +} + +#endif /* FMOD_SUPPORT_DSOUND */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_channel_dsound_eax2.cpp b/win32/src/fmod_channel_dsound_eax2.cpp new file mode 100755 index 0000000..35d50fd --- /dev/null +++ b/win32/src/fmod_channel_dsound_eax2.cpp @@ -0,0 +1,193 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_dsound.h" +#include "fmod_channel_dsound_eax2.h" +#include "fmod_eax2.h" + + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundEAX2::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + EAXBUFFERPROPERTIES dsprops; + GUID guid; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + NOW SET CHANNEL PROPERTIES + */ + dsprops.lDirect = prop->Direct; + dsprops.lDirectHF = prop->DirectHF; + dsprops.lRoom = prop->Room; + dsprops.lRoomHF = prop->RoomHF; + dsprops.lObstruction = prop->Obstruction; + dsprops.flObstructionLFRatio = prop->ObstructionLFRatio; + dsprops.lOcclusion = prop->Occlusion; + dsprops.flOcclusionLFRatio = prop->OcclusionLFRatio; + dsprops.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + dsprops.lOutsideVolumeHF = prop->OutsideVolumeHF; + dsprops.flRoomRolloffFactor = prop->RoomRolloffFactor; + dsprops.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + dsprops.dwFlags = prop->Flags; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_BufferProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, NULL, 0, &dsprops, sizeof(EAXBUFFERPROPERTIES)); + if (FAILED(hr)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundEAX2::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + EAXBUFFERPROPERTIES dsprops; + GUID guid; + ULONG ulReceived; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + + /* + Get reverb properties + */ + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_BufferProperties, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, NULL, 0, &dsprops, sizeof(EAXBUFFERPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Direct = dsprops.lDirect; + prop->DirectHF = dsprops.lDirectHF; + prop->Room = dsprops.lRoom; + prop->RoomHF = dsprops.lRoomHF; + prop->Obstruction = dsprops.lObstruction; + prop->ObstructionLFRatio = dsprops.flObstructionLFRatio; + prop->Occlusion = dsprops.lOcclusion; + prop->OcclusionLFRatio = dsprops.flOcclusionLFRatio; + prop->OcclusionRoomRatio = dsprops.flOcclusionRoomRatio; + prop->OcclusionDirectRatio = 0; + prop->Exclusion = 0; + prop->ExclusionLFRatio = 0; + prop->OutsideVolumeHF = dsprops.lOutsideVolumeHF; + prop->DopplerFactor = 0; + prop->RolloffFactor = 0; + prop->RoomRolloffFactor = dsprops.flRoomRolloffFactor; + prop->AirAbsorptionFactor = dsprops.flAirAbsorptionFactor; + prop->Flags = dsprops.dwFlags; + + return FMOD_OK; +} + +} + +#endif diff --git a/win32/src/fmod_channel_dsound_eax2.h b/win32/src/fmod_channel_dsound_eax2.h new file mode 100755 index 0000000..1e3c966 --- /dev/null +++ b/win32/src/fmod_channel_dsound_eax2.h @@ -0,0 +1,25 @@ +#ifndef _FMOD_CHANNEL_DSOUND_EAX2_H +#define _FMOD_CHANNEL_DSOUND_EAX2_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_dsound.h" + +namespace FMOD +{ + class ChannelDSoundEAX2 : public ChannelDSound + { + public: + + ChannelDSoundEAX2() {}; + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ + +#endif diff --git a/win32/src/fmod_channel_dsound_eax3.cpp b/win32/src/fmod_channel_dsound_eax3.cpp new file mode 100755 index 0000000..3577e4b --- /dev/null +++ b/win32/src/fmod_channel_dsound_eax3.cpp @@ -0,0 +1,197 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_dsound_eax3.h" +#include "fmod_eax3.h" +#include "fmod_output_dsound.h" + + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundEAX3::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + EAXBUFFERPROPERTIES dsprops; + GUID guid; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + NOW SET CHANNEL PROPERTIES + */ + dsprops.lDirect = prop->Direct; + dsprops.lDirectHF = prop->DirectHF; + dsprops.lRoom = prop->Room; + dsprops.lRoomHF = prop->RoomHF; + dsprops.lObstruction = prop->Obstruction; + dsprops.flObstructionLFRatio = prop->ObstructionLFRatio; + dsprops.lOcclusion = prop->Occlusion; + dsprops.flOcclusionLFRatio = prop->OcclusionLFRatio; + dsprops.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + dsprops.flOcclusionDirectRatio = prop->OcclusionDirectRatio; + dsprops.lExclusion = prop->Exclusion; + dsprops.flExclusionLFRatio = prop->ExclusionLFRatio; + dsprops.lOutsideVolumeHF = prop->OutsideVolumeHF; + dsprops.flDopplerFactor = prop->DopplerFactor; + dsprops.flRolloffFactor = prop->RolloffFactor; + dsprops.flRoomRolloffFactor = prop->RoomRolloffFactor; + dsprops.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + dsprops.ulFlags = prop->Flags; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_BufferProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, NULL, 0, &dsprops, sizeof(EAXBUFFERPROPERTIES)); + if (FAILED(hr)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundEAX3::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + EAXBUFFERPROPERTIES dsprops; + GUID guid; + ULONG ulReceived; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + Get reverb properties + */ + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_BufferProperties, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, NULL, 0, &dsprops, sizeof(EAXBUFFERPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Direct = dsprops.lDirect; + prop->DirectHF = dsprops.lDirectHF; + prop->Room = dsprops.lRoom; + prop->RoomHF = dsprops.lRoomHF; + prop->Obstruction = dsprops.lObstruction; + prop->ObstructionLFRatio = dsprops.flObstructionLFRatio; + prop->Occlusion = dsprops.lOcclusion; + prop->OcclusionLFRatio = dsprops.flOcclusionLFRatio; + prop->OcclusionRoomRatio = dsprops.flOcclusionRoomRatio; + prop->OcclusionDirectRatio = dsprops.flOcclusionDirectRatio; + prop->Exclusion = dsprops.lExclusion; + prop->ExclusionLFRatio = dsprops.flExclusionLFRatio; + prop->OutsideVolumeHF = dsprops.lOutsideVolumeHF; + prop->DopplerFactor = dsprops.flDopplerFactor; + prop->RolloffFactor = dsprops.flRolloffFactor; + prop->RoomRolloffFactor = dsprops.flRoomRolloffFactor; + prop->AirAbsorptionFactor = dsprops.flAirAbsorptionFactor; + prop->Flags = dsprops.ulFlags; + + return FMOD_OK; +} + +} + +#endif diff --git a/win32/src/fmod_channel_dsound_eax3.h b/win32/src/fmod_channel_dsound_eax3.h new file mode 100755 index 0000000..9fa0b93 --- /dev/null +++ b/win32/src/fmod_channel_dsound_eax3.h @@ -0,0 +1,25 @@ +#ifndef _FMOD_CHANNEL_DSOUND_EAX3_H +#define _FMOD_CHANNEL_DSOUND_EAX3_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_dsound.h" + +namespace FMOD +{ + class ChannelDSoundEAX3 : public ChannelDSound + { + public: + + ChannelDSoundEAX3() {} + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ + +#endif diff --git a/win32/src/fmod_channel_dsound_eax4.cpp b/win32/src/fmod_channel_dsound_eax4.cpp new file mode 100755 index 0000000..e4513e1 --- /dev/null +++ b/win32/src/fmod_channel_dsound_eax4.cpp @@ -0,0 +1,314 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_dsound_eax4.h" +#include "fmod_eax4.h" +#include "fmod_output_dsound.h" +#include "fmod_soundi.h" + + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundEAX4::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + EAXSOURCEPROPERTIES eaxsrcprops; + EAXACTIVEFXSLOTS eaxslots; + GUID guid; + unsigned int flags = prop->Flags; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + Set the target FX slot(s) for this source + */ + if ((flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) && + (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) && + (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2)) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + Set ACTIVE FX slot(s) + */ + if (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[0], &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAX_NULL_GUID, sizeof(GUID)); + flags = (flags & ~FMOD_REVERB_CHANNELFLAGS_INSTANCE0); + + if (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + flags = (flags & ~FMOD_REVERB_CHANNELFLAGS_INSTANCE1); + } + else if (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + flags = (flags & ~FMOD_REVERB_CHANNELFLAGS_INSTANCE2); + } + } + else if (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[0], &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAX_NULL_GUID, sizeof(GUID)); + flags = (flags & ~FMOD_REVERB_CHANNELFLAGS_INSTANCE1); + + if (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + flags = (flags & ~FMOD_REVERB_CHANNELFLAGS_INSTANCE2); + } + } + else if (flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[0], &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAX_NULL_GUID, sizeof(GUID)); + flags = (flags & ~FMOD_REVERB_CHANNELFLAGS_INSTANCE2); + } + else // Set to Slot 0 by default + { + FMOD_memcpy(&eaxslots.guidActiveFXSlots[0], &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxslots.guidActiveFXSlots[1], &FMOD_EAX_NULL_GUID, sizeof(GUID)); + } + + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, EAXSOURCE_ACTIVEFXSLOTID, NULL, 0, &eaxslots, sizeof(EAXACTIVEFXSLOTS)); + if (FAILED(hr)) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + /* + NOW SET CHANNEL PROPERTIES + */ + eaxsrcprops.lDirect = prop->Direct; + eaxsrcprops.lDirectHF = prop->DirectHF; + eaxsrcprops.lRoom = prop->Room; + eaxsrcprops.lRoomHF = prop->RoomHF; + eaxsrcprops.lObstruction = prop->Obstruction; + eaxsrcprops.flObstructionLFRatio = prop->ObstructionLFRatio; + eaxsrcprops.lOcclusion = prop->Occlusion; + eaxsrcprops.flOcclusionLFRatio = prop->OcclusionLFRatio; + eaxsrcprops.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + eaxsrcprops.flOcclusionDirectRatio = prop->OcclusionDirectRatio; + eaxsrcprops.lExclusion = prop->Exclusion; + eaxsrcprops.flExclusionLFRatio = prop->ExclusionLFRatio; + eaxsrcprops.lOutsideVolumeHF = prop->OutsideVolumeHF; + eaxsrcprops.flDopplerFactor = prop->DopplerFactor; + eaxsrcprops.flRolloffFactor = prop->RolloffFactor; + eaxsrcprops.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxsrcprops.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + eaxsrcprops.ulFlags = flags; + + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, EAXSOURCE_ALLPARAMETERS, NULL, 0, &eaxsrcprops, sizeof(EAXSOURCEPROPERTIES)); + if (FAILED(hr)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundEAX4::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + EAXSOURCEPROPERTIES eaxsrcprops; + EAXACTIVEFXSLOTS eaxslots; + GUID guid; + GUID guidfx0, guidfx2, guidfx3; + ULONG ulReceived; + int noActiveSlots = 0; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + Get reverb properties + */ + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, EAXSOURCE_ALLPARAMETERS, NULL, 0, &eaxsrcprops, sizeof(EAXSOURCEPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Direct = eaxsrcprops.lDirect; + prop->DirectHF = eaxsrcprops.lDirectHF; + prop->Room = eaxsrcprops.lRoom; + prop->RoomHF = eaxsrcprops.lRoomHF; + prop->Obstruction = eaxsrcprops.lObstruction; + prop->ObstructionLFRatio = eaxsrcprops.flObstructionLFRatio; + prop->Occlusion = eaxsrcprops.lOcclusion; + prop->OcclusionLFRatio = eaxsrcprops.flOcclusionLFRatio; + prop->OcclusionRoomRatio = eaxsrcprops.flOcclusionRoomRatio; + prop->OcclusionDirectRatio = eaxsrcprops.flOcclusionDirectRatio; + prop->Exclusion = eaxsrcprops.lExclusion; + prop->ExclusionLFRatio = eaxsrcprops.flExclusionLFRatio; + prop->OutsideVolumeHF = eaxsrcprops.lOutsideVolumeHF; + prop->DopplerFactor = eaxsrcprops.flDopplerFactor; + prop->RolloffFactor = eaxsrcprops.flRolloffFactor; + prop->RoomRolloffFactor = eaxsrcprops.flRoomRolloffFactor; + prop->AirAbsorptionFactor = eaxsrcprops.flAirAbsorptionFactor; + prop->Flags = eaxsrcprops.ulFlags; + + hr = mBufferReverb->Get(guid, EAXSOURCE_ACTIVEFXSLOTID, NULL, 0, &eaxslots, sizeof(EAXACTIVEFXSLOTS), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + FMOD_memcpy(&guidfx0, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&guidfx2, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + FMOD_memcpy(&guidfx3, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + + if (!memcmp(&eaxslots.guidActiveFXSlots[0], &guidfx0, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + else if (!memcmp(&eaxslots.guidActiveFXSlots[0], &guidfx2, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE1; + } + else if (!memcmp(&eaxslots.guidActiveFXSlots[0], &guidfx3, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE2; + } + else + { + noActiveSlots++; + } + + if (!memcmp(&eaxslots.guidActiveFXSlots[1], &guidfx0, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + else if (!memcmp(&eaxslots.guidActiveFXSlots[1], &guidfx2, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE1; + } + else if (!memcmp(&eaxslots.guidActiveFXSlots[1], &guidfx3, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE2; + } + else + { + noActiveSlots++; + } + + if (noActiveSlots == 2) // If no slots have been assigned, assign to slot 0 + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + + return FMOD_OK; +} + +} + +#endif diff --git a/win32/src/fmod_channel_dsound_eax4.h b/win32/src/fmod_channel_dsound_eax4.h new file mode 100755 index 0000000..1446057 --- /dev/null +++ b/win32/src/fmod_channel_dsound_eax4.h @@ -0,0 +1,25 @@ +#ifndef _FMOD_CHANNEL_DSOUND_EAX4_H +#define _FMOD_CHANNEL_DSOUND_EAX4_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_dsound.h" + +namespace FMOD +{ + class ChannelDSoundEAX4 : public ChannelDSound + { + public: + + ChannelDSoundEAX4() { }; + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ + +#endif diff --git a/win32/src/fmod_channel_dsound_i3dl2.cpp b/win32/src/fmod_channel_dsound_i3dl2.cpp new file mode 100755 index 0000000..0bd8cbe --- /dev/null +++ b/win32/src/fmod_channel_dsound_i3dl2.cpp @@ -0,0 +1,188 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_I3DL2 + +#include "fmod_3dl2.h" +#include "fmod_channel_dsound_i3dl2.h" +#include "fmod_output_dsound.h" + + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundI3DL2::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + I3DL2_BUFFERPROPERTIES dsprops; + GUID guid; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + NOW SET CHANNEL PROPERTIES + */ + dsprops.lDirect = prop->Direct; + dsprops.lDirectHF = prop->DirectHF; + dsprops.lRoom = prop->Room; + dsprops.lRoomHF = prop->RoomHF; + dsprops.flRoomRolloffFactor = prop->RoomRolloffFactor; + dsprops.Obstruction.flLFRatio = prop->ObstructionLFRatio; + dsprops.Obstruction.lHFLevel = prop->Obstruction; + dsprops.Occlusion.flLFRatio = prop->OcclusionLFRatio; + dsprops.Occlusion.lHFLevel = prop->Occlusion; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_BufferProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_I3DL2BUFFER_ALL, NULL, 0, &dsprops, sizeof(I3DL2_BUFFERPROPERTIES)); + if (FAILED(hr)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelDSoundI3DL2::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + HRESULT hr; + I3DL2_BUFFERPROPERTIES dsprops; + GUID guid; + ULONG ulReceived; + + if (!mOutputDSound) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mBuffer3D) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + OBTAIN REVERB INTERFACE + */ + if (!mBufferReverb) + { + if (mFlags & CHANNELREAL_FLAG_PAUSED && mOutputDSound->mDirectXVersion >= 8) + { + mBuffer->AcquireResources(DSBPLAY_LOCHARDWARE, 0, NULL); + } + + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + hr = mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb); + if (hr != DS_OK) + { + return FMOD_ERR_UNSUPPORTED; + } + } + + /* + Get reverb properties + */ + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_BufferProperties, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, DSPROPERTY_I3DL2BUFFER_ALL, NULL, 0, &dsprops, sizeof(I3DL2_BUFFERPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Direct = dsprops.lDirect; + prop->DirectHF = dsprops.lDirectHF; + prop->Room = dsprops.lRoom; + prop->RoomHF = dsprops.lRoomHF; + prop->Obstruction = dsprops.Obstruction.lHFLevel; + prop->ObstructionLFRatio = dsprops.Obstruction.flLFRatio; + prop->Occlusion = dsprops.Occlusion.lHFLevel; + prop->OcclusionLFRatio = dsprops.Occlusion.flLFRatio; + prop->OcclusionRoomRatio = 0; + prop->OcclusionDirectRatio = 0; + prop->Exclusion = 0; + prop->ExclusionLFRatio = 0; + prop->OutsideVolumeHF = 0; + prop->DopplerFactor = 0; + prop->RolloffFactor = 0; + prop->RoomRolloffFactor = dsprops.flRoomRolloffFactor; + prop->AirAbsorptionFactor = 0; + prop->Flags = 0; + + return FMOD_OK; +} + +} + +#endif diff --git a/win32/src/fmod_channel_dsound_i3dl2.h b/win32/src/fmod_channel_dsound_i3dl2.h new file mode 100755 index 0000000..2d84379 --- /dev/null +++ b/win32/src/fmod_channel_dsound_i3dl2.h @@ -0,0 +1,25 @@ +#ifndef _FMOD_CHANNEL_DSOUND_I3DL2_H +#define _FMOD_CHANNEL_DSOUND_I3DL2_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_I3DL2 + +#include "fmod_channel_dsound.h" + +namespace FMOD +{ + class ChannelDSoundI3DL2 : public ChannelDSound + { + public: + + ChannelDSoundI3DL2() {}; + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_I3DL2 */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_channel_openal.cpp b/win32/src/fmod_channel_openal.cpp new file mode 100755 index 0000000..88561f7 --- /dev/null +++ b/win32/src/fmod_channel_openal.cpp @@ -0,0 +1,2441 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL + +#include "fmod_3d.h" +#include "fmod_channel_openal.h" +#include "fmod_debug.h" +#ifdef FMOD_SUPPORT_FSB +# include "fmod_codec_fsb.h" +#endif +#ifdef FMOD_SUPPORT_MPEG +# include "fmod_codec_mpeg.h" +#endif +#if defined(FMOD_SUPPORT_WAV) && defined(FMOD_SUPPORT_IMAADPCM) +# include "fmod_codec_wav.h" +# include "fmod_codec_wav_imaadpcm.h" +#endif +#include "fmod_dsp_codec.h" +#include "fmod_dsp_fft.h" +#include "fmod_dsp_resampler.h" +#include "fmod_dsp_wavetable.h" +#include "fmod_dspi.h" +#include "fmod_sample_openal.h" +#include "fmod_systemi.h" + +#include "fmod_output_openal.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +ChannelOpenAL::ChannelOpenAL() +{ + mDSPWaveTable = NULL; + mDSPHead = NULL; + mDSPResampler = NULL; + mDSPCodec = NULL; + mDSPLowPass = NULL; + mDSP = NULL; + mDSPConnection = NULL; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::init(int index, SystemI *system, Output *output, DSPI *dspmixtarget) +{ + FMOD_RESULT result; + FMOD_DSP_DESCRIPTION_EX descriptionex; + + ChannelReal::init(index, system, output, dspmixtarget); + + /* + Create a head unit that things can connect to + */ + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD Channel DSPHead Unit"); + descriptionex.version = 0x00010100; + descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER; + descriptionex.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + + mDSPHead = &mDSPHeadMemory; + + result = mSystem->createDSP(&descriptionex, &mDSPHead, false); + if (result != FMOD_OK) + { + return result; + } + + /* + Make sure nothing else can connect to mDSPHead + */ + mDSPHead->mDescription.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD; + + /* + Create a lowpass unit + */ + if (mSystem->mFlags & FMOD_INIT_SOFTWARE_OCCLUSION || mSystem->mFlags & FMOD_INIT_SOFTWARE_HRTF) + { + result = mSystem->createDSPByType(FMOD_DSP_TYPE_LOWPASS_SIMPLE, &mDSPLowPass); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Create a wave table unit + */ + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD WaveTable Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = 1; + descriptionex.mCategory = FMOD_DSP_CATEGORY_WAVETABLE; + descriptionex.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; + descriptionex.mDSPSoundCard = mDSPHead; + + descriptionex.read = NULL; /* DSPWavetable uses DSPWaveTable::execute not a read callback. */ + descriptionex.setparameter = DSPWaveTable::setParameterCallback; + descriptionex.getparameter = DSPWaveTable::getParameterCallback; + descriptionex.setposition = DSPWaveTable::setPositionCallback; + + mDSPWaveTable = &mDSPWaveTableMemory; + + result = mSystem->createDSP(&descriptionex, (DSPI **)&mDSPWaveTable, false); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPWaveTable->setUserData((void **)this); + if (result != FMOD_OK) + { + return result; + } + result = mDSPWaveTable->setTargetFrequency(44100); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = -mMaxFrequency; + + // Clear OpenAL sources assiociated with this channel + mNumSources = 0; + FMOD_memset(mSources, 0, 16 * sizeof(ALuint)); + + // OutputOpenAL that this channel belongs to + mOutputOAL = (OutputOpenAL *)output; + + // Allocate memory for final stream buffer + mBuffer = (short *)FMOD_Memory_Calloc(mOutputOAL->mBufferLength * sizeof(short)); + + // Allocate memory for temporary deinterleave stream buffer + mTempBuffer = (float *)FMOD_Memory_Calloc(mOutputOAL->mBufferLength * sizeof(float)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::close() +{ + FMOD_RESULT result; + + result = ChannelReal::close(); + if (result != FMOD_OK) + { + return result; + } + + if (mTempBuffer) + { + FMOD_Memory_Free(mTempBuffer); + mTempBuffer = NULL; + } + + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + mBuffer = NULL; + } + + if (mDSPWaveTable) + { + mDSPWaveTable->release(false); /* false = dont free this, as it is not alloced. */ + mDSPWaveTable = NULL; + } + + if (mDSPHead) + { + mDSPHead->release(false); /* false = dont free this, as it is not alloced. */ + mDSPHead= NULL; + } + + if (mDSPResampler) + { + mDSPResampler->release(); + mDSPResampler = NULL; + } + + mDSPCodec = NULL; /* Don't free, it points to a pool codec. */ + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setupDSPCodec(DSPI *dsp) +{ +#ifdef FMOD_SUPPORT_DSPCODEC + FMOD_RESULT result = FMOD_OK; + DSPCodec *dspcodec = NULL; + Codec *codec = NULL; + Codec *soundcodec = NULL; + SampleOpenAL *sample = NULL; + + dspcodec = SAFE_CAST(DSPCodec, dsp); + codec = SAFE_CAST(Codec, dspcodec->mCodec); + soundcodec = mSound->mCodec ? mSound->mCodec : mSound->mSubSoundParent->mCodec; + sample = SAFE_CAST(SampleOpenAL, mSound); + + if (!soundcodec) + { + return FMOD_ERR_INTERNAL; + } + + codec->mPCMBufferLength = soundcodec->mPCMBufferLength; + codec->mPCMBufferLengthBytes = soundcodec->mPCMBufferLength * sizeof(short) * dsp->mDescription.channels; + + if (!codec->waveformat) + { + return FMOD_ERR_INTERNAL; + } + + soundcodec->mDescription.getwaveformat(soundcodec, mSound->mSubSoundIndex, codec->waveformat); + + codec->mFlags = soundcodec->mFlags; + dspcodec->mCodec->mFile = &dspcodec->mMemoryFile; + + dspcodec->mMemoryFile.init(mSystem, mSound->mLengthBytes, 0); + + dspcodec->mMemoryFile.mMem = sample->mBuffer; + + /* + Copy codec specific things from the source sound to the dspcodec used in this channel. + */ + if (0) + { + } + #if defined(FMOD_SUPPORT_WAV) && defined(FMOD_SUPPORT_IMAADPCM) + if (mSound->mType == FMOD_SOUND_TYPE_WAV && mSound->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + CodecWav *srcwav = (CodecWav *)soundcodec; + CodecWav *destwav = (CodecWav *)codec; + + destwav->mSamplesPerADPCMBlock = srcwav->mSamplesPerADPCMBlock; + destwav->mReadBufferLength = srcwav->mReadBufferLength; + } + #endif + #if defined(FMOD_SUPPORT_WAV) && defined(FMOD_SUPPORT_RAW) && defined(FMOD_SUPPORT_IMAADPCM) + else if (mSound->mType == FMOD_SOUND_TYPE_RAW && mSound->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + CodecRaw *srcwav = (CodecRaw *)soundcodec; + CodecWav *destwav = (CodecWav *)codec; + + destwav->mSamplesPerADPCMBlock = srcwav->mSamplesPerADPCMBlock; + destwav->mReadBufferLength = srcwav->mReadBufferLength; + } + #endif + #if defined(FMOD_SUPPORT_FSB) + else if (mSound->mType == FMOD_SOUND_TYPE_FSB) + { + CodecFSB *fsb = (CodecFSB *)soundcodec; + + if (0) + { + } + #if defined(FMOD_SUPPORT_MPEG) + else if (mSound->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + CodecMPEG *srcmpeg = (CodecMPEG *)fsb->mMPEG; + CodecMPEG *destmpeg = (CodecMPEG *)codec; + + destmpeg->mPCMFrameLengthBytes = codec->waveformat[0].channels * 2304; + } + #endif + #if defined(FMOD_SUPPORT_IMAADPCM) + else if (mSound->mFormat == FMOD_SOUND_FORMAT_IMAADPCM) + { + CodecWav *srcwav = fsb->mADPCM; + CodecWav *destwav = (CodecWav *)codec; + + destwav->mSamplesPerADPCMBlock = srcwav->mSamplesPerADPCMBlock; + destwav->mReadBufferLength = codec->waveformat[0].channels * 36; + } + #endif + + fsb->mDescription.getwaveformat(fsb, mSound->mSubSoundIndex, codec->waveformat); + } + #endif + #if defined(FMOD_SUPPORT_MPEG) + else if (mSound->mFormat == FMOD_SOUND_FORMAT_MPEG) + { + CodecMPEG *srcmpeg = (CodecMPEG *)soundcodec; + CodecMPEG *destmpeg = (CodecMPEG *)codec; + + destmpeg->mPCMFrameLengthBytes = srcmpeg->mPCMFrameLengthBytes; + } + #endif + + dsp->mDescription.channels = mSound->mChannels; + + result = dsp->setTargetFrequency((int)mParent->mChannelGroup->mDSPHead->mDefaultFrequency); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = 0; + mDSPCodec = dspcodec; + mDSPCodec->mLength = mSound->mLength; + mDSPCodec->mDefaultFrequency = mSound->mDefaultFrequency; + mDSPCodec->mLoopCount = mLoopCount; + + mDSPCodec->mNoDMA->mLoopStart = mLoopStart; + mDSPCodec->mNoDMA->mLoopLength = mLoopLength; + mDSPCodec->mNoDMA->mMode = mMode; + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::alloc() +{ + FMOD_RESULT result; + + /* + Standard PCM *wavetable* playback. + */ + if (!(mMode & FMOD_CREATECOMPRESSEDSAMPLE)) + { + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INTERNAL; + } + + mDSPCodec = 0; + + /* + Disconnect existing nodes first. + */ + result = mDSPHead->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + if (mDSPLowPass) + { + result = mDSPLowPass->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + } + result = mDSPWaveTable->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + + if (mDSPLowPass) + { + result = mDSPHead->addInputInternal(mDSPLowPass, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + result = mDSPLowPass->addInputInternal(mDSPWaveTable, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mDSPHead->addInputInternal(mDSPWaveTable, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + } + + result = setLoopPoints(mSound->mLoopStart, mSound->mLoopLength); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = -mMaxFrequency; + + dspwave->mChannel = this; + dspwave->mSound = mSound; + dspwave->mPosition.mHi = 0; + dspwave->mPosition.mLo = 0; + dspwave->mDirection = DSPWAVETABLE_SPEEDDIR_FORWARDS; + mDSPHead->setActive(false); + if (mDSPLowPass) + { + mDSPLowPass->setActive(false); + } + mDSPWaveTable->setFinished(false); + mDSPWaveTable->setActive(false); + } + else + /* + Advanced - Compressed codec playback with external resampler unit. + */ + { +#ifdef FMOD_SUPPORT_DSPCODEC + DSPCodec *dsp; + bool dspfinished = true; + + if (mDSPCodec) + { + mDSPCodec->getFinished(&dspfinished); + } + + #ifdef FMOD_DSP_QUEUED + if (!dspfinished) /* Still active. Must have queued up a disconnect in a stop/start scenario. */ + { + mSystem->flushDSPConnectionRequests(); + } + #endif + + /* + Disconnect existing nodes first. + */ + result = mDSPHead->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + if (mDSPLowPass) + { + result = mDSPLowPass->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + } + if (mDSPWaveTable) + { + result = mDSPWaveTable->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + } + + /* + Grab a codec from the pool based on format. + */ + result = mSystem->allocateDSPCodec(mSound->mFormat, &dsp); + if (result != FMOD_OK) + { + return result; + } + + if (!dspfinished) + { + FMOD_OS_CriticalSection_Enter(mSystem->mDSPCrit); + } + + result = setupDSPCodec(dsp); + + if (!dspfinished) + { + FMOD_OS_CriticalSection_Leave(mSystem->mDSPCrit); + } + if (result != FMOD_OK) + { + return result; + } + + if (mDSPLowPass) + { + result = mDSPHead->addInputInternal(mDSPLowPass, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + result = mDSPLowPass->addInputInternal(dsp, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + } + else + { + result = mDSPHead->addInputInternal(dsp, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + } + + mDSPHead->setActive(false); + + dsp->setFinished(false); + dsp->setActive(true); +#else + return FMOD_ERR_INTERNAL; +#endif + } + + /* + Assign OpenAL sources to the channel + */ + FMOD_SPEAKERMODE speakerMode = FMOD_SPEAKERMODE_STEREO; + int requiredSources = 2; + + /* Maximum number of sources needed is equal to the speaker mode since + the DSP network will mix to that before it gets to the OpenALChannel */ + switch (mParent->mSpeakerMode) + { + case FMOD_SPEAKERMODE_MONO : requiredSources = 1; + break; + case FMOD_SPEAKERMODE_STEREO : requiredSources = 2; + break; + case FMOD_SPEAKERMODE_QUAD : requiredSources = 4; + break; + case FMOD_SPEAKERMODE_5POINT1 : requiredSources = 6; + break; + case FMOD_SPEAKERMODE_7POINT1 : requiredSources = 8; + break; + default : requiredSources = 2; + break; + } + + /* If the number of channels in the source is less than the speaker mode + then we only need enough sources for the sound source */ + if (mSound->mChannels < requiredSources) + { + requiredSources = mSound->mChannels; + } + + // Find enough sources for the number of sound channels required + for (int i = 0; i < mOutputOAL->mNumSources; i++) + { + if (!mOutputOAL->mSources[i].used) + { + // Store the source in the channel + mSources[mNumSources] = (SourceOpenAL*)&mOutputOAL->mSources[i]; + mSources[mNumSources]->used = true; + + // Check if we have found enough sources + if (++mNumSources == requiredSources) + { + break; + } + } + } + + // Setup the channel with any EAX version specific initialisation + setupChannel(); + + if (mDSPResampler || mDSPCodec) + { + DSPResampler *dspresampler; + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + dspresampler = SAFE_CAST(DSPResampler, mDSPCodec); + } + else +#endif + { + dspresampler = SAFE_CAST(DSPResampler, mDSPResampler); + } + if (!dspresampler) + { + return FMOD_ERR_INVALID_PARAM; + } + + return dspresampler->setFrequency(mSound->mDefaultFrequency); + } + else + { + DSPWaveTable *dspwave; + + dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INVALID_PARAM; + } + + return dspwave->setFrequency(mSound->mDefaultFrequency); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::alloc(DSPI *dsp) +{ + FMOD_RESULT result; + FMOD_SOUND_FORMAT format; + int channels; + + mDSPCodec = 0; + + format = dsp->mDescription.mFormat; + channels = dsp->mDescription.channels; + + { + FMOD_DSP_DESCRIPTION_EX descriptionex; + + FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + FMOD_strcpy(descriptionex.name, "FMOD Resampler Unit"); + descriptionex.version = 0x00010100; + descriptionex.channels = 0; + descriptionex.mCategory = FMOD_DSP_CATEGORY_RESAMPLER; + + result = mSystem->createDSP(&descriptionex, (DSPI **)&mDSPResampler); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPResampler->setUserData((void **)this); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPResampler->setTargetFrequency((int)mParent->mChannelGroup->mDSPHead->mDefaultFrequency); + if (result != FMOD_OK) + { + return result; + } + + mMinFrequency = 0; + } + + + /* + Connect the mixer to the resampler and the resampler to the reader + */ + result = mDSPHead->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + if (mDSPLowPass) + { + result = mDSPLowPass->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + } + result = mDSPWaveTable->disconnectFromInternal(0, false); + if (result != FMOD_OK) + { + return result; + } + + result = mDSPHead->addInputInternal(mDSPResampler, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + result = mDSPResampler->addInputInternal(dsp, false, 0, 0, false); + if (result != FMOD_OK) + { + return result; + } + + { + DSPResampler *resampler = SAFE_CAST(DSPResampler, mDSPResampler); + + resampler->mLength = mLength; + resampler->mLoopCount = mLoopCount; + resampler->mNoDMA->mLoopStart = mLoopStart; + resampler->mNoDMA->mLoopLength = mLoopLength; + resampler->mNoDMA->mMode = mMode; + + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INTERNAL; + } + dspwave->mSound = 0; + } + + mDSPHead->setActive(false); + mDSPResampler->setFinished(false); + mDSPResampler->setActive(false); + + dsp->setActive(false); + + /* + Assign OpenAL sources to the channel + */ + FMOD_SPEAKERMODE speakerMode = FMOD_SPEAKERMODE_STEREO; + int requiredSources = 2; + + /* Maximum number of sources needed is equal to the speaker mode since + the DSP network will mix to that before it gets to the OpenALChannel */ + switch (mParent->mSpeakerMode) + { + case FMOD_SPEAKERMODE_MONO : requiredSources = 1; + break; + case FMOD_SPEAKERMODE_STEREO : requiredSources = 2; + break; + case FMOD_SPEAKERMODE_QUAD : requiredSources = 4; + break; + case FMOD_SPEAKERMODE_5POINT1 : requiredSources = 6; + break; + case FMOD_SPEAKERMODE_7POINT1 : requiredSources = 8; + break; + default : requiredSources = 2; + break; + } + + /* If the number of channels in the source is less than the speaker mode + then we only need enough sources for the sound source */ + if (mSound->mChannels < requiredSources) + { + requiredSources = mSound->mChannels; + } + + // Find enough sources for the number of sound channels required + for (int i = 0; i < mOutputOAL->mNumSources; i++) + { + if (!mOutputOAL->mSources[i].used) + { + // Store the source in the channel + mSources[mNumSources] = (SourceOpenAL*)&mOutputOAL->mSources[i]; + mSources[mNumSources]->used = true; + + // Check if we have found enough sources + if (++mNumSources == requiredSources) + { + break; + } + } + } + + // Setup the channel with any EAX version specific initialisation + setupChannel(); + + if (mDSPResampler || mDSPCodec) + { + DSPResampler *dspresampler; + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + dspresampler = SAFE_CAST(DSPResampler, mDSPCodec); + } + else +#endif + { + dspresampler = SAFE_CAST(DSPResampler, mDSPResampler); + } + if (!dspresampler) + { + return FMOD_ERR_INVALID_PARAM; + } + + return dspresampler->setFrequency(mSound->mDefaultFrequency); + } + else + { + DSPWaveTable *dspwave; + + dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INVALID_PARAM; + } + + return dspwave->setFrequency(mSound->mDefaultFrequency); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::start() +{ + SampleOpenAL *sampleopenal; + + sampleopenal = SAFE_CAST(SampleOpenAL, mSound); + if (!sampleopenal) + { + return FMOD_ERR_INVALID_PARAM; + } + + // Tell the mixer to fill up all buffers with initial data + mInitialFill = true; + + for (int count = 0; count < mNumSources; count++) + { + /* + Setup the OpenAL source + */ + if (sampleopenal->mMode & FMOD_3D) + { + mOutputOAL->mOALFnTable.alSourcei(mSources[count]->sid, AL_SOURCE_RELATIVE, AL_FALSE); + } + else + { + /* + OpenAL doesn't have 2D sounds, so set up some 3D properties to fudge it + */ + mOutputOAL->mOALFnTable.alSourcei(mSources[count]->sid, AL_SOURCE_RELATIVE, AL_TRUE); + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_REFERENCE_DISTANCE, 1.0f); + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_MAX_DISTANCE, 1.0f); + switch (sampleopenal->mChannels) + { + case 0x01: // Mono + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.0f, 0.0f, 0.0f); + break; + case 0x02: // Stereo + switch (count) + { + case 0x00: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, -0.866f); + break; + case 0x01: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, -0.866f); + break; + } + break; + case 0x04: // Quad + switch (count) + { + case 0x00: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, -0.866f); + break; + case 0x01: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, -0.866f); + break; + case 0x02: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, 0.866f); + break; + case 0x03: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, 0.866f); + break; + } + break; + case 0x06: // 5.1 + switch (count) + { + case 0x00: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, -0.866f); + break; + case 0x01: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, -0.866f); + break; + case 0x02: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.0f, 0.0f, -1.0f); + break; + case 0x03: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.0f, 0.0f, 0.0f); + break; + case 0x04: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, 0.866f); + break; + case 0x05: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, 0.866f); + break; + } + break; + case 0x08: // 7.1 + switch (count) + { + case 0x00: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, -0.866f); + break; + case 0x01: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, -0.866f); + break; + case 0x02: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.0f, 0.0f, -1.0f); + break; + case 0x03: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.0f, 0.0f, 0.0f); + break; + case 0x04: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -0.5f, 0.0f, 0.866f); + break; + case 0x05: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.5f, 0.0f, 0.866f); + break; + case 0x06: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, -1.0f, 0.0f, 0.0f); + break; + case 0x07: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 1.0f, 0.0f, 0.0f); + break; + } + break; + default: + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, 0.0f, 0.0f, 0.0f); + break; + } + } + mOutputOAL->mOALFnTable.alSourcei(mSources[count]->sid, AL_LOOPING, AL_FALSE); + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_PITCH, 1.0f); + } + + if (!(mFlags & CHANNELREAL_FLAG_PAUSED)) + { + mDSPHead->setActive(true); + } + + if (mSound) + { + mDSPWaveTable->setActive(true); + } + if (mDSPResampler) + { + mDSPResampler->setActive(true); + } + if (mDSPLowPass) + { + mDSPLowPass->setActive(true); + } +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->setActive(true); + } +#endif + if (mDSP) + { + mDSP->setActive(true); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::stop() +{ + FMOD_RESULT result; + int count; + int i; + + if (mDSPHead) + { + mDSPHead->setActive(false); + mDSPHead->disconnectAll(false, true); + } + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->setFinished(true, true); + result = mDSPCodec->disconnectFromInternal(0, false); /* If we leave it hanging in there something else might double connect to it, causing an alloc. */ + if (result != FMOD_OK) + { + return result; + } + mDSPCodec->setActive(false); + mDSPCodec->freeFromPool(); + mDSPCodec = 0; + } +#endif + + if (mDSPResampler) + { + mDSPResampler->setFinished(true, true); + mDSPResampler->setActive(false); + + mDSPResampler->release(); + mDSPResampler = 0; + } + + if (mDSPWaveTable) + { + mDSPWaveTable->setFinished(true, true); + mDSPWaveTable->setActive(false); + } + + if (mDSP) + { + DSPI *prev; + int numoutputs; + + result = mDSP->getNumOutputs(&numoutputs, false); + if (result != FMOD_OK) + { + return result; + } + + for(count = 0; count < numoutputs; count++) + { + result = mDSP->getOutput(count, &prev, 0, false); + if (result == FMOD_OK) + { + result = prev->disconnectFromInternal(mDSP, false); + if (result != FMOD_OK) + { + return result; + } + } + } + } + + // Since this updates the channel's mSources it needs to be syncronized with the updateChannel call (called from mixer) + FMOD_OS_CriticalSection_Enter(mOutputOAL->mSystem->mDSPCrit); + + for(i = 0; i < mNumSources; i++) + { + mOutputOAL->mOALFnTable.alSourceStop(mSources[i]->sid); + mOutputOAL->mOALFnTable.alSourcei(mSources[i]->sid, AL_BUFFER, 0); + mSources[i]->used = false; + mSources[i] = NULL; + } + + mNumSources = 0; + + FMOD_OS_CriticalSection_Leave(mOutputOAL->mSystem->mDSPCrit); + + return ChannelReal::stop(); +} + + +/* +[ + [DESCRIPTION] + Pause or unpause the channel + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setPaused(bool paused) +{ + FMOD_RESULT result = FMOD_OK; + ALuint sourceIDs[16] = {0}; + + // Pause or unpause the DSP + result = mDSPHead->setActive(!paused); + if (result != FMOD_OK) + { + return result; + } + + // Get the IDs of all the sources used by this channel + for (int i = 0; i < mNumSources; i++) + { + sourceIDs[i] = mSources[i]->sid; + } + + /* + To avoid spamming the hardware only pause when required (i.e. when not already paused) + */ + // If we want to pause... + if (paused) + { + // ...and we were not already paused + if (!(mFlags & CHANNELREAL_FLAG_PAUSED)) + { + mOutputOAL->mOALFnTable.alSourcePausev(mNumSources, sourceIDs); + } + } + // If we want to unpause + else + { + // ... and we were already paused + if (mFlags & CHANNELREAL_FLAG_PAUSED) + { + mOutputOAL->mOALFnTable.alSourcePlayv(mNumSources, sourceIDs); + } + } + + return(ChannelReal::setPaused(paused)); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setVolume(float volume) +{ + int count; + + if (mParent->mFlags & CHANNELI_FLAG_REALMUTE) + { + volume = 0; + } + + if (mOutputOAL->mReverbVersion == REVERB_VERSION_NONE) + { + volume *= (1.0f - mParent->mDirectOcclusion) * + (1.0f - mParent->mUserDirectOcclusion) * + mParent->mChannelGroup->mRealDirectOcclusionVolume; + } + + volume *= mParent->mChannelGroup->mRealVolume; + if (mMode & FMOD_3D) + { + if (mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback) + { + volume *= mParent->mVolume3D; + } + + volume *= mParent->mConeVolume3D; + } + if (mSound && mSound->mSoundGroup) + { + volume *= mSound->mSoundGroup->mVolume; + } + volume *= mParent->mFadeVolume; + + for (count = 0; count < mNumSources; count++) + { + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_GAIN, volume); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setFrequency(float frequency) +{ + float basefrequency = mSound->mDefaultFrequency; + int count; + ALfloat pitch = 0.0f; + + frequency *= mParent->mChannelGroup->mRealPitch; + + /* + Convert frequency to pitch multiplier + */ + pitch = frequency/basefrequency; + + /* + This needs to be calculated properly + */ + for (count = 0; count < mNumSources; count++) + { + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_PITCH, pitch); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setPan(float pan, float fbpan) +{ + int count; + + if (mSound->mMode & FMOD_3D) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + Use 3D position, left speaker is at -30 degrees (sin -30, 0, -cos -30), right speaker is at +30 degrees (sin 30, 0, -cos 30) + */ + for (count = 0; count < mNumSources; count++) + { + mOutputOAL->mOALFnTable.alSource3f(mSources[count]->sid, AL_POSITION, FMOD_SIN(30.0f * pan * FMOD_PI / 180.0f), 0.0f, -FMOD_COS(30.0f * pan * FMOD_PI / 180.0f)); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setPosition(unsigned int position, FMOD_TIMEUNIT postype) +{ + unsigned int pcm, endpoint; + int channels; + FMOD_SOUND_FORMAT format; + float frequency; + + if (mSubChannelIndex > 0) + { + return FMOD_OK; + } + + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } +#ifdef FMOD_SUPPORT_DSPCODEC + else if (mDSPCodec) + { + channels = mDSPCodec->mDescription.channels; + format = FMOD_SOUND_FORMAT_PCMFLOAT; + frequency = mDSPCodec->mDefaultFrequency; + } +#endif + else if (mSound) + { + channels = mSound->mChannels; + format = mSound->mFormat; + frequency = mSound->mDefaultFrequency; + } + else if (mDSPResampler) + { + channels = mDSPResampler->mDescription.channels; + format = FMOD_SOUND_FORMAT_PCMFLOAT; + frequency = mDSPResampler->mDefaultFrequency; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (postype == FMOD_TIMEUNIT_PCM) + { + pcm = position; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getSamplesFromBytes(position, &pcm, channels, format); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + pcm = (unsigned int)((float)position / 1000.0f * frequency); + } + + if (mSound) + { + if (mMode & FMOD_LOOP_OFF) + { + endpoint = mSound->mLength - 1; + } + else + { + endpoint = mLoopStart + mLoopLength - 1; + } + } + else + { + endpoint = (unsigned int)-1; + } + + if (pcm > endpoint) + { + return FMOD_ERR_INVALID_POSITION; + } + + /* + Recurse through channel head downwards calling setposition to all dsp units. + */ +#ifdef FMOD_SUPPORT_DSPCODEC + else if (mDSPCodec) + { + return mDSPCodec->setPosition(pcm, false); + } +#endif + else if (mDSPWaveTable) + { + return mDSPWaveTable->setPosition(pcm, false); + } + else if (mDSPResampler) + { + return mDSPResampler->setPosition(pcm, true); + } + else + { + return mDSPHead->setPosition(pcm, true); /* Channel based sound is always wavetable, dspcodec, or resampler. If this is called they must have done a dsp connection and it will flush. */ + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::getPosition(unsigned int *position, FMOD_TIMEUNIT postype) +{ + int channels; + FMOD_SOUND_FORMAT format; + float frequency; + + if (!position) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES) + { + return FMOD_ERR_FORMAT; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + DSPResampler *dspcodec = SAFE_CAST(DSPResampler, mDSPCodec); + if (!dspcodec) + { + return FMOD_ERR_INVALID_PARAM; + } + + channels = mDSPCodec->mDescription.channels; + format = FMOD_SOUND_FORMAT_PCMFLOAT; + frequency = mSound->mDefaultFrequency; + + mPosition = dspcodec->mPosition.mHi; + } + else +#endif + if (mSound) + { + DSPWaveTable *dspwave = SAFE_CAST(DSPWaveTable, mDSPWaveTable); + if (!dspwave) + { + return FMOD_ERR_INVALID_PARAM; + } + + channels = mSound->mChannels; + format = mSound->mFormat; + frequency = mSound->mDefaultFrequency; + + mPosition = dspwave->mPosition.mHi; + } + else if (mDSPResampler) + { + channels = mDSPResampler->mDescription.channels; + format = FMOD_SOUND_FORMAT_PCMFLOAT; + frequency = mDSPResampler->mDefaultFrequency; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (postype == FMOD_TIMEUNIT_PCM) + { + *position = mPosition; + } + else if (postype == FMOD_TIMEUNIT_PCMBYTES) + { + SoundI::getBytesFromSamples(mPosition, position, channels, format); + } + else if (postype == FMOD_TIMEUNIT_MS) + { + *position = (unsigned int)((float)mPosition / frequency * 1000.0f); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setLoopPoints(unsigned int loopstart, unsigned int looplength) +{ + FMOD_RESULT result; + + result = ChannelReal::setLoopPoints(loopstart, looplength); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + + if (mDSPCodec) + { + mDSPCodec->mNoDMA->mLoopStart = mLoopStart; + mDSPCodec->mNoDMA->mLoopLength = mLoopLength; + } + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setLoopCount(int loopcount) +{ + FMOD_RESULT result; + + result = ChannelReal::setLoopCount(loopcount); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + + if (mDSPCodec) + { + mDSPCodec->mLoopCount = mLoopCount; + } + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + + result = ChannelReal::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + +#ifdef FMOD_SUPPORT_DSPCODEC + + if (mDSPCodec) + { + mDSPCodec->mNoDMA->mMode = mMode; + } + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::set3DAttributes() +{ + FMOD_RESULT result; + int count; + int numlisteners; + ALfloat mindistance; + ALfloat maxdistance; + ALfloat originalposition[] = { 0.0, 0.0, 0.0 }; + ALfloat position[] = { 0.0, 0.0, 0.0 }; + ALfloat velocity[] = { 0.0, 0.0, 0.0 }; + + if (!(mSound->mMode & FMOD_3D)) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputOAL) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mSystem->get3DNumListeners(&numlisteners); + if (result != FMOD_OK) + { + return result; + } + + if (numlisteners == 1) + { + position[0] = mParent->mPosition3D.x; + position[1] = mParent->mPosition3D.y; + position[2] = mParent->mPosition3D.z; + } + else + { + position[0] = 0; + position[1] = 0; + position[2] = mParent->mDistance; + } + + if (numlisteners == 1) + { + velocity[0] = mParent->mVelocity3D.x; + velocity[1] = mParent->mVelocity3D.y; + velocity[2] = mParent->mVelocity3D.z; + } + else + { + velocity[0] = 0; + velocity[1] = 0; + velocity[2] = 0; + } + + if (!(mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED)) + { + position[2] *= -1; + velocity[2] *= -1; + } + + if (mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback) + { + FMOD_VECTOR diff; + float distance; + FMOD_VECTOR pos; + + mindistance = 1.0f; + maxdistance = 1.0f; + + pos.x = position[0]; + pos.y = position[1]; + pos.z = position[2]; + + FMOD_Vector_Subtract(&pos, &mSystem->mListener[0].mPosition, &diff); + + distance = FMOD_Vector_GetLengthFast(&diff); + + if (distance > 0.0f) + { + FMOD_Vector_Scale(&diff, 1.0f / distance, &diff); + } + else + { + FMOD_Vector_Set(&diff, 0, 0, 0); + } + + FMOD_Vector_Add(&mSystem->mListener[0].mPosition, &diff, &pos); + + position[0] = pos.x; + position[1] = pos.y; + position[2] = pos.z; + + setVolume(mParent->mVolume); + } + else + { + mindistance = mParent->mMinDistance; + maxdistance = mParent->mMaxDistance; + + if (mParent->mConeOutsideAngle < 360.0f || mParent->mConeInsideAngle < 360.0f) /* Because the first part of this if statement calls setvolume all the time, we'll just call it here if cones are used. */ + { + setVolume(mParent->mVolume); + } + } + + originalposition[0] = position[0]; + originalposition[1] = position[1]; + originalposition[2] = position[2]; + + /* + Set the source properties + */ + for (count = 0; count < mNumSources; count++) + { + //mParent->mSpread L R FC SW BL BR + //make a table for each channel count and source index fraction: + //stereo -> -0.50f, 0.50f + //quad -> -0.50f, 0.50f, 0.0f, 0.0f + //5.1 -> -0.25f, 0.25f, 0.0f, 0.0f, -0.50f, 0.50f + //7.1 -> -0.17f, 0.17f, 0.0f, 0.0f, -0.50f, 0.50f, -0.33f, 0.33f + static float spreadAdjust[9][8]= + { + { 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + { 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + {-0.50f, 0.50f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + { 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + {-0.50f, 0.50f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + { 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + {-0.25f, 0.25f, 0.00f, 0.00f,-0.50f, 0.50f, 0.00f, 0.00f}, + { 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f}, + {-0.17f, 0.17f, 0.00f, 0.00f,-0.50f, 0.50f,-0.33f, 0.33f} + }; + + if (mParent->mSpread != 0.0f) + { + FMOD_VECTOR temp; + + temp.x = originalposition[0]; + temp.y = originalposition[1]; + temp.z = originalposition[2]; + FMOD_Vector_AxisRotate(&temp,&mSystem->mListener[0].mPosition,&mSystem->mListener[0].mUp,-spreadAdjust[mNumSources][count] * mParent->mSpread * FMOD_PI / 180.0f); + position[0] = temp.x; + position[1] = temp.y; + position[2] = temp.z; + } + + mOutputOAL->mOALFnTable.alSourcefv(mSources[count]->sid, AL_POSITION, position); + mOutputOAL->mOALFnTable.alSourcefv(mSources[count]->sid, AL_VELOCITY, velocity); + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_REFERENCE_DISTANCE, mindistance); + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_MAX_DISTANCE, maxdistance); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::set3DMinMaxDistance() +{ + int count; + + if (!(mSound->mMode & FMOD_3D)) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputOAL) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0; count < mNumSources; count++) + { + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_REFERENCE_DISTANCE, mParent->mMinDistance); + mOutputOAL->mOALFnTable.alSourcef(mSources[count]->sid, AL_MAX_DISTANCE, mParent->mMaxDistance); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::set3DOcclusion(float directOcclusion, float reverbOcclusion) +{ + if (mOutputOAL->mReverbVersion != REVERB_VERSION_NONE) + { + float directTransmission = (1.0f - directOcclusion) * + mParent->mChannelGroup->mRealDirectOcclusionVolume; + + if (directTransmission < 0.00001f) + { + directTransmission = 0.00001f; + } + + int directGain = int(100.0f * 20.0f * FMOD_LOG10(directTransmission)); + + float reverbTransmission = (1.0f - reverbOcclusion) * + mParent->mChannelGroup->mRealReverbOcclusionVolume; + if (reverbTransmission < 0.00001f) + { + reverbTransmission = 0.00001f; + } + int reverbGain = int(100.0f * 20.0f * FMOD_LOG10(reverbTransmission)); + + FMOD_REVERB_CHANNELPROPERTIES prop; + + getReverbProperties(&prop); + + prop.Direct = directGain >> 2; // quarter occlusion for low frequencies + prop.DirectHF = directGain; // full occlusion for high frequencies + prop.Room = reverbGain; + + return setReverbProperties(&prop); + } + else + { + return setVolume(mParent->mVolume); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + if (mMode & FMOD_2D) + { + return FMOD_ERR_NEEDS3D; + } + + return FMOD_ERR_UNSUPPORTED; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + if (mMode & FMOD_2D) + { + return FMOD_ERR_NEEDS3D; + } + + return FMOD_ERR_UNSUPPORTED; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::isPlaying(bool *isplaying, bool includethreadlatency) +{ + if (!isplaying) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFlags & CHANNELREAL_FLAG_ALLOCATED) + { + *isplaying = true; + } + else + { +#ifdef FMOD_SUPPORT_DSPCODEC + if (mDSPCodec) + { + mDSPCodec->getFinished(isplaying); + *isplaying = !*isplaying; + } + else +#endif + if (mDSPResampler) + { + mDSPResampler->getFinished(isplaying); + *isplaying = !*isplaying; + } + else + { + if (!mSound && !mDSP) + { + *isplaying = false; + } + else + { + mDSPWaveTable->getFinished(isplaying); + *isplaying = !*isplaying; + } + } + } + + if (!*isplaying) + { + mFlags &= ~(CHANNELREAL_FLAG_ALLOCATED | CHANNELREAL_FLAG_PLAYING); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal. + + [PARAMETERS] + 'spectrumarray' Pointer to an array of floats to receive spectrum data. Data range is 0-1. Decibels = 10.0f * (float)log10(val) * 2.0f; + 'windowsize' Number of PCM samples to analyze. The resulting spectrum placed in the spectrumarray parameter will be HALF the size of this value (ie 1024 will place 512 floats in spectrumarray). Must be a power of 2. (ie 128/256/512 etc). Min = 128. Max = 16384. + 'channeloffset' Channel to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' Pre-FFT window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the windowsize, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.<br> + The larger the windowsize, the more 'lag' the spectrum will seem to inherit. This is because the window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.<br> + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).<br> + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.<br> + + [PLATFORMS] + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW +] +*/ +FMOD_RESULT ChannelOpenAL::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) +{ +#ifdef FMOD_SUPPORT_GETSPECTRUM + FMOD_RESULT result; + +#ifdef FMOD_STATICFORPLUGINS + + result = FMOD_ERR_UNSUPPORTED; + +#else + + DSPFilter *dsphead; + float *buffer; + unsigned int position, length, bufferlength; + int numchannels, windowsize; + static DSPFFT fft; + + dsphead = (DSPFilter *)mDSPHead; + if (!dsphead) + { + return FMOD_ERR_INITIALIZATION; + } + + windowsize = numvalues * 2; + + if (windowsize != (1 << 7) && + windowsize != (1 << 8) && + windowsize != (1 << 9) && + windowsize != (1 << 10) && + windowsize != (1 << 11) && + windowsize != (1 << 12) && + windowsize != (1 << 13) && + windowsize != (1 << 14)) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mSound) + { + numchannels = mSound->mChannels; + } + else if (mDSP) + { + numchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = dsphead->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (windowsize > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = mSystem->getDSPBufferSize(&bufferlength, 0); + + position -= windowsize; + if ((int)position < 0) + { + position += length; + } + +// mUpdateTimeStamp.stampIn(); + + result = fft.getSpectrum(buffer, position, length, spectrumarray, windowsize, channeloffset, numchannels, windowtype); + +// mUpdateTimeStamp.stampOut(95); +#endif + return result; +#else + return FMOD_ERR_UNSUPPORTED; +#endif // FMOD_SUPPORT_GETSPECTRUM +} + + +/* +[ + [DESCRIPTION] + Retrieves the spectrum from the currently playing output signal. + + [PARAMETERS] + 'spectrumarray' Pointer to an array of floats to receive spectrum data. Data range is 0-1. Decibels = 10.0f * (float)log10(val) * 2.0f; + 'windowsize' Number of PCM samples to analyze. The resulting spectrum placed in the spectrumarray parameter will be HALF the size of this value (ie 1024 will place 512 floats in spectrumarray). Must be a power of 2. (ie 128/256/512 etc). Min = 128. Max = 16384. + 'channeloffset' Channel to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right. + 'windowtype' Pre-FFT window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation. + + [RETURN_VALUE] + + [REMARKS] + The larger the windowsize, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.<br> + The larger the windowsize, the more 'lag' the spectrum will seem to inherit. This is because the window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.<br> + If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).<br> + To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.<br> + + [PLATFORMS] + + [SEE_ALSO] + FMOD_DSP_FFT_WINDOW +] +*/ +FMOD_RESULT ChannelOpenAL::getWaveData(float *wavearray, int numvalues, int channeloffset) +{ + FMOD_RESULT result; + +#ifdef FMOD_STATICFORPLUGINS + + result = FMOD_ERR_UNSUPPORTED; + +#else + + DSPFilter *dsphead; + float *buffer; + unsigned int position, length; + int numchannels, count; + + dsphead = (DSPFilter *)mDSPHead; + if (!dsphead) + { + return FMOD_ERR_INITIALIZATION; + } + + if (mSound) + { + numchannels = mSound->mChannels; + } + else if (mDSP) + { + numchannels = mDSP->mDescription.channels; + } + else + { + return FMOD_ERR_INVALID_HANDLE; + } + + if (channeloffset >= numchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = dsphead->startBuffering(); + if (result != FMOD_OK) + { + return result; + } + + result = dsphead->getHistoryBuffer(&buffer, &position, &length); + if (result != FMOD_OK) + { + return result; + } + + if (numvalues > (int)length) + { + return FMOD_ERR_INVALID_PARAM; + } + + position -= numvalues; + if ((int)position < 0) + { + position += length; + } + + for (count = 0; count < numvalues; count++) + { + wavearray[count] = buffer[position*numchannels+channeloffset]; + position++; + if (position >= length) + { + position = 0; + } + } + +#endif + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::setupChannel() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenAL::updateChannel() +{ + FMOD_SPEAKERMODE channelsToSpeakermode[] = { FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_MONO, FMOD_SPEAKERMODE_STEREO, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_QUAD, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_5POINT1, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_7POINT1, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_RAW, FMOD_SPEAKERMODE_RAW }; + FMOD_RESULT result = FMOD_OK; + ALint processedBuffers = 0; + ALuint sourceIDs[16] = {0}; + + // This channel doesn't have any sources or it's paused + // NOTE: If channel started paused, this prevents incrementing the play position until playing + if (!(mNumSources > 0 && mNumSources < 16) || (mFlags & CHANNELREAL_FLAG_PAUSED)) + { + return FMOD_OK; + } + + /* + Determine how many buffers needs updating + */ + // Initial fill will update all buffers + processedBuffers = mOutputOAL->mNumBuffers; + if (!mInitialFill) + { + // Find the minimum number of buffers that all sources for this channel have processed + for (int i = 0; i < mNumSources; i++) + { + ALint processed = 0; + mOutputOAL->mOALFnTable.alGetSourcei(mSources[i]->sid, AL_BUFFERS_PROCESSED, &processed); + processedBuffers = min(processed, processedBuffers); + } + } + + /* + Update the stream buffers that have already played + */ + for (int bufferIndex = 0; bufferIndex < processedBuffers; bufferIndex++) + { + int outputChannels = mNumSources; + unsigned int outputSamples = mOutputOAL->mBufferLength; + float *interleavedData = NULL; + + /* + Fill buffer with data + */ + mSystem->mDSPActive = true; + { + result = mDSPHead->read(&interleavedData, &outputChannels, &outputSamples, channelsToSpeakermode[outputChannels], outputChannels, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + + mDSPTick++; + } + mSystem->mDSPActive = false; + + /* + Update all sources on this channel + */ + for (int sourceIndex = 0; sourceIndex < mNumSources; sourceIndex++) + { + if (interleavedData) + { + /* + Deinterleave data + */ + for (unsigned int i = 0, j = sourceIndex; i < outputSamples; i++, j += outputChannels) + { + mTempBuffer[i] = interleavedData[j]; + } + + /* + Convert data from internal FMOD float to PCM16 for the OpenAL sources + */ + result = DSPI::convert(mBuffer, mTempBuffer, FMOD_SOUND_FORMAT_PCM16, mDSPHead->mDescription.mFormat, outputSamples, 1, 1, 1.0f); + if (result != FMOD_OK) + { + return result; + } + } + else + { + /* + Fill buffer with silence + */ + FMOD_memset(mBuffer, 0, mOutputOAL->mBufferLength * sizeof(short)); + } + + /* + Update source queue + */ + ALuint buffer = 0; + if (mInitialFill) + { + // Initial fill will not have any buffers queued, so get the buffer manually + buffer = mSources[sourceIndex]->bid[bufferIndex]; + } + else + { + // Unqueue the used buffer to be refilled + mOutputOAL->mOALFnTable.alSourceUnqueueBuffers(mSources[sourceIndex]->sid, 1, &buffer); + } + mOutputOAL->mOALFnTable.alBufferData(buffer, AL_FORMAT_MONO16, mBuffer, mOutputOAL->mBufferLength * sizeof(short), 44100); + mOutputOAL->mOALFnTable.alSourceQueueBuffers(mSources[sourceIndex]->sid, 1, &buffer); + sourceIDs[sourceIndex] = mSources[sourceIndex]->sid; + } + } + + /* + Check for starvation, all buffers may have finished before getting new data (or this could be initial fill) + */ + if (processedBuffers == (ALint)mOutputOAL->mNumBuffers) + { + mOutputOAL->mOALFnTable.alSourcePlayv(mNumSources, sourceIDs); + } + + mInitialFill = false; + return result; +} + +} + +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_channel_openal.h b/win32/src/fmod_channel_openal.h new file mode 100755 index 0000000..9dc84b1 --- /dev/null +++ b/win32/src/fmod_channel_openal.h @@ -0,0 +1,76 @@ +#ifndef _FMOD_CHANNEL_OPENAL_H +#define _FMOD_CHANNEL_OPENAL_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL + +#include "fmod_channel_real.h" +#include "fmod_output_openal.h" +#include "fmod_dsp_wavetable.h" + +namespace FMOD +{ + class ChannelOpenAL : public ChannelReal + { + friend class OutputOpenAL; + + protected: + + DSPI *mDSPHead; + DSPFilter mDSPHeadMemory; + DSPWaveTable *mDSPWaveTable; + DSPWaveTable mDSPWaveTableMemory; + DSPResampler *mDSPResampler; // Connected between mDSPHead and mDSPWaveTable sometimes + DSPI *mDSPLowPass; // Optional if the user specifies FMOD_INIT_SOFTWARE_OCCLUSION + DSPCodec *mDSPCodec; + DSPConnection *mDSPConnection; + + OutputOpenAL *mOutputOAL; + int mNumSources; + SourceOpenAL *mSources[16]; + short *mBuffer; + float *mTempBuffer; + bool mInitialFill; + unsigned int mDSPTick; + + public: + + ChannelOpenAL(); + + FMOD_RESULT init (int index, SystemI *system, Output *output, DSPI *dspmixtarget); + FMOD_RESULT close (); + FMOD_RESULT alloc (); + FMOD_RESULT alloc (DSPI *dsp); + FMOD_RESULT start (); + FMOD_RESULT stop (); + FMOD_RESULT setVolume (float volume); + FMOD_RESULT setFrequency (float frequency); + FMOD_RESULT setPan (float pan, float fbpan = 1); + FMOD_RESULT setSpeakerLevels (int speaker, float *levels, int numlevels); + FMOD_RESULT setPaused (bool paused); + FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype); + FMOD_RESULT setLoopPoints (unsigned int loopstart, unsigned int looplength); + FMOD_RESULT setLoopCount (int loopcount); + FMOD_RESULT setMode (FMOD_MODE mode); + FMOD_RESULT set3DAttributes (); + FMOD_RESULT set3DMinMaxDistance (); + FMOD_RESULT set3DOcclusion (float directOcclusion, float reverbOcclusion); + FMOD_RESULT isPlaying (bool *isplaying, bool includethreadlatency = false); + FMOD_RESULT getSpectrum (float *spectrumarray, int numentries, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype); + FMOD_RESULT getWaveData (float *wavearray, int numentries, int channeloffset); + FMOD_RESULT getDSPHead (DSPI **dsp) { *dsp = mDSPHead; return FMOD_OK; } + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT setupDSPCodec (DSPI *dsp); + FMOD_RESULT getDSPCodec (DSPCodec **dsp) { *dsp = mDSPCodec; return FMOD_OK; } + + virtual FMOD_RESULT setupChannel (); + FMOD_RESULT updateChannel (); + }; +} + +#endif /* FMOD_SUPPORT_OPENAL */ + +#endif /* _FMOD_CHANNEL_OPENAL_H */ \ No newline at end of file diff --git a/win32/src/fmod_channel_openal_eax2.cpp b/win32/src/fmod_channel_openal_eax2.cpp new file mode 100755 index 0000000..44e6d12 --- /dev/null +++ b/win32/src/fmod_channel_openal_eax2.cpp @@ -0,0 +1,210 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal_eax2.h" +#include "fmod_eax2.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + Initialise this channel for EAX2 specific settings + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This will also reset the sources back to defaults when a new channel + is reusing sources + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX2::setupChannel() +{ + EAXBUFFERPROPERTIES eaxBufferProperties; + GUID eaxBufferGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxBufferGuid, &FMOD_DSPROPSETID_EAX20_BufferProperties, sizeof(GUID)); + + /* + Set the default channel reverb properties + */ + eaxBufferProperties.lDirect = EAXBUFFER_DEFAULTDIRECT; + eaxBufferProperties.lDirectHF = EAXBUFFER_DEFAULTDIRECTHF; + eaxBufferProperties.lRoom = EAXBUFFER_DEFAULTROOM; + eaxBufferProperties.lRoomHF = EAXBUFFER_DEFAULTROOMHF; + eaxBufferProperties.flRoomRolloffFactor = EAXBUFFER_DEFAULTROOMROLLOFFFACTOR; + eaxBufferProperties.lObstruction = EAXBUFFER_DEFAULTOBSTRUCTION; + eaxBufferProperties.flObstructionLFRatio = EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO; + eaxBufferProperties.lOcclusion = EAXBUFFER_DEFAULTOCCLUSION; + eaxBufferProperties.flOcclusionLFRatio = EAXBUFFER_DEFAULTOCCLUSIONLFRATIO; + eaxBufferProperties.flOcclusionRoomRatio = EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO; + eaxBufferProperties.lOutsideVolumeHF = EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF; + eaxBufferProperties.flAirAbsorptionFactor = EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR; + eaxBufferProperties.dwFlags = EAXBUFFER_DEFAULTFLAGS; + + /* + Apply the defaults to each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxBufferGuid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, mSources[i]->sid, &eaxBufferProperties, sizeof(EAXBUFFERPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Set the reverb properties on the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX2::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXBUFFERPROPERTIES eaxBufferProperties; + GUID eaxBufferGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(&eaxBufferGuid, &FMOD_DSPROPSETID_EAX20_BufferProperties, sizeof(GUID)); + + /* + Set the channel reverb properties + */ + eaxBufferProperties.lDirect = prop->Direct; + eaxBufferProperties.lDirectHF = prop->DirectHF; + eaxBufferProperties.lRoom = prop->Room; + eaxBufferProperties.lRoomHF = prop->RoomHF; + eaxBufferProperties.lObstruction = prop->Obstruction; + eaxBufferProperties.flObstructionLFRatio = prop->ObstructionLFRatio; + eaxBufferProperties.lOcclusion = prop->Occlusion; + eaxBufferProperties.flOcclusionLFRatio = prop->OcclusionLFRatio; + eaxBufferProperties.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + eaxBufferProperties.lOutsideVolumeHF = prop->OutsideVolumeHF; + eaxBufferProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxBufferProperties.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + eaxBufferProperties.dwFlags = prop->Flags; + + /* + Apply the values to each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxBufferGuid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, mSources[i]->sid, &eaxBufferProperties, sizeof(EAXBUFFERPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Get the reverb properties from the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX2::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXBUFFERPROPERTIES eaxBufferProperties; + GUID eaxBufferGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(&eaxBufferGuid, &FMOD_DSPROPSETID_EAX20_BufferProperties, sizeof(GUID)); + + /* + Get the channel reverb properties + */ + mOutputOAL->mEAXGet(&eaxBufferGuid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, mSources[0]->sid, &eaxBufferProperties, sizeof(EAXBUFFERPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + FMOD_memset(prop, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + prop->Direct = eaxBufferProperties.lDirect; + prop->DirectHF = eaxBufferProperties.lDirectHF; + prop->Room = eaxBufferProperties.lRoom; + prop->RoomHF = eaxBufferProperties.lRoomHF; + prop->Obstruction = eaxBufferProperties.lObstruction; + prop->ObstructionLFRatio = eaxBufferProperties.flObstructionLFRatio; + prop->Occlusion = eaxBufferProperties.lOcclusion; + prop->OcclusionLFRatio = eaxBufferProperties.flOcclusionLFRatio; + prop->OcclusionRoomRatio = eaxBufferProperties.flOcclusionRoomRatio; + prop->OutsideVolumeHF = eaxBufferProperties.lOutsideVolumeHF; + prop->RoomRolloffFactor = eaxBufferProperties.flRoomRolloffFactor; + prop->AirAbsorptionFactor = eaxBufferProperties.flAirAbsorptionFactor; + prop->Flags = eaxBufferProperties.dwFlags; + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_channel_openal_eax2.h b/win32/src/fmod_channel_openal_eax2.h new file mode 100755 index 0000000..c090dce --- /dev/null +++ b/win32/src/fmod_channel_openal_eax2.h @@ -0,0 +1,29 @@ +#ifndef _FMOD_CHANNEL_OPENAL_EAX2_H +#define _FMOD_CHANNEL_OPENAL_EAX2_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal.h" + +namespace FMOD +{ + class ChannelOpenALEAX2 : public ChannelOpenAL + { + public: + + ChannelOpenALEAX2() {}; + + FMOD_RESULT setupChannel (); + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ + +#endif /* _FMOD_CHANNEL_OPENAL_EAX2_H */ diff --git a/win32/src/fmod_channel_openal_eax3.cpp b/win32/src/fmod_channel_openal_eax3.cpp new file mode 100755 index 0000000..9d83064 --- /dev/null +++ b/win32/src/fmod_channel_openal_eax3.cpp @@ -0,0 +1,225 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal_eax3.h" +#include "fmod_eax3.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + Initialise this channel for EAX3 specific settings + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This will also reset the sources back to defaults when a new channel + is reusing sources + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX3::setupChannel() +{ + EAXBUFFERPROPERTIES eaxBufferProperties; + GUID eaxBufferGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxBufferGuid, &FMOD_DSPROPSETID_EAX30_BufferProperties, sizeof(GUID)); + + /* + Set the default channel reverb properties + */ + eaxBufferProperties.lDirect = EAXBUFFER_DEFAULTDIRECT; + eaxBufferProperties.lDirectHF = EAXBUFFER_DEFAULTDIRECTHF; + eaxBufferProperties.lRoom = EAXBUFFER_DEFAULTROOM; + eaxBufferProperties.lRoomHF = EAXBUFFER_DEFAULTROOMHF; + eaxBufferProperties.lObstruction = EAXBUFFER_DEFAULTOBSTRUCTION; + eaxBufferProperties.flObstructionLFRatio = EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO; + eaxBufferProperties.lOcclusion = EAXBUFFER_DEFAULTOCCLUSION; + eaxBufferProperties.flOcclusionLFRatio = EAXBUFFER_DEFAULTOCCLUSIONLFRATIO; + eaxBufferProperties.flOcclusionRoomRatio = EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO; + eaxBufferProperties.flOcclusionDirectRatio = EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO; + eaxBufferProperties.lExclusion = EAXBUFFER_DEFAULTEXCLUSION; + eaxBufferProperties.flExclusionLFRatio = EAXBUFFER_DEFAULTEXCLUSIONLFRATIO; + eaxBufferProperties.lOutsideVolumeHF = EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF; + eaxBufferProperties.flDopplerFactor = EAXBUFFER_DEFAULTDOPPLERFACTOR; + eaxBufferProperties.flRolloffFactor = EAXBUFFER_DEFAULTROLLOFFFACTOR; + eaxBufferProperties.flRoomRolloffFactor = EAXBUFFER_DEFAULTROOMROLLOFFFACTOR; + eaxBufferProperties.flAirAbsorptionFactor = EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR; + eaxBufferProperties.ulFlags = EAXBUFFER_DEFAULTFLAGS; + + /* + Apply the defaults to each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxBufferGuid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, mSources[i]->sid, &eaxBufferProperties, sizeof(EAXBUFFERPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Set the reverb properties on the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX3::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXBUFFERPROPERTIES eaxBufferProperties; + GUID eaxBufferGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(&eaxBufferGuid, &FMOD_DSPROPSETID_EAX30_BufferProperties, sizeof(GUID)); + + /* + Set the channel reverb properties + */ + eaxBufferProperties.lDirect = prop->Direct; + eaxBufferProperties.lDirectHF = prop->DirectHF; + eaxBufferProperties.lRoom = prop->Room; + eaxBufferProperties.lRoomHF = prop->RoomHF; + eaxBufferProperties.lObstruction = prop->Obstruction; + eaxBufferProperties.flObstructionLFRatio = prop->ObstructionLFRatio; + eaxBufferProperties.lOcclusion = prop->Occlusion; + eaxBufferProperties.flOcclusionLFRatio = prop->OcclusionLFRatio; + eaxBufferProperties.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + eaxBufferProperties.flOcclusionDirectRatio = prop->OcclusionDirectRatio; + eaxBufferProperties.lExclusion = prop->Exclusion; + eaxBufferProperties.flExclusionLFRatio = prop->ExclusionLFRatio; + eaxBufferProperties.lOutsideVolumeHF = prop->OutsideVolumeHF; + eaxBufferProperties.flDopplerFactor = prop->DopplerFactor; + eaxBufferProperties.flRolloffFactor = prop->RolloffFactor; + eaxBufferProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxBufferProperties.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + eaxBufferProperties.ulFlags = prop->Flags; + + /* + Apply the values to each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxBufferGuid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, mSources[i]->sid, &eaxBufferProperties, sizeof(EAXBUFFERPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Get the reverb properties from the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX3::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXBUFFERPROPERTIES eaxBufferProperties; + GUID eaxBufferGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(&eaxBufferGuid, &FMOD_DSPROPSETID_EAX30_BufferProperties, sizeof(GUID)); + + /* + Get the channel reverb properties + */ + mOutputOAL->mEAXGet(&eaxBufferGuid, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, mSources[0]->sid, &eaxBufferProperties, sizeof(EAXBUFFERPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + FMOD_memset(prop, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + prop->Direct = eaxBufferProperties.lDirect; + prop->DirectHF = eaxBufferProperties.lDirectHF; + prop->Room = eaxBufferProperties.lRoom; + prop->RoomHF = eaxBufferProperties.lRoomHF; + prop->Obstruction = eaxBufferProperties.lObstruction; + prop->ObstructionLFRatio = eaxBufferProperties.flObstructionLFRatio; + prop->Occlusion = eaxBufferProperties.lOcclusion; + prop->OcclusionLFRatio = eaxBufferProperties.flOcclusionLFRatio; + prop->OcclusionRoomRatio = eaxBufferProperties.flOcclusionRoomRatio; + prop->OcclusionDirectRatio = eaxBufferProperties.flOcclusionDirectRatio; + prop->Exclusion = eaxBufferProperties.lExclusion; + prop->ExclusionLFRatio = eaxBufferProperties.flExclusionLFRatio; + prop->OutsideVolumeHF = eaxBufferProperties.lOutsideVolumeHF; + prop->DopplerFactor = eaxBufferProperties.flDopplerFactor; + prop->RolloffFactor = eaxBufferProperties.flRolloffFactor; + prop->RoomRolloffFactor = eaxBufferProperties.flRoomRolloffFactor; + prop->AirAbsorptionFactor = eaxBufferProperties.flAirAbsorptionFactor; + prop->Flags = eaxBufferProperties.ulFlags; + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_channel_openal_eax3.h b/win32/src/fmod_channel_openal_eax3.h new file mode 100755 index 0000000..2967cd2 --- /dev/null +++ b/win32/src/fmod_channel_openal_eax3.h @@ -0,0 +1,29 @@ +#ifndef _FMOD_CHANNEL_OPENAL_EAX3_H +#define _FMOD_CHANNEL_OPENAL_EAX3_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal.h" + +namespace FMOD +{ + class ChannelOpenALEAX3 : public ChannelOpenAL + { + public: + + ChannelOpenALEAX3() {}; + + FMOD_RESULT setupChannel (); + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ + +#endif /* _FMOD_CHANNEL_OPENAL_EAX3_H */ diff --git a/win32/src/fmod_channel_openal_eax4.cpp b/win32/src/fmod_channel_openal_eax4.cpp new file mode 100755 index 0000000..9bd04e9 --- /dev/null +++ b/win32/src/fmod_channel_openal_eax4.cpp @@ -0,0 +1,392 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal_eax4.h" +#include "fmod_eax4.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + Initialise this channel for EAX4 specific settings + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This will also reset the sources back to defaults when a new channel + is reusing sources + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX4::setupChannel() +{ + EAXACTIVEFXSLOTS eaxSlots; + EAXSOURCEPROPERTIES eaxSourceProperties; + GUID eaxSourceGuid; + GUID eaxFXSlot0, eaxFXSlotNull; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxFXSlot0, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlotNull, &FMOD_EAX_NULL_GUID, sizeof(GUID)); + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + /* + Set the default slot 0 to on + */ + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlotNull, sizeof(GUID)); + + /* + Set the default channel reverb properties + */ + eaxSourceProperties.lDirect = EAXSOURCE_DEFAULTDIRECT; + eaxSourceProperties.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF; + eaxSourceProperties.lRoom = EAXSOURCE_DEFAULTROOM; + eaxSourceProperties.lRoomHF = EAXSOURCE_DEFAULTROOMHF; + eaxSourceProperties.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION; + eaxSourceProperties.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO; + eaxSourceProperties.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + eaxSourceProperties.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + eaxSourceProperties.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + eaxSourceProperties.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + eaxSourceProperties.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + eaxSourceProperties.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; + eaxSourceProperties.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF; + eaxSourceProperties.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR; + eaxSourceProperties.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR; + eaxSourceProperties.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR; + eaxSourceProperties.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR; + eaxSourceProperties.ulFlags = EAXSOURCE_DEFAULTFLAGS; + + /* + Apply the defaults to each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ACTIVEFXSLOTID, mSources[i]->sid, &eaxSlots, sizeof(EAXACTIVEFXSLOTS)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ALLPARAMETERS, mSources[i]->sid, &eaxSourceProperties, sizeof(EAXSOURCEPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Set the reverb properties on the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + EAX4 has 4 effects slots, but can only have 2 active on any one source. + The first slot is dedicated to reverb, the second slot is dedicated chorus, + any of the remaining two slots can be tasked for reverb + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX4::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXSOURCEPROPERTIES eaxSourceProperties; + EAXACTIVEFXSLOTS eaxSlots; + unsigned long eaxSourceFlags; + GUID eaxSourceGuid; + GUID eaxFXSlot0, eaxFXSlot2, eaxFXSlot3, eaxFXSlotNull; + int numActiveSlots; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(&eaxFXSlot0, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot2, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot3, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlotNull, &FMOD_EAX_NULL_GUID, sizeof(GUID)); + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + /* + Only support a maximum 2 active slots per channel + */ + numActiveSlots = 0; + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) + { + numActiveSlots++; + } + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + numActiveSlots++; + } + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + numActiveSlots++; + } + if (numActiveSlots > 2) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + Store the slot IDs for the chosen environment + */ + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlotNull, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlotNull, sizeof(GUID)); + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID)); + + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot2, sizeof(GUID)); + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID)); + } + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot2, sizeof(GUID)); + + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID)); + } + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot2, sizeof(GUID)); + } + else // Set to Slot 0 by default + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID)); + } + + /* + Set the channel reverb properties + */ + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_FLAGS, mSources[0]->sid, &eaxSourceFlags, sizeof(unsigned long)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + eaxSourceProperties.lDirect = prop->Direct; + eaxSourceProperties.lDirectHF = prop->DirectHF; + eaxSourceProperties.lRoom = prop->Room; + eaxSourceProperties.lRoomHF = prop->RoomHF; + eaxSourceProperties.lObstruction = prop->Obstruction; + eaxSourceProperties.flObstructionLFRatio = prop->ObstructionLFRatio; + eaxSourceProperties.lOcclusion = prop->Occlusion; + eaxSourceProperties.flOcclusionLFRatio = prop->OcclusionLFRatio; + eaxSourceProperties.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + eaxSourceProperties.flOcclusionDirectRatio = prop->OcclusionDirectRatio; + eaxSourceProperties.lExclusion = prop->Exclusion; + eaxSourceProperties.flExclusionLFRatio = prop->ExclusionLFRatio; + eaxSourceProperties.lOutsideVolumeHF = prop->OutsideVolumeHF; + eaxSourceProperties.flDopplerFactor = prop->DopplerFactor; + eaxSourceProperties.flRolloffFactor = prop->RolloffFactor; + eaxSourceProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxSourceProperties.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + + eaxSourceFlags = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_DIRECTHFAUTO) ? (eaxSourceFlags | EAXSOURCEFLAGS_DIRECTHFAUTO) : (eaxSourceFlags & ~EAXSOURCEFLAGS_DIRECTHFAUTO); + eaxSourceFlags = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_ROOMAUTO) ? (eaxSourceFlags | EAXSOURCEFLAGS_ROOMAUTO) : (eaxSourceFlags & ~EAXSOURCEFLAGS_ROOMAUTO); + eaxSourceFlags = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_ROOMHFAUTO) ? (eaxSourceFlags | EAXSOURCEFLAGS_ROOMHFAUTO) : (eaxSourceFlags & ~EAXSOURCEFLAGS_ROOMHFAUTO); + eaxSourceProperties.ulFlags = eaxSourceFlags; + + /* + Apply the active slots and properties to the sources + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ACTIVEFXSLOTID, mSources[i]->sid, &eaxSlots, sizeof(EAXACTIVEFXSLOTS)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ALLPARAMETERS, mSources[i]->sid, &eaxSourceProperties, sizeof(EAXSOURCEPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Get the reverb properties from the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + EAX4 has 4 effects slots, but can only have 2 active on any one source. + The first slot is dedicated to reverb, the second slot is dedicated chorus, + any of the remaining two slots can be tasked for reverb + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX4::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXSOURCEPROPERTIES eaxSourceProperties; + EAXACTIVEFXSLOTS eaxSlots; + GUID eaxSourceGuid; + GUID eaxFXSlot0, eaxFXSlot2, eaxFXSlot3; + int numInactiveSlots; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxFXSlot0, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot2, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot3, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + /* + Get the channel reverb properties + */ + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_ALLPARAMETERS, mSources[0]->sid, &eaxSourceProperties, sizeof(EAXSOURCEPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + FMOD_memset(prop, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + prop->Direct = eaxSourceProperties.lDirect; + prop->DirectHF = eaxSourceProperties.lDirectHF; + prop->Room = eaxSourceProperties.lRoom; + prop->RoomHF = eaxSourceProperties.lRoomHF; + prop->Obstruction = eaxSourceProperties.lObstruction; + prop->ObstructionLFRatio = eaxSourceProperties.flObstructionLFRatio; + prop->Occlusion = eaxSourceProperties.lOcclusion; + prop->OcclusionLFRatio = eaxSourceProperties.flOcclusionLFRatio; + prop->OcclusionRoomRatio = eaxSourceProperties.flOcclusionRoomRatio; + prop->OcclusionDirectRatio = eaxSourceProperties.flOcclusionDirectRatio; + prop->Exclusion = eaxSourceProperties.lExclusion; + prop->ExclusionLFRatio = eaxSourceProperties.flExclusionLFRatio; + prop->OutsideVolumeHF = eaxSourceProperties.lOutsideVolumeHF; + prop->DopplerFactor = eaxSourceProperties.flDopplerFactor; + prop->RolloffFactor = eaxSourceProperties.flRolloffFactor; + prop->RoomRolloffFactor = eaxSourceProperties.flRoomRolloffFactor; + prop->AirAbsorptionFactor = eaxSourceProperties.flAirAbsorptionFactor; + + prop->Flags = 0; + prop->Flags |= (eaxSourceProperties.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) ? FMOD_REVERB_CHANNELFLAGS_DIRECTHFAUTO : 0; + prop->Flags |= (eaxSourceProperties.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) ? FMOD_REVERB_CHANNELFLAGS_ROOMAUTO : 0; + prop->Flags |= (eaxSourceProperties.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) ? FMOD_REVERB_CHANNELFLAGS_ROOMHFAUTO : 0; + + // Get all the effect slots associated with this source + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_ACTIVEFXSLOTID, mSources[0]->sid, &eaxSlots, sizeof(EAXACTIVEFXSLOTS)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + /* + See if there are any active effect slots + */ + numInactiveSlots = 0; + + // Check active slot 0 for any of the 3 possible effect IDs + if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot2, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE2; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot3, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE3; + } + else + { + numInactiveSlots++; + } + + // Check active slot 1 for any of the 3 possible effect IDs + if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot0, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot2, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE2; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE3; + } + else + { + numInactiveSlots++; + } + + // If no slots have been assigned (max slots = 2), assign to slot 0 + if (numInactiveSlots == 2) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_channel_openal_eax4.h b/win32/src/fmod_channel_openal_eax4.h new file mode 100755 index 0000000..8f0d3bf --- /dev/null +++ b/win32/src/fmod_channel_openal_eax4.h @@ -0,0 +1,29 @@ +#ifndef _FMOD_CHANNEL_OPENAL_EAX4_H +#define _FMOD_CHANNEL_OPENAL_EAX4_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal.h" + +namespace FMOD +{ + class ChannelOpenALEAX4 : public ChannelOpenAL + { + public: + + ChannelOpenALEAX4() {}; + + FMOD_RESULT setupChannel (); + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ + +#endif /* _FMOD_CHANNEL_OPENAL_EAX4_H */ diff --git a/win32/src/fmod_channel_openal_eax5.cpp b/win32/src/fmod_channel_openal_eax5.cpp new file mode 100755 index 0000000..79f8620 --- /dev/null +++ b/win32/src/fmod_channel_openal_eax5.cpp @@ -0,0 +1,633 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal_eax5.h" +#include "fmod_eax5.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + Initialise this channel for EAX5 specific settings + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This will also reset the sources back to defaults when a new channel + is reusing sources + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX5::setupChannel() +{ + EAXSPEAKERLEVELPROPERTIES eaxSourceSpeakerLevels[8]; + EAXACTIVEFXSLOTS eaxSlots; + EAXSOURCEPROPERTIES eaxSourceProperties; + GUID eaxSourceGuid; + GUID eaxFXSlot0, eaxFXSlotNull; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxFXSlot0, &FMOD_EAXPROPERTYID_EAX50_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlotNull, &FMOD_EAX_NULL_GUID, sizeof(GUID)); + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX50_Source, sizeof(GUID)); + + /* + Set the default speaker levels + */ + eaxSourceSpeakerLevels[0].lSpeakerID = EAXSPEAKER_FRONT_LEFT; + eaxSourceSpeakerLevels[0].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[1].lSpeakerID = EAXSPEAKER_FRONT_CENTER; + eaxSourceSpeakerLevels[1].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[2].lSpeakerID = EAXSPEAKER_FRONT_RIGHT; + eaxSourceSpeakerLevels[2].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[3].lSpeakerID = EAXSPEAKER_SIDE_RIGHT; + eaxSourceSpeakerLevels[3].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[4].lSpeakerID = EAXSPEAKER_REAR_RIGHT; + eaxSourceSpeakerLevels[4].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[5].lSpeakerID = EAXSPEAKER_REAR_LEFT; + eaxSourceSpeakerLevels[5].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[6].lSpeakerID = EAXSPEAKER_SIDE_LEFT; + eaxSourceSpeakerLevels[6].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + eaxSourceSpeakerLevels[7].lSpeakerID = EAXSPEAKER_LOW_FREQUENCY; + eaxSourceSpeakerLevels[7].lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; + + /* + Set the default slot 0 to on + */ + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlotNull, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[2], &eaxFXSlotNull, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[3], &eaxFXSlotNull, sizeof(GUID)); + + /* + Set the default channel reverb properties + */ + eaxSourceProperties.lDirect = EAXSOURCE_DEFAULTDIRECT; + eaxSourceProperties.lDirectHF = EAXSOURCE_DEFAULTDIRECTHF; + eaxSourceProperties.lRoom = EAXSOURCE_DEFAULTROOM; + eaxSourceProperties.lRoomHF = EAXSOURCE_DEFAULTROOMHF; + eaxSourceProperties.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION; + eaxSourceProperties.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO; + eaxSourceProperties.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION; + eaxSourceProperties.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO; + eaxSourceProperties.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO; + eaxSourceProperties.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO; + eaxSourceProperties.lExclusion = EAXSOURCE_DEFAULTEXCLUSION; + eaxSourceProperties.flExclusionLFRatio = EAXSOURCE_DEFAULTEXCLUSIONLFRATIO; + eaxSourceProperties.lOutsideVolumeHF = EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF; + eaxSourceProperties.flDopplerFactor = EAXSOURCE_DEFAULTDOPPLERFACTOR; + eaxSourceProperties.flRolloffFactor = EAXSOURCE_DEFAULTROLLOFFFACTOR; + eaxSourceProperties.flRoomRolloffFactor = EAXSOURCE_DEFAULTROOMROLLOFFFACTOR; + eaxSourceProperties.flAirAbsorptionFactor = EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR; + eaxSourceProperties.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; + eaxSourceProperties.ulFlags = EAXSOURCE_DEFAULTFLAGS; + + /* + Apply the defaults to each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_SPEAKERLEVELS, mSources[i]->sid, &eaxSourceSpeakerLevels, 8 * sizeof(EAXSPEAKERLEVELPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ACTIVEFXSLOTID, mSources[i]->sid, &eaxSlots, sizeof(EAXACTIVEFXSLOTS)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ALLPARAMETERS, mSources[i]->sid, &eaxSourceProperties, sizeof(EAXSOURCEPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Sets the channel's speaker volume levels for each speaker individually + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX5::setSpeakerMix(float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) +{ + EAXSPEAKERLEVELPROPERTIES eaxSourceSpeakerLevels[8]; + unsigned long eaxSourceFlags; + GUID eaxSourceGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX50_Source, sizeof(GUID)); + + eaxSourceSpeakerLevels[0].lSpeakerID = EAXSPEAKER_FRONT_LEFT; + eaxSourceSpeakerLevels[0].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, frontleft))); + + eaxSourceSpeakerLevels[1].lSpeakerID = EAXSPEAKER_FRONT_CENTER; + eaxSourceSpeakerLevels[1].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, center))); + + eaxSourceSpeakerLevels[2].lSpeakerID = EAXSPEAKER_FRONT_RIGHT; + eaxSourceSpeakerLevels[2].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, frontright))); + + eaxSourceSpeakerLevels[3].lSpeakerID = EAXSPEAKER_SIDE_RIGHT; + eaxSourceSpeakerLevels[3].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, sideright))); + + eaxSourceSpeakerLevels[4].lSpeakerID = EAXSPEAKER_REAR_RIGHT; + eaxSourceSpeakerLevels[4].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, backright))); + + eaxSourceSpeakerLevels[5].lSpeakerID = EAXSPEAKER_REAR_LEFT; + eaxSourceSpeakerLevels[5].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, backleft))); + + eaxSourceSpeakerLevels[6].lSpeakerID = EAXSPEAKER_SIDE_LEFT; + eaxSourceSpeakerLevels[6].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, sideleft))); + + eaxSourceSpeakerLevels[7].lSpeakerID = EAXSPEAKER_LOW_FREQUENCY; + eaxSourceSpeakerLevels[7].lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, lfe))); + + /* + Enable or disable speaker mode usage for OpenAL + */ + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_FLAGS, mSources[0]->sid, &eaxSourceFlags, sizeof(unsigned long)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (mParent->m3DPanLevel >= 0.5f) + { + eaxSourceFlags |= EAXSOURCEFLAGS_APPLYSPEAKERLEVELS; + } + else + { + eaxSourceFlags &= ~EAXSOURCEFLAGS_APPLYSPEAKERLEVELS; + } + + /* + Set the speaker levels for each source + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_FLAGS, mSources[i]->sid, &eaxSourceFlags, sizeof(unsigned long)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_SPEAKERLEVELS, mSources[i]->sid, &eaxSourceSpeakerLevels, 8 * sizeof(EAXSPEAKERLEVELPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Sets the incoming sound levels for a particular speaker + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX5::setSpeakerLevels(int speaker, float *levels, int numlevels) +{ + long FMOD_SPEAKER_TO_EAXSPEAKER[11] = { EAXSPEAKER_FRONT_LEFT, EAXSPEAKER_FRONT_RIGHT, EAXSPEAKER_FRONT_CENTER, EAXSPEAKER_LOW_FREQUENCY, EAXSPEAKER_REAR_LEFT, EAXSPEAKER_REAR_RIGHT, EAXSPEAKER_SIDE_LEFT, EAXSPEAKER_SIDE_RIGHT, EAXSPEAKER_FRONT_LEFT, EAXSPEAKER_FRONT_LEFT, EAXSPEAKER_REAR_CENTER }; + EAXSPEAKERLEVELPROPERTIES eaxSourceSpeakerLevels; + unsigned long eaxSourceFlags; + GUID eaxSourceGuid; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX50_Source, sizeof(GUID)); + + /* + Enable or disable speaker mode usage for OpenAL + */ + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_FLAGS, mSources[0]->sid, &eaxSourceFlags, sizeof(unsigned long)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (mParent->m3DPanLevel >= 0.5f) + { + eaxSourceFlags |= EAXSOURCEFLAGS_APPLYSPEAKERLEVELS; + } + else + { + eaxSourceFlags &= ~EAXSOURCEFLAGS_APPLYSPEAKERLEVELS; + } + + /* + Set the speaker levels for each source, there may be less sources than levels + */ + numlevels = (mNumSources < numlevels) ? mNumSources : numlevels; + for (int i = 0; i < numlevels; i++) + { + eaxSourceSpeakerLevels.lSpeakerID = FMOD_SPEAKER_TO_EAXSPEAKER[speaker]; + eaxSourceSpeakerLevels.lLevel = (long)(100.0f * 20.0f * FMOD_LOG10(max(0.00001f, levels[i]))); + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_FLAGS, mSources[i]->sid, &eaxSourceFlags, sizeof(unsigned long)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_SPEAKERLEVELS, mSources[i]->sid, &eaxSourceSpeakerLevels, sizeof(EAXSPEAKERLEVELPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Set the reverb properties on the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + EAX5 has 4 effects slots, and is currently set up to allow only 2 to be active on + any one source, any of the four slots can be tasked for reverb + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX5::setReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXSOURCEPROPERTIES eaxSourceProperties; + EAXACTIVEFXSLOTS eaxSlots; + unsigned long eaxSourceFlags; + GUID eaxSourceGuid; + GUID eaxFXSlot0, eaxFXSlot1, eaxFXSlot2, eaxFXSlot3, eaxFXSlotNull; + int numActiveSlots; + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memcpy(&eaxFXSlot0, &FMOD_EAXPROPERTYID_EAX50_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot1, &FMOD_EAXPROPERTYID_EAX50_FXSlot1, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot2, &FMOD_EAXPROPERTYID_EAX50_FXSlot2, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot3, &FMOD_EAXPROPERTYID_EAX50_FXSlot3, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlotNull, &FMOD_EAX_NULL_GUID, sizeof(GUID)); + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX50_Source, sizeof(GUID)); + + /* + Only support a maximum 2 active slots per channel + */ + numActiveSlots = 0; + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) + { + numActiveSlots++; + } + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + numActiveSlots++; + } + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + numActiveSlots++; + } + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) + { + numActiveSlots++; + } + if (numActiveSlots > 2) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + Store the slot IDs for the chosen environment + */ + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlotNull, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlotNull, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[2], &eaxFXSlotNull, sizeof(GUID)); + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[3], &eaxFXSlotNull, sizeof(GUID)); + + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE0) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID)); + + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot1, sizeof(GUID)); + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot2, sizeof(GUID)); + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID)); + } + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE1) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot1, sizeof(GUID)); + + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot2, sizeof(GUID)); + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID)); + } + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE2) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot2, sizeof(GUID)); + + if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID)); + } + } + else if (prop->Flags & FMOD_REVERB_CHANNELFLAGS_INSTANCE3) + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot3, sizeof(GUID)); + } + else // Set to Slot 0 by default + { + FMOD_memcpy(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID)); + } + + /* + Set the channel reverb properties + */ + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_FLAGS, mSources[0]->sid, &eaxSourceFlags, sizeof(unsigned long)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + eaxSourceProperties.lDirect = prop->Direct; + eaxSourceProperties.lDirectHF = prop->DirectHF; + eaxSourceProperties.lRoom = prop->Room; + eaxSourceProperties.lRoomHF = prop->RoomHF; + eaxSourceProperties.lObstruction = prop->Obstruction; + eaxSourceProperties.flObstructionLFRatio = prop->ObstructionLFRatio; + eaxSourceProperties.lOcclusion = prop->Occlusion; + eaxSourceProperties.flOcclusionLFRatio = prop->OcclusionLFRatio; + eaxSourceProperties.flOcclusionRoomRatio = prop->OcclusionRoomRatio; + eaxSourceProperties.flOcclusionDirectRatio = prop->OcclusionDirectRatio; + eaxSourceProperties.lExclusion = prop->Exclusion; + eaxSourceProperties.flExclusionLFRatio = prop->ExclusionLFRatio; + eaxSourceProperties.lOutsideVolumeHF = prop->OutsideVolumeHF; + eaxSourceProperties.flDopplerFactor = prop->DopplerFactor; + eaxSourceProperties.flRolloffFactor = prop->RolloffFactor; + eaxSourceProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxSourceProperties.flAirAbsorptionFactor = prop->AirAbsorptionFactor; + eaxSourceProperties.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; + + eaxSourceFlags = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_DIRECTHFAUTO) ? (eaxSourceFlags | EAXSOURCEFLAGS_DIRECTHFAUTO) : (eaxSourceFlags & ~EAXSOURCEFLAGS_DIRECTHFAUTO); + eaxSourceFlags = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_ROOMAUTO) ? (eaxSourceFlags | EAXSOURCEFLAGS_ROOMAUTO) : (eaxSourceFlags & ~EAXSOURCEFLAGS_ROOMAUTO); + eaxSourceFlags = (prop->Flags & FMOD_REVERB_CHANNELFLAGS_ROOMHFAUTO) ? (eaxSourceFlags | EAXSOURCEFLAGS_ROOMHFAUTO) : (eaxSourceFlags & ~EAXSOURCEFLAGS_ROOMHFAUTO); + eaxSourceProperties.ulFlags = eaxSourceFlags; + + /* + Apply the active slots and properties to the sources + */ + for (int i = 0; i < mNumSources; i++) + { + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ACTIVEFXSLOTID, mSources[i]->sid, &eaxSlots, sizeof(EAXACTIVEFXSLOTS)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mOutputOAL->mEAXSet(&eaxSourceGuid, EAXSOURCE_ALLPARAMETERS, mSources[i]->sid, &eaxSourceProperties, sizeof(EAXSOURCEPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Get the reverb properties from the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + EAX5 has 4 effects slots, and is currently set up to allow only 2 to be active on + any one source, any of the four slots can be tasked for reverb + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT ChannelOpenALEAX5::getReverbProperties(FMOD_REVERB_CHANNELPROPERTIES *prop) +{ + EAXSOURCEPROPERTIES eaxSourceProperties; + EAXACTIVEFXSLOTS eaxSlots; + GUID eaxSourceGuid; + GUID eaxFXSlot0, eaxFXSlot1, eaxFXSlot2, eaxFXSlot3; + int numInactiveSlots; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mOutputOAL) + { + return FMOD_ERR_UNINITIALIZED; + } + + FMOD_memcpy(&eaxFXSlot0, &FMOD_EAXPROPERTYID_EAX50_FXSlot0, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot1, &FMOD_EAXPROPERTYID_EAX50_FXSlot1, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot2, &FMOD_EAXPROPERTYID_EAX50_FXSlot2, sizeof(GUID)); + FMOD_memcpy(&eaxFXSlot3, &FMOD_EAXPROPERTYID_EAX50_FXSlot3, sizeof(GUID)); + FMOD_memcpy(&eaxSourceGuid, &FMOD_EAXPROPERTYID_EAX50_Source, sizeof(GUID)); + + /* + Get the channel reverb properties + */ + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_ALLPARAMETERS, mSources[0]->sid, &eaxSourceProperties, sizeof(EAXSOURCEPROPERTIES)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + FMOD_memset(prop, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES)); + prop->Direct = eaxSourceProperties.lDirect; + prop->DirectHF = eaxSourceProperties.lDirectHF; + prop->Room = eaxSourceProperties.lRoom; + prop->RoomHF = eaxSourceProperties.lRoomHF; + prop->Obstruction = eaxSourceProperties.lObstruction; + prop->ObstructionLFRatio = eaxSourceProperties.flObstructionLFRatio; + prop->Occlusion = eaxSourceProperties.lOcclusion; + prop->OcclusionLFRatio = eaxSourceProperties.flOcclusionLFRatio; + prop->OcclusionRoomRatio = eaxSourceProperties.flOcclusionRoomRatio; + prop->OcclusionDirectRatio = eaxSourceProperties.flOcclusionDirectRatio; + prop->Exclusion = eaxSourceProperties.lExclusion; + prop->ExclusionLFRatio = eaxSourceProperties.flExclusionLFRatio; + prop->OutsideVolumeHF = eaxSourceProperties.lOutsideVolumeHF; + prop->DopplerFactor = eaxSourceProperties.flDopplerFactor; + prop->RolloffFactor = eaxSourceProperties.flRolloffFactor; + prop->RoomRolloffFactor = eaxSourceProperties.flRoomRolloffFactor; + prop->AirAbsorptionFactor = eaxSourceProperties.flAirAbsorptionFactor; + + prop->Flags = 0; + prop->Flags |= (eaxSourceProperties.ulFlags & EAXSOURCEFLAGS_DIRECTHFAUTO) ? FMOD_REVERB_CHANNELFLAGS_DIRECTHFAUTO : 0; + prop->Flags |= (eaxSourceProperties.ulFlags & EAXSOURCEFLAGS_ROOMAUTO) ? FMOD_REVERB_CHANNELFLAGS_ROOMAUTO : 0; + prop->Flags |= (eaxSourceProperties.ulFlags & EAXSOURCEFLAGS_ROOMHFAUTO) ? FMOD_REVERB_CHANNELFLAGS_ROOMHFAUTO : 0; + + // Get all the effect slots associated with this source + mOutputOAL->mEAXGet(&eaxSourceGuid, EAXSOURCE_ACTIVEFXSLOTID, mSources[0]->sid, &eaxSlots, sizeof(EAXACTIVEFXSLOTS)); + if (mOutputOAL->mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + /* + See if there are any active effect slots + */ + numInactiveSlots = 0; + + // Check active slot 0 for any of the 4 possible effect IDs + if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot0, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot1, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE1; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot2, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE2; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[0], &eaxFXSlot3, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE3; + } + else + { + numInactiveSlots++; + } + + // Check active slot 1 for any of the 4 possible effect IDs + if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot0, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot1, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE1; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot2, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE2; + } + else if (!memcmp(&eaxSlots.guidActiveFXSlots[1], &eaxFXSlot3, sizeof(GUID))) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE3; + } + else + { + numInactiveSlots++; + } + + // If no slots have been assigned (max slots = 2), assign to slot 0 + if (numInactiveSlots == 2) + { + prop->Flags |= FMOD_REVERB_CHANNELFLAGS_INSTANCE0; + } + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ \ No newline at end of file diff --git a/win32/src/fmod_channel_openal_eax5.h b/win32/src/fmod_channel_openal_eax5.h new file mode 100755 index 0000000..13eeccf --- /dev/null +++ b/win32/src/fmod_channel_openal_eax5.h @@ -0,0 +1,32 @@ +#ifndef _FMOD_CHANNEL_OPENAL_EAX5_H +#define _FMOD_CHANNEL_OPENAL_EAX5_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_channel_openal.h" + +namespace FMOD +{ + class ChannelOpenALEAX5 : public ChannelOpenAL + { + public: + + ChannelOpenALEAX5() {}; + + FMOD_RESULT setupChannel (); + + FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright); + FMOD_RESULT setSpeakerLevels (int speaker, float *levels, int numlevels); + + FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop); + FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop); + }; +} + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ + +#endif /* _FMOD_CHANNEL_OPENAL_EAX5_H */ diff --git a/win32/src/fmod_codec_asf.cpp b/win32/src/fmod_codec_asf.cpp new file mode 100755 index 0000000..3302241 --- /dev/null +++ b/win32/src/fmod_codec_asf.cpp @@ -0,0 +1,1180 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ASF + +#include "fmod.h" +#include "fmod_codec_asf.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_soundi.h" +#include "fmod_string.h" +#include "fmod_syncpoint.h" + +#include <windows.h> +#include <memory.h> +#include <stdio.h> + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX asfcodec; + +static const FMOD_GUID ASF_Header_Object = { 0x75B22630,0x668E,0x11CF,{0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C}}; + +PFN_WMCREATESYNCREADER CodecASF::gWMCreateSyncReader = 0; +HMODULE CodecASF::gDLLHandle = 0; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescriptionEx is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecASF::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecASF::getDescriptionEx() +{ + FMOD_memset(&asfcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + asfcodec.name = "FMOD ASF Codec"; + asfcodec.version = 0x00010100; + asfcodec.timeunits = FMOD_TIMEUNIT_MS; + asfcodec.open = &CodecASF::openCallback; + asfcodec.close = &CodecASF::closeCallback; + asfcodec.read = &CodecASF::readCallback; + asfcodec.setposition = &CodecASF::setPositionCallback; + asfcodec.soundcreate = &CodecASF::soundCreateCallback; + + asfcodec.mType = FMOD_SOUND_TYPE_ASF; + asfcodec.mSize = sizeof(CodecASF); + + return &asfcodec; +} + + + +/* +[ + [DESCRIPTION] + Retrieve video and audio stream numbers from profile + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecASF::getStreamNumbers(IWMProfile* pProfile) +{ + HRESULT hr = S_OK; + IWMStreamConfig *pStream = NULL; + DWORD dwStreams = 0; + GUID pguidStreamType; + + if ( NULL == pProfile ) + { + return FMOD_ERR_INVALID_PARAM; + } + + hr = pProfile->GetStreamCount( &dwStreams ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FILE_BAD; + } + + mAudioStreamNum = 0; + mVideoStreamNum = 0; + + for ( DWORD i = 0; i < dwStreams; i++ ) + { + hr = pProfile->GetStream( i, &pStream ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FILE_BAD; + } + + WORD wStreamNumber = 0 ; + + // + // Get the stream number of the current stream + // + + hr = pStream->GetStreamNumber( &wStreamNumber ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FILE_BAD; + } + + hr = pStream->GetStreamType( &pguidStreamType ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FILE_BAD; + } + + if( WMMEDIATYPE_Audio == pguidStreamType ) + { + mAudioStreamNum = wStreamNumber; + } + else if( WMMEDIATYPE_Video == pguidStreamType ) + { + mVideoStreamNum = wStreamNumber; + } + + if (pStream) + { + pStream->Release(); + } + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecASF::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + FMOD_RESULT result = FMOD_OK; + HRESULT hr; + WMT_STREAM_SELECTION wmtSS = WMT_ON; + IWMProfile *pProfile = NULL; + int count, numoutputs; + char header[16]; + + init(FMOD_SOUND_TYPE_ASF); + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "CodecASF::openInternal", "attempting to open as ASF..\n")); + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->read(&header, 1, 16, 0); + if (result != FMOD_OK) + { + return result; + } + + if (memcmp(header, &ASF_Header_Object, 16)) /* ASF */ + { + return FMOD_ERR_FORMAT; + } + + mSeekable = true; + mSyncPoint = 0; + mNumSyncPoints = 0; + + if (!gWMCreateSyncReader) + { + HMODULE asfmodule = NULL; + + if (!gDLLHandle) + { + gDLLHandle = LoadLibrary("wmvcore.dll"); + if (!gDLLHandle) + { + return FMOD_ERR_PLUGIN_RESOURCE; + } + } + + gWMCreateSyncReader = (PFN_WMCREATESYNCREADER)GetProcAddress(gDLLHandle,"WMCreateSyncReader"); + if (!gWMCreateSyncReader) + { + return FMOD_ERR_PLUGIN_RESOURCE; + } + + } + + mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT)); + if (!mWaveFormatMemory) + { + return FMOD_ERR_MEMORY; + } + + waveformat = mWaveFormatMemory; + + /* + Get size of file in bytes + */ + result = mFile->getSize(&mWaveFormatMemory->lengthbytes); + if (result != FMOD_OK) + { + return result; + } + + result = mFile->seek(0, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + hr = CoInitialize( NULL ); + if (hr == S_OK || hr == S_FALSE) + { + mCoInitialized = true; + } + + + hr = gWMCreateSyncReader( NULL, 0, &mReader ); + if( FAILED( hr ) ) + { + if (hr == E_OUTOFMEMORY) + { + return FMOD_ERR_MEMORY; + } + + return FMOD_ERR_FORMAT; + } + + mStream = FMOD_Object_Calloc(CROStream); + if( NULL == mStream ) + { + return FMOD_ERR_MEMORY; + } + + hr = mStream->Open( (const char *)mFile ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + hr = mReader->OpenStream( mStream ); + if (FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + /* + Get the profile interface + */ + hr = mReader->QueryInterface( IID_IWMProfile, ( VOID ** )&pProfile ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_COM; + } + + /* + Find out stream numbers for video and audio using the profile + */ + result = getStreamNumbers( pProfile ); + if (result != FMOD_OK) + { + return result; + } + + pProfile->Release(); + + hr = mReader->SetRange( 0, 0 ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + hr = mReader->SetStreamsSelected( 1, &mAudioStreamNum, &wmtSS ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + hr = mReader->SetReadStreamSamples( mAudioStreamNum, 0 ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + /* + Get the format information + */ + hr = mReader->GetOutputCount( (DWORD *)&numoutputs ); + + mPCMBufferLengthBytes = 0; + for (count=0; count < numoutputs; count++) + { + IWMOutputMediaProps *props; + WM_MEDIA_TYPE *mediatype; + DWORD mediatypesize; + WAVEFORMATEX *waveformat; + int bits; + + hr = mReader->GetOutputFormat(count, 0, &props); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + hr = props->GetMediaType(NULL, &mediatypesize); + if ( FAILED( hr ) ) + { + props->Release(); + return FMOD_ERR_FORMAT; + } + + mediatype = (WM_MEDIA_TYPE *)FMOD_Memory_Calloc(mediatypesize); + if (!mediatype) + { + props->Release(); + return FMOD_ERR_MEMORY; + } + + hr = props->GetMediaType(mediatype, &mediatypesize); + if ( FAILED( hr ) ) + { + props->Release(); + return FMOD_ERR_FORMAT; + } + + if (mediatype->lSampleSize > mPCMBufferLengthBytes) + { + mPCMBufferLengthBytes = mediatype->lSampleSize; + } + if (!memcmp(&mediatype->formattype, &WMFORMAT_WaveFormatEx, sizeof(GUID))) + { + waveformat = (WAVEFORMATEX *)mediatype->pbFormat; + + bits = waveformat->wBitsPerSample; + result = SoundI::getFormatFromBits(bits, &mWaveFormatMemory->format); + if (result != FMOD_OK) + { + props->Release(); + FMOD_Memory_Free(mediatype); + return FMOD_ERR_FORMAT; + } + mWaveFormatMemory->channels = waveformat->nChannels; + mWaveFormatMemory->blockalign = waveformat->nBlockAlign; + mWaveFormatMemory->frequency = waveformat->nSamplesPerSec; + } + + props->Release(); + + FMOD_Memory_Free(mediatype); + } + + /* + Get the ASF header information + */ + { + IWMHeaderInfo *headerinfo; + WMT_ATTR_DATATYPE wmtType; + BYTE *pbValue; + WORD cbLength; + QWORD duration; + WORD streamnum; + WORD markercount; + + hr = mReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&headerinfo ); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + streamnum = 0; + + hr = headerinfo->GetAttributeByName( &streamnum, L"Duration", &wmtType, NULL, &cbLength ); + if( FAILED( hr ) && ( ASF_E_NOTFOUND != hr ) ) + { + return FMOD_ERR_FORMAT; + } + if( ASF_E_NOTFOUND == hr ) + { + return FMOD_ERR_FORMAT; + } + + pbValue = (BYTE *)FMOD_Memory_Calloc(cbLength); + if (!pbValue) + { + return FMOD_ERR_MEMORY; + } + + hr = headerinfo->GetAttributeByName( &streamnum, L"Duration", &wmtType, pbValue, &cbLength ); + if ( FAILED( hr ) ) + { + FMOD_Memory_Free(pbValue); + return FMOD_ERR_FORMAT; + } + + duration = *((QWORD *)pbValue); + mWaveFormatMemory->lengthpcm = (unsigned int)(duration * mWaveFormatMemory->frequency / 10000 / 1000); + + FMOD_Memory_Free(pbValue); + + /* + Get seekable flag + */ + hr = headerinfo->GetAttributeByName( &streamnum, L"Seekable", &wmtType, NULL, &cbLength ); + if( FAILED( hr ) && ( ASF_E_NOTFOUND != hr ) ) + { + return FMOD_ERR_FORMAT; + } + if( ASF_E_NOTFOUND == hr ) + { + return FMOD_ERR_FORMAT; + } + + pbValue = (BYTE *)FMOD_Memory_Calloc(cbLength); + if (!pbValue) + { + return FMOD_ERR_MEMORY; + } + + hr = headerinfo->GetAttributeByName( &streamnum, L"Seekable", &wmtType, pbValue, &cbLength ); + if ( FAILED( hr ) ) + { + FMOD_Memory_Free(pbValue); + return FMOD_ERR_FORMAT; + } + + mSeekable = *((BOOL *)pbValue) ? true : false; + + FMOD_Memory_Free(pbValue); + + /* + Check for syncpoints. + */ + hr = headerinfo->GetMarkerCount(&markercount); + if ( FAILED( hr ) ) + { + return FMOD_ERR_FORMAT; + } + + if (markercount) + { + WORD count; + + mNumSyncPoints = markercount; + + mSyncPoint = (SyncPointNamed *)FMOD_Memory_Alloc(mNumSyncPoints * sizeof(SyncPointNamed)); + if (!mSyncPoint) + { + return FMOD_ERR_MEMORY; + } + + for (count=0; count < mNumSyncPoints; count++) + { + SyncPointNamed *point = &mSyncPoint[count]; + WCHAR name[256]; + WORD len = 256; + QWORD offset; + + point->mName = point->mNameMemory; + + headerinfo->GetMarker(count, name, &len, &offset); + + wcstombs(point->mName, name, len); + + point->mOffset = (unsigned int)(offset * mWaveFormatMemory->frequency / 10000000); + } + } + + headerinfo->Release(); + } + + if (!mPCMBufferLengthBytes) + { + mPCMBufferLengthBytes = 64 * 1024; + } + if (mPCMBufferLengthBytes) + { + mPCMBuffer = (unsigned char *)FMOD_Memory_Calloc(mPCMBufferLengthBytes); + if (!mPCMBuffer) + { + return FMOD_ERR_MEMORY; + } + } + + mSampleTime = 0; + mSrcDataOffset = 0; + + /* + Fill out base class members, also pointing to or allocating storage for them. + */ + numsubsounds = 0; + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecASF::closeInternal() +{ + if (mReader) + { + mReader->Close(); + mReader->Release(); + mReader = 0; + } + + if (mStream) + { + mStream->Release(); + mStream = 0; + } + + if (mPCMBuffer) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "CodecWAV::release", "Free PCM Buffer\n")); + + FMOD_Memory_Free(mPCMBuffer); + } + mPCMBufferLengthBytes = 0; + + if (mWaveFormatMemory) + { + FMOD_Memory_Free(mWaveFormatMemory); + mWaveFormatMemory = 0; + } + + if (mCoInitialized) + { + CoUninitialize(); + mCoInitialized = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecASF::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + HRESULT hr = S_OK; + INSSBuffer *pSample = NULL; + QWORD cnsDuration = 0; + DWORD dwFlags = 0; + DWORD dwOutputNum = 0; + WORD wStreamNum = 0; + BYTE *pdwBuffer = NULL; + DWORD dwLength = 0; + + *bytesread = 0; + + while (*bytesread == 0) + { + hr = mReader->GetNextSample(mAudioStreamNum, &pSample, &mSampleTime, &cnsDuration, &dwFlags, &dwOutputNum, &wStreamNum); + if (FAILED(hr)) + { + if (hr == NS_E_NO_MORE_SAMPLES) + { + return FMOD_ERR_FILE_EOF; + } + else if (hr == ASF_E_BADDATAUNIT) + { + // This should not happen at the end of files, because we seek FMOD_ASF_MAXIMUM_SEEK_POS_MS before the end + return FMOD_ERR_FILE_BAD; + } + else + { + return FMOD_ERR_FILE_BAD; + } + } + + pSample->GetBufferAndLength(&pdwBuffer, &dwLength); + + if (mExcessBytes >= dwLength) + { + mExcessBytes -= dwLength; + } + else + { + FMOD_memcpy(buffer, pdwBuffer + mExcessBytes, dwLength - mExcessBytes); + + *bytesread = dwLength - mExcessBytes; + mExcessBytes = 0; + } + + pSample->Release(); + + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecASF::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + HRESULT hr; + QWORD offset; + int retries; + unsigned int lengthms; + + if (!mSeekable) + { + return FMOD_ERR_FILE_COULDNOTSEEK; + } + + /* + Prevent GetNextSample errors by not seeking too close to the end of the file + */ + mExcessBytes = 0; + lengthms = (unsigned int)((float)waveformat->lengthpcm / waveformat->frequency * 1000.0f); + if (position > lengthms - FMOD_ASF_MAXIMUM_SEEK_POS_MS) + { + mExcessBytes = position - (lengthms - FMOD_ASF_MAXIMUM_SEEK_POS_MS); // MS + mExcessBytes = (unsigned int)((float)mExcessBytes / 1000.0f * waveformat->frequency); // Samples + SoundI::getBytesFromSamples(mExcessBytes, &mExcessBytes, waveformat->channels, waveformat->format); // Bytes + + position = lengthms - FMOD_ASF_MAXIMUM_SEEK_POS_MS; + } + + /* + I'm sure why but setting it twice seemed to fix a problem with it returning an error "ASF_E_BADDATAUNIT" in some cases. + */ + retries = 0; + do + { + offset = (QWORD)position * 10000; + + hr = mReader->SetRange( offset, 0 ); + if ( FAILED( hr ) ) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "CodecASF::setPositionInternal", "ERROR %08x subsound %d position %d postype %d.\n", hr, subsound, position, postype)); + retries ++; + position+=100; + } + } while (FAILED( hr ) && retries < 100); + + if (FAILED( hr )) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +CROStream::CROStream() : + m_cRefs( 1 ), + mFile( 0 ) +{ +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +CROStream::~CROStream() +{ +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +HRESULT CROStream::Open( LPCTSTR ptszURL ) +{ + mFile = (File *)ptszURL; + + return S_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +HRESULT CROStream::Read( void *pv, ULONG cb, ULONG *pcbRead ) +{ + FMOD_RESULT result; + + result = mFile->read(pv, 1, cb, (unsigned int *)pcbRead); + if (result != FMOD_OK) + { + return !S_OK; + } + + return S_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +HRESULT CROStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ + FMOD_RESULT result; + int dwMoveMethod; + + switch( dwOrigin ) + { + case STREAM_SEEK_SET: + dwMoveMethod = SEEK_SET; + break; + + case STREAM_SEEK_CUR: + dwMoveMethod = SEEK_CUR; + break; + + case STREAM_SEEK_END: + dwMoveMethod = SEEK_END; + break; + + default: + return( E_INVALIDARG ); + }; + + result = mFile->seek(dlibMove.LowPart, dwMoveMethod); + if (result != FMOD_OK) + { + return !S_OK; + } + + if( NULL != plibNewPosition ) + { + DWORD dwPos; + + mFile->tell((unsigned int *)&dwPos); + + plibNewPosition->LowPart = dwPos; + plibNewPosition->HighPart = 0; + } + + return S_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +HRESULT CROStream::Stat( STATSTG *pstatstg, DWORD grfStatFlag ) +{ + FMOD_RESULT result; + + if( ( NULL == pstatstg ) || ( STATFLAG_NONAME != grfStatFlag ) ) + { + return( E_INVALIDARG ); + } + + DWORD dwFileSize; + + result = mFile->getSize((unsigned int *)&dwFileSize); + if (result != FMOD_OK) + { + return !S_OK; + } + + FMOD_memset( pstatstg, 0, sizeof( STATSTG ) ); + + pstatstg->type = STGTY_STREAM; + pstatstg->cbSize.LowPart = dwFileSize; + + return( S_OK ); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +HRESULT CROStream::QueryInterface( REFIID riid, void **ppv ) +{ + if( ( IID_IUnknown == riid ) || ( IID_IStream == riid ) ) + { + *ppv = this; + AddRef(); + + return( S_OK ); + } + + return( E_NOINTERFACE ); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +ULONG CROStream::AddRef() +{ + return( InterlockedIncrement( &m_cRefs ) ); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +ULONG CROStream::Release() +{ + if( 0 == InterlockedDecrement( &m_cRefs ) ) + { + FMOD_Memory_Free(this); + return( 0 ); + } + + return( 0xbad ); +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecASF::soundCreateInternal(int subsound, FMOD_SOUND *sound) +{ + FMOD_RESULT result = FMOD_OK; + SoundI *s = (SoundI *)sound; + + if (mNumSyncPoints && mSyncPoint) + { + int count; + + for (count = 0; count < mNumSyncPoints; count++) + { + s->addSyncPointInternal(mSyncPoint[count].mOffset, FMOD_TIMEUNIT_PCM, mSyncPoint[count].mName, 0); + } + + FMOD_Memory_Free(mSyncPoint); + mSyncPoint = 0; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecASF::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecASF *asf = (CodecASF *)codec; + + return asf->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecASF::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecASF *asf = (CodecASF *)codec; + + return asf->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecASF::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecASF *asf = (CodecASF *)codec; + + return asf->readInternal(buffer, sizebytes, bytesread); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecASF::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecASF *asf = (CodecASF *)codec; + + return asf->setPositionInternal(subsound, position, postype); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecASF::soundCreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound) +{ + CodecASF *asf = (CodecASF *)codec; + + return asf->soundCreateInternal(subsound, sound); +} + +} + +#endif \ No newline at end of file diff --git a/win32/src/fmod_codec_asf.h b/win32/src/fmod_codec_asf.h new file mode 100755 index 0000000..14b7a3b --- /dev/null +++ b/win32/src/fmod_codec_asf.h @@ -0,0 +1,131 @@ +#ifndef _FMOD_CODEC_ASF_H +#define _FMOD_CODEC_ASF_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ASF + +#include "fmod_codeci.h" + +#include <objidl.h> +#include "../lib/wmsdk/include/wmsdk.h" + +namespace FMOD +{ + class CROStream : public IStream + { + public: + + CROStream(); + + // + // IUnknown methods + // + HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid, + /* [out] */ void **ppvObject ); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // + // Methods of IStream + // + HRESULT STDMETHODCALLTYPE Read( void *pv, ULONG cb, ULONG *pcbRead ); + HRESULT STDMETHODCALLTYPE Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition ); + HRESULT STDMETHODCALLTYPE Stat( STATSTG *pstatstg, DWORD grfStatFlag ); + + // + // Non-implemented methods of IStream + // + HRESULT STDMETHODCALLTYPE Write( void const *pv, ULONG cb, ULONG *pcbWritten ) + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE SetSize( ULARGE_INTEGER libNewSize ) + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE CopyTo( IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten ) + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE Commit( DWORD grfCommitFlags ) + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE Revert() + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE LockRegion( ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType ) + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE UnlockRegion( ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType ) + { + return( E_NOTIMPL ); + } + HRESULT STDMETHODCALLTYPE Clone( IStream **ppstm ) + { + return( E_NOTIMPL ); + } + + // + // CROStream method + // + HRESULT Open( /* [in] */ LPCTSTR ptszURL ); + + protected: + + ~CROStream(); + + File *mFile; + LONG m_cRefs; + }; + + typedef HRESULT (STDMETHODCALLTYPE *PFN_WMCREATESYNCREADER)( IUnknown* pUnkCert, DWORD dwRights, IWMSyncReader **ppSyncReader ); + + class SyncPointNamed; + + static const unsigned int FMOD_ASF_MAXIMUM_SEEK_POS_MS = 2000; + + class CodecASF : public Codec + { + private: + + bool mCoInitialized; + IWMSyncReader *mReader; // IWMReader pointer + CROStream *mStream; + WORD mAudioStreamNum; + WORD mVideoStreamNum; + QWORD mSampleTime; + bool mSeekable; + int mNumSyncPoints; + SyncPointNamed *mSyncPoint; + unsigned int mExcessBytes; + + static PFN_WMCREATESYNCREADER gWMCreateSyncReader; + static HMODULE gDLLHandle; + + FMOD_RESULT getStreamNumbers(IWMProfile* pProfile); + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + FMOD_RESULT soundCreateInternal(int subsound, FMOD_SOUND *sound); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + static FMOD_RESULT F_CALLBACK soundCreateCallback(FMOD_CODEC_STATE *codec, int subsound, FMOD_SOUND *sound); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_ASF */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_codec_cdda.cpp b/win32/src/fmod_codec_cdda.cpp new file mode 100755 index 0000000..aa12a22 --- /dev/null +++ b/win32/src/fmod_codec_cdda.cpp @@ -0,0 +1,373 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CDDA + +#include "fmod.h" +#include "fmod_codec_cdda.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_file_cdda.h" +#include "fmod_metadata.h" +#include "fmod_os_cdda.h" +#include "fmod_soundi.h" +#include "fmod_string.h" + +#include <memory.h> +#include <stdio.h> + + +namespace FMOD +{ + + +FMOD_CODEC_DESCRIPTION_EX cddacodec; + + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetCodecDescriptionEx is mandatory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx() + { + return CodecCDDA::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_CODEC_DESCRIPTION_EX *CodecCDDA::getDescriptionEx() +{ + FMOD_memset(&cddacodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX)); + + cddacodec.name = "FMOD CDDA Codec"; + cddacodec.version = 0x00010100; + cddacodec.timeunits = FMOD_TIMEUNIT_PCM; + cddacodec.open = &CodecCDDA::openCallback; + cddacodec.close = &CodecCDDA::closeCallback; + cddacodec.read = &CodecCDDA::readCallback; + cddacodec.setposition = &CodecCDDA::setPositionCallback; + + cddacodec.mType = FMOD_SOUND_TYPE_CDDA; + cddacodec.mSize = sizeof(CodecCDDA); + + return &cddacodec; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCDDA::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + int count; + char *filename; + CddaFile *cddafile; + FMOD_RESULT result = FMOD_OK; + + mCurrentTrack = -1; + + init(FMOD_SOUND_TYPE_CDDA); + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "CodecCDDA::openInternal", "attempting to open as CDDA..\n")); + + mFile->getName(&filename); + if (!FMOD_OS_CDDA_IsDeviceName(filename)) + { + return FMOD_ERR_FORMAT; + } + + cddafile = SAFE_CAST(CddaFile, mFile); + + result = cddafile->getNumTracks(&numsubsounds); + if (result != FMOD_OK) + { + return result; + } + + if (!numsubsounds) + { + return FMOD_ERR_CDDA_NOAUDIO; + } + + waveformat = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT) * numsubsounds); + if (!waveformat) + { + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < numsubsounds; count++) + { + unsigned int tracklength; + + result = cddafile->getTrackLength(count, &tracklength); + if (result != FMOD_OK) + { + return result; + } + + sprintf(waveformat[count].name, "Track %d", count+1); + waveformat[count].format = FMOD_SOUND_FORMAT_PCM16; + waveformat[count].channels = 2; + waveformat[count].frequency = 44100; + waveformat[count].lengthpcm = tracklength >> 2; + waveformat[count].blockalign = waveformat[count].channels * 16 / 8; + } + + mPCMBufferLengthBytes = 256 * 1024; + + result = setPositionInternal(0, 0, FMOD_TIMEUNIT_PCM); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCDDA::closeInternal() +{ + if (waveformat) + { + FMOD_Memory_Free(waveformat); + waveformat = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCDDA::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + FMOD_RESULT result = FMOD_OK; + + result = mFile->read(buffer, 1, sizebytes, bytesread); + if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF) + { + return result; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT CodecCDDA::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CddaFile *cddafile; + FMOD_RESULT result = FMOD_OK; + unsigned int offset; + + if (subsound < 0 || (numsubsounds && subsound >= numsubsounds)) + { + return FMOD_ERR_INVALID_POSITION; + } + + cddafile = SAFE_CAST(CddaFile, mFile); + + if (subsound != mCurrentTrack) + { + mCurrentTrack = subsound; + + result = cddafile->openTrack(mCurrentTrack); + if (result != FMOD_OK) + { + return result; + } + } + + result = SoundI::getBytesFromSamples(position, &offset, waveformat[mCurrentTrack].channels, waveformat[mCurrentTrack].format); + if (result != FMOD_OK) + { + return result; + } + + result = cddafile->seek(offset, SEEK_SET); + if (result != FMOD_OK) + { + return result; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCDDA::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo) +{ + CodecCDDA *cdda = (CodecCDDA *)codec; + + return cdda->openInternal(usermode, userexinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCDDA::closeCallback(FMOD_CODEC_STATE *codec) +{ + CodecCDDA *cdda = (CodecCDDA *)codec; + + return cdda->closeInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCDDA::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread) +{ + CodecCDDA *cdda = (CodecCDDA *)codec; + + return cdda->readInternal(buffer, sizebytes, bytesread); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK CodecCDDA::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype) +{ + CodecCDDA *cdda = (CodecCDDA *)codec; + + return cdda->setPositionInternal(subsound, position, postype); +} + +} + +#endif diff --git a/win32/src/fmod_codec_cdda.h b/win32/src/fmod_codec_cdda.h new file mode 100755 index 0000000..4534963 --- /dev/null +++ b/win32/src/fmod_codec_cdda.h @@ -0,0 +1,36 @@ +#ifndef _FMOD_CODEC_CDDA_H +#define _FMOD_CODEC_CDDA_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CDDA + +#include "fmod_codeci.h" + +namespace FMOD +{ + class CodecCDDA : public Codec + { + private: + + int mCurrentTrack; + + FMOD_RESULT openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + FMOD_RESULT closeInternal(); + FMOD_RESULT readInternal(void *buffer, unsigned int size, unsigned int *read); + FMOD_RESULT setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + public: + + static FMOD_RESULT F_CALLBACK openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo); + static FMOD_RESULT F_CALLBACK closeCallback(FMOD_CODEC_STATE *codec); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread); + static FMOD_RESULT F_CALLBACK setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype); + + static FMOD_CODEC_DESCRIPTION_EX *getDescriptionEx(); + }; +} + +#endif /* FMOD_SUPPORT_CDDA */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_dllmain.cpp b/win32/src/fmod_dllmain.cpp new file mode 100755 index 0000000..fd0771f --- /dev/null +++ b/win32/src/fmod_dllmain.cpp @@ -0,0 +1,6 @@ +#include <windows.h> + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + return TRUE; +} \ No newline at end of file diff --git a/win32/src/fmod_dsp_connection_asm.s b/win32/src/fmod_dsp_connection_asm.s new file mode 100755 index 0000000..8542c88 --- /dev/null +++ b/win32/src/fmod_dsp_connection_asm.s @@ -0,0 +1,732 @@ +%include "./FMOD_static/win32/src/c32.mac" + +; ========================================================================================== +; GLOBAL UNINITIALIZED DATA +; ========================================================================================== + +[SEGMENT .data use32 align=32] + +; ========================================================================================== +; CODE +; ========================================================================================== + +[SEGMENT .text use32 align=32] + + +; ================================================================================================================================= +; void FMOD_DSP_Connection_MixMonoToStereo_SIMD (float *inbuffer, float *outbuffer, unsigned int length, float lvolume, float rvolume); +; ================================================================================================================================= +proc FMOD_DSP_Connection_MixMonoToStereo_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$lvolume arg + %$rvolume arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [lvolume ][rvolume ][lvolume ][rvolume ] + ; xmm1 = [samp1 ][samp1 ][samp2 ][samp2 ] + ; xmm2 = [samp3 ][samp3 ][samp4 ][samp4 ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + movss xmm0, [ebp+%$lvolume] + movss xmm1, [ebp+%$rvolume] + shufps xmm0, xmm0, 0 + shufps xmm1, xmm1, 0 + unpcklps xmm0, xmm1 + + mov edx, [ebp+%$length] + mov ecx, edx +%if 1 + shr ecx, 2 + test ecx, ecx + jz mixloopMtoSrolledstart + +mixloopMtoSunrolled: + + movups xmm1, [esi] + movaps xmm2, xmm1 + + movaps xmm3, [edi] + movaps xmm4, [edi + 16] + + unpcklps xmm1, xmm1 + unpckhps xmm2, xmm2 + + mulps xmm1, xmm0 + mulps xmm2, xmm0 + + addps xmm3, xmm1 + addps xmm4, xmm2 + + movaps [edi], xmm3 + movaps [edi+16], xmm4 + + add edi, 32 + add esi, 16 + dec ecx + jnz near mixloopMtoSunrolled + +mixloopMtoSrolledstart: + + mov ecx, edx + and ecx, 3 +%endif + test ecx, ecx + jz mixMtoSdone + +mixloopMtoSrolled: + + movss xmm1, [esi] + movss xmm2, xmm1 + + movss xmm3, [edi] + movss xmm4, [edi+4] + + mulss xmm1, [ebp+%$lvolume] + mulss xmm2, [ebp+%$rvolume] + + addss xmm3, xmm1 + addss xmm4, xmm2 + + movss [edi], xmm3 + movss [edi+4], xmm4 + + add edi, 8 + add esi, 4 + + dec ecx + jnz near mixloopMtoSrolled + +mixMtoSdone: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax +endproc + +; ================================================================================================================================= +; void FMOD_DSP_Connection_MixStereoToStereo_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float lvolume, float rvolume); +; ================================================================================================================================= +proc FMOD_DSP_Connection_MixStereoToStereo_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$lvolume arg + %$rvolume arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [lvolume ][rvolume ][lvolume ][rvolume ] + ; xmm1 = [samp1 ][samp1 ][samp2 ][samp2 ] + ; xmm2 = [samp3 ][samp3 ][samp4 ][samp4 ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + movss xmm0, [ebp+%$lvolume] + movss xmm1, [ebp+%$rvolume] + shufps xmm0, xmm0, 0 + shufps xmm1, xmm1, 0 + unpcklps xmm0, xmm1 + + mov edx, [ebp+%$length] + mov ecx, edx +%if 1 + shr ecx, 2 + test ecx, ecx + jz mixloopStoSrolledstart + +mixloopStoSunrolled: + + movups xmm1, [esi] + movups xmm2, [esi+16] + + movaps xmm3, [edi] + movaps xmm4, [edi+16] + + mulps xmm1, xmm0 + mulps xmm2, xmm0 + + addps xmm3, xmm1 + addps xmm4, xmm2 + + movaps [edi], xmm3 + movaps [edi+16], xmm4 + + add edi, 32 + add esi, 32 + dec ecx + jnz near mixloopStoSunrolled + +mixloopStoSrolledstart: + + mov ecx, edx + and ecx, 3 +%endif + test ecx, ecx + jz mixStoSdone + +mixloopStoSrolled: + + movss xmm1, [esi] + movss xmm2, [esi+4] + + movss xmm3, [edi] + movss xmm4, [edi+4] + + mulss xmm1, [ebp+%$lvolume] + mulss xmm2, [ebp+%$rvolume] + + addss xmm3, xmm1 + addss xmm4, xmm2 + + movss [edi], xmm3 + movss [edi+4], xmm4 + + add edi, 8 + add esi, 8 + + dec ecx + jnz near mixloopStoSrolled + +mixStoSdone: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + +endproc + + +; ================================================================================================================================= +; void FMOD_DSP_Connection_MixMonoTo5_1_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to1, float *volume2to5); +; ================================================================================================================================= +proc FMOD_DSP_Connection_MixMonoTo5_1_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$volume0to3 arg + %$volume4to1 arg + %$volume2to5 arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [l00 ][l10 ][l20 ][l30 ] + ; xmm1 = [l40 ][l50 ][l00 ][l10 ] + ; xmm2 = [l20 ][l30 ][l40 ][l50 ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + mov edx, [ebp+%$length] + mov ecx, edx + + mov eax, [ebp+%$volume0to3] + movups xmm0, [eax] + mov eax, [ebp+%$volume4to1] + movups xmm1, [eax] + mov eax, [ebp+%$volume2to5] + movups xmm2, [eax] + +%if 1 + shr ecx, 2 + test ecx, ecx + jz near mixloopMto51rolledstart + +mixloopMto51unrolled: + + movss xmm3, [esi + 0] + movlps xmm4, [esi + 0] + shufps xmm3, xmm3, 0 + shufps xmm4, xmm4, 50h + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi] + addps xmm4, [edi + 16] + movaps [edi], xmm3 + movaps [edi+16], xmm4 + + movss xmm3, [esi + 4] + movss xmm4, [esi + 8] + shufps xmm3, xmm3, 0 + shufps xmm4, xmm4, 0 + mulps xmm3, xmm2 + mulps xmm4, xmm0 + addps xmm3, [edi + 32] + addps xmm4, [edi + 48] + movaps [edi+32], xmm3 + movaps [edi+48], xmm4 + + movlps xmm3, [esi + 8] + movss xmm4, [esi + 12] + shufps xmm3, xmm3, 50h + shufps xmm4, xmm4, 0 + mulps xmm3, xmm1 + mulps xmm4, xmm2 + addps xmm3, [edi + 64] + addps xmm4, [edi + 80] + movaps [edi+64], xmm3 + movaps [edi+80], xmm4 + + add edi, 96 + add esi, 16 + dec ecx + jnz near mixloopMto51unrolled + +mixloopMto51rolledstart: + + mov ecx, ebp + and ecx, 3 +%endif + test ecx, ecx + jz mixMto51done + +mixloopMto51rolled: + + movss xmm3, [esi + 0] + shufps xmm3, xmm3, 0 + movups xmm4, [edi] + mulps xmm3, xmm0 + addps xmm4, xmm3 + movups [edi], xmm4 + + movlps xmm3, [esi + 0] + shufps xmm3, xmm3, 50h + movlps xmm4, [edi + 16] + mulps xmm3, xmm1 + addps xmm4, xmm3 + movlps [edi + 16], xmm4 + + add edi, 24 + add esi, 4 + + dec ecx + jnz near mixloopMto51rolled + +mixMto51done: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + +endproc + + +; ================================================================================================================================= +; void FMOD_DSP_Connection_Mix5_1To5_1_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to1, float *volume2to5); +; ================================================================================================================================= +proc FMOD_DSP_Connection_Mix5_1To5_1_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$volume0to3 arg + %$volume4to1 arg + %$volume2to5 arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [l00 ][l10 ][l20 ][l30 ] + ; xmm1 = [l40 ][l50 ][l00 ][l10 ] + ; xmm2 = [l20 ][l30 ][l40 ][l50 ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + mov edx, [ebp+%$length] + mov ecx, edx + + mov eax, [ebp+%$volume0to3] + movups xmm0, [eax] + mov eax, [ebp+%$volume4to1] + movups xmm1, [eax] + mov eax, [ebp+%$volume2to5] + movups xmm2, [eax] + +%if 1 + shr ecx, 2 + test ecx, ecx + jz near mixloop51to51rolledstart + +mixloop51to51unrolled: + + movaps xmm3, [esi + 0] + movaps xmm4, [esi + 16] + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 0] + addps xmm4, [edi + 16] + movaps [edi], xmm3 + movaps [edi+16], xmm4 + + movaps xmm3, [esi + 32] + movaps xmm4, [esi + 48] + mulps xmm3, xmm2 + mulps xmm4, xmm0 + addps xmm3, [edi + 32] + addps xmm4, [edi + 48] + movaps [edi+32], xmm3 + movaps [edi+48], xmm4 + + movaps xmm3, [esi + 64] + movaps xmm4, [esi + 80] + mulps xmm3, xmm1 + mulps xmm4, xmm2 + addps xmm3, [edi + 64] + addps xmm4, [edi + 80] + movaps [edi+64], xmm3 + movaps [edi+80], xmm4 + + add edi, 96 + add esi, 96 + dec ecx + jnz near mixloop51to51unrolled + +mixloop51to51rolledstart: + + mov ecx, ebp + and ecx, 3 +%endif + test ecx, ecx + jz mix51to51done + +mixloop51to51rolled: + + movups xmm3, [esi + 0] + movups xmm4, [edi + 0] + mulps xmm3, xmm0 + addps xmm4, xmm3 + movups [edi], xmm4 + + movlps xmm3, [esi + 16] + movlps xmm4, [edi + 16] + mulps xmm3, xmm1 + addps xmm4, xmm3 + movlps [edi+16], xmm4 + + add edi, 24 + add esi, 24 + + dec ecx + jnz near mixloop51to51rolled + +mix51to51done: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + +endproc + + +; ================================================================================================================================= +; void FMOD_DSP_Connection_MixMonoTo7_1_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to7); +; ================================================================================================================================= +proc FMOD_DSP_Connection_MixMonoTo7_1_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$volume0to3 arg + %$volume4to7 arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [l00 ][l10 ][l20 ][l30 ] + ; xmm1 = [l40 ][l50 ][l00 ][l10 ] + ; xmm2 = [l20 ][l30 ][l40 ][l50 ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + mov edx, [ebp+%$length] + mov ecx, edx + + mov eax, [ebp+%$volume0to3] + movups xmm0, [eax] + mov eax, [ebp+%$volume4to7] + movups xmm1, [eax] + +%if 1 + shr ecx, 2 + test ecx, ecx + jz near mixloopMto71rolledstart + +mixloopMto71unrolled: + + movss xmm3, [esi + 0] + shufps xmm3, xmm3, 0 + movaps xmm4, xmm3 + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 0] + addps xmm4, [edi + 16] + movaps [edi+0], xmm3 + movaps [edi+16], xmm4 + + movss xmm3, [esi + 4] + shufps xmm3, xmm3, 0 + movaps xmm4, xmm3 + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 32] + addps xmm4, [edi + 48] + movaps [edi+32], xmm3 + movaps [edi+48], xmm4 + + movss xmm3, [esi + 8] + shufps xmm3, xmm3, 0 + movaps xmm4, xmm3 + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 64] + addps xmm4, [edi + 80] + movaps [edi+64], xmm3 + movaps [edi+80], xmm4 + + movss xmm3, [esi + 12] + shufps xmm3, xmm3, 0 + movaps xmm4, xmm3 + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 96] + addps xmm4, [edi + 112] + movaps [edi+96], xmm3 + movaps [edi+112], xmm4 + + add edi, 128 + add esi, 16 + dec ecx + jnz near mixloopMto71unrolled + +mixloopMto71rolledstart: + + mov ecx, ebp + and ecx, 3 +%endif + test ecx, ecx + jz mixMto71done + +mixloopMto71rolled: + + movss xmm3, [esi + 0] + shufps xmm3, xmm3, 0 + movaps xmm4, xmm3 + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 0] + addps xmm4, [edi + 16] + movaps [edi+0], xmm3 + movaps [edi+16], xmm4 + + add edi, 32 + add esi, 4 + + dec ecx + jnz near mixloopMto71rolled + +mixMto71done: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + +endproc + + +; ================================================================================================================================= +; void FMOD_DSP_Connection_Mix7_1To7_1_SIMD(float *inbuffer, float *outbuffer, unsigned int length, float *volume0to3, float *volume4to7); +; ================================================================================================================================= +proc FMOD_DSP_Connection_Mix7_1To7_1_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$volume0to3 arg + %$volume4to7 arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [l00 ][l10 ][l20 ][l30 ] + ; xmm1 = [l40 ][l50 ][l00 ][l10 ] + ; xmm2 = [l20 ][l30 ][l40 ][l50 ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + mov edx, [ebp+%$length] + mov ecx, edx + + mov eax, [ebp+%$volume0to3] + movups xmm0, [eax] + mov eax, [ebp+%$volume4to7] + movups xmm1, [eax] + +%if 1 + shr ecx, 2 + test ecx, ecx + jz near mixloop71to71rolledstart + +mixloop71to71unrolled: + + movaps xmm3, [esi + 0] + movaps xmm4, [esi + 16] + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 0] + addps xmm4, [edi + 16] + movaps [edi], xmm3 + movaps [edi+16], xmm4 + + movaps xmm3, [esi + 32] + movaps xmm4, [esi + 48] + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 32] + addps xmm4, [edi + 48] + movaps [edi+32], xmm3 + movaps [edi+48], xmm4 + + movaps xmm3, [esi + 64] + movaps xmm4, [esi + 80] + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 64] + addps xmm4, [edi + 80] + movaps [edi+64], xmm3 + movaps [edi+80], xmm4 + + movaps xmm3, [esi + 96] + movaps xmm4, [esi + 112] + mulps xmm3, xmm0 + mulps xmm4, xmm1 + addps xmm3, [edi + 96] + addps xmm4, [edi + 112] + movaps [edi+96], xmm3 + movaps [edi+112], xmm4 + + add edi, 128 + add esi, 128 + dec ecx + jnz near mixloop71to71unrolled + +mixloop71to71rolledstart: + + mov ecx, ebp + and ecx, 3 +%endif + test ecx, ecx + jz mix71to71done + +mixloop71to71rolled: + + movups xmm3, [esi + 0] + movups xmm4, [edi + 0] + mulps xmm3, xmm0 + addps xmm4, xmm3 + movups [edi], xmm4 + + movups xmm3, [esi + 16] + movups xmm4, [edi + 16] + mulps xmm3, xmm1 + addps xmm4, xmm3 + movups [edi+16], xmm4 + + add edi, 32 + add esi, 32 + + dec ecx + jnz near mixloop71to71rolled + +mix71to71done: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + +endproc diff --git a/win32/src/fmod_dsp_convert_asm.s b/win32/src/fmod_dsp_convert_asm.s new file mode 100755 index 0000000..d49f00b --- /dev/null +++ b/win32/src/fmod_dsp_convert_asm.s @@ -0,0 +1,148 @@ +%include "./FMOD_static/win32/src/c32.mac" + +; ========================================================================================== +; GLOBAL UNINITIALIZED DATA +; ========================================================================================== + +[SEGMENT .bss use32 align=32] + +; ========================================================================================== +; GLOBAL PREINITIALIZED DATA +; ========================================================================================== + +section .text +align 16 + +negones dd -32768.0, -32768.0, -32768.0, -32768.0 +ones dd 32767.0, 32767.0, 32767.0, 32767.0 + +; ========================================================================================== +; CODE +; ========================================================================================== + +section .text + +; ================================================================================================================================= +; void FMOD_DSP_Convert_FloatToPCM16(short *outbuffer, float *inbuffer, unsigned int length, int destchannelstep, int srcchannelstep, float volume); +; ================================================================================================================================= +proc FMOD_DSP_Convert_FloatToPCM16 + + %$outbuffer arg + %$inbuffer arg + %$length arg + %$destchannelstep arg + %$srcchannelstep arg + %$volume arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + ; xmm0 = [ ][ ][ ][ ] + ; xmm1 = [ ][ ][ ][ ] + ; xmm2 = [ ][ ][ ][ ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + mov eax, [ebp+%$srcchannelstep] + shl eax, 2 + mov ebx, [ebp+%$destchannelstep] + shl ebx, 1 + + movss xmm4, [ebp+%$volume] + shufps xmm4, xmm4, 0 + movaps xmm5, [negones] + movaps xmm6, [ones] + +%if 1 + mov ecx, [ebp+%$length] + shr ecx, 2 + test ecx, ecx + jz near convertf2int16rolledstart + +convertf2int16unrolled: + + movss xmm0, [esi] + movss xmm1, [esi + eax] + lea esi, [esi + eax * 2] + movss xmm2, [esi] + movss xmm3, [esi + eax] + lea esi, [esi + eax * 2] + + unpcklps xmm0, xmm1 + unpcklps xmm2, xmm3 + shufps xmm0, xmm2, 044h + + mulps xmm0, xmm4 ; now from -1/+1 to -32768/+32767 (and may be outside of this range) + + movhlps xmm1, xmm0 + + cvttps2pi mm0, xmm0 + cvttps2pi mm1, xmm1 + + packssdw mm0, mm0 ; clamps to -32768 to 32767 + packssdw mm1, mm1 ; clamps to -32768 to 32767 + + movd edx, mm0 + mov [edi + 0], dl + mov [edi + 1], dh + shr edx, 16 + mov [edi + ebx + 0], dl + mov [edi + ebx + 1], dh + lea edi, [edi + ebx * 2] + + movd edx, mm1 + mov [edi + 0], dl + mov [edi + 1], dh + shr edx, 16 + mov [edi + ebx + 0], dl + mov [edi + ebx + 1], dh + lea edi, [edi + ebx * 2] + + dec ecx + jnz near convertf2int16unrolled + +convertf2int16rolledstart: + + mov ecx, [ebp+%$length] + and ecx, 3 +%endif + test ecx, ecx + jz near convertf2int16done + +convertf2int16rolled: + + movss xmm0, [esi] + mulps xmm0, xmm4 ; now from -1/+1 to -32768/+32767 (and may be outside of this range) + cvttps2pi mm0, xmm0 + packssdw mm0, mm0 ; clamps to -32768 to 32767 + movd edx, mm0 + mov [edi + 0], dl + mov [edi + 1], dh + + add edi, ebx + add esi, eax + dec ecx + jnz near convertf2int16rolled + +convertf2int16done: + + emms + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax +endproc + diff --git a/win32/src/fmod_dsp_distortion.cpp b/win32/src/fmod_dsp_distortion.cpp new file mode 100755 index 0000000..f098f13 --- /dev/null +++ b/win32/src/fmod_dsp_distortion.cpp @@ -0,0 +1,612 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DISTORTION + +#include "fmod.h" +#include "fmod_dspi.h" +#include "fmod_dsp_distortion.h" +#include "fmod_systemi.h" +#include "fmod_os_misc.h" + +#include <stdio.h> + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspdistortion; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPDistortion::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + +FMOD_DSP_PARAMETERDESC dspdistortion_param[1] = +{ + { 0.0f, 1.0f, 0.5, "Level", "", "Distortion value. 0.0 to 1.0. Default = 0.5." }, +}; + +extern "C" +{ + void FMOD_DSP_Distortion_SIMD(float * inbuffer, float * outbuffer, unsigned int length, int inchannels, int outchannels, float k); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPDistortion::getDescriptionEx() +{ + FMOD_memset(&dspdistortion, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + FMOD_strcpy(dspdistortion.name, "FMOD Distortion"); + dspdistortion.version = 0x00010100; + dspdistortion.create = DSPDistortion::createCallback; + dspdistortion.release = DSPDistortion::releaseCallback; + dspdistortion.reset = DSPDistortion::resetCallback; + dspdistortion.read = DSPDistortion::readCallback; + + dspdistortion.numparameters = sizeof(dspdistortion_param) / sizeof(dspdistortion_param[0]); + dspdistortion.paramdesc = dspdistortion_param; + dspdistortion.setparameter = DSPDistortion::setParameterCallback; + dspdistortion.getparameter = DSPDistortion::getParameterCallback; +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dspdistortion.getmemoryused = &DSPDistortion::getMemoryUsedCallback; +#endif + + dspdistortion.mType = FMOD_DSP_TYPE_DISTORTION; + dspdistortion.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspdistortion.mSize = sizeof(DSPDistortion); + + return &dspdistortion; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDistortion::createInternal() +{ + int count; + + init(); + + mSupportsSIMD = FMOD_OS_SupportsSIMD(); + + for (count = 0; count < mDescription.numparameters; count++) + { + FMOD_RESULT result; + + result = setParameter(count, mDescription.paramdesc[count].defaultval); + if (result != FMOD_OK) + { + return result; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDistortion::releaseInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDistortion::resetInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDistortion::readInternal(float * inbuffer, float * outbuffer, unsigned int length, int inchannels, int outchannels) +{ + float k; + float level = mLevel; + + if (!inbuffer) + { + return FMOD_OK; + } + + if (!(speakermask & ((1 << inchannels)-1))) + { + FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); + return FMOD_OK; + } + + if (level < 1.0f) + { + k = 2.0f * level / (1.0f - level); + } + else + { + k = 2.0f * 0.9999f / (1.0f - 0.9999f); + } + + if (mSupportsSIMD) + { + FMOD_DSP_Distortion_SIMD(inbuffer, outbuffer, length, inchannels, outchannels, k); + if (!((speakermask & ((1<<inchannels)-1)) == ((1<<inchannels)-1))) + { + unsigned int len; + int count, inc; + + for (count = 0; count < inchannels; count++) + { + float *in = inbuffer + count; + float *out = outbuffer + count; + len = length >> 3; + inc = inchannels << 3; + int offset1, offset2, offset3, offset4, offset5, offset6, offset7; + offset1 = inchannels; + offset2 = inchannels * 2; + offset3 = inchannels * 3; + offset4 = inchannels * 4; + offset5 = inchannels * 5; + offset6 = inchannels * 6; + offset7 = inchannels * 7; + + if (!((1 << count) & speakermask)) + { + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + out[offset4] = in[offset4]; + out[offset5] = in[offset5]; + out[offset6] = in[offset6]; + out[offset7] = in[offset7]; + in += inc; + out += inc; + len--; + } + + len = length & 7; + + while (len) + { + outbuffer[0] = inbuffer[0]; + inbuffer += inchannels; + outbuffer += inchannels; + len--; + } + } + } + } + } + else + { + unsigned int len; + int count, inc; + int offset1, offset2, offset3, offset4, offset5, offset6, offset7; + inc = inchannels << 3; + offset1 = inchannels; + offset2 = inchannels * 2; + offset3 = inchannels * 3; + offset4 = inchannels * 4; + offset5 = inchannels * 5; + offset6 = inchannels * 6; + offset7 = inchannels * 7; + + for (count = 0; count < inchannels; count++) + { + float *in = inbuffer + count; + float *out = outbuffer + count; + len = length >> 3; + + if (!((1 << count) & speakermask)) + { + while (len) + { + out[0] = in[0]; + out[offset1] = in[offset1]; + out[offset2] = in[offset2]; + out[offset3] = in[offset3]; + out[offset4] = in[offset4]; + out[offset5] = in[offset5]; + out[offset6] = in[offset6]; + out[offset7] = in[offset7]; + in += inc; + out += inc; + len--; + } + + len = (length * inchannels) & 7; + + while (len) + { + outbuffer[0] = inbuffer[0]; + inbuffer+=inchannels; + outbuffer+=inchannels; + len--; + } + } + else + { + float kplus1 = 1.0f + k; + + while (len) + { + out[0] = kplus1 * in[0] / (1.0f + k * FMOD_FABS(in[0])); + out[offset1] = kplus1 * in[offset1] / (1.0f + k * FMOD_FABS(in[offset1])); + out[offset2] = kplus1 * in[offset2] / (1.0f + k * FMOD_FABS(in[offset2])); + out[offset3] = kplus1 * in[offset3] / (1.0f + k * FMOD_FABS(in[offset3])); + out[offset4] = kplus1 * in[offset4] / (1.0f + k * FMOD_FABS(in[offset4])); + out[offset5] = kplus1 * in[offset5] / (1.0f + k * FMOD_FABS(in[offset5])); + out[offset6] = kplus1 * in[offset6] / (1.0f + k * FMOD_FABS(in[offset6])); + out[offset7] = kplus1 * in[offset7] / (1.0f + k * FMOD_FABS(in[offset7])); + in += inc; + out += inc; + len--; + } + + len = (length * inchannels) & 7; + + while (len) + { + outbuffer[0] = kplus1 * inbuffer[0] / (1.0f + k * FMOD_FABS(inbuffer[0])); + inbuffer+=inchannels; + outbuffer+=inchannels; + len--; + } + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDistortion::setParameterInternal(int index, float value) +{ + mLevel = value; + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPDistortion::getParameterInternal(int index, float *value, char *valuestr) +{ + *value = mLevel; + sprintf(valuestr, "%.02f", mLevel); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT DSPDistortion::getMemoryUsedImpl(MemoryTracker *tracker) +{ + // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. + + return FMOD_OK; +} + +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->getParameterInternal(index, value, valuestr); +} + + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPDistortion::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) +{ + DSPDistortion *distortion = (DSPDistortion *)dsp; + + return distortion->DSPDistortion::getMemoryUsed(tracker); +} +#endif + + +} + +#endif \ No newline at end of file diff --git a/win32/src/fmod_dsp_distortion_asm.s b/win32/src/fmod_dsp_distortion_asm.s new file mode 100755 index 0000000..e1f5457 --- /dev/null +++ b/win32/src/fmod_dsp_distortion_asm.s @@ -0,0 +1,128 @@ +%include "./FMOD_static/win32/src/c32.mac" + +; ========================================================================================== +; GLOBAL UNINITIALIZED DATA +; ========================================================================================== + +[SEGMENT .data use32 align=32] + +distortion_ones dd 1.0,1.0,1.0,1.0 +distortion_fabsmask dd 7FFFFFFFh, 7FFFFFFFh, 7FFFFFFFh, 7FFFFFFFh + +; ========================================================================================== +; CODE +; ========================================================================================== + +[SEGMENT .text use32 align=32] + +; ================================================================================================================================= +; void FMOD_DSP_Distortion_SIMD(float * inbuffer, float * outbuffer, unsigned int length, int inchannels, int outchannels, float k); +; ================================================================================================================================= +proc FMOD_DSP_Distortion_SIMD + + %$inbuffer arg + %$outbuffer arg + %$length arg + %$inchannels arg + %$outchannels arg + %$k arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov esi, [ebp+%$inbuffer] + mov edi, [ebp+%$outbuffer] + + ; xmm0 = [0x7fffffff][0x7fffffff][0x7fffffff][0x7fffffff] + ; xmm1 = [k ][k ][k ][k ] + ; xmm2 = [k+1 ][k+1 ][k+1 ][k+1 ] + ; xmm3 = + ; xmm4 = + ; xmm5 = [1.0f ][1.0f ][1.0f ][1.0f ] + ; xmm6 = + ; xmm7 = + + movaps xmm0, [distortion_fabsmask] + movss xmm1, [ebp+%$k] + shufps xmm1, xmm1, 0x00 + movaps xmm2, xmm1 + movaps xmm5, [distortion_ones] + addps xmm2, xmm5 + + mov edx, [ebp+%$length] + imul edx, [ebp+%$inchannels] + mov ecx, edx + shr ecx, 3 + test ecx, ecx + jz distortionlooprolledstart + +distortionloopunrolled: + + movups xmm3, [esi] + movups xmm4, xmm3 + andps xmm3, xmm0 + mulps xmm3, xmm1 + addps xmm3, xmm5 + rcpps xmm3, xmm3 ; <--- this is the reciprical calc. + mulps xmm4, xmm2 ; | +; divps xmm4, xmm3 ; | + mulps xmm4, xmm3 ; reciprical multiply instead of a div. Only 12bit accuracy but good enough for here. much faster. + movups [edi], xmm4 + + movups xmm6, [esi+16] + movups xmm7, xmm6 + andps xmm6, xmm0 + mulps xmm6, xmm1 + addps xmm6, xmm5 + rcpps xmm6, xmm6 ; <--- this is the reciprical calc. + mulps xmm7, xmm2 ; | +; divps xmm7, xmm6 ; | + mulps xmm7, xmm6 ; reciprical multiply instead of a div. Only 12bit accuracy but good enough for here. much faster. + movups [edi+16], xmm7 + + add edi, 32 + add esi, 32 + dec ecx + jnz near distortionloopunrolled + +distortionlooprolledstart: + + xorps xmm3, xmm3 + + mov ecx, edx + and ecx, 7 + test ecx, ecx + jz distortiondone + +distortionlooprolled: + + movss xmm3, [esi] + movss xmm4, xmm3 + andps xmm3, xmm0 + mulss xmm3, xmm1 + addss xmm3, xmm5 + rcpss xmm3, xmm3 ; <--- this is the reciprical calc. + mulss xmm4, xmm2 ; | +; divss xmm4, xmm3 ; | + mulss xmm4, xmm3 ; reciprical multiply instead of a div. Only 12bit accuracy but good enough for here. much faster. + movss [edi], xmm4 + + add edi, 4 + add esi, 4 + + dec ecx + jnz near distortionlooprolled + +distortiondone: + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax +endproc diff --git a/win32/src/fmod_dsp_resampler_linear.cpp b/win32/src/fmod_dsp_resampler_linear.cpp new file mode 100755 index 0000000..98154e1 --- /dev/null +++ b/win32/src/fmod_dsp_resampler_linear.cpp @@ -0,0 +1,621 @@ +#include "fmod_settings.h" + +#include "fmod_dsp_resampler_linear.h" +#include "fmod_os_misc.h" + +#define OO4GIG (1.0f / 4294967296.0f) + +extern "C" +{ + void FMOD_Resampler_Linear_PCM16_Mono(float *out, int outlength, void *src, FMOD_UINT64P *position, FMOD_SINT64P *speed); + void FMOD_Resampler_Linear_PCM16_Stereo(float *out, int outlength, void *src, FMOD_UINT64P *position, FMOD_SINT64P *speed); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +void FMOD_Resampler_Linear(float *out, int outlength, void *src, FMOD_SOUND_FORMAT srcformat, FMOD_UINT64P *position, FMOD_SINT64P *speed, int channels) +{ + float scale = 1.0f; + bool supportssimd; + + supportssimd = FMOD_OS_SupportsSIMD(); + + switch (srcformat) + { + /* + 8 BIT + */ + case FMOD_SOUND_FORMAT_PCM8: + { + signed char *inptr = (signed char *)src; + + scale /= (float)(1<<7); + + if (channels == 1) + { + float f; + float a, b, r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r1 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r2 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r3 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r4 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r1 = (a * (1.0f - f)) + (b * f); + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else if (channels == 2) + { + float f; + float l_a,l_b, l_r1,l_r2,l_r3,l_r4; + float r_a,r_b, r_r1,r_r2,r_r3,r_r4; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r1 = (l_a * (1.0f - f)) + (l_b * f); + r_r1 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r2 = (l_a * (1.0f - f)) + (l_b * f); + r_r2 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r3 = (l_a * (1.0f - f)) + (l_b * f); + r_r3 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r4 = (l_a * (1.0f - f)) + (l_b * f); + r_r4 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + out[0] = l_r1; + out[1] = r_r1; + out[2] = l_r2; + out[3] = r_r2; + out[4] = l_r3; + out[5] = r_r3; + out[6] = l_r4; + out[7] = r_r4; + + len--; + out+=8; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r1 = (l_a * (1.0f - f)) + (l_b * f); + r_r1 = (r_a * (1.0f - f)) + (r_b * f); + + *out++ = l_r1; + *out++ = r_r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float p0 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + float p1 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + float r; + + r = (p0 * (1.0f - f)) + (p1 * f); + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 16 BIT + */ + case FMOD_SOUND_FORMAT_PCM16: + { + signed short *inptr = (signed short *)src; + + if (channels == 1) + { + if (supportssimd) + { + FMOD_Resampler_Linear_PCM16_Mono(out, outlength, src, position, speed); + } + else + { + float f; + float a, b, r1,r2,r3,r4; + int len; + + scale /= (float)(1<<15); + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r1 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r2 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r3 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r4 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r1 = (a * (1.0f - f)) + (b * f); + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + } + else if (channels == 2) + { + if (supportssimd) + { + FMOD_Resampler_Linear_PCM16_Stereo(out, outlength, src, position, speed); + } + else + { + float f; + float l_a,l_b, l_r1,l_r2,l_r3,l_r4; + float r_a,r_b, r_r1,r_r2,r_r3,r_r4; + int len; + + scale /= (float)(1<<15); + len = outlength >> 2; + + while (len) + { + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r1 = (l_a * (1.0f - f)) + (l_b * f); + r_r1 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r2 = (l_a * (1.0f - f)) + (l_b * f); + r_r2 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r3 = (l_a * (1.0f - f)) + (l_b * f); + r_r3 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r4 = (l_a * (1.0f - f)) + (l_b * f); + r_r4 = (r_a * (1.0f - f)) + (r_b * f); + position->mValue += speed->mValue; + + out[0] = l_r1; + out[1] = r_r1; + out[2] = l_r2; + out[3] = r_r2; + out[4] = l_r3; + out[5] = r_r3; + out[6] = l_r4; + out[7] = r_r4; + len--; + out+=8; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + l_a = (float)inptr[position->mHi * 2 + 0] * scale; + l_b = (float)inptr[position->mHi * 2 + 2] * scale; + r_a = (float)inptr[position->mHi * 2 + 1] * scale; + r_b = (float)inptr[position->mHi * 2 + 3] * scale; + l_r1 = (l_a * (1.0f - f)) + (l_b * f); + r_r1 = (r_a * (1.0f - f)) + (r_b * f); + + out[0] = l_r1; + out[1] = r_r1; + + position->mValue += speed->mValue; + len--; + out+=2; + } + } + } + else + { + scale /= (float)(1<<15); + + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float p0 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + float p1 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + float r; + + r = (p0 * (1.0f - f)) + (p1 * f); + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 24 BIT + */ + case FMOD_SOUND_FORMAT_PCM24: + { + FMOD_INT24 *inptr = (FMOD_INT24 *)src; + + scale /= (float)(1<<23); + + if (channels == 1) + { + while (outlength) + { + int count; + float r; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + FMOD_INT24 *s0 = &inptr[position->mHi + 0]; + FMOD_INT24 *s1 = &inptr[position->mHi + 1]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + float p1 = (float)((int)(((unsigned int)s1->val[0] << 8) | ((unsigned int)s1->val[1] << 16) | ((unsigned int)s1->val[2] << 24)) >> 8) * scale; + + r = (p0 * (1.0f - f)) + (p1 * f); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + else + { + while (outlength) + { + int count; + float r; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + FMOD_INT24 *s0 = &inptr[((position->mHi + 0) * channels) + count]; + FMOD_INT24 *s1 = &inptr[((position->mHi + 1) * channels) + count]; + float p0 = (float)((int)(((unsigned int)s0->val[0] << 8) | ((unsigned int)s0->val[1] << 16) | ((unsigned int)s0->val[2] << 24)) >> 8) * scale; + float p1 = (float)((int)(((unsigned int)s1->val[0] << 8) | ((unsigned int)s1->val[1] << 16) | ((unsigned int)s1->val[2] << 24)) >> 8) * scale; + + r = (p0 * (1.0f - f)) + (p1 * f); + + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + 32 BIT + */ + case FMOD_SOUND_FORMAT_PCM32: + { + signed int *inptr = (signed int *)src; + + scale /= (float)(1<<31); + + if (channels == 1) + { + float f; + float a, b, r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r1 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r2 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r3 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r4 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + a = (float)inptr[position->mHi + 0] * scale; + b = (float)inptr[position->mHi + 1] * scale; + r1 = (a * (1.0f - f)) + (b * f); + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float p0 = (float)inptr[((position->mHi + 0) * channels) + count] * scale; + float p1 = (float)inptr[((position->mHi + 1) * channels) + count] * scale; + float r; + + r = (p0 * (1.0f - f)) + (p1 * f); + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + + /* + FLOATING POINT. + */ + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + float *inptr = (float *)src; + + if (channels == 1) + { + float f; + float a, b, r1,r2,r3,r4; + int len; + + len = outlength >> 2; + while (len) + { + f = (float)position->mLo * OO4GIG; + a = inptr[position->mHi + 0]; + b = inptr[position->mHi + 1]; + r1 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = inptr[position->mHi + 0]; + b = inptr[position->mHi + 1]; + r2 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = inptr[position->mHi + 0]; + b = inptr[position->mHi + 1]; + r3 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + f = (float)position->mLo * OO4GIG; + a = inptr[position->mHi + 0]; + b = inptr[position->mHi + 1]; + r4 = (a * (1.0f - f)) + (b * f); + position->mValue += speed->mValue; + + out[0] = r1; + out[1] = r2; + out[2] = r3; + out[3] = r4; + len--; + out+=4; + } + + len = outlength & 3; + while (len) + { + f = position->mLo * OO4GIG; + a = inptr[position->mHi + 0]; + b = inptr[position->mHi + 1]; + r1 = (a * (1.0f - f)) + (b * f); + + *out++ = r1; + + position->mValue += speed->mValue; + len--; + } + } + else + { + while (outlength) + { + int count; + float f = position->mLo * OO4GIG; + + for (count = 0; count < channels; count++) + { + float p0 = inptr[((position->mHi + 0) * channels) + count]; + float p1 = inptr[((position->mHi + 1) * channels) + count]; + float r; + + r = (p0 * (1.0f - f)) + (p1 * f); + *out++ = r; + } + position->mValue += speed->mValue; + outlength--; + } + } + break; + } + default: + { + break; + } + }; +} + + diff --git a/win32/src/fmod_dsp_resampler_linear_asm.s b/win32/src/fmod_dsp_resampler_linear_asm.s new file mode 100755 index 0000000..4b0f5a5 --- /dev/null +++ b/win32/src/fmod_dsp_resampler_linear_asm.s @@ -0,0 +1,440 @@ +%include "./FMOD_static/win32/src/c32.mac" + +; ========================================================================================== +; GLOBAL UNINITIALIZED DATA +; ========================================================================================== + +section .bss +align 16 + +%define samples esp+0 +%define samples_2 esp+10h +%define samplesplus1 esp+20h +%define samplesplus1_2 esp+30h +%define fracs esp+40h +%define fracs_2 esp+50h + +; ========================================================================================== +; GLOBAL PREINITIALIZED DATA +; ========================================================================================== + +section .text +align 16 + +ones_over_32768 dd 0.000030517578125, 0.000030517578125, 0.000030517578125, 0.000030517578125 +ones_over_2gig dd 0.0000000004656612873077392578125, 0.0000000004656612873077392578125, 0.0000000004656612873077392578125, 0.0000000004656612873077392578125 +ones dd 1.0, 1.0, 1.0, 1.0 + +; ========================================================================================== +; CODE +; ========================================================================================== + +section .text + +; ================================================================================================================================= +; void FMOD_Resampler_Linear_PCM16_Mono(float *out, int outlength, void *src, FMOD_UINT64P *position, FMOD_SINT64P *speed); +; ================================================================================================================================= +proc FMOD_Resampler_Linear_PCM16_Mono + + %$out arg + %$outlength arg + %$src arg + %$position arg + %$speed arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov eax, [ebp + %$position] + mov eax, [eax] + + mov esi, [ebp + %$src] + shr esi, 1 + mov ebx, [ebp + %$position] + add esi, [ebx + 4] ; esi is now addr / 2 + offset. when accessing we use [esi*2] + + mov edx, [ebp + %$speed] + mov edx, [edx + 4] + mov ebx, [ebp + %$speed] + mov ebx, [ebx] + + mov edi, [ebp + %$out] + mov ecx, [ebp + %$outlength] + + ; eax = poslo + ; ebx = speedlo + ; ecx = count + ; edx = speedhi + ; esi = src + poshi + ; edi = dest + + ; xmm0 = [ ][ ][ ][ ] + ; xmm1 = [ ][ ][ ][ ] + ; xmm2 = [ ][ ][ ][ ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + push ebp + sub esp, 60h + + xorps xmm0, xmm0 + movups [samples], xmm0 + movups [samples_2], xmm0 + movups [samplesplus1], xmm0 + movups [samplesplus1_2], xmm0 + movups [fracs], xmm0 + movups [fracs_2], xmm0 + +%if 1 + shr ecx, 2 + test ecx, ecx + jz near resample16Mrolledstart + +resample16Munrolled: + + movsx ebp, word [esi*2] + mov [samples + 0], ebp ; [ 1a][ ][ ][ ] + movsx ebp, word [esi*2+2] + mov [samplesplus1 + 0], ebp ; [ 1b][ ][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs + 0], ebp ; [ 1f][ ][ ][ ] + add eax, ebx + adc esi, edx + + movsx ebp, word [esi*2] + mov [samples_2 + 0], ebp ; [ 2a][ ][ ][ ] + movsx ebp, word [esi*2+2] + mov [samplesplus1_2 + 0], ebp ; [ 2b][ ][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs_2 + 0], ebp ; [ 2f][ ][ ][ ] + add eax, ebx + adc esi, edx + + movsx ebp, word [esi*2] + mov [samples + 4], ebp ; [ 1a][ 3a][ ][ ] + movsx ebp, word [esi*2+2] + mov [samplesplus1 + 4], ebp ; [ 1b][ 3b][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs + 4], ebp ; [ 1f][ 3f][ ][ ] + add eax, ebx + adc esi, edx + + movsx ebp, word [esi*2] + mov [samples_2 + 4], ebp ; [ 2a][ 4a][ ][ ] + movsx ebp, word [esi*2+2] + mov [samplesplus1_2 + 4], ebp ; [ 2b][ 4b][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs_2 + 4], ebp ; [ 2f][ 4f][ ][ ] + add eax, ebx + adc esi, edx + + cvtpi2ps xmm0, [samples] + cvtpi2ps xmm1, [samples_2] + cvtpi2ps xmm2, [samplesplus1] + cvtpi2ps xmm3, [samplesplus1_2] + cvtpi2ps xmm4, [fracs] + cvtpi2ps xmm5, [fracs_2] + + unpcklps xmm0, xmm1 ; [ 1a][ 2a][ 3a][ 4a] + unpcklps xmm2, xmm3 ; [ 1b][ 2b][ 3b][ 4b] + unpcklps xmm4, xmm5 ; [ 1f][ 2f][ 3f][ 4f] + + mulps xmm0, [ones_over_32768] + mulps xmm2, [ones_over_32768] + mulps xmm4, [ones_over_2gig] + + movaps xmm6, [ones] + subps xmm6, xmm4 ; [ 1.0-1f][ 1.0-2f][ 1.0-3f][ 1.0-4f] + + mulps xmm2, xmm4 ; + mulps xmm0, xmm6 ; [1a * 1.0-1f][2a *1.0-1f][3a *1.0-3f][4a * 1.0-4f] + addps xmm0, xmm2 + movups [edi], xmm0 + + add edi, 16 + dec ecx + jnz near resample16Munrolled + +resample16Mrolledstart: + + add esp, 60h + pop ebp + mov ecx, [ebp+%$outlength] + push ebp + sub esp, 60h + and ecx, 3 +%endif + xorps xmm0, xmm0 + xorps xmm2, xmm2 + xorps xmm4, xmm4 + test ecx, ecx + jz near resample16Mdone + +resample16Mrolled: + + movsx ebp, word [esi*2] + mov [samples + 0], ebp ; [ 1a][ ][ ][ ] + movsx ebp, word [esi*2+2] + mov [samplesplus1 + 0], ebp ; [ 1b][ ][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs + 0], ebp ; [ 1f][ ][ ][ ] + add eax, ebx + adc esi, edx + + cvtsi2ss xmm0, [samples] + cvtsi2ss xmm2, [samplesplus1] + cvtsi2ss xmm4, [fracs] + + mulss xmm0, [ones_over_32768] + mulss xmm2, [ones_over_32768] + mulss xmm4, [ones_over_2gig] + + movss xmm6, [ones] + subss xmm6, xmm4 ; [ 1.0-1f][ 1.0-2f][ 1.0-3f][ 1.0-4f] + + mulss xmm2, xmm4 ; + mulss xmm0, xmm6 ; [1a * 1.0-1f][2a *1.0-1f][3a *1.0-3f][4a * 1.0-4f] + addss xmm0, xmm2 + movss [edi], xmm0 + + add edi, 4 + dec ecx + jnz near resample16Mrolled + +resample16Mdone: + add esp, 60h + pop ebp + + ; restore position + mov ecx, [ebp + %$position] + mov [ecx + 0], eax + + mov eax, [ebp + %$src] + shr eax, 1 + sub esi, eax + mov ecx, [ebp + %$position] + mov [ecx + 4], esi + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax +endproc + + +; ================================================================================================================================= +; void FMOD_Resampler_Linear_PCM16_Stereo(float *out, int outlength, void *src, FMOD_UINT64P *position, FMOD_SINT64P *speed); +; ================================================================================================================================= +proc FMOD_Resampler_Linear_PCM16_Stereo + + %$out arg + %$outlength arg + %$src arg + %$position arg + %$speed arg + + push eax + push ebx + push ecx + push edx + push esi + push edi + + mov eax, [ebp + %$position] + mov eax, [eax] + + mov esi, [ebp + %$src] + shr esi, 2 + mov ebx, [ebp + %$position] + add esi, [ebx + 4] ; esi is now addr / 2 + offset. when accessing we use [esi*2] + + mov edx, [ebp + %$speed] + mov edx, [edx + 4] + mov ebx, [ebp + %$speed] + mov ebx, [ebx] + + mov edi, [ebp + %$out] + mov ecx, [ebp + %$outlength] + + push ebp + sub esp, 60h + + ; eax = poslo + ; ebx = speedlo + ; ecx = count + ; edx = speedhi + ; esi = src + poshi + ; edi = dest + ; ebp = temp + + ; xmm0 = [ ][ ][ ][ ] + ; xmm1 = [ ][ ][ ][ ] + ; xmm2 = [ ][ ][ ][ ] + ; xmm3 = [ ][ ][ ][ ] + ; xmm4 = [ ][ ][ ][ ] + ; xmm5 = [ ][ ][ ][ ] + ; xmm6 = [ ][ ][ ][ ] + ; xmm7 = [ ][ ][ ][ ] + + xorps xmm0, xmm0 + movups [samples], xmm0 + movups [samples_2], xmm0 + movups [samplesplus1], xmm0 + movups [samplesplus1_2], xmm0 + movups [fracs], xmm0 + movups [fracs_2], xmm0 + +%if 1 + shr ecx, 1 + test ecx, ecx + jz near resample16Srolledstart + +resample16Sunrolled: + + movsx ebp, word [esi*4+0] + mov [samples + 0], ebp ; [ 1al][ ][ ][ ] + movsx ebp, word [esi*4+2] + mov [samples + 4], ebp ; [ 1al][ 1ar][ ][ ] + movsx ebp, word [esi*4+4] + mov [samplesplus1 + 0], ebp ; [ 1bl][ ][ ][ ] + movsx ebp, word [esi*4+6] + mov [samplesplus1 + 4], ebp ; [ 1bl][ 1br][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs + 0], ebp ; [ 1f][ ][ ][ ] + mov [fracs + 4], ebp ; [ 1f][ 1f][ ][ ] + add eax, ebx + adc esi, edx + + movsx ebp, word [esi*4+0] + mov [samples_2 + 0], ebp ; [ 2al][ ][ ][ ] + movsx ebp, word [esi*4+2] + mov [samples_2 + 4], ebp ; [ 2al][ 2ar][ ][ ] + movsx ebp, word [esi*4+4] + mov [samplesplus1_2 + 0], ebp ; [ 2bl][ ][ ][ ] + movsx ebp, word [esi*4+6] + mov [samplesplus1_2 + 4], ebp ; [ 2bl][ 2br][ ][ ] + mov ebp, eax + shr ebp, 1 + mov [fracs_2 + 0], ebp ; [ 2f][ ][ ][ ] + mov [fracs_2 + 4], ebp ; [ 2f][ 2f][ ][ ] + add eax, ebx + adc esi, edx + + cvtpi2ps xmm0, [samples] + cvtpi2ps xmm1, [samples_2] + cvtpi2ps xmm2, [samplesplus1] + cvtpi2ps xmm3, [samplesplus1_2] + cvtpi2ps xmm4, [fracs] + cvtpi2ps xmm5, [fracs_2] + + shufps xmm0, xmm1, 44h + shufps xmm2, xmm3, 44h + shufps xmm4, xmm5, 44h + + mulps xmm0, [ones_over_32768] + mulps xmm2, [ones_over_32768] + mulps xmm4, [ones_over_2gig] + + movaps xmm6, [ones] + subps xmm6, xmm4 ; [ 1.0-1f][ 1.0-1f][ 1.0-2f][ 1.0-2f] + + mulps xmm2, xmm4 ; + mulps xmm0, xmm6 ; [1al* 1.0-1f][1ar*1.0-1f][2al*1.0-2f][2ar* 1.0-2f] + addps xmm0, xmm2 + movups [edi], xmm0 + + add edi, 16 + dec ecx + jnz near resample16Sunrolled + +resample16Srolledstart: + + add esp, 60h + pop ebp + mov ecx, [ebp+%$outlength] + push ebp + sub esp, 60h + and ecx, 1 +%endif + xorps xmm0, xmm0 + xorps xmm2, xmm2 + xorps xmm4, xmm4 + test ecx, ecx + jz near resample16Sdone + +resample16Srolled: + + movsx ebp, word [esi*4] + mov [samples + 0], ebp ; [ 1al][ ][ ][ ] + movsx ebp, word [esi*4+2] + mov [samples + 4], ebp ; [ 1al][ 1ar][ ][ ] + + movsx ebp, word [esi*4+4] + mov [samplesplus1 + 0], ebp ; [ 1bl][ ][ ][ ] + movsx ebp, word [esi*4+6] + mov [samplesplus1 + 4], ebp ; [ 1bl][ 1br][ ][ ] + + mov ebp, eax + shr ebp, 1 + mov [fracs + 0], ebp ; [ 1f][ ][ ][ ] + mov [fracs + 4], ebp ; [ 1f][ 1f][ ][ ] + add eax, ebx + adc esi, edx + + cvtpi2ps xmm0, [samples] + cvtpi2ps xmm2, [samplesplus1] + cvtpi2ps xmm4, [fracs] + + mulps xmm0, [ones_over_32768] + mulps xmm2, [ones_over_32768] + mulps xmm4, [ones_over_2gig] + movups xmm6, [ones] + subps xmm6, xmm4 ; [ 1.0-1f][ 1.0-1f][ ][ ] + + mulps xmm2, xmm4 ; + mulps xmm0, xmm6 ; [1a * 1.0-1f][2a *1.0-1f][3a *1.0-3f][4a * 1.0-4f] + addps xmm0, xmm2 + movlps [edi], xmm0 + + add edi, 8 + dec ecx + jnz near resample16Srolled + +resample16Sdone: + + add esp, 60h + pop ebp + + ; restore position + mov ecx, [ebp + %$position] + mov [ecx + 0], eax + + mov eax, [ebp + %$src] + shr eax, 2 + sub esi, eax + mov ecx, [ebp + %$position] + mov [ecx + 4], esi + + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax +endproc + diff --git a/win32/src/fmod_dsp_winampplugin.cpp b/win32/src/fmod_dsp_winampplugin.cpp new file mode 100755 index 0000000..79d717f --- /dev/null +++ b/win32/src/fmod_dsp_winampplugin.cpp @@ -0,0 +1,487 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WINAMPPLUGIN + +#include "fmod.h" +#include "fmod_dsp_winampplugin.h" +#include "fmod_systemi.h" + +#define BUFFERSIZE 256 + +namespace FMOD +{ + +FMOD_DSP_DESCRIPTION_EX dspwinampplugin; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() + { + return DSPWinampPlugin::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_DSP_DESCRIPTION_EX *DSPWinampPlugin::getDescriptionEx() +{ + FMOD_memset(&dspwinampplugin, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + + // name and version is stamped in later. + dspwinampplugin.create = DSPWinampPlugin::createCallback; + dspwinampplugin.release = DSPWinampPlugin::releaseCallback; + dspwinampplugin.reset = DSPWinampPlugin::resetCallback; + dspwinampplugin.read = DSPWinampPlugin::readCallback; + + dspwinampplugin.setparameter = DSPWinampPlugin::setParameterCallback; + dspwinampplugin.getparameter = DSPWinampPlugin::getParameterCallback; + dspwinampplugin.config = DSPWinampPlugin::configCallback; + + dspwinampplugin.mType = FMOD_DSP_TYPE_WINAMPPLUGIN; + dspwinampplugin.mCategory = FMOD_DSP_CATEGORY_FILTER; + dspwinampplugin.mSize = sizeof(DSPWinampPlugin); + + return &dspwinampplugin; +} + + +short *DSPWinampPlugin::mEffectBuffer = NULL; +int DSPWinampPlugin::mEffectBufferCount = 0; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::createInternal() +{ + int result; + + if (mEffectBufferCount == 0) + { + mEffectBuffer = (short *)FMOD_Memory_Alloc(sizeof(short) * BUFFERSIZE); + if (!mEffectBuffer) + { + return FMOD_ERR_MEMORY; + } + } + + mEffectBufferCount++; + + result = mEffect->Init(mEffect); + + if (result) + { + return FMOD_ERR_PLUGIN; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::releaseInternal() +{ + mEffectBufferCount--; + + if (mEffectBufferCount == 0) + { + FMOD_Memory_Free(mEffectBuffer); + mEffectBuffer = NULL; + } + + mEffect->Quit(mEffect); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::resetInternal() +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + int count; + int rate; + int remaining = length * outchannels; + + mSystem->getSoftwareFormat(&rate, 0, 0, 0, 0, 0); + + while(remaining > 0) + { + int size = remaining; + + if (size > BUFFERSIZE) + { + size = BUFFERSIZE; + } + + /* + Convert from float to short + */ + for (count = 0; count < size; count++) + { + float val = *inbuffer++; + + mEffectBuffer[count] = (short)(val < -1.0f ? -32767 : val > 1.0f ? 32767 : val * 32767.0f); + + } + + mEffect->ModifySamples(mEffect, mEffectBuffer, size / outchannels, 16, outchannels, rate); + + /* + Convert back to floating point data + */ + for (count = 0; count < size; count++) + { + *outbuffer++ = (float)mEffectBuffer[count] / 32767.0f; + } + + remaining -= size; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::setParameterInternal(int index, float value) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::getParameterInternal(int index, float *value, char *valuestr) +{ + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT DSPWinampPlugin::showConfigDialogInternal(void *hwnd, int show) +{ + mEffect->hwndParent = hwnd; + + mEffect->Config(mEffect); + + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::createCallback(FMOD_DSP_STATE *dsp) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->createInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::releaseCallback(FMOD_DSP_STATE *dsp) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->releaseInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::resetCallback(FMOD_DSP_STATE *dsp) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->resetInternal(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->setParameterInternal(index, value); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->getParameterInternal(index, value, valuestr); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK DSPWinampPlugin::configCallback(FMOD_DSP_STATE *dsp, void *hwnd, int show) +{ + DSPWinampPlugin *winamp = (DSPWinampPlugin *)dsp; + + return winamp->showConfigDialogInternal(hwnd, show); +} +} + +#endif \ No newline at end of file diff --git a/win32/src/fmod_dsp_winampplugin.h b/win32/src/fmod_dsp_winampplugin.h new file mode 100755 index 0000000..3515f79 --- /dev/null +++ b/win32/src/fmod_dsp_winampplugin.h @@ -0,0 +1,49 @@ +#ifndef _FMOD_DSP_WINAMPPLUGIN_H +#define _FMOD_DSP_WINAMPPLUGIN_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WINAMPPLUGIN + +#include "fmod.h" +#include "fmod_dsp_filter.h" + +#include "../lib/winamp/dsp.h" + + +namespace FMOD +{ + class DSPWinampPlugin : public DSPFilter + { + private: + + static short *mEffectBuffer; + static int mEffectBufferCount; + + FMOD_RESULT createInternal(); + FMOD_RESULT releaseInternal(); + FMOD_RESULT resetInternal(); + FMOD_RESULT readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + FMOD_RESULT setParameterInternal(int index, float value); + FMOD_RESULT getParameterInternal(int index, float *value, char *valuestr); + FMOD_RESULT showConfigDialogInternal(void *hwnd, int show); + + public: + + winampDSPModule *mEffect; + + static FMOD_DSP_DESCRIPTION_EX *getDescriptionEx(); + + static FMOD_RESULT F_CALLBACK createCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK releaseCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK resetCallback(FMOD_DSP_STATE *dsp); + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + static FMOD_RESULT F_CALLBACK setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value); + static FMOD_RESULT F_CALLBACK getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr); + static FMOD_RESULT F_CALLBACK configCallback(FMOD_DSP_STATE *dsp, void *hwnd, int show); + }; +} + +#endif + +#endif diff --git a/win32/src/fmod_eax2.h b/win32/src/fmod_eax2.h new file mode 100755 index 0000000..e4b5b22 --- /dev/null +++ b/win32/src/fmod_eax2.h @@ -0,0 +1,461 @@ +/****************************************************************** +* +* EAX.H - DirectSound3D Environmental Audio Extensions version 2.0 +* Updated July 8, 1999 +* +******************************************************************* +*/ + +#ifndef EAX_H_INCLUDED +#define EAX_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#ifndef OPENAL +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif + #include <dsound.h> + /* + * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION == 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else + #include "../../lib/openal/include/al/al.h" + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // !GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID FAR name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + /* + * EAX OpenAL Extension {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + +/* +* EAX 2.0 listener property set {0306A6A8-B224-11d2-99E5-0000E8D8C722} +*/ +DEFINE_GUID(DSPROPSETID_EAX20_ListenerProperties, + 0x306a6a8, + 0xb224, + 0x11d2, + 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX20_ListenerProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX20_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times are in seconds +// - the reference for high frequency controls is 5 kHz +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + long lRoom; // room effect level at low frequencies + long lRoomHF; // room effect high-frequency level re. low frequency level + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flDecayTime; // reverberation decay time at low frequencies + float flDecayHFRatio; // high-frequency to low-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + unsigned long dwEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + float flAirAbsorptionHF; // change in level per meter at 5 kHz + unsigned long dwFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFFC0 // reserved future use + +// property ranges and defaults: + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 2.0 buffer property set {0306A6A7-B224-11d2-99E5-0000E8D8C722} +*/ +DEFINE_GUID(DSPROPSETID_EAX20_BufferProperties, + 0x306a6a7, + 0xb224, + 0x11d2, + 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX20_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level + long lDirectHF; // direct path level at high frequencies + long lRoom; // room effect level + long lRoomHF; // room effect level at high frequencies + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // occlusion room effect level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long dwFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 0.5f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO) + +// Material transmission presets +// 3 values in this order: +// 1: occlusion (or obstruction) +// 2: occlusion LF Ratio (or obstruction LF Ratio) +// 3: occlusion Room Ratio + +// Single window material preset +#define EAX_MATERIAL_SINGLEWINDOW (-2800) +#define EAX_MATERIAL_SINGLEWINDOWLF 0.71f +#define EAX_MATERIAL_SINGLEWINDOWROOMRATIO 0.43f + +// Double window material preset +#define EAX_MATERIAL_DOUBLEWINDOW (-5000) +#define EAX_MATERIAL_DOUBLEWINDOWHF 0.40f +#define EAX_MATERIAL_DOUBLEWINDOWROOMRATIO 0.24f + +// Thin door material preset +#define EAX_MATERIAL_THINDOOR (-1800) +#define EAX_MATERIAL_THINDOORLF 0.66f +#define EAX_MATERIAL_THINDOORROOMRATIO 0.66f + +// Thick door material preset +#define EAX_MATERIAL_THICKDOOR (-4400) +#define EAX_MATERIAL_THICKDOORLF 0.64f +#define EAX_MATERIAL_THICKDOORROOMRTATION 0.27f + +// Wood wall material preset +#define EAX_MATERIAL_WOODWALL (-4000) +#define EAX_MATERIAL_WOODWALLLF 0.50f +#define EAX_MATERIAL_WOODWALLROOMRATIO 0.30f + +// Brick wall material preset +#define EAX_MATERIAL_BRICKWALL (-5000) +#define EAX_MATERIAL_BRICKWALLLF 0.60f +#define EAX_MATERIAL_BRICKWALLROOMRATIO 0.24f + +// Stone wall material preset +#define EAX_MATERIAL_STONEWALL (-6000) +#define EAX_MATERIAL_STONEWALLLF 0.68f +#define EAX_MATERIAL_STONEWALLROOMRATIO 0.20f + +// Curtain material preset +#define EAX_MATERIAL_CURTAIN (-1200) +#define EAX_MATERIAL_CURTAINLF 0.15f +#define EAX_MATERIAL_CURTAINROOMRATIO 1.00f + + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/win32/src/fmod_eax3.h b/win32/src/fmod_eax3.h new file mode 100755 index 0000000..c076b21 --- /dev/null +++ b/win32/src/fmod_eax3.h @@ -0,0 +1,536 @@ +/*******************************************************************\ +* * +* EAX.H - Environmental Audio Extensions version 3.0 * +* for OpenAL and DirectSound3D * +* * +********************************************************************/ + +#ifndef EAX3_H_INCLUDED +#define EAX3_H_INCLUDED + +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif +#include <dsound.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#ifndef OPENAL + + /* + * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION == 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else // OPENAL + #include "../../lib/openal/include/al/al.h" + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // !GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID /*FAR*/ name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + + /* + * EAX OpenAL Extension + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_GUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_GUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/win32/src/fmod_eax4.h b/win32/src/fmod_eax4.h new file mode 100755 index 0000000..41561ec --- /dev/null +++ b/win32/src/fmod_eax4.h @@ -0,0 +1,1535 @@ +/*******************************************************************\ +* * +* EAX.H - Environmental Audio Extensions version 4.0 * +* for OpenAL and DirectSound3D * +* * +\*******************************************************************/ + +#ifndef EAX_H_INCLUDED +#define EAX_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#ifndef OPENAL +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif + #include <dsound.h> + + /* + * EAX Unified Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Unified Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION >= 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else // OPENAL + #include "../../lib/openal/include/al/al.h" + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID /*FAR*/ name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + /* + * EAX OpenAL Extensions + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + + + + +//////////////////////////////////////////////////////////////////////////// +// Constants + +#define EAX_MAX_FXSLOTS 4 +#define EAX_MAX_ACTIVE_FXSLOTS 2 + +// The EAX_NULL_GUID is used by EAXFXSLOT_LOADEFFECT, EAXCONTEXT_PRIMARYFXSLOTID +// and EAXSOURCE_ACTIVEFXSLOTID + +// {00000000-0000-0000-0000-000000000000} +DEFINE_GUID(EAX_NULL_GUID, + 0x00000000, + 0x0000, + 0x0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +// The EAX_PrimaryFXSlotID GUID is used by EAXSOURCE_ACTIVEFXSLOTID + +// {F317866D-924C-450C-861B-E6DAA25E7C20} +DEFINE_GUID(EAX_PrimaryFXSlotID, + 0xf317866d, + 0x924c, + 0x450c, + 0x86, 0x1b, 0xe6, 0xda, 0xa2, 0x5e, 0x7c, 0x20); + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Structures + +// Use this structure for EAXCONTEXT_ALL property. +typedef struct _EAXCONTEXTPROPERTIES +{ + GUID guidPrimaryFXSlotID; + float flDistanceFactor; + float flAirAbsorptionHF; + float flHFReference; +} EAXCONTEXTPROPERTIES, *LPEAXCONTEXTPROPERTIES; + +// Use this structure for EAXSOURCE_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +typedef struct _EAXSOURCEPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXSOURCEPROPERTIES, *LPEAXSOURCEPROPERTIES; + +// Use this structure for EAXSOURCE_ALLSENDPARAMETERS +// - all levels are hundredths of decibels +// +typedef struct _EAXSOURCEALLSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; // send level (at low and mid frequencies) + long lSendHF; // relative send level at high frequencies + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; + long lExclusion; + float flExclusionLFRatio; +} EAXSOURCEALLSENDPROPERTIES, *LPEAXSOURCEALLSENDPROPERTIES; + +// Use this structure for EAXSOURCE_ACTIVEFXSLOTID +typedef struct _EAXACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX_MAX_ACTIVE_FXSLOTS]; +} EAXACTIVEFXSLOTS, *LPEAXACTIVEFXSLOTS; + +// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for EAXSOURCE_OCCLUSIONPARAMETERS property. +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for EAXSOURCE_EXCLUSIONPARAMETERS property. +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Use this structure for EAXSOURCE_SENDPARAMETERS properties. +typedef struct _EAXSOURCESENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; + long lSendHF; +} EAXSOURCESENDPROPERTIES, *LPEAXSOURCESENDPROPERTIES; + +// Use this structure for EAXSOURCE_OCCLUSIONSENDPARAMETERS +typedef struct _EAXSOURCEOCCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXSOURCEOCCLUSIONSENDPROPERTIES, *LPEAXSOURCEOCCLUSIONSENDPROPERTIES; + +// Use this structure for EAXSOURCE_EXCLUSIONSENDPARAMETERS +typedef struct _EAXSOURCEEXCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lExclusion; + float flExclusionLFRatio; +} EAXSOURCEEXCLUSIONSENDPROPERTIES, *LPEAXSOURCEEXCLUSIONSENDPROPERTIES; + +// Use this structure for EAXFXSLOT_ALLPARAMETERS +// - all levels are hundredths of decibels +// +typedef struct _EAXFXSLOTPROPERTIES +{ + GUID guidLoadEffect; + long lVolume; + long lLock; + unsigned long ulFlags; +} EAXFXSLOTPROPERTIES, *LPEAXFXSLOTPROPERTIES; + +// Use this structure for EAXREVERB_REFLECTIONSPAN and EAXREVERB_REVERBPAN properties. +#ifndef EAXVECTOR_DEFINED +#define EAXVECTOR_DEFINED +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; +#endif + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Error Codes + +#define EAX_OK 0 +#define EAXERR_INVALID_OPERATION (-1) +#define EAXERR_INVALID_VALUE (-2) +#define EAXERR_NO_EFFECT_LOADED (-3) +#define EAXERR_UNKNOWN_EFFECT (-4) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Context Object + +// {1D4870AD-0DEF-43c0-A40C-523632296342} +DEFINE_GUID(EAXPROPERTYID_EAX40_Context, + 0x1d4870ad, + 0xdef, + 0x43c0, + 0xa4, 0xc, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42); + +// For compatibility with future EAX versions: +#define EAXPROPERTYID_EAX_Context EAXPROPERTYID_EAX40_Context + +typedef enum +{ + EAXCONTEXT_NONE = 0, + EAXCONTEXT_ALLPARAMETERS, + EAXCONTEXT_PRIMARYFXSLOTID, + EAXCONTEXT_DISTANCEFACTOR, + EAXCONTEXT_AIRABSORPTIONHF, + EAXCONTEXT_HFREFERENCE, + EAXCONTEXT_LASTERROR +} EAXCONTEXT_PROPERTY; + +// OR these flags with property id +#define EAXCONTEXT_PARAMETER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXCONTEXT_PARAMETER_DEFER 0x80000000 // changes take effect later +#define EAXCONTEXT_PARAMETER_COMMITDEFERREDSETTINGS (EAXCONTEXT_NONE | \ + EAXCONTEXT_PARAMETER_IMMEDIATE) + +// EAX Context property ranges and defaults: +#define EAXCONTEXT_DEFAULTPRIMARYFXSLOTID EAXPROPERTYID_EAX40_FXSlot0 + +#define EAXCONTEXT_MINDISTANCEFACTOR FLT_MIN //minimum positive value +#define EAXCONTEXT_MAXDISTANCEFACTOR FLT_MAX +#define EAXCONTEXT_DEFAULTDISTANCEFACTOR 1.0f + +#define EAXCONTEXT_MINAIRABSORPTIONHF (-100.0f) +#define EAXCONTEXT_MAXAIRABSORPTIONHF 0.0f +#define EAXCONTEXT_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXCONTEXT_MINHFREFERENCE 1000.0f +#define EAXCONTEXT_MAXHFREFERENCE 20000.0f +#define EAXCONTEXT_DEFAULTHFREFERENCE 5000.0f + +#define EAXCONTEXT_DEFAULTLASTERROR EAX_OK + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Effect Slot Objects + +// {C4D79F1E-F1AC-436b-A81D-A738E7045469} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot0, + 0xc4d79f1e, + 0xf1ac, + 0x436b, + 0xa8, 0x1d, 0xa7, 0x38, 0xe7, 0x4, 0x54, 0x69); + +// {08C00E96-74BE-4491-93AA-E8AD35A49117} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot1, + 0x8c00e96, + 0x74be, + 0x4491, + 0x93, 0xaa, 0xe8, 0xad, 0x35, 0xa4, 0x91, 0x17); + +// {1D433B88-F0F6-4637-919F-60E7E06B5EDD} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot2, + 0x1d433b88, + 0xf0f6, + 0x4637, + 0x91, 0x9f, 0x60, 0xe7, 0xe0, 0x6b, 0x5e, 0xdd); + +// {EFFF08EA-C7D8-44ab-93AD-6DBD5F910064} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot3, + 0xefff08ea, + 0xc7d8, + 0x44ab, + 0x93, 0xad, 0x6d, 0xbd, 0x5f, 0x91, 0x0, 0x64); + +// For compatibility with future EAX versions: +#define EAXPROPERTYID_EAX_FXSlot0 EAXPROPERTYID_EAX40_FXSlot0 +#define EAXPROPERTYID_EAX_FXSlot1 EAXPROPERTYID_EAX40_FXSlot1 +#define EAXPROPERTYID_EAX_FXSlot2 EAXPROPERTYID_EAX40_FXSlot2 +#define EAXPROPERTYID_EAX_FXSlot3 EAXPROPERTYID_EAX40_FXSlot3 + +// FXSlot object properties +typedef enum +{ + EAXFXSLOT_PARAMETER = 0, // range 0-0x40 reserved for loaded effect parameters + EAXFXSLOT_NONE = 0x10000, + EAXFXSLOT_ALLPARAMETERS, + EAXFXSLOT_LOADEFFECT, + EAXFXSLOT_VOLUME, + EAXFXSLOT_LOCK, + EAXFXSLOT_FLAGS +} EAXFXSLOT_PROPERTY; + +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXFXSLOTFLAGS_ENVIRONMENT; +// instead of: +// myFlags = 0x00000001; +// +#define EAXFXSLOTFLAGS_ENVIRONMENT 0x00000001 +#define EAXFXSLOTFLAGS_RESERVED 0xFFFFFFFE // reserved future use + +// EAX Effect Slot property ranges and defaults: +#define EAXFXSLOT_MINVOLUME (-10000) +#define EAXFXSLOT_MAXVOLUME 0 +#define EAXFXSLOT_DEFAULTVOLUME 0 + +#define EAXFXSLOT_MINLOCK 0 +#define EAXFXSLOT_MAXLOCK 1 + +enum +{ + EAXFXSLOT_UNLOCKED = 0, + EAXFXSLOT_LOCKED = 1 +}; + +#define EAXFXSLOT_DEFAULTFLAGS (EAXFXSLOTFLAGS_ENVIRONMENT) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Source Object + +// {1B86B823-22DF-4eae-8B3C-1278CE544227} +DEFINE_GUID(EAXPROPERTYID_EAX40_Source, + 0x1b86b823, + 0x22df, + 0x4eae, + 0x8b, 0x3c, 0x12, 0x78, 0xce, 0x54, 0x42, 0x27); + +// For compatibility with future EAX versions: +#define EAXPROPERTYID_EAX_Source EAXPROPERTYID_EAX40_Source + +// Source object properties +typedef enum +{ + EAXSOURCE_NONE, + EAXSOURCE_ALLPARAMETERS, + EAXSOURCE_OBSTRUCTIONPARAMETERS, + EAXSOURCE_OCCLUSIONPARAMETERS, + EAXSOURCE_EXCLUSIONPARAMETERS, + EAXSOURCE_DIRECT, + EAXSOURCE_DIRECTHF, + EAXSOURCE_ROOM, + EAXSOURCE_ROOMHF, + EAXSOURCE_OBSTRUCTION, + EAXSOURCE_OBSTRUCTIONLFRATIO, + EAXSOURCE_OCCLUSION, + EAXSOURCE_OCCLUSIONLFRATIO, + EAXSOURCE_OCCLUSIONROOMRATIO, + EAXSOURCE_OCCLUSIONDIRECTRATIO, + EAXSOURCE_EXCLUSION, + EAXSOURCE_EXCLUSIONLFRATIO, + EAXSOURCE_OUTSIDEVOLUMEHF, + EAXSOURCE_DOPPLERFACTOR, + EAXSOURCE_ROLLOFFFACTOR, + EAXSOURCE_ROOMROLLOFFFACTOR, + EAXSOURCE_AIRABSORPTIONFACTOR, + EAXSOURCE_FLAGS, + EAXSOURCE_SENDPARAMETERS, + EAXSOURCE_ALLSENDPARAMETERS, + EAXSOURCE_OCCLUSIONSENDPARAMETERS, + EAXSOURCE_EXCLUSIONSENDPARAMETERS, + EAXSOURCE_ACTIVEFXSLOTID, +} EAXSOURCE_PROPERTY; + +// OR these flags with property id +#define EAXSOURCE_PARAMETER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXSOURCE_PARAMETER_DEFERRED 0x80000000 // changes take effect later +#define EAXSOURCE_PARAMETER_COMMITDEFERREDSETTINGS (EAXSOURCE_NONE | \ + EAXSOURCE_PARAMETER_IMMEDIATE) +// Used by EAXSOURCE_FLAGS for EAXSOURCEFLAGS_xxxAUTO +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXSOURCE_DIRECTHFAUTO | EAXSOURCE_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXSOURCEFLAGS_DIRECTHFAUTO 0x00000001 // relates to EAXSOURCE_DIRECTHF +#define EAXSOURCEFLAGS_ROOMAUTO 0x00000002 // relates to EAXSOURCE_ROOM +#define EAXSOURCEFLAGS_ROOMHFAUTO 0x00000004 // relates to EAXSOURCE_ROOMHF +#define EAXSOURCEFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// EAX Source property ranges and defaults: +#define EAXSOURCE_MINSEND (-10000) +#define EAXSOURCE_MAXSEND 0 +#define EAXSOURCE_DEFAULTSEND 0 + +#define EAXSOURCE_MINSENDHF (-10000) +#define EAXSOURCE_MAXSENDHF 0 +#define EAXSOURCE_DEFAULTSENDHF 0 + +#define EAXSOURCE_MINDIRECT (-10000) +#define EAXSOURCE_MAXDIRECT 1000 +#define EAXSOURCE_DEFAULTDIRECT 0 + +#define EAXSOURCE_MINDIRECTHF (-10000) +#define EAXSOURCE_MAXDIRECTHF 0 +#define EAXSOURCE_DEFAULTDIRECTHF 0 + +#define EAXSOURCE_MINROOM (-10000) +#define EAXSOURCE_MAXROOM 1000 +#define EAXSOURCE_DEFAULTROOM 0 + +#define EAXSOURCE_MINROOMHF (-10000) +#define EAXSOURCE_MAXROOMHF 0 +#define EAXSOURCE_DEFAULTROOMHF 0 + +#define EAXSOURCE_MINOBSTRUCTION (-10000) +#define EAXSOURCE_MAXOBSTRUCTION 0 +#define EAXSOURCE_DEFAULTOBSTRUCTION 0 + +#define EAXSOURCE_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXSOURCE_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXSOURCE_MINOCCLUSION (-10000) +#define EAXSOURCE_MAXOCCLUSION 0 +#define EAXSOURCE_DEFAULTOCCLUSION 0 + +#define EAXSOURCE_MINOCCLUSIONLFRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXSOURCE_MINOCCLUSIONROOMRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXSOURCE_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXSOURCE_MINEXCLUSION (-10000) +#define EAXSOURCE_MAXEXCLUSION 0 +#define EAXSOURCE_DEFAULTEXCLUSION 0 + +#define EAXSOURCE_MINEXCLUSIONLFRATIO 0.0f +#define EAXSOURCE_MAXEXCLUSIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXSOURCE_MINOUTSIDEVOLUMEHF (-10000) +#define EAXSOURCE_MAXOUTSIDEVOLUMEHF 0 +#define EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXSOURCE_MINDOPPLERFACTOR 0.0f +#define EAXSOURCE_MAXDOPPLERFACTOR 10.f +#define EAXSOURCE_DEFAULTDOPPLERFACTOR 1.0f + +#define EAXSOURCE_MINROLLOFFFACTOR 0.0f +#define EAXSOURCE_MAXROLLOFFFACTOR 10.f +#define EAXSOURCE_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXSOURCE_MINROOMROLLOFFFACTOR 0.0f +#define EAXSOURCE_MAXROOMROLLOFFFACTOR 10.f +#define EAXSOURCE_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXSOURCE_MINAIRABSORPTIONFACTOR 0.0f +#define EAXSOURCE_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR 0.0f + +#define EAXSOURCE_DEFAULTFLAGS (EAXSOURCEFLAGS_DIRECTHFAUTO | \ + EAXSOURCEFLAGS_ROOMAUTO | \ + EAXSOURCEFLAGS_ROOMHFAUTO ) + +#define EAXSOURCE_DEFAULTACTIVEFXSLOTID {{ EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_PrimaryFXSlotID.Data1, EAX_PrimaryFXSlotID.Data2, \ + EAX_PrimaryFXSlotID.Data3, EAX_PrimaryFXSlotID.Data4[0],\ + EAX_PrimaryFXSlotID.Data4[1],EAX_PrimaryFXSlotID.Data4[2],\ + EAX_PrimaryFXSlotID.Data4[3],EAX_PrimaryFXSlotID.Data4[4],\ + EAX_PrimaryFXSlotID.Data4[5],EAX_PrimaryFXSlotID.Data4[6],\ + EAX_PrimaryFXSlotID.Data4[7] }} + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Reverb Effect + +// EAX REVERB {0CF95C8F-A3CC-4849-B0B6-832ECC1822DF} + +DEFINE_GUID(EAX_REVERB_EFFECT, + 0xcf95c8f, + 0xa3cc, + 0x4849, + 0xb0, 0xb6, 0x83, 0x2e, 0xcc, 0x18, 0x22, 0xdf); + +// Reverb effect properties +typedef enum +{ + EAXREVERB_NONE, + EAXREVERB_ALLPARAMETERS, + EAXREVERB_ENVIRONMENT, + EAXREVERB_ENVIRONMENTSIZE, + EAXREVERB_ENVIRONMENTDIFFUSION, + EAXREVERB_ROOM, + EAXREVERB_ROOMHF, + EAXREVERB_ROOMLF, + EAXREVERB_DECAYTIME, + EAXREVERB_DECAYHFRATIO, + EAXREVERB_DECAYLFRATIO, + EAXREVERB_REFLECTIONS, + EAXREVERB_REFLECTIONSDELAY, + EAXREVERB_REFLECTIONSPAN, + EAXREVERB_REVERB, + EAXREVERB_REVERBDELAY, + EAXREVERB_REVERBPAN, + EAXREVERB_ECHOTIME, + EAXREVERB_ECHODEPTH, + EAXREVERB_MODULATIONTIME, + EAXREVERB_MODULATIONDEPTH, + EAXREVERB_AIRABSORPTIONHF, + EAXREVERB_HFREFERENCE, + EAXREVERB_LFREFERENCE, + EAXREVERB_ROOMROLLOFFFACTOR, + EAXREVERB_FLAGS, +} EAXREVERB_PROPERTY; + +// OR these flags with property id +#define EAXREVERB_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXREVERB_DEFERRED 0x80000000 // changes take effect later +#define EAXREVERB_COMMITDEFERREDSETTINGS (EAXREVERB_NONE | \ + EAXREVERB_IMMEDIATE) + +// used by EAXREVERB_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by EAXREVERB_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXREVERBFLAGS_DECAYTIMESCALE | EAXREVERBFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXREVERBFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXREVERBFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXREVERBFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXREVERBFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXREVERBFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXREVERBFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXREVERBFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time +// This flag limits high-frequency decay time according to air absorption. +#define EAXREVERBFLAGS_DECAYHFLIMIT 0x00000020 +#define EAXREVERBFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Use this structure for EAXREVERB_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +typedef struct _EAXREVERBPROPERTIES +{ + unsigned long ulEnvironment; // sets all reverb properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXREVERBPROPERTIES, *LPEAXREVERBPROPERTIES; + +// Property ranges and defaults: +#define EAXREVERB_MINENVIRONMENT 0 +#define EAXREVERB_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXREVERB_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXREVERB_MINENVIRONMENTSIZE 1.0f +#define EAXREVERB_MAXENVIRONMENTSIZE 100.0f +#define EAXREVERB_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXREVERB_MINENVIRONMENTDIFFUSION 0.0f +#define EAXREVERB_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXREVERB_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXREVERB_MINROOM (-10000) +#define EAXREVERB_MAXROOM 0 +#define EAXREVERB_DEFAULTROOM (-1000) + +#define EAXREVERB_MINROOMHF (-10000) +#define EAXREVERB_MAXROOMHF 0 +#define EAXREVERB_DEFAULTROOMHF (-100) + +#define EAXREVERB_MINROOMLF (-10000) +#define EAXREVERB_MAXROOMLF 0 +#define EAXREVERB_DEFAULTROOMLF 0 + +#define EAXREVERB_MINDECAYTIME 0.1f +#define EAXREVERB_MAXDECAYTIME 20.0f +#define EAXREVERB_DEFAULTDECAYTIME 1.49f + +#define EAXREVERB_MINDECAYHFRATIO 0.1f +#define EAXREVERB_MAXDECAYHFRATIO 2.0f +#define EAXREVERB_DEFAULTDECAYHFRATIO 0.83f + +#define EAXREVERB_MINDECAYLFRATIO 0.1f +#define EAXREVERB_MAXDECAYLFRATIO 2.0f +#define EAXREVERB_DEFAULTDECAYLFRATIO 1.00f + +#define EAXREVERB_MINREFLECTIONS (-10000) +#define EAXREVERB_MAXREFLECTIONS 1000 +#define EAXREVERB_DEFAULTREFLECTIONS (-2602) + +#define EAXREVERB_MINREFLECTIONSDELAY 0.0f +#define EAXREVERB_MAXREFLECTIONSDELAY 0.3f +#define EAXREVERB_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXREVERB_DEFAULTREFLECTIONSPAN {0.0f, 0.0f, 0.0f} + +#define EAXREVERB_MINREVERB (-10000) +#define EAXREVERB_MAXREVERB 2000 +#define EAXREVERB_DEFAULTREVERB 200 + +#define EAXREVERB_MINREVERBDELAY 0.0f +#define EAXREVERB_MAXREVERBDELAY 0.1f +#define EAXREVERB_DEFAULTREVERBDELAY 0.011f + +#define EAXREVERB_DEFAULTREVERBPAN {0.0f, 0.0f, 0.0f} + +#define EAXREVERB_MINECHOTIME 0.075f +#define EAXREVERB_MAXECHOTIME 0.25f +#define EAXREVERB_DEFAULTECHOTIME 0.25f + +#define EAXREVERB_MINECHODEPTH 0.0f +#define EAXREVERB_MAXECHODEPTH 1.0f +#define EAXREVERB_DEFAULTECHODEPTH 0.0f + +#define EAXREVERB_MINMODULATIONTIME 0.04f +#define EAXREVERB_MAXMODULATIONTIME 4.0f +#define EAXREVERB_DEFAULTMODULATIONTIME 0.25f + +#define EAXREVERB_MINMODULATIONDEPTH 0.0f +#define EAXREVERB_MAXMODULATIONDEPTH 1.0f +#define EAXREVERB_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXREVERB_MINAIRABSORPTIONHF (-100.0f) +#define EAXREVERB_MAXAIRABSORPTIONHF 0.0f +#define EAXREVERB_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXREVERB_MINHFREFERENCE 1000.0f +#define EAXREVERB_MAXHFREFERENCE 20000.0f +#define EAXREVERB_DEFAULTHFREFERENCE 5000.0f + +#define EAXREVERB_MINLFREFERENCE 20.0f +#define EAXREVERB_MAXLFREFERENCE 1000.0f +#define EAXREVERB_DEFAULTLFREFERENCE 250.0f + +#define EAXREVERB_MINROOMROLLOFFFACTOR 0.0f +#define EAXREVERB_MAXROOMROLLOFFFACTOR 10.0f +#define EAXREVERB_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXREVERB_DEFAULTFLAGS (EAXREVERBFLAGS_DECAYTIMESCALE | \ + EAXREVERBFLAGS_REFLECTIONSSCALE | \ + EAXREVERBFLAGS_REFLECTIONSDELAYSCALE | \ + EAXREVERBFLAGS_REVERBSCALE | \ + EAXREVERBFLAGS_REVERBDELAYSCALE | \ + EAXREVERBFLAGS_DECAYHFLIMIT) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// + +// New Effect Types + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// AGC Compressor Effect + +// EAX AGC COMPRESSOR {BFB7A01E-7825-4039-927F-3AABDA0C560} + +DEFINE_GUID(EAX_AGCCOMPRESSOR_EFFECT, + 0xbfb7a01e, + 0x7825, + 0x4039, + 0x92, 0x7f, 0x3, 0xaa, 0xbd, 0xa0, 0xc5, 0x60); + +// AGC Compressor properties +typedef enum +{ + EAXAGCCOMPRESSOR_NONE, + EAXAGCCOMPRESSOR_ALLPARAMETERS, + EAXAGCCOMPRESSOR_ONOFF +} EAXAGCCOMPRESSOR_PROPERTY; + +// OR these flags with property id +#define EAXAGCCOMPRESSOR_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXAGCCOMPRESSOR_DEFERRED 0x80000000 // changes take effect later +#define EAXAGCCOMPRESSOR_COMMITDEFERREDSETTINGS (EAXAGCCOMPRESSOR_NONE | \ + EAXAGCCOMPRESSOR_IMMEDIATE) + +// Use this structure for EAXAGCCOMPRESSOR_ALLPARAMETERS +typedef struct _EAXAGCCOMPRESSORPROPERTIES +{ + unsigned long ulOnOff; // Switch Compressor on or off +} EAXAGCCOMPRESSORPROPERTIES, *LPEAXAGCCOMPRESSORPROPERTIES; + +// Property ranges and defaults: + +#define EAXAGCCOMPRESSOR_MINONOFF 0 +#define EAXAGCCOMPRESSOR_MAXONOFF 1 +#define EAXAGCCOMPRESSOR_DEFAULTONOFF 1 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Autowah Effect + +// EAX AUTOWAH {EC3130C0-AC7A-11D2-88DD-A024D13CE1} +DEFINE_GUID(EAX_AUTOWAH_EFFECT, + 0xec3130c0, + 0xac7a, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Autowah properties +typedef enum +{ + EAXAUTOWAH_NONE, + EAXAUTOWAH_ALLPARAMETERS, + EAXAUTOWAH_ATTACKTIME, + EAXAUTOWAH_RELEASETIME, + EAXAUTOWAH_RESONANCE, + EAXAUTOWAH_PEAKLEVEL +} EAXAUTOWAH_PROPERTY; + +// OR these flags with property id +#define EAXAUTOWAH_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXAUTOWAH_DEFERRED 0x80000000 // changes take effect later +#define EAXAUTOWAH_COMMITDEFERREDSETTINGS (EAXAUTOWAH_NONE | \ + EAXAUTOWAH_IMMEDIATE) + +// Use this structure for EAXAUTOWAH_ALLPARAMETERS +typedef struct _EAXAUTOWAHPROPERTIES +{ + float flAttackTime; // Attack time (seconds) + float flReleaseTime; // Release time (seconds) + long lResonance; // Resonance (mB) + long lPeakLevel; // Peak level (mB) +} EAXAUTOWAHPROPERTIES, *LPEAXAUTOWAHPROPERTIES; + +// Property ranges and defaults: + +#define EAXAUTOWAH_MINATTACKTIME 0.0001f +#define EAXAUTOWAH_MAXATTACKTIME 1.0f +#define EAXAUTOWAH_DEFAULTATTACKTIME 0.06f + +#define EAXAUTOWAH_MINRELEASETIME 0.0001f +#define EAXAUTOWAH_MAXRELEASETIME 1.0f +#define EAXAUTOWAH_DEFAULTRELEASETIME 0.06f + +#define EAXAUTOWAH_MINRESONANCE 600 +#define EAXAUTOWAH_MAXRESONANCE 6000 +#define EAXAUTOWAH_DEFAULTRESONANCE 6000 + +#define EAXAUTOWAH_MINPEAKLEVEL (-9000) +#define EAXAUTOWAH_MAXPEAKLEVEL 9000 +#define EAXAUTOWAH_DEFAULTPEAKLEVEL 2100 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Chorus Effect + +// EAX CHORUS {DE6D6FE0-AC79-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_CHORUS_EFFECT, + 0xde6d6fe0, + 0xac79, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + + +// Chorus properties +typedef enum +{ + EAXCHORUS_NONE, + EAXCHORUS_ALLPARAMETERS, + EAXCHORUS_WAVEFORM, + EAXCHORUS_PHASE, + EAXCHORUS_RATE, + EAXCHORUS_DEPTH, + EAXCHORUS_FEEDBACK, + EAXCHORUS_DELAY +} EAXCHORUS_PROPERTY; + +// OR these flags with property id +#define EAXCHORUS_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXCHORUS_DEFERRED 0x80000000 // changes take effect later +#define EAXCHORUS_COMMITDEFERREDSETTINGS (EAXCHORUS_NONE | \ + EAXCHORUS_IMMEDIATE) + +// used by EAXCHORUS_WAVEFORM +enum +{ + EAX_CHORUS_SINUSOID, + EAX_CHORUS_TRIANGLE +}; + +// Use this structure for EAXCHORUS_ALLPARAMETERS +typedef struct _EAXCHORUSPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (-1 to 1) + float flDelay; // Delay (seconds) +} EAXCHORUSPROPERTIES, *LPEAXCHORUSPROPERTIES; + +// Property ranges and defaults: + +#define EAXCHORUS_MINWAVEFORM 0 +#define EAXCHORUS_MAXWAVEFORM 1 +#define EAXCHORUS_DEFAULTWAVEFORM 1 + +#define EAXCHORUS_MINPHASE (-180) +#define EAXCHORUS_MAXPHASE 180 +#define EAXCHORUS_DEFAULTPHASE 90 + +#define EAXCHORUS_MINRATE 0.0f +#define EAXCHORUS_MAXRATE 10.0f +#define EAXCHORUS_DEFAULTRATE 1.1f + +#define EAXCHORUS_MINDEPTH 0.0f +#define EAXCHORUS_MAXDEPTH 1.0f +#define EAXCHORUS_DEFAULTDEPTH 0.1f + +#define EAXCHORUS_MINFEEDBACK (-1.0f) +#define EAXCHORUS_MAXFEEDBACK 1.0f +#define EAXCHORUS_DEFAULTFEEDBACK 0.25f + +#define EAXCHORUS_MINDELAY 0.0002f +#define EAXCHORUS_MAXDELAY 0.016f +#define EAXCHORUS_DEFAULTDELAY 0.016f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Distortion Effect + +// EAX DISTORTION {975A4CE0-AC7E-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_DISTORTION_EFFECT, + 0x975a4ce0, + 0xac7e, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Distortion properties +typedef enum +{ + EAXDISTORTION_NONE, + EAXDISTORTION_ALLPARAMETERS, + EAXDISTORTION_EDGE, + EAXDISTORTION_GAIN, + EAXDISTORTION_LOWPASSCUTOFF, + EAXDISTORTION_EQCENTER, + EAXDISTORTION_EQBANDWIDTH +} EAXDISTORTION_PROPERTY; + +// OR these flags with property id +#define EAXDISTORTION_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXDISTORTION_DEFERRED 0x80000000 // changes take effect later +#define EAXDISTORTION_COMMITDEFERREDSETTINGS (EAXDISTORTION_NONE | \ + EAXDISTORTION_IMMEDIATE) + +// Use this structure for EAXDISTORTION_ALLPARAMETERS +typedef struct _EAXDISTORTIONPROPERTIES +{ + float flEdge; // Controls the shape of the distortion (0 to 1) + long lGain; // Controls the post distortion gain (mB) + float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) + float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) + float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) +} EAXDISTORTIONPROPERTIES, *LPEAXDISTORTIONPROPERTIES; + +// Property ranges and defaults: + +#define EAXDISTORTION_MINEDGE 0.0f +#define EAXDISTORTION_MAXEDGE 1.0f +#define EAXDISTORTION_DEFAULTEDGE 0.2f + +#define EAXDISTORTION_MINGAIN (-6000) +#define EAXDISTORTION_MAXGAIN 0 +#define EAXDISTORTION_DEFAULTGAIN (-2600) + +#define EAXDISTORTION_MINLOWPASSCUTOFF 80.0f +#define EAXDISTORTION_MAXLOWPASSCUTOFF 24000.0f +#define EAXDISTORTION_DEFAULTLOWPASSCUTOFF 8000.0f + +#define EAXDISTORTION_MINEQCENTER 80.0f +#define EAXDISTORTION_MAXEQCENTER 24000.0f +#define EAXDISTORTION_DEFAULTEQCENTER 3600.0f + +#define EAXDISTORTION_MINEQBANDWIDTH 80.0f +#define EAXDISTORTION_MAXEQBANDWIDTH 24000.0f +#define EAXDISTORTION_DEFAULTEQBANDWIDTH 3600.0f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Echo Effect + +// EAX ECHO {E9F1BC0-AC82-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_ECHO_EFFECT, + 0xe9f1bc0, + 0xac82, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Echo properties +typedef enum +{ + EAXECHO_NONE, + EAXECHO_ALLPARAMETERS, + EAXECHO_DELAY, + EAXECHO_LRDELAY, + EAXECHO_DAMPING, + EAXECHO_FEEDBACK, + EAXECHO_SPREAD +} EAXECHO_PROPERTY; + +// OR these flags with property id +#define EAXECHO_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXECHO_DEFERRED 0x80000000 // changes take effect later +#define EAXECHO_COMMITDEFERREDSETTINGS (EAXECHO_NONE | \ + EAXECHO_IMMEDIATE) + +// Use this structure for EAXECHO_ALLPARAMETERS +typedef struct _EAXECHOPROPERTIES +{ + float flDelay; // Controls the initial delay time (seconds) + float flLRDelay; // Controls the delay time between the first and second taps (seconds) + float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) + float flFeedback; // Controls the duration of echo repetition (0 to 1) + float flSpread; // Controls the left-right spread of the echoes +} EAXECHOPROPERTIES, *LPEAXECHOPROPERTIES; + +// Property ranges and defaults: + +#define EAXECHO_MINDAMPING 0.0f +#define EAXECHO_MAXDAMPING 0.99f +#define EAXECHO_DEFAULTDAMPING 0.5f + +#define EAXECHO_MINDELAY 0.002f +#define EAXECHO_MAXDELAY 0.207f +#define EAXECHO_DEFAULTDELAY 0.1f + +#define EAXECHO_MINLRDELAY 0.0f +#define EAXECHO_MAXLRDELAY 0.404f +#define EAXECHO_DEFAULTLRDELAY 0.1f + +#define EAXECHO_MINFEEDBACK 0.0f +#define EAXECHO_MAXFEEDBACK 1.0f +#define EAXECHO_DEFAULTFEEDBACK 0.5f + +#define EAXECHO_MINSPREAD (-1.0f) +#define EAXECHO_MAXSPREAD 1.0f +#define EAXECHO_DEFAULTSPREAD (-1.0f) + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Equalizer Effect + +// EAX EQUALIZER {65F94CE0-9793-11D3-939D-C0F02DD6F0} + +DEFINE_GUID(EAX_EQUALIZER_EFFECT, + 0x65f94ce0, + 0x9793, + 0x11d3, + 0x93, 0x9d, 0x0, 0xc0, 0xf0, 0x2d, 0xd6, 0xf0); + + +// Equalizer properties +typedef enum +{ + EAXEQUALIZER_NONE, + EAXEQUALIZER_ALLPARAMETERS, + EAXEQUALIZER_LOWGAIN, + EAXEQUALIZER_LOWCUTOFF, + EAXEQUALIZER_MID1GAIN, + EAXEQUALIZER_MID1CENTER, + EAXEQUALIZER_MID1WIDTH, + EAXEQUALIZER_MID2GAIN, + EAXEQUALIZER_MID2CENTER, + EAXEQUALIZER_MID2WIDTH, + EAXEQUALIZER_HIGHGAIN, + EAXEQUALIZER_HIGHCUTOFF +} EAXEQUALIZER_PROPERTY; + +// OR these flags with property id +#define EAXEQUALIZER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXEQUALIZER_DEFERRED 0x80000000 // changes take effect later +#define EAXEQUALIZER_COMMITDEFERREDSETTINGS (EAXEQUALIZER_NONE | \ + EAXEQUALIZER_IMMEDIATE) + +// Use this structure for EAXEQUALIZER_ALLPARAMETERS +typedef struct _EAXEQUALIZERPROPERTIES +{ + long lLowGain; // (mB) + float flLowCutOff; // (Hz) + long lMid1Gain; // (mB) + float flMid1Center; // (Hz) + float flMid1Width; // (octaves) + long lMid2Gain; // (mB) + float flMid2Center; // (Hz) + float flMid2Width; // (octaves) + long lHighGain; // (mB) + float flHighCutOff; // (Hz) +} EAXEQUALIZERPROPERTIES, *LPEAXEQUALIZERPROPERTIES; + +// Property ranges and defaults: + +#define EAXEQUALIZER_MINLOWGAIN (-1800) +#define EAXEQUALIZER_MAXLOWGAIN 1800 +#define EAXEQUALIZER_DEFAULTLOWGAIN 0 + +#define EAXEQUALIZER_MINLOWCUTOFF 50.0f +#define EAXEQUALIZER_MAXLOWCUTOFF 800.0f +#define EAXEQUALIZER_DEFAULTLOWCUTOFF 200.0f + +#define EAXEQUALIZER_MINMID1GAIN (-1800) +#define EAXEQUALIZER_MAXMID1GAIN 1800 +#define EAXEQUALIZER_DEFAULTMID1GAIN 0 + +#define EAXEQUALIZER_MINMID1CENTER 200.0f +#define EAXEQUALIZER_MAXMID1CENTER 3000.0f +#define EAXEQUALIZER_DEFAULTMID1CENTER 500.0f + +#define EAXEQUALIZER_MINMID1WIDTH 0.01f +#define EAXEQUALIZER_MAXMID1WIDTH 1.0f +#define EAXEQUALIZER_DEFAULTMID1WIDTH 1.0f + +#define EAXEQUALIZER_MINMID2GAIN (-1800) +#define EAXEQUALIZER_MAXMID2GAIN 1800 +#define EAXEQUALIZER_DEFAULTMID2GAIN 0 + +#define EAXEQUALIZER_MINMID2CENTER 1000.0f +#define EAXEQUALIZER_MAXMID2CENTER 8000.0f +#define EAXEQUALIZER_DEFAULTMID2CENTER 3000.0f + +#define EAXEQUALIZER_MINMID2WIDTH 0.01f +#define EAXEQUALIZER_MAXMID2WIDTH 1.0f +#define EAXEQUALIZER_DEFAULTMID2WIDTH 1.0f + +#define EAXEQUALIZER_MINHIGHGAIN (-1800) +#define EAXEQUALIZER_MAXHIGHGAIN 1800 +#define EAXEQUALIZER_DEFAULTHIGHGAIN 0 + +#define EAXEQUALIZER_MINHIGHCUTOFF 4000.0f +#define EAXEQUALIZER_MAXHIGHCUTOFF 16000.0f +#define EAXEQUALIZER_DEFAULTHIGHCUTOFF 6000.0f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Flanger Effect + +// EAX FLANGER {A70007C0-7D2-11D3-9B1E-A024D13CE1} + +DEFINE_GUID(EAX_FLANGER_EFFECT, + 0xa70007c0, + 0x7d2, + 0x11d3, + 0x9b, 0x1e, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Flanger properties +typedef enum +{ + EAXFLANGER_NONE, + EAXFLANGER_ALLPARAMETERS, + EAXFLANGER_WAVEFORM, + EAXFLANGER_PHASE, + EAXFLANGER_RATE, + EAXFLANGER_DEPTH, + EAXFLANGER_FEEDBACK, + EAXFLANGER_DELAY +} EAXFLANGER_PROPERTY; + +// OR these flags with property id +#define EAXFLANGER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXFLANGER_DEFERRED 0x80000000 // changes take effect later +#define EAXFLANGER_COMMITDEFERREDSETTINGS (EAXFLANGER_NONE | \ + EAXFLANGER_IMMEDIATE) + +// used by EAXFLANGER_WAVEFORM +enum +{ + EAX_FLANGER_SINUSOID, + EAX_FLANGER_TRIANGLE +}; + +// Use this structure for EAXFLANGER_ALLPARAMETERS +typedef struct _EAXFLANGERPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (0 to 1) + float flDelay; // Delay (seconds) +} EAXFLANGERPROPERTIES, *LPEAXFLANGERPROPERTIES; + +// Property ranges and defaults: + +#define EAXFLANGER_MINWAVEFORM 0 +#define EAXFLANGER_MAXWAVEFORM 1 +#define EAXFLANGER_DEFAULTWAVEFORM 1 + +#define EAXFLANGER_MINPHASE (-180) +#define EAXFLANGER_MAXPHASE 180 +#define EAXFLANGER_DEFAULTPHASE 0 + +#define EAXFLANGER_MINRATE 0.0f +#define EAXFLANGER_MAXRATE 10.0f +#define EAXFLANGER_DEFAULTRATE 0.27f + +#define EAXFLANGER_MINDEPTH 0.0f +#define EAXFLANGER_MAXDEPTH 1.0f +#define EAXFLANGER_DEFAULTDEPTH 1.0f + +#define EAXFLANGER_MINFEEDBACK (-1.0f) +#define EAXFLANGER_MAXFEEDBACK 1.0f +#define EAXFLANGER_DEFAULTFEEDBACK (-0.5f) + +#define EAXFLANGER_MINDELAY 0.0002f +#define EAXFLANGER_MAXDELAY 0.004f +#define EAXFLANGER_DEFAULTDELAY 0.002f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Frequency Shifter Effect + +// EAX FREQUENCY SHIFTER {DC3E1880-9212-11D3-939D-C0F02DD6F0} + +DEFINE_GUID(EAX_FREQUENCYSHIFTER_EFFECT, + 0xdc3e1880, + 0x9212, + 0x11d3, + 0x93, 0x9d, 0x0, 0xc0, 0xf0, 0x2d, 0xd6, 0xf0); + +// Frequency Shifter properties +typedef enum +{ + EAXFREQUENCYSHIFTER_NONE, + EAXFREQUENCYSHIFTER_ALLPARAMETERS, + EAXFREQUENCYSHIFTER_FREQUENCY, + EAXFREQUENCYSHIFTER_LEFTDIRECTION, + EAXFREQUENCYSHIFTER_RIGHTDIRECTION +} EAXFREQUENCYSHIFTER_PROPERTY; + +// OR these flags with property id +#define EAXFREQUENCYSHIFTER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXFREQUENCYSHIFTER_DEFERRED 0x80000000 // changes take effect later +#define EAXFREQUENCYSHIFTER_COMMITDEFERREDSETTINGS (EAXFREQUENCYSHIFTER_NONE | \ + EAXFREQUENCYSHIFTER_IMMEDIATE) + +// used by EAXFREQUENCYSHIFTER_LEFTDIRECTION and EAXFREQUENCYSHIFTER_RIGHTDIRECTION +enum +{ + EAX_FREQUENCYSHIFTER_DOWN, + EAX_FREQUENCYSHIFTER_UP, + EAX_FREQUENCYSHIFTER_OFF +}; + +// Use this structure for EAXFREQUENCYSHIFTER_ALLPARAMETERS +typedef struct _EAXFREQUENCYSHIFTERPROPERTIES +{ + float flFrequency; // (Hz) + unsigned long ulLeftDirection; // see enum above + unsigned long ulRightDirection; // see enum above +} EAXFREQUENCYSHIFTERPROPERTIES, *LPEAXFREQUENCYSHIFTERPROPERTIES; + +// Property ranges and defaults: + +#define EAXFREQUENCYSHIFTER_MINFREQUENCY 0.0f +#define EAXFREQUENCYSHIFTER_MAXFREQUENCY 24000.0f +#define EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY 0.0f + +#define EAXFREQUENCYSHIFTER_MINLEFTDIRECTION 0 +#define EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION 2 +#define EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION 0 + +#define EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION 0 +#define EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION 2 +#define EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION 0 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Vocal Morpher Effect + +// EAX VOCAL MORPHER {E41CF10C-3383-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_VOCALMORPHER_EFFECT, + 0xe41cf10c, + 0x3383, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Vocal Morpher properties +typedef enum +{ + EAXVOCALMORPHER_NONE, + EAXVOCALMORPHER_ALLPARAMETERS, + EAXVOCALMORPHER_PHONEMEA, + EAXVOCALMORPHER_PHONEMEACOARSETUNING, + EAXVOCALMORPHER_PHONEMEB, + EAXVOCALMORPHER_PHONEMEBCOARSETUNING, + EAXVOCALMORPHER_WAVEFORM, + EAXVOCALMORPHER_RATE +} EAXVOCALMORPHER_PROPERTY; + +// OR these flags with property id +#define EAXVOCALMORPHER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXVOCALMORPHER_DEFERRED 0x80000000 // changes take effect later +#define EAXVOCALMORPHER_COMMITDEFERREDSETTINGS (EAXVOCALMORPHER_NONE | \ + EAXVOCALMORPHER_IMMEDIATE) + +// used by EAXVOCALMORPHER_PHONEMEA and EAXVOCALMORPHER_PHONEMEB +enum +{ + A, E, I, O, U, AA, AE, AH, AO, EH, ER, IH, IY, UH, UW, B, D, F, G, + J, K, L, M, N, P, R, S, T, V, Z +}; + +// used by EAXVOCALMORPHER_WAVEFORM +enum +{ + EAX_VOCALMORPHER_SINUSOID, + EAX_VOCALMORPHER_TRIANGLE, + EAX_VOCALMORPHER_SAWTOOTH +}; + +// Use this structure for EAXVOCALMORPHER_ALLPARAMETERS +typedef struct _EAXVOCALMORPHERPROPERTIES +{ + unsigned long ulPhonemeA; // see enum above + long lPhonemeACoarseTuning; // (semitones) + unsigned long ulPhonemeB; // see enum above + long lPhonemeBCoarseTuning; // (semitones) + unsigned long ulWaveform; // Waveform selector - see enum above + float flRate; // (Hz) +} EAXVOCALMORPHERPROPERTIES, *LPEAXVOCALMORPHERPROPERTIES; + +// Property ranges and defaults: + +#define EAXVOCALMORPHER_MINPHONEMEA 0 +#define EAXVOCALMORPHER_MAXPHONEMEA 29 +#define EAXVOCALMORPHER_DEFAULTPHONEMEA 0 + +#define EAXVOCALMORPHER_MINPHONEMEACOARSETUNING (-24) +#define EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING 24 +#define EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING 0 + +#define EAXVOCALMORPHER_MINPHONEMEB 0 +#define EAXVOCALMORPHER_MAXPHONEMEB 29 +#define EAXVOCALMORPHER_DEFAULTPHONEMEB 10 + +#define EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING (-24) +#define EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING 24 +#define EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING 0 + +#define EAXVOCALMORPHER_MINWAVEFORM 0 +#define EAXVOCALMORPHER_MAXWAVEFORM 2 +#define EAXVOCALMORPHER_DEFAULTWAVEFORM 0 + +#define EAXVOCALMORPHER_MINRATE 0.0f +#define EAXVOCALMORPHER_MAXRATE 10.0f +#define EAXVOCALMORPHER_DEFAULTRATE 1.41f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Pitch Shifter Effect + +// EAX PITCH SHIFTER {E7905100-AFB2-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_PITCHSHIFTER_EFFECT, + 0xe7905100, + 0xafb2, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Pitch Shifter properties +typedef enum +{ + EAXPITCHSHIFTER_NONE, + EAXPITCHSHIFTER_ALLPARAMETERS, + EAXPITCHSHIFTER_COARSETUNE, + EAXPITCHSHIFTER_FINETUNE +} EAXPITCHSHIFTER_PROPERTY; + +// OR these flags with property id +#define EAXPITCHSHIFTER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXPITCHSHIFTER_DEFERRED 0x80000000 // changes take effect later +#define EAXPITCHSHIFTER_COMMITDEFERREDSETTINGS (EAXPITCHSHIFTER_NONE | \ + EAXPITCHSHIFTER_IMMEDIATE) + +// Use this structure for EAXPITCHSHIFTER_ALLPARAMETERS +typedef struct _EAXPITCHSHIFTERPROPERTIES +{ + long lCoarseTune; // Amount of pitch shift (semitones) + long lFineTune; // Amount of pitch shift (cents) +} EAXPITCHSHIFTERPROPERTIES, *LPEAXPITCHSHIFTERPROPERTIES; + +// Property ranges and defaults: + +#define EAXPITCHSHIFTER_MINCOARSETUNE (-12) +#define EAXPITCHSHIFTER_MAXCOARSETUNE 12 +#define EAXPITCHSHIFTER_DEFAULTCOARSETUNE 12 + +#define EAXPITCHSHIFTER_MINFINETUNE (-50) +#define EAXPITCHSHIFTER_MAXFINETUNE 50 +#define EAXPITCHSHIFTER_DEFAULTFINETUNE 0 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Ring Modulator Effect + +// EAX RING MODULATOR {B89FE60-AFB5-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_RINGMODULATOR_EFFECT, + 0xb89fe60, + 0xafb5, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Ring Modulator properties +typedef enum +{ + EAXRINGMODULATOR_NONE, + EAXRINGMODULATOR_ALLPARAMETERS, + EAXRINGMODULATOR_FREQUENCY, + EAXRINGMODULATOR_HIGHPASSCUTOFF, + EAXRINGMODULATOR_WAVEFORM +} EAXRINGMODULATOR_PROPERTY; + +// OR these flags with property id +#define EAXRINGMODULATOR_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXRINGMODULATOR_DEFERRED 0x80000000 // changes take effect later +#define EAXRINGMODULATOR_COMMITDEFERREDSETTINGS (EAXRINGMODULATOR_NONE | \ + EAXRINGMODULATOR_IMMEDIATE) + +// used by EAXRINGMODULATOR_WAVEFORM +enum +{ + EAX_RINGMODULATOR_SINUSOID, + EAX_RINGMODULATOR_SAWTOOTH, + EAX_RINGMODULATOR_SQUARE +}; + +// Use this structure for EAXRINGMODULATOR_ALLPARAMETERS +typedef struct _EAXRINGMODULATORPROPERTIES +{ + float flFrequency; // Frequency of modulation (Hz) + float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) + unsigned long ulWaveform; // Waveform selector - see enum above +} EAXRINGMODULATORPROPERTIES, *LPEAXRINGMODULATORPROPERTIES; + +// Property ranges and defaults: + +#define EAXRINGMODULATOR_MINFREQUENCY 0.0f +#define EAXRINGMODULATOR_MAXFREQUENCY 8000.0f +#define EAXRINGMODULATOR_DEFAULTFREQUENCY 440.0f + +#define EAXRINGMODULATOR_MINHIGHPASSCUTOFF 0.0f +#define EAXRINGMODULATOR_MAXHIGHPASSCUTOFF 24000.0f +#define EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF 800.0f + +#define EAXRINGMODULATOR_MINWAVEFORM 0 +#define EAXRINGMODULATOR_MAXWAVEFORM 2 +#define EAXRINGMODULATOR_DEFAULTWAVEFORM 0 + +//////////////////////////////////////////////////////////////////////////// + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/win32/src/fmod_eax5.h b/win32/src/fmod_eax5.h new file mode 100755 index 0000000..08da598 --- /dev/null +++ b/win32/src/fmod_eax5.h @@ -0,0 +1,2067 @@ +/*******************************************************************\ +* * +* EAX.H - Environmental Audio Extensions version 5.0 * +* for OpenAL and DirectSound3D * +* * +* File revision 1.0 (EAX 5.0 SDK Release) * +* * +\*******************************************************************/ + + +//////////////////////////////////////////////////////////////////////////// +// +// If an application wishes to support both EAX 4.0 and EAX 5.0 +// it can use the following pre-processor definition to include +// all the EAX 4.0 GUIDs and Structures :- +// _INCLUDE_EAX_40_DEFINITIONS +// All EAX 4.0 GUIDS and Structures will include '40' in the name +// e.g EAX40CONTEXTPROPERTIES for the EAX 4.0 Context Properties Structure +// EAXPROPERTYID_EAX40_Context for the EAX 4.0 Context Object GUID +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef EAX_H_INCLUDED +#define EAX_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#ifndef OPENAL +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif + #include <dsound.h> + + /* + * EAX Unified Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Unified Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION >= 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else // OPENAL + #include "../../lib/openal/include/al/al.h" + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID /*FAR*/ name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + /* + * EAX OpenAL Extensions + */ + typedef ALenum (__cdecl *EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (__cdecl *EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + + + + +//////////////////////////////////////////////////////////////////////////// +// Constants + +#define EAX50_MAX_FXSLOTS 4 +#define EAX50_MAX_ACTIVE_FXSLOTS 4 + +#define EAX_MAX_FXSLOTS EAX50_MAX_FXSLOTS +#define EAX_MAX_ACTIVE_FXSLOTS EAX50_MAX_ACTIVE_FXSLOTS + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40_MAX_FXSLOTS 4 +#define EAX40_MAX_ACTIVE_FXSLOTS 2 +#endif + +// The EAX_NULL_GUID is used by EAXFXSLOT_LOADEFFECT, EAXCONTEXT_PRIMARYFXSLOTID +// and EAXSOURCE_ACTIVEFXSLOTID + +// {00000000-0000-0000-0000-000000000000} +DEFINE_GUID(EAX_NULL_GUID, + 0x00000000, + 0x0000, + 0x0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +// The EAX_PrimaryFXSlotID GUID is used by EAXSOURCE_ACTIVEFXSLOTID +// {F317866D-924C-450C-861B-E6DAA25E7C20} +DEFINE_GUID(EAX_PrimaryFXSlotID, + 0xf317866d, + 0x924c, + 0x450c, + 0x86, 0x1b, 0xe6, 0xda, 0xa2, 0x5e, 0x7c, 0x20); + + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Structures + +///////////////////////////////////////////////////////// +// Use this structure for EAXCONTEXT_EAXSESSION property +#ifndef EAX50SESSIONPROPERTIES_DEFINED +#define EAX50SESSIONPROPERTIES_DEFINED +typedef struct _EAX50SESSIONPROPERTIES +{ + unsigned long ulEAXVersion; + unsigned long ulMaxActiveSends; +} EAX50SESSIONPROPERTIES, *LPEAX50SESSIONPROPERTIES; +#endif + +#define EAXSESSIONPROPERTIES EAX50SESSIONPROPERTIES +#define LPEAXSESSIONPROPERTIES LPEAX50SESSIONPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +// No equivalent in EAX 4.0 +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXCONTEXT_ALL property. +#ifndef EAX50CONTEXTPROPERTIES_DEFINED +#define EAX50CONTEXTPROPERTIES_DEFINED +typedef struct _EAX50CONTEXTPROPERTIES +{ + GUID guidPrimaryFXSlotID; + float flDistanceFactor; + float flAirAbsorptionHF; + float flHFReference; + float flMacroFXFactor; +} EAX50CONTEXTPROPERTIES, *LPEAX50CONTEXTPROPERTIES; +#endif + +#define EAXCONTEXTPROPERTIES EAX50CONTEXTPROPERTIES +#define LPEAXCONTEXTPROPERTIES LPEAX50CONTEXTPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#ifndef EAX40CONTEXTPROPERTIES_DEFINED +#define EAX40CONTEXTPROPERTIES_DEFINED +typedef struct _EAX40CONTEXTPROPERTIES +{ + GUID guidPrimaryFXSlotID; + float flDistanceFactor; + float flAirAbsorptionHF; + float flHFReference; +} EAX40CONTEXTPROPERTIES, *LPEAX40CONTEXTPROPERTIES; +#endif +#endif + + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXSOURCEFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +#ifndef EAX50SOURCEPROPERTIES_DEFINED +#define EAX50SOURCEPROPERTIES_DEFINED +typedef struct _EAX50SOURCEPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties + float flMacroFXFactor; // controls the near-field effect +} EAX50SOURCEPROPERTIES, *LPEAX50SOURCEPROPERTIES; +#endif + +#define EAXSOURCEPROPERTIES EAX50SOURCEPROPERTIES +#define LPEAXSOURCEPROPERTIES LPEAX50SOURCEPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#ifndef EAX40SOURCEPROPERTIES_DEFINED +#define EAX40SOURCEPROPERTIES_DEFINED +typedef struct _EAX40SOURCEPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies EAXREVERB_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAX40SOURCEPROPERTIES, *LPEAX40SOURCEPROPERTIES; +#endif +#endif + + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_ALL2DPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXSOURCEFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +#ifndef EAX50SOURCE2DPROPERTIES_DEFINED +#define EAX50SOURCE2DPROPERTIES_DEFINED +typedef struct _EAX50SOURCE2DPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + unsigned long ulFlags; // modifies the behavior of properties +} EAX50SOURCE2DPROPERTIES, *LPEAX50SOURCE2DPROPERTIES; +#endif + +#define EAXSOURCE2DPROPERTIES EAX50SOURCE2DPROPERTIES +#define LPEAXSOURCE2DPROPERTIES LPEAX50SOURCE2DPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +// No EAX 4.0 Equivalent +#endif + + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_ALLSENDPARAMETERS +// - all levels are hundredths of decibels +// +#ifndef EAX50SOURCEALLSENDPROPERTIES_DEFINED +#define EAX50SOURCEALLSENDPROPERTIES_DEFINED +typedef struct _EAX50SOURCEALLSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; // send level (at low and mid frequencies) + long lSendHF; // relative send level at high frequencies + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; + long lExclusion; + float flExclusionLFRatio; +} EAX50SOURCEALLSENDPROPERTIES, *LPEAX50SOURCEALLSENDPROPERTIES; +#endif + +#define EAXSOURCEALLSENDPROPERTIES EAX50SOURCEALLSENDPROPERTIES +#define LPEAXSOURCEALLSENDPROPERTIES LPEAX50SOURCEALLSENDPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCEALLSENDPROPERTIES EAXSOURCEALLSENDPROPERTIES +#define LPEAX40SOURCEALLSENDPROPERTIES LPEAXSOURCEALLSENDPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_SPEAKERLEVELS +// - level is in hundredths of decibels +// +#ifndef EAX50SPEAKERLEVELPROPERTIES_DEFINED +#define EAX50SPEAKERLEVELPROPERTIES_DEFINED +typedef struct _EAX50SPEAKERLEVELPROPERTIES +{ + long lSpeakerID; + long lLevel; +} EAX50SPEAKERLEVELPROPERTIES, *LPEAX50SPEAKERLEVELPROPERTIES; +#endif + +#define EAXSPEAKERLEVELPROPERTIES EAX50SPEAKERLEVELPROPERTIES +#define LPEAXSPEAKERLEVELPROPERTIES LPEAX50SPEAKERLEVELPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +// No EAX 4.0 Equivalent +#endif + + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_ACTIVEFXSLOTID +// +#ifndef EAX50ACTIVEFXSLOTS_DEFINED +#define EAX50ACTIVEFXSLOTS_DEFINED +typedef struct _EAX50ACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX_MAX_ACTIVE_FXSLOTS]; +} EAX50ACTIVEFXSLOTS, *LPEAX50ACTIVEFXSLOTS; +#endif + +#define EAXACTIVEFXSLOTS EAX50ACTIVEFXSLOTS +#define LPEAXACTIVEFXSLOTS LPEAX50ACTIVEFXSLOTS + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#ifndef EAX40ACTIVEFXSLOTS_DEFINED +#define EAX40ACTIVEFXSLOTS_DEFINED +typedef struct _EAX40ACTIVEFXSLOTS +{ + GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; +} EAX40ACTIVEFXSLOTS, *LPEAX40ACTIVEFXSLOTS; +#endif +#endif + + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. +// +#ifndef EAX50OBSTRUCTIONPROPERTIES_DEFINED +#define EAX50OBSTRUCTIONPROPERTIES_DEFINED +typedef struct _EAX50OBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAX50OBSTRUCTIONPROPERTIES, *LPEAX50OBSTRUCTIONPROPERTIES; +#endif + +#define EAXOBSTRUCTIONPROPERTIES EAX50OBSTRUCTIONPROPERTIES +#define LPEAXOBSTRUCTIONPROPERTIES LPEAX50OBSTRUCTIONPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40OBSTRUCTIONPROPERTIES EAXOBSTRUCTIONPROPERTIES +#define LPEAX40OBSTRUCTIONPROPERTIES LPEAXOBSTRUCTIONPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_OCCLUSIONPARAMETERS property. +// +#ifndef EAX50OCCLUSIONPROPERTIES_DEFINED +#define EAX50OCCLUSIONPROPERTIES_DEFINED +typedef struct _EAX50OCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAX50OCCLUSIONPROPERTIES, *LPEAX50OCCLUSIONPROPERTIES; +#endif + +#define EAXOCCLUSIONPROPERTIES EAX50OCCLUSIONPROPERTIES +#define LPEAXOCCLUSIONPROPERTIES LPEAX50OCCLUSIONPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40OCCLUSIONPROPERTIES EAXOCCLUSIONPROPERTIES +#define LPEAX40OCCLUSIONPROPERTIES LPEAXOCCLUSIONPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_EXCLUSIONPARAMETERS property. +// +#ifndef EAX50EXCLUSIONPROPERTIES_DEFINED +#define EAX50EXCLUSIONPROPERTIES_DEFINED +typedef struct _EAX50EXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAX50EXCLUSIONPROPERTIES, *LPEAX50EXCLUSIONPROPERTIES; +#endif + +#define EAXEXCLUSIONPROPERTIES EAX50EXCLUSIONPROPERTIES +#define LPEAXEXCLUSIONPROPERTIES LPEAX50EXCLUSIONPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40EXCLUSIONPROPERTIES EAXEXCLUSIONPROPERTIES +#define LPEAX40EXCLUSIONPROPERTIES LPEAXEXCLUSIONPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_SENDPARAMETERS properties. +// +#ifndef EAX50SOURCESENDPROPERTIES_DEFINED +#define EAX50SOURCESENDPROPERTIES_DEFINED +typedef struct _EAX50SOURCESENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lSend; + long lSendHF; +} EAX50SOURCESENDPROPERTIES, *LPEAX50SOURCESENDPROPERTIES; +#endif + +#define EAXSOURCESENDPROPERTIES EAX50SOURCESENDPROPERTIES +#define LPEAXSOURCESENDPROPERTIES LPEAX50SOURCESENDPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCESENDPROPERTIES EAXSOURCESENDPROPERTIES +#define LPEAX40SOURCESENDPROPERTIES LPEAXSOURCESENDPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_OCCLUSIONSENDPARAMETERS +// +#ifndef EAX50SOURCEOCCLUSIONSENDPROPERTIES_DEFINED +#define EAX50SOURCEOCCLUSIONSENDPROPERTIES_DEFINED +typedef struct _EAX50SOURCEOCCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAX50SOURCEOCCLUSIONSENDPROPERTIES, *LPEAX50SOURCEOCCLUSIONSENDPROPERTIES; +#endif + +#define EAXSOURCEOCCLUSIONSENDPROPERTIES EAX50SOURCEOCCLUSIONSENDPROPERTIES +#define LPEAXSOURCEOCCLUSIONSENDPROPERTIES LPEAX50SOURCEOCCLUSIONSENDPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCEOCCLUSIONSENDPROPERTIES EAXSOURCEOCCLUSIONSENDPROPERTIES +#define LPEAX40SOURCEOCCLUSIONSENDPROPERTIES LPEAXSOURCEOCCLUSIONSENDPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXSOURCE_EXCLUSIONSENDPARAMETERS +// +#ifndef EAX50SOURCEEXCLUSIONSENDPROPERTIES_DEFINED +#define EAX50SOURCEEXCLUSIONSENDPROPERTIES_DEFINED +typedef struct _EAX50SOURCEEXCLUSIONSENDPROPERTIES +{ + GUID guidReceivingFXSlotID; + long lExclusion; + float flExclusionLFRatio; +} EAX50SOURCEEXCLUSIONSENDPROPERTIES, *LPEAX50SOURCEEXCLUSIONSENDPROPERTIES; +#endif + +#define EAXSOURCEEXCLUSIONSENDPROPERTIES EAX50SOURCEEXCLUSIONSENDPROPERTIES +#define LPEAXSOURCEEXCLUSIONSENDPROPERTIES LPEAX50SOURCEEXCLUSIONSENDPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCEEXCLUSIONSENDPROPERTIES EAXSOURCEEXCLUSIONSENDPROPERTIES +#define LPEAX40SOURCEEXCLUSIONSENDPROPERTIES LPEAXSOURCEEXCLUSIONSENDPROPERTIES +#endif + +///////////////////////////////////////////////////////// +// Use this structure for EAXFXSLOT_ALLPARAMETERS +// - all levels are hundredths of decibels +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myFXSlot.guidLoadEffect = EAX_REVERB_EFFECT; +// myFXSlot.lVolume = 0; +// myFXSlot.lLock = 1; +// myFXSlot.ulFlags = myFlags /* see EAXFXSLOTFLAGS below */ ; +// instead of: +// myFXSlot = { EAX_REVERB_EFFECT, 0, 1, 0x00000001 }; +// +#ifndef EAX50FXSLOTPROPERTIES_DEFINED +#define EAX50FXSLOTPROPERTIES_DEFINED +typedef struct _EAX50FXSLOTPROPERTIES +{ + GUID guidLoadEffect; + long lVolume; + long lLock; + unsigned long ulFlags; + long lOcclusion; + float flOcclusionLFRatio; +} EAX50FXSLOTPROPERTIES, *LPEAX50FXSLOTPROPERTIES; +#endif + +#define EAXFXSLOTPROPERTIES EAX50FXSLOTPROPERTIES +#define LPEAXFXSLOTPROPERTIES LPEAX50FXSLOTPROPERTIES + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#ifndef EAX40FXSLOTPROPERTIES_DEFINED +#define EAX40FXSLOTPROPERTIES_DEFINED +typedef struct _EAX40FXSLOTPROPERTIES +{ + GUID guidLoadEffect; + long lVolume; + long lLock; + unsigned long ulFlags; +} EAX40FXSLOTPROPERTIES, *LPEAX40FXSLOTPROPERTIES; +#endif +#endif + + +///////////////////////////////////////////////////////// +// Use this structure for EAXREVERB_REFLECTIONSPAN and EAXREVERB_REVERBPAN properties. +// +#ifndef EAXVECTOR_DEFINED +#define EAXVECTOR_DEFINED +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; +#endif + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Error Codes + +#define EAX_OK 0 +#define EAXERR_INVALID_OPERATION (-1) +#define EAXERR_INVALID_VALUE (-2) +#define EAXERR_NO_EFFECT_LOADED (-3) +#define EAXERR_UNKNOWN_EFFECT (-4) +#define EAXERR_INCOMPATIBLE_SOURCE_TYPE (-5) +#define EAXERR_INCOMPATIBLE_EAX_VERSION (-6) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Context Object + +// {57E13437-B932-4ab2-B8BD-5266C1A887EE} +DEFINE_GUID(EAXPROPERTYID_EAX50_Context, + 0x57e13437, + 0xb932, + 0x4ab2, + 0xb8, 0xbd, 0x52, 0x66, 0xc1, 0xa8, 0x87, 0xee); + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +// {1D4870AD-0DEF-43c0-A40C-523632296342} +DEFINE_GUID(EAXPROPERTYID_EAX40_Context, + 0x1d4870ad, + 0xdef, + 0x43c0, + 0xa4, 0xc, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42); +#endif + +#define EAXPROPERTYID_EAX_Context EAXPROPERTYID_EAX50_Context + +typedef enum +{ + EAXCONTEXT_NONE = 0, + EAXCONTEXT_ALLPARAMETERS, + EAXCONTEXT_PRIMARYFXSLOTID, + EAXCONTEXT_DISTANCEFACTOR, + EAXCONTEXT_AIRABSORPTIONHF, + EAXCONTEXT_HFREFERENCE, + EAXCONTEXT_LASTERROR, + EAXCONTEXT_SPEAKERCONFIG, + EAXCONTEXT_EAXSESSION, + EAXCONTEXT_MACROFXFACTOR +} EAXCONTEXT_PROPERTY; + +// OR these flags with property id +#define EAXCONTEXT_PARAMETER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXCONTEXT_PARAMETER_DEFER 0x80000000 // changes take effect later +#define EAXCONTEXT_PARAMETER_COMMITDEFERREDSETTINGS (EAXCONTEXT_NONE | \ + EAXCONTEXT_PARAMETER_IMMEDIATE) + +// EAX Context property ranges and defaults: +#define EAXCONTEXT_DEFAULTPRIMARYFXSLOTID EAXPROPERTYID_EAX_FXSlot0 + +#define EAXCONTEXT_MINDISTANCEFACTOR FLT_MIN //minimum positive value +#define EAXCONTEXT_MAXDISTANCEFACTOR FLT_MAX +#define EAXCONTEXT_DEFAULTDISTANCEFACTOR 1.0f + +#define EAXCONTEXT_MINAIRABSORPTIONHF (-100.0f) +#define EAXCONTEXT_MAXAIRABSORPTIONHF 0.0f +#define EAXCONTEXT_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXCONTEXT_MINHFREFERENCE 1000.0f +#define EAXCONTEXT_MAXHFREFERENCE 20000.0f +#define EAXCONTEXT_DEFAULTHFREFERENCE 5000.0f + +#define EAXCONTEXT_DEFAULTLASTERROR EAX_OK + +enum { + HEADPHONES = 0, + SPEAKERS_2, + SPEAKERS_4, + SPEAKERS_5, // 5.1 speakers + SPEAKERS_6, // 6.1 speakers + SPEAKERS_7, // 7.1 speakers +}; + +enum { + EAX_40 = 5, // EAX 4.0 + EAX_50 = 6, // EAX 5.0 +}; + +// min,max, default values for ulEAXVersion in struct EAXSESSIONPROPERTIES +#define EAXCONTEXT_MINEAXSESSION EAX_40 +#define EAXCONTEXT_MAXEAXSESSION EAX_50 +#define EAXCONTEXT_DEFAULTEAXSESSION EAX_40 + +// min,max, default values for ulMaxActiveSends in struct EAXSESSIONPROPERTIES +#define EAXCONTEXT_MINMAXACTIVESENDS 2 +#define EAXCONTEXT_MAXMAXACTIVESENDS 4 +#define EAXCONTEXT_DEFAULTMAXACTIVESENDS 2 + +#define EAXCONTEXT_MINMACROFXFACTOR 0.0f +#define EAXCONTEXT_MAXMACROFXFACTOR 1.0f +#define EAXCONTEXT_DEFAULTMACROFXFACTOR 0.0f + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Effect Slot Objects +// + +// {91F9590F-C388-407a-84B0-1BAE0EF71ABC} +DEFINE_GUID(EAXPROPERTYID_EAX50_FXSlot0, + 0x91f9590f, + 0xc388, + 0x407a, + 0x84, 0xb0, 0x1b, 0xae, 0xe, 0xf7, 0x1a, 0xbc); +// {8F5F7ACA-9608-4965-8137-8213C7B9D9DE} +DEFINE_GUID(EAXPROPERTYID_EAX50_FXSlot1, + 0x8f5f7aca, + 0x9608, + 0x4965, + 0x81, 0x37, 0x82, 0x13, 0xc7, 0xb9, 0xd9, 0xde); +// {3C0F5252-9834-46f0-A1D8-5B95C4A00A30} +DEFINE_GUID(EAXPROPERTYID_EAX50_FXSlot2, + 0x3c0f5252, + 0x9834, + 0x46f0, + 0xa1, 0xd8, 0x5b, 0x95, 0xc4, 0xa0, 0xa, 0x30); +// {E2EB0EAA-E806-45e7-9F86-06C1571A6FA3} +DEFINE_GUID(EAXPROPERTYID_EAX50_FXSlot3, + 0xe2eb0eaa, + 0xe806, + 0x45e7, + 0x9f, 0x86, 0x6, 0xc1, 0x57, 0x1a, 0x6f, 0xa3); + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +// {C4D79F1E-F1AC-436b-A81D-A738E7045469} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot0, + 0xc4d79f1e, + 0xf1ac, + 0x436b, + 0xa8, 0x1d, 0xa7, 0x38, 0xe7, 0x4, 0x54, 0x69); +// {08C00E96-74BE-4491-93AA-E8AD35A49117} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot1, + 0x8c00e96, + 0x74be, + 0x4491, + 0x93, 0xaa, 0xe8, 0xad, 0x35, 0xa4, 0x91, 0x17); +// {1D433B88-F0F6-4637-919F-60E7E06B5EDD} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot2, + 0x1d433b88, + 0xf0f6, + 0x4637, + 0x91, 0x9f, 0x60, 0xe7, 0xe0, 0x6b, 0x5e, 0xdd); +// {EFFF08EA-C7D8-44ab-93AD-6DBD5F910064} +DEFINE_GUID(EAXPROPERTYID_EAX40_FXSlot3, + 0xefff08ea, + 0xc7d8, + 0x44ab, + 0x93, 0xad, 0x6d, 0xbd, 0x5f, 0x91, 0x0, 0x64); +#endif + +#define EAXPROPERTYID_EAX_FXSlot0 EAXPROPERTYID_EAX50_FXSlot0 +#define EAXPROPERTYID_EAX_FXSlot1 EAXPROPERTYID_EAX50_FXSlot1 +#define EAXPROPERTYID_EAX_FXSlot2 EAXPROPERTYID_EAX50_FXSlot2 +#define EAXPROPERTYID_EAX_FXSlot3 EAXPROPERTYID_EAX50_FXSlot3 + +// FXSlot object properties +typedef enum +{ + EAXFXSLOT_PARAMETER = 0, // range 0-0x40 reserved for loaded effect parameters + EAXFXSLOT_NONE = 0x10000, + EAXFXSLOT_ALLPARAMETERS, + EAXFXSLOT_LOADEFFECT, + EAXFXSLOT_VOLUME, + EAXFXSLOT_LOCK, + EAXFXSLOT_FLAGS, + EAXFXSLOT_OCCLUSION, + EAXFXSLOT_OCCLUSIONLFRATIO +} EAXFXSLOT_PROPERTY; + +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXFXSLOTFLAGS_ENVIRONMENT; +// instead of: +// myFlags = 0x00000001; +// +#define EAXFXSLOTFLAGS_ENVIRONMENT 0x00000001 +#define EAXFXSLOTFLAGS_UPMIX 0x00000002 + +#define EAXFXSLOTFLAGS_RESERVED 0xFFFFFFFC // reserved future use + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40FXSLOTFLAGS_RESERVED 0xFFFFFFFE // reserved future use +#endif + +// EAX Effect Slot property ranges and defaults: +#define EAXFXSLOT_MINVOLUME (-10000) +#define EAXFXSLOT_MAXVOLUME 0 +#define EAXFXSLOT_DEFAULTVOLUME 0 + +enum +{ + EAXFXSLOT_UNLOCKED = 0, + EAXFXSLOT_LOCKED = 1 +}; + +#define EAXFXSLOT_MINLOCK 0 +#define EAXFXSLOT_MAXLOCK 1 + +#define EAXFXSLOT_MINOCCLUSION (-10000) +#define EAXFXSLOT_MAXOCCLUSION 0 +#define EAXFXSLOT_DEFAULTOCCLUSION 0 + +#define EAXFXSLOT_MINOCCLUSIONLFRATIO 0.0f +#define EAXFXSLOT_MAXOCCLUSIONLFRATIO 1.0f +#define EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXFXSLOT_DEFAULTFLAGS (EAXFXSLOTFLAGS_ENVIRONMENT | \ + EAXFXSLOTFLAGS_UPMIX ) // ignored for reverb + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40FXSLOT_DEFAULTFLAGS (EAXFXSLOTFLAGS_ENVIRONMENT) +#endif + + +//////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////// +// Source Object + +// {5EDF82F0-24A7-4f38-8E64-2F09CA05DEE1} +DEFINE_GUID(EAXPROPERTYID_EAX50_Source, + 0x5edf82f0, + 0x24a7, + 0x4f38, + 0x8e, 0x64, 0x2f, 0x9, 0xca, 0x5, 0xde, 0xe1); + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +// {1B86B823-22DF-4eae-8B3C-1278CE544227} +DEFINE_GUID(EAXPROPERTYID_EAX40_Source, + 0x1b86b823, + 0x22df, + 0x4eae, + 0x8b, 0x3c, 0x12, 0x78, 0xce, 0x54, 0x42, 0x27); +#endif + +#define EAXPROPERTYID_EAX_Source EAXPROPERTYID_EAX50_Source + + +// Source object properties +typedef enum +{ + EAXSOURCE_NONE, + EAXSOURCE_ALLPARAMETERS, + EAXSOURCE_OBSTRUCTIONPARAMETERS, + EAXSOURCE_OCCLUSIONPARAMETERS, + EAXSOURCE_EXCLUSIONPARAMETERS, + EAXSOURCE_DIRECT, + EAXSOURCE_DIRECTHF, + EAXSOURCE_ROOM, + EAXSOURCE_ROOMHF, + EAXSOURCE_OBSTRUCTION, + EAXSOURCE_OBSTRUCTIONLFRATIO, + EAXSOURCE_OCCLUSION, + EAXSOURCE_OCCLUSIONLFRATIO, + EAXSOURCE_OCCLUSIONROOMRATIO, + EAXSOURCE_OCCLUSIONDIRECTRATIO, + EAXSOURCE_EXCLUSION, + EAXSOURCE_EXCLUSIONLFRATIO, + EAXSOURCE_OUTSIDEVOLUMEHF, + EAXSOURCE_DOPPLERFACTOR, + EAXSOURCE_ROLLOFFFACTOR, + EAXSOURCE_ROOMROLLOFFFACTOR, + EAXSOURCE_AIRABSORPTIONFACTOR, + EAXSOURCE_FLAGS, + EAXSOURCE_SENDPARAMETERS, + EAXSOURCE_ALLSENDPARAMETERS, + EAXSOURCE_OCCLUSIONSENDPARAMETERS, + EAXSOURCE_EXCLUSIONSENDPARAMETERS, + EAXSOURCE_ACTIVEFXSLOTID, + EAXSOURCE_MACROFXFACTOR, + EAXSOURCE_SPEAKERLEVELS, + EAXSOURCE_ALL2DPARAMETERS, +} EAXSOURCE_PROPERTY; + +// OR these flags with property id +#define EAXSOURCE_PARAMETER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXSOURCE_PARAMETER_DEFERRED 0x80000000 // changes take effect later +#define EAXSOURCE_PARAMETER_COMMITDEFERREDSETTINGS (EAXSOURCE_NONE | \ + EAXSOURCE_PARAMETER_IMMEDIATE) +// Used by EAXSOURCE_FLAGS for EAXSOURCEFLAGS_xxxAUTO +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXSOURCE_DIRECTHFAUTO | EAXSOURCE_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXSOURCEFLAGS_DIRECTHFAUTO 0x00000001 // relates to EAXSOURCE_DIRECTHF +#define EAXSOURCEFLAGS_ROOMAUTO 0x00000002 // relates to EAXSOURCE_ROOM +#define EAXSOURCEFLAGS_ROOMHFAUTO 0x00000004 // relates to EAXSOURCE_ROOMHF +#define EAXSOURCEFLAGS_3DELEVATIONFILTER 0x00000008 +#define EAXSOURCEFLAGS_UPMIX 0x00000010 +#define EAXSOURCEFLAGS_APPLYSPEAKERLEVELS 0x00000020 + +#define EAXSOURCEFLAGS_RESERVED 0xFFFFFFC0 // reserved future use + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCEFLAGS_RESERVED 0xFFFFFFF8 // reserved future use +#endif + + +// EAX Source property ranges and defaults: +#define EAXSOURCE_MINSEND (-10000) +#define EAXSOURCE_MAXSEND 0 +#define EAXSOURCE_DEFAULTSEND 0 + +#define EAXSOURCE_MINSENDHF (-10000) +#define EAXSOURCE_MAXSENDHF 0 +#define EAXSOURCE_DEFAULTSENDHF 0 + +#define EAXSOURCE_MINDIRECT (-10000) +#define EAXSOURCE_MAXDIRECT 1000 +#define EAXSOURCE_DEFAULTDIRECT 0 + +#define EAXSOURCE_MINDIRECTHF (-10000) +#define EAXSOURCE_MAXDIRECTHF 0 +#define EAXSOURCE_DEFAULTDIRECTHF 0 + +#define EAXSOURCE_MINROOM (-10000) +#define EAXSOURCE_MAXROOM 1000 +#define EAXSOURCE_DEFAULTROOM 0 + +#define EAXSOURCE_MINROOMHF (-10000) +#define EAXSOURCE_MAXROOMHF 0 +#define EAXSOURCE_DEFAULTROOMHF 0 + +#define EAXSOURCE_MINOBSTRUCTION (-10000) +#define EAXSOURCE_MAXOBSTRUCTION 0 +#define EAXSOURCE_DEFAULTOBSTRUCTION 0 + +#define EAXSOURCE_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXSOURCE_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXSOURCE_MINOCCLUSION (-10000) +#define EAXSOURCE_MAXOCCLUSION 0 +#define EAXSOURCE_DEFAULTOCCLUSION 0 + +#define EAXSOURCE_MINOCCLUSIONLFRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXSOURCE_MINOCCLUSIONROOMRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXSOURCE_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXSOURCE_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXSOURCE_MINEXCLUSION (-10000) +#define EAXSOURCE_MAXEXCLUSION 0 +#define EAXSOURCE_DEFAULTEXCLUSION 0 + +#define EAXSOURCE_MINEXCLUSIONLFRATIO 0.0f +#define EAXSOURCE_MAXEXCLUSIONLFRATIO 1.0f +#define EAXSOURCE_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXSOURCE_MINOUTSIDEVOLUMEHF (-10000) +#define EAXSOURCE_MAXOUTSIDEVOLUMEHF 0 +#define EAXSOURCE_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXSOURCE_MINDOPPLERFACTOR 0.0f +#define EAXSOURCE_MAXDOPPLERFACTOR 10.f +#define EAXSOURCE_DEFAULTDOPPLERFACTOR 1.0f + +#define EAXSOURCE_MINROLLOFFFACTOR 0.0f +#define EAXSOURCE_MAXROLLOFFFACTOR 10.f +#define EAXSOURCE_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXSOURCE_MINROOMROLLOFFFACTOR 0.0f +#define EAXSOURCE_MAXROOMROLLOFFFACTOR 10.f +#define EAXSOURCE_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXSOURCE_MINAIRABSORPTIONFACTOR 0.0f +#define EAXSOURCE_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXSOURCE_DEFAULTAIRABSORPTIONFACTOR 0.0f + +#define EAXSOURCE_MINMACROFXFACTOR 0.0f +#define EAXSOURCE_MAXMACROFXFACTOR 1.0f +#define EAXSOURCE_DEFAULTMACROFXFACTOR 1.0f + +#define EAXSOURCE_MINSPEAKERLEVEL (-10000) +#define EAXSOURCE_MAXSPEAKERLEVEL 0 +#define EAXSOURCE_DEFAULTSPEAKERLEVEL (-10000) + +enum +{ + EAXSPEAKER_FRONT_LEFT = 1, + EAXSPEAKER_FRONT_CENTER = 2, + EAXSPEAKER_FRONT_RIGHT = 3, + EAXSPEAKER_SIDE_RIGHT = 4, + EAXSPEAKER_REAR_RIGHT = 5, + EAXSPEAKER_REAR_CENTER = 6, + EAXSPEAKER_REAR_LEFT = 7, + EAXSPEAKER_SIDE_LEFT = 8, + EAXSPEAKER_LOW_FREQUENCY = 9 +}; + +// EAXSOURCEFLAGS_DIRECTHFAUTO, EAXSOURCEFLAGS_ROOMAUTO and EAXSOURCEFLAGS_ROOMHFAUTO are ignored for 2D sources +// EAXSOURCEFLAGS_UPMIX is ignored for 3D sources +#define EAXSOURCE_DEFAULTFLAGS (EAXSOURCEFLAGS_DIRECTHFAUTO | \ + EAXSOURCEFLAGS_ROOMAUTO | \ + EAXSOURCEFLAGS_ROOMHFAUTO | \ + EAXSOURCEFLAGS_UPMIX ) + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCE_DEFAULTFLAGS (EAXSOURCEFLAGS_DIRECTHFAUTO | \ + EAXSOURCEFLAGS_ROOMAUTO | \ + EAXSOURCEFLAGS_ROOMHFAUTO ) + +#endif + + +// A 3D Source's default ACTIVEFXSLOTID is { EAX_NULL_GUID, EAX_PrimaryFXSlotID, EAX_NULL_GUID, EAX_NULL_GUID } +#define EAXSOURCE_3DDEFAULTACTIVEFXSLOTID {{ EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_PrimaryFXSlotID.Data1, EAX_PrimaryFXSlotID.Data2, \ + EAX_PrimaryFXSlotID.Data3, EAX_PrimaryFXSlotID.Data4[0], \ + EAX_PrimaryFXSlotID.Data4[1],EAX_PrimaryFXSlotID.Data4[2], \ + EAX_PrimaryFXSlotID.Data4[3],EAX_PrimaryFXSlotID.Data4[4], \ + EAX_PrimaryFXSlotID.Data4[5],EAX_PrimaryFXSlotID.Data4[6], \ + EAX_PrimaryFXSlotID.Data4[7] }, \ + { EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] } } + +// A 2D Source's default ACTIVEFXSLOTID is { EAX_NULL_GUID, EAX_NULL_GUID, EAX_NULL_GUID, EAX_NULL_GUID } +#define EAXSOURCE_2DDEFAULTACTIVEFXSLOTID {{ EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] } } + +#ifdef _INCLUDE_EAX_40_DEFINITIONS +#define EAX40SOURCE_DEFAULTACTIVEFXSLOTID {{ EAX_NULL_GUID.Data1, EAX_NULL_GUID.Data2, EAX_NULL_GUID.Data3, \ + EAX_NULL_GUID.Data4[0],EAX_NULL_GUID.Data4[1],EAX_NULL_GUID.Data4[2],\ + EAX_NULL_GUID.Data4[3],EAX_NULL_GUID.Data4[4],EAX_NULL_GUID.Data4[5],\ + EAX_NULL_GUID.Data4[6],EAX_NULL_GUID.Data4[7] }, \ + { EAX_PrimaryFXSlotID.Data1, EAX_PrimaryFXSlotID.Data2, \ + EAX_PrimaryFXSlotID.Data3, EAX_PrimaryFXSlotID.Data4[0],\ + EAX_PrimaryFXSlotID.Data4[1],EAX_PrimaryFXSlotID.Data4[2],\ + EAX_PrimaryFXSlotID.Data4[3],EAX_PrimaryFXSlotID.Data4[4],\ + EAX_PrimaryFXSlotID.Data4[5],EAX_PrimaryFXSlotID.Data4[6],\ + EAX_PrimaryFXSlotID.Data4[7] }} +#endif + + +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// +// Reverb Effect + +// EAX REVERB {0CF95C8F-A3CC-4849-B0B6-832ECC1822DF} +DEFINE_GUID(EAX_REVERB_EFFECT, + 0xcf95c8f, + 0xa3cc, + 0x4849, + 0xb0, 0xb6, 0x83, 0x2e, 0xcc, 0x18, 0x22, 0xdf); + +// Reverb effect properties +typedef enum +{ + EAXREVERB_NONE, + EAXREVERB_ALLPARAMETERS, + EAXREVERB_ENVIRONMENT, + EAXREVERB_ENVIRONMENTSIZE, + EAXREVERB_ENVIRONMENTDIFFUSION, + EAXREVERB_ROOM, + EAXREVERB_ROOMHF, + EAXREVERB_ROOMLF, + EAXREVERB_DECAYTIME, + EAXREVERB_DECAYHFRATIO, + EAXREVERB_DECAYLFRATIO, + EAXREVERB_REFLECTIONS, + EAXREVERB_REFLECTIONSDELAY, + EAXREVERB_REFLECTIONSPAN, + EAXREVERB_REVERB, + EAXREVERB_REVERBDELAY, + EAXREVERB_REVERBPAN, + EAXREVERB_ECHOTIME, + EAXREVERB_ECHODEPTH, + EAXREVERB_MODULATIONTIME, + EAXREVERB_MODULATIONDEPTH, + EAXREVERB_AIRABSORPTIONHF, + EAXREVERB_HFREFERENCE, + EAXREVERB_LFREFERENCE, + EAXREVERB_ROOMROLLOFFFACTOR, + EAXREVERB_FLAGS, +} EAXREVERB_PROPERTY; + +// OR these flags with property id +#define EAXREVERB_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXREVERB_DEFERRED 0x80000000 // changes take effect later +#define EAXREVERB_COMMITDEFERREDSETTINGS (EAXREVERB_NONE | \ + EAXREVERB_IMMEDIATE) + +// used by EAXREVERB_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by EAXREVERB_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXREVERBFLAGS_DECAYTIMESCALE | EAXREVERBFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXREVERBFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXREVERBFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXREVERBFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXREVERBFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXREVERBFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXREVERBFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXREVERBFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time +// This flag limits high-frequency decay time according to air absorption. +#define EAXREVERBFLAGS_DECAYHFLIMIT 0x00000020 +#define EAXREVERBFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Use this structure for EAXREVERB_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myReverb.lRoom = -1000; +// myReverb.lRoomHF = -100; +// ... +// myReverb.dwFlags = myFlags /* see EAXREVERBFLAGS below */ ; +// instead of: +// myReverb = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +#ifndef EAXREVERBPROPERTIES_DEFINED +#define EAXREVERBPROPERTIES_DEFINED +typedef struct _EAXREVERBPROPERTIES +{ + unsigned long ulEnvironment; // sets all reverb properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXREVERBPROPERTIES, *LPEAXREVERBPROPERTIES; +#endif + +// Property ranges and defaults: +#define EAXREVERB_MINENVIRONMENT 0 +#define EAXREVERB_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXREVERB_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXREVERB_MINENVIRONMENTSIZE 1.0f +#define EAXREVERB_MAXENVIRONMENTSIZE 100.0f +#define EAXREVERB_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXREVERB_MINENVIRONMENTDIFFUSION 0.0f +#define EAXREVERB_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXREVERB_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXREVERB_MINROOM (-10000) +#define EAXREVERB_MAXROOM 0 +#define EAXREVERB_DEFAULTROOM (-1000) + +#define EAXREVERB_MINROOMHF (-10000) +#define EAXREVERB_MAXROOMHF 0 +#define EAXREVERB_DEFAULTROOMHF (-100) + +#define EAXREVERB_MINROOMLF (-10000) +#define EAXREVERB_MAXROOMLF 0 +#define EAXREVERB_DEFAULTROOMLF 0 + +#define EAXREVERB_MINDECAYTIME 0.1f +#define EAXREVERB_MAXDECAYTIME 20.0f +#define EAXREVERB_DEFAULTDECAYTIME 1.49f + +#define EAXREVERB_MINDECAYHFRATIO 0.1f +#define EAXREVERB_MAXDECAYHFRATIO 2.0f +#define EAXREVERB_DEFAULTDECAYHFRATIO 0.83f + +#define EAXREVERB_MINDECAYLFRATIO 0.1f +#define EAXREVERB_MAXDECAYLFRATIO 2.0f +#define EAXREVERB_DEFAULTDECAYLFRATIO 1.00f + +#define EAXREVERB_MINREFLECTIONS (-10000) +#define EAXREVERB_MAXREFLECTIONS 1000 +#define EAXREVERB_DEFAULTREFLECTIONS (-2602) + +#define EAXREVERB_MINREFLECTIONSDELAY 0.0f +#define EAXREVERB_MAXREFLECTIONSDELAY 0.3f +#define EAXREVERB_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXREVERB_DEFAULTREFLECTIONSPAN {0.0f, 0.0f, 0.0f} + +#define EAXREVERB_MINREVERB (-10000) +#define EAXREVERB_MAXREVERB 2000 +#define EAXREVERB_DEFAULTREVERB 200 + +#define EAXREVERB_MINREVERBDELAY 0.0f +#define EAXREVERB_MAXREVERBDELAY 0.1f +#define EAXREVERB_DEFAULTREVERBDELAY 0.011f + +#define EAXREVERB_DEFAULTREVERBPAN {0.0f, 0.0f, 0.0f} + +#define EAXREVERB_MINECHOTIME 0.075f +#define EAXREVERB_MAXECHOTIME 0.25f +#define EAXREVERB_DEFAULTECHOTIME 0.25f + +#define EAXREVERB_MINECHODEPTH 0.0f +#define EAXREVERB_MAXECHODEPTH 1.0f +#define EAXREVERB_DEFAULTECHODEPTH 0.0f + +#define EAXREVERB_MINMODULATIONTIME 0.04f +#define EAXREVERB_MAXMODULATIONTIME 4.0f +#define EAXREVERB_DEFAULTMODULATIONTIME 0.25f + +#define EAXREVERB_MINMODULATIONDEPTH 0.0f +#define EAXREVERB_MAXMODULATIONDEPTH 1.0f +#define EAXREVERB_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXREVERB_MINAIRABSORPTIONHF (-100.0f) +#define EAXREVERB_MAXAIRABSORPTIONHF 0.0f +#define EAXREVERB_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXREVERB_MINHFREFERENCE 1000.0f +#define EAXREVERB_MAXHFREFERENCE 20000.0f +#define EAXREVERB_DEFAULTHFREFERENCE 5000.0f + +#define EAXREVERB_MINLFREFERENCE 20.0f +#define EAXREVERB_MAXLFREFERENCE 1000.0f +#define EAXREVERB_DEFAULTLFREFERENCE 250.0f + +#define EAXREVERB_MINROOMROLLOFFFACTOR 0.0f +#define EAXREVERB_MAXROOMROLLOFFFACTOR 10.0f +#define EAXREVERB_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXREVERB_DEFAULTFLAGS (EAXREVERBFLAGS_DECAYTIMESCALE | \ + EAXREVERBFLAGS_REFLECTIONSSCALE | \ + EAXREVERBFLAGS_REFLECTIONSDELAYSCALE | \ + EAXREVERBFLAGS_REVERBSCALE | \ + EAXREVERBFLAGS_REVERBDELAYSCALE | \ + EAXREVERBFLAGS_DECAYHFLIMIT) +//////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////// + +// New Effect Types + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// AGC Compressor Effect + +// EAX AGC COMPRESSOR {BFB7A01E-7825-4039-927F-3AABDA0C560} + +DEFINE_GUID(EAX_AGCCOMPRESSOR_EFFECT, + 0xbfb7a01e, + 0x7825, + 0x4039, + 0x92, 0x7f, 0x3, 0xaa, 0xbd, 0xa0, 0xc5, 0x60); + +// AGC Compressor properties +typedef enum +{ + EAXAGCCOMPRESSOR_NONE, + EAXAGCCOMPRESSOR_ALLPARAMETERS, + EAXAGCCOMPRESSOR_ONOFF +} EAXAGCCOMPRESSOR_PROPERTY; + +// OR these flags with property id +#define EAXAGCCOMPRESSOR_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXAGCCOMPRESSOR_DEFERRED 0x80000000 // changes take effect later +#define EAXAGCCOMPRESSOR_COMMITDEFERREDSETTINGS (EAXAGCCOMPRESSOR_NONE | \ + EAXAGCCOMPRESSOR_IMMEDIATE) + +// Use this structure for EAXAGCCOMPRESSOR_ALLPARAMETERS +#ifndef EAXAGCCOMPRESSORPROPERTIES_DEFINED +#define EAXAGCCOMPRESSORPROPERTIES_DEFINED +typedef struct _EAXAGCCOMPRESSORPROPERTIES +{ + unsigned long ulOnOff; // Switch Compressor on or off +} EAXAGCCOMPRESSORPROPERTIES, *LPEAXAGCCOMPRESSORPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXAGCCOMPRESSOR_MINONOFF 0 +#define EAXAGCCOMPRESSOR_MAXONOFF 1 +#define EAXAGCCOMPRESSOR_DEFAULTONOFF 1 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Autowah Effect + +// EAX AUTOWAH {EC3130C0-AC7A-11D2-88DD-A024D13CE1} +DEFINE_GUID(EAX_AUTOWAH_EFFECT, + 0xec3130c0, + 0xac7a, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Autowah properties +typedef enum +{ + EAXAUTOWAH_NONE, + EAXAUTOWAH_ALLPARAMETERS, + EAXAUTOWAH_ATTACKTIME, + EAXAUTOWAH_RELEASETIME, + EAXAUTOWAH_RESONANCE, + EAXAUTOWAH_PEAKLEVEL +} EAXAUTOWAH_PROPERTY; + +// OR these flags with property id +#define EAXAUTOWAH_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXAUTOWAH_DEFERRED 0x80000000 // changes take effect later +#define EAXAUTOWAH_COMMITDEFERREDSETTINGS (EAXAUTOWAH_NONE | \ + EAXAUTOWAH_IMMEDIATE) + +// Use this structure for EAXAUTOWAH_ALLPARAMETERS +#ifndef EAXAUTOWAHPROPERTIES_DEFINED +#define EAXAUTOWAHPROPERTIES_DEFINED +typedef struct _EAXAUTOWAHPROPERTIES +{ + float flAttackTime; // Attack time (seconds) + float flReleaseTime; // Release time (seconds) + long lResonance; // Resonance (mB) + long lPeakLevel; // Peak level (mB) +} EAXAUTOWAHPROPERTIES, *LPEAXAUTOWAHPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXAUTOWAH_MINATTACKTIME 0.0001f +#define EAXAUTOWAH_MAXATTACKTIME 1.0f +#define EAXAUTOWAH_DEFAULTATTACKTIME 0.06f + +#define EAXAUTOWAH_MINRELEASETIME 0.0001f +#define EAXAUTOWAH_MAXRELEASETIME 1.0f +#define EAXAUTOWAH_DEFAULTRELEASETIME 0.06f + +#define EAXAUTOWAH_MINRESONANCE 600 +#define EAXAUTOWAH_MAXRESONANCE 6000 +#define EAXAUTOWAH_DEFAULTRESONANCE 6000 + +#define EAXAUTOWAH_MINPEAKLEVEL (-9000) +#define EAXAUTOWAH_MAXPEAKLEVEL 9000 +#define EAXAUTOWAH_DEFAULTPEAKLEVEL 2100 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Chorus Effect + +// EAX CHORUS {DE6D6FE0-AC79-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_CHORUS_EFFECT, + 0xde6d6fe0, + 0xac79, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + + +// Chorus properties +typedef enum +{ + EAXCHORUS_NONE, + EAXCHORUS_ALLPARAMETERS, + EAXCHORUS_WAVEFORM, + EAXCHORUS_PHASE, + EAXCHORUS_RATE, + EAXCHORUS_DEPTH, + EAXCHORUS_FEEDBACK, + EAXCHORUS_DELAY +} EAXCHORUS_PROPERTY; + +// OR these flags with property id +#define EAXCHORUS_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXCHORUS_DEFERRED 0x80000000 // changes take effect later +#define EAXCHORUS_COMMITDEFERREDSETTINGS (EAXCHORUS_NONE | \ + EAXCHORUS_IMMEDIATE) + +// used by EAXCHORUS_WAVEFORM +enum +{ + EAX_CHORUS_SINUSOID, + EAX_CHORUS_TRIANGLE +}; + +// Use this structure for EAXCHORUS_ALLPARAMETERS +#ifndef EAXCHORUSPROPERTIES_DEFINED +#define EAXCHORUSPROPERTIES_DEFINED +typedef struct _EAXCHORUSPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (-1 to 1) + float flDelay; // Delay (seconds) +} EAXCHORUSPROPERTIES, *LPEAXCHORUSPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXCHORUS_MINWAVEFORM 0 +#define EAXCHORUS_MAXWAVEFORM 1 +#define EAXCHORUS_DEFAULTWAVEFORM 1 + +#define EAXCHORUS_MINPHASE (-180) +#define EAXCHORUS_MAXPHASE 180 +#define EAXCHORUS_DEFAULTPHASE 90 + +#define EAXCHORUS_MINRATE 0.0f +#define EAXCHORUS_MAXRATE 10.0f +#define EAXCHORUS_DEFAULTRATE 1.1f + +#define EAXCHORUS_MINDEPTH 0.0f +#define EAXCHORUS_MAXDEPTH 1.0f +#define EAXCHORUS_DEFAULTDEPTH 0.1f + +#define EAXCHORUS_MINFEEDBACK (-1.0f) +#define EAXCHORUS_MAXFEEDBACK 1.0f +#define EAXCHORUS_DEFAULTFEEDBACK 0.25f + +#define EAXCHORUS_MINDELAY 0.0002f +#define EAXCHORUS_MAXDELAY 0.016f +#define EAXCHORUS_DEFAULTDELAY 0.016f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Distortion Effect + +// EAX DISTORTION {975A4CE0-AC7E-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_DISTORTION_EFFECT, + 0x975a4ce0, + 0xac7e, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Distortion properties +typedef enum +{ + EAXDISTORTION_NONE, + EAXDISTORTION_ALLPARAMETERS, + EAXDISTORTION_EDGE, + EAXDISTORTION_GAIN, + EAXDISTORTION_LOWPASSCUTOFF, + EAXDISTORTION_EQCENTER, + EAXDISTORTION_EQBANDWIDTH +} EAXDISTORTION_PROPERTY; + +// OR these flags with property id +#define EAXDISTORTION_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXDISTORTION_DEFERRED 0x80000000 // changes take effect later +#define EAXDISTORTION_COMMITDEFERREDSETTINGS (EAXDISTORTION_NONE | \ + EAXDISTORTION_IMMEDIATE) + +// Use this structure for EAXDISTORTION_ALLPARAMETERS +#ifndef EAXDISTORTIONPROPERTIES_DEFINED +#define EAXDISTORTIONPROPERTIES_DEFINED +typedef struct _EAXDISTORTIONPROPERTIES +{ + float flEdge; // Controls the shape of the distortion (0 to 1) + long lGain; // Controls the post distortion gain (mB) + float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) + float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) + float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) +} EAXDISTORTIONPROPERTIES, *LPEAXDISTORTIONPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXDISTORTION_MINEDGE 0.0f +#define EAXDISTORTION_MAXEDGE 1.0f +#define EAXDISTORTION_DEFAULTEDGE 0.2f + +#define EAXDISTORTION_MINGAIN (-6000) +#define EAXDISTORTION_MAXGAIN 0 +#define EAXDISTORTION_DEFAULTGAIN (-2600) + +#define EAXDISTORTION_MINLOWPASSCUTOFF 80.0f +#define EAXDISTORTION_MAXLOWPASSCUTOFF 24000.0f +#define EAXDISTORTION_DEFAULTLOWPASSCUTOFF 8000.0f + +#define EAXDISTORTION_MINEQCENTER 80.0f +#define EAXDISTORTION_MAXEQCENTER 24000.0f +#define EAXDISTORTION_DEFAULTEQCENTER 3600.0f + +#define EAXDISTORTION_MINEQBANDWIDTH 80.0f +#define EAXDISTORTION_MAXEQBANDWIDTH 24000.0f +#define EAXDISTORTION_DEFAULTEQBANDWIDTH 3600.0f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Echo Effect + +// EAX ECHO {E9F1BC0-AC82-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_ECHO_EFFECT, + 0xe9f1bc0, + 0xac82, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Echo properties +typedef enum +{ + EAXECHO_NONE, + EAXECHO_ALLPARAMETERS, + EAXECHO_DELAY, + EAXECHO_LRDELAY, + EAXECHO_DAMPING, + EAXECHO_FEEDBACK, + EAXECHO_SPREAD +} EAXECHO_PROPERTY; + +// OR these flags with property id +#define EAXECHO_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXECHO_DEFERRED 0x80000000 // changes take effect later +#define EAXECHO_COMMITDEFERREDSETTINGS (EAXECHO_NONE | \ + EAXECHO_IMMEDIATE) + +// Use this structure for EAXECHO_ALLPARAMETERS +#ifndef EAXECHOPROPERTIES_DEFINED +#define EAXECHOPROPERTIES_DEFINED +typedef struct _EAXECHOPROPERTIES +{ + float flDelay; // Controls the initial delay time (seconds) + float flLRDelay; // Controls the delay time between the first and second taps (seconds) + float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) + float flFeedback; // Controls the duration of echo repetition (0 to 1) + float flSpread; // Controls the left-right spread of the echoes +} EAXECHOPROPERTIES, *LPEAXECHOPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXECHO_MINDAMPING 0.0f +#define EAXECHO_MAXDAMPING 0.99f +#define EAXECHO_DEFAULTDAMPING 0.5f + +#define EAXECHO_MINDELAY 0.002f +#define EAXECHO_MAXDELAY 0.207f +#define EAXECHO_DEFAULTDELAY 0.1f + +#define EAXECHO_MINLRDELAY 0.0f +#define EAXECHO_MAXLRDELAY 0.404f +#define EAXECHO_DEFAULTLRDELAY 0.1f + +#define EAXECHO_MINFEEDBACK 0.0f +#define EAXECHO_MAXFEEDBACK 1.0f +#define EAXECHO_DEFAULTFEEDBACK 0.5f + +#define EAXECHO_MINSPREAD (-1.0f) +#define EAXECHO_MAXSPREAD 1.0f +#define EAXECHO_DEFAULTSPREAD (-1.0f) + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Equalizer Effect + +// EAX EQUALIZER {65F94CE0-9793-11D3-939D-C0F02DD6F0} + +DEFINE_GUID(EAX_EQUALIZER_EFFECT, + 0x65f94ce0, + 0x9793, + 0x11d3, + 0x93, 0x9d, 0x0, 0xc0, 0xf0, 0x2d, 0xd6, 0xf0); + + +// Equalizer properties +typedef enum +{ + EAXEQUALIZER_NONE, + EAXEQUALIZER_ALLPARAMETERS, + EAXEQUALIZER_LOWGAIN, + EAXEQUALIZER_LOWCUTOFF, + EAXEQUALIZER_MID1GAIN, + EAXEQUALIZER_MID1CENTER, + EAXEQUALIZER_MID1WIDTH, + EAXEQUALIZER_MID2GAIN, + EAXEQUALIZER_MID2CENTER, + EAXEQUALIZER_MID2WIDTH, + EAXEQUALIZER_HIGHGAIN, + EAXEQUALIZER_HIGHCUTOFF +} EAXEQUALIZER_PROPERTY; + +// OR these flags with property id +#define EAXEQUALIZER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXEQUALIZER_DEFERRED 0x80000000 // changes take effect later +#define EAXEQUALIZER_COMMITDEFERREDSETTINGS (EAXEQUALIZER_NONE | \ + EAXEQUALIZER_IMMEDIATE) + +// Use this structure for EAXEQUALIZER_ALLPARAMETERS +#ifndef EAXEQUALIZERPROPERTIES_DEFINED +#define EAXEQUALIZERPROPERTIES_DEFINED +typedef struct _EAXEQUALIZERPROPERTIES +{ + long lLowGain; // (mB) + float flLowCutOff; // (Hz) + long lMid1Gain; // (mB) + float flMid1Center; // (Hz) + float flMid1Width; // (octaves) + long lMid2Gain; // (mB) + float flMid2Center; // (Hz) + float flMid2Width; // (octaves) + long lHighGain; // (mB) + float flHighCutOff; // (Hz) +} EAXEQUALIZERPROPERTIES, *LPEAXEQUALIZERPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXEQUALIZER_MINLOWGAIN (-1800) +#define EAXEQUALIZER_MAXLOWGAIN 1800 +#define EAXEQUALIZER_DEFAULTLOWGAIN 0 + +#define EAXEQUALIZER_MINLOWCUTOFF 50.0f +#define EAXEQUALIZER_MAXLOWCUTOFF 800.0f +#define EAXEQUALIZER_DEFAULTLOWCUTOFF 200.0f + +#define EAXEQUALIZER_MINMID1GAIN (-1800) +#define EAXEQUALIZER_MAXMID1GAIN 1800 +#define EAXEQUALIZER_DEFAULTMID1GAIN 0 + +#define EAXEQUALIZER_MINMID1CENTER 200.0f +#define EAXEQUALIZER_MAXMID1CENTER 3000.0f +#define EAXEQUALIZER_DEFAULTMID1CENTER 500.0f + +#define EAXEQUALIZER_MINMID1WIDTH 0.01f +#define EAXEQUALIZER_MAXMID1WIDTH 1.0f +#define EAXEQUALIZER_DEFAULTMID1WIDTH 1.0f + +#define EAXEQUALIZER_MINMID2GAIN (-1800) +#define EAXEQUALIZER_MAXMID2GAIN 1800 +#define EAXEQUALIZER_DEFAULTMID2GAIN 0 + +#define EAXEQUALIZER_MINMID2CENTER 1000.0f +#define EAXEQUALIZER_MAXMID2CENTER 8000.0f +#define EAXEQUALIZER_DEFAULTMID2CENTER 3000.0f + +#define EAXEQUALIZER_MINMID2WIDTH 0.01f +#define EAXEQUALIZER_MAXMID2WIDTH 1.0f +#define EAXEQUALIZER_DEFAULTMID2WIDTH 1.0f + +#define EAXEQUALIZER_MINHIGHGAIN (-1800) +#define EAXEQUALIZER_MAXHIGHGAIN 1800 +#define EAXEQUALIZER_DEFAULTHIGHGAIN 0 + +#define EAXEQUALIZER_MINHIGHCUTOFF 4000.0f +#define EAXEQUALIZER_MAXHIGHCUTOFF 16000.0f +#define EAXEQUALIZER_DEFAULTHIGHCUTOFF 6000.0f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Flanger Effect + +// EAX FLANGER {A70007C0-7D2-11D3-9B1E-A024D13CE1} + +DEFINE_GUID(EAX_FLANGER_EFFECT, + 0xa70007c0, + 0x7d2, + 0x11d3, + 0x9b, 0x1e, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Flanger properties +typedef enum +{ + EAXFLANGER_NONE, + EAXFLANGER_ALLPARAMETERS, + EAXFLANGER_WAVEFORM, + EAXFLANGER_PHASE, + EAXFLANGER_RATE, + EAXFLANGER_DEPTH, + EAXFLANGER_FEEDBACK, + EAXFLANGER_DELAY +} EAXFLANGER_PROPERTY; + +// OR these flags with property id +#define EAXFLANGER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXFLANGER_DEFERRED 0x80000000 // changes take effect later +#define EAXFLANGER_COMMITDEFERREDSETTINGS (EAXFLANGER_NONE | \ + EAXFLANGER_IMMEDIATE) + +// used by EAXFLANGER_WAVEFORM +enum +{ + EAX_FLANGER_SINUSOID, + EAX_FLANGER_TRIANGLE +}; + +// Use this structure for EAXFLANGER_ALLPARAMETERS +#ifndef EAXFLANGERPROPERTIES_DEFINED +#define EAXFLANGERPROPERTIES_DEFINED +typedef struct _EAXFLANGERPROPERTIES +{ + unsigned long ulWaveform; // Waveform selector - see enum above + long lPhase; // Phase (Degrees) + float flRate; // Rate (Hz) + float flDepth; // Depth (0 to 1) + float flFeedback; // Feedback (0 to 1) + float flDelay; // Delay (seconds) +} EAXFLANGERPROPERTIES, *LPEAXFLANGERPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXFLANGER_MINWAVEFORM 0 +#define EAXFLANGER_MAXWAVEFORM 1 +#define EAXFLANGER_DEFAULTWAVEFORM 1 + +#define EAXFLANGER_MINPHASE (-180) +#define EAXFLANGER_MAXPHASE 180 +#define EAXFLANGER_DEFAULTPHASE 0 + +#define EAXFLANGER_MINRATE 0.0f +#define EAXFLANGER_MAXRATE 10.0f +#define EAXFLANGER_DEFAULTRATE 0.27f + +#define EAXFLANGER_MINDEPTH 0.0f +#define EAXFLANGER_MAXDEPTH 1.0f +#define EAXFLANGER_DEFAULTDEPTH 1.0f + +#define EAXFLANGER_MINFEEDBACK (-1.0f) +#define EAXFLANGER_MAXFEEDBACK 1.0f +#define EAXFLANGER_DEFAULTFEEDBACK (-0.5f) + +#define EAXFLANGER_MINDELAY 0.0002f +#define EAXFLANGER_MAXDELAY 0.004f +#define EAXFLANGER_DEFAULTDELAY 0.002f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Frequency Shifter Effect + +// EAX FREQUENCY SHIFTER {DC3E1880-9212-11D3-939D-C0F02DD6F0} + +DEFINE_GUID(EAX_FREQUENCYSHIFTER_EFFECT, + 0xdc3e1880, + 0x9212, + 0x11d3, + 0x93, 0x9d, 0x0, 0xc0, 0xf0, 0x2d, 0xd6, 0xf0); + +// Frequency Shifter properties +typedef enum +{ + EAXFREQUENCYSHIFTER_NONE, + EAXFREQUENCYSHIFTER_ALLPARAMETERS, + EAXFREQUENCYSHIFTER_FREQUENCY, + EAXFREQUENCYSHIFTER_LEFTDIRECTION, + EAXFREQUENCYSHIFTER_RIGHTDIRECTION +} EAXFREQUENCYSHIFTER_PROPERTY; + +// OR these flags with property id +#define EAXFREQUENCYSHIFTER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXFREQUENCYSHIFTER_DEFERRED 0x80000000 // changes take effect later +#define EAXFREQUENCYSHIFTER_COMMITDEFERREDSETTINGS (EAXFREQUENCYSHIFTER_NONE | \ + EAXFREQUENCYSHIFTER_IMMEDIATE) + +// used by EAXFREQUENCYSHIFTER_LEFTDIRECTION and EAXFREQUENCYSHIFTER_RIGHTDIRECTION +enum +{ + EAX_FREQUENCYSHIFTER_DOWN, + EAX_FREQUENCYSHIFTER_UP, + EAX_FREQUENCYSHIFTER_OFF +}; + +// Use this structure for EAXFREQUENCYSHIFTER_ALLPARAMETERS +#ifndef EAXFREQUENCYSHIFTERPROPERTIES_DEFINED +#define EAXFREQUENCYSHIFTERPROPERTIES_DEFINED +typedef struct _EAXFREQUENCYSHIFTERPROPERTIES +{ + float flFrequency; // (Hz) + unsigned long ulLeftDirection; // see enum above + unsigned long ulRightDirection; // see enum above +} EAXFREQUENCYSHIFTERPROPERTIES, *LPEAXFREQUENCYSHIFTERPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXFREQUENCYSHIFTER_MINFREQUENCY 0.0f +#define EAXFREQUENCYSHIFTER_MAXFREQUENCY 24000.0f +#define EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY 0.0f + +#define EAXFREQUENCYSHIFTER_MINLEFTDIRECTION 0 +#define EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION 2 +#define EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION 0 + +#define EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION 0 +#define EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION 2 +#define EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION 0 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Vocal Morpher Effect + +// EAX VOCAL MORPHER {E41CF10C-3383-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_VOCALMORPHER_EFFECT, + 0xe41cf10c, + 0x3383, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Vocal Morpher properties +typedef enum +{ + EAXVOCALMORPHER_NONE, + EAXVOCALMORPHER_ALLPARAMETERS, + EAXVOCALMORPHER_PHONEMEA, + EAXVOCALMORPHER_PHONEMEACOARSETUNING, + EAXVOCALMORPHER_PHONEMEB, + EAXVOCALMORPHER_PHONEMEBCOARSETUNING, + EAXVOCALMORPHER_WAVEFORM, + EAXVOCALMORPHER_RATE +} EAXVOCALMORPHER_PROPERTY; + +// OR these flags with property id +#define EAXVOCALMORPHER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXVOCALMORPHER_DEFERRED 0x80000000 // changes take effect later +#define EAXVOCALMORPHER_COMMITDEFERREDSETTINGS (EAXVOCALMORPHER_NONE | \ + EAXVOCALMORPHER_IMMEDIATE) + +// used by EAXVOCALMORPHER_PHONEMEA and EAXVOCALMORPHER_PHONEMEB +enum +{ + A, E, I, O, U, AA, AE, AH, AO, EH, ER, IH, IY, UH, UW, B, D, F, G, + J, K, L, M, N, P, R, S, T, V, Z +}; + +// used by EAXVOCALMORPHER_WAVEFORM +enum +{ + EAX_VOCALMORPHER_SINUSOID, + EAX_VOCALMORPHER_TRIANGLE, + EAX_VOCALMORPHER_SAWTOOTH +}; + +// Use this structure for EAXVOCALMORPHER_ALLPARAMETERS +#ifndef EAXVOCALMORPHERPROPERTIES_DEFINED +#define EAXVOCALMORPHERPROPERTIES_DEFINED +typedef struct _EAXVOCALMORPHERPROPERTIES +{ + unsigned long ulPhonemeA; // see enum above + long lPhonemeACoarseTuning; // (semitones) + unsigned long ulPhonemeB; // see enum above + long lPhonemeBCoarseTuning; // (semitones) + unsigned long ulWaveform; // Waveform selector - see enum above + float flRate; // (Hz) +} EAXVOCALMORPHERPROPERTIES, *LPEAXVOCALMORPHERPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXVOCALMORPHER_MINPHONEMEA 0 +#define EAXVOCALMORPHER_MAXPHONEMEA 29 +#define EAXVOCALMORPHER_DEFAULTPHONEMEA 0 + +#define EAXVOCALMORPHER_MINPHONEMEACOARSETUNING (-24) +#define EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING 24 +#define EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING 0 + +#define EAXVOCALMORPHER_MINPHONEMEB 0 +#define EAXVOCALMORPHER_MAXPHONEMEB 29 +#define EAXVOCALMORPHER_DEFAULTPHONEMEB 10 + +#define EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING (-24) +#define EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING 24 +#define EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING 0 + +#define EAXVOCALMORPHER_MINWAVEFORM 0 +#define EAXVOCALMORPHER_MAXWAVEFORM 2 +#define EAXVOCALMORPHER_DEFAULTWAVEFORM 0 + +#define EAXVOCALMORPHER_MINRATE 0.0f +#define EAXVOCALMORPHER_MAXRATE 10.0f +#define EAXVOCALMORPHER_DEFAULTRATE 1.41f + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Pitch Shifter Effect + +// EAX PITCH SHIFTER {E7905100-AFB2-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_PITCHSHIFTER_EFFECT, + 0xe7905100, + 0xafb2, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Pitch Shifter properties +typedef enum +{ + EAXPITCHSHIFTER_NONE, + EAXPITCHSHIFTER_ALLPARAMETERS, + EAXPITCHSHIFTER_COARSETUNE, + EAXPITCHSHIFTER_FINETUNE +} EAXPITCHSHIFTER_PROPERTY; + +// OR these flags with property id +#define EAXPITCHSHIFTER_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXPITCHSHIFTER_DEFERRED 0x80000000 // changes take effect later +#define EAXPITCHSHIFTER_COMMITDEFERREDSETTINGS (EAXPITCHSHIFTER_NONE | \ + EAXPITCHSHIFTER_IMMEDIATE) + +// Use this structure for EAXPITCHSHIFTER_ALLPARAMETERS +#ifndef EAXPITCHSHIFTERPROPERTIES_DEFINED +#define EAXPITCHSHIFTERPROPERTIES_DEFINED +typedef struct _EAXPITCHSHIFTERPROPERTIES +{ + long lCoarseTune; // Amount of pitch shift (semitones) + long lFineTune; // Amount of pitch shift (cents) +} EAXPITCHSHIFTERPROPERTIES, *LPEAXPITCHSHIFTERPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXPITCHSHIFTER_MINCOARSETUNE (-12) +#define EAXPITCHSHIFTER_MAXCOARSETUNE 12 +#define EAXPITCHSHIFTER_DEFAULTCOARSETUNE 12 + +#define EAXPITCHSHIFTER_MINFINETUNE (-50) +#define EAXPITCHSHIFTER_MAXFINETUNE 50 +#define EAXPITCHSHIFTER_DEFAULTFINETUNE 0 + +//////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////// +// Ring Modulator Effect + +// EAX RING MODULATOR {B89FE60-AFB5-11D2-88DD-A024D13CE1} + +DEFINE_GUID(EAX_RINGMODULATOR_EFFECT, + 0xb89fe60, + 0xafb5, + 0x11d2, + 0x88, 0xdd, 0x0, 0xa0, 0x24, 0xd1, 0x3c, 0xe1); + +// Ring Modulator properties +typedef enum +{ + EAXRINGMODULATOR_NONE, + EAXRINGMODULATOR_ALLPARAMETERS, + EAXRINGMODULATOR_FREQUENCY, + EAXRINGMODULATOR_HIGHPASSCUTOFF, + EAXRINGMODULATOR_WAVEFORM +} EAXRINGMODULATOR_PROPERTY; + +// OR these flags with property id +#define EAXRINGMODULATOR_IMMEDIATE 0x00000000 // changes take effect immediately +#define EAXRINGMODULATOR_DEFERRED 0x80000000 // changes take effect later +#define EAXRINGMODULATOR_COMMITDEFERREDSETTINGS (EAXRINGMODULATOR_NONE | \ + EAXRINGMODULATOR_IMMEDIATE) + +// used by EAXRINGMODULATOR_WAVEFORM +enum +{ + EAX_RINGMODULATOR_SINUSOID, + EAX_RINGMODULATOR_SAWTOOTH, + EAX_RINGMODULATOR_SQUARE +}; + +// Use this structure for EAXRINGMODULATOR_ALLPARAMETERS +#ifndef EAXRINGMODULATORPROPERTIES_DEFINED +#define EAXRINGMODULATORPROPERTIES_DEFINED +typedef struct _EAXRINGMODULATORPROPERTIES +{ + float flFrequency; // Frequency of modulation (Hz) + float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) + unsigned long ulWaveform; // Waveform selector - see enum above +} EAXRINGMODULATORPROPERTIES, *LPEAXRINGMODULATORPROPERTIES; +#endif + +// Property ranges and defaults: + +#define EAXRINGMODULATOR_MINFREQUENCY 0.0f +#define EAXRINGMODULATOR_MAXFREQUENCY 8000.0f +#define EAXRINGMODULATOR_DEFAULTFREQUENCY 440.0f + +#define EAXRINGMODULATOR_MINHIGHPASSCUTOFF 0.0f +#define EAXRINGMODULATOR_MAXHIGHPASSCUTOFF 24000.0f +#define EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF 800.0f + +#define EAXRINGMODULATOR_MINWAVEFORM 0 +#define EAXRINGMODULATOR_MAXWAVEFORM 2 +#define EAXRINGMODULATOR_DEFAULTWAVEFORM 0 + +//////////////////////////////////////////////////////////////////////////// + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/win32/src/fmod_file_cdda.cpp b/win32/src/fmod_file_cdda.cpp new file mode 100755 index 0000000..639891f --- /dev/null +++ b/win32/src/fmod_file_cdda.cpp @@ -0,0 +1,624 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_CDDA + +#include "fmod_debug.h" +#include "fmod_file_cdda.h" +#include "fmod_os_cdda.h" +#include "fmod_string.h" +#include "fmod_time.h" + +#include <stdio.h> +#include <string.h> + + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +CddaFile::CddaFile() +{ + mDevice = 0; + mLastTimeAccessed = 0; + mGotUserToc = false; + mJitterCorrection = false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::init(bool force_aspi, bool jitter_correction) +{ + mJitterCorrection = jitter_correction; + return FMOD_OS_CDDA_Init(force_aspi); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::shutDown() +{ + return FMOD_OS_CDDA_Shutdown(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::reallyOpen(const char *name_or_data, unsigned int *filesize) +{ + int numtracks; + FMOD_RESULT result = FMOD_OK; + + mReadBuf = 0; + mReadPtr = 0; + mReadSizebytes = 0; + mStartSector = 0; + mCurrentSector = 0; + mSectorsLeft = 0; + mSectorsInChunk = 26; + mJitterBuf = 0; + mJitterBufSectors = 7; + mJitterBufEmpty = true; + mGotUserToc = false; + + result = FMOD_OS_CDDA_OpenDevice((char *)name_or_data, &mDevice); + if (result != FMOD_OK) + { + return result; + } + + if (!FMOD_OS_CDDA_TestUnitReady(mDevice)) + { + FMOD_OS_CDDA_DEBUGPRINT("CddaFile::reallyOpen: Device not ready (Make sure there's a CD/DVD in the drive!)"); + return FMOD_ERR_CDDA_NODISC; + } + + result = FMOD_OS_CDDA_ReadTocRaw(mDevice, &mDevice->toc); + if (result != FMOD_OK) + { + result = FMOD_OS_CDDA_ReadToc(mDevice, &mDevice->toc); + if (result != FMOD_OK) + { + return result; + } + } + +#ifdef FMOD_OS_CDDA_DUMPTOC + { + unsigned int i; + for (i=0;i < mDevice->toc.num_tracks;i++) + { + unsigned int len = (((mDevice->toc.num_sectors[i] * SIZEOF_CDDA_SECTOR) >> 2) / 44100) * 1000; + FMOD_OS_CDDA_DEBUGPRINT("Track %d. 0x%08lx %d:%d (%d)\n", mDevice->toc.track_number[i], mDevice->toc.start_sector[i], len / 1000 / 60, len / 1000 % 60, *((unsigned char *)&mDevice->toc.flags[i])); + } + } +#endif + + mReadBuf = (char *)FMOD_Memory_Alloc(mSectorsInChunk * SIZEOF_CDDA_SECTOR); + if (!mReadBuf) + { + reallyClose(); + return FMOD_ERR_MEMORY; + } + + FMOD_memset(mReadBuf, 0, mSectorsInChunk * SIZEOF_CDDA_SECTOR); + + if (mJitterCorrection) + { + mJitterBuf = (char *)FMOD_Memory_Alloc(SIZEOF_CDDA_SECTOR); + if (!mJitterBuf) + { + reallyClose(); + return FMOD_ERR_MEMORY; + } + + FMOD_memset(mJitterBuf, 0, SIZEOF_CDDA_SECTOR); + mJitterBufEmpty = true; + } + + result = getNumTracks(&numtracks); + if (result != FMOD_OK) + { + return result; + } + if (numtracks < 1) + { + FMOD_OS_CDDA_DEBUGPRINT("CddaFile::reallyOpen: No audio tracks! (Please insert a CD/DVD that contains one or more audio tracks)\n"); + return FMOD_ERR_CDDA_NOAUDIO; + } + + result = getTrackLength(0, filesize); + if (result != FMOD_OK) + { + return result; + } + + *filesize <<= 3; + mDevice->usertoc.numtracks--; + + FMOD_OS_CDDA_DEBUGPRINT("CddaFile::reallyOpen: ok\n"); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::reallyClose() +{ + FMOD_RESULT result = FMOD_OK; + + if (mDevice) + { + result = FMOD_OS_CDDA_CloseDevice(mDevice); + mDevice = 0; + } + + if (mReadBuf) + { + FMOD_Memory_Free(mReadBuf); + mReadBuf = 0; + } + + if (mJitterBuf) + { + FMOD_Memory_Free(mJitterBuf); + mJitterBuf = 0; + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::reallyRead(void *buffer, unsigned int size, unsigned int *rd) +{ + int bytestocopy, count; + unsigned int sectors_to_read = 0; + FMOD_RESULT result = FMOD_OK; +#ifdef FMOD_OS_CDDA_DEBUG + unsigned int starttime; + FMOD_OS_Time_GetMs(&starttime); +#endif + + if (!mDevice) + { + return FMOD_ERR_INVALID_PARAM; + } + + *rd = 0; + + while (size) + { + if (!mReadSizebytes) + { + sectors_to_read = (mSectorsLeft < mSectorsInChunk) ? mSectorsLeft : mSectorsInChunk; + if (!sectors_to_read) + { + *rd = 0; + return FMOD_ERR_FILE_EOF; + } + + for (count = 0; count < 10; count++) + { + result = FMOD_OS_CDDA_ReadSectors(mDevice, mReadBuf, mCurrentSector, sectors_to_read); + if (result == FMOD_OK) + { + break; + } + else + { + FMOD_OS_Time_Sleep(1); + } + } + + FMOD_OS_Time_GetMs(&mLastTimeAccessed); + + if (count >= 10) + { + return FMOD_ERR_CDDA_READ; + } + + if (mJitterCorrection) + { + doJitterCorrection(sectors_to_read); + } + else + { + mCurrentSector += sectors_to_read; + mSectorsLeft -= sectors_to_read; + mReadPtr = mReadBuf; + mReadSizebytes = sectors_to_read * SIZEOF_CDDA_SECTOR; + } + +#ifdef FMOD_OS_CDDA_DEBUG + { + unsigned int elapsed; + FMOD_OS_Time_GetMs(&elapsed); + elapsed -= starttime; + FMOD_OS_CDDA_AddProfileData((int)elapsed, mReadSizebytes); + } +#endif + } + + bytestocopy = (mReadSizebytes < (unsigned int)size) ? mReadSizebytes : size; + + FMOD_memcpy((char *)buffer, mReadPtr, bytestocopy); + + *rd += bytestocopy; + mReadPtr += bytestocopy; + mReadSizebytes -= bytestocopy; + + buffer = (void *)((char *)buffer + bytestocopy); + size -= bytestocopy; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::doJitterCorrection(unsigned int sectors_to_read) +{ + if (!mJitterBufEmpty) + { + int offset, skip_bytes; + int compare_size = ((sectors_to_read < mJitterBufSectors ? sectors_to_read : mJitterBufSectors) - 1) * SIZEOF_CDDA_SECTOR; + int start_compare = compare_size >> 1; + char *ptr_forward, *ptr_back; + bool found = false; + + if ((compare_size <= 0) || (sectors_to_read <= (mJitterBufSectors >> 1))) + { + FMOD_memset(mReadBuf, 0, sectors_to_read * SIZEOF_CDDA_SECTOR); + FMOD_memset(mJitterBuf, 0, SIZEOF_CDDA_SECTOR); + mCurrentSector += sectors_to_read; + mSectorsLeft -= sectors_to_read; + mReadPtr = mReadBuf; + mReadSizebytes = sectors_to_read * SIZEOF_CDDA_SECTOR; + + return FMOD_OK; + } + + ptr_forward = ptr_back = (mReadBuf + start_compare); + skip_bytes = start_compare; + + for (offset=0;offset < start_compare;offset += 4, ptr_forward += 4, ptr_back -= 4) + { + if (!memcmp(ptr_forward, mJitterBuf, SIZEOF_CDDA_SECTOR)) + { + skip_bytes = start_compare + offset + SIZEOF_CDDA_SECTOR; + found = true; + break; + } + + if (!memcmp(ptr_back, mJitterBuf, SIZEOF_CDDA_SECTOR)) + { + skip_bytes = start_compare - offset + SIZEOF_CDDA_SECTOR; + found = true; + break; + } + } + + if (!found) + { + /* + Jitter error - couldn't match up this read to the last one - it's gonna sound bad... + */ + skip_bytes = 0; + } + + if ((unsigned int)sectors_to_read == mSectorsLeft) + { + mCurrentSector += sectors_to_read; + mSectorsLeft -= sectors_to_read; + } + else + { + mCurrentSector += (sectors_to_read - (mJitterBufSectors >> 1) - 1); + mSectorsLeft -= (sectors_to_read - (mJitterBufSectors >> 1) - 1); + } + mReadPtr = mReadBuf + skip_bytes; + mReadSizebytes = (sectors_to_read * SIZEOF_CDDA_SECTOR) - skip_bytes; + } + else + { + mCurrentSector += sectors_to_read; + mSectorsLeft -= sectors_to_read; + mReadPtr = mReadBuf; + mReadSizebytes = sectors_to_read * SIZEOF_CDDA_SECTOR; + } + + FMOD_memcpy(mJitterBuf, mReadBuf + ((sectors_to_read - 1) * SIZEOF_CDDA_SECTOR), SIZEOF_CDDA_SECTOR); + mJitterBufEmpty = false; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::reallySeek(unsigned int pos) +{ + unsigned int num_sectors; + + pos /= SIZEOF_CDDA_SECTOR; + + num_sectors = (mCurrentSector - mStartSector) + mSectorsLeft; + if (pos >= num_sectors) + { + return FMOD_ERR_INVALID_PARAM; + } + mCurrentSector = mStartSector + pos; + mSectorsLeft = num_sectors - pos; + + FMOD_memset(mReadBuf, 0, mSectorsInChunk * SIZEOF_CDDA_SECTOR); + mReadPtr = 0; + mReadSizebytes = 0; + mJitterBufEmpty = true; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::getNumTracks(int *numtracks) +{ + if (!numtracks || !mDevice) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numtracks = mDevice->toc.num_tracks - 1; + if (*numtracks < 0) + { + *numtracks = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::getTrackLength(unsigned int track, unsigned int *tracklength) +{ + if (!tracklength || !mDevice || (track >= (mDevice->toc.num_tracks - 1))) + { + return FMOD_ERR_INVALID_PARAM; + } + + *tracklength = mDevice->toc.num_sectors[track] * SIZEOF_CDDA_SECTOR; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::openTrack(unsigned int track) +{ + unsigned int starttime, thistime; + + if (track >= (mDevice->toc.num_tracks - 1)) /* -1 for leadout track */ + { + return FMOD_ERR_INVALID_PARAM; + } + + mStartSector = mDevice->toc.start_sector[track]; + mCurrentSector = mStartSector; + mSectorsLeft = mDevice->toc.num_sectors[track]; + mLength = mSectorsLeft * SIZEOF_CDDA_SECTOR; + mReadPtr = 0; + mReadSizebytes = 0; + mJitterBufEmpty = true; + FMOD_memset(mReadBuf, 0, mSectorsInChunk * SIZEOF_CDDA_SECTOR); + + FMOD_OS_Time_GetMs(&thistime); + if ((thistime - mLastTimeAccessed) > (FMOD_CDDA_SPINDOWN_TIME * 1000)) + { + FMOD_OS_CDDA_SetSpeed(mDevice, 4); + + FMOD_OS_Time_GetMs(&starttime); + for (;;) + { + FMOD_OS_Time_GetMs(&thistime); + if ((thistime - starttime) > (FMOD_CDDA_SPINUP_TIME * 1000)) + { + break; + } + + FMOD_OS_CDDA_ReadSectors(mDevice, mReadBuf, mStartSector, 1); + FMOD_OS_Time_Sleep(20); + } + + FMOD_OS_Time_GetMs(&mLastTimeAccessed); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT CddaFile::getMetadata(Metadata **metadata) +{ + if (!mDevice || !metadata) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mGotUserToc) + { + return FMOD_ERR_TAGNOTFOUND; + } + + mMetadata.addTag(FMOD_TAGTYPE_FMOD, "CDTOC", &mDevice->usertoc, sizeof(FMOD_CDTOC), FMOD_TAGDATATYPE_CDTOC, true); + mGotUserToc = true; + *metadata = &mMetadata; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT CddaFile::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_FILE, sizeof(CddaFile)); + + return File::getMemoryUsedImpl(tracker); +} +#endif + +} + +#endif \ No newline at end of file diff --git a/win32/src/fmod_os_cdda.cpp b/win32/src/fmod_os_cdda.cpp new file mode 100755 index 0000000..db39001 --- /dev/null +++ b/win32/src/fmod_os_cdda.cpp @@ -0,0 +1,2809 @@ +#include "fmod_settings.h" + +#include "fmod_memory.h" +#include "fmod_os_cdda.h" +#include "fmod_string.h" +#include "fmod_time.h" +#include "fmod_debug.h" + +#include <windows.h> +#include <stdio.h> +#include <stddef.h> + + +namespace FMOD +{ + + +//============================================================================================= +// Constants +//============================================================================================= +#define SAFE_FREE(x) if ((x)) { FMOD_Memory_Free((x)); (x) = 0; } + +static const char FMOD_CDDA_ASPIDLLNAME[] = "wnaspi32.dll"; +static const char FMOD_CDDA_GETASPI32SUPPORTINFO[] = "GetASPI32SupportInfo"; +static const char FMOD_CDDA_SENDASPI32COMMAND[] = "SendASPI32Command"; + +#define SIZEOF_QSUBCHANNEL 0 +#define SIZEOF_CDROM_SECTOR 2048 +#define SIZEOF_CDAUDIO (SIZEOF_CDDA_SECTOR - SIZEOF_QSUBCHANNEL) +#define USING_NTSCSI (FMOD_CDDA_GetASPI32SupportInfo == FMOD_CDDA_NTSCSI_GetASPI32SupportInfo) + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +#define FILE_ANY_ACCESS 0 +#ifndef FILE_READ_ACCESS + #define FILE_READ_ACCESS (0x0001) +#endif +#ifndef FILE_WRITE_ACCESS + #define FILE_WRITE_ACCESS (0x0002) +#endif + +#define IOCTL_SCSI_BASE 0x00000004 + +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 + +#define CTL_CODE( DevType, Function, Method, Access ) ( \ + ((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) + +#define IOCTL_SCSI_PASS_THROUGH CTL_CODE( IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_MINIPORT CTL_CODE( IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE( IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE( IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_GET_ADDRESS CTL_CODE( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS ) + +/* + Peripheral device type definitions +*/ +#define DTYPE_WORM 0x04 // Write-once read-multiple +#define DTYPE_CDROM 0x05 // CD-ROM device +#define DTYPE_UNKNOWN 0x1F // Unknown or no device type + +/* + SCSI commands +*/ +#define SCSI_TST_U_RDY 0x00 // Test Unit Ready (MANDATORY) +#define SCSI_INQUIRY 0x12 // Inquiry (MANDATORY) +#define SCSI_MODE_SEN6 0x1A // Mode Sense 6-byte (Device Specific) +#define SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E // Prevent/Allow Medium Removal (O) +#define SCSI_READ_TOC 0x43 // Read TOC (O) +#define SCSI_MODE_SEN10 0x5A // Mode Sense 10-byte (Device Specific) +#define SCSI_READ_MMC 0xBE +#define SCSI_SET_SPEED 0xBB + +/* + SCSI mode page codes for use with SCSI_MODE_SEN6 and SCSI_MODE_SEN10 commands +*/ +#define SCSI_MODEPAGE_ERROR_RECOVERY 0x01 // Read/write error recovery page +#define SCSI_MODEPAGE_CDPARAMETERS 0x0D // CD parameters page +#define SCSI_MODEPAGE_CDAUDIO_CONTROL 0x0E // CD audio control page +#define SCSI_MODEPAGE_POWER_CONDITION 0x1A // Power condition page +#define SCSI_MODEPAGE_TIMEOUT_AND_PROTECT 0x1D // Timeout and protect page +#define SCSI_MODEPAGE_CAPS 0x2A // CD capabilities and mechanism status page +#define SCSI_MODEPAGE_ALL 0x3F // All mode pages + +#define SCSI_READ_TOC_FORMAT_CDTEXT 5 + +#define SENSE_LEN 14 // Default sense buffer length +#define SRB_DIR_SCSI 0x00 // Direction determined by SCSI +#define SRB_POSTING 0x01 // Enable ASPI posting +#define SRB_ENABLE_RESIDUAL_COUNT 0x04 // Enable residual byte count reporting +#define SRB_DIR_IN 0x08 // Transfer from SCSI target to host +#define SRB_DIR_OUT 0x10 // Transfer from host to SCSI target +#define SRB_EVENT_NOTIFY 0x40 // Enable ASPI event notification + +/* + ASPI Commands +*/ +#define SC_HA_INQUIRY 0x00 // Host adapter inquiry +#define SC_GET_DEV_TYPE 0x01 // Get device type +#define SC_EXEC_SCSI_CMD 0x02 // Execute SCSI command +#define SC_ABORT_SRB 0x03 // Abort an SRB +#define SC_RESET_DEV 0x04 // SCSI bus device reset +#define SC_SET_HA_PARMS 0x05 // Set HA parameters +#define SC_GET_DISK_INFO 0x06 // Get Disk information +#define SC_RESCAN_SCSI_BUS 0x07 // Rebuild SCSI device map +#define SC_GETSET_TIMEOUTS 0x08 // Get/Set target timeouts + +/* + SRB Status +*/ +#define SS_PENDING 0x00 // SRB being processed +#define SS_COMP 0x01 // SRB completed without error +#define SS_ABORTED 0x02 // SRB aborted +#define SS_ABORT_FAIL 0x03 // Unable to abort SRB +#define SS_ERR 0x04 // SRB completed with error +#define SS_INVALID_CMD 0x80 // Invalid ASPI command +#define SS_INVALID_HA 0x81 // Invalid host adapter number +#define SS_NO_DEVICE 0x82 // SCSI device not installed +#define SS_INVALID_SRB 0xE0 // Invalid parameter set in SRB +#define SS_OLD_MANAGER 0xE1 // ASPI manager doesn't support Windows +#define SS_BUFFER_ALIGN 0xE1 // Buffer not aligned (replaces OLD_MANAGER in Win32) +#define SS_ILLEGAL_MODE 0xE2 // Unsupported Windows mode +#define SS_NO_ASPI 0xE3 // No ASPI managers resident +#define SS_FAILED_INIT 0xE4 // ASPI for windows failed init +#define SS_ASPI_IS_BUSY 0xE5 // No resources available to execute cmd +#define SS_BUFFER_TO_BIG 0xE6 // Buffer size to big to handle! +#define SS_MISMATCHED_COMPONENTS 0xE7 // The DLLs/EXEs of ASPI don't version check +#define SS_NO_ADAPTERS 0xE8 // No host adapters to manage +#define SS_INSUFFICIENT_RESOURCES 0xE9 // Couldn't allocate resources needed to init +#define SS_ASPI_IS_SHUTDOWN 0xEA // Call came to ASPI after PROCESS_DETACH +#define SS_BAD_INSTALL 0xEB // The DLL or other components are installed wrong + + +//============================================================================================= +// Typedefs +//============================================================================================= +#ifdef FMOD_SUPPORT_PRAGMAPACK + #pragma pack(1) +#endif + +struct SRB +{ + BYTE SRB_Cmd; + BYTE SRB_Status; + BYTE SRB_HaId; + BYTE SRB_Flags; + DWORD SRB_Hdr_Rsvd; +}; + +typedef struct // Offset +{ // HX/DEC + BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY + BYTE SRB_Status; // 01/001 ASPI command status byte + BYTE SRB_HaId; // 02/002 ASPI host adapter number + BYTE SRB_Flags; // 03/003 ASPI request flags + DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 + BYTE HA_Count; // 08/008 Number of host adapters present + BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter + BYTE HA_ManagerId[16]; // 0A/010 String describing the manager + BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter + BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters + WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0 +} +SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry; + +typedef struct // Offset +{ // HX/DEC + BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GET_DEV_TYPE + BYTE SRB_Status; // 01/001 ASPI command status byte + BYTE SRB_HaId; // 02/002 ASPI host adapter number + BYTE SRB_Flags; // 03/003 Reserved, MUST = 0 + DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 + BYTE SRB_Target; // 08/008 Target's SCSI ID + BYTE SRB_Lun; // 09/009 Target's LUN number + BYTE SRB_DeviceType; // 0A/010 Target's peripheral device type + BYTE SRB_Rsvd1; // 0B/011 Reserved, MUST = 0 +} +SRB_GDEVBlock, *PSRB_GDEVBlock, FAR *LPSRB_GDEVBlock; + +typedef struct // Offset +{ // HX/DEC + BYTE SRB_Cmd; // 00/000 ASPI command code = SC_EXEC_SCSI_CMD + BYTE SRB_Status; // 01/001 ASPI command status byte + BYTE SRB_HaId; // 02/002 ASPI host adapter number + BYTE SRB_Flags; // 03/003 ASPI request flags + DWORD SRB_Hdr_Rsvd; // 04/004 Reserved + BYTE SRB_Target; // 08/008 Target's SCSI ID + BYTE SRB_Lun; // 09/009 Target's LUN number + WORD SRB_Rsvd1; // 0A/010 Reserved for Alignment + DWORD SRB_BufLen; // 0C/012 Data Allocation Length + BYTE FAR *SRB_BufPointer; // 10/016 Data Buffer Pointer + BYTE SRB_SenseLen; // 14/020 Sense Allocation Length + BYTE SRB_CDBLen; // 15/021 CDB Length + BYTE SRB_HaStat; // 16/022 Host Adapter Status + BYTE SRB_TargStat; // 17/023 Target Status + VOID FAR *SRB_PostProc; // 18/024 Post routine + BYTE SRB_Rsvd2[20]; // 1C/028 Reserved, MUST = 0 + BYTE CDBByte[16]; // 30/048 SCSI CDB + BYTE SenseArea[SENSE_LEN+2]; // 50/064 Request Sense buffer +} +SRB_ExecSCSICmd, *PSRB_ExecSCSICmd, FAR *LPSRB_ExecSCSICmd; + +typedef struct // Offset +{ // HX/DEC + BYTE SRB_Cmd; // 00/000 ASPI command code = SC_ABORT_SRB + BYTE SRB_Status; // 01/001 ASPI command status byte + BYTE SRB_HaId; // 02/002 ASPI host adapter number + BYTE SRB_Flags; // 03/003 Reserved + DWORD SRB_Hdr_Rsvd; // 04/004 Reserved + VOID FAR *SRB_ToAbort; // 08/008 Pointer to SRB to abort +} +SRB_Abort, *PSRB_Abort, FAR *LPSRB_Abort; + +#ifdef FMOD_SUPPORT_PRAGMAPACK + #ifdef CODEWARRIOR + #pragma pack(0) + #else + #pragma pack() + #endif +#endif + +typedef struct +{ + unsigned char ha; + unsigned char tgt; + unsigned char lun; + unsigned char letter; + bool used; + HANDLE device; + FMOD_CDDA_InquiryData inq_data; + +} FMOD_CDDA_NTSCSIDRIVE; + +typedef struct +{ + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH; + +typedef struct +{ + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + PVOID DataBuffer; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH_DIRECT; + +typedef struct +{ + SCSI_PASS_THROUGH_DIRECT spt; + ULONG Filler; + UCHAR ucSenseBuf[32]; +} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; + +typedef struct +{ + ULONG Length; + UCHAR PortNumber; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; +} SCSI_ADDRESS; + +typedef void (*POSTPROCFUNC)(); + +typedef struct +{ + unsigned char session; + + unsigned char pre_emphasis : 1; + unsigned char digital_copy_permitted : 1; + unsigned char data_track : 1; + unsigned char four_channel : 1; + unsigned char adr : 4; + + unsigned char tno; + unsigned char point; + unsigned char min; + unsigned char sec; + unsigned char frame; + unsigned char zero; + unsigned char pmin; + unsigned char psec; + unsigned char pframe; + +} FMOD_CDDA_RawTocEntry; + +typedef struct +{ + unsigned char datalen_hi; + unsigned char datalen_lo; + unsigned char first_session; + unsigned char last_session; + FMOD_CDDA_RawTocEntry toc_entry[1]; + +} FMOD_CDDA_RawTocDesc; + + +static char FMOD_CDDA_StrUnknown[] = "Unknown"; + +FMOD_CDDA_StringList FMOD_CDDA_Aspi_Strings[] = +{ + { SS_PENDING, "SRB being processed" }, + { SS_COMP, "SRB completed without error" }, + { SS_ABORTED, "SRB aborted" }, + { SS_ABORT_FAIL, "Unable to abort SRB" }, + { SS_ERR, "SRB completed with error" }, + { SS_INVALID_CMD, "Invalid ASPI command" }, + { SS_INVALID_HA, "Invalid host adapter number" }, + { SS_NO_DEVICE, "SCSI device not installed" }, + { SS_INVALID_SRB, "Invalid parameter set in SRB" }, + { SS_OLD_MANAGER, "ASPI manager doesn't support Windows" }, + { SS_BUFFER_ALIGN, "Buffer not aligned" }, + { SS_ILLEGAL_MODE, "Unsupported Windows mode" }, + { SS_NO_ASPI, "No ASPI managers resident" }, + { SS_FAILED_INIT, "ASPI for windows failed init" }, + { SS_ASPI_IS_BUSY, "No resources available to execute cmd" }, + { SS_BUFFER_TO_BIG, "Buffer size too big to handle!" }, + { SS_MISMATCHED_COMPONENTS, "The DLLs/EXEs of ASPI don't version check" }, + { SS_NO_ADAPTERS, "No host adapters to manage" }, + { SS_INSUFFICIENT_RESOURCES, "Couldn't allocate resources needed to init" }, + { SS_ASPI_IS_SHUTDOWN, "Call came to ASPI after PROCESS_DETACH" }, + { SS_BAD_INSTALL, "The DLL or other components are installed wrong" }, + { -1, (char *)-1 } +}; + + +//============================================================================================= +// Function declarations +//============================================================================================= +FMOD_RESULT FMOD_CDDA_Close(FMOD_CDDA_DEVICE *cdrom); +bool FMOD_CDDA_InquiryCommand(unsigned char adapter_id, unsigned char target_id, unsigned char lun_id); +bool FMOD_CDDA_PreventMediaRemoval(FMOD_CDDA_DEVICE *cdrom, bool prevent); +int FMOD_CDDA_IssueScsiCmd(FMOD_CDDA_DEVICE *cdrom, int flags, unsigned char *cdb_data, int cdb_len, char *buf, int buflen); + +FMOD_RESULT FMOD_CDDA_NTSCSI_Init(); +bool FMOD_CDDA_NTSCSI_Shutdown(); +DWORD FMOD_CDDA_NTSCSI_GetASPI32SupportInfo(); +DWORD FMOD_CDDA_NTSCSI_SendASPI32Command(SRB *srb); +int FMOD_CDDA_NTSCSI_GetDeviceIndex(unsigned char ha, unsigned char tgt, unsigned char lun); + + +//============================================================================================= +// Variables +//============================================================================================= +static bool FMOD_CDDA_Initialised = false; +static FUNC_GETASPI32SUPPORTINFO FMOD_CDDA_GetASPI32SupportInfo = 0; +static FUNC_SENDASPI32COMMAND FMOD_CDDA_SendASPI32Command = 0; +static HINSTANCE FMOD_CDDA_DllHandle = 0; +static int FMOD_CDDA_NumDevices = 0; +static unsigned int FMOD_CDDA_Aspi_Timeout = 10000; +static FMOD_CDDA_DEVICE *FMOD_CDDA_DeviceList[FMOD_CDDA_MAX_DEVICES]; +static bool FMOD_CDDA_NTSCSI_Initialised = false; +static FMOD_CDDA_NTSCSIDRIVE *FMOD_CDDA_NTSCSI_Drive = 0; +static int FMOD_CDDA_NTSCSI_NumAdapters; +static int FMOD_CDDA_NTSCSI_AdapterLookup[FMOD_CDDA_MAX_DEVICES]; +static int FMOD_CDDA_FakeLun = 0; + + +#ifdef FMOD_OS_CDDA_DEBUG +void FMOD_OS_CDDA_DebugInit() +{ + FILE *fp = fopen(FMOD_OS_CDDA_LOG_FILENAME, "wt"); + if (fp) + { + OSVERSIONINFO ver; + + fprintf(fp, "FMOD Version:\t%d.%d.%d\n", (((unsigned int)FMOD_VERSION) >> 16) & 0xffff, (((unsigned int)FMOD_VERSION) >> 8) & 0xff, FMOD_VERSION & 0xff); + ver.dwOSVersionInfoSize = sizeof(ver); + fprintf(fp, "OS Version:\t"); + if (GetVersionEx(&ver)) + { + signed char is95 = ((ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (ver.dwMinorVersion == 0)); + fprintf(fp, "%d.%d Build %d %s\n", ver.dwMajorVersion, + ver.dwMinorVersion, + is95 ? (ver.dwBuildNumber & 0xffff) : ver.dwBuildNumber, + ver.szCSDVersion); + } + fclose(fp); + } +} +void FMOD_OS_CDDA_DebugPrint(char *format, ...) +{ + FILE *fp = 0; + va_list arglist; + va_start(arglist, format); + for (;;) + { + fp = fopen(FMOD_OS_CDDA_LOG_FILENAME, "at"); + if (fp) + { + break; + } + else + { + Sleep(10); + } + } + vfprintf(fp, format, arglist); + fclose(fp); + va_end(arglist); +} +void FMOD_OS_CDDA_AddProfileData(int elapsedtime, unsigned int bytesread) +{ + if (elapsedtime > 0) + { + int bytespersecond = (int)(bytesread / ((float)elapsedtime / 1000.0f)); + FMOD_OS_CDDA_DEBUGPRINT(" %d kb/s\n", bytespersecond / 1024); + } +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +char *FMOD_CDDA_GetString(unsigned int value, FMOD_CDDA_StringList *stringlist) +{ + int i; + + for (i=0;stringlist[i].string != (char *)-1;i++) + { + if (stringlist[i].value == value) + { + return stringlist[i].string; + } + } + + return FMOD_CDDA_StrUnknown; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CDDA_Init(bool force_aspi) +{ + unsigned int status, error; + int num_ha, adapter_id, lun_id, target_id, i; + bool ntscsi_ok = false; + + if (FMOD_CDDA_Initialised) + { + return FMOD_OK; + } + + FMOD_OS_CDDA_DEBUGINIT; + + for (i=0;i < FMOD_CDDA_MAX_DEVICES;i++) + { + FMOD_CDDA_DeviceList[i] = 0; + } + FMOD_CDDA_NumDevices = 0; + + /* + Try using NTSCSI first + */ + if (!force_aspi) + { + if (FMOD_CDDA_NTSCSI_Init() == FMOD_OK) + { + FMOD_CDDA_GetASPI32SupportInfo = FMOD_CDDA_NTSCSI_GetASPI32SupportInfo; + FMOD_CDDA_SendASPI32Command = FMOD_CDDA_NTSCSI_SendASPI32Command; + FMOD_OS_CDDA_DEBUGPRINT("Using NTSCSI\n"); + ntscsi_ok = true; + } + } + + if (!ntscsi_ok) + { + /* + Then try using ASPI + */ + char dllname[2048]; + + for (i=0;i < FMOD_CDDA_MAX_DEVICES;i++) + { + FMOD_CDDA_DeviceList[i] = 0; + } + FMOD_CDDA_NumDevices = 0; + + FMOD_strcpy(dllname, FMOD_CDDA_ASPIDLLNAME); + if (!(FMOD_CDDA_DllHandle = LoadLibrary(dllname))) + { + GetSystemDirectory(dllname, 2047); + FMOD_strcat(dllname, "\\"); + FMOD_strcat(dllname, FMOD_CDDA_ASPIDLLNAME); + + if (!(FMOD_CDDA_DllHandle = LoadLibrary(dllname))) + { + GetWindowsDirectory(dllname, 2047); + FMOD_strcat(dllname, "\\"); + FMOD_strcat(dllname, FMOD_CDDA_ASPIDLLNAME); + FMOD_CDDA_DllHandle = LoadLibrary(dllname); + } + } + + if (!FMOD_CDDA_DllHandle) + { + FMOD_OS_CDDA_Shutdown(); + return FMOD_ERR_CDDA_DRIVERS; + } + + FMOD_CDDA_GetASPI32SupportInfo = (FUNC_GETASPI32SUPPORTINFO)GetProcAddress(FMOD_CDDA_DllHandle, FMOD_CDDA_GETASPI32SUPPORTINFO); + FMOD_CDDA_SendASPI32Command = (FUNC_SENDASPI32COMMAND)GetProcAddress(FMOD_CDDA_DllHandle, FMOD_CDDA_SENDASPI32COMMAND); + + if (!FMOD_CDDA_GetASPI32SupportInfo || !FMOD_CDDA_SendASPI32Command) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_Init: GetProcAddress failed!\n"); + FMOD_OS_CDDA_Shutdown(); + + return FMOD_ERR_CDDA_DRIVERS; + } + + FMOD_OS_CDDA_DEBUGPRINT("Using ASPI\n"); + } + + status = FMOD_CDDA_GetASPI32SupportInfo(); + error = HIBYTE(LOWORD(status)); + if (error == SS_COMP) + { + num_ha = LOBYTE(LOWORD(status)); + } + else + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_Init: ASPI/NTSCSI didn't startup properly : %s\n", FMOD_CDDA_GetString(error, FMOD_CDDA_Aspi_Strings)); + FMOD_OS_CDDA_Shutdown(); + + return FMOD_ERR_CDDA_INIT; + } + + /* + Get info on all available cdrom drives + */ + for (adapter_id=0;adapter_id < 16;adapter_id++) + { + for (target_id=0;target_id < 12;target_id++) + { + for (lun_id=0;lun_id < 8;lun_id++) + { + SRB_GDEVBlock srb; + unsigned int start_time; + + FMOD_memset(&srb, 0, sizeof(srb)); + + srb.SRB_Cmd = SC_GET_DEV_TYPE; + srb.SRB_HaId = adapter_id; + srb.SRB_Target = target_id; + srb.SRB_Lun = lun_id; + + FMOD_CDDA_SendASPI32Command((SRB *)&srb); + + start_time = GetTickCount(); + while ((srb.SRB_Status == SS_PENDING) && ((GetTickCount() - start_time) < 10000)) + { + FMOD_OS_Time_Sleep(1); + } + +#ifdef FMOD_OS_CDDA_DEBUG + switch (srb.SRB_DeviceType) + { + case DTYPE_WORM : + FMOD_OS_CDDA_DEBUGPRINT("%02d:%02d:%02d = DTYPE_WORM", adapter_id, target_id, lun_id); + break; + case DTYPE_CDROM : + FMOD_OS_CDDA_DEBUGPRINT("%02d:%02d:%02d = DTYPE_CDROM", adapter_id, target_id, lun_id); + break; + } +#endif + + if ((srb.SRB_DeviceType == DTYPE_CDROM) || (srb.SRB_DeviceType == DTYPE_WORM)) + { + FMOD_CDDA_InquiryCommand(adapter_id, target_id, lun_id); + +#ifdef FMOD_OS_CDDA_DEBUG + if (USING_NTSCSI) + { + char letter = FMOD_CDDA_NTSCSI_GetDeviceIndex(adapter_id, target_id, lun_id); + if (letter) + { + FMOD_OS_CDDA_DEBUGPRINT(" = %c:\n", letter + 'A'); + } + else + { + FMOD_OS_CDDA_DEBUGPRINT(" = ???:\n"); + } + } + else + { + FMOD_OS_CDDA_DEBUGPRINT("\n"); + } +#endif + } + } + } + } + + if (FMOD_CDDA_NumDevices < 1) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_Init: no cdrom devices found\n"); + FMOD_OS_CDDA_Shutdown(); + + return FMOD_ERR_CDDA_NODEVICES; + } + + FMOD_CDDA_Initialised = true; + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_Init: Ok\n"); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CDDA_Shutdown() +{ + int i; + + if (!FMOD_CDDA_Initialised) + { + return FMOD_OK; + } + + for (i=0;i < FMOD_CDDA_MAX_DEVICES;i++) + { + if (FMOD_CDDA_DeviceList[i]) + { + FMOD_CDDA_Close(FMOD_CDDA_DeviceList[i]); + FMOD_Memory_Free(FMOD_CDDA_DeviceList[i]); + FMOD_CDDA_DeviceList[i] = 0; + } + } + + if (FMOD_CDDA_DllHandle) + { + FreeLibrary(FMOD_CDDA_DllHandle); + FMOD_CDDA_DllHandle = 0; + } + + FMOD_CDDA_NTSCSI_Shutdown(); + + FMOD_CDDA_GetASPI32SupportInfo = 0; + FMOD_CDDA_SendASPI32Command = 0; + + FMOD_CDDA_Initialised = false; + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +bool FMOD_OS_CDDA_IsDriveLetter(char *name) +{ + if (((name[1] == ':') && (name[2] == 0)) || ((name[2] == ':') && (name[4] == 0))) + { + /* + Drive letter style - "d:" (or wide char) + */ + if (FMOD_isalpha(name[0])) + { + return true; + } + } + + return false; +} + + +bool FMOD_OS_CDDA_IsDeviceName(char *name) +{ + if (!name) + { + return false; + } + + if (FMOD_OS_CDDA_IsDriveLetter(name)) + { + return true; + } + else if ((name[2] == ':') && (name[5] == ':') && (name[8] == 0)) + { + /* + SCSI address style - "00:01:00" + */ + if (FMOD_isdigit(name[0]) && FMOD_isdigit(name[1]) && + FMOD_isdigit(name[3]) && FMOD_isdigit(name[4]) && + FMOD_isdigit(name[6]) && FMOD_isdigit(name[7])) + { + return true; + } + } + + return false; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CDDA_GetNumDevices(int *num) +{ + FMOD_RESULT result; + + if (!FMOD_CDDA_Initialised) + { + result = FMOD_OS_CDDA_Init(false); + if (result != FMOD_OK) + { + return result; + } + } + + if (!num) + { + return FMOD_ERR_INVALID_PARAM; + } + + *num = FMOD_CDDA_NumDevices; + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CDDA_GetDeviceName(int devicenum, char *name, int namelen, char *scsiaddr, int scsiaddrlen, char *devicename, int devicenamelen) +{ + FMOD_RESULT result; + FMOD_CDDA_DEVICE *device; + + if (!FMOD_CDDA_Initialised) + { + result = FMOD_OS_CDDA_Init(false); + if (result != FMOD_OK) + { + return result; + } + } + + device = FMOD_CDDA_DeviceList[devicenum]; + if (device) + { + if (name && namelen) + { + FMOD_strncpy(name, device->name, namelen); + } + + if (scsiaddr && scsiaddrlen) + { + FMOD_strncpy(scsiaddr, device->scsiaddr, scsiaddrlen); + } + + if (devicename && devicenamelen) + { + FMOD_strncpy(devicename, device->devicename, devicenamelen); + } + } + else + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CDDA_GetDriveFromLetter(int letter, FMOD_CDDA_DEVICE **device) +{ + int i; + FMOD_CDDA_DEVICE *d; + + letter -= ((letter > 'Z') ? 'a' : 'A'); + + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: %c:\n", letter + 'a'); + + if (!device) + { + return FMOD_ERR_INVALID_PARAM; + } + *device = 0; + + for (i=0; i < FMOD_CDDA_NumDevices; i++) + { + d = FMOD_CDDA_DeviceList[i]; + if (d->letter == letter) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Already mapped: %c: == %02d:%02d:%02d\n", d->letter + 'a', d->adapter_id, d->target_id, d->lun_id); + *device = d; + return FMOD_OK; + } + } + + /* + If we're using ASPI we have to try to map the specified drive letter to a SCSI id to + find what drive we're after + */ + if (!USING_NTSCSI) + { + MCI_OPEN_PARMS mciOpenParms; + MCI_STATUS_PARMS mciStatusParms; + MCIERROR err; + MCIDEVICEID device_id; + char drive_letter[] = "c:"; + int numtracks, count; + int mci_toc[99], aspi_toc[99]; + + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Finding SCSI ID for %c:\n", letter + 'a'); + + /* + If there's only one drive found by ASPI then it's probably our man + */ + if (FMOD_CDDA_NumDevices == 1) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Only one drive\n"); + + /* + Make sure they specified a valid drive letter though + */ + FMOD_memset(&mciOpenParms, 0, sizeof(MCI_OPEN_PARMS)); + mciOpenParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + drive_letter[0] = letter + 'A'; + mciOpenParms.lpstrElementName = drive_letter; + err = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&mciOpenParms); + if (err) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Open failed\n"); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + device_id = mciOpenParms.wDeviceID; + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + + d = FMOD_CDDA_DeviceList[0]; + d->letter = letter; + *device = d; + return FMOD_OK; + } + + /* + If there's only one drive left unmapped then it's probably our man + */ + { + int unmapped = 0; + + for (i=0;i < FMOD_CDDA_NumDevices;i++) + { + if (FMOD_CDDA_DeviceList[i]->letter == 0) + { + unmapped++; + } + } + + if (unmapped == 1) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Only one drive unmapped\n"); + + for (i=0;i < FMOD_CDDA_NumDevices;i++) + { + if (FMOD_CDDA_DeviceList[i]->letter == 0) + { + /* + Make sure they specified a valid drive letter though + */ + FMOD_memset(&mciOpenParms, 0, sizeof(MCI_OPEN_PARMS)); + mciOpenParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + drive_letter[0] = letter + 'A'; + mciOpenParms.lpstrElementName = drive_letter; + err = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&mciOpenParms); + if (err) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Open failed\n"); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + device_id = mciOpenParms.wDeviceID; + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + + d = FMOD_CDDA_DeviceList[i]; + d->letter = letter; + *device = d; + return FMOD_OK; + } + } + } + } + + /* + Read the TOC using MCI + */ + FMOD_memset(mci_toc, 0, sizeof(int) * 99); + FMOD_memset(aspi_toc, 0, sizeof(int) * 99); + + FMOD_memset(&mciOpenParms, 0, sizeof(MCI_OPEN_PARMS)); + mciOpenParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + drive_letter[0] = letter + 'A'; + mciOpenParms.lpstrElementName = drive_letter; + err = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&mciOpenParms); + if (err) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Open failed\n"); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + device_id = mciOpenParms.wDeviceID; + + FMOD_memset(&mciStatusParms, 0, sizeof(MCI_STATUS_PARMS)); + mciStatusParms.dwItem = MCI_STATUS_READY; + err = mciSendCommand(device_id, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID) &mciStatusParms); + if (err || (mciStatusParms.dwReturn == FALSE)) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Status ready failed\n"); + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + + FMOD_memset(&mciStatusParms, 0, sizeof(MCI_STATUS_PARMS)); + mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + err = mciSendCommand(device_id, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&mciStatusParms); + if (err || (mciStatusParms.dwReturn == 0)) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Number of tracks failed\n"); + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + numtracks = (int)mciStatusParms.dwReturn; + + for (count = 1; count <= numtracks; count++) + { + FMOD_memset(&mciStatusParms, 0, sizeof(MCI_STATUS_PARMS)); + mciStatusParms.dwItem = MCI_STATUS_POSITION; + mciStatusParms.dwTrack = count; + err = mciSendCommand(device_id, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms); + if (err) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Position failed\n"); + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + mci_toc[count - 1] = (MCI_MSF_MINUTE(mciStatusParms.dwReturn) * 60 * 75) + + (MCI_MSF_SECOND(mciStatusParms.dwReturn) * 75) + + MCI_MSF_FRAME(mciStatusParms.dwReturn); + } + + FMOD_memset(&mciStatusParms, 0, sizeof(MCI_STATUS_PARMS)); + mciStatusParms.dwItem = MCI_STATUS_LENGTH; + mciStatusParms.dwTrack = numtracks; + err = mciSendCommand(device_id, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms); + if (err) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Length failed\n"); + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + return FMOD_ERR_CDDA_INVALID_DEVICE; + } + mci_toc[numtracks] = mci_toc[numtracks - 1] + (MCI_MSF_MINUTE(mciStatusParms.dwReturn) * 60 * 75) + + (MCI_MSF_SECOND(mciStatusParms.dwReturn) * 75) + + MCI_MSF_FRAME(mciStatusParms.dwReturn) + 1; + numtracks++; + + mciSendCommand(device_id, MCI_CLOSE, 0, 0); + +#ifdef FMOD_OS_CDDA_DEBUG + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: MCI\n"); + for (count = 0; count < numtracks; count++) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: %d. %d\n", count, mci_toc[count]); + } +#endif + + /* + Read the TOC of all unmapped drives using ASPI and compare it to the MCI TOC + */ + for (i=0;i < FMOD_CDDA_NumDevices;i++) + { + d = FMOD_CDDA_DeviceList[i]; + if (d->letter == 0) + { + FMOD_CDDA_TOC toc; + int aspi_numtracks = 0; + + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Trying %s\n", d->scsiaddr); + + if (FMOD_OS_CDDA_ReadTocRaw(d, &toc) != FMOD_OK) + { + if (FMOD_OS_CDDA_ReadToc(d, &toc) != FMOD_OK) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: ReadToc/Raw failed\n"); + continue; + } + } + + for (count = 0; count < (int)(toc.num_tracks - 1); count++) + { + aspi_toc[aspi_numtracks++] = toc.start_sector[count] + 150; + } + aspi_toc[aspi_numtracks++] = toc.start_sector[count]; + +#ifdef FMOD_OS_CDDA_DEBUG + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: ASPI\n"); + for (count = 0; count < aspi_numtracks; count++) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: %d. %d\n", count, aspi_toc[count]); + } +#endif + + if (aspi_numtracks != numtracks) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Different number of tracks\n"); + continue; + } + + { + bool goto_next_drive = false; + + for (count = 0; count < numtracks; count++) + { + if (mci_toc[count] != aspi_toc[count]) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Different TOC\n"); + goto_next_drive = true; + break; + } + } + + if (goto_next_drive) + { + continue; + } + } + + d->letter = letter; + *device = d; + + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: %c: == %02d:%02d:%02d\n", d->letter + 'a', d->adapter_id, d->target_id, d->lun_id); + + return FMOD_OK; + } + } + } + + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_GetDriveFromLetter: Failed finding %c:\n", letter + 'a'); + + return FMOD_ERR_CDDA_INVALID_DEVICE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_CDDA_OpenDevice(char *name, FMOD_CDDA_DEVICE **device) +{ + int count; + FMOD_CDDA_DEVICE *d = 0; + + if (!device) + { + return FMOD_ERR_INVALID_PARAM; + } + + for (count = 0; count < FMOD_CDDA_MAX_DEVICES; count++) + { + d = FMOD_CDDA_DeviceList[count]; + if (d) + { + if (d->name) + { + if (!FMOD_stricmp(name, d->name)) + { + break; + } + } + + if (d->scsiaddr) + { + if (!FMOD_strcmp(name, d->scsiaddr)) + { + break; + } + } + + if (d->devicename) + { + if (!FMOD_strcmp(name, d->devicename)) + { + break; + } + } + } + } + if (count >= FMOD_CDDA_MAX_DEVICES) + { + d = 0; + + if (FMOD_OS_CDDA_IsDriveLetter(name)) + { + FMOD_OS_CDDA_GetDriveFromLetter(name[0], &d); + } + } + + if (d) + { + *device = d; + d->device_open++; + } + else + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_OpenDevice: Couldn't access device %s\n", name); + *device = 0; + return FMOD_ERR_FILE_NOTFOUND; + } + + FMOD_OS_CDDA_DEBUGPRINT("FMOD_OS_CDDA_OpenDevice: Opened %s\n", name); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_CDDA_CloseDevice(FMOD_CDDA_DEVICE *device) +{ + if (!device) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (FMOD_CDDA_NTSCSI_Drive) + { + if (FMOD_CDDA_NTSCSI_Drive[device->letter].used && (FMOD_CDDA_NTSCSI_Drive[device->letter].device != INVALID_HANDLE_VALUE)) + { + CloseHandle(FMOD_CDDA_NTSCSI_Drive[device->letter].device); + FMOD_CDDA_NTSCSI_Drive[device->letter].device = INVALID_HANDLE_VALUE; + } + } + + device->device_open--; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_CDDA_ReadSectors(FMOD_CDDA_DEVICE *device, char *buf, unsigned int start_sector, unsigned int num_sectors) +{ + unsigned char cmd[12]; + + if (!device) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memset(buf, 0, num_sectors * SIZEOF_CDDA_SECTOR); + FMOD_memset(cmd, 0, sizeof(cmd)); + + /* + Could support other read methods here i.e. MMC2/3/4, proprietory ones etc. + */ + cmd[0] = SCSI_READ_MMC; + cmd[1] = device->lun_id << 5; + cmd[2] = (unsigned char)(start_sector >> 24); + cmd[3] = (unsigned char)((start_sector >> 16) & 0xff); + cmd[4] = (unsigned char)((start_sector >> 8) & 0xff); + cmd[5] = (unsigned char)(start_sector & 0xff); + cmd[7] = (unsigned char)((num_sectors >> 8) & 0xff); + cmd[8] = (unsigned char)(num_sectors & 0xff); + cmd[9] = 0xF8; // Vendor specific byte + + if (FMOD_CDDA_IssueScsiCmd(device, SRB_DIR_IN, cmd, sizeof(cmd), buf, num_sectors * SIZEOF_CDDA_SECTOR) != SS_COMP) + { + return FMOD_ERR_CDDA_READ; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +static unsigned int FMOD_OS_CDDA_hmsf_to_lba(unsigned char hours, unsigned char minutes, unsigned char seconds, unsigned char frames) +{ + return (hours * 1053696) + (minutes * 60 * 75) + (seconds * 75) + frames; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void FMOD_CDDA_AddUserTocEntry(FMOD_CDDA_DEVICE *device, unsigned int sector) +{ + device->usertoc.min[device->usertoc.numtracks] = sector / (60 * 75); + device->usertoc.sec[device->usertoc.numtracks] = ((sector % (60 * 75)) / 75); + device->usertoc.frame[device->usertoc.numtracks] = sector % 75; + device->usertoc.numtracks++; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_CDDA_ReadTocRaw(FMOD_CDDA_DEVICE *device, FMOD_CDDA_TOC *toc) +{ + char buf[SIZEOF_CDROM_SECTOR]; + int num_tracks, datasize, first_track, last_track, session, i; + unsigned int leadout_start, leadout_start_orig, first_session_leadout, max_leadout, last_start; + unsigned char cmd[10]; + FMOD_CDDA_RawTocDesc *t = (FMOD_CDDA_RawTocDesc *)buf; + FMOD_CDDA_RawTocEntry *toc_entry; + + if (!device || !toc) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memset(buf, 0, sizeof(buf)); + FMOD_memset(toc, 0, sizeof(FMOD_CDDA_TOC)); + if (toc == &device->toc) + { + FMOD_memset(&device->usertoc, 0, sizeof(FMOD_CDTOC)); + } + + cmd[0] = SCSI_READ_TOC; // Operation code + cmd[1] = 0; // TIME | Reserved + cmd[2] = 2; // Format | Reserved + cmd[3] = 0; // Reserved + cmd[4] = 0; // Reserved + cmd[5] = 0; // Reserved + cmd[6] = 1; // Track/session number + cmd[7] = SIZEOF_CDROM_SECTOR >> 8; // Allocation length msb + cmd[8] = SIZEOF_CDROM_SECTOR & 0xff; // Allocation length lsb + cmd[9] = 0; // Control + + if (FMOD_CDDA_IssueScsiCmd(device, SRB_DIR_IN, cmd, sizeof(cmd), buf, SIZEOF_CDROM_SECTOR) != SS_COMP) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "SCSI cmd failed\n")); + return FMOD_ERR_CDDA_NODISC; + } + + datasize = (t->datalen_hi << 8) | (t->datalen_lo - 2); + if ((datasize < (4 * sizeof(FMOD_CDDA_RawTocEntry))) || ((datasize % sizeof(FMOD_CDDA_RawTocEntry)) != 0)) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "Wrong amount of data returned\n")); + return FMOD_ERR_CDDA_READ; + } + + num_tracks = datasize / sizeof(FMOD_CDDA_RawTocEntry); + first_track = last_track = session = 0; + leadout_start = leadout_start_orig = first_session_leadout = max_leadout = last_start = 0; + + if (num_tracks > 0) + { + toc->num_tracks = 0; + + for (i=0;i < num_tracks;i++) + { + toc_entry = &t->toc_entry[i]; + + if (toc_entry->tno != 0) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d toc_entry->tno = %d\n", i, toc_entry->tno)); + } + + switch (toc_entry->point) + { + // First track number + case 0xA0 : + if ((session + 1) == toc_entry->session) + { + session = toc_entry->session; + } + else + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d session = %d should = %d\n", i, toc_entry->session, session + 1)); + } + + if (toc_entry->adr != 1) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d adr = %d should = 1\n", i, toc_entry->adr)); + } + + if ((first_track < toc_entry->pmin) && (last_track < toc_entry->pmin)) + { + first_track = toc_entry->pmin; + } + else + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d first track >= %d or %d\n", i, toc_entry->pmin, first_track, last_track)); + } + break; + + // Last track number + case 0xA1 : + if (session != toc_entry->session) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d session = %d should = %d\n", i, toc_entry->session, session)); + } + + if (toc_entry->adr != 1) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d adr = %d should = 1\n", i, toc_entry->adr)); + } + + if ((first_track <= toc_entry->pmin) && (last_track < toc_entry->pmin)) + { + last_track = toc_entry->pmin; + } + else + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d last track >= %d or %d\n", i, toc_entry->pmin, first_track, last_track)); + } + break; + + // Start location of leadout area + case 0xA2 : + if (session != toc_entry->session) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d session = %d should = %d\n", i, toc_entry->session, session)); + } + + if (toc_entry->adr != 1) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d adr = %d should = 1\n", i, toc_entry->adr)); + } + + { + unsigned int tmp = FMOD_OS_CDDA_hmsf_to_lba(toc_entry->zero, toc_entry->pmin, toc_entry->psec, toc_entry->pframe); + + if (first_session_leadout == 0) + { + first_session_leadout = tmp - 150; + } + + if (tmp > leadout_start) + { + leadout_start_orig = tmp; + leadout_start = tmp; + } + else + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d leadout start = %d <= %d\n", i, tmp, leadout_start)); + } + } + break; + + // Start time of next possible program area. Used to identify multi-session discs + case 0xB0 : + if (session != toc_entry->session) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d session = %d should = %d\n", i, toc_entry->session, session)); + } + + if (toc_entry->adr != 5) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d adr = %d should = 5\n", i, toc_entry->adr)); + } + + if (FMOD_OS_CDDA_hmsf_to_lba(0, toc_entry->min, toc_entry->sec, toc_entry->frame) < (leadout_start + 6750)) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d next program area = %u < leadout_start + 6750 = %u\n", i, FMOD_OS_CDDA_hmsf_to_lba(0, toc_entry->min, toc_entry->sec, toc_entry->frame), leadout_start + 6750)); + } + + if ((max_leadout != 0) && (FMOD_OS_CDDA_hmsf_to_lba(toc_entry->zero, toc_entry->pmin, toc_entry->psec, toc_entry->pframe) != max_leadout)) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d max leadout = %u != last max leadout = %u\n", i, FMOD_OS_CDDA_hmsf_to_lba(toc_entry->zero, toc_entry->pmin, toc_entry->psec, toc_entry->pframe), max_leadout)); + } + + if (max_leadout == 0) + { + max_leadout = FMOD_OS_CDDA_hmsf_to_lba(toc_entry->zero, toc_entry->pmin, toc_entry->psec, toc_entry->pframe); + } + break; + + // Not interested in these + case 0xB1 : + case 0xB2 : + case 0xB3 : + case 0xB4 : + case 0xB5 : + case 0xB6 : + case 0xC0 : + case 0xC1 : + break; + + // Actual track info + default : + if (session != toc_entry->session) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d session = %d should = %d\n", i, toc_entry->session, session)); + } + + if ((toc_entry->point < first_track) || (toc_entry->point > last_track)) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d track number %d should be between %d and %d\n", i, toc_entry->point, first_track, last_track)); + } + else + { + unsigned int track_start = FMOD_OS_CDDA_hmsf_to_lba(toc_entry->zero, toc_entry->pmin, toc_entry->psec, toc_entry->pframe); + + if (leadout_start < track_start) + { + leadout_start = track_start + 1; + } + + if ((track_start < last_start) || (track_start >= leadout_start)) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_OS_CDDA_ReadTocRaw", "entry %d track start = %u < last_start (%u) or > leadout_start (%u)\n", i, track_start, last_start, leadout_start)); + } + else + { + last_start = track_start; + + *(unsigned char *)(&toc->flags[toc->num_tracks]) = 0; + toc->track_number[toc->num_tracks] = toc_entry->point; + toc->start_sector[toc->num_tracks] = FMOD_OS_CDDA_hmsf_to_lba(toc_entry->zero, toc_entry->pmin, toc_entry->psec, toc_entry->pframe); + toc->start_sector[toc->num_tracks] -= 150; + + if ((toc_entry->point != first_track) && (toc->num_sectors[toc->num_tracks - 1] == 0)) + { + toc->num_sectors[toc->num_tracks - 1] = toc->start_sector[toc->num_tracks] - toc->start_sector[toc->num_tracks - 1]; + } + + if (toc_entry->point == last_track) + { + /* + 06/09/05 AJS - 150 because last audio track was always 2 seconds out + */ + toc->num_sectors[toc->num_tracks] = leadout_start - toc->start_sector[toc->num_tracks] - 150; + } + + if (toc == &device->toc) + { + FMOD_CDDA_AddUserTocEntry(device, toc->start_sector[toc->num_tracks] + (75 * 2)); + } + + if (!toc_entry->data_track || (toc != &device->toc)) + { + toc->num_tracks++; + } + } + } + + break; + } + } + + *(unsigned char *)(&toc->flags[toc->num_tracks]) = 0; + toc->track_number[toc->num_tracks] = 0xAA; + toc->start_sector[toc->num_tracks] = leadout_start_orig; + toc->num_tracks++; + + if (toc == &device->toc) + { + FMOD_CDDA_AddUserTocEntry(device, leadout_start_orig); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_CDDA_ReadToc(FMOD_CDDA_DEVICE *device, FMOD_CDDA_TOC *toc) +{ + int num_tracks, i; + unsigned char cmd[10]; + unsigned char buf[SIZEOF_CDROM_SECTOR]; + + if (!device || !toc) + { + return FMOD_ERR_INVALID_PARAM; + } + + FMOD_memset(buf, 0, sizeof(buf)); + FMOD_memset(toc, 0, sizeof(FMOD_CDDA_TOC)); + if (toc == &device->toc) + { + FMOD_memset(&device->usertoc, 0, sizeof(FMOD_CDTOC)); + } + + cmd[0] = SCSI_READ_TOC; // Operation code + cmd[1] = 0; // Logical unit number + cmd[2] = 0; // Reserved + cmd[3] = 0; // Reserved + cmd[4] = 0; // Reserved + cmd[5] = 0; // Reserved + cmd[6] = 1; // Starting track + cmd[7] = SIZEOF_CDROM_SECTOR >> 8; // Allocation length msb + cmd[8] = SIZEOF_CDROM_SECTOR & 0xff; // Allocation length lsb + cmd[9] = 0; // Control + + if (FMOD_CDDA_IssueScsiCmd(device, SRB_DIR_IN, cmd, sizeof(cmd), (char *)buf, SIZEOF_CDROM_SECTOR) != SS_COMP) + { + return FMOD_ERR_CDDA_NODISC; + } + + num_tracks = (((unsigned char)buf[0] << 8) | ((unsigned char)buf[1]) - 2) / 8; + if (num_tracks > 0) + { + unsigned char *toc_stream = buf + 4; + unsigned char *p ; + + toc->num_tracks = 0; + + for (i=0;i < num_tracks;i++) + { + p = toc_stream + (i * 8); + *(unsigned char *)(&toc->flags[toc->num_tracks]) = p[1]; + toc->track_number[toc->num_tracks] = p[2]; + FMOD_memcpy(&toc->start_sector[toc->num_tracks], &p[4], sizeof(unsigned int)); + toc->start_sector[toc->num_tracks] = FMOD_SWAPENDIAN_DWORD(toc->start_sector[toc->num_tracks]); + + if (toc->num_tracks && (toc->num_sectors[toc->num_tracks - 1] == 0)) + { + /* + Note: We can never get the correct length of the last audio track in a session if there are + more sessions following. The "start_sector[thistrack] - start_sector[lasttrack]" doesn't + take into account the big gap between sessions. + */ + + toc->num_sectors[toc->num_tracks - 1] = toc->start_sector[toc->num_tracks] - toc->start_sector[toc->num_tracks - 1]; + + if (toc->flags[toc->num_tracks].data_track) + { +// toc->num_sectors[toc->num_tracks - 1] -= 6750; + /* + This'll adjust for a session leadout and new session info - it's not correct but it works + */ + toc->num_sectors[toc->num_tracks - 1] -= 11250; + } + } + + /* + Only add user TOC entries if we're reading into the cdrom's TOC + */ + if (toc == &device->toc) + { + FMOD_CDDA_AddUserTocEntry(device, toc->start_sector[toc->num_tracks] + (75 * 2)); + } + else + { + /* + If this is the leadout track and we're not reading to the user TOC then adjust the pos + (this is only for the ASPI drive mapping stuff, so it lines up with MCI) + */ + if (toc->track_number[toc->num_tracks] == 0xAA) + { + toc->start_sector[toc->num_tracks] += 150; + } + } + + if (!toc->flags[toc->num_tracks].data_track || (toc->track_number[toc->num_tracks] == 0xAA) || (toc != &device->toc)) + { + toc->num_tracks++; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool FMOD_CDDA_ScsiAbort(SRB_ExecSCSICmd *srb_to_abort, unsigned char adapter_id) +{ + SRB_Abort srb; + DWORD status = 0; + + FMOD_memset(&srb, 0, sizeof(srb)); + srb.SRB_Cmd = SC_ABORT_SRB; + srb.SRB_HaId = adapter_id; + srb.SRB_Flags = 0; + srb.SRB_ToAbort = (SRB *)&srb_to_abort; + + status = FMOD_CDDA_SendASPI32Command((SRB *)&srb); + + if (srb.SRB_Status != SS_COMP) + { + return false; + } + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +int FMOD_CDDA_IssueScsiCmd_Internal(FMOD_CDDA_DEVICE *cdrom, int flags, unsigned char *cdb_data, int cdb_len, char *buf, int buflen, unsigned char adapter_id, unsigned char target_id, unsigned char lun_id) +{ + SRB_ExecSCSICmd srb; + DWORD aspi_status; + DWORD event_status = 0; + HANDLE event = 0; + + if ((event = CreateEvent(0, TRUE, FALSE, 0)) == 0) + { + return SS_ABORTED; + } + + FMOD_memset(&srb, 0, sizeof(srb)); + srb.SRB_Cmd = SC_EXEC_SCSI_CMD; + srb.SRB_HaId = adapter_id; + srb.SRB_Flags = flags | SRB_EVENT_NOTIFY; + srb.SRB_Target = target_id; + srb.SRB_Lun = lun_id; + srb.SRB_SenseLen = SENSE_LEN; + srb.SRB_CDBLen = cdb_len; + srb.SRB_BufLen = buflen; + srb.SRB_BufPointer = (unsigned char *)buf; + srb.SRB_PostProc = (VOID FAR*)event; + if (cdb_data != 0) + { + FMOD_memcpy(&srb.CDBByte, cdb_data, cdb_len); + } + + ResetEvent(event); + +#ifdef FMOD_OS_CDDA_DEBUG + { + unsigned int start_time; + FMOD_OS_Time_GetMs(&start_time); +#endif + + /* + We do this so it works from codec_cdda.dll + */ + if (cdrom) + { + aspi_status = cdrom->SendASPI32Command((SRB *)&srb); + } + else + { + aspi_status = FMOD_CDDA_SendASPI32Command((SRB *)&srb); + } + + /* + Wait until we're signalled + */ + if (aspi_status == SS_PENDING) + { + event_status = WaitForSingleObject(event, FMOD_CDDA_Aspi_Timeout); + if (event_status == WAIT_TIMEOUT) + { + ResetEvent(event); + } + + if (srb.SRB_Status == SS_PENDING) + { + /* + Timed out + */ + FMOD_CDDA_ScsiAbort(&srb, adapter_id); + CloseHandle(event); + return SS_ABORTED; + } + } + +#ifdef FMOD_OS_CDDA_DEBUG + { + unsigned int elapsed; + FMOD_OS_Time_GetMs(&elapsed); + elapsed -= start_time; + FMOD_OS_CDDA_DEBUGPRINT("%d\n", elapsed); + } + } +#endif + + if (cdrom) + { + cdrom->sense_key.SK = srb.SenseArea[2] & 0xf; + cdrom->sense_key.ASC = srb.SenseArea[12]; + cdrom->sense_key.ASCQ = srb.SenseArea[13]; + } + + /* + Check ASPI command status + */ + if (srb.SRB_Status != SS_COMP) + { + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_CDDA_IssueScsiCmd_Internal", "failed : status = %02x SK = %02x ASC = %02x ASCQ = %02x\n", + srb.SRB_Status, cdrom->sense_key.SK, cdrom->sense_key.ASC, cdrom->sense_key.ASCQ)); + } + + CloseHandle(event); + + return srb.SRB_Status; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +int FMOD_CDDA_IssueScsiCmd(FMOD_CDDA_DEVICE *cdrom, int flags, unsigned char *cdb_data, int cdb_len, char *buf, int buflen) +{ + if (!cdrom) + { + return SS_ABORTED; + } + + return FMOD_CDDA_IssueScsiCmd_Internal(cdrom, flags, cdb_data, cdb_len, buf, buflen, cdrom->adapter_id, cdrom->target_id, cdrom->lun_id); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_CDDA_Close(FMOD_CDDA_DEVICE *cdrom) +{ + if (!cdrom) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (cdrom->device_open) + { + FMOD_CDDA_PreventMediaRemoval(cdrom, false); + } + + SAFE_FREE(cdrom->caps); + SAFE_FREE(cdrom->name); + SAFE_FREE(cdrom->scsiaddr); + SAFE_FREE(cdrom->devicename); + + cdrom->device_open = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool FMOD_CDDA_InquiryCommand(unsigned char adapter_id, unsigned char target_id, unsigned char lun_id) +{ + int letter; + unsigned char cmd[6]; + FMOD_CDDA_InquiryData inq_data; + + FMOD_memset(&inq_data, 0, sizeof(inq_data)); + + cmd[0] = SCSI_INQUIRY; // Operation code + cmd[1] = lun_id << 5; // Logical unit number + cmd[2] = 0; // Page code + cmd[3] = 0; // Reserved + cmd[4] = sizeof(inq_data); // Allocation length + cmd[5] = 0; // Control + + if (FMOD_CDDA_IssueScsiCmd_Internal(0, SRB_DIR_IN, cmd, sizeof(cmd), (char *)&inq_data, sizeof(inq_data), adapter_id, target_id, lun_id) != SS_COMP) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_InquiryCommand: SCSI cmd failed\n"); + return false; + } + + if (USING_NTSCSI) + { + letter = FMOD_CDDA_NTSCSI_GetDeviceIndex(adapter_id, target_id, lun_id); + if (!letter) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_InquiryCommand: Invalid drive letter\n"); + FLOG((FMOD_DEBUG_USER_ANDREW, __FILE__, __LINE__, "FMOD_CDDA_InquiryCommand", "Invalid drive letter\n")); + return false; + } + } + else + { + /* + Defer drive letter mapping until the user tries to access a drive + */ + letter = 0; + } + + if (FMOD_CDDA_NumDevices < FMOD_CDDA_MAX_DEVICES) + { + FMOD_CDDA_DEVICE *cdrom; + char tmp[256]; + + cdrom = FMOD_CDDA_DeviceList[FMOD_CDDA_NumDevices] = (FMOD_CDDA_DEVICE *)FMOD_Memory_Calloc(sizeof(FMOD_CDDA_DEVICE)); + if (!FMOD_CDDA_DeviceList[FMOD_CDDA_NumDevices]) + { + return false; + } + + cdrom->letter = (USING_NTSCSI) ? FMOD_CDDA_NTSCSI_GetDeviceIndex(adapter_id, target_id, lun_id) : 0; + cdrom->target_id = target_id; + cdrom->adapter_id = adapter_id; + cdrom->lun_id = lun_id; + cdrom->device_open = 0; + cdrom->speed = 32; + cdrom->toc.num_tracks = 0; + cdrom->caps = 0; + + FMOD_memset(&cdrom->sense_key, 0, sizeof(cdrom->sense_key)); + FMOD_memset(&cdrom->toc, 0, sizeof(cdrom->toc)); + FMOD_memset(&cdrom->usertoc, 0, sizeof(cdrom->usertoc)); + FMOD_memcpy(&cdrom->inq_data, &inq_data, sizeof(inq_data)); + + if (cdrom->letter) + { + sprintf(tmp, "%c:", cdrom->letter + 'A'); + cdrom->name = FMOD_strdup(tmp); + if (!cdrom->name) + { + return false; + } + } + + sprintf(tmp, "%02x:%02x:%02x", cdrom->target_id, cdrom->adapter_id, cdrom->lun_id); + cdrom->scsiaddr = FMOD_strdup(tmp); + if (!cdrom->scsiaddr) + { + return false; + } + + tmp[0] = 0; + FMOD_strncat(tmp, cdrom->inq_data.vendor_id, 8); + FMOD_strcat(tmp, " "); + FMOD_strncat(tmp, cdrom->inq_data.product_id, 16); + FMOD_strcat(tmp, " "); + FMOD_strncat(tmp, cdrom->inq_data.product_revision, 4); + cdrom->devicename = FMOD_strdup(tmp); + if (!cdrom->devicename) + { + return false; + } + + cdrom->SendASPI32Command = FMOD_CDDA_SendASPI32Command; + FMOD_CDDA_NumDevices++; + return true; + } + else + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_InquiryCommand: too many cdrom devices!\n"); + return false; + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool FMOD_OS_CDDA_TestUnitReady(FMOD_CDDA_DEVICE *device) +{ + unsigned char cmd[6]; + + if (!device) + { + return false; + } + + cmd[0] = SCSI_TST_U_RDY; // Operation code + cmd[1] = device->lun_id << 5; // Logical unit number + cmd[2] = 0; // Reserved + cmd[3] = 0; // Reserved + cmd[4] = 0; // Reserved + cmd[5] = 0; // Control + + if (FMOD_CDDA_IssueScsiCmd(device, SRB_DIR_IN, cmd, sizeof(cmd), 0, 0) != SS_COMP) + { + return false; + } + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool FMOD_CDDA_PreventMediaRemoval(FMOD_CDDA_DEVICE *cdrom, bool prevent) +{ + unsigned char cmd[6]; + + if (!cdrom) + { + return false; + } + + cmd[0] = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; // Operation code + cmd[1] = cdrom->lun_id << 5; // Logical unit number + cmd[2] = 0; // Reserved + cmd[3] = 0; // Reserved + cmd[4] = prevent ? 1 : 0; // Prevent + cmd[5] = 0; // Control + + if (FMOD_CDDA_IssueScsiCmd(cdrom, SRB_DIR_OUT, cmd, sizeof(cmd), 0, 0) != SS_COMP) + { + return false; + } + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_CDDA_SetSpeed(FMOD_CDDA_DEVICE *cdrom, int speed) +{ + int cmd_size = 10; + unsigned char speed_hi, speed_lo; + unsigned char cmd[12]; + + if (!cdrom) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (speed == -1) + { + speed = cdrom->speed; + if (speed <= 0) + { + return FMOD_ERR_INVALID_PARAM; + } + } + + FMOD_memset(cmd, 0, sizeof(cmd)); + + speed = (1764 * speed) / 10; + speed_hi = (unsigned char)((speed >> 8) & 0xFF); + speed_lo = (unsigned char)(speed & 0xFF); + + cmd[0] = SCSI_SET_SPEED; // Operation Code + cmd[1] = cdrom->lun_id << 5; // Lun + cmd[2] = speed_hi; // High word of speed + cmd[3] = speed_lo; // Low word of speed + cmd[4] = 0xFF; // High word of speed + cmd[5] = 0xFF; // Low word of speed + cmd_size = 12; + + if (FMOD_CDDA_IssueScsiCmd(cdrom, SRB_DIR_OUT, cmd, cmd_size, 0, 0) != SS_COMP) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +int FMOD_CDDA_NTSCSI_GetNumAdapters() +{ + int buf[256], i, num_adapters = 0; + + FMOD_memset(buf, 0, sizeof(buf)); + buf[0] = 1; + + for (i=0;i < 26;i++) + { + if (FMOD_CDDA_NTSCSI_Drive[i].used) + { + buf[FMOD_CDDA_NTSCSI_Drive[i].ha] = 1; + } + } + + for (i=0;i <= 255;i++) + { + if (buf[i]) + { + FMOD_CDDA_NTSCSI_AdapterLookup[num_adapters] = i; + num_adapters++; + } + } + + return num_adapters; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +HANDLE FMOD_CDDA_NTSCSI_GetFileHandle(int i) +{ + char buf[12]; + DWORD dwFlags; + OSVERSIONINFO ver; + HANDLE handle = 0; + + /* + NT 4.0 wants just the GENERIC_READ flag, and Win2K wants both GENERIC_READ and GENERIC_WRITE. Try both... + */ + FMOD_memset(&ver, 0, sizeof(ver)); + ver.dwOSVersionInfoSize = sizeof(ver); + GetVersionEx(&ver); + + /* + If Win2K or greater, add GENERIC_WRITE + */ + dwFlags = GENERIC_READ; + + if ((ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ver.dwMajorVersion > 4)) + { + dwFlags |= GENERIC_WRITE; + } + + sprintf(buf, "\\\\.\\%c:", (char)('A' + i)); + handle = CreateFile(buf, dwFlags, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (handle == INVALID_HANDLE_VALUE) + { + /* + Didn't work - try again with the GENERIC_WRITE bit flipped + */ + dwFlags ^= GENERIC_WRITE; + handle = CreateFile(buf, dwFlags, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + } + + if (handle == INVALID_HANDLE_VALUE) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_NTSCSI_GetFileHandle: i == %d failed\n", i); + } + + return handle; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool FMOD_CDDA_NTSCSI_GetDriveInformation(int i, FMOD_CDDA_NTSCSIDRIVE *drive) +{ + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER spt_dwb; + SCSI_ADDRESS scsi_addr; + HANDLE handle; + BOOL status; + DWORD length, returned; + bool result = true; + unsigned char inq_data[192]; + + if (!drive) + { + return false; + } + + drive->used = false; + + handle = FMOD_CDDA_NTSCSI_GetFileHandle(i); + if (handle == INVALID_HANDLE_VALUE) + { + return false; + } + + FMOD_memset(inq_data, 0, 192); + FMOD_memset(&spt_dwb, 0, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER)); + + spt_dwb.spt.Length = sizeof(SCSI_PASS_THROUGH); + spt_dwb.spt.CdbLength = 6; + spt_dwb.spt.SenseInfoLength = 24; + spt_dwb.spt.DataIn = SCSI_IOCTL_DATA_IN; + spt_dwb.spt.DataTransferLength = 192; + spt_dwb.spt.TimeOutValue = 2; + spt_dwb.spt.DataBuffer = inq_data; + spt_dwb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); + spt_dwb.spt.Cdb[0] = SCSI_INQUIRY; + spt_dwb.spt.Cdb[4] = 192; + + length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER); + + status = DeviceIoControl(handle, IOCTL_SCSI_PASS_THROUGH_DIRECT, &spt_dwb, length, &spt_dwb, length, &returned, 0); + if (!status) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_NTSCSI_GetDriveInformation: SCSI_INQUIRY failed\n"); + CloseHandle(handle); + return false; + } + + FMOD_memcpy((unsigned char *)&drive->inq_data, inq_data, 36); + + /* + Get the address (path/tgt/lun) of the drive via IOCTL_SCSI_GET_ADDRESS + */ + FMOD_memset(&scsi_addr, 0, sizeof(SCSI_ADDRESS)); + scsi_addr.Length = sizeof(SCSI_ADDRESS); + if (DeviceIoControl(handle, IOCTL_SCSI_GET_ADDRESS, 0, 0, &scsi_addr, sizeof(SCSI_ADDRESS), &returned, 0)) + { + drive->used = true; + drive->ha = scsi_addr.PortNumber; + drive->tgt = scsi_addr.TargetId; + drive->lun = scsi_addr.Lun; + drive->letter = i; + FMOD_OS_CDDA_DEBUGPRINT(" portnumber = %02d\n pathid = %02d\n targetid = %02d\n lun = %02d\n", scsi_addr.PortNumber, scsi_addr.PathId, scsi_addr.TargetId, scsi_addr.Lun); + FMOD_OS_CDDA_DEBUGPRINT(" %c: = %02d:%02d:%02d\n", drive->letter + 'A', drive->ha, drive->tgt, drive->lun); + } + else + { + /* + Some USB/Firewire etc. devices don't support IOCTL_SCSI_GET_ADDRESS but we can just fake a + SCSI address without hurting anyone. + */ +/* + result = false; + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_NTSCSI_GetDriveInformation: IOCTL_SCSI_GET_ADDRESS failed on %c:\n", i + 'a'); +*/ + drive->used = true; + drive->ha = 10; + drive->tgt = 10; + drive->lun = FMOD_CDDA_FakeLun++; + drive->letter = i; + FMOD_OS_CDDA_DEBUGPRINT(" portnumber = %02d\n pathid = %02d\n targetid = %02d\n lun = %02d\n", drive->ha, scsi_addr.PathId, drive->tgt, drive->lun); + FMOD_OS_CDDA_DEBUGPRINT(" %c: = %02d:%02d:%02d\n", drive->letter + 'A', drive->ha, drive->tgt, drive->lun); + } + + drive->device = INVALID_HANDLE_VALUE; + CloseHandle(handle); + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_CDDA_NTSCSI_Init() +{ + char buf[4]; + int i, num_drives = 0; + + if (FMOD_CDDA_NTSCSI_Initialised) + { + return FMOD_OK; + } + + FMOD_CDDA_NTSCSI_Drive = (FMOD_CDDA_NTSCSIDRIVE *)FMOD_Memory_Calloc(sizeof(FMOD_CDDA_NTSCSIDRIVE) * 26); + FMOD_CDDA_NTSCSI_NumAdapters = 0; + FMOD_CDDA_FakeLun = 0; + + for (i=0;i < 26;i++) + { + FMOD_CDDA_NTSCSI_Drive[i].device = INVALID_HANDLE_VALUE; + } + + FMOD_OS_CDDA_DEBUGPRINT("Finding devices:\n"); + for (i=2;i < 26;i++) + { + UINT type; + + sprintf(buf, "%c:\\", (char)('A' + i)); + type = GetDriveType(buf); + +#ifdef FMOD_OS_CDDA_DEBUG + switch (type) + { + case DRIVE_UNKNOWN : + FMOD_OS_CDDA_DEBUGPRINT("%s = DRIVE_UNKNOWN\n", buf); + break; + case DRIVE_NO_ROOT_DIR : + break; + case DRIVE_REMOVABLE : + FMOD_OS_CDDA_DEBUGPRINT("%s = DRIVE_REMOVABLE\n", buf); + break; + case DRIVE_FIXED : + FMOD_OS_CDDA_DEBUGPRINT("%s = DRIVE_FIXED\n", buf); + break; + case DRIVE_REMOTE : + FMOD_OS_CDDA_DEBUGPRINT("%s = DRIVE_REMOTE\n", buf); + break; + case DRIVE_CDROM : + FMOD_OS_CDDA_DEBUGPRINT("%s = DRIVE_CDROM\n", buf); + break; + case DRIVE_RAMDISK : + FMOD_OS_CDDA_DEBUGPRINT("%s = DRIVE_RAMDISK\n", buf); + break; + default : + FMOD_OS_CDDA_DEBUGPRINT("%s = ???\n", buf); + break; + } +#endif + + if (type == DRIVE_CDROM) + { + FMOD_CDDA_NTSCSI_GetDriveInformation(i, &FMOD_CDDA_NTSCSI_Drive[i]); + if (FMOD_CDDA_NTSCSI_Drive[i].used) + { + num_drives++; + } + } + } + + FMOD_CDDA_NTSCSI_NumAdapters = FMOD_CDDA_NTSCSI_GetNumAdapters(); + FMOD_CDDA_NTSCSI_Initialised = true; + + return num_drives ? FMOD_OK : FMOD_ERR_CDDA_NODEVICES; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool FMOD_CDDA_NTSCSI_Shutdown() +{ + int i; + + if (!FMOD_CDDA_NTSCSI_Initialised) + { + return false; + } + + for (i=2;i < 26;i++) + { + if (FMOD_CDDA_NTSCSI_Drive[i].used) + { + CloseHandle(FMOD_CDDA_NTSCSI_Drive[i].device); + } + } + + SAFE_FREE(FMOD_CDDA_NTSCSI_Drive); + + FMOD_CDDA_NTSCSI_Initialised = false; + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +int FMOD_CDDA_NTSCSI_GetDeviceIndex(unsigned char ha, unsigned char tgt, unsigned char lun) +{ + int i; + + for (i=2;i < 26;i++) + { + if (FMOD_CDDA_NTSCSI_Drive[i].used) + { + if ((FMOD_CDDA_NTSCSI_Drive[i].ha == ha) && + (FMOD_CDDA_NTSCSI_Drive[i].tgt == tgt) && + (FMOD_CDDA_NTSCSI_Drive[i].lun == lun)) + { + return i; + } + } + } + + return 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +DWORD FMOD_CDDA_NTSCSI_HAInquiry(SRB_HAInquiry *srb) +{ + srb->HA_Count = FMOD_CDDA_NTSCSI_NumAdapters; + + if (srb->SRB_HaId >= FMOD_CDDA_NTSCSI_NumAdapters) + { + srb->SRB_Status = SS_INVALID_HA; + return SS_INVALID_HA; + } + + srb->HA_SCSI_ID = 7; + FMOD_memcpy(srb->HA_ManagerId, "FMODASPI v1.0 ", 16); + FMOD_memcpy(srb->HA_Identifier, "SCSI Adapter ", 16); + srb->HA_Identifier[13] = (char)('0' + srb->SRB_HaId); + FMOD_memset(srb->HA_Unique, 0, 16); + srb->HA_Unique[3] = 8; + + *((DWORD *)&srb->HA_Unique[4]) = 64 * 1024; + + srb->SRB_Status = SS_COMP; + + return SS_COMP; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +DWORD FMOD_CDDA_NTSCSI_GetDeviceType(SRB_GDEVBlock *srb) +{ + srb->SRB_Status = SS_NO_DEVICE; + + if (FMOD_CDDA_NTSCSI_GetDeviceIndex(srb->SRB_HaId, srb->SRB_Target, srb->SRB_Lun)) + { + srb->SRB_Status = SS_COMP; + } + + if (srb->SRB_Status == SS_COMP) + { + srb->SRB_DeviceType = DTYPE_CDROM; + } + else + { + srb->SRB_DeviceType = DTYPE_UNKNOWN; + } + + return srb->SRB_Status; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +DWORD FMOD_CDDA_NTSCSI_ExecScsiCmd(SRB_ExecSCSICmd *srb, bool retrying) +{ + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER spt_dwb; + BOOL status; + int index; + ULONG length, returned = 0; + + index = FMOD_CDDA_NTSCSI_GetDeviceIndex(srb->SRB_HaId, srb->SRB_Target, srb->SRB_Lun); + if (index == 0) + { + srb->SRB_Status = SS_ERR; + return SS_ERR; + } + + if (srb->CDBByte[0] == SCSI_INQUIRY) + { + srb->SRB_Status = SS_COMP; + FMOD_memcpy(srb->SRB_BufPointer, (char *)&FMOD_CDDA_NTSCSI_Drive[index].inq_data, 36); + return SS_COMP; + } + + if (FMOD_CDDA_NTSCSI_Drive[index].device == INVALID_HANDLE_VALUE) + { + FMOD_CDDA_NTSCSI_Drive[index].device = FMOD_CDDA_NTSCSI_GetFileHandle(FMOD_CDDA_NTSCSI_Drive[index].letter); + } + + FMOD_memset(&spt_dwb, 0, sizeof(spt_dwb)); + + spt_dwb.spt.Length = sizeof(SCSI_PASS_THROUGH); + if (srb->SRB_Flags & SRB_DIR_IN) + { + spt_dwb.spt.DataIn = SCSI_IOCTL_DATA_IN; + } + else if (srb->SRB_Flags & SRB_DIR_OUT) + { + spt_dwb.spt.DataIn = SCSI_IOCTL_DATA_OUT; + } + else + { + spt_dwb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + } + spt_dwb.spt.DataTransferLength = srb->SRB_BufLen; + spt_dwb.spt.TimeOutValue = 15; + spt_dwb.spt.DataBuffer = srb->SRB_BufPointer; + spt_dwb.spt.SenseInfoLength = srb->SRB_SenseLen; + spt_dwb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); + spt_dwb.spt.CdbLength = srb->SRB_CDBLen; + FMOD_memcpy(spt_dwb.spt.Cdb, srb->CDBByte, srb->SRB_CDBLen); + + length = sizeof(spt_dwb); + + status = DeviceIoControl(FMOD_CDDA_NTSCSI_Drive[index].device, IOCTL_SCSI_PASS_THROUGH_DIRECT, + &spt_dwb, length, &spt_dwb, length, &returned, 0); + + FMOD_memcpy(srb->SenseArea, spt_dwb.ucSenseBuf, srb->SRB_SenseLen); + + if (status) + { + srb->SRB_Status = SS_COMP; + if (spt_dwb.spt.ScsiStatus) + { + srb->SRB_Status = SS_ERR; + } + } + else + { + DWORD rc; + + srb->SRB_Status = SS_ERR; + srb->SRB_TargStat = 0x0004; + + rc = GetLastError(); + + if (rc == ERROR_SEM_TIMEOUT) + { + /* + DeviceIoControl timed out? Or maybe the device did? + */ + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_NTSCSI_ExecScsiCmd: ERROR_SEM_TIMEOUT\n"); + } + else if (rc == ERROR_IO_DEVICE) + { + FMOD_OS_CDDA_DEBUGPRINT("FMOD_CDDA_NTSCSI_ExecScsiCmd: ERROR_IO_DEVICE\n"); + } + else + { + if (!retrying && ((rc == ERROR_MEDIA_CHANGED) || (rc == ERROR_INVALID_HANDLE))) + { + if (rc != ERROR_INVALID_HANDLE) + { + CloseHandle(FMOD_CDDA_NTSCSI_Drive[index].device); + } + + FMOD_CDDA_NTSCSI_GetDriveInformation(index, &FMOD_CDDA_NTSCSI_Drive[index]); + return FMOD_CDDA_NTSCSI_ExecScsiCmd(srb, true); + } + } + } + + return srb->SRB_Status; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +DWORD FMOD_CDDA_NTSCSI_GetASPI32SupportInfo() +{ + DWORD rc; + + if (!FMOD_CDDA_NTSCSI_NumAdapters) + { + rc = (DWORD)(MAKEWORD(0, SS_NO_ADAPTERS)); + } + else + { + rc = (DWORD)(MAKEWORD(FMOD_CDDA_NTSCSI_NumAdapters, SS_COMP)); + } + + return rc; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +DWORD FMOD_CDDA_NTSCSI_SendASPI32Command(SRB *srb) +{ + if (!srb) + { + return SS_ERR; + } + + if (srb->SRB_HaId < FMOD_CDDA_NTSCSI_NumAdapters) + { + srb->SRB_HaId = FMOD_CDDA_NTSCSI_AdapterLookup[srb->SRB_HaId]; + } + + switch(srb->SRB_Cmd) + { + case SC_HA_INQUIRY : + return FMOD_CDDA_NTSCSI_HAInquiry((SRB_HAInquiry *)srb); + + case SC_GET_DEV_TYPE : + return FMOD_CDDA_NTSCSI_GetDeviceType((SRB_GDEVBlock *)srb); + + case SC_EXEC_SCSI_CMD : + return FMOD_CDDA_NTSCSI_ExecScsiCmd((SRB_ExecSCSICmd *)srb, false); + + case SC_RESET_DEV : + default : + srb->SRB_Status = SS_ERR; + return SS_ERR; + } + + return SS_ERR; +} + + +} diff --git a/win32/src/fmod_os_misc.cpp b/win32/src/fmod_os_misc.cpp new file mode 100755 index 0000000..f7049e9 --- /dev/null +++ b/win32/src/fmod_os_misc.cpp @@ -0,0 +1,829 @@ +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_os_misc.h" +#include "fmod_memory.h" + +#include "MeteredSection.h" + +#include <windows.h> +#include <stdio.h> +#include <process.h> + +#define USEMETEREDSECTIONS + +int gSizeofCriticalSection = sizeof(CRITICAL_SECTION); +#ifdef USEMETEREDSECTIONS +int gSizeofSemaphore = sizeof(METERED_SECTION); +#else +int gSizeofSemaphore = 0; +#endif + +#if defined(FMOD_DEBUG) && !defined(PLATFORM_WINDOWS64) + +#define MS_VC_EXCEPTION 0x406D1388 + +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +static void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) +{ + THREADNAME_INFO info; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (DWORD*)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} + +#endif + + +/* + [DESCRIPTION] + OS based memory alloc + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +void *FMOD_OS_Memory_Alloc(int size, FMOD_MEMORY_TYPE type) +{ + return malloc(size); +} + + +/* + [DESCRIPTION] + OS based memory re-alloc. + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +void *FMOD_OS_Memory_Realloc(void *ptr, int size, FMOD_MEMORY_TYPE type) +{ + return realloc(ptr, size); +} + + +/* + [DESCRIPTION] + OS based free + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +void FMOD_OS_Memory_Free(void *ptr, FMOD_MEMORY_TYPE type) +{ + free(ptr); +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_File_DriveStatus() +{ + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_File_Open(const char *name, char *mode, int unicode, unsigned int *filesize, void **handle) +{ + if (unicode) + { + *handle = CreateFileW((WCHAR *)name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + else + { + *handle = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (*handle == INVALID_HANDLE_VALUE) + { + DWORD err = GetLastError(); + + if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND && err != ERROR_INVALID_NAME) + { + return FMOD_ERR_FILE_BAD; + } + + return FMOD_ERR_FILE_NOTFOUND; + } + + *filesize = GetFileSize(*handle, NULL); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_File_Close(void *handle) +{ + if (handle == INVALID_HANDLE_VALUE) + { + return FMOD_ERR_FILE_BAD; + } + + CloseHandle(handle); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_File_Cancel(void *handle) +{ + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_File_Read(void *handle, void *buf, unsigned int count, unsigned int *read) +{ + DWORD rd; + + if (!ReadFile(handle, buf, count, &rd, NULL)) + { + DWORD lasterr = GetLastError(); + + return FMOD_ERR_FILE_BAD; + } + + *read = rd; + + if (rd != count) + { + return FMOD_ERR_FILE_EOF; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_File_Seek(void *handle, unsigned int offset) +{ + DWORD retval; + + retval = SetFilePointer(handle, offset, NULL, FILE_BEGIN); + + if (retval == (unsigned int)-1) + { + return FMOD_ERR_FILE_BAD; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Debug to console function + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Debug_OutputStr(const char *s) +{ + OutputDebugStringA(s); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Gets the current time in nanoseconds. (1000th of a millisecond) + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Time_GetNs(unsigned int *ns) +{ + LARGE_INTEGER val,freq; + + if (QueryPerformanceCounter(&val)) + { + QueryPerformanceFrequency(&freq); + val.QuadPart *= 1000; + val.QuadPart *= 1000; + val.QuadPart /= freq.QuadPart; + + *ns = val.LowPart; + } + else + { + *ns = timeGetTime() * 1000; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Gets the current time in milliseconds. (1000th of a second) + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Time_GetMs(unsigned int *ms) +{ + *ms = timeGetTime(); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Time_Sleep(unsigned int ms) +{ + Sleep(ms); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Thread_GetCurrentID(FMOD_UINT_NATIVE *id) +{ + *id = GetCurrentThreadId(); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + 'priority' -1 to 2, -1 = low 0 = normal, 1 = high, 2 = critical + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Thread_Create(const char *name, THREAD_RETURNTYPE (THREAD_CALLCONV *func)(void *param), void *param, FMOD_THREAD_PRIORITY priority, void *stack, int stacksize, void **handle) +{ + HRESULT hr; + unsigned int id; + + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = (void *)_beginthreadex(NULL, stacksize, func, param,0, (unsigned int *)&id); + if (!*handle) + { + return FMOD_ERR_MEMORY; + } + + switch (priority) + { + case FMOD_THREAD_PRIORITY_VERYLOW: + hr = SetThreadPriority(*handle, THREAD_PRIORITY_LOWEST); + break; + case FMOD_THREAD_PRIORITY_LOW: + hr = SetThreadPriority(*handle, THREAD_PRIORITY_BELOW_NORMAL); + break; + case FMOD_THREAD_PRIORITY_NORMAL: + hr = SetThreadPriority(*handle, THREAD_PRIORITY_NORMAL); + break; + case FMOD_THREAD_PRIORITY_HIGH: + hr = SetThreadPriority(*handle, THREAD_PRIORITY_ABOVE_NORMAL); + break; + case FMOD_THREAD_PRIORITY_VERYHIGH: + hr = SetThreadPriority(*handle, THREAD_PRIORITY_HIGHEST); + break; + case FMOD_THREAD_PRIORITY_CRITICAL: + hr = SetThreadPriority(*handle, THREAD_PRIORITY_TIME_CRITICAL); + break; + }; + +#if defined(FMOD_DEBUG) && !defined(PLATFORM_WINDOWS64) + SetThreadName(id, name); +#endif + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Thread_Destroy(void *handle) +{ + CloseHandle(handle); + + return FMOD_OK; +} + + +CRITICAL_SECTION gMemoryCrit; + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CriticalSection_Create(FMOD_OS_CRITICALSECTION **crit, bool memorycrit) +{ + FMOD_RESULT result = FMOD_OK; + + if (!crit) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (memorycrit) + { + *crit = (FMOD_OS_CRITICALSECTION *)&gMemoryCrit; /* Can't use alloc on the critical section used in the memory system. */ + } + else + { + *crit = (FMOD_OS_CRITICALSECTION *)FMOD_Memory_Alloc(sizeof(CRITICAL_SECTION)); + if (!*crit) + { + return FMOD_ERR_MEMORY; + } + } + + InitializeCriticalSection((CRITICAL_SECTION *)(*crit)); + + return result; +} + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CriticalSection_Free(FMOD_OS_CRITICALSECTION *crit, bool memorycrit) +{ + if (!crit) + { + return FMOD_ERR_INVALID_PARAM; + } + + DeleteCriticalSection((CRITICAL_SECTION *)crit); + + if (memorycrit) + { + return FMOD_OK; + } + + FMOD_Memory_Free(crit); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CriticalSection_Enter(FMOD_OS_CRITICALSECTION *crit) +{ + if (!crit) + { + return FMOD_ERR_INVALID_PARAM; + } + + EnterCriticalSection((CRITICAL_SECTION *)crit); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CriticalSection_Leave(FMOD_OS_CRITICALSECTION *crit) +{ + if (!crit) + { + return FMOD_ERR_INVALID_PARAM; + } + + LeaveCriticalSection((CRITICAL_SECTION *)crit); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Semaphore_Create(FMOD_OS_SEMAPHORE **sema) +{ + if (!sema) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef USEMETEREDSECTIONS + *sema = (FMOD_OS_SEMAPHORE *)CreateMeteredSection(0, 0xffff, NULL); + if (!*sema) + { + return FMOD_ERR_MEMORY; + } +#else + *sema = (FMOD_OS_SEMAPHORE *)CreateSemaphore(NULL, 0, 0xffff, NULL); +#endif + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Semaphore_Free(FMOD_OS_SEMAPHORE *sema) +{ + if (sema == INVALID_HANDLE_VALUE) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef USEMETEREDSECTIONS + CloseMeteredSection((LPMETERED_SECTION)sema); +#else + CloseHandle((HANDLE)sema); +#endif + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Semaphore_Wait(FMOD_OS_SEMAPHORE *sema) +{ + if (sema == INVALID_HANDLE_VALUE) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef USEMETEREDSECTIONS + EnterMeteredSection((LPMETERED_SECTION)sema, INFINITE); +#else + WaitForSingleObject((HANDLE)sema, INFINITE); +#endif + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Semaphore_Signal(FMOD_OS_SEMAPHORE *sema, bool interrupt) +{ + if (sema == INVALID_HANDLE_VALUE) + { + return FMOD_ERR_INVALID_PARAM; + } + +#ifdef USEMETEREDSECTIONS + LeaveMeteredSection((LPMETERED_SECTION)sema, 1, NULL); +#else + ReleaseSemaphore((HANDLE)sema, 1, NULL); +#endif + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Library_Load(const char *dllname, FMOD_OS_LIBRARY **handle) +{ + if (!dllname || !handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = (FMOD_OS_LIBRARY *)LoadLibraryA(dllname); + if (!*handle) + { + DWORD err = GetLastError(); + + return FMOD_ERR_FILE_NOTFOUND; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Library_GetProcAddress(FMOD_OS_LIBRARY *handle, const char *procname, void **address) +{ + if (!handle || !address) + { + return FMOD_ERR_INVALID_PARAM; + } + + *address = (void *)GetProcAddress((HMODULE)handle, procname); + if (!*address) + { + return FMOD_ERR_FILE_BAD; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Library_Free(FMOD_OS_LIBRARY *handle) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!FreeLibrary((HMODULE)handle)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_CheckDriverList(bool *devicelistchanged) +{ + static int lastNumDrivers = -1; + int numDrivers = -1; + + numDrivers = waveOutGetNumDevs(); + numDrivers += waveInGetNumDevs(); + + *devicelistchanged = (numDrivers != lastNumDrivers && lastNumDrivers != -1); + lastNumDrivers = numDrivers; + + return FMOD_OK; +} + + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +extern FMOD_OS_CRITICALSECTION *gResolveCrit; + +int FMOD_OS_GetMemoryUsed() +{ + int total = 0; + + if (gResolveCrit) + { + total += gSizeofCriticalSection; + } + + return total; +} + +#endif diff --git a/win32/src/fmod_os_misc_asm.s b/win32/src/fmod_os_misc_asm.s new file mode 100755 index 0000000..5dd29c6 --- /dev/null +++ b/win32/src/fmod_os_misc_asm.s @@ -0,0 +1,41 @@ +%include "./FMOD_static/win32/src/c32.mac" + +; ========================================================================================== +; GLOBAL UNINITIALIZED DATA +; ========================================================================================== + +[SEGMENT .data use32 align=32] + +; ========================================================================================== +; CODE +; ========================================================================================== + +[SEGMENT .text use32 align=32] + +; ================================================================================================================================= +; int FMOD_OS_SupportsSSE +; ================================================================================================================================= +proc FMOD_OS_SupportsSSE + + push ebx + push ecx + push edx + push esi + push edi + + mov eax, 1 + cpuid + test edx, 02000000h + jnz SSEFound + mov eax, 0 + jmp SSETestEnd + SSEFound: + mov eax, 1 + SSETestEnd: + + pop edi + pop esi + pop edx + pop ecx + pop ebx +endproc diff --git a/win32/src/fmod_os_net.cpp b/win32/src/fmod_os_net.cpp new file mode 100755 index 0000000..1d59471 --- /dev/null +++ b/win32/src/fmod_os_net.cpp @@ -0,0 +1,501 @@ +#include "fmod_net.h" +#include "fmod_os_misc.h" +#include "fmod_time.h" + +#include <windows.h> + + +static const int FMOD_NET_MAXADDRLEN = 46; + +static int FMOD_OS_Net_Init_Count = 0; + +FMOD_OS_CRITICALSECTION *gResolveCrit = 0; + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Init() +{ + FMOD_RESULT result; + + if (FMOD_OS_Net_Init_Count == 0) + { + WSADATA wsadata; + + WSAStartup(0x0002, &wsadata); + + result = FMOD_OS_CriticalSection_Create(&gResolveCrit); + if (result != FMOD_OK) + { + WSACleanup(); + return result; + } + + FMOD_OS_Net_Init_Count++; + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Shutdown() +{ + if (FMOD_OS_Net_Init_Count > 0) + { + FMOD_OS_Net_Init_Count--; + if (FMOD_OS_Net_Init_Count == 0) + { + FMOD_OS_CriticalSection_Free(gResolveCrit); + gResolveCrit = 0; + + WSACleanup(); + } + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Connect(const char *host, const unsigned short port, void **handle) +{ + struct sockaddr_in server; + struct hostent *h; + FMOD_RESULT result; + SOCKET sock; + int rc; + struct timeval tv; + unsigned long nonblocking = 1; + fd_set writefd; + unsigned int thistime = 0, lasttime = 0, elapsedtime = 0; + unsigned int timeout = FMOD_Net_Timeout; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) + { + return FMOD_ERR_MEMORY; + } + else + { + *handle = (void *)sock; + } + + FMOD_memset(&server, 0, sizeof(struct sockaddr_in)); + + *((unsigned long *)&server.sin_addr) = inet_addr(host); + + if (*((unsigned long *)&server.sin_addr) == INADDR_NONE) + { + char buf[MAXGETHOSTSTRUCT]; + HANDLE handle; + + result = FMOD_OS_CriticalSection_Enter(gResolveCrit); + if (result != FMOD_OK) + { + closesocket(sock); + return result; + } + + FMOD_memset(buf, 0, MAXGETHOSTSTRUCT); + + handle = WSAAsyncGetHostByName(NULL, NULL, host, buf, MAXGETHOSTSTRUCT); + + h = (struct hostent *)buf; + + FMOD_OS_Time_GetMs(&lasttime); + + for (;;) + { + if (h->h_name) + { + break; + } + + FMOD_OS_Time_GetMs(&thistime); + + elapsedtime += (thistime - lasttime); + lasttime = thistime; + + if (elapsedtime >= timeout) + { + WSACancelAsyncRequest(handle); + FMOD_OS_CriticalSection_Leave(gResolveCrit); + return FMOD_ERR_NET_URL; + } + + FMOD_OS_Time_Sleep(10); + } + + FMOD_memcpy(&server.sin_addr, (struct in_addr *)h->h_addr, sizeof(struct in_addr)); + FMOD_OS_CriticalSection_Leave(gResolveCrit); + } + + server.sin_family = AF_INET; + server.sin_port = htons(port); + + FD_ZERO(&writefd); + FD_SET(sock, &writefd); + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + ioctlsocket(sock, FIONBIO, &nonblocking); + + rc = connect(sock, (struct sockaddr *)&server, sizeof(server)); + if (rc == SOCKET_ERROR) + { + int rc = WSAGetLastError(); + + if (rc != WSAEWOULDBLOCK) + { + closesocket(sock); + return FMOD_ERR_NET_CONNECT; + } + } + + /* + Select will block for timeout length, or return when connected + */ + rc = select(0, 0, &writefd, 0, &tv); + if (rc <= 0) + { + closesocket(sock); + return FMOD_ERR_NET_CONNECT; + } + + nonblocking = 0; + ioctlsocket(sock, FIONBIO, &nonblocking); + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Begin listening on the designated port, non-blocking + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Listen(const unsigned short port, void **listenhandle) +{ + SOCKET listensocket; + SOCKADDR_IN address; + u_long nonblocking = TRUE; + + // Create the socket + listensocket = socket(AF_INET, SOCK_STREAM, 0); + if (listensocket == INVALID_SOCKET) + { + return FMOD_ERR_MEMORY; + } + + // Set blocking mode + if (ioctlsocket(listensocket, FIONBIO, &nonblocking) == SOCKET_ERROR) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + // Set to listen from any IP on the defined port + FMOD_memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.S_un.S_addr = htonl(INADDR_ANY); + + // Bind the address to the socket + if (bind(listensocket, (struct sockaddr*)&address, sizeof(address)) == SOCKET_ERROR) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + // Start listening for connections + if (listen(listensocket, 3) == SOCKET_ERROR) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + *listenhandle = (void *)listensocket; + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Accept a pending connection from the queue + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Accept(const void *listenhandle, void **clienthandle) +{ + SOCKET listensocket = (SOCKET)listenhandle; + SOCKET clientsocket = NULL; + + if (listenhandle == (void *)-1) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + // Accept a waiting connection + clientsocket = accept(listensocket, NULL, NULL); + if(clientsocket == INVALID_SOCKET) + { + int errorcode = WSAGetLastError(); + if(errorcode == WSAEWOULDBLOCK) + { + return FMOD_ERR_NET_WOULD_BLOCK; + } + else + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + } + + *clienthandle = (void *)clientsocket; + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Close(const void *handle) +{ + SOCKET sock = (SOCKET)handle; + + if (sock != -1) + { + closesocket(sock); + } + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +FMOD_RESULT FMOD_OS_Net_Write(const void *handle, const char *buf, const unsigned int len, unsigned int *byteswritten) +{ + SOCKET sock = (SOCKET)handle; + int written, bytestowrite = len; + + if (handle == (void *)-1) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + *byteswritten = 0; + + while (bytestowrite) + { + written = send(sock, buf, bytestowrite, 0); + if (written == SOCKET_ERROR) + { + int errorcode = WSAGetLastError(); + if(errorcode == WSAEWOULDBLOCK) + { + return FMOD_ERR_NET_WOULD_BLOCK; + } + else + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + } + + *byteswritten += written; + bytestowrite -= written; + buf += written; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_Net_Read(const void *handle, char *buf, const unsigned int len, unsigned int *bytesread) +{ + SOCKET sock = (SOCKET)handle; + int read, bytestoread = len; + + if (handle == (void *)-1) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + if (!buf || (len <= 0)) + { + return FMOD_ERR_INVALID_PARAM; + } + + *bytesread = 0; + + while (bytestoread) + { + read = recv(sock, buf, bytestoread, 0); + + if (read == SOCKET_ERROR) + { + int errorcode = WSAGetLastError(); + if(errorcode == WSAEWOULDBLOCK) + { + return FMOD_ERR_NET_WOULD_BLOCK; + } + else + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + } + else if (read == 0) + { + /* + Connection closed gracefully + */ + return FMOD_ERR_FILE_EOF; + } + else + { + *bytesread += read; + bytestoread -= read; + buf += read; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_Net_ReadLine(const void *handle, char *buf, const unsigned int len) +{ + unsigned int pos, read; + char c = 0; + + buf[0] = 0; + + if (handle == (void *)-1) + { + return FMOD_ERR_NET_SOCKET_ERROR; + } + + if (!buf || (len <= 0)) + { + return FMOD_ERR_INVALID_PARAM; + } + + pos = 0; + + while (pos < len) + { + FMOD_RESULT result = FMOD_OS_Net_Read(handle, &c, 1, &read); + if (result == FMOD_ERR_NET_WOULD_BLOCK) + { + return FMOD_ERR_NET_WOULD_BLOCK; + } + + if (read == 1) + { + if (c == '\n') + { + break; + } + else if (c != '\r') + { + buf[pos++] = c; + } + } + else + { + break; + } + } + + if (pos >= len) + { + pos = len - 1; + } + + buf[pos] = 0; + + return FMOD_OK; +} diff --git a/win32/src/fmod_os_output.cpp b/win32/src/fmod_os_output.cpp new file mode 100755 index 0000000..9d639d9 --- /dev/null +++ b/win32/src/fmod_os_output.cpp @@ -0,0 +1,112 @@ +#include "fmod_settings.h" + +#include "fmod.h" +#include "fmod_memory.h" +#include "fmod_output.h" +#include "fmod_output_asio.h" +#include "fmod_output_wasapi.h" +#include "fmod_output_dsound.h" +#include "fmod_output_winmm.h" +#include "fmod_output_openal.h" +#include "fmod_pluginfactory.h" + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_Output_Register(FMOD::PluginFactory *pluginfactory) +{ +#ifdef FMOD_USE_PLUGINS + + CHECK_RESULT(pluginfactory->tryLoadPlugin("output_dsound")); + CHECK_RESULT(pluginfactory->tryLoadPlugin("output_winmm")); + CHECK_RESULT(pluginfactory->tryLoadPlugin("output_wasapi")); + CHECK_RESULT(pluginfactory->tryLoadPlugin("output_asio")); + +#else + + #ifdef FMOD_SUPPORT_DSOUND + CHECK_RESULT(pluginfactory->registerOutput(FMOD::OutputDSound::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_WINMM + CHECK_RESULT(pluginfactory->registerOutput(FMOD::OutputWinMM::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_WASAPI + CHECK_RESULT(pluginfactory->registerOutput(FMOD::OutputWASAPI::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_ASIO + CHECK_RESULT(pluginfactory->registerOutput(FMOD::OutputASIO::getDescriptionEx())); + #endif + #ifdef FMOD_SUPPORT_OPENAL + CHECK_RESULT(pluginfactory->registerOutput(FMOD::OutputOpenAL::getDescriptionEx())); + #endif + +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT FMOD_OS_Output_GetDefault(FMOD_OUTPUTTYPE *outputtype) +{ + if (!outputtype) + { + return FMOD_ERR_INVALID_PARAM; + } + + *outputtype = FMOD_OUTPUTTYPE_DSOUND; + +#ifdef FMOD_SUPPORT_WASAPI + /* + Check COM support for WASAPI, override default output mode if supported + */ + { + IMMDeviceEnumerator *enumerator = NULL; + HRESULT hResult = S_OK; + + hResult = CoInitialize(NULL); + + if (SUCCEEDED(CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator))) + { + *outputtype = FMOD_OUTPUTTYPE_WASAPI; + enumerator->Release(); + } + + if (hResult == S_OK || hResult == S_FALSE) + { + CoUninitialize(); + } + } +#endif + + return FMOD_OK; +} diff --git a/win32/src/fmod_output_asio.cpp b/win32/src/fmod_output_asio.cpp new file mode 100755 index 0000000..d0e5808 --- /dev/null +++ b/win32/src/fmod_output_asio.cpp @@ -0,0 +1,1812 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ASIO + +#include "fmod.h" +#include "fmod_downmix.h" +#include "fmod_dspi.h" +#include "fmod_output_asio.h" +#include "fmod_soundi.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + +FMOD_OUTPUT_DESCRIPTION_EX asiooutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputASIO::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +OutputASIO *gASIOObject = 0; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputASIO::getDescriptionEx() +{ + FMOD_memset(&asiooutput, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + asiooutput.name = "FMOD ASIO Output"; + asiooutput.version = 0x00010100; + asiooutput.polling = false; + asiooutput.getnumdrivers = &OutputASIO::getNumDriversCallback; + asiooutput.getdrivername = &OutputASIO::getDriverNameCallback; + asiooutput.getdrivercaps = &OutputASIO::getDriverCapsCallback; + asiooutput.init = &OutputASIO::initCallback; + asiooutput.close = &OutputASIO::closeCallback; + asiooutput.start = &OutputASIO::startCallback; + asiooutput.stop = &OutputASIO::stopCallback; + asiooutput.gethandle = &OutputASIO::getHandleCallback; + + /* + Private members + */ +#ifdef FMOD_SUPPORT_RECORDING + asiooutput.record_getnumdrivers = &OutputASIO::recordGetNumDriversCallback; + asiooutput.record_getdriverinfo = &OutputASIO::recordGetDriverInfoCallback; + asiooutput.record_start = &OutputASIO::recordStartCallback; + asiooutput.record_stop = &OutputASIO::recordStopCallback; + asiooutput.record_getposition = &OutputASIO::recordGetPositionCallback; + asiooutput.record_lock = &OutputASIO::recordLockCallback; + asiooutput.record_unlock = &OutputASIO::recordUnlockCallback; +#endif + asiooutput.mType = FMOD_OUTPUTTYPE_ASIO; + asiooutput.mSize = sizeof(OutputASIO); + + return &asiooutput; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +void OutputASIO::bufferSwitch(long index, ASIOBool processNow) +{ + ASIOTime timeInfo; + FMOD_memset (&timeInfo, 0, sizeof (timeInfo)); + + if (ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK) + { + timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid; + } + + bufferSwitchTimeInfo(&timeInfo, index, processNow); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +void OutputASIO::sampleRateDidChange(ASIOSampleRate sRate) +{ + /* Gizza */ +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +long OutputASIO::asioMessage(long selector, long value, void* message, double* opt) +{ + // currently the parameters "value", "message" and "opt" are not used. + long ret = 0; + switch(selector) + { + case kAsioSelectorSupported: + if(value == kAsioResetRequest + || value == kAsioEngineVersion + || value == kAsioResyncRequest + || value == kAsioLatenciesChanged + // the following three were added for ASIO 2.0, you don't necessarily have to support them + || value == kAsioSupportsTimeInfo + || value == kAsioSupportsTimeCode + || value == kAsioSupportsInputMonitor) + ret = 1L; + break; + case kAsioResetRequest: + // defer the task and perform the reset of the driver during the next "safe" situation + // You cannot reset the driver right now, as this code is called from the driver. + // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction + // Afterwards you initialize the driver again. + ret = 1L; + break; + case kAsioResyncRequest: + // This informs the application, that the driver encountered some non fatal data loss. + // It is used for synchronization purposes of different media. + // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the + // Windows Multimedia system, which could loose data because the Mutex was hold too long + // by another thread. + // However a driver can issue it in other situations, too. + ret = 1L; + break; + case kAsioLatenciesChanged: + // This will inform the host application that the drivers were latencies changed. + // Beware, it this does not mean that the buffer sizes have changed! + // You might need to update internal delay data. + ret = 1L; + break; + case kAsioEngineVersion: + // return the supported ASIO version of the host application + // If a host applications does not implement this selector, ASIO 1.0 is assumed + // by the driver + ret = 2L; + break; + case kAsioSupportsTimeInfo: + // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback + // is supported. + // For compatibility with ASIO 1.0 drivers the host application should always support + // the "old" bufferSwitch method, too. + ret = 1; + break; + case kAsioSupportsTimeCode: + // informs the driver wether application is interested in time code info. + // If an application does not need to know about time code, the driver has less work + // to do. + ret = 0; + break; + } + return ret; +} + + +#ifdef FMOD_SUPPORT_SOFTWARE +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::updateMixer(ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess) +{ + FMOD_RESULT result; + int count, channels; + + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&channels); + } + else + { + result = mSystem->getSoftwareFormat(0, 0, &channels, 0, 0, 0); + if (result != FMOD_OK) + { + return FMOD_OK; + } + } + +#ifdef FMOD_SUPPORT_RECORDING + + if (mRecordNumActive && mRecordSound) + { + /* + Interleave ASIO record buffers to the FMOD style (normal) interleaved buffer, + */ + for (count = 0; count < mInputChannels + mOutputChannels; count++) + { + if (mBufferInfo[count].isInput && mBufferInfo[count].channelNum < mRecordSound->mChannels) + { + int numsamples = mBufferPreferredSize; + float *dest = mInterleavedRecordBuffer + (mInterleavedRecordBufferPos * mRecordSound->mChannels) + mBufferInfo[count].channelNum; + int count2; + + /* + Do processing for the outputs only. + ASIO works with individual mono channels, (ie 1 for left and 1 for right), so it has to be + de-interleaved from our interleaved stereo stream. + */ + switch (mChannelInfo[count].type) + { + case ASIOSTInt16LSB: + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCM16, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTInt24LSB: // used for 20 bits as well + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCM24, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTInt32LSB: + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCM32, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + { + double *src = (double *)mBufferInfo[count].buffers[doubleBufferIndex]; + + for (count2=0; count2<numsamples; count2++) + { + dest[count2] = (float)src[count2 * mRecordSound->mChannels]; + } + break; + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + } + case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + { + void *src = mBufferInfo[count].buffers[doubleBufferIndex]; + + FMOD_memset(dest, 0, numsamples * 4); + break; + } + + + /* + MSB + */ + + case ASIOSTInt16MSB: + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCM16, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTInt24MSB: // used for 20 bits as well + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCM24, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTInt32MSB: + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCM32, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + { + DSPI::convert(dest, mBufferInfo[count].buffers[doubleBufferIndex], mRecordFormat, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, mRecordSound->mChannels, 1, 1.0f); + break; + } + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + { + double *src = (double *)mBufferInfo[count].buffers[doubleBufferIndex]; + + for (count2=0; count2<numsamples; count2++) + { + dest[count2] = (float)src[count2 * mRecordSound->mChannels]; + } + break; + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + } + case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + { + void *src = mBufferInfo[count].buffers[doubleBufferIndex]; + + FMOD_memset(dest, 0, numsamples * 4); + break; + } + } + } + } + + mInterleavedRecordBufferPos += mBufferPreferredSize; + if (mInterleavedRecordBufferPos >= (unsigned int)mRecordBufferLength) + { + if (mRecordLoop) + { + mInterleavedRecordBufferPos -= (unsigned int)mRecordBufferLength; + } + else + { + mRecordNumActive = 0; + } + } + + } +#endif + + /* + Update the mixer to the interleaved buffer. + */ + result = mix(mInterleavedBuffer, mBufferPreferredSize); + if (result != FMOD_OK) + { + return FMOD_OK; + } + + /* + Now de-interleave the mixed result to the ASIO buffers. + */ + int speaker = 0; + for (count = 0; count < mInputChannels + mOutputChannels; count++) + { + if (!mBufferInfo[count].isInput && mBufferInfo[count].channelNum < channels) + { + int numsamples = mBufferPreferredSize; + float *src = 0; + int count2; + + if (mSystem->mASIOSpeakerList[speaker] != FMOD_SPEAKER_NULL) + { + src = mInterleavedBuffer + (int)mSystem->mASIOSpeakerList[speaker]; + } + speaker++; + + /* + Do processing for the outputs only. + ASIO works with individual mono channels, (ie 1 for left and 1 for right), so it has to be + de-interleaved from our interleaved stereo stream. + */ + switch (mChannelInfo[count].type) + { + case ASIOSTInt16LSB: + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCM16, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 2); + } + break; + } + case ASIOSTInt24LSB: // used for 20 bits as well + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCM24, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 3); + } + break; + } + case ASIOSTInt32LSB: + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCM32, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 4); + } + break; + } + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCMFLOAT, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 4); + } + break; + } + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + { + if (src) + { + double *dest = (double *)mBufferInfo[count].buffers[doubleBufferIndex]; + + for (count2=0; count2<numsamples; count2++) + { + dest[count2] = (double)src[count2 * channels]; + } + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 8); + } + break; + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + } + case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 4); + break; + } + + + /* + MSB + */ + + case ASIOSTInt16MSB: + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCM16, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 2); + } + break; + } + case ASIOSTInt24MSB: // used for 20 bits as well + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCM24, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 3); + } + break; + } + case ASIOSTInt32MSB: + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCM32, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 4); + } + break; + } + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + { + if (src) + { + DSPI::convert(mBufferInfo[count].buffers[doubleBufferIndex], src, FMOD_SOUND_FORMAT_PCMFLOAT, FMOD_SOUND_FORMAT_PCMFLOAT, numsamples, 1, channels, 1.0f); + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 4); + } + break; + } + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + { + if (src) + { + double *dest = (double *)mBufferInfo[count].buffers[doubleBufferIndex]; + + for (count2=0; count2<numsamples; count2++) + { + dest[count2] = (double)src[count2 * channels]; + } + } + else + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + FMOD_memset(dest, 0, numsamples * 8); + } + break; + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + } + case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + { + void *dest = mBufferInfo[count].buffers[doubleBufferIndex]; + + FMOD_memset(dest, 0, numsamples * 4); + break; + } + } + } + } + + /* + Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place + */ + if (mPostOutput) + { + ASIOOutputReady(); + } + + return FMOD_OK; +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +ASIOTime * OutputASIO::bufferSwitchTimeInfo(ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess) +{ +#ifdef FMOD_SUPPORT_SOFTWARE + gASIOObject->updateMixer(params, doubleBufferIndex, directProcess); +#endif + + return 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::getNumDrivers(int *numdrivers) +{ + /* + Set up gGlobal - for debug / file / memory access by this plugin. + */ + Plugin::init(); + + if (!mHandle) + { + mHandle = FMOD_Object_Calloc(AsioDrivers); + if (!mHandle) + { + return FMOD_ERR_MEMORY; + } + } + + *numdrivers = mHandle->asioGetNumDev(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::getDriverName(int driver, char *name, int namelen) +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + if (name && namelen >= 1) + { + if (!mHandle) + { + mHandle = FMOD_Object_Calloc(AsioDrivers); + if (!mHandle) + { + return FMOD_ERR_MEMORY; + } + } + + mHandle->asioGetDriverName(driver, name, namelen); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::getDriverCaps(int id, FMOD_CAPS *caps) +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + if (!mHandle) + { + mHandle = FMOD_Object_Calloc(AsioDrivers); + if (!mHandle) + { + return FMOD_ERR_MEMORY; + } + } + + if (mHandle->asioGetNumDev() <= 0) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputASIO::getDriverCaps", "Error - No sound devices!\n")); + return FMOD_ERR_PLUGIN_RESOURCE; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + ASIODriverInfo asioinf; + ASIOError err; + ASIOSampleRate mixratef; + ASIOBufferInfo *info; + char name[256]; + int offset = 0; + int count; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "Initializing.\n")); + + if (!mHandle) + { + mHandle = FMOD_Object_Calloc(AsioDrivers); + if (!mHandle) + { + return FMOD_ERR_MEMORY; + } + } + + if (mHandle->asioGetNumDev() <= 0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "Error - No sound devices!\n")); + return FMOD_ERR_OUTPUT_INIT; + } + + if (selecteddriver < 0) + { + selecteddriver = 0; + } + + mHandle->asioGetDriverName(selecteddriver, name, 256); + + FMOD_memset(&asioinf, 0, sizeof(ASIODriverInfo)); + asioinf.sysRef = (void *)extradriverdata; + + err = mHandle->loadDriver(name); + if (!err) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "loadDriver failed using %s as the driver name (%d).\n", name, err)); + return FMOD_ERR_OUTPUT_INIT; + } + + + *outputformat = FMOD_SOUND_FORMAT_PCMFLOAT; /* Change ASIO mix output to float to handle all ASIO types */ + + /* + Initialize ASIO. + */ + err = ASIOInit(&asioinf); + if (err != ASE_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "ASIOInit failed (%d).\n", err)); + return FMOD_ERR_OUTPUT_INIT; + } + + + err = ASIOGetBufferSize(&mBufferMinSize, &mBufferMaxSize, &mBufferPreferredSize, &mBufferGranularity); + if (err != ASE_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "ASIOGetBufferSize failed (%d).\n", err)); + return FMOD_ERR_OUTPUT_INIT; + } + + /* + Get the sample rate from the control panel. + */ + err = ASIOGetSampleRate(&mixratef); + if (err != ASE_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "ASIOGetSampleRate failed (%d).\n", err)); + return FMOD_ERR_OUTPUT_INIT; + } + + *outputrate = (int)mixratef; + + if (ASIOOutputReady() == ASE_OK) + { + mPostOutput = true; + } + else + { + mPostOutput = false; + } + + err = ASIOGetChannels(&mInputChannels, &mOutputChannels); + if (err != ASE_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "ASIOGetChannels failed (%d).\n", err)); + return FMOD_ERR_OUTPUT_INIT; + } + + /* + Allocate the asio buffer and channel infos for all inputs and outputs. + */ + mBufferInfo = (ASIOBufferInfo *)FMOD_Memory_Calloc(sizeof(ASIOBufferInfo) * (mInputChannels + mOutputChannels)); + if (!mBufferInfo) + { + return FMOD_ERR_MEMORY; + } + + mChannelInfo = (ASIOChannelInfo *)FMOD_Memory_Calloc(sizeof(ASIOChannelInfo) * (mInputChannels + mOutputChannels)); + if (!mChannelInfo) + { + FMOD_Memory_Free(mBufferInfo); + mBufferInfo = NULL; + return FMOD_ERR_MEMORY; + } + + /* + Allocate an interleaved output buffer for the mixer, that we can deinterleave later in the asio callback. + */ + { + unsigned int lenbytes; + + SoundI::getBytesFromSamples(mBufferPreferredSize, &lenbytes, outputchannels, *outputformat); + + mInterleavedBuffer = (float *)FMOD_Memory_Calloc(lenbytes); + if (!mInterleavedBuffer) + { + FMOD_Memory_Free(mBufferInfo); + FMOD_Memory_Free(mChannelInfo); + + mBufferInfo = NULL; + mChannelInfo = NULL; + + return FMOD_ERR_MEMORY; + } + } + + + /* + Prepare output channels + */ + info = mBufferInfo; + + for (count = 0; count < mInputChannels; count++, info++) + { + info->isInput = ASIOTrue; + info->channelNum = count; + info->buffers[0] = info->buffers[1] = 0; + } + for (count = 0; count < mOutputChannels; count++, info++) + { + info->isInput = ASIOFalse; + info->channelNum = count; + info->buffers[0] = info->buffers[1] = 0; + } + + mCallbacks.bufferSwitch = bufferSwitch; + mCallbacks.sampleRateDidChange = sampleRateDidChange; + mCallbacks.asioMessage = asioMessage; + mCallbacks.bufferSwitchTimeInfo = bufferSwitchTimeInfo; + + gASIOObject = this; + + err = ASIOCreateBuffers(mBufferInfo, (mInputChannels + mOutputChannels), mBufferPreferredSize, &mCallbacks); + if (err != ASE_OK) + { + FMOD_Memory_Free(mBufferInfo); + FMOD_Memory_Free(mChannelInfo); + FMOD_Memory_Free(mInterleavedBuffer); + + mBufferInfo = NULL; + mChannelInfo = NULL; + mInterleavedBuffer = NULL; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::init", "ASIOCreateBuffers failed (%d).\n", err)); + + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + + mSystem->mAdvancedSettings.ASIOChannelList = (char **)FMOD_Memory_Calloc(sizeof(char *) * (mInputChannels + mOutputChannels)); + if (!mSystem->mAdvancedSettings.ASIOChannelList) + { + return FMOD_ERR_MEMORY; + } + + mSystem->mAdvancedSettings.ASIONumChannels = 0; + + // now get all the buffer details, sample word length, name, word clock group and activation + for (count = 0; count < (mInputChannels + mOutputChannels); count++) + { + mChannelInfo[count].channel = mBufferInfo[count].channelNum; + mChannelInfo[count].isInput = mBufferInfo[count].isInput; + err = ASIOGetChannelInfo(&mChannelInfo[count]); + if (err != ASE_OK) + { + break; + } + + if (!mChannelInfo[count].isInput) + { + mSystem->mAdvancedSettings.ASIOChannelList[mSystem->mAdvancedSettings.ASIONumChannels++] = mChannelInfo[count].name; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::close() +{ + /* + Set up gGlobal - for debug / file / memory access by this plugin. + */ + Plugin::init(); + + if (mBufferInfo) + { + ASIODisposeBuffers(); + FMOD_Memory_Free(mBufferInfo); + mBufferInfo = NULL; + } + + if (mChannelInfo) + { + FMOD_Memory_Free(mChannelInfo); + mChannelInfo = 0; + } + + ASIOExit(); + + if (mInterleavedBuffer) + { + FMOD_Memory_Free(mInterleavedBuffer); + mInterleavedBuffer = 0; + } + + if (mHandle) + { + mHandle->close(); + + FMOD_Memory_Free(mHandle); + mHandle = 0; + } + + if(mSystem->mAdvancedSettings.ASIOChannelList) + { + FMOD_Memory_Free(mSystem->mAdvancedSettings.ASIOChannelList); + mSystem->mAdvancedSettings.ASIOChannelList = 0; + mSystem->mAdvancedSettings.ASIONumChannels = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::getHandle(void **handle) +{ + *handle = mHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::start() +{ + ASIOError err; + + err = ASIOStart(); + + if (err != ASE_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputASIO::start", "ASIOStart failed (%d).\n", err)); + + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::stop() +{ + ASIOError err = ASE_OK; + + err = ASIOStop(); + + if (err != ASE_OK) + { + /* + FIXME: Errorcode? + */ + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +#ifdef FMOD_SUPPORT_RECORDING +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordGetNumDrivers(int *numdrivers) +{ + if (!mHandle) + { + mHandle = FMOD_Object_Calloc(AsioDrivers); + if (!mHandle) + { + return FMOD_ERR_MEMORY; + } + } + + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numdrivers = 1; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + ASIO does not provide GUIDs + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + if (id < 0 || id >= 1) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + if (!mHandle) + { + mHandle = FMOD_Object_Calloc(AsioDrivers); + if (!mHandle) + { + return FMOD_ERR_MEMORY; + } + } + + mHandle->asioGetDriverName(id, name, namelen); + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordStart(FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop) +{ + SoundI *soundi; + unsigned int lenbytes; + + soundi = (SoundI *)sound; + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + ASIO has multiple channels instead of lots of different drivers for + different mics + */ + if (mRecordNumActive) + { + return FMOD_ERR_UNSUPPORTED; + } + + /* + Allocate an interleaved output buffer for the mixer, that we can deinterleave later in the asio callback. + */ + recordinfo->mRecordBufferLength = mRecordBufferLength = mBufferPreferredSize * 2; + recordinfo->mRecordFormat = mRecordFormat =FMOD_SOUND_FORMAT_PCMFLOAT; + mRecordLoop = loop; + recordinfo->mRecordSound = mRecordSound = soundi; + + SoundI::getBytesFromSamples(recordinfo->mRecordBufferLength, &lenbytes, soundi->mChannels, FMOD_SOUND_FORMAT_PCMFLOAT); + + mInterleavedRecordBuffer = (float *)FMOD_Memory_Calloc(lenbytes); + if (!mInterleavedRecordBuffer) + { + return FMOD_ERR_MEMORY; + } + + mInterleavedRecordBufferPos = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordStop(FMOD_RECORDING_INFO *recordinfo) +{ + mRecordSound = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordGetPosition(FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + if (!pcm) + { + return FMOD_ERR_INVALID_PARAM; + } + + *pcm = mInterleavedRecordBufferPos; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordLock(FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD_RESULT result; + char *src = 0; + unsigned int lenbytes; + + if (!mRecordSound) + { + return FMOD_ERR_INTERNAL; + } + + result = SoundI::getBytesFromSamples(mRecordBufferLength, &lenbytes, mRecordSound->mChannels, mRecordFormat); + if (result != FMOD_OK) + { + return result; + } + + src = (char *)mInterleavedRecordBuffer; + + if (offset >= lenbytes || offset < 0 || length < 0) + { + *ptr1 = 0; + *ptr2 = 0; + *len1 = 0; + *len2 = 0; + return FMOD_ERR_INVALID_PARAM; + } + + if (offset + length <= lenbytes) + { + *ptr1 = src + offset; + *len1 = length; + *ptr2 = 0; + *len2 = 0; + } + /* + Otherwise return wrapped pointers in pt1 and ptr2 + */ + else + { + *ptr1 = src + offset; + *len1 = lenbytes - offset; + *ptr2 = src; + *len2 = length - (lenbytes - offset); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::recordUnlock(FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + return FMOD_OK; +} +#endif + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->getDriverName(id, name, namelen); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputASIO::getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->getDriverCaps(id, caps); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::startCallback(FMOD_OUTPUT_STATE *output) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->start(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::stopCallback(FMOD_OUTPUT_STATE *output) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->getHandle(handle); +} + + +#ifdef FMOD_SUPPORT_RECORDING +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordGetNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordGetNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordGetDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordGetDriverInfo(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordStartCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordStart(recordinfo, (Sound *)sound, loop ? true : false); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordStopCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordStop(recordinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordGetPositionCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordGetPosition(recordinfo, pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordLockCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordLock(recordinfo, offset, length, ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputASIO::recordUnlockCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + OutputASIO *asio = (OutputASIO *)output; + + return asio->recordUnlock(recordinfo, ptr1, ptr2, len1, len2); +} +#endif + +} + +#endif diff --git a/win32/src/fmod_output_asio.h b/win32/src/fmod_output_asio.h new file mode 100755 index 0000000..2086cd7 --- /dev/null +++ b/win32/src/fmod_output_asio.h @@ -0,0 +1,101 @@ +#ifndef _FMOD_OUTPUT_ASIO_H +#define _FMOD_OUTPUT_ASIO_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_ASIO + +#include "fmod_outputi.h" + +#include "asio\asiosys.h" +#include "asio\asio.h" +#include "asio\asiodrivers.h" + +namespace FMOD +{ + class OutputASIO : public Output + { + private: + + AsioDrivers *mHandle; + long mInputChannels; + long mOutputChannels; + long mBufferMinSize; + long mBufferMaxSize; + long mBufferPreferredSize; + long mBufferGranularity; + bool mPostOutput; + float *mInterleavedBuffer; + +#ifdef FMOD_SUPPORT_RECORDING + float *mInterleavedRecordBuffer; + unsigned int mInterleavedRecordBufferPos; + bool mRecordLoop; + SoundI *mRecordSound; + FMOD_SOUND_FORMAT mRecordFormat; + unsigned int mRecordBufferLength; +#endif + + ASIOCallbacks mCallbacks; + ASIOBufferInfo *mBufferInfo; + ASIOChannelInfo *mChannelInfo; + + + static void bufferSwitch(long index, ASIOBool processNow); + static ASIOTime * bufferSwitchTimeInfo(ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess); + static void sampleRateDidChange(ASIOSampleRate sRate); + static long asioMessage(long selector, long value, void* message, double* opt); + +#ifdef FMOD_SUPPORT_SOFTWARE + FMOD_RESULT updateMixer(ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess); +#endif + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT enumerate(); + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverName(int id, char *name, int namelen); + FMOD_RESULT getDriverCaps(int id, FMOD_CAPS *caps); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT getHandle(void **handle); + FMOD_RESULT start(); + FMOD_RESULT stop(); + +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT recordEnumerate(); + FMOD_RESULT recordGetNumDrivers(int *numdrivers); + FMOD_RESULT recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordStart (FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop); + FMOD_RESULT recordStop (FMOD_RECORDING_INFO *recordinfo); + FMOD_RESULT recordGetPosition (FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + FMOD_RESULT recordLock (FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT recordUnlock (FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); +#endif + + static FMOD_RESULT F_CALLBACK getNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverNameCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen); + static FMOD_RESULT F_CALLBACK getDriverCapsCallback (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK startCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK stopCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + +#ifdef FMOD_SUPPORT_RECORDING + static FMOD_RESULT F_CALLBACK recordGetNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordStartCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop); + static FMOD_RESULT F_CALLBACK recordStopCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo); + static FMOD_RESULT F_CALLBACK recordGetPositionCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK recordLockCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + static FMOD_RESULT F_CALLBACK recordUnlockCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); +#endif + }; +} + +#endif /* #ifdef FMOD_SUPPORT_ASIO */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_output_dsound.cpp b/win32/src/fmod_output_dsound.cpp new file mode 100755 index 0000000..907c7c3 --- /dev/null +++ b/win32/src/fmod_output_dsound.cpp @@ -0,0 +1,3211 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSOUND + +#include "fmod.h" +#include "fmod_channelpool.h" +#include "fmod_codec_wav.h" +#include "fmod_debug.h" +#include "fmod_file.h" +#include "fmod_file_disk.h" +#include "fmod_channel_dsound.h" +#include "fmod_channel_dsound_eax2.h" +#include "fmod_channel_dsound_eax3.h" +#include "fmod_channel_dsound_eax4.h" +#include "fmod_channel_dsound_i3dl2.h" +#include "fmod_downmix.h" +#include "fmod_output_dsound.h" +#include "fmod_sample_dsound.h" +#include "fmod_speakermap.h" +#include "fmod_stringw.h" +#include "fmod_systemi.h" +#include "fmod_os_misc.h" + +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif +#include <dsound.h> + +namespace FMOD +{ + +//#define USE_SOFTWAREBUFFERS +//#define DISABLE_SOFTWAREBUFFERS +#define USE_HARDWARE2DBUFFERS + +static const FMOD_GUID IID_IDirectSound3DListener = { 0x279AFA84, 0x4981, 0x11CE, { 0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60} }; +static const FMOD_GUID IID_IDirectSound3DBuffer = { 0x279AFA86, 0x4981, 0x11CE, { 0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60} }; +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} }; +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} }; + +static const FMOD_GUID DS3DALG_NO_VIRTUALIZATION = { 0xc241333f, 0x1c1b, 0x11d2, { 0x94,0xf5,0x00,0xc0,0x4f,0xc2,0x8a,0xca} }; +static const FMOD_GUID DS3DALG_HRTF_LIGHT = { 0xc2413342, 0x1c1b, 0x11d2, { 0x94,0xf5,0x00,0xc0,0x4f,0xc2,0x8a,0xca} }; +static const FMOD_GUID DS3DALG_HRTF_FULL = { 0xc2413340, 0x1c1b, 0x11d2, { 0x94,0xf5,0x00,0xc0,0x4f,0xc2,0x8a,0xca} }; + + +FMOD_OUTPUT_DESCRIPTION_EX dsoundoutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputDSound::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputDSound::getDescriptionEx() +{ + FMOD_memset(&dsoundoutput, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + dsoundoutput.name = "FMOD DirectSound Output"; + dsoundoutput.version = 0x00010100; + dsoundoutput.polling = true; + dsoundoutput.getnumdrivers = &OutputDSound::getNumDriversCallback; + dsoundoutput.getdriverinfo = &OutputDSound::getDriverInfoCallback; + dsoundoutput.getdriverinfow = &OutputDSound::getDriverInfoWCallback; + dsoundoutput.close = &OutputDSound::closeCallback; + dsoundoutput.start = &OutputDSound::startCallback; + dsoundoutput.stop = &OutputDSound::stopCallback; + dsoundoutput.update = &OutputDSound::updateCallback; + dsoundoutput.gethandle = &OutputDSound::getHandleCallback; + dsoundoutput.getposition = &OutputDSound::getPositionCallback; + dsoundoutput.lock = &OutputDSound::lockCallback; + dsoundoutput.unlock = &OutputDSound::unlockCallback; + + /* + Private members + */ + dsoundoutput.getsamplemaxchannels = &OutputDSound::getSampleMaxChannelsCallback; + dsoundoutput.getdrivercapsex2 = &OutputDSound::getDriverCapsExCallback; + dsoundoutput.initex = &OutputDSound::initExCallback; + dsoundoutput.createsample = &OutputDSound::createSampleCallback; + dsoundoutput.getsoundram = &OutputDSound::getSoundRAMCallback; +#ifdef FMOD_SUPPORT_RECORDING + dsoundoutput.record_getnumdrivers = &OutputDSound::recordGetNumDriversCallback; + dsoundoutput.record_getdriverinfo = &OutputDSound::recordGetDriverInfoCallback; + dsoundoutput.record_getdriverinfow = &OutputDSound::recordGetDriverInfoWCallback; + dsoundoutput.record_start = &OutputDSound::recordStartCallback; + dsoundoutput.record_stop = &OutputDSound::recordStopCallback; + dsoundoutput.record_getposition = &OutputDSound::recordGetPositionCallback; + dsoundoutput.record_lock = &OutputDSound::recordLockCallback; + dsoundoutput.record_unlock = &OutputDSound::recordUnlockCallback; +#endif + dsoundoutput.reverb_setproperties = &OutputDSound::setReverbPropertiesCallback; + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + dsoundoutput.getmemoryused = &OutputDSound::getMemoryUsedCallback; +#endif + + dsoundoutput.mType = FMOD_OUTPUTTYPE_DSOUND; + dsoundoutput.mSize = sizeof(OutputDSound); + + return &dsoundoutput; +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + This has to be done to selectively load different functions because of different DLL versions. + + [SEE_ALSO] +*/ +FMOD_RESULT OutputDSound::registerDLL() +{ + char szPath[512]; + char szFile[512]; + HMODULE ddrawmodule = 0; + + if (mDLLInitialized) + { + return FMOD_OK; + } + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::registerDLL", "Register DLL\n")); + + if (!mDSoundModule) + { + mDSoundModule = LoadLibrary("dsound.dll"); + } + + if (mInitFlags & (FMOD_INIT_DSOUND_HRTFNONE | FMOD_INIT_DSOUND_HRTFLIGHT | FMOD_INIT_DSOUND_HRTFFULL)) + { + mDSound3DModule = LoadLibrary("dsound3d.dll"); + } + + mDirectXVersion = 3; /* Start off thinking it is dx3, now try to find dx8 then dx9 */ + + /* + Query dsound.dll for ds interfaces. + */ + mDirectSoundCreate = (PFN_DSCREATE8)GetProcAddress(mDSoundModule,"DirectSoundCreate8"); + if (mDirectSoundCreate) + { + mDirectXVersion = 8; + } + else + { + mDirectSoundCreate = (PFN_DSCREATE8)GetProcAddress(mDSoundModule,"DirectSoundCreate"); + if (!mDirectSoundCreate) + { + return FMOD_ERR_OUTPUT_INIT; + } + } + + mDirectSoundEnumerate = (PFN_DSENUMERATE)GetProcAddress(mDSoundModule,"DirectSoundEnumerateW"); + + if (mDirectXVersion >= 8 && GetSystemDirectory( szPath, MAX_PATH ) != 0 ) + { + FMOD_RESULT result; + DiskFile diskfp; + File *fp = &diskfp; + + FMOD_strcpy( szFile, szPath ); + FMOD_strcat( szFile, "\\d3d9.dll"); + + fp->init(mSystem, 0,0); + result = fp->open(szFile, 0); + if (result == FMOD_OK) + { + fp->close(); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::registerDLL", "Detected DIRECTX 9\n")); + mDirectXVersion = 9; + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::registerDLL", "Detected DIRECTX 8\n")); + } + } + else + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::registerDLL", "Detected DIRECTX %d\n", mDirectXVersion)); + } + + mDirectSoundCaptureCreate = (PFN_DSCCREATE8)GetProcAddress(mDSoundModule, "DirectSoundCaptureCreate8"); + if (mDirectSoundCaptureCreate) + { + mDirectSoundCaptureCreate = (PFN_DSCCREATE8)GetProcAddress(mDSoundModule, "DirectSoundCaptureCreate"); + } + + mDirectSoundCaptureEnumerate = (PFN_DSCENUMERATE)GetProcAddress(mDSoundModule, "DirectSoundCaptureEnumerateW"); + + mDLLInitialized = true; + + return FMOD_OK; +} + + +/* + [DESCRIPTION] + Callback to enumerate each found output driver + + [PARAMETERS] + + [RETURN_VALUE] + TRUE + + [REMARKS] + The first enumerated driver is skipped as it has no GUID, (it's a duplicate + anyway), the next driver enumerated is always the default, therefore it will be + in element 0 of FMODs driver list as required. + + [SEE_ALSO] +*/ +BOOL CALLBACK FMOD_Output_DSound_EnumProc(LPGUID lpGUID, LPCWSTR lpszDesc, LPCWSTR lpszDrvName, LPVOID lpContext) +{ + FMOD::OutputDSound *outputdsound = (FMOD::OutputDSound *)lpContext; + + // Don't allow more drivers than the maximum number of drivers + if (outputdsound->mNumDrivers < FMOD_OUTPUT_MAXDRIVERS) + { + // Don't add pseudo driver "Primary..." (no GUID), it is just a dupe anyway + if (lpGUID) + { + outputdsound->mDriverName[outputdsound->mNumDrivers] = (short *)FMOD_Memory_Calloc((FMOD_strlenW((short *)lpszDesc) + 1) * sizeof(short)); + if (outputdsound->mDriverName[outputdsound->mNumDrivers]) + { + FMOD_strncpyW(outputdsound->mDriverName[outputdsound->mNumDrivers], (short *)lpszDesc, FMOD_strlenW((short *)lpszDesc)); + } + + FMOD_memcpy(&outputdsound->mGUID[outputdsound->mNumDrivers], lpGUID, sizeof(GUID)); + + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, (short *)lpszDesc, FMOD_STRING_MAXNAMELEN); + FMOD_wtoa(driverName); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "FMOD_Output_DSound_EnumProc", "Enumerating \"%s\"\n", driverName)); + } + + outputdsound->mNumDrivers++; + } + } + + return TRUE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::enumerate() +{ + FMOD_RESULT result; + + if (mEnumerated) + { + return FMOD_OK; + } + + result = registerDLL(); + if (result != FMOD_OK) + { + return result; + } + + for (int i = 0; i < mNumDrivers; i++) + { + if (mDriverName[i]) + { + FMOD_Memory_Free(mDriverName[i]); + mDriverName[i] = NULL; + } + } + mNumDrivers = 0; + + if (mDirectSoundEnumerate) + { + (*mDirectSoundEnumerate)((LPDSENUMCALLBACKW)FMOD_Output_DSound_EnumProc, (void *)this); + } + + mEnumerated = true; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getNumDrivers(int *numdrivers) +{ + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mEnumerated) + { + FMOD_RESULT result; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + *numdrivers = mNumDrivers; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getDriverInfo(int driver, char *name, int namelen, FMOD_GUID *guid) +{ + if (!mEnumerated) + { + FMOD_RESULT result; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (driver < 0 || driver >= mNumDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, mDriverName[driver], FMOD_STRING_MAXNAMELEN - 1); + FMOD_wtoa(driverName); + + FMOD_strncpy(name, (char *)driverName, namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + FMOD_memcpy(guid, &mGUID[driver], sizeof(FMOD_GUID)); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getDriverInfoW(int driver, short *name, int namelen, FMOD_GUID *guid) +{ + if (!mEnumerated) + { + FMOD_RESULT result; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (driver < 0 || driver >= mNumDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + FMOD_strncpyW(name, mDriverName[driver], namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + FMOD_memcpy(guid, &mGUID[driver], sizeof(FMOD_GUID)); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getDriverCapsEx(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels) +{ + FMOD_RESULT result; + DSCAPS dscaps; + HRESULT hr; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "Register DLL\n")); + + result = registerDLL(); + if (result != FMOD_OK) + { + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "Enumerate Drivers\n")); + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + + if (!mNumDrivers) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "No sound devices!\n")); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + if (!mCoInitialized) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "CoInitialize\n")); + hr = CoInitialize(0); + if (hr == S_OK || hr == S_FALSE) + { + mCoInitialized = true; + } + } + + /* + CREATE DIRECTSOUND OBJECT + */ + hr = !DS_OK; + if (mDirectSoundCreate) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "DirectSoundCreate8 : id = %d\n", id)); + + if (id >= 0) + { + hr = (*mDirectSoundCreate)(&mGUID[id], (LPDIRECTSOUND8 *)&mDirectSound, 0); + } + else + { + hr = (*mDirectSoundCreate)(0, (LPDIRECTSOUND8 *)&mDirectSound, 0); + } + } + if (hr != DS_OK) + { + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + /* + PROCESS DIRECTSOUND CAPS FOR THIS CARD + */ + FMOD_memset(&dscaps, 0, sizeof(DSCAPS)); + dscaps.dwSize = sizeof(DSCAPS); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "GetCaps\n")); + + hr = mDirectSound->GetCaps(&dscaps); + if (hr != DS_OK) + { + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + if (minfrequency) + { + *minfrequency = dscaps.dwMinSecondarySampleRate; + } + if (maxfrequency) + { + *maxfrequency = dscaps.dwMaxSecondarySampleRate; + } + + #ifdef USE_SOFTWAREBUFFERS + *num2dchannels = 0; + #else + *num2dchannels = dscaps.dwFreeHwMixingStreamingBuffers; + #endif + + if (!*num2dchannels) + { +#ifdef DISABLE_SOFTWAREBUFFERS + *num2dchannels = 0; +#else + *num2dchannels = 32; +#endif + } + + if (!mSystem->mMaxHardwareChannels3D) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "User set max hardware channels to 0. Avoiding any hardware calls.\n")); + *num3dchannels = 0; + } + else if (dscaps.dwMaxHw3DAllBuffers == 0 || dscaps.dwMaxHw3DAllBuffers > 200 || (dscaps.dwFlags & DSCAPS_EMULDRIVER)) /* > 200 has gotta be a lie (some drivers report 256 hw channels then crash on the dx voice manager code.. reject them) Also reject cards that have emulated drivers. */ + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "Bad Caps or emulated driver. Reverting back to software\n")); + *num3dchannels = 0; + } + else if ((id >= 0) && !FMOD_strcmpW(mDriverName[id], (short *)L"Realtek HD")) + { + // Realtek HA Audio EAX2 initialization causes memory corruption (32-bit XP64, driver 5.10.0.5657); disable 3D as a work-around. + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::getDriverCapsEx", "Realtek HD Audio EAX2 bug work-around. Reverting back to software\n")); + *num3dchannels = 0; + } + else + { + *num3dchannels = dscaps.dwFreeHw3DStreamingBuffers; + } + + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_HARDWARE_EMULATED); + } + + if (*num3dchannels) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_HARDWARE); + } + else + { + *minfrequency = DSBFREQUENCY_MIN; + *maxfrequency = DSBFREQUENCY_MAX; + } + + *totalchannels = *num2dchannels + *num3dchannels; + + if (mDirectXVersion >= 9) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_MULTICHANNEL); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM8); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM16); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM24); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM32); + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT); + } + + /* + Get hadware reverb caps + */ + if (*num3dchannels) + { + #if defined(FMOD_SUPPORT_EAX) + if (initEAX4()) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_REVERB_EAX4); + mReverbVersion = REVERB_VERSION_EAX4; + closeEAX4(); + } + else + { + closeEAX4(); + + if (initEAX3()) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_REVERB_EAX3); + mReverbVersion = REVERB_VERSION_EAX3; + closeEAX3(); + } + else + { + closeEAX3(); + + if (initEAX2()) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_REVERB_EAX2); + mReverbVersion = REVERB_VERSION_EAX2; + } + + closeEAX2(); + } + } + #endif + + #if defined(FMOD_SUPPORT_I3DL2) + if (mReverbVersion != REVERB_VERSION_EAX4 && mReverbVersion != REVERB_VERSION_EAX3 && mReverbVersion != REVERB_VERSION_EAX2) + { + if (initI3DL2()) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_REVERB_I3DL2); + mReverbVersion = REVERB_VERSION_I3DL2; + closeI3DL2(); + } + else + { + closeI3DL2(); + } + } + #endif + } + + + /* + Find out the control panel speaker mode. + */ + { + DWORD speakermode = 0; + + hr= mDirectSound->GetSpeakerConfig(&speakermode); + + speakermode &= 0xFFFF; + + switch (speakermode) + { + case DSSPEAKER_DIRECTOUT: + { + break; + } + case DSSPEAKER_HEADPHONE: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_STEREO; + break; + } + case DSSPEAKER_MONO: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_MONO; + break; + } + case DSSPEAKER_STEREO: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_STEREO; + break; + } + case DSSPEAKER_QUAD: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_QUAD; + break; + } + case DSSPEAKER_SURROUND: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_SURROUND; + break; + } + case DSSPEAKER_5POINT1: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_5POINT1; + break; + } + case DSSPEAKER_7POINT1: + case 8: + { + *controlpanelspeakermode = FMOD_SPEAKERMODE_7POINT1; + break; + } + } + } + + + close(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata) +{ + FMOD_RESULT result; + HRESULT hr; + DSBUFFERDESC dsbdesc; + DSCAPS dscaps; + GUID guid; + WAVEFORMATEX pcmwf; + LPDIRECTSOUNDBUFFER primary = 0; + int num2dchannels = 0; + int num3dchannels = 0; + + mNeedToCommit = false; + mFeaturesReverb = 0; + mBufferReverb = 0; + mSampleReverb = 0; + mReverbVersion = REVERB_VERSION_NONE; + + + result = registerDLL(); + if (result != FMOD_OK) + { + return result; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Enumerate Drivers\n")); + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + + if (!mNumDrivers) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::init", "No sound devices!\n")); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + if (!mCoInitialized) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "CoInitialize\n")); + hr = CoInitialize(0); + if (hr == S_OK || hr == S_FALSE) + { + mCoInitialized = true; + } + } + + mInitFlags = flags; + + /* + CREATE DIRECTSOUND OBJECT + */ + hr = !DS_OK; + if (mDirectSoundCreate) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "DirectSoundCreate8 : mSelectedDriver = %d\n", selecteddriver)); + hr = (*mDirectSoundCreate)(&mGUID[selecteddriver], (LPDIRECTSOUND8 *)&mDirectSound, 0); + } + if (hr != DS_OK) + { + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + /* + SET COOPERATIVE LEVEL + */ + mGlobalFocus = false; + if (!extradriverdata) + { + mGlobalFocus = true; + extradriverdata = GetDesktopWindow(); /* As it is global focus, desktop is the safest bet and we don't lose sound anywhere. */ + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "SetCooperativeLevel\n")); + + hr = mDirectSound->SetCooperativeLevel((HWND)extradriverdata, DSSCL_PRIORITY); + if (hr != DS_OK) + { + close(); + return FMOD_ERR_OUTPUT_ALLOCATED; + } + + /* + FIND DIRECTSOUND SPEAKERMODE + */ + #if 1 + mSpeakerMode = *speakermode; + #else + { + DWORD speakermode = 0; + + hr= mDirectSound->GetSpeakerConfig(&speakermode); + + speakermode &= 0xFFFF; + + switch (speakermode) + { + case DSSPEAKER_DIRECTOUT: + { + break; + } + case DSSPEAKER_HEADPHONE: + { + mSpeakerMode = FMOD_SPEAKERMODE_STEREO; + break; + } + case DSSPEAKER_MONO: + { + mSpeakerMode = FMOD_SPEAKERMODE_MONO; + break; + } + case DSSPEAKER_STEREO: + { + mSpeakerMode = FMOD_SPEAKERMODE_STEREO; + break; + } + case DSSPEAKER_QUAD: + { + mSpeakerMode = FMOD_SPEAKERMODE_QUAD; + break; + } + case DSSPEAKER_SURROUND: + { + mSpeakerMode = FMOD_SPEAKERMODE_SURROUND; + break; + } + case DSSPEAKER_5POINT1: + { + mSpeakerMode = FMOD_SPEAKERMODE_5POINT1; + break; + } + case DSSPEAKER_7POINT1: + case 8: + { + mSpeakerMode = FMOD_SPEAKERMODE_7POINT1; + break; + } + } + } + #endif + + /* + PROCESS DIRECTSOUND CAPS FOR THIS CARD + */ + FMOD_memset(&dscaps, 0, sizeof(DSCAPS)); + dscaps.dwSize = sizeof(DSCAPS); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "GetCaps\n")); + + hr = mDirectSound->GetCaps(&dscaps); + + mMinFrequency = (float)dscaps.dwMinSecondarySampleRate; + mMaxFrequency = (float)dscaps.dwMaxSecondarySampleRate; + + #ifdef USE_SOFTWAREBUFFERS + num2dchannels = 0; + #else + num2dchannels = dscaps.dwFreeHwMixingStreamingBuffers; + #endif + + if (!num2dchannels) + { +#ifdef DISABLE_SOFTWAREBUFFERS + mUseSoftware2DBuffers = false; + num2dchannels = 0; +#else + mUseSoftware2DBuffers = true; + num2dchannels = 32; +#endif + } + else + { + mUseSoftware3DBuffers = false; + } + + if (!mSystem->mMaxHardwareChannels3D) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "User set max hardware channels to 0. Avoiding any hardware calls.\n")); + num3dchannels = 0; + } + else if (dscaps.dwMaxHw3DAllBuffers == 0 || dscaps.dwMaxHw3DAllBuffers > 200 || (dscaps.dwFlags & DSCAPS_EMULDRIVER)) /* > 200 has gotta be a lie (some drivers report 256 hw channels then crash on the dx voice manager code.. reject them) Also reject cards that have emulated drivers. */ + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Bad Caps or emulated driver. Reverting back to software\n")); + num3dchannels = 0; + } + else if ((selecteddriver >= 0) && !FMOD_strcmpW(mDriverName[selecteddriver], (short *)L"Realtek HD")) + { + // Realtek HD Audio EAX2 initialization causes memory corruption (32-bit XP64, driver 5.10.0.5657); disable 3D as a work-around. + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Realtek HD Audio EAX2 bug work-around. Reverting back to software\n")); + num3dchannels = 0; + } + else + { + #ifdef USE_SOFTWAREBUFFERS + num3dchannels = 0; + #else + num3dchannels = dscaps.dwFreeHw3DStreamingBuffers; + #endif + } + + if (!num3dchannels) + { +#ifdef DISABLE_SOFTWAREBUFFERS + mUseSoftware3DBuffers = false; + num3dchannels = 0; +#else + mUseSoftware3DBuffers = true; + mMinFrequency = DSBFREQUENCY_MIN; + mMaxFrequency = DSBFREQUENCY_MAX; + num3dchannels = 32; +#endif + } + else + { + mUseSoftware3DBuffers = false; + } + + /* + Handle min/max channel allocation + */ + if (num2dchannels < mSystem->mMinHardwareChannels2D) + { + num2dchannels = 0; + } + if (num3dchannels < mSystem->mMinHardwareChannels3D) + { + num3dchannels = 0; + } + if (num2dchannels > mSystem->mMaxHardwareChannels2D) + { + num2dchannels = mSystem->mMaxHardwareChannels2D; + } + if (num3dchannels > mSystem->mMaxHardwareChannels3D) + { + num3dchannels = mSystem->mMaxHardwareChannels3D; + } + + /* + Remember buffersize + */ + mBufferLength = dspbufferlength; + mNumBuffers = dspnumbuffers; + + if (1) + { + int buffersizems; + + do + { + buffersizems = (mBufferLength * mNumBuffers) * 1000 / *outputrate; + + if (buffersizems < 500) + { + mNumBuffers++; + } + + } while (buffersizems < 500); + } + mMixAheadBlocks = mNumBuffers - dspnumbuffers; /* Remember what the user specified. */ + + /* + SET UP PRIMARY BUFFER + */ + FMOD_memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); /* Zero it out. */ + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbdesc.dwBufferBytes = 0; /* Buffer size is determined by sound hardware. */ + dsbdesc.lpwfxFormat = 0; /* Must be 0 for primary buffers. */ + + if (mDirectXVersion < 8) + { + dsbdesc.dwSize -= sizeof(GUID); + } + + dsbdesc.dwFlags |= DSBCAPS_CTRL3D; + + /* + CREATE ONE PRIMARY BUFFER + */ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Create Primary Buffer\n")); + + hr = mDirectSound->CreateSoundBuffer(&dsbdesc, &primary, 0); + if (FAILED(hr)) + { + close(); + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + + /* + SET PRIMARY BUFFER'S FORMAT + */ + FMOD_memset(&pcmwf, 0, sizeof(WAVE_FORMATEX)); + pcmwf.wFormatTag = WAVE_FORMAT_PCM; + pcmwf.nChannels = 2; + pcmwf.wBitsPerSample = 16; + pcmwf.nBlockAlign = pcmwf.nChannels * pcmwf.wBitsPerSample / 8; + pcmwf.nSamplesPerSec = 44100; + pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign; + pcmwf.cbSize = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Set Primary Buffer Format\n")); + + hr = primary->SetFormat(&pcmwf); + if (hr != DS_OK) + { + close(); + return FMOD_ERR_OUTPUT_FORMAT; + } + + /* + GET DS3D LISTENER INTERFACE + */ + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Getting Listener Interface\n")); + + FMOD_memcpy(&guid, &IID_IDirectSound3DListener, sizeof(GUID)); + + hr = primary->QueryInterface(guid, (void **)&mDirectSoundListener); + if (hr != DS_OK) + { + num3dchannels = 0; + } + + primary->Release(); + + /* + Initialise hardware reverb + */ + if (num3dchannels && !mUseSoftware3DBuffers) + { + #if defined(FMOD_SUPPORT_EAX) + + if (initEAX4()) + { + mReverbVersion = REVERB_VERSION_EAX4; + } + else + { + closeEAX4(); + + if (initEAX3()) + { + mReverbVersion = REVERB_VERSION_EAX3; + } + else + { + closeEAX3(); + + if (initEAX2()) + { + mReverbVersion = REVERB_VERSION_EAX2; + } + else + { + closeEAX2(); + } + } + } + #endif + + #if defined(FMOD_SUPPORT_I3DL2) + if (mReverbVersion != REVERB_VERSION_EAX4 && mReverbVersion != REVERB_VERSION_EAX3 && mReverbVersion != REVERB_VERSION_EAX2) + { + if (initI3DL2()) + { + mReverbVersion = REVERB_VERSION_I3DL2; + } + } + #endif + } + + /* + SET UP 2D CHANNEL POOL + */ + if (num2dchannels) + { + int count; + + mChannelPool = FMOD_Object_Alloc(ChannelPool); + if (!mChannelPool) + { + close(); + return FMOD_ERR_MEMORY; + } + + result = mChannelPool->init(mSystem, this, num2dchannels); + if (result != FMOD_OK) + { + close(); + return result; + } + + mChannel2D = (ChannelDSound *)FMOD_Memory_Calloc(sizeof(ChannelDSound) * num2dchannels); + if (!mChannel2D) + { + close(); + return FMOD_ERR_MEMORY; + } + + for (count = 0; count < num2dchannels; count++) + { + new (&mChannel2D[count]) ChannelDSound; + CHECK_RESULT(mChannelPool->setChannel(count, &mChannel2D[count], 0)); + } + } + + /* + SET UP 3D CHANNEL POOL + */ + if (num3dchannels) + { + int count; + + mChannelPool3D = FMOD_Object_Alloc(ChannelPool); + if (!mChannelPool3D) + { + close(); + return FMOD_ERR_MEMORY; + } + + result = mChannelPool3D->init(mSystem, this, num3dchannels); + if (result != FMOD_OK) + { + close(); + return result; + } + + switch(mReverbVersion) + { +#if defined(FMOD_SUPPORT_EAX) + case REVERB_VERSION_EAX4: + { + mChannel3D = (ChannelDSoundEAX4 *)FMOD_Memory_Calloc(sizeof(ChannelDSoundEAX4) * num3dchannels); + break; + } + case REVERB_VERSION_EAX3: + { + mChannel3D = (ChannelDSoundEAX3 *)FMOD_Memory_Calloc(sizeof(ChannelDSoundEAX3) * num3dchannels); + break; + } + case REVERB_VERSION_EAX2: + { + mChannel3D = (ChannelDSoundEAX2 *)FMOD_Memory_Calloc(sizeof(ChannelDSoundEAX2) * num3dchannels); + break; + } +#endif +#if defined(FMOD_SUPPORT_I3DL2) + case REVERB_VERSION_I3DL2: + { + mChannel3D = (ChannelDSoundI3DL2 *)FMOD_Memory_Calloc(sizeof(ChannelDSoundI3DL2) * num3dchannels); + break; + } +#endif + default: + { + mChannel3D = (ChannelDSound *)FMOD_Memory_Calloc(sizeof(ChannelDSound) * num3dchannels); + break; + } + } + + if (!mChannel3D) + { + close(); + return FMOD_ERR_MEMORY; + } + + + for (count = 0; count < num3dchannels; count++) + { + + switch(mReverbVersion) + { +#if defined(FMOD_SUPPORT_EAX) + case REVERB_VERSION_EAX4: + { + new (&mChannel3D[count]) ChannelDSoundEAX4; + break; + } + case REVERB_VERSION_EAX3: + { + new (&mChannel3D[count]) ChannelDSoundEAX3; + break; + } + case REVERB_VERSION_EAX2: + { + new (&mChannel3D[count]) ChannelDSoundEAX2; + break; + } +#endif +#if defined(FMOD_SUPPORT_I3DL2) + case REVERB_VERSION_I3DL2: + { + new (&mChannel3D[count]) ChannelDSoundI3DL2; + break; + } +#endif + default: + { + new (&mChannel3D[count]) ChannelDSound; + break; + } + } + + CHECK_RESULT(mChannelPool3D->setChannel(count, &mChannel3D[count])); + } + } + + mLastDopplerScale = -1.0f; + mLastDistancerScale = -1.0f; + mLastRolloffrScale = -1.0f; + + mBufferMemoryCurrent = mBufferMemoryMax = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::init", "Done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::close() +{ + int count; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "\n")); + + switch (mReverbVersion) + { +#if defined(FMOD_SUPPORT_EAX) + case REVERB_VERSION_EAX4: + { + closeEAX4(); + break; + } + case REVERB_VERSION_EAX3: + { + closeEAX3(); + break; + } + case REVERB_VERSION_EAX2: + { + closeEAX2(); + break; + } +#endif +#if defined (FMOD_SUPPORT_I3DL2) + case REVERB_VERSION_I3DL2: + { + closeI3DL2(); + break; + } +#endif + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "Free channel pool 2d\n")); + + if (mChannelPool) + { + mChannelPool->release(); + mChannelPool = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "Free channel pool 3d\n")); + + if (mChannelPool3D) + { + mChannelPool3D->release(); + mChannelPool3D = 0; + } + if (mChannel2D) + { + FMOD_Memory_Free(mChannel2D); + mChannel2D = 0; + } + if (mChannel3D) + { + FMOD_Memory_Free(mChannel3D); + mChannel3D = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "Release directsound listener\n")); + + if (mDirectSoundListener) + { + mDirectSoundListener->Release(); + mDirectSoundListener = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "Release directsound object\n")); + + if (mDirectSound) + { + mDirectSound->Release(); + mDirectSound = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "FreeLibrary on dsound3d.dll\n")); + + if (mDSound3DModule) + { + FreeLibrary(mDSound3DModule); + mDSound3DModule = 0; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "FreeLibrary on dsound.dll\n")); + + if (mDSoundModule) + { + FreeLibrary(mDSoundModule); + mDSoundModule = 0; + mDLLInitialized = false; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "Free driver list\n")); + + for (count=0; count < mNumDrivers; count++) + { + if (mDriverName[count]) + { + FMOD_Memory_Free(mDriverName[count]); + mDriverName[count] = 0; + } + } + + mEnumerated = false; + mNumDrivers = 0; + +#ifdef FMOD_SUPPORT_RECORDING + for (count=0; count < mRecordNumDrivers; count++) + { + if (mRecordDriverName[count]) + { + FMOD_Memory_Free(mRecordDriverName[count]); + mRecordDriverName[count] = 0; + } + } + + mRecordEnumerated = false; + mRecordNumDrivers = 0; +#endif + + if (mCoInitialized) + { + CoUninitialize(); + mCoInitialized = false; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputDSound::close", "done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getHandle(void **handle) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = mDirectSound; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::update() +{ + HRESULT hr; + int numlisteners; + FMOD_RESULT result; + float dopplerscale, distancescale, rolloffscale; + FMOD_VECTOR pos = { 0.0f, 0.0f, 0.0f }, vel = { 0.0f, 0.0f, 0.0f }; + FMOD_VECTOR front = { 0.0f, 0.0f, 1.0f }, top = { 0.0f, 1.0f, 0.0f }; + + if (!mDirectSoundListener || !mChannelPool3D) + { + return FMOD_OK; + } + + result = mSystem->get3DNumListeners(&numlisteners); + if (result != FMOD_OK) + { + return result; + } + + if (numlisteners == 1) + { + result = mSystem->get3DListenerAttributes(0, &pos, &vel, &front, &top); + if (result != FMOD_OK) + { + return result; + } + } + + result = mSystem->get3DSettings(&dopplerscale, &distancescale, &rolloffscale); + if (result != FMOD_OK) + { + return result; + } + + if (dopplerscale != mLastDopplerScale || + distancescale != mLastDistancerScale || + rolloffscale != mLastRolloffrScale || + mSystem->mListener[0].mMoved || + mSystem->mListener[0].mRotated) + { + DS3DLISTENER dsListenerParams; + + FMOD_memset(&dsListenerParams, 0, sizeof(dsListenerParams)); + dsListenerParams.dwSize = sizeof(DS3DLISTENER); + dsListenerParams.flDistanceFactor = 1.0f / distancescale; + dsListenerParams.flDopplerFactor = dopplerscale; + dsListenerParams.flRolloffFactor = rolloffscale; + dsListenerParams.vOrientFront.x = front.x; + dsListenerParams.vOrientFront.y = front.y; + dsListenerParams.vOrientFront.z = front.z; + dsListenerParams.vOrientTop.x = top.x; + dsListenerParams.vOrientTop.y = top.y; + dsListenerParams.vOrientTop.z = top.z; + dsListenerParams.vPosition.x = pos.x; + dsListenerParams.vPosition.y = pos.y; + dsListenerParams.vPosition.z = pos.z; + dsListenerParams.vVelocity.x = vel.x; + dsListenerParams.vVelocity.y = vel.y; + dsListenerParams.vVelocity.z = vel.z; + + if (mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED) + { + dsListenerParams.vOrientFront.z = -dsListenerParams.vOrientFront.z; + dsListenerParams.vOrientTop.z = -dsListenerParams.vOrientTop.z; + dsListenerParams.vPosition.z = -dsListenerParams.vPosition.z; + dsListenerParams.vVelocity.z = -dsListenerParams.vVelocity.z; + } + + hr = mDirectSoundListener->SetAllParameters(&dsListenerParams, DS3D_DEFERRED); //mInitFlags & FMOD_INIT_DSOUND_DEFERRED ? DS3D_DEFERRED : DS3D_IMMEDIATE); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::update", "IDirectSoundListener::SetAllParameters returned errcode %08X\n", hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + mNeedToCommit = true; + + mLastDopplerScale = dopplerscale; + mLastDistancerScale = distancescale; + mLastRolloffrScale = rolloffscale; + + /* + Update channels using tweaked distance models or fudge positioning for multilistener stuff. + */ + { + int numchannels, count; + ChannelReal *channelreal; + ChannelDSound *channeldsound; + + if (mChannelPool3D) + { + result = mChannelPool3D->getNumChannels(&numchannels); + if (result != FMOD_OK) + { + return result; + } + } + else + { + numchannels = 0; + } + + for (count = 0; count < numchannels; count++) + { + result = mChannelPool3D->getChannel(count, &channelreal); + if (result != FMOD_OK) + { + return result; + } + + channeldsound = SAFE_CAST(ChannelDSound, channelreal); + if (!channeldsound) + { + return FMOD_ERR_INTERNAL; + } + + if (numlisteners > 1 || channeldsound->mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF) || mSystem->mRolloffCallback || (channeldsound->mParent && channeldsound->mParent->m3DDopplerLevel != 1.0f)) + { + result = channeldsound->set3DAttributes(); + if (result != FMOD_OK) + { + return result; + } + } + } + } + } + + if (mNeedToCommit) + { + if (mDirectSoundListener->CommitDeferredSettings() != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::update", "IDirectSoundListener::CommitDeferredSettings returned errcode %08X\n", hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mNeedToCommit = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample) +{ + WAVE_FORMATEXTENSIBLE pcmwf; + DSBUFFERDESC dsbdesc; + int bits; + HRESULT hr; + FMOD_RESULT result; + SampleDSound *newsample = 0; + GUID nullguid = { 0x00000000L, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::createSample", "length %d, channels %d, format %d, mode %08x\n", waveformat ? waveformat->lengthpcm : 0, waveformat ? waveformat->channels : 0, waveformat ? waveformat->format : FMOD_SOUND_FORMAT_NONE, mode)); + + if (!sample) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (waveformat) + { + result = SoundI::getBitsFromFormat(waveformat->format, &bits); + if (result != FMOD_OK) + { + return result; + } + + if (!bits && waveformat->format != FMOD_SOUND_FORMAT_NONE) + { + if (waveformat->format == FMOD_SOUND_FORMAT_MPEG || waveformat->format == FMOD_SOUND_FORMAT_IMAADPCM) + { + return FMOD_ERR_NEEDSSOFTWARE; + } + else + { + return FMOD_ERR_FORMAT; + } + } + + if (mDirectXVersion < 9 && (bits > 16 || waveformat->channels > 2)) + { + return FMOD_ERR_FORMAT; + } + } + + if (*sample == 0) + { + newsample = FMOD_Object_Calloc(SampleDSound); + if (!newsample) + { + return FMOD_ERR_MEMORY; + } + } + else + { + newsample = SAFE_CAST(SampleDSound, *sample); + } + + if (!waveformat) + { + *sample = newsample; + return FMOD_OK; + } + + newsample->mFormat = waveformat->format; + + /* + ALLOCATE NEW SECONDARY BUFFER FOR THIS SAMPLE DEFINITION + */ + FMOD_memset(&dsbdesc,0,sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = sizeof(dsbdesc); + dsbdesc.dwBufferBytes = waveformat->lengthpcm * bits / 8 * waveformat->channels; + dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&pcmwf; + dsbdesc.guid3DAlgorithm = nullguid; + + newsample->mLengthBytes = dsbdesc.dwBufferBytes; + + if (mUseSoftware3DBuffers && mode & FMOD_3D) + { + if (mInitFlags & FMOD_INIT_DSOUND_HRTFNONE) + { + FMOD_memcpy(&dsbdesc.guid3DAlgorithm, &DS3DALG_NO_VIRTUALIZATION, sizeof(GUID)); + } + else if (mInitFlags & FMOD_INIT_DSOUND_HRTFLIGHT) + { + FMOD_memcpy(&dsbdesc.guid3DAlgorithm, &DS3DALG_HRTF_LIGHT, sizeof(GUID)); + } + else if (mInitFlags & FMOD_INIT_DSOUND_HRTFFULL) + { + FMOD_memcpy(&dsbdesc.guid3DAlgorithm, &DS3DALG_HRTF_FULL, sizeof(GUID)); + } + } + + dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME; + if (mGlobalFocus) + { + dsbdesc.dwFlags |= DSBCAPS_GLOBALFOCUS; + } + if (!(mode & FMOD_UNIQUE)) + { + dsbdesc.dwFlags |= DSBCAPS_LOCDEFER; + } + + newsample->mLOCSoftware = true; + if (mode & FMOD_3D) + { + if (!mUseSoftware3DBuffers) + { + newsample->mLOCSoftware = false; + } + + dsbdesc.dwFlags |= DSBCAPS_CTRL3D; + if (mode & FMOD_UNIQUE) + { + if (mUseSoftware3DBuffers) + { + dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE; + } + else + { + dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; + } + } + } + else + { + /* + Use software buffer if channelcount > 2 or it is a nonstandard bitdepth, hardware is bound to die if this is attempted. + */ + if (!mUseSoftware2DBuffers && waveformat->channels <= 2 && (waveformat->format == FMOD_SOUND_FORMAT_PCM8 || waveformat->format == FMOD_SOUND_FORMAT_PCM16)) if (!mUseSoftware2DBuffers) + { + newsample->mLOCSoftware = false; + } + + dsbdesc.dwFlags |= DSBCAPS_CTRLPAN; /* 2d sounds require pan */ + if (mode & FMOD_UNIQUE) + { + dsbdesc.dwFlags &= ~DSBCAPS_LOCDEFER; + + if (mUseSoftware2DBuffers) + { + dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE; + } + else + { + dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; + } + } + } + + FMOD_memset(&pcmwf,0,sizeof(WAVE_FORMATEXTENSIBLE)); + + if (mode & FMOD_3D && (waveformat->channels > 2 || (waveformat->format != FMOD_SOUND_FORMAT_PCM8 && waveformat->format != FMOD_SOUND_FORMAT_PCM16))) + { + return FMOD_ERR_FORMAT; + } + + /* + We could use WAVEFORMATEXTENSIBLE for everything, but virtually every time we try to use it a creative card falls over on it. + 1. Audigy NX bluescreens if you use WAVEFORMATEXTENSIBLE even for plain PCM, mono, 16bit voices. + 2. SBLive value reported to go silent if channelmask is 0, even though the microsoft docs say 0 = direct out! + 3. Even if you specify SPEAKER_STEREO as is recommended by Microsoft, the panning biases towards the left on some cards. + */ + if (mDirectXVersion >= 9 && !(mode & FMOD_3D) && (waveformat->channels > 2 || (waveformat->format != FMOD_SOUND_FORMAT_PCM8 && waveformat->format != FMOD_SOUND_FORMAT_PCM16))) + { + pcmwf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + } + else + { + pcmwf.Format.wFormatTag = WAVE_FORMAT_PCM; + } + + pcmwf.Format.nChannels = waveformat->channels; + pcmwf.Format.wBitsPerSample = bits; + pcmwf.Format.nBlockAlign = pcmwf.Format.nChannels * pcmwf.Format.wBitsPerSample / 8; + pcmwf.Format.nSamplesPerSec = DEFAULT_FREQUENCY; + pcmwf.Format.nAvgBytesPerSec = pcmwf.Format.nSamplesPerSec * pcmwf.Format.nBlockAlign; + + if (pcmwf.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + pcmwf.Format.cbSize = 22; + pcmwf.Samples.wValidBitsPerSample = bits; + + if (waveformat->channelmask & SPEAKER_WAVEFORMAT_MASK) + { + pcmwf.dwChannelMask = waveformat->channelmask & SPEAKER_WAVEFORMAT_MASK; + } + else + { + switch (mSpeakerMode) + { + case FMOD_SPEAKERMODE_MONO: /* 1 speaker setup. This includes 1 speaker. */ + { + pcmwf.dwChannelMask = SPEAKER_MONO; + break; + } + case FMOD_SPEAKERMODE_STEREO: /* 2 speaker setup. This includes front left, front right. */ + { + pcmwf.dwChannelMask = SPEAKER_STEREO; + break; + } + case FMOD_SPEAKERMODE_QUAD: /* 4 speaker setup. This includes front, center, left, rear and a subwoofer. Also known as a "quad" speaker configuration. */ + { + pcmwf.dwChannelMask = SPEAKER_QUAD; + break; + } + case FMOD_SPEAKERMODE_SURROUND: /* 5 speaker setup. This includes front, center, left, rear left, right right. */ + { + pcmwf.dwChannelMask = SPEAKER_SURROUND; + break; + } + case FMOD_SPEAKERMODE_5POINT1: /* 5.1 speaker setup. This includes front, center, left, rear left, rear right and a subwoofer. */ + { + pcmwf.dwChannelMask = SPEAKER_5POINT1; + break; + } + case FMOD_SPEAKERMODE_7POINT1: /* 7.1 speaker setup. This includes front, center, left, rear left, rear right, side left, side right and a subwoofer. */ + { + pcmwf.dwChannelMask = SPEAKER_7POINT1; + break; + } + default: /* There is no specific speakermode. Sound channels are mapped in order of input to output. See remarks for more information. */ + { + pcmwf.dwChannelMask = 0; + break; + } + }; + } + + if (waveformat->format == FMOD_SOUND_FORMAT_PCMFLOAT) + { + FMOD_memcpy(&pcmwf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); + } + else + { + FMOD_memcpy(&pcmwf.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)); + } + } + + /* + CREATE THE BUFFER + */ + hr = mDirectSound->CreateSoundBuffer(&dsbdesc, (LPDIRECTSOUNDBUFFER *)&newsample->mBuffer, NULL); + if (FAILED(hr)) + { + FMOD_Memory_Free(newsample); + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + + mBufferMemoryCurrent += dsbdesc.dwBufferBytes; + if (mBufferMemoryCurrent > mBufferMemoryMax) + { + mBufferMemoryMax = mBufferMemoryCurrent; + } + + /* + GET 3D INTERFACE IF IT IS UNIQUE + */ + if (mode & FMOD_3D && mode & FMOD_UNIQUE) + { + GUID guid; + + FMOD_memcpy(&guid, &IID_IDirectSound3DBuffer, sizeof(GUID)); + + if (newsample->mBuffer->QueryInterface(guid, (LPVOID *)&newsample->mBuffer3D) != DS_OK) + { + FMOD_Memory_Free(newsample); + + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + } + + /* + CLEAR OUT BUFFER + */ + { + void *ptr1, *ptr2; + unsigned int len1, len2; + + hr = newsample->lock(0, waveformat->lengthpcm * bits / 8 * waveformat->channels, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + + if (ptr1) + { + FMOD_memset(ptr1, 0, len1); + } + if (ptr2) + { + FMOD_memset(ptr2, 0, len2); + } + + hr = newsample->unlock(ptr1, ptr2, len1, len2); + if (hr != FMOD_OK) + { + return result; + } + } + + /* + Make DirectSound do pre-processing on sound effects. + */ + newsample->mBuffer->SetCurrentPosition(0); + newsample->mLength = waveformat->lengthpcm; + newsample->mOutput = this; + + *sample = newsample; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::createSample", "done\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getSoundRAM(int *currentalloced, int *maxalloced, int *total) +{ + if (currentalloced) + { + *currentalloced = mBufferMemoryCurrent; + } + if (maxalloced) + { + *maxalloced = mBufferMemoryMax; + } + if (total) + { + *total = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::setReverbProperties(const FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_OK; + + switch (mReverbVersion) + { +#if defined(FMOD_SUPPORT_EAX) + case REVERB_VERSION_EAX4: + { + result = setPropertiesEAX4(prop); + break; + } + case REVERB_VERSION_EAX3: + { + result = setPropertiesEAX3(prop); + break; + } + case REVERB_VERSION_EAX2: + { + result = setPropertiesEAX2(prop); + break; + } +#endif +#if defined (FMOD_SUPPORT_I3DL2) + case REVERB_VERSION_I3DL2: + { + result = setPropertiesI3DL2(prop); + break; + } +#endif + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getReverbProperties(FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_ERR_UNSUPPORTED; + + switch (mReverbVersion) + { +#if defined(FMOD_SUPPORT_EAX) + case REVERB_VERSION_EAX4: + { + result = getPropertiesEAX4(prop); + break; + } + case REVERB_VERSION_EAX3: + { + result = getPropertiesEAX3(prop); + break; + } + case REVERB_VERSION_EAX2: + { + result = getPropertiesEAX2(prop); + break; + } +#endif +#if defined (FMOD_SUPPORT_I3DL2) + case REVERB_VERSION_I3DL2: + { + result = getPropertiesI3DL2(prop); + break; + } +#endif + } + + return result; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::start() +{ + FMOD_RESULT result; + SampleDSound *sample; + HRESULT hr; + FMOD_CODEC_WAVEFORMAT waveformat; + bool oldusesoftware; + + FMOD_memset(&waveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + + result = mSystem->getSoftwareFormat(&waveformat.frequency, &waveformat.format, &waveformat.channels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + waveformat.lengthpcm = mBufferLength * mNumBuffers; + + oldusesoftware = mUseSoftware2DBuffers; + + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&waveformat.channels); + } + + #if 0 + mUseSoftware2DBuffers = true; /* Force it to use software buffers? On creative cards this screws 5.1/7.1 mixing - it only works if you use hardware buffers. */ + #endif + + result = createSample(FMOD_2D | FMOD_HARDWARE | FMOD_LOOP_NORMAL | FMOD_UNIQUE, &waveformat, &mSample); + if (result != FMOD_OK) + { + mUseSoftware2DBuffers = oldusesoftware; + return result; + } + + mUseSoftware2DBuffers = oldusesoftware; + + sample = SAFE_CAST(SampleDSound, mSample); + + sample->mDefaultFrequency = (float)waveformat.frequency; + sample->mFormat = waveformat.format; + sample->mChannels = waveformat.channels; + sample->mSystem = mSystem; + +#ifdef FMOD_OUTPUT_DSOUND_USETIMER + result = OutputTimer::start(); +#else + result = OutputPolled::start(); +#endif + if (result != FMOD_OK) + { + return result; + } + + hr = sample->mBuffer->SetVolume(0); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::start", "IDirectSoundBuffer::SetVolume(0) returned errcode %08X\n", hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + hr = sample->mBuffer->SetFrequency(waveformat.frequency); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::start", "IDirectSoundBuffer::SetFrequency(%d) returned errcode %08X\n", waveformat.frequency, hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + hr = sample->mBuffer->SetPan(DSBPAN_CENTER); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::start", "IDirectSoundBuffer::SetPan(DSBPAN_CENTER) returned errcode %08X\n", hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + hr = sample->mBuffer->Play(0,0,DSBPLAY_LOOPING); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::start", "IDirectSoundBuffer::Play returned errcode %08X\n", hr)); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::stop() +{ + FMOD_RESULT result; + SampleDSound *sample; + + sample = SAFE_CAST(SampleDSound, mSample); + if (!sample) + { + return FMOD_ERR_UNINITIALIZED; + } + + sample->mBuffer->Stop(); /* Dont return an error, the sound device may have died, just free stuff as normal. */ + +#ifdef FMOD_OUTPUT_DSOUND_USETIMER + result = OutputTimer::stop(); +#else + result = OutputPolled::stop(); +#endif + if (result != FMOD_OK) + { + return result; + } + + result = mSample->release(); + if (result != FMOD_OK) + { + return result; + } + + mSample = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getPosition(unsigned int *pcm) +{ + SampleDSound *sample; + HRESULT hr; + DWORD dwPlay, dwWrite; + +#if 0 + unsigned int curr = timeGetTime(); + static unsigned int last = 0; + char s[256]; + sprintf(s, "delta = %d ms\n", curr - last); + OutputDebugString(s); + last = curr; +#endif + + if (!mSample) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!pcm) + { + return FMOD_ERR_INVALID_PARAM; + } + + sample = SAFE_CAST(SampleDSound, mSample); + hr = sample->mBuffer->GetCurrentPosition(&dwPlay, &dwWrite); + if (hr != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputDSound::start", "IDirectSoundBuffer::Play returned errcode %08X\n", hr)); + + sample->mBuffer->Restore(); + sample->mBuffer->Play(0, 0, DSBPLAY_LOOPING); + + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (mMixAheadBlocks) + { + return SoundI::getSamplesFromBytes(dwWrite, pcm, sample->mChannels, sample->mFormat); + } + else + { + return SoundI::getSamplesFromBytes(dwPlay, pcm, sample->mChannels, sample->mFormat); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + if (!mSample) + { + return FMOD_ERR_INVALID_PARAM; + } + + return mSample->lock(offset, length, ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + if (!mSample) + { + return FMOD_ERR_INVALID_PARAM; + } + + return mSample->unlock(ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +int OutputDSound::getSampleMaxChannels(FMOD_MODE mode, FMOD_SOUND_FORMAT format) +{ + if (mode & FMOD_3D) + { + return 1; + } + else + { + return 8; /* 2D voices can go up to 8 channels wide. */ + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT OutputDSound::getMemoryUsedImpl(MemoryTracker *tracker) +{ + int i; + + tracker->add(false, FMOD_MEMBITS_OUTPUT, sizeof(*this)); + + if (mSampleReverb) + { + CHECK_RESULT(mSampleReverb->getMemoryUsed(tracker)); + } + + if (mChannel2D) + { + int num2dchannels = 0; + + if (mChannelPool) + { + CHECK_RESULT(mChannelPool->getNumChannels(&num2dchannels)); + } + + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelDSound) * num2dchannels); + } + + if (mChannel3D) + { + int num3dchannels = 0; + + if (mChannelPool3D) + { + CHECK_RESULT(mChannelPool3D->getNumChannels(&num3dchannels)); + } + + switch(mReverbVersion) + { +#if defined(FMOD_SUPPORT_EAX) + case REVERB_VERSION_EAX4: + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelDSoundEAX4) * num3dchannels); + break; + } + case REVERB_VERSION_EAX3: + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelDSoundEAX3) * num3dchannels); + break; + } + case REVERB_VERSION_EAX2: + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelDSoundEAX2) * num3dchannels); + break; + } +#endif +#if defined(FMOD_SUPPORT_I3DL2) + case REVERB_VERSION_I3DL2: + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelDSoundI3DL2) * num3dchannels); + break; + } +#endif + default: + { + tracker->add(false, FMOD_MEMBITS_CHANNEL, sizeof(ChannelDSound) * num3dchannels); + break; + } + } + } + + if (mSample) + { + CHECK_RESULT(mSample->getMemoryUsed(tracker)); + } + + for (i=0; i < mNumDrivers; i++) + { + if (mDriverName[i]) + { + tracker->add(false, FMOD_MEMBITS_STRING, (FMOD_strlenW(mDriverName[i]) + 1) * sizeof(short)); + } + } + +#ifdef FMOD_SUPPORT_RECORDING + for (i=0; i < mRecordNumDrivers; i++) + { + if (mRecordDriverName[i]) + { + tracker->add(false, FMOD_MEMBITS_STRING, (FMOD_strlenW(mRecordDriverName[i]) + 1) * sizeof(short)); + } + } +#endif + + CHECK_RESULT(OutputPolled::getMemoryUsedImpl(tracker)); + + return FMOD_OK; +} + +#endif + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::getDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getDriverInfo(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::getDriverInfoWCallback(FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getDriverInfoW(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getDriverCapsExCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getDriverCapsEx(id, caps, minfrequency, maxfrequency, controlpanelspeakermode, num2dchannels, num3dchannels, totalchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::initExCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->init(selecteddriver, flags, outputrate, outputchannels, outputformat, speakermode, dspbufferlength, dspnumbuffers, max2dchannels, max3dchannels, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::startCallback(FMOD_OUTPUT_STATE *output) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->start(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::stopCallback(FMOD_OUTPUT_STATE *output) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::updateCallback(FMOD_OUTPUT_STATE *output) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->update(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getHandle(handle); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::getPositionCallback(FMOD_OUTPUT_STATE *output, unsigned int *pcm) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getPosition(pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::lockCallback(FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->lock(offset, length, ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::unlockCallback(FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->unlock(ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::createSampleCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->createSample(mode, waveformat, sample); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::getSoundRAMCallback(FMOD_OUTPUT_STATE *output, int *currentalloced, int *maxalloced, int *total) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getSoundRAM(currentalloced, maxalloced, total); +} + + +#ifdef FMOD_SUPPORT_RECORDING +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordGetNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordGetNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordGetDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordGetDriverInfo(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordGetDriverInfoWCallback(FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordGetDriverInfoW(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordStartCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordStart(recordinfo, (Sound *)sound, loop ? true : false); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordStopCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordStop(recordinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordGetPositionCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordGetPosition(recordinfo, pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordLockCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordLock(recordinfo, offset, length, ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::recordUnlockCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->recordUnlock(recordinfo, ptr1, ptr2, len1, len2); +} +#endif + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputDSound::setReverbPropertiesCallback(FMOD_OUTPUT_STATE *output, const FMOD_REVERB_PROPERTIES *prop) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->setReverbProperties(prop); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +int F_CALLBACK OutputDSound::getSampleMaxChannelsCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getSampleMaxChannels(mode, format); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT F_CALLBACK OutputDSound::getMemoryUsedCallback(FMOD_OUTPUT_STATE *output, MemoryTracker *tracker) +{ + OutputDSound *dsound = (OutputDSound *)output; + + return dsound->getMemoryUsed(tracker); +} + +#endif + +} + +#endif /* FMOD_SUPPORT_DSOUND */ diff --git a/win32/src/fmod_output_dsound.h b/win32/src/fmod_output_dsound.h new file mode 100755 index 0000000..b2f3514 --- /dev/null +++ b/win32/src/fmod_output_dsound.h @@ -0,0 +1,252 @@ +#ifndef _FMOD_OUTPUT_DSOUND_H +#define _FMOD_OUTPUT_DSOUND_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSOUND + +//#define FMOD_OUTPUT_DSOUND_USETIMER + +#ifdef FMOD_OUTPUT_DSOUND_USETIMER + #include "fmod_output_timer.h" +#else + #include "fmod_output_polled.h" +#endif +#include "fmod_os_misc.h" + +#ifdef PLATFORM_32BIT + typedef unsigned long DWORD; + typedef DWORD DWORD_PTR; +#endif + +#include <dsound.h> + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +/* + Directsound function pointers definitions. +*/ +typedef HRESULT (WINAPI * PFN_DSCREATE) (LPGUID lpguid, LPDIRECTSOUND * ppDS, IUnknown FAR * pUnkOuter); +typedef BOOL (WINAPI * PFN_DSENUMERATE) (LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext); +typedef HRESULT (WINAPI * PFN_DSCREATE8) (LPCGUID lpcGuidDevice, LPDIRECTSOUND8 * ppDS8, LPUNKNOWN pUnkOuter); +typedef HRESULT (WINAPI * PFN_DSCCREATE) (LPGUID lpGUID, LPDIRECTSOUNDCAPTURE *lplpDSC,LPUNKNOWN pUnkOuter); +typedef BOOL (WINAPI * PFN_DSCENUMERATE) (LPDSENUMCALLBACKW lpDSEnumCallback,LPVOID lpContext); +typedef HRESULT (WINAPI * PFN_DSCCREATE8) (LPCGUID lpcGUID, LPDIRECTSOUNDCAPTURE8 *lplpDSC, LPUNKNOWN pUnkOuter); + +static const FMOD_GUID FMOD_DSPROPSETID_I3DL2_ListenerProperties = { 0xDA0F0520, 0x300A, 0x11D3, { 0x8A, 0x2B, 0x00, 0x60, 0x97, 0x0D, 0xB0, 0x11 } }; +static const FMOD_GUID FMOD_DSPROPSETID_I3DL2_BufferProperties = { 0xDA0F0521, 0x300A, 0x11D3, { 0x8A, 0x2B, 0x00, 0x60, 0x97, 0x0D, 0xB0, 0x11 } }; +static const FMOD_GUID FMOD_DSPROPSETID_EAX20_BufferProperties = { 0x306a6a7, 0xb224, 0x11d2, { 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22 } }; +static const FMOD_GUID FMOD_DSPROPSETID_EAX20_ListenerProperties = { 0x306a6a8, 0xb224, 0x11d2, { 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22 } }; +static const FMOD_GUID FMOD_DSPROPSETID_EAX30_ListenerProperties = { 0xa8fa6882, 0xb476, 0x11d3, { 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87 } }; +static const FMOD_GUID FMOD_DSPROPSETID_EAX30_BufferProperties = { 0xa8fa6881, 0xb476, 0x11d3, { 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX40_Context = { 0x1d4870ad, 0xdef, 0x43c0, { 0xa4, 0xc, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX40_Source = { 0x1b86b823, 0x22df, 0x4eae, { 0x8b, 0x3c, 0x12, 0x78, 0xce, 0x54, 0x42, 0x27 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX40_FXSlot0 = { 0xc4d79f1e, 0xf1ac, 0x436b, { 0xa8, 0x1d, 0xa7, 0x38, 0xe7, 0x4, 0x54, 0x69 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX40_FXSlot1 = { 0x8c00e96, 0x74be, 0x4491, { 0x93, 0xaa, 0xe8, 0xad, 0x35, 0xa4, 0x91, 0x17 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX40_FXSlot2 = { 0x1d433b88, 0xf0f6, 0x4637, { 0x91, 0x9f, 0x60, 0xe7, 0xe0, 0x6b, 0x5e, 0xdd } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX40_FXSlot3 = { 0xefff08ea, 0xc7d8, 0x44ab, { 0x93, 0xad, 0x6d, 0xbd, 0x5f, 0x91, 0x0, 0x64 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX50_Context = { 0x57e13437, 0xb932, 0x4ab2, { 0xb8, 0xbd, 0x52, 0x66, 0xc1, 0xa8, 0x87, 0xee } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX50_Source = { 0x5edf82f0, 0x24a7, 0x4f38, { 0x8e, 0x64, 0x2f, 0x09, 0xca, 0x05, 0xde, 0xe1 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX50_FXSlot0 = { 0x91f9590f, 0xc388, 0x407a, { 0x84, 0xb0, 0x1b, 0xae, 0x0e, 0xf7, 0x1a, 0xbc } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX50_FXSlot1 = { 0x8f5f7aca, 0x9608, 0x4965, { 0x81, 0x37, 0x82, 0x13, 0xc7, 0xb9, 0xd9, 0xde } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX50_FXSlot2 = { 0x3c0f5252, 0x9834, 0x46f0, { 0xa1, 0xd8, 0x5b, 0x95, 0xc4, 0xa0, 0x0a, 0x30 } }; +static const FMOD_GUID FMOD_EAXPROPERTYID_EAX50_FXSlot3 = { 0xe2eb0eaa, 0xe806, 0x45e7, { 0x9f, 0x86, 0x06, 0xc1, 0x57, 0x1a, 0x6f, 0xa3 } }; +static const FMOD_GUID FMOD_EAX_NULL_GUID = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static const FMOD_GUID FMOD_EAX_REVERB_EFFECT = { 0xcf95c8f, 0xa3cc, 0x4849, { 0xb0, 0xb6, 0x83, 0x2e, 0xcc, 0x18, 0x22, 0xdf } }; +static const FMOD_GUID FMOD_IID_IKsPropertySet = { 0x31efac30, 0x515c, 0x11d0, { 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 } }; + + +namespace FMOD +{ + class ChannelDSound; + class SampleDSound; + class Sound; + + typedef enum + { + REVERB_VERSION_NONE, + REVERB_VERSION_EAX5, + REVERB_VERSION_EAX4, + REVERB_VERSION_EAX3, + REVERB_VERSION_EAX2, + REVERB_VERSION_I3DL2 + } REVERB_VERSION; + + typedef struct FMOD_DSoundRecordMembers + { + IDirectSoundCapture8 *mDirectSoundCapture; + IDirectSoundCaptureBuffer *mDirectSoundCaptureBuffer; + } FMOD_DSoundRecordMembers; + +#ifdef FMOD_OUTPUT_DSOUND_USETIMER + class OutputDSound : public OutputTimer +#else + class OutputDSound : public OutputPolled +#endif + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + friend class SampleDSound; + friend class ChannelDSound; + friend class ChannelDSoundEAX2; + friend class ChannelDSoundEAX3; + friend class ChannelDSoundEAX4; + friend class ChannelDSoundI3DL2; + + private: + + bool mDLLInitialized; + bool mCoInitialized; + bool mGlobalFocus; + bool mNeedToCommit; + FMOD_INITFLAGS mInitFlags; + + int mDirectXVersion; + HMODULE mDSoundModule; + HMODULE mDSound3DModule; + + PFN_DSCREATE8 mDirectSoundCreate; + PFN_DSENUMERATE mDirectSoundEnumerate; + IDirectSound8 *mDirectSound; + IDirectSound3DListener *mDirectSoundListener; + + PFN_DSCENUMERATE mDirectSoundCaptureEnumerate; + PFN_DSCCREATE8 mDirectSoundCaptureCreate; + + REVERB_VERSION mReverbVersion; + unsigned int mFeaturesReverb; + IKsPropertySet *mBufferReverb; + SampleDSound *mSampleReverb; + + bool mUseSoftware2DBuffers; + bool mUseSoftware3DBuffers; + float mMinFrequency; + float mMaxFrequency; + unsigned int mBufferLength; + unsigned int mNumBuffers; + + float mLastDopplerScale; + float mLastDistancerScale; + float mLastRolloffrScale; + + FMOD_SPEAKERMODE mSpeakerMode; + + ChannelDSound *mChannel2D; + ChannelDSound *mChannel3D; + Sample *mSample; + + unsigned int mBufferMemoryCurrent; + unsigned int mBufferMemoryMax; + + FMOD_RESULT registerDLL(); + + bool initI3DL2(); + FMOD_RESULT closeI3DL2(); + FMOD_RESULT setPropertiesI3DL2(const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT getPropertiesI3DL2(FMOD_REVERB_PROPERTIES *prop); + bool querySupportI3DL2(unsigned int ulQuery); + + bool initEAX2(); + FMOD_RESULT closeEAX2(); + FMOD_RESULT setPropertiesEAX2(const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT getPropertiesEAX2(FMOD_REVERB_PROPERTIES *prop); + bool querySupportEAX2(unsigned int ulQuery); + + bool initEAX3(); + FMOD_RESULT closeEAX3(); + FMOD_RESULT setPropertiesEAX3(const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT getPropertiesEAX3(FMOD_REVERB_PROPERTIES *prop); + bool querySupportEAX3(unsigned int ulQuery); + + bool initEAX4(); + FMOD_RESULT closeEAX4(); + FMOD_RESULT setPropertiesEAX4(const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT getPropertiesEAX4(FMOD_REVERB_PROPERTIES *prop); + bool querySupportEAX4(FMOD_GUID guid, unsigned long ulProperty); + + + public: + + int mNumDrivers; /* Public for C callback access */ + short *mDriverName[FMOD_OUTPUT_MAXDRIVERS]; /* Public for C callback access */ + GUID mGUID[FMOD_OUTPUT_MAXDRIVERS]; + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT enumerate(); + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverCapsEx(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT getHandle(void **handle); + FMOD_RESULT update(); + FMOD_RESULT createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + FMOD_RESULT getSoundRAM(int *currentalloced, int *maxalloced, int *total); + FMOD_RESULT setReverbProperties(const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT getReverbProperties(FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT start(); + FMOD_RESULT stop(); + FMOD_RESULT getPosition(unsigned int *pcm); + FMOD_RESULT lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + int getSampleMaxChannels(FMOD_MODE mode, FMOD_SOUND_FORMAT format); + +#ifdef FMOD_SUPPORT_RECORDING + int mRecordNumDrivers; /* Public for C callback access */ + short *mRecordDriverName[FMOD_OUTPUT_MAXDRIVERS]; /* Public for C callback access */ + GUID mRecordGUID[FMOD_OUTPUT_MAXDRIVERS]; + + FMOD_RESULT recordEnumerate(); + FMOD_RESULT recordGetNumDrivers(int *numdrivers); + FMOD_RESULT recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordGetDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordStart (FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop); + FMOD_RESULT recordStop (FMOD_RECORDING_INFO *recordinfo); + FMOD_RESULT recordGetPosition (FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + FMOD_RESULT recordLock (FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT recordUnlock (FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); +#endif + + static FMOD_RESULT F_CALLBACK getNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverInfoCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK getDriverInfoWCallback (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK getDriverCapsExCallback (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels); + static FMOD_RESULT F_CALLBACK initExCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK startCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK stopCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK updateCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + static FMOD_RESULT F_CALLBACK getPositionCallback (FMOD_OUTPUT_STATE *output, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK lockCallback (FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + static FMOD_RESULT F_CALLBACK unlockCallback (FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + static FMOD_RESULT F_CALLBACK createSampleCallback (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + static FMOD_RESULT F_CALLBACK getSoundRAMCallback (FMOD_OUTPUT_STATE *output, int *currentalloced, int *maxalloced, int *total); + static FMOD_RESULT F_CALLBACK setReverbPropertiesCallback (FMOD_OUTPUT_STATE *output, const FMOD_REVERB_PROPERTIES *prop); + static int F_CALLBACK getSampleMaxChannelsCallback (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format); + +#ifdef FMOD_SUPPORT_RECORDING + static FMOD_RESULT F_CALLBACK recordGetNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoWCallback (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordStartCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop); + static FMOD_RESULT F_CALLBACK recordStopCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo); + static FMOD_RESULT F_CALLBACK recordGetPositionCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK recordLockCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + static FMOD_RESULT F_CALLBACK recordUnlockCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); +#endif + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback (FMOD_OUTPUT_STATE *output, MemoryTracker *tracker); +#endif + + }; +} + +#endif /* FMOD_SUPPORT_DSOUND */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_output_dsound_eax2.cpp b/win32/src/fmod_output_dsound_eax2.cpp new file mode 100755 index 0000000..25c3a7f --- /dev/null +++ b/win32/src/fmod_output_dsound_eax2.cpp @@ -0,0 +1,353 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_dsound.h" +#include "fmod_eax2.h" +#include "fmod_localcriticalsection.h" +#include "fmod_sample_dsound.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::querySupportEAX2(unsigned int ulQuery) +{ + unsigned long ulSupport = 0; + HRESULT hr; + GUID guid; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX2", "check properties %08X\n", ulQuery)); + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->QuerySupport(guid, ulQuery, &ulSupport); + if ( FAILED(hr) ) + { + return false; + } + + if ( (ulSupport&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) == + (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET) ) + { + mFeaturesReverb |= (DWORD)(1 << ulQuery); + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX2", "success.\n")); + + return true; + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX2", "failed.\n")); + + return false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::initEAX2() +{ + FMOD_RESULT result; + ULONG support = 0; + HRESULT hr = DS_OK; + GUID guid; + FMOD_CODEC_WAVEFORMAT waveformat; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX2", "Querying EAX2\n")); + + if (mBufferReverb) + { + mBufferReverb->Release(); + mBufferReverb = 0; + } + + /* + Create EAX sample + */ + + FMOD_memset(&waveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + waveformat.frequency = 44100; + waveformat.lengthpcm = 1024; + waveformat.channels = 1; + waveformat.format = FMOD_SOUND_FORMAT_PCM16; + + result = createSample(FMOD_3D | FMOD_UNIQUE, &waveformat, (Sample **)&mSampleReverb); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX2", "could not create hardware sample.\n")); + return false; + } + + mSampleReverb->mSystem = mSystem; + + /* + OBTAIN EAX INTERFACE TO BUFFER + */ + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + if(mSampleReverb->mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb) != DS_OK) + { + mBufferReverb = 0; + return false; + } + + /* + QUERY FOR EAX support + */ + support = querySupportEAX2(DSPROPERTY_EAXLISTENER_NONE); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ALLPARAMETERS); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ENVIRONMENT); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ROOM); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ROOMHF); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_DECAYTIME); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_DECAYHFRATIO); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_REFLECTIONS); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_REVERB); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_REVERBDELAY); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR); + if (support) support = querySupportEAX2(DSPROPERTY_EAXLISTENER_FLAGS); + + if (!mFeaturesReverb) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX2", "failed EAX check, releasing reverb interface\n")); + return false; + } + + /* + Turn down reverb to start with. + */ + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ROOM)) + { + DWORD Room = EAXLISTENER_MINROOM; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX2", "setting reverb environment to OFF\n")); + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_ListenerProperties, sizeof(GUID)); + + mBufferReverb->Set(guid, DSPROPERTY_EAXLISTENER_ROOM, 0, 0, &Room, sizeof(DWORD)); + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX2", "found!\n")); + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::closeEAX2() +{ + if (mBufferReverb) + { + /* + Turn down reverb again + */ + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ROOM)) + { + GUID guid; + DWORD Room = EAXLISTENER_MINROOM; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_ListenerProperties, sizeof(GUID)); + + mBufferReverb->Set(guid, DSPROPERTY_EAXLISTENER_ROOM, 0, 0, &Room, sizeof(DWORD)); + } + + mBufferReverb->Release(); + mBufferReverb = NULL; + } + + if (mSampleReverb) + { + mSampleReverb->release(); + mSampleReverb = NULL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::setPropertiesEAX2(const FMOD_REVERB_PROPERTIES *prop) +{ + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX2", "Entering \n")); + + if ( !(mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS) ) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX2", "failed - apparently DSPROPERTY_EAXLISTENER_ALLPARAMETERS isnt supported\n")); + + return FMOD_ERR_UNSUPPORTED; + } + + if (mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS) + { + HRESULT hr; + EAXLISTENERPROPERTIES dsprop = {0}; + GUID guid; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX2", "setting listener properties\n")); + + dsprop.flEnvironmentSize = prop->EnvSize; + dsprop.dwEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + dsprop.flEnvironmentDiffusion = prop->EnvDiffusion; + dsprop.lRoom = prop->Room; + dsprop.lRoomHF = prop->RoomHF; + dsprop.flDecayTime = prop->DecayTime; + dsprop.flDecayHFRatio = prop->DecayHFRatio; + dsprop.lReflections = prop->Reflections; + dsprop.flReflectionsDelay = prop->ReflectionsDelay; + dsprop.lReverb = prop->Reverb; + dsprop.flReverbDelay = prop->ReverbDelay; + dsprop.flAirAbsorptionHF = prop->AirAbsorptionHF; + dsprop.flRoomRolloffFactor = prop->RoomRolloffFactor; + dsprop.dwFlags = prop->Flags & 0xFF; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &dsprop, sizeof(EAXLISTENERPROPERTIES)); + + if (FAILED(hr)) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX2", "setting listener properties FAILED\n")); + + return FMOD_ERR_INVALID_PARAM; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getPropertiesEAX2(FMOD_REVERB_PROPERTIES *prop) +{ + ULONG ulReceived; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if ( !(mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS) ) + { + return FMOD_ERR_UNSUPPORTED; + } + + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS)) + { + GUID guid; + HRESULT hr; + EAXLISTENERPROPERTIES dsprop = {0}; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX20_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &dsprop, sizeof(EAXLISTENERPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Environment = dsprop.dwEnvironment; + prop->EnvSize = dsprop.flEnvironmentSize ; + prop->EnvDiffusion = dsprop.flEnvironmentDiffusion ; + prop->Room = dsprop.lRoom ; + prop->RoomHF = dsprop.lRoomHF ; + prop->RoomLF = 0; + prop->DecayTime = dsprop.flDecayTime ; + prop->DecayHFRatio = dsprop.flDecayHFRatio ; + prop->DecayLFRatio = 0; + prop->Reflections = dsprop.lReflections ; + prop->ReflectionsDelay = dsprop.flReflectionsDelay ; + prop->ReflectionsPan[0] = 0; + prop->ReflectionsPan[1] = 0; + prop->ReflectionsPan[2] = 0; + prop->Reverb = dsprop.lReverb ; + prop->ReverbDelay = dsprop.flReverbDelay ; + prop->ReverbPan[0] = 0; + prop->ReverbPan[1] = 0; + prop->ReverbPan[2] = 0; + prop->EchoTime = 0; + prop->EchoDepth = 0; + prop->ModulationTime = 0; + prop->ModulationDepth = 0; + prop->AirAbsorptionHF = dsprop.flAirAbsorptionHF ; + prop->HFReference = 0; + prop->LFReference = 0; + prop->RoomRolloffFactor = dsprop.flRoomRolloffFactor ; + prop->Flags = dsprop.dwFlags ; + } + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ diff --git a/win32/src/fmod_output_dsound_eax3.cpp b/win32/src/fmod_output_dsound_eax3.cpp new file mode 100755 index 0000000..f0cc05c --- /dev/null +++ b/win32/src/fmod_output_dsound_eax3.cpp @@ -0,0 +1,381 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_dsound.h" +#include "fmod_eax3.h" +#include "fmod_localcriticalsection.h" +#include "fmod_sample_dsound.h" +#include "fmod_systemi.h" + + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::querySupportEAX3(unsigned int ulQuery) +{ + unsigned long ulSupport = 0; + HRESULT hr; + GUID guid; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX3", "check properties %08X\n", ulQuery)); + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->QuerySupport(guid, ulQuery, &ulSupport); + if ( FAILED(hr) ) + { + return false; + } + + if ( (ulSupport&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) == + (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET) ) + { + mFeaturesReverb |= (DWORD)(1 << ulQuery); + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX3", "success\n")); + + return true; + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX3", "failed.\n")); + + return false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::initEAX3() +{ + FMOD_RESULT result; + ULONG support = 0; + HRESULT hr = DS_OK; + GUID guid; + FMOD_CODEC_WAVEFORMAT waveformat; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "Querying EAX3\n")); + + if (mBufferReverb) + { + mBufferReverb->Release(); + mBufferReverb = NULL; + } + + /* + Create EAX sample + */ + + FMOD_memset(&waveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + waveformat.frequency = 44100; + waveformat.lengthpcm = 1024; + waveformat.channels = 1; + waveformat.format = FMOD_SOUND_FORMAT_PCM16; + + result = createSample(FMOD_3D | FMOD_UNIQUE, &waveformat, (Sample **)&mSampleReverb); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX3", "could not create hardware sample.\n")); + return false; + } + + mSampleReverb->mSystem = mSystem; + + /* + OBTAIN EAX INTERFACE TO BUFFER + */ + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + if(mSampleReverb->mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb) != DS_OK) + { + mBufferReverb = NULL; + return false; + } + + /* + QUERY FOR EAX support + */ + support = querySupportEAX3(DSPROPERTY_EAXLISTENER_NONE); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ALLPARAMETERS); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ENVIRONMENT); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ROOM); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ROOMHF); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ROOMLF); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_DECAYTIME); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_DECAYHFRATIO); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_DECAYLFRATIO); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_REFLECTIONS); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_REFLECTIONSPAN); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_REVERB); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_REVERBDELAY); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_REVERBPAN); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ECHOTIME); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ECHODEPTH); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_MODULATIONTIME); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_MODULATIONDEPTH); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_HFREFERENCE); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_LFREFERENCE); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR); + if (support) support = querySupportEAX3(DSPROPERTY_EAXLISTENER_FLAGS); + + if (!mFeaturesReverb) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX3", "failed EAX check, releasing reverb interface\n")); + return false; + } + + /* + Turn down reverb to start with. + */ + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ROOM)) + { + DWORD Room = EAXLISTENER_MINROOM; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX3", "setting reverb environment to OFF\n")); + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_ListenerProperties, sizeof(GUID)); + + mBufferReverb->Set(guid, DSPROPERTY_EAXLISTENER_ROOM, NULL, 0, &Room, sizeof(DWORD)); + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX3", "found!\n")); + + return true; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::closeEAX3() +{ + if (mBufferReverb) + { + /* + Turn down reverb again + */ + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ROOM)) + { + GUID guid; + DWORD Room = EAXLISTENER_MINROOM; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_ListenerProperties, sizeof(GUID)); + + mBufferReverb->Set(guid, DSPROPERTY_EAXLISTENER_ROOM, NULL, 0, &Room, sizeof(DWORD)); + } + + mBufferReverb->Release(); + mBufferReverb = NULL; + } + + if (mSampleReverb) + { + mSampleReverb->release(); + mSampleReverb = NULL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::setPropertiesEAX3(const FMOD_REVERB_PROPERTIES *prop) +{ + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + +// FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX3", "Entering \n")); + + if ( !(mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS) ) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX3", "failed - apparently DSPROPERTY_EAXLISTENER_ALLPARAMETERS isnt supported\n")); + + return FMOD_ERR_UNSUPPORTED; + } + + if (mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS) + { + HRESULT hr; + EAXLISTENERPROPERTIES dsprop = {0}; + GUID guid; + +// FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX3", "setting listener properties\n")); + + dsprop.ulEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + dsprop.flEnvironmentSize = prop->EnvSize; + dsprop.flEnvironmentDiffusion = prop->EnvDiffusion; + dsprop.lRoom = prop->Room; + dsprop.lRoomHF = prop->RoomHF; + dsprop.lRoomLF = prop->RoomLF; + dsprop.flDecayTime = prop->DecayTime; + dsprop.flDecayHFRatio = prop->DecayHFRatio; + dsprop.flDecayLFRatio = prop->DecayLFRatio; + dsprop.lReflections = prop->Reflections; + dsprop.flReflectionsDelay = prop->ReflectionsDelay; + dsprop.vReflectionsPan.x = prop->ReflectionsPan[0]; + dsprop.vReflectionsPan.y = prop->ReflectionsPan[1]; + dsprop.vReflectionsPan.z = prop->ReflectionsPan[2]; + dsprop.lReverb = prop->Reverb; + dsprop.flReverbDelay = prop->ReverbDelay; + dsprop.vReverbPan.x = prop->ReverbPan[0]; + dsprop.vReverbPan.y = prop->ReverbPan[1]; + dsprop.vReverbPan.z = prop->ReverbPan[2]; + dsprop.flEchoTime = prop->EchoTime; + dsprop.flEchoDepth = prop->EchoDepth; + dsprop.flModulationTime = prop->ModulationTime; + dsprop.flModulationDepth = prop->ModulationDepth; + dsprop.flAirAbsorptionHF = prop->AirAbsorptionHF; + dsprop.flHFReference = prop->HFReference; + dsprop.flLFReference = prop->LFReference; + dsprop.flRoomRolloffFactor = prop->RoomRolloffFactor; + dsprop.ulFlags = prop->Flags & 0xFF; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, NULL, 0, &dsprop, sizeof(EAXLISTENERPROPERTIES)); + + if (FAILED(hr)) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX3", "setting listener properties FAILED\n")); + + return FMOD_ERR_INVALID_PARAM; + } + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getPropertiesEAX3(FMOD_REVERB_PROPERTIES *prop) +{ + ULONG ulReceived; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if ( !(mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS) ) + { + return FMOD_ERR_UNSUPPORTED; + } + + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_EAXLISTENER_ALLPARAMETERS)) + { + GUID guid; + HRESULT hr; + EAXLISTENERPROPERTIES dsprop = {0}; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_EAX30_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, NULL, 0, &dsprop, sizeof(EAXLISTENERPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Environment = dsprop.ulEnvironment ; + prop->EnvSize = dsprop.flEnvironmentSize ; + prop->EnvDiffusion = dsprop.flEnvironmentDiffusion ; + prop->Room = dsprop.lRoom ; + prop->RoomHF = dsprop.lRoomHF ; + prop->RoomLF = dsprop.lRoomLF ; + prop->DecayTime = dsprop.flDecayTime ; + prop->DecayHFRatio = dsprop.flDecayHFRatio ; + prop->DecayLFRatio = dsprop.flDecayLFRatio ; + prop->Reflections = dsprop.lReflections ; + prop->ReflectionsDelay = dsprop.flReflectionsDelay ; + prop->ReflectionsPan[0] = dsprop.vReflectionsPan.x ; + prop->ReflectionsPan[1] = dsprop.vReflectionsPan.y ; + prop->ReflectionsPan[2] = dsprop.vReflectionsPan.z ; + prop->Reverb = dsprop.lReverb ; + prop->ReverbDelay = dsprop.flReverbDelay ; + prop->ReverbPan[0] = dsprop.vReverbPan.x ; + prop->ReverbPan[1] = dsprop.vReverbPan.y ; + prop->ReverbPan[2] = dsprop.vReverbPan.z ; + prop->EchoTime = dsprop.flEchoTime ; + prop->EchoDepth = dsprop.flEchoDepth ; + prop->ModulationTime = dsprop.flModulationTime ; + prop->ModulationDepth = dsprop.flModulationDepth ; + prop->AirAbsorptionHF = dsprop.flAirAbsorptionHF ; + prop->HFReference = dsprop.flHFReference ; + prop->LFReference = dsprop.flLFReference ; + prop->RoomRolloffFactor = dsprop.flRoomRolloffFactor ; + prop->Flags = dsprop.ulFlags ; + } + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ + diff --git a/win32/src/fmod_output_dsound_eax4.cpp b/win32/src/fmod_output_dsound_eax4.cpp new file mode 100755 index 0000000..bec8c3f --- /dev/null +++ b/win32/src/fmod_output_dsound_eax4.cpp @@ -0,0 +1,440 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_dsound.h" +#include "fmod_eax4.h" +#include "fmod_localcriticalsection.h" +#include "fmod_sample_dsound.h" +#include "fmod_systemi.h" + + +#include <stdio.h> + +namespace FMOD +{ + +bool gFXslot2set = false; +bool gFXslot3set = false; + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::querySupportEAX4(FMOD_GUID guid, unsigned long ulProperty) +{ + HRESULT hr; + unsigned long ulSupport; + GUID g; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX4", "check properties %08X\n", ulProperty)); + + FMOD_memcpy(&g, &guid, sizeof(GUID)); + + hr = mBufferReverb->QuerySupport(g, ulProperty, &ulSupport); + if ( FAILED(hr) ) + { + return false; + } + + if ( (ulSupport&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) == + (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET) ) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX4", "success.\n")); + + return true; + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportEAX4", "failed.\n")); + + return false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::initEAX4() +{ + FMOD_RESULT result; + ULONG support = 0; + HRESULT hr = DS_OK; + GUID guid; + FMOD_CODEC_WAVEFORMAT waveformat; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "Querying EAX4\n")); + + if (mBufferReverb) + { + mBufferReverb->Release(); + mBufferReverb = NULL; + } + + /* + Create EAX sample + */ + + FMOD_memset(&waveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + waveformat.frequency = 44100; + waveformat.lengthpcm = 1024; + waveformat.channels = 1; + waveformat.format = FMOD_SOUND_FORMAT_PCM16; + + result = createSample(FMOD_3D | FMOD_UNIQUE, &waveformat, (Sample **)&mSampleReverb); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "could not create hardware sample.\n")); + return false; + } + + mSampleReverb->mSystem = mSystem; + + + /* + OBTAIN EAX INTERFACE TO BUFFER + */ + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + if(mSampleReverb->mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb) != DS_OK) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "failed (1).\n")); + + mBufferReverb = NULL; + return false; + } + + /* + QUERY FOR EAX support + */ + support = querySupportEAX4(FMOD_EAXPROPERTYID_EAX40_Context, EAXCONTEXT_ALLPARAMETERS); + if (support) support = querySupportEAX4(FMOD_EAXPROPERTYID_EAX40_FXSlot0, EAXFXSLOT_ALLPARAMETERS); + if (support) support = querySupportEAX4(FMOD_EAXPROPERTYID_EAX40_FXSlot1, EAXFXSLOT_ALLPARAMETERS); + if (support) support = querySupportEAX4(FMOD_EAXPROPERTYID_EAX40_FXSlot2, EAXFXSLOT_ALLPARAMETERS); + if (support) support = querySupportEAX4(FMOD_EAXPROPERTYID_EAX40_FXSlot3, EAXFXSLOT_ALLPARAMETERS); + if (support) support = querySupportEAX4(FMOD_EAXPROPERTYID_EAX40_Source, EAXSOURCE_ALLPARAMETERS); + + if (support) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "EAX 4.0 - success\n")); + } + else + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "failed (2).\n")); + + return false; + } + + + /* + Turn down reverb to start with + */ + { + long room = EAXREVERB_MINROOM; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "setting reverb environment to OFF\n")); + + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + hr = mBufferReverb->Set(guid, EAXREVERB_ROOM, 0, 0, &room, sizeof(long)); + if (FAILED(hr)) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "failed (3).\n")); + return false; + } + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initEAX4", "found!\n")); + + return true; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::closeEAX4() +{ + GUID guid; + long lock = EAXFXSLOT_UNLOCKED; + + if (mBufferReverb) + { + /* + Unlock FX slots + */ + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + mBufferReverb->Set(guid, EAXFXSLOT_LOCKED, 0, 0, &lock, sizeof(long)); + + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + mBufferReverb->Set(guid, EAXFXSLOT_LOCKED, 0, 0, &lock, sizeof(long)); + + mBufferReverb->Release(); + mBufferReverb = NULL; + } + + if (mSampleReverb) + { + mSampleReverb->release(); + mSampleReverb = NULL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::setPropertiesEAX4(const FMOD_REVERB_PROPERTIES *prop) +{ + HRESULT hr; + EAXREVERBPROPERTIES eaxprop = {0}; + EAXFXSLOTPROPERTIES fxslot; + GUID guid; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + switch(prop->Instance) + { + case 1: + { + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + + if (!gFXslot2set) + { + FMOD_memcpy(&fxslot.guidLoadEffect, &FMOD_EAX_REVERB_EFFECT, sizeof(GUID)); + fxslot.lLock = EAXFXSLOT_LOCKED; + fxslot.lVolume = 0; + fxslot.ulFlags = EAXFXSLOTFLAGS_ENVIRONMENT; + + hr = mBufferReverb->Set(guid, EAXFXSLOT_ALLPARAMETERS, NULL, 0, &fxslot, sizeof(EAXFXSLOTPROPERTIES)); + if (FAILED(hr)) + { + ULONG ulReceived; + GUID effect; + + /* + Check if the slot contains a reverb. If so, we can still use that slot. + */ + hr = mBufferReverb->Get(guid, EAXFXSLOT_LOADEFFECT, NULL, 0, &effect, sizeof(GUID), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (memcmp(&effect, &fxslot.guidLoadEffect, sizeof(GUID))) + { + return FMOD_ERR_REVERB_INSTANCE; + } + } + + gFXslot2set = true; + } + + break; + } + case 2: + { + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + + if (!gFXslot3set) + { + FMOD_memcpy(&fxslot.guidLoadEffect, &FMOD_EAX_REVERB_EFFECT, sizeof(GUID)); + fxslot.lLock = EAXFXSLOT_LOCKED; + fxslot.lVolume = 0; + fxslot.ulFlags = EAXFXSLOTFLAGS_ENVIRONMENT; + + hr = mBufferReverb->Set(guid, EAXFXSLOT_ALLPARAMETERS, NULL, 0, &fxslot, sizeof(EAXFXSLOTPROPERTIES)); + if (FAILED(hr)) + { + ULONG ulReceived; + GUID effect; + + /* + Check if the slot contains a reverb. If so, we can still use that slot. + */ + hr = mBufferReverb->Get(guid, EAXFXSLOT_LOADEFFECT, NULL, 0, &effect, sizeof(GUID), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (memcmp(&effect, &fxslot.guidLoadEffect, sizeof(GUID))) + { + return FMOD_ERR_REVERB_INSTANCE; + } + } + + gFXslot3set = true; + } + + break; + } + default: + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + break; + } + + eaxprop.ulEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + eaxprop.flEnvironmentSize = prop->EnvSize; + eaxprop.flEnvironmentDiffusion = prop->EnvDiffusion; + eaxprop.lRoom = prop->Room; + eaxprop.lRoomHF = prop->RoomHF; + eaxprop.lRoomLF = prop->RoomLF; + eaxprop.flDecayTime = prop->DecayTime; + eaxprop.flDecayHFRatio = prop->DecayHFRatio; + eaxprop.flDecayLFRatio = prop->DecayLFRatio; + eaxprop.lReflections = prop->Reflections; + eaxprop.flReflectionsDelay = prop->ReflectionsDelay; + eaxprop.vReflectionsPan.x = prop->ReflectionsPan[0]; + eaxprop.vReflectionsPan.y = prop->ReflectionsPan[1]; + eaxprop.vReflectionsPan.z = prop->ReflectionsPan[2]; + eaxprop.lReverb = prop->Reverb; + eaxprop.flReverbDelay = prop->ReverbDelay; + eaxprop.vReverbPan.x = prop->ReverbPan[0]; + eaxprop.vReverbPan.y = prop->ReverbPan[1]; + eaxprop.vReverbPan.z = prop->ReverbPan[2]; + eaxprop.flEchoTime = prop->EchoTime; + eaxprop.flEchoDepth = prop->EchoDepth; + eaxprop.flModulationTime = prop->ModulationTime; + eaxprop.flModulationDepth = prop->ModulationDepth; + eaxprop.flAirAbsorptionHF = prop->AirAbsorptionHF; + eaxprop.flHFReference = prop->HFReference; + eaxprop.flLFReference = prop->LFReference; + eaxprop.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxprop.ulFlags = prop->Flags & 0xFF; + + hr = mBufferReverb->Set(guid, EAXREVERB_ALLPARAMETERS, NULL, 0, &eaxprop, sizeof(EAXREVERBPROPERTIES)); + if (FAILED(hr)) + { + return FMOD_ERR_INVALID_PARAM; + } + + return FMOD_OK; +} + + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getPropertiesEAX4(FMOD_REVERB_PROPERTIES *prop) +{ + HRESULT hr; + EAXREVERBPROPERTIES eaxprop; + ULONG ulReceived; + GUID guid; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + switch(prop->Instance) + { + case 1: + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + break; + case 2: + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + break; + default: + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + break; + } + + hr = mBufferReverb->Get(guid, EAXREVERB_ALLPARAMETERS, NULL, 0, &eaxprop, sizeof(EAXREVERBPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Environment = eaxprop.ulEnvironment ; + prop->EnvSize = eaxprop.flEnvironmentSize ; + prop->EnvDiffusion = eaxprop.flEnvironmentDiffusion ; + prop->Room = eaxprop.lRoom ; + prop->RoomHF = eaxprop.lRoomHF ; + prop->RoomLF = eaxprop.lRoomLF ; + prop->DecayTime = eaxprop.flDecayTime ; + prop->DecayHFRatio = eaxprop.flDecayHFRatio ; + prop->DecayLFRatio = eaxprop.flDecayLFRatio ; + prop->Reflections = eaxprop.lReflections ; + prop->ReflectionsDelay = eaxprop.flReflectionsDelay ; + prop->ReflectionsPan[0] = eaxprop.vReflectionsPan.x ; + prop->ReflectionsPan[1] = eaxprop.vReflectionsPan.y ; + prop->ReflectionsPan[2] = eaxprop.vReflectionsPan.z ; + prop->Reverb = eaxprop.lReverb ; + prop->ReverbDelay = eaxprop.flReverbDelay ; + prop->ReverbPan[0] = eaxprop.vReverbPan.x ; + prop->ReverbPan[1] = eaxprop.vReverbPan.y ; + prop->ReverbPan[2] = eaxprop.vReverbPan.z ; + prop->EchoTime = eaxprop.flEchoTime ; + prop->EchoDepth = eaxprop.flEchoDepth ; + prop->ModulationTime = eaxprop.flModulationTime ; + prop->ModulationDepth = eaxprop.flModulationDepth ; + prop->AirAbsorptionHF = eaxprop.flAirAbsorptionHF ; + prop->HFReference = eaxprop.flHFReference ; + prop->LFReference = eaxprop.flLFReference ; + prop->RoomRolloffFactor = eaxprop.flRoomRolloffFactor ; + prop->Flags = eaxprop.ulFlags ; + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_EAX */ + diff --git a/win32/src/fmod_output_dsound_i3dl2.cpp b/win32/src/fmod_output_dsound_i3dl2.cpp new file mode 100755 index 0000000..d8702b3 --- /dev/null +++ b/win32/src/fmod_output_dsound_i3dl2.cpp @@ -0,0 +1,348 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_I3DL2 + +#include "fmod.h" +#include "fmod_3dl2.h" +#include "fmod_localcriticalsection.h" +#include "fmod_output_dsound.h" +#include "fmod_sample_dsound.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::querySupportI3DL2(unsigned int ulQuery) +{ + unsigned long ulSupport = 0; + HRESULT hr; + GUID guid; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportI3DL2", "check properties %08X\n", ulQuery)); + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->QuerySupport(guid, ulQuery, &ulSupport); + if ( FAILED(hr) ) + { + return false; + } + + if ( (ulSupport&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) == + (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET) ) + { + mFeaturesReverb |= (DWORD)(1 << ulQuery); + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::querySupportI3DL2", "success\n")); + + return true; + } + + return false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +bool OutputDSound::initI3DL2() +{ + FMOD_RESULT result; + ULONG support = 0; + HRESULT hr = DS_OK; + GUID guid; + FMOD_CODEC_WAVEFORMAT waveformat; + + if (mBufferReverb) + { + mBufferReverb->Release(); + mBufferReverb = 0; + } + + /* + Create I3DL2 sample + */ + + FMOD_memset(&waveformat, 0, sizeof(FMOD_CODEC_WAVEFORMAT)); + waveformat.frequency = 44100; + waveformat.lengthpcm = 1024; + waveformat.channels = 1; + waveformat.format = FMOD_SOUND_FORMAT_PCM16; + + result = createSample(FMOD_3D | FMOD_UNIQUE, &waveformat, (Sample **)&mSampleReverb); + if (result != FMOD_OK) + { + return false; + } + + /* + OBTAIN I3DL2 INTERFACE TO BUFFER + */ + FMOD_memcpy(&guid, &FMOD_IID_IKsPropertySet, sizeof(GUID)); + + if(mSampleReverb->mBuffer3D->QueryInterface(guid, (void **)&mBufferReverb) != DS_OK) + { + mBufferReverb = 0; + return false; + } + + /* + QUERY FOR I3DL2 support + */ + support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_ALL); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_ROOM); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_ROOMHF); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_ROOMROLLOFFFACTOR); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_DECAYTIME); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_DECAYHFRATIO); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_REFLECTIONS); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_REFLECTIONSDELAY); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_REVERB); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_REVERBDELAY); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_DIFFUSION); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_DENSITY); + if (support) support = querySupportI3DL2(DSPROPERTY_I3DL2LISTENER_HFREFERENCE); + + if (support) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initI3DL2", "I3DL2 - success\n")); + } + else + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initI3DL2", "I3DL2 - FAIL\n")); + } + + if (!mFeaturesReverb) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::initI3DL2", "failed I3DL2 check, releasing reverb interface\n")); + return false; + } + + /* + Turn down reverb to start with. + */ + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_I3DL2LISTENER_ROOM)) + { + DWORD Room = I3DL2LISTENER_MINROOM; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_I3DL2LISTENER_ROOM, 0, 0, &Room, sizeof(DWORD)); + } + + return true; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::closeI3DL2() +{ + if (mBufferReverb) + { + /* + Turn down reverb again + */ + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_I3DL2LISTENER_ROOM)) + { + GUID guid; + DWORD Room = I3DL2LISTENER_MINROOM; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_ListenerProperties, sizeof(GUID)); + + mBufferReverb->Set(guid, DSPROPERTY_I3DL2LISTENER_ROOM, 0, 0, &Room, sizeof(DWORD)); + } + + mBufferReverb->Release(); + mBufferReverb = 0; + } + + if (mSampleReverb) + { + mSampleReverb->release(); + mSampleReverb = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::setPropertiesI3DL2(const FMOD_REVERB_PROPERTIES *prop) +{ + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesI3DL2", "Entering \n")); + + if ( !(mFeaturesReverb & (DWORD)1 << DSPROPERTY_I3DL2LISTENER_ALL) ) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesI3DL2", "failed - apparently DSPROPERTY_I3DL2LISTENER_ALL isnt supported\n")); + + return FMOD_ERR_UNSUPPORTED; + } + + if (mFeaturesReverb & (DWORD)1 << DSPROPERTY_I3DL2LISTENER_ALL) + { + HRESULT hr; + I3DL2_LISTENERPROPERTIES dsprop = {0}; + GUID guid; + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesI3DL2", "setting listener properties\n")); + + dsprop.lRoom = prop->Room; + dsprop.lRoomHF = prop->RoomHF; + dsprop.flRoomRolloffFactor = prop->RoomRolloffFactor; + dsprop.flDecayTime = prop->DecayTime; + dsprop.flDecayHFRatio = prop->DecayHFRatio; + dsprop.lReflections = prop->Reflections; + dsprop.flReflectionsDelay = prop->ReflectionsDelay; + dsprop.lReverb = prop->Reverb; + dsprop.flReverbDelay = prop->ReverbDelay; + dsprop.flDiffusion = prop->Diffusion; + dsprop.flDensity = prop->Density; + dsprop.flHFReference = prop->HFReference; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Set(guid, DSPROPERTY_I3DL2LISTENER_ALL, 0, 0, &dsprop, sizeof(I3DL2_LISTENERPROPERTIES)); + if (FAILED(hr)) + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputDSound::setPropertiesEAX2", "setting listener properties FAILED\n")); + + return FMOD_ERR_INVALID_PARAM; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::getPropertiesI3DL2(FMOD_REVERB_PROPERTIES *prop) +{ + ULONG ulReceived; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + if ( !(mFeaturesReverb & (DWORD)1 << DSPROPERTY_I3DL2LISTENER_ALL) ) + { + return FMOD_ERR_UNSUPPORTED; + } + + if ((mFeaturesReverb & (DWORD)1 << DSPROPERTY_I3DL2LISTENER_ALL)) + { + GUID guid; + HRESULT hr; + I3DL2_LISTENERPROPERTIES dsprop = {0}; + + FMOD_memcpy(&guid, &FMOD_DSPROPSETID_I3DL2_ListenerProperties, sizeof(GUID)); + + hr = mBufferReverb->Get(guid, DSPROPERTY_I3DL2LISTENER_ALL, 0, 0, &dsprop, sizeof(I3DL2_LISTENERPROPERTIES), &ulReceived); + if (FAILED(hr)) + { + return FMOD_ERR_UNSUPPORTED; + } + + prop->Environment = 0; + prop->EnvSize = 0; + prop->EnvDiffusion = 0; + prop->Room = dsprop.lRoom; + prop->RoomHF = dsprop.lRoomHF; + prop->RoomLF = 0; + prop->DecayTime = dsprop.flDecayTime; + prop->DecayHFRatio = dsprop.flDecayHFRatio; + prop->DecayLFRatio = 0; + prop->Reflections = dsprop.lReflections; + prop->ReflectionsDelay = dsprop.flReflectionsDelay; + prop->ReflectionsPan[0] = 0; + prop->ReflectionsPan[1] = 0; + prop->ReflectionsPan[2] = 0; + prop->Reverb = dsprop.lReverb; + prop->ReverbDelay = dsprop.flReverbDelay; + prop->ReverbPan[0] = 0; + prop->ReverbPan[1] = 0; + prop->ReverbPan[2] = 0; + prop->EchoTime = 0; + prop->EchoDepth = 0; + prop->ModulationTime = 0; + prop->ModulationDepth = 0; + prop->AirAbsorptionHF = 0; + prop->HFReference = dsprop.flHFReference; + prop->LFReference = 0; + prop->RoomRolloffFactor = dsprop.flRoomRolloffFactor; + prop->Flags = 0; + prop->Density = dsprop.flDensity; + prop->Diffusion = dsprop.flDiffusion; + } + + return FMOD_OK; +} + +} + +#endif /* FMOD_SUPPORT_I3DL2 */ \ No newline at end of file diff --git a/win32/src/fmod_output_dsound_record.cpp b/win32/src/fmod_output_dsound_record.cpp new file mode 100755 index 0000000..0b4c3ae --- /dev/null +++ b/win32/src/fmod_output_dsound_record.cpp @@ -0,0 +1,546 @@ +#include "fmod_settings.h" + +#if defined(FMOD_SUPPORT_DSOUND) && defined(FMOD_SUPPORT_RECORDING) + +#include "fmod.hpp" +#include "fmod_output_dsound.h" +#include "fmod_soundi.h" +#include "fmod_stringw.h" +#include "fmod_codec_wav.h" + +#include <dxsdkver.h> +#if (_DXSDK_PRODUCT_MAJOR < 9 || (_DXSDK_PRODUCT_MAJOR == 9 && _DXSDK_PRODUCT_MINOR < 21)) + #include <dplay.h> /* This defines DWORD_PTR for dsound.h to use. */ +#endif +#include <dsound.h> + +namespace FMOD +{ + +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} }; +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} }; + + +/* +[ + [DESCRIPTION] + Callback to enumerate each found input driver + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + The first enumerated driver is skipped as it has no GUID, (it's a duplicate + anyway), the next driver enumerated is always the default, therefore it will be + in element 0 of FMODs driver list as required. + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +BOOL CALLBACK FMOD_Output_DSound_RecordEnumProc(LPGUID lpGUID, LPCWSTR lpszDesc, LPCWSTR lpszDrvName, LPVOID lpContext) +{ + FMOD::OutputDSound *outputdsound = (FMOD::OutputDSound *)lpContext; + + // Don't allow more drivers than the maximum number of drivers + if (outputdsound->mRecordNumDrivers < FMOD_OUTPUT_MAXDRIVERS) + { + // Don't add pseudo driver "Primary..." (no GUID), it is just a dupe anyway + if (lpGUID) + { + outputdsound->mRecordDriverName[outputdsound->mRecordNumDrivers] = (short *)FMOD_Memory_Calloc((FMOD_strlenW((short *)lpszDesc) + 1) * sizeof(short)); + if (outputdsound->mRecordDriverName[outputdsound->mRecordNumDrivers]) + { + FMOD_strncpyW(outputdsound->mRecordDriverName[outputdsound->mRecordNumDrivers], (short *)lpszDesc, FMOD_strlenW((short *)lpszDesc)); + } + + FMOD_memcpy(&outputdsound->mRecordGUID[outputdsound->mRecordNumDrivers], lpGUID, sizeof(GUID)); + + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, (short *)lpszDesc, FMOD_STRING_MAXNAMELEN); + FMOD_wtoa(driverName); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "FMOD_Output_DSound_RecordEnumProc", "Enumerating \"%s\"\n", driverName)); + } + + outputdsound->mRecordNumDrivers++; + } + } + + return TRUE; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordEnumerate() +{ + FMOD_RESULT result; + + if (mRecordEnumerated) + { + return FMOD_OK; + } + + result = registerDLL(); + if (result != FMOD_OK) + { + return result; + } + + for (int i = 0; i < mRecordNumDrivers; i++) + { + if (mRecordDriverName[i]) + { + FMOD_Memory_Free(mRecordDriverName[i]); + mRecordDriverName[i] = NULL; + } + } + mRecordNumDrivers = 0; + + if (mDirectSoundCaptureEnumerate) + { + (*mDirectSoundCaptureEnumerate)((LPDSENUMCALLBACKW)FMOD_Output_DSound_RecordEnumProc, (void *)this); + } + + mRecordEnumerated = true; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordGetNumDrivers(int *numdrivers) +{ + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRecordEnumerated) + { + FMOD_RESULT result; + + result = recordEnumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + *numdrivers = mRecordNumDrivers; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + if (!mRecordEnumerated) + { + FMOD_RESULT result; + + result = recordEnumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (id < 0 || id >= mRecordNumDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, mRecordDriverName[id], FMOD_STRING_MAXNAMELEN - 1); + FMOD_wtoa(driverName); + + FMOD_strncpy(name, (char *)driverName, namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + FMOD_memcpy(guid, &mRecordGUID[id], sizeof(FMOD_GUID)); + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordGetDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + if (!mRecordEnumerated) + { + FMOD_RESULT result; + + result = recordEnumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (id < 0 || id >= mRecordNumDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + FMOD_strncpyW(name, mRecordDriverName[id], namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + FMOD_memcpy(guid, &mRecordGUID[id], sizeof(FMOD_GUID)); + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordStart(FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop) +{ + FMOD_RESULT result; + DSCBUFFERDESC dscbd; + WAVE_FORMATEXTENSIBLE wfx; + SoundI *soundi; + int bits; + HRESULT hr; + FMOD_DSoundRecordMembers *recordMembers = NULL; + + soundi = (SoundI *)sound; + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + result = registerDLL(); + if (result != FMOD_OK) + { + return result; + } + + if (!mDirectSoundCaptureCreate) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + recordinfo->mRecordPlatformSpecific = recordMembers = (FMOD_DSoundRecordMembers *)FMOD_Object_Calloc(FMOD_DSoundRecordMembers); + if (recordMembers == NULL) + { + return FMOD_ERR_MEMORY; + } + + // ======================================================================================================== + // CREATE CAPTURE SYSTEM + // ======================================================================================================== + hr = (*mDirectSoundCaptureCreate)(&mRecordGUID[recordinfo->mRecordId], &recordMembers->mDirectSoundCapture, 0); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + // ======================================================================================================== + // CREATE AND START CAPTURE BUFFER + // ======================================================================================================== + recordinfo->mRecordFormat = soundi->mFormat; + recordinfo->mRecordChannels = soundi->mChannels; + recordinfo->mRecordBufferLength = soundi->mLength; + recordinfo->mRecordRate = (int)soundi->mDefaultFrequency; + + SoundI::getBitsFromFormat(recordinfo->mRecordFormat, &bits); + + FMOD_memset(&wfx, 0, sizeof(WAVE_FORMATEXTENSIBLE)); + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfx.Format.nChannels = recordinfo->mRecordChannels; + wfx.Format.wBitsPerSample = bits; + wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8; + wfx.Format.nSamplesPerSec = recordinfo->mRecordRate; + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Format.cbSize = 22; // Designates extra data + wfx.Samples.wValidBitsPerSample = bits; + FMOD_memcpy(&wfx.SubFormat, (soundi->mFormat == FMOD_SOUND_FORMAT_PCMFLOAT ? &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : &KSDATAFORMAT_SUBTYPE_PCM), sizeof(GUID)); + + dscbd.dwSize = sizeof(DSCBUFFERDESC); + dscbd.dwFlags = 0; + dscbd.dwBufferBytes = recordinfo->mRecordBufferLength * wfx.Format.nBlockAlign; + dscbd.dwReserved = 0; + dscbd.lpwfxFormat = (WAVEFORMATEX*)&wfx; + dscbd.dwFXCount = 0; + dscbd.lpDSCFXDesc = 0; + + hr = recordMembers->mDirectSoundCapture->CreateCaptureBuffer(&dscbd, &recordMembers->mDirectSoundCaptureBuffer, 0); + if (hr != DS_OK) + { + return FMOD_ERR_FORMAT; + } + + hr = recordMembers->mDirectSoundCaptureBuffer->Start(loop); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordStop(FMOD_RECORDING_INFO *recordinfo) +{ + FMOD_DSoundRecordMembers *recordMembers = (FMOD_DSoundRecordMembers *)recordinfo->mRecordPlatformSpecific; + + if (recordinfo) + { + if (recordMembers->mDirectSoundCaptureBuffer) + { + recordMembers->mDirectSoundCaptureBuffer->Stop(); + recordMembers->mDirectSoundCaptureBuffer->Release(); + recordMembers->mDirectSoundCaptureBuffer = 0; + } + + if (recordMembers->mDirectSoundCapture) + { + recordMembers->mDirectSoundCapture->Release(); + recordMembers->mDirectSoundCapture = 0; + } + + FMOD_Memory_Free(recordinfo->mRecordPlatformSpecific); + recordinfo->mRecordPlatformSpecific = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordGetPosition(FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + FMOD_RESULT result; + HRESULT hr = DS_OK; + unsigned int position = 0, lengthbytes; + FMOD_SOUND_FORMAT format; + int bits, channels; + FMOD_DSoundRecordMembers *recordMembers = (FMOD_DSoundRecordMembers *)recordinfo->mRecordPlatformSpecific; + + if (!recordMembers->mDirectSoundCaptureBuffer) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + hr = recordMembers->mDirectSoundCaptureBuffer->GetCurrentPosition(0, (LPDWORD)&position); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + result = recordinfo->mRecordSound->getLength(&lengthbytes, FMOD_TIMEUNIT_PCMBYTES); + if (result != FMOD_OK) + { + return result; + } + + result = recordinfo->mRecordSound->getFormat(0, &format, &channels, &bits); + if (result != FMOD_OK) + { + return result; + } + + if (position >= lengthbytes) + { + *pcm = 0; + return FMOD_ERR_OUTPUT_DRIVERCALL;; + } + + position *= 8; + position /= bits; + position /= channels; + + if (pcm) + { + *pcm = position; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordLock(FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD_DSoundRecordMembers *recordMembers = (FMOD_DSoundRecordMembers *)recordinfo->mRecordPlatformSpecific; + + recordMembers->mDirectSoundCaptureBuffer->Lock(offset, length, ptr1, (DWORD *)len1, ptr2, (DWORD *)len2, 0); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputDSound::recordUnlock(FMOD_RECORDING_INFO *recordinfo, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + FMOD_DSoundRecordMembers *recordMembers = (FMOD_DSoundRecordMembers *)recordinfo->mRecordPlatformSpecific; + + recordMembers->mDirectSoundCaptureBuffer->Unlock(ptr1, len1, ptr2, len2); + return FMOD_OK; +} + +} + +#endif \ No newline at end of file diff --git a/win32/src/fmod_output_openal.cpp b/win32/src/fmod_output_openal.cpp new file mode 100755 index 0000000..e36fae2 --- /dev/null +++ b/win32/src/fmod_output_openal.cpp @@ -0,0 +1,2224 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL + +#include "fmod_output_openal.h" +#include "fmod_sample_openal.h" +#include "fmod_channel_openal.h" +#include "fmod_channel_openal_eax2.h" +#include "fmod_channel_openal_eax3.h" +#include "fmod_channel_openal_eax4.h" +#include "fmod_channel_openal_eax5.h" + +namespace FMOD +{ + +#define SPEED_OF_SOUND 340.0f // Same as A3D + +#define EXIT_ON_CONDITION(condition, error) \ + if (condition) { close(); return error; } +#define SAFE_RELEASE(pPointer) \ + if ((pPointer) != NULL) { (pPointer)->release(); (pPointer) = NULL; } +#define SAFE_FREE(pPointer) \ + if ((pPointer) != NULL) { FMOD_Memory_Free((pPointer)); (pPointer) = NULL; } + +FMOD_OUTPUT_DESCRIPTION_EX openaloutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. + This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputOpenAL::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + Assign methods for various FMOD output hooks required by output type + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OUTPUT_DESCRIPTION_EX + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputOpenAL::getDescriptionEx() +{ + openaloutput.name = "FMOD OpenAL Output"; + openaloutput.version = 0x00010100; + openaloutput.mType = FMOD_OUTPUTTYPE_OPENAL; + openaloutput.mSize = sizeof(OutputOpenAL); + openaloutput.polling = true; + + openaloutput.getnumdrivers = &OutputOpenAL::getNumDriversCallback; + openaloutput.getdrivername = &OutputOpenAL::getDriverNameCallback; + openaloutput.getdrivercapsex2 = &OutputOpenAL::getDriverCapsExCallback; + openaloutput.init = &OutputOpenAL::initCallback; + openaloutput.close = &OutputOpenAL::closeCallback; + openaloutput.start = &OutputOpenAL::startCallback; + openaloutput.stop = &OutputOpenAL::stopCallback; + openaloutput.gethandle = &OutputOpenAL::getHandleCallback; + openaloutput.update = &OutputOpenAL::updateCallback; + openaloutput.postmixcallback = &OutputOpenAL::postMixCallback; + openaloutput.getposition = &OutputOpenAL::getPositionCallback; + openaloutput.lock = &OutputOpenAL::lockCallback; + openaloutput.unlock = &OutputOpenAL::unlockCallback; + openaloutput.createsample = &OutputOpenAL::createSampleCallback; + openaloutput.reverb_setproperties = &OutputOpenAL::setReverbPropertiesCallback; + openaloutput.getsamplemaxchannels = &OutputOpenAL::getSampleMaxChannelsCallback; + + return &openaloutput; +} + + +/* +[ + [DESCRIPTION] + OpenAL Constructor + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +OutputOpenAL::OutputOpenAL() +{ + mDLLInitialised = false; + mEnumerated = false; + mInitialised = false; + mNumDrivers = 0; +} + + +/* +[ + [DESCRIPTION] + Load the OpenAL DLL + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::registerDLL() +{ + if (mDLLInitialised) + { + return FMOD_OK; + } + + if (!LoadOALLibrary(NULL, &mOALFnTable)) + { + return FMOD_ERR_OUTPUT_INIT; + } + + mDLLInitialised = true; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Enumerate a list of OpenAL devices + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + OpenAL devices list extracts device names for native devices i.e. Creative, the + generic devices use whatever is currently the default + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::enumerate() +{ + FMOD_RESULT result = FMOD_OK; + char *deviceList = NULL; + ALCdevice *device = NULL; + ALCcontext *context = NULL; + + /* + Set up gGlobal - for debug / file / memory access by this plugin. + */ + Plugin::init(); + + // Check if already enumerated + if (mEnumerated) + { + return FMOD_OK; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::enumerate", "Enumerating...\n")); + + // Do one time initialisation code + if (!mSetupOnce) + { + // Ensure the DLL is loaded + result = registerDLL(); + if (result != FMOD_OK) + { + return result; + } + + mSetupOnce = true; + } + + // Clean up any driver enumeration (may have enumerated once already) + for (int i = 0; i < mNumDrivers; i++) + { + SAFE_FREE(mDriverNames[i]); + } + mNumDrivers = 0; + + // Get a list of all devices, each device is NULL terminated, list is double NULL terminated + deviceList = (char *)mOALFnTable.alcGetString(NULL, ALC_DEVICE_SPECIFIER); + while (*deviceList != NULL && mNumDrivers < FMOD_OUTPUT_MAXDRIVERS) + { + // Open the device + device = mOALFnTable.alcOpenDevice(deviceList); + if (device == NULL) + { + // Couldn't open device, move onto the next + deviceList += FMOD_strlen(deviceList) + 1; + continue; + } + + // Create a context for the device + context = mOALFnTable.alcCreateContext(device, NULL); + if (context == NULL) + { + // Couldn't create context, move onto the next + mOALFnTable.alcCloseDevice(device); + deviceList += FMOD_strlen(deviceList) + 1; + continue; + } + + // Make context active + mOALFnTable.alcGetError(device); + mOALFnTable.alcMakeContextCurrent(context); + if (mOALFnTable.alcGetError(device) != ALC_NO_ERROR) + { + // Couldn't set context as current, move onto the next + mOALFnTable.alcDestroyContext(context); + mOALFnTable.alcCloseDevice(device); + deviceList += FMOD_strlen(deviceList) + 1; + continue; + } + + // Get the version of OpenAL the device supports + int majorVersion = 0; + int minorVersion = 0; + mOALFnTable.alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &majorVersion); + mOALFnTable.alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &minorVersion); + mOALFnTable.alcGetError(device); + + // Only add devices that are OpenAL 1.1 compliant or greater + if (majorVersion >= 1 && minorVersion >= 1) + { + // Get the name of this device (may be different to name in above list due to fallback support) + const char *deviceName = mOALFnTable.alcGetString(device, ALC_DEVICE_SPECIFIER); + bool dupeFound = false; + + // Check the list if this device is a duplicate + for (int i = 0; i < mNumDrivers; i++) + { + if (FMOD_strcmp(deviceName, mDriverNames[i]) == 0) + { + // Found a device already in the list, ignore it (e.g. may have fallen back from hardware to software) + dupeFound = true; + break; + } + } + + // Only add if not a duplicate entry + if (!dupeFound) + { + // Allocate memory for the device name + mDriverNames[mNumDrivers] = (char *)FMOD_Memory_Calloc(FMOD_strlen(deviceName) + 1); + if (mDriverNames[mNumDrivers] == NULL) + { + mOALFnTable.alcMakeContextCurrent(NULL); + mOALFnTable.alcDestroyContext(context); + mOALFnTable.alcCloseDevice(device); + return FMOD_ERR_MEMORY; + } + + // Add the new device to the list of useable enumerated devices + FMOD_strcpy(mDriverNames[mNumDrivers], deviceName); + mNumDrivers++; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::enumerate", "Found Driver: \"%s\"\n", deviceName)); + } + } + + // Clean up and move onto the next device in the list + mOALFnTable.alcMakeContextCurrent(NULL); + mOALFnTable.alcDestroyContext(context); + mOALFnTable.alcCloseDevice(device); + deviceList += FMOD_strlen(deviceList) + 1; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::enumerate", "Done.\n")); + mEnumerated = true; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the number of OpenAL devices + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getNumDrivers(int *numdrivers) +{ + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mEnumerated) + { + FMOD_RESULT result; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + *numdrivers = mNumDrivers; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the name of an OpenAL device given its index + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getDriverName(int driver, char *name, int namelen) +{ + if (!mEnumerated) + { + FMOD_RESULT result; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (driver < 0 || driver >= mNumDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + FMOD_strncpy(name, mDriverNames[driver], namelen - 1); + name[namelen - 1] = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the capabilities of the OpenAL device at the given index + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getDriverCapsEx(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels) +{ + ALCdevice *device = NULL; + ALCcontext *context = NULL; + FMOD_RESULT result = FMOD_OK; + + if (!mEnumerated) + { + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + // Check if using the software mode (fallback from hardware is already considered in the enumeration) + if (FMOD_strcmp(mDriverNames[id], "Generic Software") == 0 || + FMOD_strcmp(mDriverNames[id], "DirectSound") == 0 || + FMOD_strcmp(mDriverNames[id], "MMSYSTEM") == 0) + { + *caps |= FMOD_CAPS_HARDWARE_EMULATED; + } + // Must be using hardware through DSound or a native driver + else + { + *caps |= FMOD_CAPS_HARDWARE; + } + + // OpenAL only supports PCM8 and PCM16, but we force PCM16 + *caps |= FMOD_CAPS_OUTPUT_FORMAT_PCM16; + + // OpenAL frequency is forced to 44100 + *minfrequency = *maxfrequency = 44100; + + /* + Check for EAX support + */ + #ifdef FMOD_SUPPORT_EAX + // Open the selected device + device = mOALFnTable.alcOpenDevice(mDriverNames[id]); + if (!device) + { + return FMOD_ERR_OUTPUT_INIT; + } + + // Create a context for the device + context = mOALFnTable.alcCreateContext(device, NULL); + if (!context) + { + mOALFnTable.alcCloseDevice(device); + return FMOD_ERR_OUTPUT_INIT; + } + + // Make context active + mOALFnTable.alcGetError(device); + mOALFnTable.alcMakeContextCurrent(context); + if (mOALFnTable.alcGetError(device) != ALC_NO_ERROR) + { + mOALFnTable.alcDestroyContext(context); + mOALFnTable.alcCloseDevice(device); + return FMOD_ERR_OUTPUT_INIT; + } + + // Determine highest EAX version supported + if (mOALFnTable.alIsExtensionPresent("EAX5.0")) + { + *caps |= FMOD_CAPS_REVERB_EAX5; + + result = getSpeakerModeEAX5(controlpanelspeakermode); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_WARNING, __FILE__, __LINE__, "OutputOpenAL::getDriverCapsEx", "Could not get speaker mode, defaulting to Stereo\n")); + *controlpanelspeakermode = FMOD_SPEAKERMODE_STEREO; + } + mNumHwChannels = 128; + } + else if (mOALFnTable.alIsExtensionPresent("EAX4.0")) + { + *caps |= FMOD_CAPS_REVERB_EAX4; + mNumHwChannels = 64; + } + else if (mOALFnTable.alIsExtensionPresent("EAX3.0")) + { + *caps |= FMOD_CAPS_REVERB_EAX3; + mNumHwChannels = 64; + } + else if (mOALFnTable.alIsExtensionPresent("EAX2.0")) + { + *caps |= FMOD_CAPS_REVERB_EAX2; + mNumHwChannels = 32; + } + else + { + mNumHwChannels = 8; + } + + // Cleanup OpenAL device + mOALFnTable.alcMakeContextCurrent(NULL); + mOALFnTable.alcDestroyContext(context); + mOALFnTable.alcCloseDevice(device); + #endif + + *num2dchannels = mNumHwChannels; + *num3dchannels = mNumHwChannels; + *totalchannels = mNumHwChannels; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Initialise EAX for use by OpenAL + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::initEAX() +{ + #ifdef FMOD_SUPPORT_EAX + // Determine supported version of EAX + if (mOALFnTable.alIsExtensionPresent("EAX5.0")) + { + mReverbVersion = REVERB_VERSION_EAX5; + mNumHwChannels = 128; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 5.0 detected\n")); + } + else if (mOALFnTable.alIsExtensionPresent("EAX4.0")) + { + mReverbVersion = REVERB_VERSION_EAX4; + mNumHwChannels = 60; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 4.0 detected\n")); + } + else if (mOALFnTable.alIsExtensionPresent("EAX3.0")) + { + mReverbVersion = REVERB_VERSION_EAX3; + mNumHwChannels = 60; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 3.0 detected\n")); + } + else if (mOALFnTable.alIsExtensionPresent("EAX2.0")) + { + mReverbVersion = REVERB_VERSION_EAX2; + mNumHwChannels = 30; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 2.0 detected\n")); + } + else + { + // Should not get this far, software mode OpenAL emulates EAX2 + mReverbVersion = REVERB_VERSION_NONE; + mNumHwChannels = 8; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "No EAX version detected")); + } + + // Load EAX Get and Set functions + mEAXGet = (EAXGet)mOALFnTable.alGetProcAddress("EAXGet"); + mEAXSet = (EAXSet)mOALFnTable.alGetProcAddress("EAXSet"); + if (!mEAXGet || !mEAXSet) + { + mReverbVersion = REVERB_VERSION_NONE; + } + + // Determine if board with XRAM + if (mOALFnTable.alIsExtensionPresent("EAX-RAM")) + { + // Load EAX GetBufferMode and SetBufferMode functions + mEAXGetBufferMode = (EAXGetBufferMode)mOALFnTable.alGetProcAddress("EAXGetBufferMode"); + mEAXSetBufferMode = (EAXSetBufferMode)mOALFnTable.alGetProcAddress("EAXSetBufferMode"); + if (!mEAXGetBufferMode || !mEAXSetBufferMode) + { + // Will fail on none XFi XRAM cards + } + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX-RAM detected\n")); + } + + // EAX5.0 Requires you to setup a session before making any EAX5.0 calls + if (mReverbVersion == REVERB_VERSION_EAX5) + { + setupSessionEAX5(); + } + #else + mReverbVersion = REVERB_VERSION_NONE; + mNumHwChannels = 8; + #endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Initialise OpenAL output + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + FMOD_RESULT result = FMOD_OK; + int formatBits = 0; + mDevice = NULL; + mContext = NULL; + mBufferData = NULL; + mNumChannels = 0; + mChannels = NULL; + mNumSources = 0; + mSources = NULL; + mMixerReverbDisabled = false; + mEAXSet = NULL; + mEAXGet = NULL; + mEAXSetBufferMode = NULL; + mEAXGetBufferMode = NULL; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Initialising...\n")); + + if (!mEnumerated) + { + result = enumerate(); + EXIT_ON_CONDITION(result != FMOD_OK, result); + } + + if (mNumDrivers <= 0) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "No sound devices with support for OpenAL v1.1\n")); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + // Use provided number of channels + mOutputChannels = outputchannels; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Using provided number of channels: %d\n", mOutputChannels)); + // Override specified output rate + mRate = *outputrate = 44100; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Forcing sample rate: 44100\n")); + // Override specified output format + mFormat = *outputformat = FMOD_SOUND_FORMAT_PCM16; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Forcing output format: PCM16\n")); + + // Get the number of bits per sample + result = SoundI::getBitsFromFormat(mFormat, &formatBits); + EXIT_ON_CONDITION(result != FMOD_OK, result); + // Set buffer size + mNumBuffers = dspnumbuffers; + mBufferLength = dspbufferlength; + mBufferLengthBytes = mBufferLength * mOutputChannels * (formatBits / 8); + + // Open the selected device + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Opening device\n")); + mDevice = mOALFnTable.alcOpenDevice(mDriverNames[selecteddriver]); + if (!mDevice) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't open device: %s", mDriverNames[selecteddriver])); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + // Create a context for the device + mContext = mOALFnTable.alcCreateContext(mDevice, NULL); + if (!mContext) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't create context for device: %s", mDriverNames[selecteddriver])); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + // Make context active + mOALFnTable.alcGetError(mDevice); + mOALFnTable.alcMakeContextCurrent(mContext); + if (mOALFnTable.alcGetError(mDevice) != ALC_NO_ERROR) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't make context current for device: %s", mDriverNames[selecteddriver])); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + // Set speed of sound + mOALFnTable.alSpeedOfSound(SPEED_OF_SOUND); + if (mOALFnTable.alcGetError(mDevice) != ALC_NO_ERROR) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't set speed of sound to %.02f", SPEED_OF_SOUND)); + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + // Initialised EAX + result = initEAX(); + EXIT_ON_CONDITION(result != FMOD_OK, result); + + // Clear Error Codes + mOALFnTable.alGetError(); + mOALFnTable.alcGetError(mDevice); + + // Limit hardware channels by the user set max + if (mNumHwChannels > mSystem->mMaxHardwareChannels3D) + { + mNumHwChannels = mSystem->mMaxHardwareChannels3D; + } + // Reserve hardware channels for the FMOD software mixer + mNumChannels = mNumHwChannels - mOutputChannels; + // Shouldn't go negative, mNumHwChannels min is set at 8, but just in case + if (mNumChannels < 0) + { + close(); + return FMOD_ERR_OUTPUT_INIT; + } + + // Allocate memory for mix buffer + mBufferData = (char *)FMOD_Memory_Calloc(mNumBuffers * mBufferLengthBytes); + EXIT_ON_CONDITION(mBufferData == NULL, FMOD_ERR_MEMORY); + + // Allocate memory for OpenAL sources + mSources = (SourceOpenAL *)FMOD_Memory_Calloc(mNumHwChannels * sizeof(SourceOpenAL)); + EXIT_ON_CONDITION(mSources == NULL, FMOD_ERR_MEMORY); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Initialising device with %d channels\n", mNumHwChannels)); + + /* + Create OpenAL sources for each hardware channel + */ + // Clear errors + mOALFnTable.alGetError(); + do + { + mOALFnTable.alGenSources(1, &mSources[mNumSources].sid); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + break; // Could not create OpenAL source + } + + // Assign this source as currently unused + mSources[mNumSources].used = false; + + // Allocate space for the buffer IDs + mSources[mNumSources].bid = (ALuint *)FMOD_Memory_Calloc(mNumBuffers * sizeof(ALuint)); + EXIT_ON_CONDITION(mSources[mNumSources].bid == NULL, FMOD_ERR_MEMORY); + + // Generate buffers for each source + for (int i = 0; i < mNumBuffers; i++) + { + mOALFnTable.alGenBuffers(1, &mSources[mNumSources].bid[i]); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + break; // Could not create OpenAL buffer + } + + if (mEAXSetBufferMode) + { + mEAXSetBufferMode(1, &mSources[mNumSources].bid[i], mOALFnTable.alGetEnumValue("AL_STORAGE_ACCESSIBLE")); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + break; // Could not change OpenAL buffer mode + } + } + + mOALFnTable.alBufferData(mSources[mNumSources].bid[i], AL_FORMAT_MONO16, mBufferData, mBufferLength * (formatBits / 8), mRate); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + break; // Could not fill OpenAL buffer + } + } + + mNumSources++; + + } while (mNumSources < mNumHwChannels); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Initialised device with %d channels\n", mNumSources)); + + // Update mNumChannels incase enough sources couldn't be created + mNumChannels = mNumSources - mOutputChannels; + // Ensure enough sources were created for the FMOD software mixer + if (mNumSources < mOutputChannels) + { + return FMOD_ERR_OUTPUT_INIT; + } + + // FMOD software mixer uses the top OpenAL sources for output + mMixerSourceOffset = mNumSources - mOutputChannels; + + // Set up the hardware channel pool + mChannelPool3D = FMOD_Object_Alloc(ChannelPool); + EXIT_ON_CONDITION(mChannelPool3D == NULL, FMOD_ERR_MEMORY); + result = mChannelPool3D->init(mSystem, this, mNumChannels); + EXIT_ON_CONDITION(result != FMOD_OK, result); + + // Allocate channel memory + switch (mReverbVersion) + { + #ifdef FMOD_SUPPORT_EAX + case REVERB_VERSION_EAX5: + { + mChannels = (ChannelOpenALEAX5 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX5) * mNumChannels); + break; + } + case REVERB_VERSION_EAX4: + { + mChannels = (ChannelOpenALEAX4 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX4) * mNumChannels); + break; + } + case REVERB_VERSION_EAX3: + { + mChannels = (ChannelOpenALEAX3 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX3) * mNumChannels); + break; + } + case REVERB_VERSION_EAX2: + { + mChannels = (ChannelOpenALEAX2 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX2) * mNumChannels); + break; + } + #endif + default: + { + mChannels = (ChannelOpenAL *)FMOD_Memory_Calloc(sizeof(ChannelOpenAL) * mNumChannels); + break; + } + } + EXIT_ON_CONDITION(mChannels == NULL, FMOD_ERR_MEMORY); + + // Create the channels + for (int i = 0; i < mNumChannels; i++) + { + switch (mReverbVersion) + { + #ifdef FMOD_SUPPORT_EAX + case REVERB_VERSION_EAX5: + { + new (&mChannels[i]) ChannelOpenALEAX5; + break; + } + case REVERB_VERSION_EAX4: + { + new (&mChannels[i]) ChannelOpenALEAX4; + break; + } + case REVERB_VERSION_EAX3: + { + new (&mChannels[i]) ChannelOpenALEAX3; + break; + } + case REVERB_VERSION_EAX2: + { + new (&mChannels[i]) ChannelOpenALEAX2; + break; + } + #endif + default: + { + new (&mChannels[i]) ChannelOpenAL; + break; + } + } + + // Apply the channel to the channel pool + result = mChannelPool3D->setChannel(i, &mChannels[i]); + EXIT_ON_CONDITION(result != FMOD_OK, result); + } + + // 2D channel pool is the same as the 3D pool + mChannelPool = mChannelPool3D; + + // Get format enum for mix buffer + switch (mOutputChannels) + { + case 1: + mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_MONO16"); + break; + case 2: + mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_STEREO16"); + break; + case 4: + mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_QUAD16"); + break; + case 6: + mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_51CHN16"); + break; + case 7: + mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_61CHN16"); + break; + case 8: + mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_71CHN16"); + break; + default: + mFormatOAL = 0; + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "alGetEnumValue failed\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + mInitialised = true; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Done.\n")); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Shutdown and cleanup resources created + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::close() +{ + /* + Set up gGlobal - for debug / file / memory access by this plugin. + */ + Plugin::init(); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Closing...\n")); + + // Release the channel pool, (both pools are the same) + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Free channel pool\n")); + SAFE_RELEASE(mChannelPool); + mChannelPool = mChannelPool3D = NULL; + // Free the channel data + SAFE_FREE(mChannels); + + // Cleanup OpenAL sources + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Free OpenAL sources and buffers\n")); + if (mSources) + { + for (int i = 0; i < mNumSources; i++) + { + mOALFnTable.alDeleteSources(1, &mSources[i].sid); + mOALFnTable.alDeleteBuffers(mNumBuffers, mSources[i].bid); + SAFE_FREE(mSources[i].bid); + } + SAFE_FREE(mSources); + } + + // Clean up the mix buffer + SAFE_FREE(mBufferData) + + // Cleanup OpenAL context and device + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Closing OpenAL device\n")); + if (mContext) + { + mOALFnTable.alcMakeContextCurrent(NULL); + mOALFnTable.alcDestroyContext(mContext); + mContext = NULL; + } + if (mDevice) + { + mOALFnTable.alcCloseDevice(mDevice); + mDevice = NULL; + } + + // Cleanup driver list + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Free OpenAL driver list\n")); + for (int i = 0; i < mNumDrivers; i++) + { + SAFE_FREE(mDriverNames[i]); + } + + // Cleanup OpenAL library + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "FreeLibrary on OpenAL32.dll\n")); + UnloadOALLibrary(); + + mDLLInitialised = false; + mEnumerated = false; + mSetupOnce = false; + mNumDrivers = 0; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Done.\n")); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Start playing the software mixer buffer + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::start() +{ + FMOD_RESULT result = FMOD_OK; + + // Reset PCM base position + mPcmBase = 0; + + // Mark the mixer sources as in use by software mixer + for (int i = 0; i < mOutputChannels; i++) + { + mSources[mMixerSourceOffset + i].used = true; + } + + // Fill OpenAL buffers + for (int j = 0; j < mNumBuffers; j++) + { + int dataOffset = j * mBufferLengthBytes; + + // Note: If using OpenAL "Generic Software" device, OpenAL will downmix to stereo + mOALFnTable.alBufferData(mSources[mMixerSourceOffset].bid[j], mFormatOAL, mBufferData + dataOffset, mBufferLengthBytes, mRate); + mOALFnTable.alSourceQueueBuffers(mSources[mMixerSourceOffset].sid, 1, &mSources[mMixerSourceOffset].bid[j]); + } + + // Start the mixer thread + result = OutputTimer::start(); + if (result != FMOD_OK) + { + return result; + } + + // Set the properties of the mixer source, then start it + mOALFnTable.alSourcef(mSources[mMixerSourceOffset].sid, AL_GAIN, 1.0f); + mOALFnTable.alSourcef(mSources[mMixerSourceOffset].sid, AL_PITCH, 1.0f); + mOALFnTable.alSourcei(mSources[mMixerSourceOffset].sid, AL_LOOPING, AL_FALSE); + mOALFnTable.alSourcePlay(mSources[mMixerSourceOffset].sid); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Stop playing the shared buffer + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::stop() +{ + FMOD_RESULT result = FMOD_OK; + + if (mInitialised) + { + // Stop the mixer thread + result = OutputTimer::stop(); + if (result != FMOD_OK) + { + return result; + } + + // Stop the mixer source + mOALFnTable.alSourceStop(mSources[mMixerSourceOffset].sid); + mOALFnTable.alSourcei(mSources[mMixerSourceOffset].sid, AL_BUFFER, NULL); + + // Mark the mixer sources as not in use by software mixer + for (int i = 0; i < mOutputChannels; i++) + { + mSources[mMixerSourceOffset + i].used = false; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return a handle to the OpenAL device + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getHandle(void **handle) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = mDevice; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Update OpenAL parameters + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::update() +{ + FMOD_RESULT result = FMOD_OK; + int numListeners = 0; + float dopplerScale = 0.0f; + float distanceScale = 0.0f; + float rolloffScale = 0.0f; + static float lastDopplerScale = 0.0f; + static float lastDistanceScale = 0.0f; + static float lastRolloffScale = 0.0f; + + // Note: FMOD default is LHS, OAL default is RHS + FMOD_VECTOR fPosition = { 0.0f, 0.0f, 0.0f }; + FMOD_VECTOR fVelocity = { 0.0f, 0.0f, 0.0f }; + FMOD_VECTOR fForward = { 0.0f, 0.0f, 1.0f }; + FMOD_VECTOR fUp = { 0.0f, 1.0f, 0.0f }; + ALfloat alPosition[] = { 0.0f, 0.0f, 0.0f }; + ALfloat alVelocity[] = { 0.0f, 0.0f, 0.0f }; + ALfloat alOrientation[] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; // First 3 are Forward, second 3 are Up + + // Get the number of FMOD listeners + result = mSystem->get3DNumListeners(&numListeners); + if (result != FMOD_OK) + { + return result; + } + + // If there is only one listener get its details + if (numListeners == 1) + { + result = mSystem->get3DListenerAttributes(0, &fPosition, &fVelocity, &fForward, &fUp); + if (result != FMOD_OK) + { + return result; + } + } + + // Get FMOD 3D sound settings + result = mSystem->get3DSettings(&dopplerScale, &distanceScale, &rolloffScale); + if (result != FMOD_OK) + { + return result; + } + + // Only update settings if necessary + if (!(dopplerScale != lastDopplerScale || + distanceScale != lastDistanceScale || + rolloffScale != lastRolloffScale || + mSystem->mListener[0].mMoved || + mSystem->mListener[0].mRotated)) + { + return FMOD_OK; + } + + /* Update OpenAL listener details from FMOD data */ + alPosition[0] = fPosition.x; + alPosition[1] = fPosition.y; + alPosition[2] = fPosition.z; + + alVelocity[0] = fVelocity.x; + alVelocity[1] = fVelocity.y; + alVelocity[2] = fVelocity.z; + + alOrientation[0] = fForward.x; + alOrientation[1] = fForward.y; + alOrientation[2] = fForward.z; + alOrientation[3] = fUp.x; + alOrientation[4] = fUp.y; + alOrientation[5] = fUp.z; + + // Flip the Z-axis if FMOD is not in RHS mode + if (!(mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED)) + { + alPosition[2] *= -1; // fPosition.z + alVelocity[2] *= -1; // fVelocity.z + alOrientation[2] *= -1; // fForward.z + alOrientation[5] *= -1; // fUp.z + } + + // Set the listener properties + mOALFnTable.alListenerfv(AL_POSITION, alPosition); + mOALFnTable.alListenerfv(AL_VELOCITY, alVelocity); + mOALFnTable.alListenerfv(AL_ORIENTATION, alOrientation); + + // Update the rolloff scale of all the sources + for (int i = 0; i < mNumSources; i++) + { + mOALFnTable.alSourcef(mSources[i].sid, AL_ROLLOFF_FACTOR, rolloffScale); + } + + // Update the doppler factor + mOALFnTable.alDopplerFactor(dopplerScale); + // Update distance factor by setting the speed of sound + mOALFnTable.alSpeedOfSound(SPEED_OF_SOUND * distanceScale); + + // Remember scale values for next update() + lastDopplerScale = dopplerScale; + lastDistanceScale = distanceScale; + lastRolloffScale = rolloffScale; + + // Update channels using tweaked distance models or fudge positioning for multilistener stuff + for (int j = 0; j < mNumChannels; j++) + { + ChannelReal *channelReal = NULL; + ChannelOpenAL *channelOpenAL = NULL; + + result = mChannelPool3D->getChannel(j, &channelReal); + if (result != FMOD_OK) + { + return result; + } + + channelOpenAL = SAFE_CAST(ChannelOpenAL, channelReal); + if (channelOpenAL == NULL) + { + return FMOD_ERR_INTERNAL; + } + + // Process the channel for multilistener or tweaked distance models + if (numListeners > 1 || channelOpenAL->mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF)) + { + result = channelOpenAL->set3DAttributes(); + if (result != FMOD_OK) + { + return result; + } + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Process the OpenAL sources + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::postMix() +{ + FMOD_RESULT result = FMOD_OK; + ChannelReal *channelReal = NULL; + ChannelOpenAL *channelOpenAL = NULL; + static int lastUpdatedChannel = 0; + + // Only update half of the channels each mix *optimisation* + for (int i = 0; i < mNumChannels; i += 2) + { + result = mChannelPool3D->getChannel(lastUpdatedChannel++, &channelReal); + if (result != FMOD_OK) + { + return result; + } + + channelOpenAL = SAFE_CAST(ChannelOpenAL, channelReal); + if (channelOpenAL == NULL) + { + return FMOD_ERR_INTERNAL; + } + + result = channelOpenAL->updateChannel(); + if (result != FMOD_OK) + { + return result; + } + + // Wrap the current channel index + if (lastUpdatedChannel >= mNumChannels) + { + lastUpdatedChannel = 0; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the sample offset in the buffer of the block currently playing + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getPosition(unsigned int *pcm) +{ + if (pcm == NULL) + { + return FMOD_ERR_INVALID_PARAM; + } + + // Get index of current buffer + ALint processed = 0; + mOALFnTable.alGetSourcei(mSources[mMixerSourceOffset].sid, AL_BUFFERS_PROCESSED, &processed); + + // Check for starvation + if (processed == mNumBuffers) + { + mOALFnTable.alSourcePlay(mSources[mMixerSourceOffset].sid); + processed = 0; + } + + // Return the sample number of the current buffer being processed + *pcm = (mPcmBase + (processed * mBufferLength)) % (mNumBuffers * mBufferLength); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Give the mixer thread access to the shared mixer buffer + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + if (!mBufferData) + { + return FMOD_ERR_INVALID_PARAM; + } + + *ptr1 = mBufferData + offset; + *len1 = length; + *ptr2 = NULL; + *len2 = 0; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + ALuint bufferId = 0; + ALint playing = AL_STOPPED; + char *pNewDataPos = (char *)ptr1; + unsigned int dataToBuffer = len1; + + if (!mBufferData) + { + return FMOD_ERR_INVALID_PARAM; + } + + // Keep looping until all the data written to mBufferData by the mixer thread is in OpenAL buffers + while (dataToBuffer >= mBufferLengthBytes) + { + // Remove a used buffer from the source + mOALFnTable.alSourceUnqueueBuffers(mSources[mMixerSourceOffset].sid, 1, &bufferId); + // Copy the next chunk of new data into the currently unqueued OpenAL buffer + mOALFnTable.alBufferData(bufferId, mFormatOAL, pNewDataPos, mBufferLengthBytes, mRate); + // Requeue the buffer that is now filled with new data + mOALFnTable.alSourceQueueBuffers(mSources[mMixerSourceOffset].sid, 1, &bufferId); + + // Update positions for next chunk of data to buffer + pNewDataPos += mBufferLengthBytes; + dataToBuffer -= mBufferLengthBytes; + + // Move to the next sample write position in mBufferData + mPcmBase = (mPcmBase + mBufferLength) % (mNumBuffers * mBufferLength); + } + + // If the buffers have stopped, start them up again (this means all buffers finished before new data arrived) + mOALFnTable.alGetSourcei(mSources[mMixerSourceOffset].sid, AL_SOURCE_STATE, &playing); + if (playing != AL_PLAYING) + { + mOALFnTable.alSourcePlay(mSources[mMixerSourceOffset].sid); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Create an OpenAL hardware sample + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample) +{ + FMOD_RESULT result = FMOD_OK; + SampleOpenAL *newsample = NULL; + unsigned int overflowBytes = 0; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::createSample", "PCM Length: %d, Byte Length: %d, Num Channels: %d, Format: %d, Mode: %08x\n", waveformat ? waveformat->lengthpcm : 0, waveformat ? waveformat->lengthbytes : 0, waveformat ? waveformat->channels : 0, waveformat ? waveformat->format : FMOD_SOUND_FORMAT_NONE, mode)); + + if (!sample) + { + return FMOD_ERR_INVALID_PARAM; + } + + // If a waveformat was specified then ensure it is valid + if (waveformat) + { + int formatBits = 0; + result = SoundI::getBitsFromFormat(waveformat->format, &formatBits); + if (result != FMOD_OK) + { + return result; + } + + // If no bits are returned and the format isn't NONE, MPEG or IMMADPCM then the format is invalid + if (formatBits == 0 && waveformat->format != FMOD_SOUND_FORMAT_NONE + #ifdef FMOD_SUPPORT_DSPCODEC + #ifdef FMOD_SUPPORT_IMAADPCM + && waveformat->format != FMOD_SOUND_FORMAT_IMAADPCM + #endif + #ifdef FMOD_SUPPORT_MPEG + && waveformat->format != FMOD_SOUND_FORMAT_MPEG + #endif + #endif + ) + { + return FMOD_ERR_FORMAT; + } + } + + // Check if a sample was provided + if (*sample == NULL) + { + // If not, allocate one + newsample = FMOD_Object_Calloc(SampleOpenAL); + if (newsample == NULL) + { + return FMOD_ERR_MEMORY; + } + } + else + { + // If so, use it + newsample = SAFE_CAST(SampleOpenAL, *sample); + } + + // If no waveformat is provided, nothing more to do + if (waveformat == NULL) + { + *sample = newsample; + return FMOD_OK; + } + + // Check for compressed formats + if (waveformat->format == FMOD_SOUND_FORMAT_IMAADPCM || waveformat->format == FMOD_SOUND_FORMAT_MPEG) + { + newsample->mLengthBytes = waveformat->lengthbytes; + newsample->mLoopPointDataEnd = NULL; + overflowBytes = 0; + } + else + { + // Get the length of the sample in bytes + result = SoundI::getBytesFromSamples(waveformat->lengthpcm, &newsample->mLengthBytes, waveformat->channels, waveformat->format); + if (result != FMOD_OK) + { + return result; + } + + // Get the length of the overflow bytes for resampling and loop points + result = SoundI::getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowBytes, waveformat->channels, waveformat->format); + if (result != FMOD_OK) + { + return result; + } + + if (overflowBytes <= 8) + { + // Loop points can use already allocated data + newsample->mLoopPointDataEnd = newsample->mLoopPointDataEndMemory; + } + else + { + // Loop point data is large so allocate now + newsample->mLoopPointDataEnd = (char *)FMOD_Memory_Calloc(overflowBytes); + if (newsample->mLoopPointDataEnd = NULL) + { + return FMOD_ERR_MEMORY; + } + } + } + + // Only allocate sample memory if needed + if (mode & FMOD_OPENMEMORY_POINT) + { + newsample->mBufferMemory = NULL; + newsample->mBuffer = NULL; + } + else + { + // Allocate enough memory for the sample, overflow at either end and 16 byte alignment + newsample->mBufferMemory = FMOD_Memory_Calloc(newsample->mLengthBytes + (overflowBytes * 2) + 16); + if (!newsample->mBufferMemory) + { + return FMOD_ERR_MEMORY; + } + + // Move the memory pointer past the the front overflow and align to 16 byte boundry + newsample->mBuffer = (char *)(((unsigned int)newsample->mBufferMemory + overflowBytes + 15) & ~15); + } + + newsample->mFormat = waveformat->format; + newsample->mLength = waveformat->lengthpcm; + *sample = newsample; + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::createSample", "done\n")); + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Pass the reverb properties through to the current reverb version + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::setReverbProperties(const FMOD_REVERB_PROPERTIES *prop) +{ + FMOD_RESULT result = FMOD_OK; + + #if defined(FMOD_SUPPORT_EAX) + switch (mReverbVersion) + { + case REVERB_VERSION_EAX5: + { + result = setPropertiesEAX5(prop); + break; + } + case REVERB_VERSION_EAX4: + { + result = setPropertiesEAX4(prop); + break; + } + case REVERB_VERSION_EAX3: + { + result = setPropertiesEAX3(prop); + break; + } + case REVERB_VERSION_EAX2: + { + result = setPropertiesEAX2(prop); + break; + } + } + #endif + + return result; +} + + +/* +[ + [DESCRIPTION] + Get the maximum number of channels a sample can have + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +int OutputOpenAL::getSampleMaxChannels(FMOD_MODE mode, FMOD_SOUND_FORMAT format) +{ + return 16; /* 2D and 3D voices can be panned without needing to split them up. Allow up to 16 channel wide. */ +} + + +/* +[ + [DESCRIPTION] + Get a free OpenALChannel from the channel pool + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + Each channel is associated with OpenAL sources, if not enough are available then no channel can be returned + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getFreeChannel(FMOD_MODE mode, ChannelReal **realchannel, int numchannels, int numsoundchannels, int *found) +{ + FMOD_RESULT result = FMOD_OK; + ChannelOpenAL *channel = NULL; + FMOD_SPEAKERMODE speakerMode = FMOD_SPEAKERMODE_STEREO; + int requiredSources = 2; + int sourcesFound = 0; + + // Try to get a free channel from the pool + result = Output::getFreeChannel(mode, realchannel, numchannels, numsoundchannels, found); + if (result != FMOD_OK) + { + return result; + } + + channel = SAFE_CAST(ChannelOpenAL, *realchannel); + + /* Maximum number of sources needed is equal to the speaker mode since + the DSP network will mix to that before it gets to the OpenALChannel */ + switch (mSystem->mSpeakerMode) + { + case FMOD_SPEAKERMODE_MONO : requiredSources = 1; + break; + case FMOD_SPEAKERMODE_STEREO : requiredSources = 2; + break; + case FMOD_SPEAKERMODE_QUAD : requiredSources = 4; + break; + case FMOD_SPEAKERMODE_5POINT1 : requiredSources = 6; + break; + case FMOD_SPEAKERMODE_7POINT1 : requiredSources = 8; + break; + default : requiredSources = 2; + break; + } + + /* If the number of channels in the source is less than the speaker mode + then we only need enough sources for the sound source */ + if (numsoundchannels < requiredSources) + { + requiredSources = numsoundchannels; + } + + // Find enough sources for the number of sound channels required + for (int i = 0; i < mNumSources; i++) + { + if (!mSources[i].used) + { + // Check if we have found enough sources + if (++sourcesFound == requiredSources) + { + break; + } + } + } + + // Check if there aren't enough source + if (sourcesFound != requiredSources) + { + // Remove any real channels that were retrieved from "OutputPolled::getFreeChannel" + while ((*found)--) + { + if (realchannel[*found]) + { + // Remove the flags set by the channel real base code + realchannel[*found]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED; + realchannel[*found]->mFlags &= ~CHANNELREAL_FLAG_IN_USE; + realchannel[*found]->mFlags |= CHANNELREAL_FLAG_STOPPED; + realchannel[*found] = NULL; + } + } + + return FMOD_ERR_CHANNEL_ALLOC; + } + + return FMOD_OK; +} + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->getDriverName(id, name, namelen); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getDriverCapsExCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->getDriverCapsEx(id, caps, minfrequency, maxfrequency, controlpanelspeakermode, num2dchannels, num3dchannels, totalchannels); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::startCallback(FMOD_OUTPUT_STATE *output) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->start(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::stopCallback(FMOD_OUTPUT_STATE *output) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::updateCallback(FMOD_OUTPUT_STATE *output) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->update(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->getHandle(handle); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::getPositionCallback(FMOD_OUTPUT_STATE *output, unsigned int *pcm) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->getPosition(pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::lockCallback(FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->lock(offset, length, ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::unlockCallback(FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->unlock(ptr1, ptr2, len1, len2); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::createSampleCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->createSample(mode, waveformat, sample); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::setReverbPropertiesCallback(FMOD_OUTPUT_STATE *output, const FMOD_REVERB_PROPERTIES *prop) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->setReverbProperties(prop); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +int F_CALLBACK OutputOpenAL::getSampleMaxChannelsCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->getSampleMaxChannels(mode, format); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputOpenAL::postMixCallback(FMOD_OUTPUT_STATE *output) +{ + OutputOpenAL *openal = (OutputOpenAL *)output; + + return openal->postMix(); +} + +} /* namespace FMOD */ + +#endif /* #ifdef FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_output_openal.h b/win32/src/fmod_output_openal.h new file mode 100755 index 0000000..3da5355 --- /dev/null +++ b/win32/src/fmod_output_openal.h @@ -0,0 +1,132 @@ +#ifndef _FMOD_OUTPUT_OPENAL_H +#define _FMOD_OUTPUT_OPENAL_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL + +#include "fmod_systemi.h" +#include "fmod_output_timer.h" +#include "fmod_output_dsound.h" + +#include "../../lib/openal/utils/LoadOAL.h" + +namespace FMOD +{ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + + typedef ALboolean (*EAXSetBufferMode)(ALsizei n, ALuint *buffers, ALint value); + typedef ALenum (*EAXGetBufferMode)(ALuint buffer, ALint *value); + + typedef struct + { + bool used; + ALuint sid; + ALuint *bid; + } SourceOpenAL; + + class OutputOpenAL : public OutputTimer + { + friend class SampleOpenAL; + friend class ChannelOpenAL; + friend class ChannelOpenALEAX2; + friend class ChannelOpenALEAX3; + friend class ChannelOpenALEAX4; + friend class ChannelOpenALEAX5; + + private: + + OPENALFNTABLE mOALFnTable; + bool mSetupOnce; + bool mInitialised; + bool mDLLInitialised; + + ALCdevice *mDevice; + ALCcontext *mContext; + int mNumDrivers; + char *mDriverNames[FMOD_OUTPUT_MAXDRIVERS]; + int mNumBuffers; + unsigned int mBufferLength; + unsigned int mBufferLengthBytes; + unsigned int mPcmBase; + char *mBufferData; + int mNumChannels; + ChannelOpenAL *mChannels; + int mNumSources; + SourceOpenAL *mSources; + unsigned int mMixerSourceOffset; + + int mRate; + FMOD_SOUND_FORMAT mFormat; + ALuint mFormatOAL; + int mNumHwChannels; + int mOutputChannels; + REVERB_VERSION mReverbVersion; + bool mMixerReverbDisabled; + + EAXSet mEAXSet; + EAXGet mEAXGet; + EAXSetBufferMode mEAXSetBufferMode; + EAXGetBufferMode mEAXGetBufferMode; + + // Private methods + FMOD_RESULT registerDLL (); + FMOD_RESULT enumerate (); + FMOD_RESULT initEAX (); + FMOD_RESULT setupSessionEAX5 (); + FMOD_RESULT getSpeakerModeEAX5 (FMOD_SPEAKERMODE *speakerMode); + FMOD_RESULT lockSlotEAX4 (GUID &slotGUID); + FMOD_RESULT lockSlotEAX5 (GUID &slotGUID); + FMOD_RESULT setPropertiesEAX2 (const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT setPropertiesEAX3 (const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT setPropertiesEAX4 (const FMOD_REVERB_PROPERTIES *prop); + FMOD_RESULT setPropertiesEAX5 (const FMOD_REVERB_PROPERTIES *prop); + + public: + + OutputOpenAL(); + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + // Public methods + FMOD_RESULT getFreeChannel (FMOD_MODE mode, ChannelReal **realchannel, int numchannels, int numsoundchannels, int *found); + FMOD_RESULT getNumDrivers (int *numdrivers); + FMOD_RESULT getDriverName (int id, char *name, int namelen); + FMOD_RESULT getDriverCapsEx (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels); + FMOD_RESULT init (int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close (); + FMOD_RESULT start (); + FMOD_RESULT stop (); + FMOD_RESULT getHandle (void **handle); + FMOD_RESULT update (); + FMOD_RESULT postMix (); + FMOD_RESULT getPosition (unsigned int *pcm); + FMOD_RESULT lock (unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlock (void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + FMOD_RESULT createSample (FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + FMOD_RESULT setReverbProperties (const FMOD_REVERB_PROPERTIES *prop); + int getSampleMaxChannels (FMOD_MODE mode, FMOD_SOUND_FORMAT format); + + // Public callbacks + static FMOD_RESULT F_CALLBACK getNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverNameCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen); + static FMOD_RESULT F_CALLBACK getDriverCapsExCallback (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK startCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK stopCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + static FMOD_RESULT F_CALLBACK updateCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK postMixCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getPositionCallback (FMOD_OUTPUT_STATE *output, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK lockCallback (FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + static FMOD_RESULT F_CALLBACK unlockCallback (FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + static FMOD_RESULT F_CALLBACK createSampleCallback (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample); + static FMOD_RESULT F_CALLBACK setReverbPropertiesCallback (FMOD_OUTPUT_STATE *output, const FMOD_REVERB_PROPERTIES *prop); + static int F_CALLBACK getSampleMaxChannelsCallback (FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format); + }; +} + +#endif /* #ifdef FMOD_SUPPORT_OPENAL */ + +#endif /* #ifndef _FMOD_OUTPUT_OPENAL_H */ \ No newline at end of file diff --git a/win32/src/fmod_output_openal_eax2.cpp b/win32/src/fmod_output_openal_eax2.cpp new file mode 100755 index 0000000..91db828 --- /dev/null +++ b/win32/src/fmod_output_openal_eax2.cpp @@ -0,0 +1,95 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_openal.h" +#include "fmod_eax2.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + Set properties for EAX2 + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::setPropertiesEAX2(const FMOD_REVERB_PROPERTIES *prop) +{ + GUID bufferGUID; + GUID listenerGUID; + EAXLISTENERPROPERTIES eaxProperties; + int room; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX2", "Setting reverb properties\n")); + + /* + Make sure all reverb effects are disabled on the mixer sources + */ + if (!mMixerReverbDisabled) + { + room = -10000; + FMOD_memcpy(&bufferGUID, &FMOD_DSPROPSETID_EAX20_BufferProperties, sizeof(GUID)); + + // Only the first mixer source is technically used (multichannel source), but set all anyway + for (int i = mMixerSourceOffset; i < mNumSources; i++) + { + mEAXSet(&bufferGUID, DSPROPERTY_EAXBUFFER_ROOM, mSources[i].sid, &room, sizeof(int)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_INTERNAL; + } + } + + mMixerReverbDisabled = true; + } + + FMOD_memcpy(&listenerGUID, &FMOD_DSPROPSETID_EAX20_ListenerProperties, sizeof(GUID)); + + eaxProperties.flEnvironmentSize = prop->EnvSize; + eaxProperties.dwEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + eaxProperties.flEnvironmentDiffusion = prop->EnvDiffusion; + eaxProperties.lRoom = prop->Room; + eaxProperties.lRoomHF = prop->RoomHF; + eaxProperties.flDecayTime = prop->DecayTime; + eaxProperties.flDecayHFRatio = prop->DecayHFRatio; + eaxProperties.lReflections = prop->Reflections; + eaxProperties.flReflectionsDelay = prop->ReflectionsDelay; + eaxProperties.lReverb = prop->Reverb; + eaxProperties.flReverbDelay = prop->ReverbDelay; + eaxProperties.flAirAbsorptionHF = prop->AirAbsorptionHF; + eaxProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxProperties.dwFlags = prop->Flags & 0xFF; // Mask out FMOD specific flags, leaving only EAX flags + + mEAXSet(&listenerGUID, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, &eaxProperties, sizeof(EAXLISTENERPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX2", "Error setting reverb properties\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + +} /* namespace FMOD */ + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_output_openal_eax3.cpp b/win32/src/fmod_output_openal_eax3.cpp new file mode 100755 index 0000000..497be52 --- /dev/null +++ b/win32/src/fmod_output_openal_eax3.cpp @@ -0,0 +1,109 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_openal.h" +#include "fmod_eax3.h" + +namespace FMOD +{ + +/* +[ + [DESCRIPTION] + Set properties for EAX3 + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::setPropertiesEAX3(const FMOD_REVERB_PROPERTIES *prop) +{ + GUID bufferGUID; + GUID listenerGUID; + EAXLISTENERPROPERTIES eaxProperties; + int room; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX3", "Setting reverb properties\n")); + + /* + Make sure all reverb effects are disabled on the mixer sources + */ + if (!mMixerReverbDisabled) + { + room = -10000; + FMOD_memcpy(&bufferGUID, &FMOD_DSPROPSETID_EAX30_BufferProperties, sizeof(GUID)); + + // Only the first mixer source is technically used (multichannel source), but set all anyway + for (int i = mMixerSourceOffset; i < mNumSources; i++) + { + mEAXSet(&bufferGUID, DSPROPERTY_EAXBUFFER_ROOM, mSources[i].sid, &room, sizeof(int)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_INTERNAL; + } + } + + mMixerReverbDisabled = true; + } + + FMOD_memcpy(&listenerGUID, &FMOD_DSPROPSETID_EAX30_ListenerProperties, sizeof(GUID)); + + eaxProperties.ulEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + eaxProperties.flEnvironmentSize = prop->EnvSize; + eaxProperties.flEnvironmentDiffusion = prop->EnvDiffusion; + eaxProperties.lRoom = prop->Room; + eaxProperties.lRoomHF = prop->RoomHF; + eaxProperties.lRoomLF = prop->RoomLF; + eaxProperties.flDecayTime = prop->DecayTime; + eaxProperties.flDecayHFRatio = prop->DecayHFRatio; + eaxProperties.flDecayLFRatio = prop->DecayLFRatio; + eaxProperties.lReflections = prop->Reflections; + eaxProperties.flReflectionsDelay = prop->ReflectionsDelay; + eaxProperties.vReflectionsPan.x = prop->ReflectionsPan[0]; + eaxProperties.vReflectionsPan.y = prop->ReflectionsPan[1]; + eaxProperties.vReflectionsPan.z = prop->ReflectionsPan[2]; + eaxProperties.lReverb = prop->Reverb; + eaxProperties.flReverbDelay = prop->ReverbDelay; + eaxProperties.vReverbPan.x = prop->ReverbPan[0]; + eaxProperties.vReverbPan.y = prop->ReverbPan[1]; + eaxProperties.vReverbPan.z = prop->ReverbPan[2]; + eaxProperties.flEchoTime = prop->EchoTime; + eaxProperties.flEchoDepth = prop->EchoDepth; + eaxProperties.flModulationTime = prop->ModulationTime; + eaxProperties.flModulationDepth = prop->ModulationDepth; + eaxProperties.flAirAbsorptionHF = prop->AirAbsorptionHF; + eaxProperties.flHFReference = prop->HFReference; + eaxProperties.flLFReference = prop->LFReference; + eaxProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxProperties.ulFlags = prop->Flags & 0xFF; // Mask out FMOD specific flags, leaving only EAX flags + + mEAXSet(&listenerGUID, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, &eaxProperties, sizeof(EAXLISTENERPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX3", "Error setting reverb properties\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + +} /* namespace FMOD */ + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_output_openal_eax4.cpp b/win32/src/fmod_output_openal_eax4.cpp new file mode 100755 index 0000000..e8ef5c0 --- /dev/null +++ b/win32/src/fmod_output_openal_eax4.cpp @@ -0,0 +1,225 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_openal.h" +#include "fmod_eax4.h" + +namespace FMOD +{ + +bool gEAX4LockedOALFxSlot2 = false; +bool gEAX4LockedOALFxSlot3 = false; + +/* +[ + [DESCRIPTION] + Attempt to lock an EAX slot + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::lockSlotEAX4(GUID &slotGUID) +{ + int slotIsLocked; + EAXFXSLOTPROPERTIES fxSlot; + + FMOD_memcpy(&fxSlot.guidLoadEffect, &FMOD_EAX_REVERB_EFFECT, sizeof(GUID)); + + /* + Check if this slot is locked + */ + mEAXGet(&slotGUID, EAXFXSLOT_LOCK, 0, &slotIsLocked, sizeof(int)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (slotIsLocked) + { + GUID effect; + + // Find out what type of effect is in the locked slot + mEAXGet(&slotGUID, EAXFXSLOT_LOADEFFECT, 0, &effect, sizeof(GUID)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + // Check if the slot contains a reverb, if so we can still use that slot + if (memcmp(&effect, &fxSlot.guidLoadEffect, sizeof(GUID))) + { + return FMOD_ERR_REVERB_INSTANCE; + } + } + else // Slot isn't locked + { + fxSlot.lLock = EAXFXSLOT_LOCKED; + fxSlot.lVolume = EAXFXSLOT_DEFAULTVOLUME; + fxSlot.ulFlags = EAXFXSLOT_DEFAULTFLAGS; + + // Lock the slot + mEAXSet(&slotGUID, EAXFXSLOT_ALLPARAMETERS, 0, &fxSlot, sizeof(EAXFXSLOTPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_REVERB_INSTANCE; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Set properties for EAX4 + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + With EAX4 the first slot is locked as reverb so we can still use it, + the second slot is locked as chorus, so it is no use to us, slots + 3 and 4 are free to be tasked as reverb + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::setPropertiesEAX4(const FMOD_REVERB_PROPERTIES *prop) +{ + GUID slotGUID; + GUID sourceGUID; + EAXREVERBPROPERTIES eaxProperties; + FMOD_RESULT result; + int room; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX4", "Setting reverb properties for instance: %d\n", prop->Instance)); + + /* + Make sure all reverb effects are disabled on the mixer sources + */ + if (!mMixerReverbDisabled) + { + room = -10000; + FMOD_memcpy(&sourceGUID, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID)); + + // Only the first mixer source is technically used (multichannel source), but set all anyway + for (int i = mMixerSourceOffset; i < mNumSources; i++) + { + mEAXSet(&sourceGUID, EAXSOURCE_ROOM, mSources[i].sid, &room, sizeof(int)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_INTERNAL; + } + } + + mMixerReverbDisabled = true; + } + + /* + Each reverb instance is equivalent to a hardware effect slot, + attempt to lock that slot for use by FMOD + */ + switch (prop->Instance) + { + case 1: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID)); + if (!gEAX4LockedOALFxSlot2) + { + result = lockSlotEAX4(slotGUID); + if (result != FMOD_OK) + { + return result; + } + + gEAX4LockedOALFxSlot2 = true; + } + break; + } + case 2: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID)); + if (!gEAX4LockedOALFxSlot3) + { + result = lockSlotEAX4(slotGUID); + if (result != FMOD_OK) + { + return result; + } + + gEAX4LockedOALFxSlot3 = true; + } + break; + } + default: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID)); + } + } + + eaxProperties.ulEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + eaxProperties.flEnvironmentSize = prop->EnvSize; + eaxProperties.flEnvironmentDiffusion = prop->EnvDiffusion; + eaxProperties.lRoom = prop->Room; + eaxProperties.lRoomHF = prop->RoomHF; + eaxProperties.lRoomLF = prop->RoomLF; + eaxProperties.flDecayTime = prop->DecayTime; + eaxProperties.flDecayHFRatio = prop->DecayHFRatio; + eaxProperties.flDecayLFRatio = prop->DecayLFRatio; + eaxProperties.lReflections = prop->Reflections; + eaxProperties.flReflectionsDelay = prop->ReflectionsDelay; + eaxProperties.vReflectionsPan.x = prop->ReflectionsPan[0]; + eaxProperties.vReflectionsPan.y = prop->ReflectionsPan[1]; + eaxProperties.vReflectionsPan.z = prop->ReflectionsPan[2]; + eaxProperties.lReverb = prop->Reverb; + eaxProperties.flReverbDelay = prop->ReverbDelay; + eaxProperties.vReverbPan.x = prop->ReverbPan[0]; + eaxProperties.vReverbPan.y = prop->ReverbPan[1]; + eaxProperties.vReverbPan.z = prop->ReverbPan[2]; + eaxProperties.flEchoTime = prop->EchoTime; + eaxProperties.flEchoDepth = prop->EchoDepth; + eaxProperties.flModulationTime = prop->ModulationTime; + eaxProperties.flModulationDepth = prop->ModulationDepth; + eaxProperties.flAirAbsorptionHF = prop->AirAbsorptionHF; + eaxProperties.flHFReference = prop->HFReference; + eaxProperties.flLFReference = prop->LFReference; + eaxProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxProperties.ulFlags = prop->Flags & 0xFF; // Mask out FMOD specific flags, leaving only EAX flags + + mEAXSet(&slotGUID, EAXREVERB_ALLPARAMETERS, 0, &eaxProperties, sizeof(EAXREVERBPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX4", "Error setting reverb properties\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + +} /* namespace FMOD */ + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_output_openal_eax5.cpp b/win32/src/fmod_output_openal_eax5.cpp new file mode 100755 index 0000000..d85f467 --- /dev/null +++ b/win32/src/fmod_output_openal_eax5.cpp @@ -0,0 +1,386 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL +#ifdef FMOD_SUPPORT_EAX + +#include "fmod_output_openal.h" +#include "fmod_eax5.h" + +namespace FMOD +{ + +bool gEAX5LockedOALFxSlot0 = false; +bool gEAX5LockedOALFxSlot1 = false; +bool gEAX5LockedOALFxSlot2 = false; +bool gEAX5LockedOALFxSlot3 = false; + +/* +[ + [DESCRIPTION] + Create an EAX5 session + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + A session must be created before using EAX5 + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::setupSessionEAX5() +{ + GUID guid; + EAXSESSIONPROPERTIES eaxSession; + + eaxSession.ulEAXVersion = EAX_50; + eaxSession.ulMaxActiveSends = 2; + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX50_Context, sizeof(GUID)); + + mEAXSet(&guid, EAXCONTEXT_EAXSESSION, 0, &eaxSession, sizeof(EAXSESSIONPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_INTERNAL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Get the current speaker mode + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This is called from getCaps, before the main device has been initialised + so we need to do EAX5 specific initialisation here for the temporary + device + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::getSpeakerModeEAX5(FMOD_SPEAKERMODE *speakerMode) +{ + EAXGet eaxGet = NULL; + EAXSet eaxSet = NULL; + long oalSpeakerMode = 0; + GUID guid; + EAXSESSIONPROPERTIES eaxSession; + + /* + Get the EAX function pointers + */ + eaxGet = (EAXGet)mOALFnTable.alGetProcAddress("EAXGet"); + eaxSet = (EAXSet)mOALFnTable.alGetProcAddress("EAXSet"); + if (!eaxGet || !eaxSet) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + /* + Create an EAX5 session here for the getCaps temporary OAL context + */ + eaxSession.ulEAXVersion = EAX_50; + eaxSession.ulMaxActiveSends = 2; + FMOD_memcpy(&guid, &FMOD_EAXPROPERTYID_EAX50_Context, sizeof(GUID)); + + eaxSet(&guid, EAXCONTEXT_EAXSESSION, 0, &eaxSession, sizeof(EAXSESSIONPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + /* + Determine the speaker mode + */ + eaxGet(&guid, EAXCONTEXT_SPEAKERCONFIG, 0, &oalSpeakerMode, sizeof(long)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + switch (oalSpeakerMode) + { + case HEADPHONES: + case SPEAKERS_2: + { + *speakerMode = FMOD_SPEAKERMODE_STEREO; + break; + } + case SPEAKERS_4: + { + *speakerMode = FMOD_SPEAKERMODE_QUAD; + break; + } + case SPEAKERS_5: + { + *speakerMode = FMOD_SPEAKERMODE_5POINT1; + break; + } + case SPEAKERS_6: + { + *speakerMode = FMOD_SPEAKERMODE_RAW; + break; + } + case SPEAKERS_7: + { + *speakerMode = FMOD_SPEAKERMODE_7POINT1; + break; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Attempt to lock an EAX slot + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::lockSlotEAX5(GUID &slotGUID) +{ + int slotIsLocked; + EAXFXSLOTPROPERTIES fxSlot; + + FMOD_memcpy(&fxSlot.guidLoadEffect, &FMOD_EAX_REVERB_EFFECT, sizeof(GUID)); + + /* + Check if this slot is locked + */ + mEAXGet(&slotGUID, EAXFXSLOT_LOCK, 0, &slotIsLocked, sizeof(int)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + if (slotIsLocked) + { + GUID effect; + + // Find out what type of effect is in the locked slot + mEAXGet(&slotGUID, EAXFXSLOT_LOADEFFECT, 0, &effect, sizeof(GUID)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_REVERB_INSTANCE; + } + + // Check if the slot contains a reverb, if so we can still use that slot + if (memcmp(&effect, &fxSlot.guidLoadEffect, sizeof(GUID))) + { + return FMOD_ERR_REVERB_INSTANCE; + } + } + else // Slot isn't locked + { + fxSlot.lLock = EAXFXSLOT_LOCKED; + fxSlot.lVolume = EAXFXSLOT_DEFAULTVOLUME; + fxSlot.ulFlags = EAXFXSLOT_DEFAULTFLAGS; + fxSlot.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION; + fxSlot.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO; + + // Lock the slot + mEAXSet(&slotGUID, EAXFXSLOT_ALLPARAMETERS, 0, &fxSlot, sizeof(EAXFXSLOTPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_REVERB_INSTANCE; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Set properties for EAX5 + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + With EAX5 all four slots can be locked with reverb + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputOpenAL::setPropertiesEAX5(const FMOD_REVERB_PROPERTIES *prop) +{ + GUID slotGUID; + GUID sourceGUID; + EAXREVERBPROPERTIES eaxProperties; + FMOD_RESULT result; + int room; + + if (!prop) + { + return FMOD_ERR_INVALID_PARAM; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX5", "Setting reverb properties for instance: %d\n", prop->Instance)); + + /* + Make sure all reverb effects are disabled on the mixer sources + */ + if (!mMixerReverbDisabled) + { + room = -10000; + FMOD_memcpy(&sourceGUID, &FMOD_EAXPROPERTYID_EAX50_Source, sizeof(GUID)); + + // Only the first mixer source is technically used (multichannel source), but set all anyway + for (int i = mMixerSourceOffset; i < mNumSources; i++) + { + mEAXSet(&sourceGUID, EAXSOURCE_ROOM, mSources[i].sid, &room, sizeof(int)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + return FMOD_ERR_INTERNAL; + } + } + + mMixerReverbDisabled = true; + } + + /* + Each reverb instance is equivalent to a hardware effect slot, + attempt to lock that slot for use by FMOD + */ + switch (prop->Instance) + { + case 1: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX50_FXSlot1, sizeof(GUID)); + if (!gEAX5LockedOALFxSlot1) + { + result = lockSlotEAX5(slotGUID); + if (result != FMOD_OK) + { + return result; + } + + gEAX5LockedOALFxSlot1 = true; + } + break; + } + case 2: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX50_FXSlot2, sizeof(GUID)); + if (!gEAX5LockedOALFxSlot2) + { + result = lockSlotEAX5(slotGUID); + if (result != FMOD_OK) + { + return result; + } + + gEAX5LockedOALFxSlot2 = true; + } + break; + } + case 3: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX50_FXSlot3, sizeof(GUID)); + if (!gEAX5LockedOALFxSlot3) + { + result = lockSlotEAX5(slotGUID); + if (result != FMOD_OK) + { + return result; + } + + gEAX5LockedOALFxSlot3 = true; + } + break; + } + default: + { + FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX50_FXSlot0, sizeof(GUID)); + if (!gEAX5LockedOALFxSlot0) + { + result = lockSlotEAX5(slotGUID); + if (result != FMOD_OK) + { + return result; + } + + gEAX5LockedOALFxSlot0 = true; + } + } + } + + FMOD_memset(&eaxProperties, 0, sizeof(EAXREVERBPROPERTIES)); + eaxProperties.ulEnvironment = prop->Environment < 0 ? 0 : prop->Environment; + eaxProperties.flEnvironmentSize = prop->EnvSize; + eaxProperties.flEnvironmentDiffusion = prop->EnvDiffusion; + eaxProperties.lRoom = prop->Room; + eaxProperties.lRoomHF = prop->RoomHF; + eaxProperties.lRoomLF = prop->RoomLF; + eaxProperties.flDecayTime = prop->DecayTime; + eaxProperties.flDecayHFRatio = prop->DecayHFRatio; + eaxProperties.flDecayLFRatio = prop->DecayLFRatio; + eaxProperties.lReflections = prop->Reflections; + eaxProperties.flReflectionsDelay = prop->ReflectionsDelay; + eaxProperties.vReflectionsPan.x = prop->ReflectionsPan[0]; + eaxProperties.vReflectionsPan.y = prop->ReflectionsPan[1]; + eaxProperties.vReflectionsPan.z = prop->ReflectionsPan[2]; + eaxProperties.lReverb = prop->Reverb; + eaxProperties.flReverbDelay = prop->ReverbDelay; + eaxProperties.vReverbPan.x = prop->ReverbPan[0]; + eaxProperties.vReverbPan.y = prop->ReverbPan[1]; + eaxProperties.vReverbPan.z = prop->ReverbPan[2]; + eaxProperties.flEchoTime = prop->EchoTime; + eaxProperties.flEchoDepth = prop->EchoDepth; + eaxProperties.flModulationTime = prop->ModulationTime; + eaxProperties.flModulationDepth = prop->ModulationDepth; + eaxProperties.flAirAbsorptionHF = prop->AirAbsorptionHF; + eaxProperties.flHFReference = prop->HFReference; + eaxProperties.flLFReference = prop->LFReference; + eaxProperties.flRoomRolloffFactor = prop->RoomRolloffFactor; + eaxProperties.ulFlags = prop->Flags & 0xFF; // Mask out FMOD specific flags, leaving only EAX flags + + mEAXSet(&slotGUID, EAXREVERB_ALLPARAMETERS, 0, &eaxProperties, sizeof(EAXREVERBPROPERTIES)); + if (mOALFnTable.alGetError() != AL_NO_ERROR) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX5", "Error setting reverb properties\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + +} /* namespace FMOD */ + +#endif /* FMOD_SUPPORT_EAX */ +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_output_timer.cpp b/win32/src/fmod_output_timer.cpp new file mode 100755 index 0000000..d29dd9c --- /dev/null +++ b/win32/src/fmod_output_timer.cpp @@ -0,0 +1,329 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_SOFTWARE + +#include "fmod_output_timer.h" +#include "fmod_systemi.h" + + +// #define DUMPMIXERTODISK + +#ifdef DUMPMIXERTODISK + +#include <stdio.h> +static FILE *fp = 0; + +#endif + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputTimer::timerFunc() +{ + FMOD_RESULT result = FMOD_OK; + unsigned int playPosition = 0; + unsigned int blockSize = 0; + int numBlocks = 0; + FMOD_SOUND_FORMAT outputFormat = FMOD_SOUND_FORMAT_NONE; + int outputChannels = 0; + + // Playing has stopped + if (!mPlaying) + { + return FMOD_OK; + } + + // Attempt to apply a pro-audio thread characteristic to reduce stuttering + if (!mThreadElevated) + { + HANDLE (WINAPI *SetThreadTask)(LPCTSTR, LPDWORD); + FMOD_OS_LIBRARY *avrtHandle = NULL; + DWORD taskIndex = 0; + + result = FMOD_OS_Library_Load("avrt.dll", &avrtHandle); + if (result == FMOD_OK) // If this fails, OS does not support setting thread characteristics + { + result = FMOD_OS_Library_GetProcAddress(avrtHandle, "AvSetMmThreadCharacteristicsA", (void **)&SetThreadTask); + if (result == FMOD_OK) + { + SetThreadTask("Pro Audio", &taskIndex); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputTimer::timerFunc", "Mixer thread set to 'Pro Audio' characteristic\n")); + FMOD_OS_Library_Free(avrtHandle); + } + } + + mThreadElevated = true; // This will prevent reattempting to elevate if already elevated and if the OS doesn't support it + } + + mSystem->mDSPTimeStamp.stampIn(); + + result = mSystem->getDSPBufferSize(&blockSize, &numBlocks); + if (result != FMOD_OK) + { + return result; + } + + result = mSystem->getSoftwareFormat(0, &outputFormat, &outputChannels, 0, 0, 0); + if (result != FMOD_OK) + { + return result; + } + + if (mDescription.getposition) + { + result = mDescription.getposition(this, &playPosition); + if (result != FMOD_OK) + { + return result; + } + } + + // Determine which block is currently playing + playPosition /= blockSize; + playPosition %= numBlocks; + + FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "OutputTimer::timerFunc", "Play block %8d, Fill block %8d\n", playPosition, mFillBlock)); + + while (mFillBlock != (int)playPosition) + { + void *ptr1 = NULL; + void *ptr2 = NULL; + unsigned int len1 = 0; + unsigned int len2 = 0; + unsigned int writePositionBytes = 0; + unsigned int blockSizeBytes = 0; + unsigned int samplesLocked = 0; + + result = SoundI::getBytesFromSamples(blockSize, &blockSizeBytes, outputChannels, outputFormat); + if (result != FMOD_OK) + { + return result; + } + result = SoundI::getBytesFromSamples(mFillBlock * blockSize, &writePositionBytes, outputChannels, outputFormat); + if (result != FMOD_OK) + { + return result; + } + + if (mDescription.lock) + { + result = mDescription.lock(this, writePositionBytes, blockSizeBytes, &ptr1, &ptr2, &len1, &len2); + if (result != FMOD_OK) + { + return result; + } + } + + result = SoundI::getSamplesFromBytes(len1, &samplesLocked, outputChannels, outputFormat); + if (result != FMOD_OK) + { + return result; + } + + result = mix(ptr1, samplesLocked); + if (result != FMOD_OK) + { + return result; + } + +#ifdef DUMPMIXERTODISK + if (fp) + { + fwrite(ptr1, len1, 1, fp); + } +#endif + + /* + ptr2 and len2 should never be non 0. All updates are block aligned. + */ + if (mDescription.unlock) + { + result = mDescription.unlock(this, ptr1, ptr2, len1, len2); + if (result != FMOD_OK) + { + return result; + } + } + + mFillBlock++; + if (mFillBlock >= numBlocks) + { + mFillBlock = 0; + } + } + + mSystem->mDSPTimeStamp.stampOut(95); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputTimer::start() +{ + FMOD_RESULT result = FMOD_OK; + unsigned int blockSize = 0; + int sampleRate = 0; + + /* + Calculate the callback time for the mixer + */ + result = mSystem->getDSPBufferSize(&blockSize, NULL); + if (result != FMOD_OK) + { + return result; + } + + result = mSystem->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL); + if (result != FMOD_OK) + { + return result; + } + + mMixerTimerPeriod = (float)blockSize * 1000.0f / (float)sampleRate; + if (mMixerTimerPeriod < 20) + { + mMixerTimerPeriod /= 3; + if (mMixerTimerPeriod < 1) + { + mMixerTimerPeriod = 1; + } + } + else + { + mMixerTimerPeriod = 10; + } + + FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "OutputTimer::start", "Starting timer callback that triggers every %d ms\n", (int)mMixerTimerPeriod)); + mPlaying = true; + + // Mixer update callback critical section + result = FMOD_OS_CriticalSection_Create(&mMixerCrit); + if (result != FMOD_OK) + { + return result; + } + + // Setup the timed callback event + mThreadElevated = false; + mMixerTimerID = timeSetEvent((unsigned int)mMixerTimerPeriod, 0, (LPTIMECALLBACK)timerFuncCallback, (UINT_PTR)this, TIME_PERIODIC); + if (!mMixerTimerID) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputTimer::start", "timeSetEvent failed! Something is really wrong with your PC. timer period = %d ms\n", (int)mMixerTimerPeriod)); + return FMOD_ERR_OUTPUT_INIT; + } + +#ifdef DUMPMIXERTODISK + fp = fopen("/media/fmod4output.raw", "wb"); +#endif + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputTimer::stop() +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + + // Stop mixer callbacks + timeKillEvent(mMixerTimerID); + mMixerTimerID = 0; + + // If timer still ticks a few times ensure the callback will do nothing + mPlaying = false; + + // Ensure that the mixer is finish before returning (wont re-enter because of mPlaying) + FMOD_OS_CriticalSection_Enter(mMixerCrit); + +#ifdef DUMPMIXERTODISK + if (fp) + { + fclose(fp); + fp = 0; + } +#endif + + FMOD_OS_CriticalSection_Leave(mMixerCrit); + FMOD_OS_CriticalSection_Free(mMixerCrit); + mMixerCrit = NULL; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +void CALLBACK OutputTimer::timerFuncCallback(UINT uTimerID, UINT uMsg, UINT_PTR dwUser, UINT_PTR dw1, UINT_PTR dw2) +{ + OutputTimer *outputTimer = (OutputTimer *)dwUser; + + FMOD_OS_CriticalSection_Enter(outputTimer->mMixerCrit); + outputTimer->timerFunc(); + FMOD_OS_CriticalSection_Leave(outputTimer->mMixerCrit); +} + +} + +#endif \ No newline at end of file diff --git a/win32/src/fmod_output_timer.h b/win32/src/fmod_output_timer.h new file mode 100755 index 0000000..8d515bc --- /dev/null +++ b/win32/src/fmod_output_timer.h @@ -0,0 +1,37 @@ +#ifndef _FMOD_OUTPUT_TIMER_H +#define _FMOD_OUTPUT_TIMER_H + +#include "fmod_settings.h" + +#include "fmod_outputi.h" +#include "fmod_codec_wav.h" + +namespace FMOD +{ + class OutputTimer : public Output + { + private: + + float mMixerTimerPeriod; + unsigned int mMixerTimerID; + bool mPlaying; + bool mThreadElevated; + FMOD_OS_CRITICALSECTION *mMixerCrit; + + FMOD_RESULT timerFunc(); + + static void CALLBACK timerFuncCallback(UINT uTimerID, UINT uMsg, UINT_PTR dwUser, UINT_PTR dw1, UINT_PTR dw2); + + protected: + + int mFillBlock; + + public: + + FMOD_RESULT start(); + FMOD_RESULT stop(); + }; +} + +#endif + diff --git a/win32/src/fmod_output_wasapi.cpp b/win32/src/fmod_output_wasapi.cpp new file mode 100755 index 0000000..9d44ee8 --- /dev/null +++ b/win32/src/fmod_output_wasapi.cpp @@ -0,0 +1,2646 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WASAPI + +#include "fmod_output_wasapi.h" +#include "fmod_codec_wav.h" +#include "fmod_downmix.h" +#include "fmod_localcriticalsection.h" + +namespace FMOD +{ + +#define RECORD_LENGTH_MS 100 +#define MFTIMES_PER_SEC 10000000 +#define MFTIMES_PER_MILLISEC 10000 +#define FEEDER_TIMEOUT_DEFAULT_MS 500 +#define FEEDER_TIMEOUT_PARTIAL_MS 3 + +#define EXIT_ON_CONDITION(condition, error) \ + if (condition) { fResult = (error); goto Exit; } +#define SAFE_RELEASE(pPointer) \ + if ((pPointer) != NULL) { (pPointer)->Release(); (pPointer) = NULL; } +#define SAFE_FREE(pPointer) \ + if ((pPointer) != NULL) { FMOD_Memory_Free((pPointer)); (pPointer) = NULL; } + +FMOD_OUTPUT_DESCRIPTION_EX wasapioutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. + This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputWASAPI::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + Assign methods for various FMOD output hooks required by output type + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OUTPUT_DESCRIPTION_EX + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputWASAPI::getDescriptionEx() +{ + FMOD_memset(&wasapioutput, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + wasapioutput.name = "FMOD WASAPI Output"; + wasapioutput.version = 0x00010100; + wasapioutput.mType = FMOD_OUTPUTTYPE_WASAPI; + wasapioutput.mSize = sizeof(OutputWASAPI); + wasapioutput.polling = false; + + wasapioutput.getnumdrivers = &OutputWASAPI::getNumDriversCallback; + wasapioutput.getdriverinfo = &OutputWASAPI::getDriverInfoCallback; + wasapioutput.getdriverinfow = &OutputWASAPI::getDriverInfoWCallback; + wasapioutput.getdrivercapsex = &OutputWASAPI::getDriverCapsExCallback; + wasapioutput.initex = &OutputWASAPI::initExCallback; + wasapioutput.close = &OutputWASAPI::closeCallback; + wasapioutput.start = &OutputWASAPI::startCallback; + wasapioutput.stop = &OutputWASAPI::stopCallback; + wasapioutput.gethandle = &OutputWASAPI::getHandleCallback; + +#ifdef FMOD_SUPPORT_RECORDING + wasapioutput.record_getnumdrivers = &OutputWASAPI::recordGetNumDriversCallback; + wasapioutput.record_getdriverinfo = &OutputWASAPI::recordGetDriverInfoCallback; + wasapioutput.record_getdriverinfow = &OutputWASAPI::recordGetDriverInfoWCallback; + wasapioutput.record_getdrivercaps = &OutputWASAPI::recordGetDriverCapsCallback; + wasapioutput.record_start = &OutputWASAPI::recordStartCallback; + wasapioutput.record_stop = &OutputWASAPI::recordStopCallback; + wasapioutput.record_getposition = &OutputWASAPI::recordGetPositionCallback; + wasapioutput.record_lock = &OutputWASAPI::recordLockCallback; +#endif + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + wasapioutput.getmemoryused = &OutputWASAPI::getMemoryUsedCallback; +#endif + + return &wasapioutput; +} + + +/* +[ + [DESCRIPTION] + Enumerate the default devices for element 0 of the lists + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::enumerateDefaultDevices(IMMDeviceEnumerator *pEnumerator) +{ + HRESULT hResult = S_OK; + FMOD_RESULT fResult = FMOD_OK; + IMMDevice *pDevice = NULL; + IPropertyStore *pProperties = NULL; + short deviceString[FMOD_STRING_MAXNAMELEN] = {0}; + PROPVARIANT propDesc; + PROPVARIANT propName; + + hResult = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult) && hResult != E_NOTFOUND, FMOD_ERR_OUTPUT_INIT); + + // Add the default output driver if available + if (hResult != E_NOTFOUND) + { + hResult = pDevice->OpenPropertyStore(STGM_READ, &pProperties); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + PropVariantInit(&propDesc); + PropVariantInit(&propName); + + // Get the description of the endpoint device, e.g. "Speakers" + hResult = pProperties->GetValue(PKEY_Device_DeviceDesc, &propDesc); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the name of the adapter the endpoint device is connected to, e.g. "XYZ Audio Adapter" + hResult = pProperties->GetValue(PKEY_DeviceInterface_FriendlyName, &propName); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Combine properties into an output string + _snwprintf((wchar_t *)deviceString, FMOD_STRING_MAXNAMELEN, L"%s (%s)", propDesc.pwszVal, propName.pwszVal); + + mRenderDrivers[0].name = (short *)FMOD_Memory_Calloc((FMOD_strlenW(deviceString) + 1) * sizeof(short)); + EXIT_ON_CONDITION(mRenderDrivers[0].name == NULL, FMOD_ERR_MEMORY); + FMOD_strncpyW(mRenderDrivers[0].name, deviceString, FMOD_strlenW(deviceString)); + + hResult = pDevice->GetId(&mRenderDrivers[0].id); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + mNumRenderDrivers++; + + // Cleanup + PropVariantClear(&propDesc); + PropVariantClear(&propName); + SAFE_RELEASE(pProperties); + SAFE_RELEASE(pDevice); + } + + hResult = pEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult) && hResult != E_NOTFOUND, FMOD_ERR_OUTPUT_INIT); + + // Add the default input driver if available + if (hResult != E_NOTFOUND) + { + hResult = pDevice->OpenPropertyStore(STGM_READ, &pProperties); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + PropVariantInit(&propDesc); + PropVariantInit(&propName); + + // Get the description of the endpoint device, e.g. "Speakers" + hResult = pProperties->GetValue(PKEY_Device_DeviceDesc, &propDesc); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the name of the adapter the endpoint device is connected to, e.g. "XYZ Audio Adapter" + hResult = pProperties->GetValue(PKEY_DeviceInterface_FriendlyName, &propName); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Combine properties into an output string + _snwprintf((wchar_t *)deviceString, FMOD_STRING_MAXNAMELEN, L"%s (%s)", propDesc.pwszVal, propName.pwszVal); + + mCaptureDrivers[0].name = (short *)FMOD_Memory_Calloc((FMOD_strlenW(deviceString) + 1) * sizeof(short)); + EXIT_ON_CONDITION(mCaptureDrivers[0].name == NULL, FMOD_ERR_MEMORY); + FMOD_strncpyW(mCaptureDrivers[0].name, deviceString, FMOD_strlenW(deviceString)); + + hResult = pDevice->GetId(&mCaptureDrivers[0].id); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + mNumCaptureDrivers++; + } + +Exit: + // Cleanup + PropVariantClear(&propDesc); + PropVariantClear(&propName); + SAFE_RELEASE(pProperties); + SAFE_RELEASE(pDevice); + + return fResult; +} + + +/* +[ + [DESCRIPTION] + Extract information about attached audio devices + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::enumerate() +{ + HRESULT hResult = S_OK; + FMOD_RESULT fResult = FMOD_OK; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDeviceCollection *pCollection = NULL; + IMMDevice *pDevice = NULL; + IPropertyStore *pProperties = NULL; + unsigned int numDevices = 0; + unsigned int deviceIndex = 0; + PROPVARIANT propDesc; + PROPVARIANT propName; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + if (mEnumerated) + { + return FMOD_OK; + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::enumerate", "Enumerating...\n")); + + if (!mCoInitialized) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::enumerate", "Initialise COM library.\n")); + + hResult = CoInitialize(NULL); + if (hResult == S_OK || hResult == S_FALSE) + { + mCoInitialized = true; + } + } + + cleanUpEnumeration(); + + // Get an enumerator to collect endpoint information + hResult = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_COM); + + // Get the default devices + fResult = enumerateDefaultDevices(pEnumerator); + EXIT_ON_CONDITION(fResult != FMOD_OK, fResult); + + // Get a collection of all endpoints available in the system + hResult = pEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &pCollection); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Retrieve the number of endpoints + hResult = pCollection->GetCount(&numDevices); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get details about each endpoint + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) + { + short deviceString[FMOD_STRING_MAXNAMELEN] = {0}; + IMMEndpoint *pEndPoint = NULL; + EDataFlow dataFlow = eAll; + + hResult = pCollection->Item(deviceIndex, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hResult = pDevice->OpenPropertyStore(STGM_READ, &pProperties); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + PropVariantInit(&propDesc); + PropVariantInit(&propName); + + // Get the description of the endpoint device, e.g. "Speakers" + hResult = pProperties->GetValue(PKEY_Device_DeviceDesc, &propDesc); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the name of the adapter the endpoint device is connected to, e.g. "XYZ Audio Adapter" + hResult = pProperties->GetValue(PKEY_DeviceInterface_FriendlyName, &propName); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Combine properties into an output string + _snwprintf((wchar_t *)deviceString, FMOD_STRING_MAXNAMELEN, L"%s (%s)", propDesc.pwszVal, propName.pwszVal); + + // Determine if the device in an input device or an output device + pDevice->QueryInterface(IID_IMMEndpoint, (void**)&pEndPoint); + pEndPoint->GetDataFlow(&dataFlow); + + // Add to the outputs list (ensure there is still room in the list and this is not the default device already listed) + if ((dataFlow == eRender || dataFlow == eAll) && (mNumRenderDrivers < FMOD_OUTPUT_MAXDRIVERS) && (!mRenderDrivers[0].name || (FMOD_strcmpW(deviceString, mRenderDrivers[0].name) != 0))) + { + mRenderDrivers[mNumRenderDrivers].name = (short *)FMOD_Memory_Calloc((FMOD_strlenW(deviceString) + 1) * sizeof(short)); + EXIT_ON_CONDITION(mRenderDrivers[mNumRenderDrivers].name == NULL, FMOD_ERR_MEMORY); + FMOD_strncpyW(mRenderDrivers[mNumRenderDrivers].name, deviceString, FMOD_strlenW(deviceString)); + + hResult = pDevice->GetId(&mRenderDrivers[mNumRenderDrivers].id); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + mNumRenderDrivers++; + } + + // Add to the inputs list (ensure there is still room in the list and this is not the default device already listed) + if ((dataFlow == eCapture || dataFlow == eAll) && (mNumCaptureDrivers < FMOD_OUTPUT_MAXDRIVERS) && (!mCaptureDrivers[0].name || (FMOD_strcmpW(deviceString, mCaptureDrivers[0].name) != 0))) + { + mCaptureDrivers[mNumCaptureDrivers].name = (short *)FMOD_Memory_Calloc((FMOD_strlenW(deviceString) + 1) * sizeof(short)); + EXIT_ON_CONDITION(mCaptureDrivers[mNumCaptureDrivers].name == NULL, FMOD_ERR_MEMORY); + FMOD_strncpyW(mCaptureDrivers[mNumCaptureDrivers].name, deviceString, FMOD_strlenW(deviceString)); + + hResult = pDevice->GetId(&mCaptureDrivers[mNumCaptureDrivers].id); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + mNumCaptureDrivers++; + } + + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, deviceString, FMOD_STRING_MAXNAMELEN - 1); + FMOD_wtoa(driverName); + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::enumerate", "Found Driver: \"%s\".\n", driverName)); + } + + PropVariantClear(&propDesc); + PropVariantClear(&propName); + SAFE_RELEASE(pProperties); + SAFE_RELEASE(pDevice); + } + + // Enumeration completed successfully, clean up + SAFE_RELEASE(pCollection); + SAFE_RELEASE(pEnumerator); + + mEnumerated = true; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::enumerate", "Done.\n")); + return FMOD_OK; + +Exit: + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::enumerate", "Enumeration Failed!\n")); + + // Enumeration failed, clean up + PropVariantClear(&propName); + PropVariantClear(&propDesc); + SAFE_RELEASE(pProperties); + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pCollection); + SAFE_RELEASE(pEnumerator); + + return fResult; +} + + +/* +[ + [DESCRIPTION] + Return the number of attached audio output devices + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::getNumDrivers(int *numdrivers) +{ + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mEnumerated) + { + FMOD_RESULT result = FMOD_OK; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + *numdrivers = mNumRenderDrivers; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Take a wide char UUID string and produce an FMOD GUID + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This parsing is naughty, we are not supposed to make assumptions about the + structure of a MMDevice ID, but we need a GUID, so we are going to parse it + NOTE: The Structure of the MMDevice ID is a UUID. e.g. + "{0.0.0.00000000}.{ac233b1d-4807-4f89-949e-e9256fe037c0}" + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::parseUUIDString(WCHAR *srcString, FMOD_GUID *destGUID) +{ + WCHAR dataChunk[9] = {0}; + int index = 18; // Start at 18 to jump past the initial 0's section + + // Read 8 wide chars for Data1, then skip the '-' + FMOD_memcpy(dataChunk, &srcString[index], sizeof(WCHAR) * 8); + destGUID->Data1 = wcstoul(dataChunk, NULL, 16); + index += 8 + 1; + + // Read 4 wide chars for Data2, then skip the '-' + FMOD_memset(dataChunk, 0, 9); + FMOD_memcpy(dataChunk, &srcString[index], sizeof(WCHAR) * 4); + destGUID->Data2 = (unsigned short)wcstoul(dataChunk, NULL, 16); + index += 4 + 1; + + // Read 4 wide chars for Data3, then skip the '-' + FMOD_memset(dataChunk, 0, 9); + FMOD_memcpy(dataChunk, &srcString[index], sizeof(WCHAR) * 4); + destGUID->Data3 = (unsigned short)wcstoul(dataChunk, NULL, 16); + index += 4 + 1; + + // Read 2 wide chars for Data4[0] + FMOD_memset(dataChunk, 0, 9); + FMOD_memcpy(dataChunk, &srcString[index], sizeof(WCHAR) * 2); + destGUID->Data4[0] = (unsigned char)wcstoul(dataChunk, NULL, 16); + index += 2 + 0; + + // Read 2 wide chars for Data4[1], then skip the '-' + FMOD_memset(dataChunk, 0, 9); + FMOD_memcpy(dataChunk, &srcString[index], sizeof(WCHAR) * 2); + destGUID->Data4[1] = (unsigned char)wcstoul(dataChunk, NULL, 16); + index += 2 + 1; + + // Read 2 wide chars at a time for the remainder of Data4 + for (int i = 2; i < 8; i++) + { + FMOD_memset(dataChunk, 0, 9); + FMOD_memcpy(dataChunk, &srcString[index], sizeof(WCHAR) * 2); + destGUID->Data4[i] = (unsigned char)wcstoul(dataChunk, NULL, 16); + index += 2 + 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the name and GUID of an attached output device given its index + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::getDriverInfo(int driver, char *name, int namelen, FMOD_GUID *guid) +{ + if (!mEnumerated) + { + FMOD_RESULT result = FMOD_OK; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (driver < 0 || driver >= mNumRenderDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, mRenderDrivers[driver].name, FMOD_STRING_MAXNAMELEN - 1); + FMOD_wtoa(driverName); + + FMOD_strncpy(name, (char *)driverName, namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + parseUUIDString(mRenderDrivers[driver].id, guid); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the name and GUID of an attached output device given its index + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::getDriverInfoW(int driver, short *name, int namelen, FMOD_GUID *guid) +{ + if (!mEnumerated) + { + FMOD_RESULT result = FMOD_OK; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (driver < 0 || driver >= mNumRenderDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + FMOD_strncpyW(name, mRenderDrivers[driver].name, namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + parseUUIDString(mRenderDrivers[driver].id, guid); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Determine capabilities of the device specified + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + Currently only shared mode caps are reported as the user doesn't specify that they want + excusive mode until after Init + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::getDriverCapsEx(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDevice *pDevice = NULL; + IAudioClient *pAudioClient = NULL; + WAVE_FORMATEX *pWindowsMixFormat = NULL; + + // Enumerate devices if haven't already + if (!mEnumerated) + { + fResult = enumerate(); + if (fResult != FMOD_OK) + { + return fResult; + } + } + + // Get an enumerator to collect endpoint information + hResult = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the selected driver + hResult = pEnumerator->GetDevice(mRenderDrivers[id].id, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Create connection to audio engine + hResult = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the control panel mix format details + hResult = pAudioClient->GetMixFormat(&pWindowsMixFormat); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + /* + Establish the speaker mode + Note: Number of channels must be equal to mix format + */ + switch (pWindowsMixFormat->nChannels) + { + case 2 : *controlpanelspeakermode = FMOD_SPEAKERMODE_STEREO; + break; + case 4 : *controlpanelspeakermode = FMOD_SPEAKERMODE_QUAD; + break; + case 6 : *controlpanelspeakermode = FMOD_SPEAKERMODE_5POINT1; + break; + case 8 : *controlpanelspeakermode = FMOD_SPEAKERMODE_7POINT1; + break; + default : *controlpanelspeakermode = FMOD_SPEAKERMODE_STEREO; + break; + } + + /* + Establish the min / max frequencies + Note: Sample rate must be equal to mix format + */ + *minfrequency = *maxfrequency = pWindowsMixFormat->nSamplesPerSec; + + /* + Establish the caps + Note: WASAPI supports any format (trivial conversion) + */ + *caps = FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT | FMOD_CAPS_OUTPUT_FORMAT_PCM32 | + FMOD_CAPS_OUTPUT_FORMAT_PCM24 | FMOD_CAPS_OUTPUT_FORMAT_PCM16 | + FMOD_CAPS_OUTPUT_FORMAT_PCM8; + + // Multichannel support is tied to the mix format + if (pWindowsMixFormat->nChannels > 2) + { + *caps |= FMOD_CAPS_OUTPUT_MULTICHANNEL; + } + +Exit: + CoTaskMemFree(pWindowsMixFormat); + SAFE_RELEASE(pAudioClient); + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pEnumerator); + + return fResult; +} + + +/* +[ + [DESCRIPTION] + Initialise the audio output device + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::initEx(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata) +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + BOOL bResult = FALSE; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDeviceCollection *pCollection = NULL; + IMMDevice *pDevice = NULL; + WAVE_FORMATEX *pWindowsMixFormat = NULL; + WAVE_FORMATEX *pSimilarWaveFormat = NULL; + WAVE_FORMATEXTENSIBLE waveFormat; + int bits = 0; + REFERENCE_TIME hnsBufferLengthTime = 0; + REFERENCE_TIME hnsPeriod = 0; + BYTE *pLockedBuffer = NULL; + LARGE_INTEGER eventStartTime; + + // Enumerate devices if haven't already + if (!mEnumerated) + { + fResult = enumerate(); + if (fResult != FMOD_OK) + { + return fResult; + } + } + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::initEx", "Initialising...\n")); + + // Cannot initialise without at least one device + if (mNumRenderDrivers == 0) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::initEx", "No output sound devices found\n")); + return FMOD_ERR_OUTPUT_INIT; + } + + // Extract the mode from the init flags + if (flags & FMOD_INIT_WASAPI_EXCLUSIVE) + { + mExclusiveMode = true; + } + + // Get an enumerator to collect endpoint information + hResult = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the selected device + hResult = pEnumerator->GetDevice(mRenderDrivers[selecteddriver].id, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Create connection to audio engine + hResult = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mRenderAudioClient); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Prepare for format details + FMOD_memset(&waveFormat, 0, sizeof(WAVE_FORMATEXTENSIBLE)); + hResult = mRenderAudioClient->GetMixFormat(&pWindowsMixFormat); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the number of bits per sample + fResult = SoundI::getBitsFromFormat(*outputformat, &bits); + EXIT_ON_CONDITION(fResult != FMOD_OK, fResult); + + // We may need to force the FMOD channel count if it doesn't match nicely with the output + if (outputchannels != pWindowsMixFormat->nChannels) + { + if ((outputchannels == 1 || outputchannels == 2) && (outputchannels < pWindowsMixFormat->nChannels)) + { + // Exception case, allow mono or stereo output if there are enough channels for it + } + else + { + // Force the output to the windows mix format + outputchannels = pWindowsMixFormat->nChannels; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::initEx", "Forcing output channels to match Windows mix format: %d channel(s)\n", outputchannels)); + } + } + + // We need to update the output channels here so the DSP system knows (incase we use a resampler) + if (!mSystem->mDownmix) + { + mSystem->mMaxOutputChannels = outputchannels; + } + + // Override the speaker mode, unless its set to raw + if (*speakermode != FMOD_SPEAKERMODE_RAW) + { + switch (outputchannels) + { + case 1: *speakermode = FMOD_SPEAKERMODE_MONO; + break; + case 2: *speakermode = FMOD_SPEAKERMODE_STEREO; + break; + case 4: *speakermode = FMOD_SPEAKERMODE_QUAD; + break; + case 5: *speakermode = FMOD_SPEAKERMODE_SURROUND; + break; + case 6: *speakermode = FMOD_SPEAKERMODE_5POINT1; + break; + case 8: *speakermode = FMOD_SPEAKERMODE_7POINT1; + break; + } + } + + // Set the device to use the user requested values + waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + waveFormat.Format.nChannels = pWindowsMixFormat->nChannels; + waveFormat.Format.wBitsPerSample = bits; + waveFormat.Format.nBlockAlign = waveFormat.Format.nChannels * waveFormat.Format.wBitsPerSample / 8; + waveFormat.Format.nSamplesPerSec = *outputrate; + waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign; + waveFormat.Format.cbSize = 22; // Designates extra data + waveFormat.Samples.wValidBitsPerSample = bits; + FMOD_memcpy(&waveFormat.SubFormat, (*outputformat == FMOD_SOUND_FORMAT_PCMFLOAT ? &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : &KSDATAFORMAT_SUBTYPE_PCM), sizeof(GUID)); + + // See if the device can do the requested format (may work in exclusive mode) + hResult = mRenderAudioClient->IsFormatSupported((mExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED), (WAVE_FORMATEX*)&waveFormat, &pSimilarWaveFormat); + if (hResult == AUDCLNT_E_UNSUPPORTED_FORMAT || hResult == S_FALSE) + { + waveFormat.Format.nSamplesPerSec = pWindowsMixFormat->nSamplesPerSec; + waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign; + } + + // Setup a DSPResampler to handle resampling if required + mMixerResamplerDSP = NULL; + if (*outputrate != (int)waveFormat.Format.nSamplesPerSec) + { + FMOD_RESULT result = FMOD_OK; + unsigned int blocklength = 0; + FMOD_DSP_DESCRIPTION_EX description; + + blocklength = dspbufferlength; + blocklength /= 16; /* Round down to nearest 16 bytes */ + blocklength *= 16; + + FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); + description.channels = outputchannels; + description.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; /* Read callback will always give floats */ + description.userdata = this; + description.read = mixerResampleReadCallback; + description.mResamplerBlockLength = blocklength; + + mMixerResamplerDSP = FMOD_Object_Alloc(DSPResampler); + mMixerResamplerDSP->mSystem = mSystem; + + result = mMixerResamplerDSP->alloc(&description); + EXIT_ON_CONDITION(result != FMOD_OK, result); + + mMixerResamplerDSP->mTargetFrequency = (int)waveFormat.Format.nSamplesPerSec; /* Need to overwrite the default target frequency set in alloc */ + mMixerResamplerDSP->mLength = (unsigned int)-1; + mMixerResamplerDSP->mLoopCount = 0; + + result = mMixerResamplerDSP->setFrequency((float)*outputrate); + EXIT_ON_CONDITION(result != FMOD_OK, result); + + result = mMixerResamplerDSP->setFinished(false); + EXIT_ON_CONDITION(result != FMOD_OK, result); + + // Adjust the dspbufferlength so that after resampling we achieve the same latency desired by the user (don't do setDSPBufferSize, might screw other things up) + dspbufferlength = (int)(((float)dspbufferlength / (float)*outputrate) * waveFormat.Format.nSamplesPerSec); + } + + // Create a buffer to handle converting the number of channels to the output channel count + mMixerChannelConversionBuffer = NULL; + mRenderChannels = pWindowsMixFormat->nChannels; + if (outputchannels != waveFormat.Format.nChannels) + { + mMixerChannelConversionBufferSizeBytes = dspbufferlength * outputchannels * sizeof(float); + mMixerChannelConversionBuffer = (float *)FMOD_Memory_Calloc(mMixerChannelConversionBufferSizeBytes); + EXIT_ON_CONDITION(mMixerChannelConversionBuffer == NULL, FMOD_ERR_MEMORY); + } + + // Due to sample rate or channel conversions we will take care of format conversion also, prevents dsp_soundcard doing it (we need floats) + mMixerFormatConversionBuffer = NULL; + mRenderFormat = *outputformat; + if (*outputformat != FMOD_SOUND_FORMAT_PCMFLOAT) + { + mMixerFormatConversionBufferSizeBytes = dspbufferlength * mRenderChannels * sizeof(float); + mMixerFormatConversionBuffer = (float *)FMOD_Memory_Calloc(mMixerFormatConversionBufferSizeBytes); + EXIT_ON_CONDITION(mMixerFormatConversionBuffer == NULL, FMOD_ERR_MEMORY); + + *outputformat = FMOD_SOUND_FORMAT_PCMFLOAT; // This will stop dsp_soundcard converting + } + + if (mExclusiveMode) + { + REFERENCE_TIME hnsRequestedDuration = 0; + + // Get the minimum duration for lowest latency + hResult = mRenderAudioClient->GetDevicePeriod(NULL, &hnsRequestedDuration); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Internally WASAPI will double buffer for exclusive mode + hnsPeriod = hnsRequestedDuration; + hnsBufferLengthTime = hnsRequestedDuration; // Must equal period + } + else + { + REFERENCE_TIME hnsRequestedDuration = 0; + + // Get the default duration (fixed shared poll rate, should be 10ms) + hResult = mRenderAudioClient->GetDevicePeriod(&hnsRequestedDuration, NULL); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hnsPeriod = hnsRequestedDuration; // Must be shared poll rate + hnsBufferLengthTime = hnsRequestedDuration * dspnumbuffers; + } + + // This time init for real, this may still format error if the mix format isnt supported in exclusive mode + hResult = mRenderAudioClient->Initialize((mExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED), 0, hnsBufferLengthTime, hnsPeriod, (WAVE_FORMATEX*)&waveFormat, NULL); + EXIT_ON_CONDITION(hResult == AUDCLNT_E_UNSUPPORTED_FORMAT, FMOD_ERR_OUTPUT_FORMAT); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Store the block alignment for later conversions + mRenderBlockAlign = waveFormat.Format.nBlockAlign; + // Store the buffer size information + mMixerBufferBlockSize = dspbufferlength; + mMixerBufferNumBlocks = dspnumbuffers; + + // Create and set an event handle to wait on in the feeder thread + mFeederTimeout = FEEDER_TIMEOUT_DEFAULT_MS; + eventStartTime.QuadPart = -10000; // Start the timer 1ms after setting it + mFeederEventHandle = CreateWaitableTimer(NULL, FALSE, NULL); + EXIT_ON_CONDITION(mFeederEventHandle == NULL, FMOD_ERR_OUTPUT_INIT); + bResult = SetWaitableTimer(mFeederEventHandle, &eventStartTime, (int)(hnsPeriod / MFTIMES_PER_MILLISEC), NULL, NULL, FALSE); + EXIT_ON_CONDITION(bResult == 0, FMOD_ERR_OUTPUT_INIT); + + // Calculate the actual length of the buffer in frames + hResult = mRenderAudioClient->GetBufferSize(&mRenderBufferLength); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get a rendering interface for the stream to output sound + hResult = mRenderAudioClient->GetService(IID_IAudioRenderClient, (void**)&mRenderClient); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Initialise the shared buffer with silence by getting it all then releasing + hResult = mRenderClient->GetBuffer(mRenderBufferLength, &pLockedBuffer); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + hResult = mRenderClient->ReleaseBuffer(mRenderBufferLength, 0); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Setup and allocate the mixer buffer + mMixerReadPosition = 0; + mMixerWritePosition = 0; + mMixerBufferLength = mMixerBufferBlockSize * mMixerBufferNumBlocks; + mMixerBuffer = (BYTE*)FMOD_Memory_Calloc(mMixerBufferLength * mRenderBlockAlign); + EXIT_ON_CONDITION(mMixerBuffer == NULL, FMOD_ERR_MEMORY); + + // Successfully initialised audio output, cleanup + mInitialised = true; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::initEx", "Done.\n")); + +Exit: + CoTaskMemFree(pSimilarWaveFormat); + CoTaskMemFree(pWindowsMixFormat); + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pEnumerator); + + return fResult; +} + + +/* +[ + [DESCRIPTION] + Shutdown and cleanup resources created + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::close() +{ + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::close", "Closing...\n")); + + SAFE_RELEASE(mRenderClient); + SAFE_RELEASE(mRenderAudioClient); + + cleanUpEnumeration(); + + if (mMixerResamplerDSP) + { + mMixerResamplerDSP->release(true); + mMixerResamplerDSP = NULL; + } + + SAFE_FREE(mMixerChannelConversionBuffer); + SAFE_FREE(mMixerFormatConversionBuffer); + SAFE_FREE(mMixerBuffer); + + if (mFeederEventHandle) + { + CloseHandle(mFeederEventHandle); + mFeederEventHandle = NULL; + } + + if (mCoInitialized) + { + CoUninitialize(); + mCoInitialized = false; + } + + mInitialised = false; + mEnumerated = false; + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::close", "Done.\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return a handle to the rendering client instance + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::getHandle(void **handle) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + *handle = mRenderClient; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Update the hardware / Vista SW mixer + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + This thread is high priority, it must execute fast, simply copy + the mix data from the mixer thread to the hardware. + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::feederUpdate() +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + DWORD dResult = 0; + unsigned int numFramesPadding = 0; + unsigned int numFramesAvailable = 0; + BYTE *pLockedBuffer = NULL; + + if (!mFeederThreadElevated) + { + FMOD_OS_LIBRARY *avrtHandle = NULL; + + fResult = FMOD_OS_Library_Load("avrt.dll", &avrtHandle); + if (fResult == FMOD_OK) + { + HANDLE (WINAPI *setThreadTask)(LPCTSTR, LPDWORD); + + fResult = FMOD_OS_Library_GetProcAddress(avrtHandle, "AvSetMmThreadCharacteristicsA", (void **)&setThreadTask); + if (fResult == FMOD_OK) + { + DWORD taskIndex = 0; + + setThreadTask(mExclusiveMode ? "Pro Audio" : "Audio", &taskIndex); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "WASAPI feeder thread set to '%s' characteristic\n", mExclusiveMode ? "Pro Audio" : "Audio")); + } + + FMOD_OS_Library_Free(avrtHandle); + } + + // Thread is elevated or the OS doesn't support elevation, either way don't try again + mFeederThreadElevated = true; + } + + dResult = WaitForSingleObject(mFeederEventHandle, mFeederTimeout); + if (dResult == WAIT_FAILED || (dResult == WAIT_TIMEOUT && mFeederTimeout != FEEDER_TIMEOUT_PARTIAL_MS)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "WASAPI feeder event timer failed or is not responding...\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + // Find out how much of the buffer is filled (padded) + hResult = mRenderAudioClient->GetCurrentPadding(&numFramesPadding); + if (FAILED(hResult)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "Cannot determine buffer usage, aborting mix.\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + numFramesAvailable = mRenderBufferLength - numFramesPadding; + if (numFramesPadding == 0) + { + FLOG((FMOD_DEBUG_LEVEL_WARNING, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "Starvation detected in WASAPI output buffer!\n")); + } + + // Ensure we don't ask for more than we can provide + if (numFramesAvailable > (unsigned int)mMixerBufferLength) + { + numFramesAvailable = mMixerBufferLength; + mFeederTimeout = FEEDER_TIMEOUT_PARTIAL_MS; + FLOG((FMOD_DEBUG_LEVEL_WARNING, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "DSP buffer not large enough to satisfy requested read!\n")); + } + else + { + mFeederTimeout = FEEDER_TIMEOUT_DEFAULT_MS; + } + + hResult = mRenderClient->GetBuffer(numFramesAvailable, &pLockedBuffer); + if (FAILED(hResult)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "Cannot lock the buffer to write data, aborting mix.\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + /* + Copy the required data from the mix buffer + */ + { + int requiredData = numFramesAvailable; + int requiredDataBytes = requiredData * mRenderBlockAlign; + int mixerBufferLengthBytes = mMixerBufferLength * mRenderBlockAlign; + int mixerReadPositionBytes = mMixerReadPosition * mRenderBlockAlign; + + // Do we need to wrap read from the mixer buffer + if (mixerReadPositionBytes + requiredDataBytes > mixerBufferLengthBytes) + { + int sizeBytes = mixerBufferLengthBytes - mixerReadPositionBytes; + + FMOD_memcpy(pLockedBuffer, mMixerBuffer + mixerReadPositionBytes, sizeBytes); + FMOD_memcpy(pLockedBuffer + sizeBytes, mMixerBuffer , requiredDataBytes - sizeBytes); + } + else + { + FMOD_memcpy(pLockedBuffer, mMixerBuffer + mixerReadPositionBytes, requiredDataBytes); + } + + mMixerReadPosition += requiredData; + mMixerReadPosition %= mMixerBufferLength; + } + + hResult = mRenderClient->ReleaseBuffer(numFramesAvailable, 0); + if (FAILED(hResult)) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "Cannot release the write buffer, aborting mix.\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + // Tell the mixer to check if it needs to mix + mMixerThread.wakeupThread(); +//MGB FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::feederUpdate", "Read %d samples from mixer buffer\n", numFramesAvailable)); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Buffer up mix data so the feeder thread can copy the pre-mixed data quickly + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::mixerUpdate() +{ + FMOD_RESULT result = FMOD_OK; + int writeBlock = mMixerWritePosition / mMixerBufferBlockSize; + int readBlock = mMixerReadPosition / mMixerBufferBlockSize; + int numChannels = 0; + + mSystem->mDSPTimeStamp.stampIn(); + + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&numChannels); + } + else + { + result = mSystem->getSoftwareFormat(NULL, NULL, &numChannels, NULL, NULL, NULL); + if (result != FMOD_OK) + { + return result; + } + } + + while (writeBlock != readBlock) + { + void *mixLocation = mMixerBuffer + ((writeBlock * mMixerBufferBlockSize) * mRenderBlockAlign); + + // Do we need to resample to a set rate for the output? + if (mMixerResamplerDSP) + { + // Do we need to change the number of channels, i.e. fill output with a subset of the channels + if (mMixerChannelConversionBuffer) + { + // Do we need to change the format from float to the output format? + if (mMixerFormatConversionBuffer) + { + // Resample pulls on the mix via a resample read callback + mMixerResamplerDSP->mBuffer = mMixerChannelConversionBuffer; + result = mMixerResamplerDSP->read(&mMixerChannelConversionBuffer, &numChannels, &mMixerBufferBlockSize, (FMOD_SPEAKERMODE)0, numChannels, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + + // Mix a mono or stereo source into the front left and front right for the multichannel output + result = channelConvert(mMixerFormatConversionBuffer, mMixerChannelConversionBuffer, mRenderChannels, numChannels, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + return result; + } + + // We must convert here since the above resampler / channel convert needs floats (i.e. we cant use the convert in dsp_soundcard) + result = DSPI::convert(mixLocation, mMixerFormatConversionBuffer, mRenderFormat, FMOD_SOUND_FORMAT_PCMFLOAT, mMixerBufferBlockSize * mRenderChannels, 1, 1, 1.0f); + if (result != FMOD_OK) + { + return result; + } + } + // No Format conversion required + else + { + // Resample pulls on the mix via a resample read callback + mMixerResamplerDSP->mBuffer = mMixerChannelConversionBuffer; + result = mMixerResamplerDSP->read(&mMixerChannelConversionBuffer, &numChannels, &mMixerBufferBlockSize, (FMOD_SPEAKERMODE)0, numChannels, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + + // Mix a mono or stereo source into the front left and front right for the multichannel output + result = channelConvert((float*)mixLocation, mMixerChannelConversionBuffer, mRenderChannels, numChannels, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + // Do we need to change the format from float to the output format? + if (mMixerFormatConversionBuffer) + { + // Resample pulls on the mix via a resample read callback + mMixerResamplerDSP->mBuffer = mMixerFormatConversionBuffer; + result = mMixerResamplerDSP->read(&mMixerFormatConversionBuffer, &numChannels, &mMixerBufferBlockSize, (FMOD_SPEAKERMODE)0, numChannels, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + + // We must convert here since the above resampler needs floats (i.e. we cant use the convert in dsp_soundcard) + result = DSPI::convert(mixLocation, mMixerFormatConversionBuffer, mRenderFormat, FMOD_SOUND_FORMAT_PCMFLOAT, mMixerBufferBlockSize * mRenderChannels, 1, 1, 1.0f); + if (result != FMOD_OK) + { + return result; + } + } + // No Format conversion required + else + { + // Resample pulls on the mix via a resample read callback + mMixerResamplerDSP->mBuffer = (float*)mixLocation; + result = mMixerResamplerDSP->read((float**)&mixLocation, &numChannels, &mMixerBufferBlockSize, (FMOD_SPEAKERMODE)0, numChannels, mDSPTick); + if (result != FMOD_OK) + { + return result; + } + } + } + + mDSPTick++; + } + // No resample required + else + { + // Do we need to change the number of channels, i.e. fill output with a subset of the channels + if (mMixerChannelConversionBuffer) + { + // Do we need to change the format from float to the output format? + if (mMixerFormatConversionBuffer) + { + // Straight mix of the DSP tree + result = mix(mMixerChannelConversionBuffer, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::mixerUpdate", "Mix failed!\n")); + return result; + } + + // Mix a mono or stereo source into the front left and front right for the multichannel output + result = channelConvert(mMixerFormatConversionBuffer, mMixerChannelConversionBuffer, mRenderChannels, numChannels, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + return result; + } + + // We must convert here since the above channel convert needs floats (i.e. we cant use the convert in dsp_soundcard) + result = DSPI::convert(mixLocation, mMixerFormatConversionBuffer, mRenderFormat, FMOD_SOUND_FORMAT_PCMFLOAT, mMixerBufferBlockSize * mRenderChannels, 1, 1, 1.0f); + if (result != FMOD_OK) + { + return result; + } + } + // No Format conversion required + else + { + // Straight mix of the DSP tree + result = mix(mMixerChannelConversionBuffer, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::mixerUpdate", "Mix failed!\n")); + return result; + } + + // Mix a mono or stereo source into the front left and front right for the multichannel output + result = channelConvert((float*)mixLocation, mMixerChannelConversionBuffer, mRenderChannels, numChannels, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + return result; + } + } + } + else + { + // Do we need to change the format from float to the output format? + if (mMixerFormatConversionBuffer) + { + // Straight mix of the DSP tree + result = mix(mMixerFormatConversionBuffer, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::mixerUpdate", "Mix failed!\n")); + return result; + } + + // We must convert here since the above resampler needs floats (i.e. we cant use the convert in dsp_soundcard) + result = DSPI::convert(mixLocation, mMixerFormatConversionBuffer, mRenderFormat, FMOD_SOUND_FORMAT_PCMFLOAT, mMixerBufferBlockSize * mRenderChannels, 1, 1, 1.0f); + if (result != FMOD_OK) + { + return result; + } + } + // No Format conversion required + else + { + // Straight mix of the DSP tree + result = mix(mixLocation, mMixerBufferBlockSize); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::mixerUpdate", "Mix failed!\n")); + return result; + } + } + } + } + + writeBlock++; + writeBlock %= mMixerBufferNumBlocks; + } + + mMixerWritePosition = mMixerBufferBlockSize * writeBlock; + + mSystem->mDSPTimeStamp.stampOut(95); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Read for the mixer resampler + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::mixerResampleRead(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + FMOD_RESULT result = FMOD_OK; + + result = mix(outbuffer, length); + if (result != FMOD_OK) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::mixerResampleRead", "Mix failed!\n")); + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Start playing the shared buffer + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::start() +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + + mFeederThreadElevated = false; + fResult = mFeederThread.initThread("FMOD (WASAPI) feeder thread", feederThreadCallback, this, Thread::PRIORITY_NORMAL, 0, 0, false, 0, mSystem); + if (fResult != FMOD_OK) + { + return fResult; + } + + fResult = mMixerThread.initThread("FMOD mixer thread", mixerThreadCallback, this, Thread::PRIORITY_HIGH, 0, 0, true, 0, mSystem); + if (fResult != FMOD_OK) + { + mFeederThread.closeThread(); + return fResult; + } + + hResult = mRenderAudioClient->Start(); + if (FAILED(hResult)) + { + mFeederThread.closeThread(); + mMixerThread.closeThread(); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Stop playing the shared buffer + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::stop() +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + + if (mInitialised) + { + fResult = mFeederThread.closeThread(); + if (fResult != FMOD_OK) + { + return fResult; + } + mFeederThreadElevated = false; + + fResult = mMixerThread.closeThread(); + if (fResult != FMOD_OK) + { + return fResult; + } + + hResult = mRenderAudioClient->Stop(); + if (FAILED(hResult)) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Handle converting from one channel layout to another + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::channelConvert(float *outbuffer, float *inbuffer, int outchannels, int inchannels, int length) +{ + // Only allow mono or stereo source + if (inchannels <= 0 || inchannels > 2) + { + return FMOD_ERR_INVALID_PARAM; + } + + // Don't allow down mixing channels + if (outchannels < inchannels) + { + return FMOD_ERR_INVALID_PARAM; + } + + // Mix a mono or stereo source into the front left and right for the multichannel output + FMOD_memset(outbuffer, 0, length * outchannels * sizeof(float)); + for (int i = 0; i < length; i++) + { + if (inchannels == 1) // Source is mono + { + outbuffer[(i * outchannels) + 0] = inbuffer[(i * inchannels) + 0] * 0.707f; + outbuffer[(i * outchannels) + 1] = inbuffer[(i * inchannels) + 0] * 0.707f; + } + else if (inchannels == 2) // Source is stereo + { + outbuffer[(i * outchannels) + 0] = inbuffer[(i * inchannels) + 0]; + outbuffer[(i * outchannels) + 1] = inbuffer[(i * inchannels) + 1]; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Handle converting from one channel layout to another + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::cleanUpEnumeration() +{ + int i; + // Clean up any driver enumeration (may have enumerated once already) + for (i = 0; i < FMOD_OUTPUT_MAXDRIVERS; i++) + { + SAFE_FREE(mRenderDrivers[i].name); + CoTaskMemFree(mRenderDrivers[i].id); + mRenderDrivers[i].id = 0; + } + for (i = 0; i < FMOD_OUTPUT_MAXDRIVERS; i++) + { + SAFE_FREE(mCaptureDrivers[i].name); + CoTaskMemFree(mCaptureDrivers[i].id); + mCaptureDrivers[i].id = 0; + } + mNumRenderDrivers = 0; + mNumCaptureDrivers = 0; + + return FMOD_OK; +} + +#ifdef FMOD_SUPPORT_RECORDING +/* +[ + [DESCRIPTION] + Return the number of attached audio input devices + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordGetNumDrivers(int *numdrivers) +{ + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mEnumerated) + { + FMOD_RESULT result = FMOD_OK; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + *numdrivers = mNumCaptureDrivers; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the name of an attached input device given its index + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + if (!mEnumerated) + { + FMOD_RESULT result = FMOD_OK; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (id < 0 || id >= mNumCaptureDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + short driverName[FMOD_STRING_MAXNAMELEN] = {0}; + + FMOD_strncpyW(driverName, mCaptureDrivers[id].name, FMOD_STRING_MAXNAMELEN - 1); + FMOD_wtoa(driverName); + + FMOD_strncpy(name, (char *)driverName, namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + parseUUIDString(mCaptureDrivers[id].id, guid); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return the name of an attached input device given its index + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordGetDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + if (!mEnumerated) + { + FMOD_RESULT result = FMOD_OK; + + result = enumerate(); + if (result != FMOD_OK) + { + return result; + } + } + + if (id < 0 || id >= mNumCaptureDrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (name && namelen >= 1) + { + FMOD_strncpyW(name, mCaptureDrivers[id].name, namelen - 1); + name[namelen - 1] = 0; + } + + if (guid) + { + parseUUIDString(mCaptureDrivers[id].id, guid); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Determine capabilities of the device specified + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + Currently only shared mode caps are reported as the user doesn't specify that they want + excusive mode until after Init + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordGetDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency) +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDevice *pDevice = NULL; + IAudioClient *pAudioClient = NULL; + WAVE_FORMATEX *pWindowsMixFormat = NULL; + + // Enumerate devices if haven't already + if (!mEnumerated) + { + fResult = enumerate(); + if (fResult != FMOD_OK) + { + return fResult; + } + } + + // Get an enumerator to collect endpoint information + hResult = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the selected driver + hResult = pEnumerator->GetDevice(mCaptureDrivers[id].id, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Create connection to audio engine + hResult = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Get the control panel mix format details + hResult = pAudioClient->GetMixFormat(&pWindowsMixFormat); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + /* + Establish the min / max frequencies + Note: Sample rate must be equal to mix format + */ + *minfrequency = *maxfrequency = pWindowsMixFormat->nSamplesPerSec; + + /* + Establish the caps + Note: WASAPI supports any format (trivial conversion) + */ + *caps = FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT | FMOD_CAPS_OUTPUT_FORMAT_PCM32 | + FMOD_CAPS_OUTPUT_FORMAT_PCM24 | FMOD_CAPS_OUTPUT_FORMAT_PCM16 | + FMOD_CAPS_OUTPUT_FORMAT_PCM8; + + // Multichannel support is tied to the mix format + if (pWindowsMixFormat->nChannels > 2) + { + *caps |= FMOD_CAPS_OUTPUT_MULTICHANNEL; + } + +Exit: + CoTaskMemFree(pWindowsMixFormat); + SAFE_RELEASE(pAudioClient); + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pEnumerator); + + return fResult; +} + + +/* +[ + [DESCRIPTION] + Start recording from the selected input source + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordStart(FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop) +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + BOOL bResult = FALSE; + SoundI *pSoundI = (SoundI *)sound; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDeviceCollection *pCollection = NULL; + IMMDevice *pDevice = NULL; + WAVE_FORMATEX *pWindowsMixFormat = NULL; + WAVE_FORMATEX *pSimilarWaveFormat = NULL; + WAVE_FORMATEXTENSIBLE waveFormat = {0}; + REFERENCE_TIME hnsBufferLengthTime = 0; + REFERENCE_TIME hnsPeriod = 0; + int bits = 0; + LARGE_INTEGER eventStartTime = {0}; + FMOD_WASAPIRecordMembers *recordMembers = NULL; + + if (!pSoundI || pSoundI->mLength < 1) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mEnumerated) + { + fResult = enumerate(); + EXIT_ON_CONDITION(fResult != FMOD_OK, fResult); + } + + if (mNumCaptureDrivers == 0) + { + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWASAPI::recordStart", "No input sound devices found")); + EXIT_ON_CONDITION(true, FMOD_ERR_OUTPUT_INIT); + } + + recordinfo->mRecordPlatformSpecific = recordMembers = (FMOD_WASAPIRecordMembers *)FMOD_Object_Calloc(FMOD_WASAPIRecordMembers); + EXIT_ON_CONDITION(recordMembers == NULL, FMOD_ERR_MEMORY); + + hResult = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&pEnumerator); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hResult = pEnumerator->GetDevice(mCaptureDrivers[recordinfo->mRecordId].id, &pDevice); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hResult = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&recordMembers->mCaptureAudioClient); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hResult = recordMembers->mCaptureAudioClient->GetMixFormat(&pWindowsMixFormat); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + fResult = SoundI::getBitsFromFormat(pSoundI->mFormat, &bits); + EXIT_ON_CONDITION(fResult != FMOD_OK, fResult); + + waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + waveFormat.Format.nChannels = pSoundI->mChannels; + waveFormat.Format.wBitsPerSample = bits; + waveFormat.Format.nBlockAlign = waveFormat.Format.nChannels * waveFormat.Format.wBitsPerSample / 8; + waveFormat.Format.nSamplesPerSec = (DWORD)pSoundI->mDefaultFrequency; + waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign; + waveFormat.Format.cbSize = 22; // Designates extra data + waveFormat.Samples.wValidBitsPerSample = bits; + FMOD_memcpy(&waveFormat.SubFormat, (pSoundI->mFormat == FMOD_SOUND_FORMAT_PCMFLOAT ? &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : &KSDATAFORMAT_SUBTYPE_PCM), sizeof(GUID)); + + // See if the device can do the requested format (may work in exclusive mode) + hResult = recordMembers->mCaptureAudioClient->IsFormatSupported((mExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED), (WAVE_FORMATEX *)&waveFormat, &pSimilarWaveFormat); + if (hResult == AUDCLNT_E_UNSUPPORTED_FORMAT || hResult == S_FALSE) + { + // Usually the mix format number of channels must be used, but mono isn't normally provided so allow a simple conversion case from stereo to mono + if (pSoundI->mChannels == 1 && pWindowsMixFormat->nChannels == 2) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordStart", "Cannot use requested number of channels natively, FMOD will remix\n")); + waveFormat.Format.nChannels = pWindowsMixFormat->nChannels; + waveFormat.Format.nBlockAlign = waveFormat.Format.nChannels * waveFormat.Format.wBitsPerSample / 8; + waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign; + recordMembers->mRecordStereoToMono = true; + } + + // FMODs resampler will be used to deliver the required frequency, don't change pSoundI->mDefaultFrequency (FMOD will resample to this) + if (pSoundI->mDefaultFrequency != pWindowsMixFormat->nSamplesPerSec) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordStart", "Cannot use requested record frequency natively, FMOD will resample\n")); + waveFormat.Format.nSamplesPerSec = pWindowsMixFormat->nSamplesPerSec; + waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign; + } + } + + if (mExclusiveMode) + { + REFERENCE_TIME hnsRequestedDuration = 0; + + // Get the minimum duration for lowest latency + hResult = recordMembers->mCaptureAudioClient->GetDevicePeriod(NULL, &hnsRequestedDuration); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Internally WASAPI will double buffer for exclusive mode + hnsPeriod = hnsRequestedDuration; + hnsBufferLengthTime = hnsRequestedDuration; // Must equal period + } + else + { + REFERENCE_TIME hnsRequestedDuration = 0; + + // Get the default duration (fixed shared poll rate, should be 10ms) + hResult = recordMembers->mCaptureAudioClient->GetDevicePeriod(&hnsRequestedDuration, NULL); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hnsPeriod = hnsRequestedDuration; // Must be shared poll rate + hnsBufferLengthTime = RECORD_LENGTH_MS * MFTIMES_PER_MILLISEC; + } + + // This time init for real, this may still format error if the mix format isnt supported in exclusive mode + hResult = recordMembers->mCaptureAudioClient->Initialize((mExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED), 0, hnsBufferLengthTime, hnsPeriod, (WAVE_FORMATEX*)&waveFormat, NULL); + EXIT_ON_CONDITION(hResult == AUDCLNT_E_UNSUPPORTED_FORMAT, FMOD_ERR_FORMAT); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Remember the hardware frequency so FMOD can resample to pSoundI->mDefaultFrequency if required + recordinfo->mRecordRate = waveFormat.Format.nSamplesPerSec; + // Set the hardware format to the desired format, WASAPI handles format conversion, not FMOD + recordinfo->mRecordFormat = pSoundI->mFormat; + + hResult = recordMembers->mCaptureAudioClient->GetBufferSize(&recordMembers->mCaptureBufferLength); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + hResult = recordMembers->mCaptureAudioClient->GetService(IID_IAudioCaptureClient, (void **)&recordMembers->mCaptureClient); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + + // Setup and allocate the record buffer + recordinfo->mRecordBufferLength = (RECORD_LENGTH_MS * recordinfo->mRecordRate) / 1000; + recordMembers->mRecordWritePosition = 0; + recordMembers->mRecordBlockAlign = pSoundI->mChannels * (waveFormat.Format.wBitsPerSample / 8); + recordMembers->mRecordBuffer = (BYTE *)FMOD_Memory_Calloc(recordinfo->mRecordBufferLength * recordMembers->mRecordBlockAlign); + EXIT_ON_CONDITION(recordMembers->mRecordBuffer == NULL, FMOD_ERR_MEMORY); + + if (!mRecordInitialised) + { + // Create and set an event handle to wait on in the record thread + eventStartTime.QuadPart = -10000; // Start the timer 1ms after setting it + mRecordEventHandle = CreateWaitableTimer(NULL, FALSE, NULL); + EXIT_ON_CONDITION(mRecordEventHandle == NULL, FMOD_ERR_OUTPUT_INIT); + bResult = SetWaitableTimer(mRecordEventHandle, &eventStartTime, (int)(hnsPeriod / MFTIMES_PER_MILLISEC), NULL, NULL, FALSE); + EXIT_ON_CONDITION(bResult == 0, FMOD_ERR_OUTPUT_INIT); + + // Start the record thread (waits on timer event handle) + fResult = mRecordThread.initThread("FMOD record thread", recordThreadCallback, this, Thread::PRIORITY_NORMAL, 0, 0, false, 0, mSystem); + EXIT_ON_CONDITION(fResult != FMOD_OK, fResult); + + mRecordInitialised = true; + } + + hResult = recordMembers->mCaptureAudioClient->Start(); + EXIT_ON_CONDITION(FAILED(hResult), FMOD_ERR_OUTPUT_INIT); + +Exit: + CoTaskMemFree(pSimilarWaveFormat); + CoTaskMemFree(pWindowsMixFormat); + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pEnumerator); + + return fResult; +} + + +/* +[ + [DESCRIPTION] + Stop the recording of an input source + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordStop(FMOD_RECORDING_INFO *recordinfo) +{ + FMOD_RESULT fResult = FMOD_OK; + HRESULT hResult = S_OK; + + if (recordinfo) + { + FMOD_WASAPIRecordMembers *recordMembers = (FMOD_WASAPIRecordMembers *)recordinfo->mRecordPlatformSpecific; + + hResult = recordMembers->mCaptureAudioClient->Stop(); + if (FAILED(hResult)) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + SAFE_RELEASE(recordMembers->mCaptureClient); + SAFE_RELEASE(recordMembers->mCaptureAudioClient); + SAFE_FREE(recordMembers->mRecordBuffer); + SAFE_FREE(recordinfo->mRecordPlatformSpecific); + } + + if (mRecordNumActive == 0) + { + fResult = mRecordThread.closeThread(); + CHECK_RESULT(fResult); + + CloseHandle(mRecordEventHandle); + mRecordEventHandle = 0; + + mRecordThreadElevated = false; + mRecordInitialised = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Function to populate the ring buffer with recorded data from the hardware + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordUpdate() +{ + FMOD_RESULT fResult = FMOD_OK; + DWORD dResult = 0; + + if (!mRecordNumActive) + { + return FMOD_OK; + } + + if (!mRecordThreadElevated) + { + FMOD_OS_LIBRARY *avrtHandle = NULL; + + fResult = FMOD_OS_Library_Load("avrt.dll", &avrtHandle); + if (fResult == FMOD_OK) + { + HANDLE (WINAPI *setThreadTask)(LPCTSTR, LPDWORD); + + fResult = FMOD_OS_Library_GetProcAddress(avrtHandle, "AvSetMmThreadCharacteristicsA", (void **)&setThreadTask); + if (fResult == FMOD_OK) + { + DWORD taskIndex = 0; + + setThreadTask(mExclusiveMode ? "Pro Audio" : "Audio", &taskIndex); + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordUpdate", "Record thread set to '%s' characteristic\n", mExclusiveMode ? "Pro Audio" : "Audio")); + } + + FMOD_OS_Library_Free(avrtHandle); + } + + // Thread is elevated or the OS doesn't support elevation, either way don't try again + mRecordThreadElevated = true; + } + + dResult = WaitForSingleObject(mRecordEventHandle, 2000); + if (dResult != WAIT_OBJECT_0) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordUpdate", "Record event timer not responding...\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + { + FMOD_RECORDING_INFO *current = NULL; + LocalCriticalSection recordInfoCrit(mRecordInfoCrit, true); + + current = SAFE_CAST(FMOD_RECORDING_INFO, mRecordInfoHead.getNext()); + while (current != &mRecordInfoHead) + { + HRESULT hResult = S_OK; + UINT32 nextPacketSize = 0; + FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, current->getNext()); + FMOD_WASAPIRecordMembers *recordMembers = (FMOD_WASAPIRecordMembers *)current->mRecordPlatformSpecific; + + // Stop querying the hardware if we already know it's finish / unplugged, system update will stop it + if (current->mRecordFinished) + { + current = next; + continue; + } + + // Find out if there is a packet waiting, size of 0 indicates no packet + hResult = recordMembers->mCaptureClient->GetNextPacketSize(&nextPacketSize); + if (hResult == AUDCLNT_E_DEVICE_INVALIDATED) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordUpdate", "Recording device has been unplugged, aborting capture.\n")); + + // Mark this recording device for stopping in system update + current->mRecordFinished = true; + current = next; + + continue; + } + else if (FAILED(hResult)) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordUpdate", "Cannot determine buffer usage, aborting capture.\n")); + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (nextPacketSize == recordMembers->mCaptureBufferLength) + { + FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputWASAPI::recordUpdate", "Overrun detected in WASAPI input buffer!\n")); + } + + while (nextPacketSize) + { + UINT32 framesInPacket = 0; + BYTE *pPacketData = NULL; + DWORD flags = 0; + int recordWritePosition = recordMembers->mRecordWritePosition; + BYTE *pRecordPosition = recordMembers->mRecordBuffer + (recordMembers->mRecordWritePosition * recordMembers->mRecordBlockAlign); + + hResult = recordMembers->mCaptureClient->GetBuffer(&pPacketData, &framesInPacket, &flags, NULL, NULL); + if (FAILED(hResult)) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + if (recordMembers->mRecordStereoToMono) + { + int bytesPerSample = recordMembers->mRecordBlockAlign; // Since we know the target buffer is mono, blockAlign == bytesPerSample + + for (unsigned int i = 0; i <= framesInPacket; i++) + { + FMOD_memcpy(pRecordPosition, pPacketData, bytesPerSample); + + pPacketData += (bytesPerSample * 2); + pRecordPosition += (bytesPerSample * 1); + + if (pRecordPosition == (recordMembers->mRecordBuffer + (current->mRecordBufferLength * recordMembers->mRecordBlockAlign))) + { + pRecordPosition = recordMembers->mRecordBuffer; + } + } + } + else + { + // If we dont have to wrap the packet around the ring buffer, copy directly + if (recordWritePosition + framesInPacket <= current->mRecordBufferLength) + { + FMOD_memcpy(pRecordPosition, pPacketData, framesInPacket * recordMembers->mRecordBlockAlign); + } + // Othewise copy to the end of the buffer, then continue at the start + else + { + int endFrameLength = current->mRecordBufferLength - recordWritePosition; + int startFrameLength = framesInPacket - endFrameLength; + + FMOD_memcpy(pRecordPosition, pPacketData, endFrameLength * recordMembers->mRecordBlockAlign); + + pPacketData += endFrameLength * recordMembers->mRecordBlockAlign; + pRecordPosition = recordMembers->mRecordBuffer; + + FMOD_memcpy(pRecordPosition, pPacketData, startFrameLength * recordMembers->mRecordBlockAlign); + } + } + + hResult = recordMembers->mCaptureClient->ReleaseBuffer(framesInPacket); + if (FAILED(hResult)) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + hResult = recordMembers->mCaptureClient->GetNextPacketSize(&nextPacketSize); + if (FAILED(hResult)) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + recordWritePosition += framesInPacket; + recordWritePosition %= current->mRecordBufferLength; + recordMembers->mRecordWritePosition = recordWritePosition; + } + + current = next; + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Return a position value in samples so that FMOD knows how much has been recorded + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordGetPosition(FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + FMOD_WASAPIRecordMembers *recordMembers = (FMOD_WASAPIRecordMembers *)recordinfo->mRecordPlatformSpecific; + + *pcm = recordMembers->mRecordWritePosition; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + Lock the requested number of bytes + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::recordLock(FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD_WASAPIRecordMembers *recordMembers = (FMOD_WASAPIRecordMembers *)recordinfo->mRecordPlatformSpecific; + unsigned int lenbytes = recordinfo->mRecordBufferLength * recordMembers->mRecordBlockAlign; + + // Cant lock more than the sample length + if (length > lenbytes) + { + length = lenbytes; + } + + // Ensure parameters are valid + if (offset >= lenbytes || offset < 0 || length < 0) + { + *ptr1 = *ptr2 = NULL; + *len1 = *len2 = 0; + return FMOD_ERR_INVALID_PARAM; + } + + // If it is not a wrapping lock just fill out ptr1 + if (offset + length <= lenbytes) + { + *ptr1 = recordMembers->mRecordBuffer + offset; + *len1 = length; + *ptr2 = NULL; + *len2 = 0; + } + // Otherwise return wrapped pointers in ptr1 and ptr2 + else + { + *ptr1 = recordMembers->mRecordBuffer + offset; + *len1 = lenbytes - offset; + *ptr2 = recordMembers->mRecordBuffer; + *len2 = length - (lenbytes - offset); + } + + return FMOD_OK; +} +#endif /* FMOD_SUPPORT_RECORDING */ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWASAPI::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_OUTPUT, sizeof(*this)); + + for (int i = 0; i < mNumRenderDrivers; i++) + { + if (mRenderDrivers[i].name) + { + tracker->add(false, FMOD_MEMBITS_STRING, FMOD_strlenW((mRenderDrivers[i].name) + 1) * sizeof(short)); + } + } + + for (int j = 0; j < mNumCaptureDrivers; j++) + { + if (mCaptureDrivers[j].name) + { + tracker->add(false, FMOD_MEMBITS_STRING, FMOD_strlenW((mCaptureDrivers[j].name) + 1) * sizeof(short)); + } + } + + if (mMixerResamplerDSP) + { + CHECK_RESULT(mMixerResamplerDSP->getMemoryUsed(tracker)); + } + + if (mMixerChannelConversionBuffer) + { + tracker->add(false, FMOD_MEMBITS_OUTPUT, mMixerChannelConversionBufferSizeBytes); + } + + if (mMixerFormatConversionBuffer) + { + tracker->add(false, FMOD_MEMBITS_OUTPUT, mMixerFormatConversionBufferSizeBytes); + } + + if (mMixerBuffer) + { + tracker->add(false, FMOD_MEMBITS_OUTPUT, mMixerBufferLength * mRenderBlockAlign); + } + + CHECK_RESULT(Output::getMemoryUsedImpl(tracker)); + + return FMOD_OK; +} +#endif /* FMOD_SUPPORT_MEMORYTRACKER */ + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + +FMOD_RESULT F_CALLBACK OutputWASAPI::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->getNumDrivers(numdrivers); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::getDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->getDriverInfo(id, name, namelen, guid); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::getDriverInfoWCallback(FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->getDriverInfoW(id, name, namelen, guid); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::getDriverCapsExCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->getDriverCapsEx(id, caps, minfrequency, maxfrequency, controlpanelspeakermode); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::initExCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->initEx(selecteddriver, flags, outputrate, outputchannels, outputformat, speakermode, dspbufferlength, dspnumbuffers, max2dchannels, max3dchannels, extradriverdata); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->close(); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::startCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->start(); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::stopCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->stop(); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->getHandle(handle); +} + + +void OutputWASAPI::feederThreadCallback(void *data) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)data; + + wasapi->feederUpdate(); +} + + +void OutputWASAPI::mixerThreadCallback(void *data) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)data; + + wasapi->mixerUpdate(); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::mixerResampleReadCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ + DSPResampler *resampler = (DSPResampler *)dsp_state; + OutputWASAPI *wasapi = NULL; + + resampler->getUserData((void **)&wasapi); + + return wasapi->mixerResampleRead(inbuffer, outbuffer, length, inchannels, outchannels); +} + + +#ifdef FMOD_SUPPORT_RECORDING +FMOD_RESULT F_CALLBACK OutputWASAPI::recordGetNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordGetNumDrivers(numdrivers); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordGetDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordGetDriverInfo(id, name, namelen, guid); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordGetDriverInfoWCallback(FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordGetDriverInfoW(id, name, namelen, guid); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordGetDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordGetDriverCaps(id, caps, minfrequency, maxfrequency); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordStartCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordStart(recordinfo, (Sound *)sound, loop ? true : false); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordStopCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordStop(recordinfo); +} + + +void OutputWASAPI::recordThreadCallback(void *data) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)data; + + wasapi->recordUpdate(); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordGetPositionCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordGetPosition(recordinfo, pcm); +} + + +FMOD_RESULT F_CALLBACK OutputWASAPI::recordLockCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->recordLock(recordinfo, offset, length, ptr1, ptr2, len1, len2); +} +#endif /* FMOD_SUPPORT_RECORDING */ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER +FMOD_RESULT F_CALLBACK OutputWASAPI::getMemoryUsedCallback(FMOD_OUTPUT_STATE *output, MemoryTracker *tracker) +{ + OutputWASAPI *wasapi = (OutputWASAPI *)output; + + return wasapi->getMemoryUsed(tracker); +} +#endif /* FMOD_SUPPORT_MEMORYTRACKER */ + +} /* namespace FMOD */ + +#endif /* FMOD_SUPPORT_WASAPI */ diff --git a/win32/src/fmod_output_wasapi.h b/win32/src/fmod_output_wasapi.h new file mode 100755 index 0000000..a4b9dad --- /dev/null +++ b/win32/src/fmod_output_wasapi.h @@ -0,0 +1,171 @@ +#ifndef _FMOD_OUTPUT_WASAPI_H +#define _FMOD_OUTPUT_WASAPI_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WASAPI + +#include "fmod_systemi.h" +#include "fmod_outputi.h" +#include "fmod_memorytracker.h" +#include "fmod_stringw.h" + +// 0x0600 is Vista (safe since WASAPI output mode is only available on Vista) +#define _WIN32_WINNT 0x0601 +#include <Windows.h> + +#include "wasapi\fmod_audioclient.h" +#include "wasapi\fmod_mmdeviceapi.h" + +namespace FMOD +{ + typedef struct FMOD_WASAPIDriver + { + LPWSTR id; + short *name; + } FMOD_WASAPIDriver; + + typedef struct FMOD_WASAPIRecordMembers + { + bool mRecordStereoToMono; + IAudioClient *mCaptureAudioClient; + IAudioCaptureClient *mCaptureClient; + UINT mCaptureBufferLength; + BYTE *mRecordBuffer; + int mRecordBlockAlign; + int mRecordWritePosition; + } FMOD_WASAPIRecordMembers; + + class OutputWASAPI : public Output + { + DECLARE_MEMORYTRACKER_NONVIRTUAL + + private: + + /* + General variables + */ + bool mCoInitialized; + bool mInitialised; + bool mExclusiveMode; + + /* + Render variables + */ + int mNumRenderDrivers; + FMOD_WASAPIDriver mRenderDrivers[FMOD_OUTPUT_MAXDRIVERS]; + IAudioClient *mRenderAudioClient; + IAudioRenderClient *mRenderClient; + UINT mRenderBufferLength; + int mRenderBlockAlign; + FMOD_SOUND_FORMAT mRenderFormat; + int mRenderChannels; + HANDLE mFeederEventHandle; + bool mFeederThreadElevated; + Thread mFeederThread; + unsigned int mFeederTimeout; + Thread mMixerThread; + BYTE *mMixerBuffer; + int mMixerBufferLength; + UINT mMixerBufferBlockSize; + int mMixerBufferNumBlocks; + int mMixerReadPosition; + int mMixerWritePosition; + DSPResampler *mMixerResamplerDSP; + float *mMixerFormatConversionBuffer; + unsigned int mMixerFormatConversionBufferSizeBytes; + float *mMixerChannelConversionBuffer; + unsigned int mMixerChannelConversionBufferSizeBytes; + + /* + Capture variables + */ + int mNumCaptureDrivers; + FMOD_WASAPIDriver mCaptureDrivers[FMOD_OUTPUT_MAXDRIVERS]; +#ifdef FMOD_SUPPORT_RECORDING + HANDLE mRecordEventHandle; + bool mRecordThreadElevated; + Thread mRecordThread; + + bool mRecordInitialised; +#endif + + /* + Private methods + */ + FMOD_RESULT enumerateDefaultDevices (IMMDeviceEnumerator *pEnumerator); + FMOD_RESULT enumerate (); + FMOD_RESULT parseUUIDString (WCHAR *srcString, FMOD_GUID *destGUID); + FMOD_RESULT feederUpdate (); + FMOD_RESULT mixerUpdate (); + FMOD_RESULT channelConvert (float *outbuffer, float *inbuffer, int outchannels, int inchannels, int length); + FMOD_RESULT cleanUpEnumeration (); + +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT recordUpdate (); +#endif + + static void feederThreadCallback (void *userdata); + static void mixerThreadCallback (void *userdata); + +#ifdef FMOD_SUPPORT_RECORDING + static void recordThreadCallback (void *userdata); +#endif + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT getNumDrivers (int *numdrivers); + FMOD_RESULT getDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverInfoW (int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverCapsEx (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode); + FMOD_RESULT initEx (int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata); + FMOD_RESULT close (); + FMOD_RESULT getHandle (void **handle); + FMOD_RESULT start (); + FMOD_RESULT stop (); + FMOD_RESULT mixerResampleRead (float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT recordGetNumDrivers (int *numdrivers); + FMOD_RESULT recordGetDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordGetDriverInfoW (int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordGetDriverCaps (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency); + FMOD_RESULT recordStart (FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop); + FMOD_RESULT recordStop (FMOD_RECORDING_INFO *recordinfo); + FMOD_RESULT recordGetPosition (FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + FMOD_RESULT recordLock (FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); +#endif + + static FMOD_RESULT F_CALLBACK getNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverInfoCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK getDriverInfoWCallback (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK getDriverCapsExCallback (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode); + static FMOD_RESULT F_CALLBACK initExCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, FMOD_SPEAKERMODE *speakermode, int dspbufferlength, int dspnumbuffers, int max2dchannels, int max3dchannels, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK startCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK stopCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + static FMOD_RESULT F_CALLBACK mixerResampleReadCallback (FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + +#ifdef FMOD_SUPPORT_RECORDING + static FMOD_RESULT F_CALLBACK recordGetNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoWCallback (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordGetDriverCapsCallback (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency); + static FMOD_RESULT F_CALLBACK recordStartCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop); + static FMOD_RESULT F_CALLBACK recordStopCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo); + static FMOD_RESULT F_CALLBACK recordGetPositionCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK recordLockCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); +#endif + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + static FMOD_RESULT F_CALLBACK getMemoryUsedCallback (FMOD_OUTPUT_STATE *output, MemoryTracker *tracker); +#endif + }; +} + +#endif /* FMOD_SUPPORT_WASAPI */ + +#endif /* _FMOD_OUTPUT_WASAPI_H */ \ No newline at end of file diff --git a/win32/src/fmod_output_winmm.cpp b/win32/src/fmod_output_winmm.cpp new file mode 100755 index 0000000..202e18d --- /dev/null +++ b/win32/src/fmod_output_winmm.cpp @@ -0,0 +1,1726 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WINMM + +#include "fmod.h" +#include "fmod_codec_wav.h" +#include "fmod_downmix.h" +#include "fmod_output_winmm.h" +#include "fmod_memory.h" +#include "fmod_soundi.h" +#include "fmod_stringw.h" +#include "fmod_systemi.h" + +#include <windows.h> +#include <mmsystem.h> + +namespace FMOD +{ + +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 } }; +static const FMOD_GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 } }; + +FMOD_OUTPUT_DESCRIPTION_EX winmmoutput; + +#ifdef PLUGIN_EXPORTS + +#ifdef __cplusplus +extern "C" { +#endif + + /* + FMODGetOutputDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. + Must be declared with F_API to make it export as stdcall. + */ + F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx() + { + return OutputWinMM::getDescriptionEx(); + } + +#ifdef __cplusplus +} +#endif + +#endif /* PLUGIN_EXPORTS */ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_OUTPUT_DESCRIPTION_EX *OutputWinMM::getDescriptionEx() +{ + FMOD_memset(&winmmoutput, 0, sizeof(FMOD_OUTPUT_DESCRIPTION_EX)); + + winmmoutput.name = "FMOD WinMM Output"; + winmmoutput.version = 0x00010100; + winmmoutput.polling = true; + winmmoutput.mType = FMOD_OUTPUTTYPE_WINMM; + winmmoutput.mSize = sizeof(OutputWinMM); + + winmmoutput.getnumdrivers = &OutputWinMM::getNumDriversCallback; + winmmoutput.getdriverinfo = &OutputWinMM::getDriverInfoCallback; + winmmoutput.getdriverinfow = &OutputWinMM::getDriverInfoWCallback; + winmmoutput.getdrivercaps = &OutputWinMM::getDriverCapsCallback; + winmmoutput.init = &OutputWinMM::initCallback; + winmmoutput.close = &OutputWinMM::closeCallback; + winmmoutput.start = &OutputWinMM::startCallback; + winmmoutput.stop = &OutputWinMM::stopCallback; + winmmoutput.gethandle = &OutputWinMM::getHandleCallback; + winmmoutput.getposition = &OutputWinMM::getPositionCallback; + winmmoutput.lock = &OutputWinMM::lockCallback; + +#ifdef FMOD_SUPPORT_RECORDING + winmmoutput.record_getnumdrivers = &OutputWinMM::recordGetNumDriversCallback; + winmmoutput.record_getdriverinfo = &OutputWinMM::recordGetDriverInfoCallback; + winmmoutput.record_getdriverinfow = &OutputWinMM::recordGetDriverInfoWCallback; + winmmoutput.record_start = &OutputWinMM::recordStartCallback; + winmmoutput.record_stop = &OutputWinMM::recordStopCallback; + winmmoutput.record_getposition = &OutputWinMM::recordGetPositionCallback; + winmmoutput.record_lock = &OutputWinMM::recordLockCallback; + winmmoutput.record_unlock = 0; +#endif + + return &winmmoutput; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getNumDrivers(int *numdrivers) +{ + *numdrivers = waveOutGetNumDevs(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + WAVEOUTCAPSA waveoutcaps; + + if (name && namelen >= 1) + { + waveOutGetDevCapsA(id, &waveoutcaps, sizeof(waveoutcaps)); + + FMOD_strncpy(name, waveoutcaps.szPname, namelen); + name[namelen - 1] = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + WAVEOUTCAPSW waveoutcaps; + + if (name && namelen >= 1) + { + waveOutGetDevCapsW(id, &waveoutcaps, sizeof(waveoutcaps)); + + FMOD_strncpyW(name, (short *)waveoutcaps.szPname, namelen); + name[namelen - 1] = 0; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::testFormat(int id, FMOD_SOUND_FORMAT format, int channels) +{ + HWAVEOUT handle; + WAVE_FORMATEXTENSIBLE pcmwf; + UINT hr; + int bits; + + SoundI::getBitsFromFormat(format, &bits); + + /* + Initialize waveout. + */ + FMOD_memset(&pcmwf,0,sizeof(WAVE_FORMATEXTENSIBLE)); + + if (bits > 16 || channels > 2) + { + pcmwf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + } + else + { + pcmwf.Format.wFormatTag = WAVE_FORMAT_PCM; + } + + pcmwf.Format.nChannels = channels; + pcmwf.Format.wBitsPerSample = bits; + pcmwf.Format.nBlockAlign = pcmwf.Format.nChannels * pcmwf.Format.wBitsPerSample / 8; + pcmwf.Format.nSamplesPerSec = 44100; + pcmwf.Format.nAvgBytesPerSec = pcmwf.Format.nSamplesPerSec * pcmwf.Format.nBlockAlign; + + if (pcmwf.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + pcmwf.Format.cbSize = 22; + pcmwf.Samples.wValidBitsPerSample = bits; + pcmwf.dwChannelMask = 0; /* FIXME - this should be set according to mulchaud.rtf */ + if (format == FMOD_SOUND_FORMAT_PCMFLOAT) + { + FMOD_memcpy(&pcmwf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); + } + else + { + FMOD_memcpy(&pcmwf.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)); + } + } + + hr = waveOutOpen(&handle, id, (WAVEFORMATEX *)&pcmwf.Format, 0, 0, 0); + + waveOutClose(handle); + + if (hr) + { + return FMOD_ERR_FORMAT; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getDriverCaps(int id, FMOD_CAPS *caps) +{ + if (testFormat(id, FMOD_SOUND_FORMAT_PCM16, 8) == FMOD_OK) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_MULTICHANNEL); + } + if (testFormat(id, FMOD_SOUND_FORMAT_PCM8, 2) == FMOD_OK) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM8); + } + if (testFormat(id, FMOD_SOUND_FORMAT_PCM16, 2) == FMOD_OK) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM16); + } + if (testFormat(id, FMOD_SOUND_FORMAT_PCM24, 2) == FMOD_OK) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM24); + } + if (testFormat(id, FMOD_SOUND_FORMAT_PCM32, 2) == FMOD_OK) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCM32); + } + if (testFormat(id, FMOD_SOUND_FORMAT_PCMFLOAT, 2) == FMOD_OK) + { + *caps = (FMOD_CAPS)(*caps | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + WAVE_FORMATEXTENSIBLE pcmwf; + int bits; + UINT hr; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Initializing.\n")); + + if (!mCoInitialized) + { + hr = CoInitialize(NULL); + if (hr == S_OK || S_FALSE) + { + mCoInitialized = true; + } + } + + SoundI::getBitsFromFormat(*outputformat, &bits); + + /* + Initialize waveout. + */ + FMOD_memset(&pcmwf,0,sizeof(WAVE_FORMATEXTENSIBLE)); + if (bits > 16 || outputchannels > 2) + { + pcmwf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + } + else + { + pcmwf.Format.wFormatTag = WAVE_FORMAT_PCM; + } + + pcmwf.Format.nChannels = outputchannels; + pcmwf.Format.wBitsPerSample = bits; + pcmwf.Format.nBlockAlign = pcmwf.Format.nChannels * pcmwf.Format.wBitsPerSample / 8; + pcmwf.Format.nSamplesPerSec = *outputrate; + pcmwf.Format.nAvgBytesPerSec = pcmwf.Format.nSamplesPerSec * pcmwf.Format.nBlockAlign; + + if (pcmwf.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + pcmwf.Format.cbSize = 22; + pcmwf.Samples.wValidBitsPerSample = bits; + pcmwf.dwChannelMask = 0; /* FIXME - this should be set according to mulchaud.rtf */ + if (*outputformat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + FMOD_memcpy(&pcmwf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); + } + else + { + FMOD_memcpy(&pcmwf.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)); + } + } + + hr = waveOutOpen(&mHandle, selecteddriver, (WAVEFORMATEX *)&pcmwf.Format, 0, 0, 0); + if (hr) + { +#ifdef FMOD_DEBUG + switch (hr) + { + case MMSYSERR_BADDEVICEID : + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Specified device ID is out of range.\n")); + break; + case MMSYSERR_ALLOCATED : + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Specified resource is already allocated.\n")); + break; + case MMSYSERR_NOMEM : + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Unable to allocate or lock memory.\n")); + break; + case WAVERR_BADFORMAT : + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Attempted to open with an unsupported wave format.\n")); + break; + case WAVERR_SYNC : + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Attempted to open a synchronous driver without specifying the WAVE_ALLOWSYNC flag.\n")); + break; + default : + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Unknown Error trying to open wave device!.\n")); + break; + }; +#endif + if (hr == WAVERR_BADFORMAT) + { + return FMOD_ERR_FORMAT; + } + if (hr == MMSYSERR_ALLOCATED) + { + return FMOD_ERR_OUTPUT_ALLOCATED; + } + + return FMOD_ERR_OUTPUT_INIT; + } + else + { + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Opened wave device successfully\n")); + } + + + /* + CREATE AND START LOOPING WAVEOUT BLOCK + */ + { + FMOD_RESULT result; + WAVEHDR *wavehdr; + HRESULT hr; + int count, loops; + unsigned int bufferlength; + int numbuffers; + unsigned int mode; + + result = mSystem->getDSPBufferSize(&bufferlength, &numbuffers); + if (result != FMOD_OK) + { + return result; + } + + mode = WHDR_BEGINLOOP | WHDR_ENDLOOP; + SoundI::getBytesFromSamples(bufferlength * numbuffers, &mBlockLengthBytes, outputchannels, *outputformat); + loops = -1; + mNumBlocks = 1; + + mBuffer = (char *)FMOD_Memory_Calloc(mBlockLengthBytes); + if (!mBuffer) + { + return FMOD_ERR_MEMORY; + } + + /* + Allocate and set up waveout blocks + */ + for (count = 0; count < mNumBlocks; count++) + { + wavehdr = &mBlock[count].wavehdr; + + mBlock[count].data = (char *)mBuffer + (mBlockLengthBytes / mNumBlocks * count); + + wavehdr->dwFlags = mode; + wavehdr->lpData = (LPSTR)mBlock[count].data; + wavehdr->dwBufferLength = mBlockLengthBytes / mNumBlocks; + wavehdr->dwBytesRecorded = 0; + wavehdr->dwUser = count; + wavehdr->dwLoops = loops; + + hr = waveOutPrepareHeader(mHandle, wavehdr, sizeof(WAVEHDR)); + if (hr) + { + mRunning = false; + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + } + } + + FLOG((FMOD_DEBUG_LEVEL_ALL, __FILE__, __LINE__, "OutputWinMM::init", "Done.\n")); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::close() +{ + int count; + + Plugin::init(); /* Set up gGlobal - for debug / file / memory access by this plugin. */ + + mRunning = false; + + if (mNumBlocks) + { + for (count = 0; count < mNumBlocks; count++) + { + waveOutUnprepareHeader(mHandle, &mBlock[count].wavehdr, sizeof(WAVEHDR)); + mBlock[count].wavehdr.dwFlags &= ~WHDR_PREPARED; + mBlock[count].data = NULL; + } + mNumBlocks = 0; + } + + if (mBuffer) + { + FMOD_Memory_Free(mBuffer); + } + mBuffer = NULL; + + waveOutClose(mHandle); + + if (mCoInitialized) + { + CoUninitialize(); + mCoInitialized = false; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getHandle(void **handle) +{ + *handle = mHandle; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::start() +{ + FMOD_RESULT result; + MMRESULT hr; + WAVEHDR *wavehdr; + int count; + int totalblocks; + + result = OutputPolled::start(); + if (result != FMOD_OK) + { + return result; + } + + totalblocks = mNumBlocks; + + mRunning = true; + + for (count=0; count < totalblocks; count++) + { + wavehdr = &mBlock[count].wavehdr; + + hr = waveOutWrite(mHandle, wavehdr, sizeof(WAVEHDR)); + if (hr) + { + mRunning = false; + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + } + + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::stop() +{ + FMOD_RESULT result; + + result = OutputPolled::stop(); + if (result != FMOD_OK) + { + return result; + } + + mRunning = false; + + waveOutReset(mHandle); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getPosition(unsigned int *pcm) +{ + FMOD_RESULT result; + MMTIME mmt; + int bits; + int outputrate; + int outputchannels; + FMOD_SOUND_FORMAT outputformat; + + mmt.wType = TIME_BYTES; + waveOutGetPosition(mHandle, &mmt, sizeof(MMTIME)); + + /* + Get the values FMOD had set for rate, format, channels. + */ + result = mSystem->getSoftwareFormat(&outputrate, &outputformat, &outputchannels, 0, 0, &bits); + if (result != FMOD_OK) + { + return result; + } + + if (mSystem->mDownmix) + { + mSystem->mDownmix->getOutputChannels(&outputchannels); + } + + mmt.u.cb = mmt.u.cb * 8 / bits; + mmt.u.cb /= outputchannels; + + *pcm = mmt.u.cb; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + offset %= mBlockLengthBytes; + + if (offset + length > mBlockLengthBytes) + { + *ptr1 = (char *)mBuffer + offset; + *ptr2 = (char *)mBuffer; + *len1 = mBlockLengthBytes - offset; + *len2 = length - (mBlockLengthBytes - offset); + } + else + { + *ptr1 = (char *)mBuffer + offset; + *ptr2 = NULL; + *len1 = length; + *len2 = 0; + } + + return FMOD_OK; +} + + + +#ifdef FMOD_SUPPORT_RECORDING +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordThread() +{ + if (!mRecording) + { + return FMOD_OK; + } + + while (mRecordCurrentBlock < mRecordNextBlock) + { + unsigned int fillblock; + int lastblock; + + fillblock = mRecordCurrentBlock % RECORD_MAXBLOCKS; + + waveInUnprepareHeader(mRecordHandle, &mRecordBlock[fillblock].wavehdr, sizeof(WAVEHDR)); + + /* + Prepare the last block to be added. It needs to be at least a triple buffer for this to work. + */ + lastblock = fillblock - 1; + if (lastblock < 0) + { + lastblock = RECORD_MAXBLOCKS - 1; + } + + waveInPrepareHeader(mRecordHandle, &mRecordBlock[lastblock].wavehdr, sizeof(WAVEHDR)); + waveInAddBuffer (mRecordHandle, &mRecordBlock[lastblock].wavehdr, sizeof(WAVEHDR)); + + mRecordCurrentBlock++; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +] +*/ +void OutputWinMM::recordThreadCallback(void *data) +{ + OutputWinMM *winmm = (OutputWinMM *)data; + + winmm->recordThread(); +} + + +/* + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [SEE_ALSO] +*/ +void CALLBACK OutputWinMM::recordCallback(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) +{ + OutputWinMM *winmm = (OutputWinMM *)dwInstance; + + if (!winmm->mRecording) + { + return; + } + + if (uMsg == WIM_DATA) + { + winmm->mRecordNextBlock++; + winmm->mRecordThread.wakeupThread(); + } +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordGetNumDrivers(int *numdrivers) +{ + if (!numdrivers) + { + return FMOD_ERR_INVALID_PARAM; + } + + *numdrivers = waveInGetNumDevs(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + WinMM does not provide GUIDs + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid) +{ + WAVEINCAPSA waveincaps; + + if (name && namelen >= 1) + { + waveInGetDevCapsA(id, &waveincaps, sizeof(waveincaps)); + + FMOD_strncpy(name, waveincaps.szPname, namelen - 1); + name[namelen - 1] = 0; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + WinMM does not provide GUIDs + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordGetDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid) +{ + WAVEINCAPSW waveincaps; + + if (name && namelen >= 1) + { + waveInGetDevCapsW(id, &waveincaps, sizeof(waveincaps)); + + FMOD_strncpyW(name, (short *)waveincaps.szPname, namelen - 1); + name[namelen - 1] = 0; + } + + return FMOD_OK; +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordStart(FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop) +{ + FMOD_RESULT result; + MMRESULT hr; + WAVE_FORMATEXTENSIBLE wfx; + WAVEHDR *wavehdr; + FMOD_MODE mode; + int count, bits; + SoundI *soundi; + + soundi = (SoundI *)sound; + if (!soundi) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (soundi->mLength < 1) + { + return FMOD_ERR_INVALID_PARAM; + } + + /* + No multi recording with WinMM + */ + if (mRecordNumActive) + { + return FMOD_ERR_UNSUPPORTED; + } + + + // ======================================================================================================== + // CREATE AND START CAPTURE BUFFER + // ======================================================================================================== + + recordinfo->mRecordFormat = soundi->mFormat; + recordinfo->mRecordChannels = soundi->mChannels; + mode = soundi->mMode; + recordinfo->mRecordRate = (int)soundi->mDefaultFrequency; + + SoundI::getBitsFromFormat(recordinfo->mRecordFormat, &bits); + + FMOD_memset(&wfx, 0, sizeof(WAVE_FORMATEXTENSIBLE)); + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfx.Format.nChannels = recordinfo->mRecordChannels; + wfx.Format.wBitsPerSample = bits; + wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8; + wfx.Format.nSamplesPerSec = recordinfo->mRecordRate; + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Format.cbSize = 22; // Designates extra data + wfx.Samples.wValidBitsPerSample = bits; + FMOD_memcpy(&wfx.SubFormat, (soundi->mFormat == FMOD_SOUND_FORMAT_PCMFLOAT ? &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : &KSDATAFORMAT_SUBTYPE_PCM), sizeof(GUID)); + + mRecordBlockAlign = wfx.Format.nBlockAlign; + + mRecordBlockLenBytes = RECORD_BLOCKLENMS; + mRecordBlockLenBytes *= recordinfo->mRecordRate; + mRecordBlockLenBytes /= 1000; + + recordinfo->mRecordBufferLength = mRecordBlockLenBytes * RECORD_MAXBLOCKS; + + mRecordBlockLenBytes *= mRecordBlockAlign; + + hr = waveInOpen(&mRecordHandle, recordinfo->mRecordId, (WAVEFORMATEX*)&wfx, (unsigned long)&recordCallback, (DWORD)this, CALLBACK_FUNCTION); + if (hr) + { + switch (hr) + { + case MMSYSERR_BADDEVICEID : + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWinMM::recordStart", "Specified device ID is out of range.\n")); + break; + case MMSYSERR_ALLOCATED : + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWinMM::recordStart", "Specified resource is already allocated.\n")); + break; + case MMSYSERR_NOMEM : + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWinMM::recordStart", "Unable to allocate or lock memory.\n")); + break; + case WAVERR_BADFORMAT : + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWinMM::recordStart", "Attempted to open with an unsupported wave format.\n")); + break; + case WAVERR_SYNC : + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWinMM::recordStart", "Attempted to open a synchronous driver without specifying the WAVE_ALLOWSYNC flag.\n")); + break; + default : + FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputWinMM::recordStart", "Unknown Error trying to open wave device!.")); + break; + }; + + if (hr == MMSYSERR_ALLOCATED) + { + return FMOD_ERR_OUTPUT_ALLOCATED; + } + else if (hr == WAVERR_BADFORMAT) + { + return FMOD_ERR_OUTPUT_FORMAT; + } + else + { + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + } + + /* + Allocate and set up wavein blocks + */ + + mRecordData = (char *)FMOD_Memory_Calloc(mRecordBlockLenBytes * RECORD_MAXBLOCKS); + + for (count=0; count < RECORD_MAXBLOCKS; count++) + { + wavehdr = &mRecordBlock[count].wavehdr; + + mRecordBlock[count].data = mRecordData + (mRecordBlockLenBytes * count); + + wavehdr->dwFlags = 0; + wavehdr->lpData = (LPSTR)mRecordBlock[count].data; + wavehdr->dwBufferLength = mRecordBlockLenBytes; + wavehdr->dwBytesRecorded = mRecordBlockLenBytes; + wavehdr->dwUser = count; + wavehdr->dwLoops = 0; + } + + for (count=0; count < RECORD_MAXBLOCKS - 1; count++) + { + wavehdr = &mRecordBlock[count].wavehdr; + + hr = waveInPrepareHeader(mRecordHandle, wavehdr, sizeof(WAVEHDR)); + if (hr) + { + mRecording = false; + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + hr = waveInAddBuffer(mRecordHandle, wavehdr, sizeof(WAVEHDR)); + if (hr) + { + mRecording = false; + return FMOD_ERR_OUTPUT_CREATEBUFFER; + } + } + + mRecordCurrentBlock = 0; + mRecordNextBlock = 0; + mRecording = true; + + result = mRecordThread.initThread("FMOD Record thread", recordThreadCallback, this, Thread::PRIORITY_CRITICAL, 0, 0, true, 0, mSystem); + if (result != FMOD_OK) + { + return result; + } + + hr = waveInStart(mRecordHandle); + if (hr) + { + mRecording = false; + return FMOD_ERR_RECORD; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordStop(FMOD_RECORDING_INFO *recordinfo) +{ + int count; + + mRecording = FALSE; + + mRecordThread.closeThread(); + + waveInReset(mRecordHandle); + waveInClose(mRecordHandle); + + for (count = 0; count < RECORD_MAXBLOCKS; count++) + { + waveInUnprepareHeader(mRecordHandle, &mRecordBlock[count].wavehdr, sizeof(WAVEHDR)); + mRecordBlock[count].data = NULL; + } + + if (mRecordData) + { + FMOD_Memory_Free(mRecordData); + mRecordData = NULL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordGetPosition(FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + int currblock; + + if (!pcm) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (!mRecordBlockAlign) + { + return FMOD_ERR_INTERNAL; + } + + currblock = mRecordCurrentBlock % RECORD_MAXBLOCKS; + + *pcm = currblock * mRecordBlockLenBytes / mRecordBlockAlign; + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::recordLock(FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + unsigned int lenbytes = mRecordBlockLenBytes * RECORD_MAXBLOCKS; + + /* + Cant lock more than the sample length + */ + if (length > lenbytes) + { + length = lenbytes; + } + + if (offset >= lenbytes || offset < 0 || length < 0) + { + *ptr1 = NULL; + *ptr2 = NULL; + *len1 = 0; + *len2 = 0; + return FMOD_ERR_INVALID_PARAM; + } + + /* + If it is not a wrapping lock just fill out ptr1 + */ + if (offset + length <= lenbytes) + { + *ptr1 = ((char *)mRecordData + offset); + *len1 = length; + *ptr2 = NULL; + *len2 = 0; + } + + /* + Otherwise return wrapped pointers in pt1 and ptr2 + */ + else + { + *ptr1 = ((char *)mRecordData + offset); + *len1 = lenbytes - offset; + *ptr2 = mRecordData; + *len2 = length - (lenbytes - offset); + } + + return FMOD_OK; +} +#endif + + +/* + ============================================================================================================== + + CALLBACK INTERFACE + + ============================================================================================================== +*/ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->getNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::getDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->getDriverInfo(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::getDriverInfoWCallback(FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->getDriverInfoW(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT OutputWinMM::getDriverCapsCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->getDriverCaps(id, caps); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::closeCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->close(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::startCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->start(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::stopCallback(FMOD_OUTPUT_STATE *output) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->stop(); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->getHandle(handle); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::getPositionCallback(FMOD_OUTPUT_STATE *output, unsigned int *pcm) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->getPosition(pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::lockCallback(FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->lock(offset, length, ptr1, ptr2, len1, len2); +} + +#ifdef FMOD_SUPPORT_RECORDING +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordGetNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordGetNumDrivers(numdrivers); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordGetDriverInfoCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordGetDriverInfo(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordGetDriverInfoWCallback(FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordGetDriverInfoW(id, name, namelen, guid); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordStartCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordStart(recordinfo, (Sound *)sound, loop ? true : false); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordStopCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordStop(recordinfo); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordGetPositionCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordGetPosition(recordinfo, pcm); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT F_CALLBACK OutputWinMM::recordLockCallback(FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + OutputWinMM *winmm = (OutputWinMM *)output; + + return winmm->recordLock(recordinfo, offset, length, ptr1, ptr2, len1, len2); +} +#endif + +} + +#endif /* #ifdef FMOD_SUPPORT_WINMM */ diff --git a/win32/src/fmod_output_winmm.h b/win32/src/fmod_output_winmm.h new file mode 100755 index 0000000..ee729e7 --- /dev/null +++ b/win32/src/fmod_output_winmm.h @@ -0,0 +1,116 @@ +#ifndef _FMOD_OUTPUT_WINMM_H +#define _FMOD_OUTPUT_WINMM_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WINMM + +#include "fmod_output_polled.h" + +#include <windows.h> +#include <mmsystem.h> + +namespace FMOD +{ + const int PLAY_MAXBLOCKS = 1; /* number of buffers */ + const int RECORD_MAXBLOCKS = 100; /* number of buffers */ + const int RECORD_BLOCKLENMS = 5; /* size of each buffer - 100 * 5 = 500ms with 5ms granularity */ + + typedef struct + { + WAVEHDR wavehdr; + char *data; + } SoundBlock; + + class OutputWinMM : public OutputPolled + { + private: + + bool mCoInitialized; + + /* + Playback information. + */ + HWAVEOUT mHandle; + bool mRunning; + char *mBuffer; + SoundBlock mBlock[PLAY_MAXBLOCKS]; + int mNumBlocks; + unsigned int mBlockLengthBytes; + + /* + Record information. + */ +#ifdef FMOD_SUPPORT_RECORDING + HWAVEIN mRecordHandle; + Thread mRecordThread; + bool mRecording; + int mRecordCurrentBlock; + int mRecordNextBlock; + SoundBlock mRecordBlock[RECORD_MAXBLOCKS]; + char *mRecordData; + unsigned int mRecordBlockLenBytes; + int mRecordBlockAlign; + + static void CALLBACK recordCallback(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + static void recordThreadCallback(void *userdata); + FMOD_RESULT recordThread(); +#endif + + FMOD_RESULT testFormat(int id, FMOD_SOUND_FORMAT format, int channels); + + public: + + static FMOD_OUTPUT_DESCRIPTION_EX *getDescriptionEx(); + + FMOD_RESULT enumerate(); + FMOD_RESULT getNumDrivers(int *numdrivers); + FMOD_RESULT getDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT getDriverCaps(int id, FMOD_CAPS *caps); + FMOD_RESULT init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbuffersize, int dspnumbuffers, void *extradriverdata); + FMOD_RESULT close(); + FMOD_RESULT getHandle(void **handle); + FMOD_RESULT start(); + FMOD_RESULT stop(); + FMOD_RESULT getPosition(unsigned int *pcm); + FMOD_RESULT lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + +#ifdef FMOD_SUPPORT_RECORDING + FMOD_RESULT recordGetNumDrivers(int *numdrivers); + FMOD_RESULT recordGetDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordGetDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid); + FMOD_RESULT recordStart (FMOD_RECORDING_INFO *recordinfo, Sound *sound, bool loop); + FMOD_RESULT recordStop (FMOD_RECORDING_INFO *recordinfo); + FMOD_RESULT recordGetPosition (FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + FMOD_RESULT recordLock (FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); +#endif + + static FMOD_RESULT F_CALLBACK getNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK getDriverInfoCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK getDriverInfoWCallback (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK getDriverCapsCallback (FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps); + static FMOD_RESULT F_CALLBACK initCallback (FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata); + static FMOD_RESULT F_CALLBACK closeCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK startCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK stopCallback (FMOD_OUTPUT_STATE *output); + static FMOD_RESULT F_CALLBACK getHandleCallback (FMOD_OUTPUT_STATE *output, void **handle); + static FMOD_RESULT F_CALLBACK getPositionCallback (FMOD_OUTPUT_STATE *output, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK lockCallback (FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + static FMOD_RESULT F_CALLBACK unlockCallback (FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); +#ifdef FMOD_SUPPORT_RECORDING + static FMOD_RESULT F_CALLBACK recordGetNumDriversCallback (FMOD_OUTPUT_STATE *output, int *numdrivers); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoCallback (FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordGetDriverInfoWCallback (FMOD_OUTPUT_STATE *output, int id, short *name, int namelen, FMOD_GUID *guid); + static FMOD_RESULT F_CALLBACK recordStartCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, FMOD_SOUND *sound, int loop); + static FMOD_RESULT F_CALLBACK recordStopCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo); + static FMOD_RESULT F_CALLBACK recordGetPositionCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int *pcm); + static FMOD_RESULT F_CALLBACK recordLockCallback (FMOD_OUTPUT_STATE *output, FMOD_RECORDING_INFO *recordinfo, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); +#endif + }; +} + +#endif /* #ifdef FMOD_SUPPORT_WINMM */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_sample_dsound.cpp b/win32/src/fmod_sample_dsound.cpp new file mode 100755 index 0000000..5203055 --- /dev/null +++ b/win32/src/fmod_sample_dsound.cpp @@ -0,0 +1,256 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSOUND + +#include "fmod.h" +#include "fmod_output_dsound.h" +#include "fmod_sample_dsound.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +SampleDSound::SampleDSound() +{ + mBuffer = 0; + mBuffer3D = 0; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleDSound::release(bool freethis) +{ + FMOD_RESULT result; + + if (!mSystem) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (mFlags & FMOD_SOUND_FLAG_PRELOADEDFSB) + { + return FMOD_ERR_PRELOADED; + } + + if (mFlags & FMOD_SOUND_FLAG_PROGRAMMERSOUND) + { + return FMOD_ERR_PROGRAMMERSOUND; + } + + while (mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_ERROR) + { + FMOD_OS_Time_Sleep(2); + } + + result = mSystem->stopSound(this); + if (result != FMOD_OK) + { + return result; + } + + /* + Free sample's secondary buffer + */ + if (mBuffer) + { + if (mOutput) + { + mOutput->mBufferMemoryCurrent -= mLengthBytes; + } + + mBuffer->Release(); + mBuffer = 0; + } + + if(mBuffer3D) + { + mBuffer3D->Release(); + mBuffer3D = 0; + } + + return Sample::release(freethis); +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleDSound::lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + HRESULT hr; + + if (!mBuffer) + { + return FMOD_ERR_INVALID_PARAM; + } + + hr = mBuffer->Lock(offset, length, ptr1, (DWORD *)len1, ptr2, (DWORD *)len2, 0); /* not DSBLOCK_FROMWRITECURSOR OR DSBLOCK_ENTIREBUFFER */ + if (hr == E_INVALIDARG) + { + return FMOD_ERR_INVALID_PARAM; + } + else if (hr == DSBSTATUS_BUFFERLOST) + { + DWORD locflags = 0; + + if (!(mMode & FMOD_UNIQUE)) + { + if (mLOCSoftware) + { + locflags |= DSBPLAY_LOCSOFTWARE; + } + else + { + locflags |= DSBPLAY_LOCHARDWARE; + } + } + mBuffer->Restore(); + mBuffer->Play(0, 0, locflags | DSBPLAY_LOOPING); + } + else if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + + } + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + FMOD_OK + + [REMARKS] + + [PLATFORMS] + Win32, Win64 + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleDSound::unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + HRESULT hr; + + if (!mBuffer) + { + return FMOD_ERR_INVALID_PARAM; + } + + if (mFormat == FMOD_SOUND_FORMAT_PCM8) + { + if (ptr1 && len1) + { + unsigned int count; + unsigned char *destptr = (unsigned char *)ptr1; + + for (count = 0; count < len1; count++) + { + *destptr++ ^= 128; + } + } + if (ptr2 && len2) + { + unsigned int count; + unsigned char *destptr = (unsigned char *)ptr2; + + for (count = 0; count < len2; count++) + { + *destptr++ ^= 128; + } + } + } + + hr = mBuffer->Unlock(ptr1, len1, ptr2, len2); + if (hr != DS_OK) + { + return FMOD_ERR_OUTPUT_DRIVERCALL; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ + +#ifdef FMOD_SUPPORT_MEMORYTRACKER + +FMOD_RESULT SampleDSound::getMemoryUsedImpl(MemoryTracker *tracker) +{ + tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(SampleDSound) - sizeof(Sample)); // only the SampleDSound members... + + return Sample::getMemoryUsedImpl(tracker); // all the base class members +} + +#endif + +} + +#endif /* FMOD_SUPPORT_DSOUND */ diff --git a/win32/src/fmod_sample_dsound.h b/win32/src/fmod_sample_dsound.h new file mode 100755 index 0000000..345cf81 --- /dev/null +++ b/win32/src/fmod_sample_dsound.h @@ -0,0 +1,52 @@ +#ifndef _FMOD_SAMPLE_DSOUND_H +#define _FMOD_SAMPLE_DSOUND_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_DSOUND + +#include "fmod_memory.h" +#include "fmod_soundi.h" +#include "fmod_sound_sample.h" + +#ifndef _FMOD_MEMORYTRACKER_H +#include "fmod_memorytracker.h" +#endif + +struct IDirectSoundBuffer8; +struct IDirectSound3DBuffer; +struct OutputDSound; + +namespace FMOD +{ + class SampleDSound : public Sample + { + DECLARE_MEMORYTRACKER + + friend class ChannelDSound; + friend class OutputDSound; + + #ifdef FMOD_SUPPORT_OPENAL + friend class OutputOpenAL; + #endif + + private: + + IDirectSoundBuffer8 *mBuffer; + IDirectSound3DBuffer *mBuffer3D; + OutputDSound *mOutput; + bool mLOCSoftware; + + public: + + SampleDSound(); + + FMOD_RESULT release(bool freethis = true); + FMOD_RESULT lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + }; +} + +#endif /* FMOD_SUPPORT_DSOUND */ + +#endif \ No newline at end of file diff --git a/win32/src/fmod_sample_openal.cpp b/win32/src/fmod_sample_openal.cpp new file mode 100755 index 0000000..9dba742 --- /dev/null +++ b/win32/src/fmod_sample_openal.cpp @@ -0,0 +1,503 @@ +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL + +#include "fmod_memory.h" +#include "fmod_output.h" +#include "fmod_output_software.h" +#include "fmod_sample_openal.h" +#include "fmod_systemi.h" + +namespace FMOD +{ + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +SampleOpenAL::SampleOpenAL() +{ + mBuffer = 0; + mBufferMemory = 0; + mDataEndCopied = false; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::release(bool freethis) +{ + FMOD_RESULT result; + + if (!mSystem) + { + return FMOD_ERR_UNINITIALIZED; + } + + if (mFlags & FMOD_SOUND_FLAG_PRELOADEDFSB) + { + return FMOD_ERR_PRELOADED; + } + + if (mFlags & FMOD_SOUND_FLAG_PROGRAMMERSOUND) + { + return FMOD_ERR_PROGRAMMERSOUND; + } + + while (mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_ERROR) + { + FMOD_OS_Time_Sleep(2); + } + + result = mSystem->stopSound(this); + if (result != FMOD_OK) + { + return result; + } + + if (mBufferMemory) + { + FMOD_Memory_Free(mBufferMemory); + mBufferMemory = 0; + } + + if (mLoopPointDataEnd && mLoopPointDataEnd != mLoopPointDataEndMemory) + { + FMOD_Memory_Free(mLoopPointDataEnd); + mLoopPointDataEnd = 0; + } + + mBuffer = 0; + + return Sample::release(freethis); +} + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::setMode(FMOD_MODE mode) +{ + FMOD_RESULT result; + + result = Sample::setMode(mode); + if (result != FMOD_OK) + { + return result; + } + + result = setLoopPointData(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) +{ + FMOD_RESULT result; + char *src = 0; + unsigned int overflowbytes, pointB; + + /* + If we're locking part of the sample that has been modified by the loop point stuff, restore the old data first. + */ + result = getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes); + if (result != FMOD_OK) + { + return result; + } + + result = getBytesFromSamples(mLoopStart + mLoopLength, &pointB); + if (result != FMOD_OK) + { + return result; + } + + if (offset >= pointB && offset < pointB + overflowbytes) + { + result = restoreLoopPointData(); + if (result != FMOD_OK) + { + return result; + } + } + + src = (char *)mBuffer; + + if (offset >= mLengthBytes || offset < 0 || length < 0) + { + *ptr1 = 0; + if (ptr2) + { + *ptr2 = 0; + } + *len1 = 0; + if (len2) + { + *len2 = 0; + } + return FMOD_ERR_INVALID_PARAM; + } + + if (offset + length <= mLengthBytes) + { + *ptr1 = src + offset; + *len1 = length; + if (ptr2) + { + *ptr2 = 0; + } + if (len2) + { + *len2 = 0; + } + } + /* + Otherwise return wrapped pointers in pt1 and ptr2 + */ + else + { + *ptr1 = src + offset; + *len1 = mLengthBytes - offset; + *ptr2 = src; + *len2 = length - (mLengthBytes - offset); + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) +{ + FMOD_RESULT result; + + result = setLoopPointData(); + if (result != FMOD_OK) + { + return result; + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::setBufferData(void *data) +{ + mBuffer = data; + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::setLoopPoints(unsigned int loopstart, unsigned int looplength) +{ + if (loopstart >= mLength || loopstart + looplength > mLength) + { + return FMOD_ERR_INVALID_PARAM; + } + + restoreLoopPointData(); + + mLoopStart = loopstart; + mLoopLength = looplength; + + setLoopPointData(); + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::setLoopPointData() +{ + FMOD_RESULT result; + unsigned int overflowbytes, pointA, pointB; + + if ((mFormat != FMOD_SOUND_FORMAT_PCM8 && + mFormat != FMOD_SOUND_FORMAT_PCM16 && + mFormat != FMOD_SOUND_FORMAT_PCM24 && + mFormat != FMOD_SOUND_FORMAT_PCM32 && + mFormat != FMOD_SOUND_FORMAT_PCMFLOAT) || !mLoopPointDataEnd) + { + return FMOD_OK; + } + + result = getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart, &pointA); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart + mLoopLength, &pointB); + if (result != FMOD_OK) + { + return result; + } + + if (mMode & FMOD_LOOP_BIDI) + { + int count; + + FMOD_memcpy(mLoopPointDataEnd, (char *)mBuffer + pointB, overflowbytes); /* backup the data first */ + mDataEndCopied = true; + + switch (mFormat) + { + case FMOD_SOUND_FORMAT_PCM8: + { + char *srcptr, *destptr; + + srcptr = destptr = (char *)mBuffer + pointB; + srcptr -= mChannels; + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + *destptr = *srcptr; + destptr++; + srcptr--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM16: + { + short *srcptr, *destptr; + + srcptr = destptr = (short *)((char *)mBuffer + pointB); + srcptr -= mChannels; + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + *destptr = *srcptr; + destptr++; + srcptr--; + } + break; + } + case FMOD_SOUND_FORMAT_PCM24: + { + char *srcptr, *destptr; + + srcptr = destptr = (char *)mBuffer + pointB; + srcptr -= (mChannels * 3); + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + destptr[0] = srcptr[0]; + destptr[1] = srcptr[1]; + destptr[2] = srcptr[2]; + destptr += 3; + srcptr -= 3; + } + break; + } + case FMOD_SOUND_FORMAT_PCM32: + case FMOD_SOUND_FORMAT_PCMFLOAT: + { + int *srcptr, *destptr; + + srcptr = destptr = (int *)((char *)mBuffer + pointB); + srcptr -= mChannels; + for (count=0; count < FMOD_DSP_RESAMPLER_OVERFLOWLENGTH * mChannels; count++) + { + *destptr = *srcptr; + destptr++; + srcptr--; + } + break; + } + default: + { + break; + } + } + } + else if (mMode & FMOD_LOOP_NORMAL) + { + FMOD_memcpy(mLoopPointDataEnd, (char *)mBuffer + pointB, overflowbytes); + mDataEndCopied = true; + + FMOD_memcpy((char *)mBuffer + pointB, (char *)mBuffer + pointA, overflowbytes); + } + else if (mMode & FMOD_LOOP_OFF) + { + if(mDataEndCopied) + { + FMOD_memcpy((char *)mBuffer + pointB, mLoopPointDataEnd,overflowbytes); + } + } + + return FMOD_OK; +} + + +/* +[ + [DESCRIPTION] + + [PARAMETERS] + + [RETURN_VALUE] + + [REMARKS] + + [PLATFORMS] + + [SEE_ALSO] +] +*/ +FMOD_RESULT SampleOpenAL::restoreLoopPointData() +{ + FMOD_RESULT result; + unsigned int overflowbytes, pointA, pointB; + + if ((mFormat != FMOD_SOUND_FORMAT_PCM8 && + mFormat != FMOD_SOUND_FORMAT_PCM16 && + mFormat != FMOD_SOUND_FORMAT_PCM24 && + mFormat != FMOD_SOUND_FORMAT_PCM32 && + mFormat != FMOD_SOUND_FORMAT_PCMFLOAT) || !mLoopPointDataEnd) + { + return FMOD_OK; + } + + result = getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowbytes); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart, &pointA); + if (result != FMOD_OK) + { + return result; + } + result = getBytesFromSamples(mLoopStart + mLoopLength, &pointB); + if (result != FMOD_OK) + { + return result; + } + + if(mDataEndCopied) + { + FMOD_memcpy((char *)mBuffer + pointB, mLoopPointDataEnd,overflowbytes); + } + + return FMOD_OK; +} + + +} + +#endif /* FMOD_SUPPORT_OPENAL */ diff --git a/win32/src/fmod_sample_openal.h b/win32/src/fmod_sample_openal.h new file mode 100755 index 0000000..43f1fb6 --- /dev/null +++ b/win32/src/fmod_sample_openal.h @@ -0,0 +1,45 @@ +#ifndef _FMOD_SAMPLE_OPENAL_H +#define _FMOD_SAMPLE_OPENAL_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_OPENAL + +#include "fmod_dsp_resampler.h" +#include "fmod_sound_sample.h" + +namespace FMOD +{ + class SampleOpenAL : public Sample + { + friend class OutputOpenAL; + friend class ChannelOpenAL; + friend class DSPWaveTable; + + private: + + void *mBuffer; + void *mBufferMemory; + char *mLoopPointDataEnd; + char mLoopPointDataEndMemory[8]; + bool mDataEndCopied; + + public: + + SampleOpenAL(); + + FMOD_RESULT release(bool freethis = true); + FMOD_RESULT lockInternal(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2); + FMOD_RESULT unlockInternal(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2); + FMOD_RESULT setBufferData(void *data); + + FMOD_RESULT setLoopPoints(unsigned int loopstart, unsigned int looplength); + FMOD_RESULT setLoopPointData(); + FMOD_RESULT restoreLoopPointData(); + FMOD_RESULT setMode(FMOD_MODE mode); + }; +} + +#endif /* FMOD_SUPPORT_OPENAL */ + +#endif \ No newline at end of file diff --git a/win32/src/resource.h b/win32/src/resource.h new file mode 100755 index 0000000..40aa8c3 --- /dev/null +++ b/win32/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by fmod.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win32/src/wasapi/fmod_audioclient.h b/win32/src/wasapi/fmod_audioclient.h new file mode 100755 index 0000000..75d14aa --- /dev/null +++ b/win32/src/wasapi/fmod_audioclient.h @@ -0,0 +1,109 @@ +#ifndef _FMOD_AUDIOCLIENT_H +#define _FMOD_AUDIOCLIENT_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WASAPI + +#include "fmod_codec_wav.h" + +// GUIDs +static const GUID IID_IAudioClock = { 0xcd63314f, 0x3fba, 0x4a1b, { 0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7 } }; +static const GUID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; +static const GUID IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; +static const GUID IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } }; +static const GUID KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; +static const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + +// Helper defines +typedef LONGLONG REFERENCE_TIME; +typedef const FMOD_GUID* LPCFMOD_GUID; +typedef enum +{ + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_SHAREMODE_EXCLUSIVE +} AUDCLNT_SHAREMODE; + +// Error defines +#define FACILITY_AUDCLNT 0x889 +#define AUDCLNT_ERR(n) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, n) +#define AUDCLNT_SUCCESS(n) MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_AUDCLNT, n) +#define AUDCLNT_E_NOT_INITIALIZED AUDCLNT_ERR(0x001) +#define AUDCLNT_E_ALREADY_INITIALIZED AUDCLNT_ERR(0x002) +#define AUDCLNT_E_WRONG_ENDPOINT_TYPE AUDCLNT_ERR(0x003) +#define AUDCLNT_E_DEVICE_INVALIDATED AUDCLNT_ERR(0x004) +#define AUDCLNT_E_NOT_STOPPED AUDCLNT_ERR(0x005) +#define AUDCLNT_E_BUFFER_TOO_LARGE AUDCLNT_ERR(0x006) +#define AUDCLNT_E_OUT_OF_ORDER AUDCLNT_ERR(0x007) +#define AUDCLNT_E_UNSUPPORTED_FORMAT AUDCLNT_ERR(0x008) +#define AUDCLNT_E_INVALID_SIZE AUDCLNT_ERR(0x009) +#define AUDCLNT_E_DEVICE_IN_USE AUDCLNT_ERR(0x00a) +#define AUDCLNT_E_BUFFER_OPERATION_PENDING AUDCLNT_ERR(0x00b) +#define AUDCLNT_E_THREAD_NOT_REGISTERED AUDCLNT_ERR(0x00c) +#define AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED AUDCLNT_ERR(0x00e) +#define AUDCLNT_E_ENDPOINT_CREATE_FAILED AUDCLNT_ERR(0x00f) +#define AUDCLNT_E_SERVICE_NOT_RUNNING AUDCLNT_ERR(0x010) +#define AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED AUDCLNT_ERR(0x011) +#define AUDCLNT_E_EXCLUSIVE_MODE_ONLY AUDCLNT_ERR(0x012) +#define AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL AUDCLNT_ERR(0x013) +#define AUDCLNT_E_EVENTHANDLE_NOT_SET AUDCLNT_ERR(0x014) +#define AUDCLNT_E_INCORRECT_BUFFER_SIZE AUDCLNT_ERR(0x015) +#define AUDCLNT_E_BUFFER_SIZE_ERROR AUDCLNT_ERR(0x016) +#define AUDCLNT_E_CPUUSAGE_EXCEEDED AUDCLNT_ERR(0x017) +#define AUDCLNT_S_BUFFER_EMPTY AUDCLNT_SUCCESS(0x001) +#define AUDCLNT_S_THREAD_ALREADY_REGISTERED AUDCLNT_SUCCESS(0x002) +#define AUDCLNT_S_POSITION_STALLED AUDCLNT_SUCCESS(0x003) + +// AudioClient stream flags +#define AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 +#define AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 +#define AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 +#define AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 + +// Used to create and initialize a connection to the audio engine +struct IAudioClient : public IUnknown +{ + public: + virtual HRESULT F_STDCALL Initialize (AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const FMOD::WAVE_FORMATEX *pFormat, LPCFMOD_GUID AudioSessionGuid) = 0; + virtual HRESULT F_STDCALL GetBufferSize (UINT32 *pNumBufferFrames) = 0; + virtual HRESULT F_STDCALL GetStreamLatency (REFERENCE_TIME *phnsLatency) = 0; + virtual HRESULT F_STDCALL GetCurrentPadding (UINT32 *pNumPaddingFrames) = 0; + virtual HRESULT F_STDCALL IsFormatSupported (AUDCLNT_SHAREMODE ShareMode, const FMOD::WAVE_FORMATEX *pFormat, FMOD::WAVE_FORMATEX **ppClosestMatch) = 0; + virtual HRESULT F_STDCALL GetMixFormat (FMOD::WAVE_FORMATEX **ppDeviceFormat) = 0; + virtual HRESULT F_STDCALL GetDevicePeriod (REFERENCE_TIME *phnsDefaultDevicePeriod, REFERENCE_TIME *phnsMinimumDevicePeriod) = 0; + virtual HRESULT F_STDCALL Start (void) = 0; + virtual HRESULT F_STDCALL Stop (void) = 0; + virtual HRESULT F_STDCALL Reset (void) = 0; + virtual HRESULT F_STDCALL SetEventHandle (HANDLE eventHandle) = 0; + virtual HRESULT F_STDCALL GetService (REFIID riid, void **ppv) = 0; +}; + +// Used for monitoring the stream data rate and stream position +struct IAudioClock : public IUnknown +{ + public: + virtual HRESULT STDMETHODCALLTYPE GetFrequency (UINT64 *pu64Frequency) = 0; + virtual HRESULT STDMETHODCALLTYPE GetPosition (UINT64 *pu64Position, UINT64 *pu64QPCPosition) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCharacteristics (DWORD *pdwCharacteristics) = 0; +}; + +// Used by audio-rendering applications to stream their output data to a rendering endpoint device +struct IAudioRenderClient : public IUnknown +{ + public: + virtual HRESULT F_STDCALL GetBuffer (UINT32 NumFramesRequested, BYTE **ppData) = 0; + virtual HRESULT F_STDCALL ReleaseBuffer (UINT32 NumFramesWritten, DWORD dwFlags) = 0; +}; + +// Used by a capture application to read capture data from a capture endpoint buffer +struct IAudioCaptureClient : public IUnknown +{ + public: + virtual HRESULT F_STDCALL GetBuffer (BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) = 0; + virtual HRESULT F_STDCALL ReleaseBuffer (UINT32 NumFramesRead) = 0; + virtual HRESULT F_STDCALL GetNextPacketSize (UINT32 *pNumFramesInNextPacket) = 0; +}; + +#endif /* FMOD_SUPPORT_WASAPI */ + +#endif /* _FMOD_AUDIOCLIENT_H */ \ No newline at end of file diff --git a/win32/src/wasapi/fmod_mmdeviceapi.h b/win32/src/wasapi/fmod_mmdeviceapi.h new file mode 100755 index 0000000..f981f04 --- /dev/null +++ b/win32/src/wasapi/fmod_mmdeviceapi.h @@ -0,0 +1,125 @@ +#ifndef _FMOD_MMDEVICEAPI_H +#define _FMOD_MMDEVICEAPI_H + +#include "fmod_settings.h" + +#ifdef FMOD_SUPPORT_WASAPI + +// Audio data flow direction +typedef enum +{ + eRender, + eCapture, + eAll, + EDataFlow_enum_count +} EDataFlow; + +// Role assigned to an audio endpoint +typedef enum +{ + eConsole, + eMultimedia, + eCommunications, + ERole_enum_count +} ERole; + +// Identifier for a property +#ifndef PROPERTYKEY_DEFINED +#define PROPERTYKEY_DEFINED +typedef struct +{ + FMOD_GUID category; + DWORD propertyID; +} PROPERTYKEY; +#endif + +// GUIDs +static const GUID CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, { 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e }}; +static const GUID IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, { 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 }}; +static const GUID IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089, { 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 }}; + +// Property keys +static const PROPERTYKEY PKEY_Device_DeviceDesc = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 2 }; +static const PROPERTYKEY PKEY_DeviceInterface_FriendlyName = { { 0x026e516e, 0xb814, 0x414b, { 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22 } }, 2 }; + +// Helper defines +#define REFPROPERTYKEY const PROPERTYKEY & +#define REFPROPVARIANT const PROPVARIANT & + +// Error defines +#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) +#define E_UNSUPPORTED_TYPE HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE) + +// Device states +#define DEVICE_STATE_ACTIVE 0x00000001 +#define DEVICE_STATE_DISABLED 0x00000002 +#define DEVICE_STATE_NOTPRESENT 0x00000004 +#define DEVICE_STATE_UNPLUGGED 0x00000008 +#define DEVICE_STATEMASK_ALL 0x0000000f + + +// Property variant management +#define PropVariantInit(pvar) FMOD_memset((pvar), 0, sizeof(PROPVARIANT)) +WINOLEAPI PropVariantClear ( PROPVARIANT * pvar ); + +// Provides methods for enumerating, getting, and setting property values +struct IPropertyStore : public IUnknown +{ + public: + virtual HRESULT F_STDCALL GetCount (DWORD *cProps) = 0; + virtual HRESULT F_STDCALL GetAt (DWORD iProp, PROPERTYKEY *pkey) = 0; + virtual HRESULT F_STDCALL GetValue (REFPROPERTYKEY key, PROPVARIANT *pv) = 0; + virtual HRESULT F_STDCALL SetValue (REFPROPERTYKEY key, REFPROPVARIANT propvar) = 0; + virtual HRESULT F_STDCALL Commit (void) = 0; +}; + +// Provices notification when an endpoint state has changed +struct IMMNotificationClient : public IUnknown +{ + public: + virtual HRESULT F_STDCALL OnDeviceStateChanged (LPCWSTR pwstrDeviceId, DWORD dwNewState) = 0; + virtual HRESULT F_STDCALL OnDeviceAdded (LPCWSTR pwstrDeviceId) = 0; + virtual HRESULT F_STDCALL OnDeviceRemoved (LPCWSTR pwstrDeviceId) = 0; + virtual HRESULT F_STDCALL OnDefaultDeviceChanged (EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) = 0; + virtual HRESULT F_STDCALL OnPropertyValueChanged (LPCWSTR pwstrDeviceId, const PROPERTYKEY key) = 0; +}; + +// Encapsulate the generic features of a multimedia device resource +struct IMMDevice : public IUnknown +{ + public: + virtual HRESULT F_STDCALL Activate (REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) = 0; + virtual HRESULT F_STDCALL OpenPropertyStore (DWORD stgmAccess, IPropertyStore **ppProperties) = 0; + virtual HRESULT F_STDCALL GetId (LPWSTR *ppstrId) = 0; + virtual HRESULT F_STDCALL GetState (DWORD *pdwState) = 0; +}; + +// An audio endpoint device +struct IMMEndpoint : public IUnknown +{ + public: + virtual HRESULT F_STDCALL GetDataFlow (EDataFlow *pDataFlow) = 0; +}; + +// A collection of multimedia device resources +struct IMMDeviceCollection : public IUnknown +{ + public: + virtual HRESULT F_STDCALL GetCount (UINT *pcDevices) = 0; + virtual HRESULT F_STDCALL Item (UINT nDevice, IMMDevice **ppDevice) = 0; +}; + +// Provides methods for enumerating multimedia device resources +struct IMMDeviceEnumerator : public IUnknown +{ + public: + virtual HRESULT F_STDCALL EnumAudioEndpoints (EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices) = 0; + virtual HRESULT F_STDCALL GetDefaultAudioEndpoint (EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint) = 0; + virtual HRESULT F_STDCALL GetDevice (LPCWSTR pwstrId, IMMDevice **ppDevice) = 0; + virtual HRESULT F_STDCALL RegisterEndpointNotificationCallback (IMMNotificationClient *pClient) = 0; + virtual HRESULT F_STDCALL UnregisterEndpointNotificationCallback (IMMNotificationClient *pClient) = 0; +}; + +#endif /* FMOD_SUPPORT_WASAPI */ + +#endif /* _FMOD_MMDEVICEAPI_H */ \ No newline at end of file