diff --git a/veejay-current/libsample/Makefile.am b/veejay-current/libsample/Makefile.am new file mode 100644 index 00000000..f6aee591 --- /dev/null +++ b/veejay-current/libsample/Makefile.am @@ -0,0 +1,23 @@ +# Makefile for veejay + +INCLUDES = -I$(top_srcdir) -I$(includedir) -I$(top_srcdir)/vjmem -I$(top_srcdir)/vjmsg \ + ${XML_CFLAGS} + +SAMPLE_LIB_FILE = libsample.la + +SAMPLE_ALL_LIB_OPTS = \ + -version-info $(LT_AGE):$(LT_REVISION):$(LT_AGE) \ + -release $(LT_RELEASE) \ + -export-dynamic + +noinst_LTLIBRARIES = $(SAMPLE_LIB_FILE) + +libsample_la_SOURCES = hash.c sampleadm.c + + +libsample_la_LDFLAGS = $(SAMPLE_ALL_LIB_OPTS) \ + -L$(top_builddir)/libvjmem -lvjmem \ + -L$(top_builddir)/libvjmsg -lvjmsg \ + ${XML_LIBS} \ + -DDYNAMIC -O3 -Wall -rdynamic + diff --git a/veejay-current/libsample/hash.c b/veejay-current/libsample/hash.c new file mode 100644 index 00000000..51635df1 --- /dev/null +++ b/veejay-current/libsample/hash.c @@ -0,0 +1,1035 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.c,v 1.1.1.1 2004/10/27 23:48:59 niels Exp $ + * $Name: $ + */ + +#include +#include +#include +#include +#define HASH_IMPLEMENTATION +#include "hash.h" + +#ifdef KAZLIB_RCSID +static const char rcsid[] = "$Id: hash.c,v 1.1.1.1 2004/10/27 23:48:59 niels Exp $"; +#endif + +#define INIT_BITS 6 +#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ +#define INIT_MASK ((INIT_SIZE) - 1) + +#define next hash_next +#define key hash_key +#define data hash_data +#define hkey hash_hkey + +#define table hash_table +#define nchains hash_nchains +#define nodecount hash_nodecount +#define maxcount hash_maxcount +#define highmark hash_highmark +#define lowmark hash_lowmark +#define compare hash_compare +#define function hash_function +#define allocnode hash_allocnode +#define freenode hash_freenode +#define context hash_context +#define mask hash_mask +#define dynamic hash_dynamic + +#define table hash_table +#define chain hash_chain + +static hnode_t *hnode_alloc(void *context); +static void hnode_free(hnode_t *node, void *context); +static hash_val_t hash_fun_default(const void *key); +static int hash_comp_default(const void *key1, const void *key2); + +int hash_val_t_bit; + +/* + * Compute the number of bits in the hash_val_t type. We know that hash_val_t + * is an unsigned integral type. Thus the highest value it can hold is a + * Mersenne number (power of two, less one). We initialize a hash_val_t + * object with this value and then shift bits out one by one while counting. + * Notes: + * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power + * of two. This means that its binary representation consists of all one + * bits, and hence ``val'' is initialized to all one bits. + * 2. While bits remain in val, we increment the bit count and shift it to the + * right, replacing the topmost bit by zero. + */ + +static void compute_bits(void) +{ + hash_val_t val = HASH_VAL_T_MAX; /* 1 */ + int bits = 0; + + while (val) { /* 2 */ + bits++; + val >>= 1; + } + + hash_val_t_bit = bits; +} + +/* + * Verify whether the given argument is a power of two. + */ + +static int is_power_of_two(hash_val_t arg) +{ + if (arg == 0) + return 0; + while ((arg & 1) == 0) + arg >>= 1; + return (arg == 1); +} + +/* + * Compute a shift amount from a given table size + */ + +static hash_val_t compute_mask(hashcount_t size) +{ + assert (is_power_of_two(size)); + assert (size >= 2); + + return size - 1; +} + +/* + * Initialize the table of pointers to null. + */ + +static void clear_table(hash_t *hash) +{ + hash_val_t i; + + for (i = 0; i < hash->nchains; i++) + hash->table[i] = NULL; +} + +/* + * Double the size of a dynamic table. This works as follows. Each chain splits + * into two adjacent chains. The shift amount increases by one, exposing an + * additional bit of each hashed key. For each node in the original chain, the + * value of this newly exposed bit will decide which of the two new chains will + * receive the node: if the bit is 1, the chain with the higher index will have + * the node, otherwise the lower chain will receive the node. In this manner, + * the hash table will continue to function exactly as before without having to + * rehash any of the keys. + * Notes: + * 1. Overflow check. + * 2. The new number of chains is twice the old number of chains. + * 3. The new mask is one bit wider than the previous, revealing a + * new bit in all hashed keys. + * 4. Allocate a new table of chain pointers that is twice as large as the + * previous one. + * 5. If the reallocation was successful, we perform the rest of the growth + * algorithm, otherwise we do nothing. + * 6. The exposed_bit variable holds a mask with which each hashed key can be + * AND-ed to test the value of its newly exposed bit. + * 7. Now loop over each chain in the table and sort its nodes into two + * chains based on the value of each node's newly exposed hash bit. + * 8. The low chain replaces the current chain. The high chain goes + * into the corresponding sister chain in the upper half of the table. + * 9. We have finished dealing with the chains and nodes. We now update + * the various bookeeping fields of the hash structure. + */ + +static void grow_table(hash_t *hash) +{ + hnode_t **newtable; + + assert (2 * hash->nchains > hash->nchains); /* 1 */ + + newtable = realloc(hash->table, + sizeof *newtable * hash->nchains * 2); /* 4 */ + + if (newtable) { /* 5 */ + hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ + hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ + hash_val_t chain; + + assert (mask != hash->mask); + + for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ + hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; + + for (hptr = newtable[chain]; hptr != 0; hptr = next) { + next = hptr->next; + + if (hptr->hkey & exposed_bit) { + hptr->next = high_chain; + high_chain = hptr; + } else { + hptr->next = low_chain; + low_chain = hptr; + } + } + + newtable[chain] = low_chain; /* 8 */ + newtable[chain + hash->nchains] = high_chain; + } + + hash->table = newtable; /* 9 */ + hash->mask = mask; + hash->nchains *= 2; + hash->lowmark *= 2; + hash->highmark *= 2; + } + assert (hash_verify(hash)); +} + +/* + * Cut a table size in half. This is done by folding together adjacent chains + * and populating the lower half of the table with these chains. The chains are + * simply spliced together. Once this is done, the whole table is reallocated + * to a smaller object. + * Notes: + * 1. It is illegal to have a hash table with one slot. This would mean that + * hash->shift is equal to hash_val_t_bit, an illegal shift value. + * Also, other things could go wrong, such as hash->lowmark becoming zero. + * 2. Looping over each pair of sister chains, the low_chain is set to + * point to the head node of the chain in the lower half of the table, + * and high_chain points to the head node of the sister in the upper half. + * 3. The intent here is to compute a pointer to the last node of the + * lower chain into the low_tail variable. If this chain is empty, + * low_tail ends up with a null value. + * 4. If the lower chain is not empty, we simply tack the upper chain onto it. + * If the upper chain is a null pointer, nothing happens. + * 5. Otherwise if the lower chain is empty but the upper one is not, + * If the low chain is empty, but the high chain is not, then the + * high chain is simply transferred to the lower half of the table. + * 6. Otherwise if both chains are empty, there is nothing to do. + * 7. All the chain pointers are in the lower half of the table now, so + * we reallocate it to a smaller object. This, of course, invalidates + * all pointer-to-pointers which reference into the table from the + * first node of each chain. + * 8. Though it's unlikely, the reallocation may fail. In this case we + * pretend that the table _was_ reallocated to a smaller object. + * 9. Finally, update the various table parameters to reflect the new size. + */ + +static void shrink_table(hash_t *hash) +{ + hash_val_t chain, nchains; + hnode_t **newtable, *low_tail, *low_chain, *high_chain; + + assert (hash->nchains >= 2); /* 1 */ + nchains = hash->nchains / 2; + + for (chain = 0; chain < nchains; chain++) { + low_chain = hash->table[chain]; /* 2 */ + high_chain = hash->table[chain + nchains]; + for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) + ; /* 3 */ + if (low_chain != 0) /* 4 */ + low_tail->next = high_chain; + else if (high_chain != 0) /* 5 */ + hash->table[chain] = high_chain; + else + assert (hash->table[chain] == NULL); /* 6 */ + } + newtable = realloc(hash->table, + sizeof *newtable * nchains); /* 7 */ + if (newtable) /* 8 */ + hash->table = newtable; + hash->mask >>= 1; /* 9 */ + hash->nchains = nchains; + hash->lowmark /= 2; + hash->highmark /= 2; + assert (hash_verify(hash)); +} + + +/* + * Create a dynamic hash table. Both the hash table structure and the table + * itself are dynamically allocated. Furthermore, the table is extendible in + * that it will automatically grow as its load factor increases beyond a + * certain threshold. + * Notes: + * 1. If the number of bits in the hash_val_t type has not been computed yet, + * we do so here, because this is likely to be the first function that the + * user calls. + * 2. Allocate a hash table control structure. + * 3. If a hash table control structure is successfully allocated, we + * proceed to initialize it. Otherwise we return a null pointer. + * 4. We try to allocate the table of hash chains. + * 5. If we were able to allocate the hash chain table, we can finish + * initializing the hash structure and the table. Otherwise, we must + * backtrack by freeing the hash structure. + * 6. INIT_SIZE should be a power of two. The high and low marks are always set + * to be twice the table size and half the table size respectively. When the + * number of nodes in the table grows beyond the high size (beyond load + * factor 2), it will double in size to cut the load factor down to about + * about 1. If the table shrinks down to or beneath load factor 0.5, + * it will shrink, bringing the load up to about 1. However, the table + * will never shrink beneath INIT_SIZE even if it's emptied. + * 7. This indicates that the table is dynamically allocated and dynamically + * resized on the fly. A table that has this value set to zero is + * assumed to be statically allocated and will not be resized. + * 8. The table of chains must be properly reset to all null pointers. + */ + +hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun, + hash_fun_t hashfun) +{ + hash_t *hash; + + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + hash = malloc(sizeof *hash); /* 2 */ + + if (hash) { /* 3 */ + hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ + if (hash->table) { /* 5 */ + hash->nchains = INIT_SIZE; /* 6 */ + hash->highmark = INIT_SIZE * 2; + hash->lowmark = INIT_SIZE / 2; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->allocnode = hnode_alloc; + hash->freenode = hnode_free; + hash->context = NULL; + hash->mask = INIT_MASK; + hash->dynamic = 1; /* 7 */ + clear_table(hash); /* 8 */ + assert (hash_verify(hash)); + return hash; + } + free(hash); + } + + return NULL; +} + +/* + * Select a different set of node allocator routines. + */ + +void hash_set_allocator(hash_t *hash, hnode_alloc_t al, + hnode_free_t fr, void *context) +{ + assert (hash_count(hash) == 0); + assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); + + hash->allocnode = al ? al : hnode_alloc; + hash->freenode = fr ? fr : hnode_free; + hash->context = context; +} + +/* + * Free every node in the hash using the hash->freenode() function pointer, and + * cause the hash to become empty. + */ + +void hash_free_nodes(hash_t *hash) +{ + hscan_t hs; + hnode_t *node; + hash_scan_begin(&hs, hash); + while ((node = hash_scan_next(&hs))) { + hash_scan_delete(hash, node); + hash->freenode(node, hash->context); + } + hash->nodecount = 0; + clear_table(hash); +} + +/* + * Obsolescent function for removing all nodes from a table, + * freeing them and then freeing the table all in one step. + */ + +void hash_free(hash_t *hash) +{ +#ifdef KAZLIB_OBSOLESCENT_DEBUG + assert ("call to obsolescent function hash_free()" && 0); +#endif + hash_free_nodes(hash); + hash_destroy(hash); +} + +/* + * Free a dynamic hash table structure. + */ + +void hash_destroy(hash_t *hash) +{ + assert (hash_val_t_bit != 0); + assert (hash_isempty(hash)); + free(hash->table); + free(hash); +} + +/* + * Initialize a user supplied hash structure. The user also supplies a table of + * chains which is assigned to the hash structure. The table is static---it + * will not grow or shrink. + * 1. See note 1. in hash_create(). + * 2. The user supplied array of pointers hopefully contains nchains nodes. + * 3. See note 7. in hash_create(). + * 4. We must dynamically compute the mask from the given power of two table + * size. + * 5. The user supplied table can't be assumed to contain null pointers, + * so we reset it here. + */ + +hash_t *hash_init(hash_t *hash, hashcount_t maxcount, + hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, + hashcount_t nchains) +{ + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + assert (is_power_of_two(nchains)); + + hash->table = table; /* 2 */ + hash->nchains = nchains; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->dynamic = 0; /* 3 */ + hash->mask = compute_mask(nchains); /* 4 */ + clear_table(hash); /* 5 */ + + assert (hash_verify(hash)); + + return hash; +} + +/* + * Reset the hash scanner so that the next element retrieved by + * hash_scan_next() shall be the first element on the first non-empty chain. + * Notes: + * 1. Locate the first non empty chain. + * 2. If an empty chain is found, remember which one it is and set the next + * pointer to refer to its first element. + * 3. Otherwise if a chain is not found, set the next pointer to NULL + * so that hash_scan_next() shall indicate failure. + */ + +void hash_scan_begin(hscan_t *scan, hash_t *hash) +{ + hash_val_t nchains = hash->nchains; + hash_val_t chain; + + scan->table = hash; + + /* 1 */ + + for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) + ; + + if (chain < nchains) { /* 2 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { /* 3 */ + scan->next = NULL; + } +} + +/* + * Retrieve the next node from the hash table, and update the pointer + * for the next invocation of hash_scan_next(). + * Notes: + * 1. Remember the next pointer in a temporary value so that it can be + * returned. + * 2. This assertion essentially checks whether the module has been properly + * initialized. The first point of interaction with the module should be + * either hash_create() or hash_init(), both of which set hash_val_t_bit to + * a non zero value. + * 3. If the next pointer we are returning is not NULL, then the user is + * allowed to call hash_scan_next() again. We prepare the new next pointer + * for that call right now. That way the user is allowed to delete the node + * we are about to return, since we will no longer be needing it to locate + * the next node. + * 4. If there is a next node in the chain (next->next), then that becomes the + * new next node, otherwise ... + * 5. We have exhausted the current chain, and must locate the next subsequent + * non-empty chain in the table. + * 6. If a non-empty chain is found, the first element of that chain becomes + * the new next node. Otherwise there is no new next node and we set the + * pointer to NULL so that the next time hash_scan_next() is called, a null + * pointer shall be immediately returned. + */ + + +hnode_t *hash_scan_next(hscan_t *scan) +{ + hnode_t *next = scan->next; /* 1 */ + hash_t *hash = scan->table; + hash_val_t chain = scan->chain + 1; + hash_val_t nchains = hash->nchains; + + assert (hash_val_t_bit != 0); /* 2 */ + + if (next) { /* 3 */ + if (next->next) { /* 4 */ + scan->next = next->next; + } else { + while (chain < nchains && hash->table[chain] == 0) /* 5 */ + chain++; + if (chain < nchains) { /* 6 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { + scan->next = NULL; + } + } + } + return next; +} + +/* + * Insert a node into the hash table. + * Notes: + * 1. It's illegal to insert more than the maximum number of nodes. The client + * should verify that the hash table is not full before attempting an + * insertion. + * 2. The same key may not be inserted into a table twice. + * 3. If the table is dynamic and the load factor is already at >= 2, + * grow the table. + * 4. We take the bottom N bits of the hash value to derive the chain index, + * where N is the base 2 logarithm of the size of the hash table. + */ + +void hash_insert(hash_t *hash, hnode_t *node, const void *key) +{ + hash_val_t hkey, chain; + + assert (hash_val_t_bit != 0); + assert (node->next == NULL); + assert (hash->nodecount < hash->maxcount); /* 1 */ + assert (hash_lookup(hash, key) == NULL); /* 2 */ + + if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ + grow_table(hash); + + hkey = hash->function(key); + chain = hkey & hash->mask; /* 4 */ + + node->key = key; + node->hkey = hkey; + node->next = hash->table[chain]; + hash->table[chain] = node; + hash->nodecount++; + + assert (hash_verify(hash)); +} + +/* + * Find a node in the hash table and return a pointer to it. + * Notes: + * 1. We hash the key and keep the entire hash value. As an optimization, when + * we descend down the chain, we can compare hash values first and only if + * hash values match do we perform a full key comparison. + * 2. To locate the chain from among 2^N chains, we look at the lower N bits of + * the hash value by anding them with the current mask. + * 3. Looping through the chain, we compare the stored hash value inside each + * node against our computed hash. If they match, then we do a full + * comparison between the unhashed keys. If these match, we have located the + * entry. + */ + +hnode_t *hash_lookup(hash_t *hash, const void *key) +{ + hash_val_t hkey, chain; + hnode_t *nptr; + + hkey = hash->function(key); /* 1 */ + chain = hkey & hash->mask; /* 2 */ + + for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ + if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) + return nptr; + } + + return NULL; +} + +/* + * Delete the given node from the hash table. Since the chains + * are singly linked, we must locate the start of the node's chain + * and traverse. + * Notes: + * 1. The node must belong to this hash table, and its key must not have + * been tampered with. + * 2. If this deletion will take the node count below the low mark, we + * shrink the table now. + * 3. Determine which chain the node belongs to, and fetch the pointer + * to the first node in this chain. + * 4. If the node being deleted is the first node in the chain, then + * simply update the chain head pointer. + * 5. Otherwise advance to the node's predecessor, and splice out + * by updating the predecessor's next pointer. + * 6. Indicate that the node is no longer in a hash table. + */ + +hnode_t *hash_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (hash_lookup(hash, node->key) == node); /* 1 */ + assert (hash_val_t_bit != 0); + + if (hash->dynamic && hash->nodecount <= hash->lowmark + && hash->nodecount > INIT_SIZE) + shrink_table(hash); /* 2 */ + + chain = node->hkey & hash->mask; /* 3 */ + hptr = hash->table[chain]; + + if (hptr == node) { /* 4 */ + hash->table[chain] = node->next; + } else { + while (hptr->next != node) { /* 5 */ + assert (hptr != 0); + hptr = hptr->next; + } + assert (hptr->next == node); + hptr->next = node->next; + } + + hash->nodecount--; + assert (hash_verify(hash)); + + node->next = NULL; /* 6 */ + return node; +} + +int hash_alloc_insert(hash_t *hash, const void *key, void *data) +{ + hnode_t *node = hash->allocnode(hash->context); + + if (node) { + hnode_init(node, data); + hash_insert(hash, node, key); + return 1; + } + return 0; +} + +void hash_delete_free(hash_t *hash, hnode_t *node) +{ + hash_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Exactly like hash_delete, except does not trigger table shrinkage. This is to be + * used from within a hash table scan operation. See notes for hash_delete. + */ + +hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (hash_lookup(hash, node->key) == node); + assert (hash_val_t_bit != 0); + + chain = node->hkey & hash->mask; + hptr = hash->table[chain]; + + if (hptr == node) { + hash->table[chain] = node->next; + } else { + while (hptr->next != node) + hptr = hptr->next; + hptr->next = node->next; + } + + hash->nodecount--; + assert (hash_verify(hash)); + node->next = NULL; + + return node; +} + +/* + * Like hash_delete_free but based on hash_scan_delete. + */ + +void hash_scan_delfree(hash_t *hash, hnode_t *node) +{ + hash_scan_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Verify whether the given object is a valid hash table. This means + * Notes: + * 1. If the hash table is dynamic, verify whether the high and + * low expansion/shrinkage thresholds are powers of two. + * 2. Count all nodes in the table, and test each hash value + * to see whether it is correct for the node's chain. + */ + +int hash_verify(hash_t *hash) +{ + hashcount_t count = 0; + hash_val_t chain; + hnode_t *hptr; + + if (hash->dynamic) { /* 1 */ + if (hash->lowmark >= hash->highmark) + return 0; + if (!is_power_of_two(hash->highmark)) + return 0; + if (!is_power_of_two(hash->lowmark)) + return 0; + } + + for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ + for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { + if ((hptr->hkey & hash->mask) != chain) + return 0; + count++; + } + } + + if (count != hash->nodecount) + return 0; + + return 1; +} + +/* + * Test whether the hash table is full and return 1 if this is true, + * 0 if it is false. + */ + +#undef hash_isfull +int hash_isfull(hash_t *hash) +{ + return hash->nodecount == hash->maxcount; +} + +/* + * Test whether the hash table is empty and return 1 if this is true, + * 0 if it is false. + */ + +#undef hash_isempty +int hash_isempty(hash_t *hash) +{ + return hash->nodecount == 0; +} + +static hnode_t *hnode_alloc(void *context) +{ + return malloc(sizeof *hnode_alloc(NULL)); +} + +static void hnode_free(hnode_t *node, void *context) +{ + if(node) free(node); +} + + +/* + * Create a hash table node dynamically and assign it the given data. + */ + +hnode_t *hnode_create(void *data) +{ + hnode_t *node = malloc(sizeof *node); + if (node) { + node->data = data; + node->next = NULL; + } + return node; +} + +/* + * Initialize a client-supplied node + */ + +hnode_t *hnode_init(hnode_t *hnode, void *data) +{ + hnode->data = data; + hnode->next = NULL; + return hnode; +} + +/* + * Destroy a dynamically allocated node. + */ + +void hnode_destroy(hnode_t *hnode) +{ + free(hnode); +} + +#undef hnode_put +void hnode_put(hnode_t *node, void *data) +{ + node->data = data; +} + +#undef hnode_get +void *hnode_get(hnode_t *node) +{ + return node->data; +} + +#undef hnode_getkey +const void *hnode_getkey(hnode_t *node) +{ + return node->key; +} + +#undef hash_count +hashcount_t hash_count(hash_t *hash) +{ + return hash->nodecount; +} + +#undef hash_size +hashcount_t hash_size(hash_t *hash) +{ + return hash->nchains; +} + +static hash_val_t hash_fun_default(const void *key) +{ + static unsigned long randbox[] = { + 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, + 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, + 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, + 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, + }; + + const unsigned char *str = key; + hash_val_t acc = 0; + + while (*str) { + acc ^= randbox[(*str + acc) & 0xf]; + acc = (acc << 1) | (acc >> 31); + acc &= 0xffffffffU; + acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; + acc = (acc << 2) | (acc >> 30); + acc &= 0xffffffffU; + } + return acc; +} + +static int hash_comp_default(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} + +#ifdef KAZLIB_TEST_MAIN + +#include +#include +#include + +typedef char input_t[256]; + +static int tokenize(char *string, ...) +{ + char **tokptr; + va_list arglist; + int tokcount = 0; + + va_start(arglist, string); + tokptr = va_arg(arglist, char **); + while (tokptr) { + while (*string && isspace((unsigned char) *string)) + string++; + if (!*string) + break; + *tokptr = string; + while (*string && !isspace((unsigned char) *string)) + string++; + tokptr = va_arg(arglist, char **); + tokcount++; + if (!*string) + break; + *string++ = 0; + } + va_end(arglist); + + return tokcount; +} + +static char *dupstring(char *str) +{ + int sz = strlen(str) + 1; + char *new = malloc(sz); + if (new) + memcpy(new, str, sz); + return new; +} + +static hnode_t *new_node(void *c) +{ + static hnode_t few[5]; + static int count; + + if (count < 5) + return few + count++; + + return NULL; +} + +static void del_node(hnode_t *n, void *c) +{ +} + +int main(void) +{ + input_t in; + hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0); + hnode_t *hn; + hscan_t hs; + char *tok1, *tok2, *val; + const char *key; + int prompt = 0; + + char *help = + "a add value to hash table\n" + "d delete value from hash table\n" + "l lookup value in hash table\n" + "n show size of hash table\n" + "c show number of entries\n" + "t dump whole hash table\n" + "+ increase hash table (private func)\n" + "- decrease hash table (private func)\n" + "b print hash_t_bit value\n" + "p turn prompt on\n" + "s switch to non-functioning allocator\n" + "q quit"; + + if (!h) + puts("hash_create failed"); + + for (;;) { + if (prompt) + putchar('>'); + fflush(stdout); + + if (!fgets(in, sizeof(input_t), stdin)) + break; + + switch(in[0]) { + case '?': + puts(help); + break; + case 'b': + printf("%d\n", hash_val_t_bit); + break; + case 'a': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } + key = dupstring(tok1); + val = dupstring(tok2); + + if (!key || !val) { + puts("out of memory"); + free((void *) key); + free(val); + } + + if (!hash_alloc_insert(h, key, val)) { + puts("hash_alloc_insert failed"); + free((void *) key); + free(val); + break; + } + break; + case 'd': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + hn = hash_lookup(h, tok1); + if (!hn) { + puts("hash_lookup failed"); + break; + } + val = hnode_get(hn); + key = hnode_getkey(hn); + hash_scan_delfree(h, hn); + free((void *) key); + free(val); + break; + case 'l': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + hn = hash_lookup(h, tok1); + if (!hn) { + puts("hash_lookup failed"); + break; + } + val = hnode_get(hn); + puts(val); + break; + case 'n': + printf("%lu\n", (unsigned long) hash_size(h)); + break; + case 'c': + printf("%lu\n", (unsigned long) hash_count(h)); + break; + case 't': + hash_scan_begin(&hs, h); + while ((hn = hash_scan_next(&hs))) + printf("%s\t%s\n", (char*) hnode_getkey(hn), + (char*) hnode_get(hn)); + break; + case '+': + grow_table(h); /* private function */ + break; + case '-': + shrink_table(h); /* private function */ + break; + case 'q': + exit(0); + break; + case '\0': + break; + case 'p': + prompt = 1; + break; + case 's': + hash_set_allocator(h, new_node, del_node, NULL); + break; + default: + putchar('?'); + putchar('\n'); + break; + } + } + + return 0; +} + +#endif diff --git a/veejay-current/libsample/hash.h b/veejay-current/libsample/hash.h new file mode 100644 index 00000000..9677632b --- /dev/null +++ b/veejay-current/libsample/hash.h @@ -0,0 +1,240 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.h,v 1.1.1.1 2004/10/27 23:48:59 niels Exp $ + * $Name: $ + */ + +#ifndef HASH_H +#define HASH_H + +#include +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#include "sfx.h" +#endif + +/* + * Blurb for inclusion into C++ translation units + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long hashcount_t; +#define HASHCOUNT_T_MAX ULONG_MAX + +typedef unsigned long hash_val_t; +#define HASH_VAL_T_MAX ULONG_MAX + +extern int hash_val_t_bit; + +#ifndef HASH_VAL_T_BIT +#define HASH_VAL_T_BIT ((int) hash_val_t_bit) +#endif + +/* + * Hash chain node structure. + * Notes: + * 1. This preprocessing directive is for debugging purposes. The effect is + * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the + * inclusion of this header, then the structure shall be declared as having + * the single member int __OPAQUE__. This way, any attempts by the + * client code to violate the principles of information hiding (by accessing + * the structure directly) can be diagnosed at translation time. However, + * note the resulting compiled unit is not suitable for linking. + * 2. This is a pointer to the next node in the chain. In the last node of a + * chain, this pointer is null. + * 3. The key is a pointer to some user supplied data that contains a unique + * identifier for each hash node in a given table. The interpretation of + * the data is up to the user. When creating or initializing a hash table, + * the user must supply a pointer to a function for comparing two keys, + * and a pointer to a function for hashing a key into a numeric value. + * 4. The value is a user-supplied pointer to void which may refer to + * any data object. It is not interpreted in any way by the hashing + * module. + * 5. The hashed key is stored in each node so that we don't have to rehash + * each key when the table must grow or shrink. + */ + +typedef struct hnode_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ + struct hnode_t *hash_next; /* 2 */ + const void *hash_key; /* 3 */ + void *hash_data; /* 4 */ + hash_val_t hash_hkey; /* 5 */ + #else + int hash_dummy; + #endif +} hnode_t; + +/* + * The comparison function pointer type. A comparison function takes two keys + * and produces a value of -1 if the left key is less than the right key, a + * value of 0 if the keys are equal, and a value of 1 if the left key is + * greater than the right key. + */ + +typedef int (*hash_comp_t)(const void *, const void *); + +/* + * The hashing function performs some computation on a key and produces an + * integral value of type hash_val_t based on that key. For best results, the + * function should have a good randomness properties in *all* significant bits + * over the set of keys that are being inserted into a given hash table. In + * particular, the most significant bits of hash_val_t are most significant to + * the hash module. Only as the hash table expands are less significant bits + * examined. Thus a function that has good distribution in its upper bits but + * not lower is preferrable to one that has poor distribution in the upper bits + * but not the lower ones. + */ + +typedef hash_val_t (*hash_fun_t)(const void *); + +/* + * allocator functions + */ + +typedef hnode_t *(*hnode_alloc_t)(void *); +typedef void (*hnode_free_t)(hnode_t *, void *); + +/* + * This is the hash table control structure. It keeps track of information + * about a hash table, as well as the hash table itself. + * Notes: + * 1. Pointer to the hash table proper. The table is an array of pointers to + * hash nodes (of type hnode_t). If the table is empty, every element of + * this table is a null pointer. A non-null entry points to the first + * element of a chain of nodes. + * 2. This member keeps track of the size of the hash table---that is, the + * number of chain pointers. + * 3. The count member maintains the number of elements that are presently + * in the hash table. + * 4. The maximum count is the greatest number of nodes that can populate this + * table. If the table contains this many nodes, no more can be inserted, + * and the hash_isfull() function returns true. + * 5. The high mark is a population threshold, measured as a number of nodes, + * which, if exceeded, will trigger a table expansion. Only dynamic hash + * tables are subject to this expansion. + * 6. The low mark is a minimum population threshold, measured as a number of + * nodes. If the table population drops below this value, a table shrinkage + * will occur. Only dynamic tables are subject to this reduction. No table + * will shrink beneath a certain absolute minimum number of nodes. + * 7. This is the a pointer to the hash table's comparison function. The + * function is set once at initialization or creation time. + * 8. Pointer to the table's hashing function, set once at creation or + * initialization time. + * 9. The current hash table mask. If the size of the hash table is 2^N, + * this value has its low N bits set to 1, and the others clear. It is used + * to select bits from the result of the hashing function to compute an + * index into the table. + * 10. A flag which indicates whether the table is to be dynamically resized. It + * is set to 1 in dynamically allocated tables, 0 in tables that are + * statically allocated. + */ + +typedef struct hash_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + struct hnode_t **hash_table; /* 1 */ + hashcount_t hash_nchains; /* 2 */ + hashcount_t hash_nodecount; /* 3 */ + hashcount_t hash_maxcount; /* 4 */ + hashcount_t hash_highmark; /* 5 */ + hashcount_t hash_lowmark; /* 6 */ + hash_comp_t hash_compare; /* 7 */ + hash_fun_t hash_function; /* 8 */ + hnode_alloc_t hash_allocnode; + hnode_free_t hash_freenode; + void *hash_context; + hash_val_t hash_mask; /* 9 */ + int hash_dynamic; /* 10 */ + #else + int hash_dummy; + #endif +} hash_t; + +/* + * Hash scanner structure, used for traversals of the data structure. + * Notes: + * 1. Pointer to the hash table that is being traversed. + * 2. Reference to the current chain in the table being traversed (the chain + * that contains the next node that shall be retrieved). + * 3. Pointer to the node that will be retrieved by the subsequent call to + * hash_scan_next(). + */ + +typedef struct hscan_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + hash_t *hash_table; /* 1 */ + hash_val_t hash_chain; /* 2 */ + hnode_t *hash_next; /* 3 */ + #else + int hash_dummy; + #endif +} hscan_t; + +extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t); +extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); +extern void hash_destroy(hash_t *); +extern void hash_free_nodes(hash_t *); +extern void hash_free(hash_t *); +extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t, + hash_fun_t, hnode_t **, hashcount_t); +extern void hash_insert(hash_t *, hnode_t *, const void *); +extern hnode_t *hash_lookup(hash_t *, const void *); +extern hnode_t *hash_delete(hash_t *, hnode_t *); +extern int hash_alloc_insert(hash_t *, const void *, void *); +extern void hash_delete_free(hash_t *, hnode_t *); + +extern void hnode_put(hnode_t *, void *); +extern void *hnode_get(hnode_t *); +extern const void *hnode_getkey(hnode_t *); +extern hashcount_t hash_count(hash_t *); +extern hashcount_t hash_size(hash_t *); + +extern int hash_isfull(hash_t *); +extern int hash_isempty(hash_t *); + +extern void hash_scan_begin(hscan_t *, hash_t *); +extern hnode_t *hash_scan_next(hscan_t *); +extern hnode_t *hash_scan_delete(hash_t *, hnode_t *); +extern void hash_scan_delfree(hash_t *, hnode_t *); + +extern int hash_verify(hash_t *); + +extern hnode_t *hnode_create(void *); +extern hnode_t *hnode_init(hnode_t *, void *); +extern void hnode_destroy(hnode_t *); + +#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) +#else +#define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) +#endif +#define hash_isempty(H) ((H)->hash_nodecount == 0) +#define hash_count(H) ((H)->hash_nodecount) +#define hash_size(H) ((H)->hash_nchains) +#define hnode_get(N) ((N)->hash_data) +#define hnode_getkey(N) ((N)->hash_key) +#define hnode_put(N, V) ((N)->hash_data = (V)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/veejay-current/libsample/sampleadm.c b/veejay-current/libsample/sampleadm.c new file mode 100644 index 00000000..8124b303 --- /dev/null +++ b/veejay-current/libsample/sampleadm.c @@ -0,0 +1,2561 @@ +/* + * Linux VeeJay + * + * Copyright(C)2002 Niels Elburg + * + * 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. + * + * + * 05/03/2003: Added XML code from Jeff Carpenter ( jrc@dshome.net ) + * 05/03/2003: Included more clip properties in Jeff's code + * Create is used to write the Clip to XML, Parse is used to load from XML + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +//todo: change this into enum +//#define KAZLIB_OPAQUE_DEBUG 1 + +#ifdef HAVE_XML2 + +#define XMLTAG_RENDER_ENTRY "render_entry" +#define XMLTAG_CLIPS "veejay_clips" +#define XMLTAG_CLIP "clip" +#define XMLTAG_CLIPID "clipid" +#define XMLTAG_CLIPDESCR "description" +#define XMLTAG_FIRSTFRAME "startframe" +#define XMLTAG_LASTFRAME "endframe" +#define XMLTAG_EFFECTS "effects" +#define XMLTAG_VOL "volume" +#define XMLTAG_EFFECT "effect" +#define XMLTAG_EFFECTID "effectid" +#define XMLTAG_ARGUMENTS "arguments" +#define XMLTAG_ARGUMENT "argument" +#define XMLTAG_EFFECTSOURCE "source" +#define XMLTAG_EFFECTCHANNEL "channel" +#define XMLTAG_EFFECTTRIMMER "trimmer" +#define XMLTAG_EFFECTOFFSET "offset" +#define XMLTAG_EFFECTACTIVE "active" +#define XMLTAG_EFFECTAUDIOFLAG "use_audio" +#define XMLTAG_EFFECTAUDIOVOLUME "chain_volume" +#define XMLTAG_SPEED "speed" +#define XMLTAG_FRAMEDUP "frameduplicator" +#define XMLTAG_LOOPTYPE "looptype" +#define XMLTAG_MAXLOOPS "maxloops" +#define XMLTAG_NEXTCLIP "nextclip" +#define XMLTAG_DEPTH "depth" +#define XMLTAG_PLAYMODE "playmode" +#define XMLTAG_VOLUME "volume" +#define XMLTAG_SUBAUDIO "subaudio" +#define XMLTAG_MARKERSTART "markerstart" +#define XMLTAG_MARKEREND "markerend" +#define XMLTAG_EFFECTPOS "position" +#define XMLTAG_FADER_ACTIVE "chain_fade" +#define XMLTAG_FADER_VAL "chain_fade_value" +#define XMLTAG_FADER_INC "chain_fade_increment" +#define XMLTAG_FADER_DIRECTION "chain_direction" +#define XMLTAG_LASTENTRY "current_entry" +#define XMLTAG_CHAIN_ENABLED "fx" +#endif + +#define FOURCC(a,b,c,d) ( (d<<24) | ((c&0xff)<<16) | ((b&0xff)<<8) | (a&0xff) ) + +#define FOURCC_RIFF FOURCC ('R', 'I', 'F', 'F') +#define FOURCC_WAVE FOURCC ('W', 'A', 'V', 'E') +#define FOURCC_FMT FOURCC ('f', 'm', 't', ' ') +#define FOURCC_DATA FOURCC ('d', 'a', 't', 'a') + + +#define VJ_IMAGE_EFFECT_MIN vj_effect_get_min_i() +#define VJ_IMAGE_EFFECT_MAX vj_effect_get_max_i() +#define VJ_VIDEO_EFFECT_MIN vj_effect_get_min_v() +#define VJ_VIDEO_EFFECT_MAX vj_effect_get_max_v() + +static int this_clip_id = 0; /* next available clip id */ +static int next_avail_num = 0; /* available clip id */ +static int initialized = 0; /* whether we are initialized or not */ +static hash_t *ClipHash; /* hash of clip information structs */ +static int avail_num[CLIP_MAX_CLIPS]; /* an array of freed clip id's */ + +static int clipadm_state = CLIP_PEEK; /* default state */ + + + + +/**************************************************************************************************** + * + * clip_size + * + * returns current clip_id pointer. size is actually this_clip_id - next_avail_num, + * but people tend to use size as in length. + * + ****************************************************************************************************/ +int clip_size() +{ + return this_clip_id; +} + +int clip_verify() { + return hash_verify( ClipHash ); +} + + + +/**************************************************************************************************** + * + * int_hash + * + * internal usage. returns hash_val_t for key + * + ****************************************************************************************************/ +static inline hash_val_t int_hash(const void *key) +{ + return (hash_val_t) key; +} + + + +/**************************************************************************************************** + * + * int_compare + * + * internal usage. compares keys for hash. + * + ****************************************************************************************************/ +static inline int int_compare(const void *key1, const void *key2) +{ + return ((int) key1 < (int) key2 ? -1 : + ((int) key1 > (int) key2 ? +1 : 0)); +} + +int clip_update(clip_info *clip, int s1) { + if(s1 <= 0 || s1 >= CLIP_MAX_CLIPS) return 0; + if(clip) { + hnode_t *clip_node = hnode_create(clip); + hnode_put(clip_node, (void*) s1); + hnode_destroy(clip_node); + return 1; + } + return 0; +} + + + +/**************************************************************************************************** + * + * clip_init() + * + * call before using any other function as clip_skeleton_new + * + ****************************************************************************************************/ +void clip_init(int len) +{ + if (!initialized) { + int i; + for (i = 0; i < CLIP_MAX_CLIPS; i++) + avail_num[i] = 0; + this_clip_id = 1; /* do not start with zero */ + if (! + (ClipHash = + hash_create(HASHCOUNT_T_MAX, int_compare, int_hash))) { + fprintf(stderr, + "--DEBUG: clip_init(): cannot create clipHash\n"); + } + initialized = 1; + } +} + +int clip_set_state(int new_state) +{ + if (new_state == CLIP_LOAD || new_state == CLIP_RUN + || new_state == CLIP_PEEK) { + clipadm_state = new_state; + } + return clipadm_state; +} + +int clip_get_state() +{ + return clipadm_state; +} + +/**************************************************************************************************** + * + * clip_skeleton_new(long , long) + * + * create a new clip, give start and end of new clip. returns clip info block. + * + ****************************************************************************************************/ + +clip_info *clip_skeleton_new(long startFrame, long endFrame) +{ + + + clip_info *si; + int i, j, n, id = 0; + + if (!initialized) { + fprintf(stderr, "Clip Administrator not initialized! Re-init\n"); + return NULL; + } + si = (clip_info *) vj_malloc(sizeof(clip_info)); + if(startFrame < 0) startFrame = 0; + if(endFrame <= startFrame && (endFrame !=0 && startFrame != 0)) + { + veejay_msg(VEEJAY_MSG_ERROR,"End frame must be greater then start frame"); + return NULL; + } + + if (!si) { + fprintf(stderr, "Unable to allocate memory for new clip\n"); + return NULL; + } + + /* perhaps we can reclaim a clip id */ + for (n = 0; n <= next_avail_num; n++) { + if (avail_num[n] != 0) { + id = avail_num[n]; + avail_num[n] = 0; + break; + } + } + if (id == 0) { /* no we cannot not */ + if(this_clip_id==0) this_clip_id = 1; // first clip to create + si->clip_id = this_clip_id; + this_clip_id++; // set next number + } else { /* yet it is possible */ + si->clip_id = id; + //this_clip_id++; + } + sprintf(si->descr, "%s", "Untitled"); + for(n=0; n < CLIP_MAX_RENDER;n++) { + si->first_frame[n] = startFrame; + si->last_frame[n] = endFrame; + } + si->speed = 1; + si->looptype = 1; + si->max_loops = 0; + si->next_clip_id = 0; + si->playmode = 0; + si->depth = 0; + si->sub_audio = 0; + si->audio_volume = 50; + si->marker_start = 0; + si->marker_end = 0; + si->dup = 0; + si->active_render_entry = 0; + si->loop_dec = 0; + si->max_loops2 = 0; + si->fader_active = 0; + si->fader_val = 0; + si->fader_inc = 0; + si->fader_direction = 0; + si->rec_total_bytes = 0; + si->encoder_format = 0; + si->encoder_base = (char*) vj_malloc(sizeof(char) * 255); + si->sequence_num = 0; + si->encoder_duration = 0; + si->encoder_num_frames = 0; + si->encoder_destination = (char*) vj_malloc(sizeof(char) * 255); + si->encoder_succes_frames = 0; + si->encoder_active = 0; + si->encoder_total_frames = 0; + si->rec_total_bytes = 0; + si->encoder_max_size = 0; + si->encoder_width = 0; + si->encoder_height = 0; + si->encoder_duration = 0; + // si->encoder_buf = (char*) malloc(sizeof(char) * 10 * 65535 + 16); + si->auto_switch = 0; + si->selected_entry = 0; + si->effect_toggle = 1; + si->offset = 0; + sprintf(si->descr, "%s", "Untitled"); + + /* the effect chain is initially empty ! */ + for (i = 0; i < CLIP_MAX_EFFECTS; i++) { + + si->effect_chain[i] = + (clip_eff_chain *) vj_malloc(sizeof(clip_eff_chain)); + if (si->effect_chain[i] == NULL) { + veejay_msg(VEEJAY_MSG_ERROR, "Error allocating entry %d in Effect Chain for new clip",i); + return NULL; + } + si->effect_chain[i]->is_rendering = 0; + si->effect_chain[i]->effect_id = -1; + si->effect_chain[i]->e_flag = 0; + si->effect_chain[i]->frame_offset = -1; + si->effect_chain[i]->frame_trimmer = 0; + si->effect_chain[i]->volume = 50; + si->effect_chain[i]->a_flag = 0; + si->effect_chain[i]->source_type = 0; + si->effect_chain[i]->channel = id; /* with myself by default */ + /* effect parameters initially 0 */ + for (j = 0; j < CLIP_MAX_PARAMETERS; j++) { + si->effect_chain[i]->arg[j] = 0; + } + + } + return si; +} + +int clip_store(clip_info * skel) +{ + hnode_t *clip_node; + if (!skel) + return -1; + clip_node = hnode_create(skel); + if (!clip_node) + return -1; + if (!clip_exists(skel->clip_id)) { + hash_insert(ClipHash, clip_node, (void *) skel->clip_id); + } else { + hnode_put(clip_node, (void *) skel->clip_id); + } + return 0; +} + +/**************************************************************************************************** + * + * clip_get(int clip_id) + * + * returns clip information struct or NULL on error. + * + ****************************************************************************************************/ +clip_info *clip_get(int clip_id) +{ + clip_info *si; + //hnode_t *clip_node; + // if (!initialized) +// return NULL; + // if (clip_id <= 0) +// return NULL; + // for (i = 0; i <= next_avail_num; i++) +// if (avail_num[i] == clip_id) +// return NULL; + hnode_t *clip_node = hash_lookup(ClipHash, (void *) clip_id); + if (clip_node) { + si = (clip_info *) hnode_get(clip_node); + if(si) return si; + } + return NULL; +} + +/**************************************************************************************************** + * + * clip_exists(int clip_id) + * + * returns 1 if a clip exists in cliphash, or 0 if not. + * + ****************************************************************************************************/ + + +int clip_exists(int clip_id) { + + hnode_t *clip_node; + if (!clip_id) return 0; + + clip_node = hash_lookup(ClipHash, (void*) clip_id); + if (!clip_node) { + return 0; + } + + if(!clip_get(clip_id)) return 0; + return 1; +} +/* +int clip_exists(int clip_id) +{ + if(clip_id < 1 || clip_id > CLIP_MAX_CLIPS) return 0; + return (clip_get(clip_id) == NULL ? 0 : 1); +} +*/ + +int clip_copy(int clip_id) +{ + clip_info *org, *copy; + int c, i; + if (!clip_exists(clip_id)) + return -1; + org = clip_get(clip_id); + copy = clip_skeleton_new(org->first_frame[org->active_render_entry], org->last_frame[org->active_render_entry]); + + if (clip_store(copy) != 0) + return -1; + + clip_set_framedup(copy->clip_id, clip_get_framedup(clip_id)); + clip_set_speed(copy->clip_id, clip_get_speed(clip_id)); + clip_set_looptype(copy->clip_id, clip_get_looptype(clip_id)); + clip_set_next(copy->clip_id, clip_get_next(clip_id)); + clip_set_loops(copy->clip_id, clip_get_loops(clip_id)); + clip_set_depth(copy->clip_id, clip_get_depth(clip_id)); + + for (c = 0; c < CLIP_MAX_EFFECTS; c++) { + int effect_id = clip_get_effect(clip_id, c); + if (effect_id != -1) { + clip_chain_add(copy->clip_id, c, effect_id); + if (vj_effect_get_extra_frame(effect_id)) { + int source = clip_get_chain_source(clip_id, c); + + int args[CLIP_MAX_PARAMETERS]; + int *p = &args[0]; + int n_args = vj_effect_get_num_params(effect_id); + clip_set_chain_source(copy->clip_id, c, source); + clip_set_chain_channel(copy->clip_id, c, source); + + clip_get_all_effect_arg(clip_id, c, p, n_args, -1); + + for (i = 0; i < CLIP_MAX_PARAMETERS; i++) { + clip_set_effect_arg(copy->clip_id, c, i, args[i] + ); + } + } + } + } + return copy->clip_id; +} + +/**************************************************************************************************** + * + * clip_get_startFrame(int clip_id) + * + * returns first frame of clip. + * + ****************************************************************************************************/ +int clip_get_longest(int clip_id) +{ + clip_info *si = clip_get(clip_id); + if(si) + { + int len = (si->last_frame[si->active_render_entry] - + si->first_frame[si->active_render_entry] ); + int c = 0; + int tmp = 0; + int t=0; + int _id=0; + int speed = si->speed; + int duration = len / speed; //how many frames are played of this clip + + if( si->looptype == 2) duration *= 2; // pingpong loop duration + + for(c=0; c < CLIP_MAX_EFFECTS; c++) + { + _id = clip_get_chain_channel(clip_id,c); + t = clip_get_chain_source(clip_id,c); + + if(t==0 && clip_exists(_id)) + { + tmp = clip_get_endFrame( _id) - clip_get_startFrame(_id); + if(tmp>0) + { + tmp = tmp / clip_get_speed(_id); + if(tmp < 0) tmp *= -1; + if(clip_get_looptype(_id)==2) tmp *= 2; //pingpong loop underlying clip + } + if(tmp > duration) duration = tmp; //which one is longer ... + } + } + veejay_msg(VEEJAY_MSG_WARNING, "Length of clip in video frames: %ld",duration); + return duration; + } + return 0; +} + +int clip_get_startFrame(int clip_id) +{ + clip_info *si = clip_get(clip_id); + if (si) { + if (si->marker_start != 0 && si->marker_end != 0) + return si->marker_start; + else + return si->first_frame[si->active_render_entry]; + } + return -1; +} + +int clip_get_short_info(int clip_id, int *start, int *end, int *loop, int *speed) { + clip_info *si = clip_get(clip_id); + if(si) { + if(si->marker_start != 0 && si->marker_end !=0) { + *start = si->marker_start; + *end = si->marker_end; + } + else { + *start = si->first_frame[si->active_render_entry]; + *end = si->last_frame[si->active_render_entry]; + } + *speed = si->speed; + *loop = si->looptype; + return 0; + } + *start = 0; + *end = 0; + *loop = 0; + *speed = 0; + return -1; +} + +int clip_entry_is_rendering(int s1, int position) { + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS || position < 0) + return -1; + return clip->effect_chain[position]->is_rendering; +} + +int clip_entry_set_is_rendering(int s1, int position, int value) { + clip_info *si = clip_get(s1); + if (!si) + return -1; + if( position >= CLIP_MAX_EFFECTS || position < 0) return -1; + + si->effect_chain[position]->is_rendering = value; + return ( clip_update( si,s1 )); +} + + +int clip_update_offset(int s1, int n_frame) +{ + int len; + clip_info *si = clip_get(s1); + + if(!si) return -1; + si->offset = (n_frame - si->first_frame[si->active_render_entry]); + len = si->last_frame[si->active_render_entry] - si->first_frame[si->active_render_entry]; + if(si->offset < 0) + { + veejay_msg(VEEJAY_MSG_WARNING,"Clip bounces outside clip by %d frames", + si->offset); + si->offset = 0; + } + if(si->offset > len) + { + veejay_msg(VEEJAY_MSG_WARNING,"Clip bounces outside clip with %d frames", + si->offset); + si->offset = len; + } + return ( clip_update(si,s1)); +} + +int clip_set_manual_fader( int s1, int value) +{ + clip_info *si = clip_get(s1); + if(!si) return -1; + si->fader_active = 2; + si->fader_val = (float) value; + si->fader_inc = 0.0; + si->fader_direction = 0.0; + + /* inconsistency check */ + if(si->effect_toggle == 0) si->effect_toggle = 1; + return (clip_update(si,s1)); + +} + +int clip_set_fader_active( int s1, int nframes, int direction ) { + clip_info *si = clip_get(s1); + if(!si) return -1; + if(nframes <= 0) return -1; + si->fader_active = 1; + si->fader_val = 0.0; + si->fader_inc = (float) (255.0 / (float) nframes); + si->fader_direction = direction; + + /* inconsistency check */ + if(si->effect_toggle == 0) si->effect_toggle = 1; + return (clip_update(si,s1)); +} + + +int clip_reset_fader(int s1) { + clip_info *si = clip_get(s1); + if(!si) return -1; + si->fader_active = 0; + si->fader_val = 0; + si->fader_inc = 0; + return (clip_update(si,s1)); +} + +int clip_get_fader_active(int s1) { + clip_info *si = clip_get(s1); + if(!si) return -1; + return (si->fader_active); +} + +float clip_get_fader_val(int s1) { + clip_info *si = clip_get(s1); + if(!si) return -1; + return (si->fader_val); +} + +float clip_get_fader_inc(int s1) { + clip_info *si = clip_get(s1); + if(!si) return -1; + return (si->fader_inc); +} + +int clip_get_fader_direction(int s1) { + clip_info *si = clip_get(s1); + if(!si) return -1; + return si->fader_direction; +} + +int clip_set_fader_val(int s1, float val) { + clip_info *si = clip_get(s1); + if(!si) return -1; + si->fader_val = val; + return (clip_update(si,s1)); +} + +int clip_apply_fader_inc(int s1) { + clip_info *si = clip_get(s1); + if(!si) return -1; + si->fader_val += si->fader_inc; + if(si->fader_val > 255.0 ) si->fader_val = 255.0; + if(si->fader_val < 0.0 ) si->fader_val = 0.0; + clip_update(si,s1); + if(si->fader_direction) return si->fader_val; + return (255 - si->fader_val); +} + + + +int clip_set_fader_inc(int s1, float inc) { + clip_info *si = clip_get(s1); + if(!si) return -1; + si->fader_inc = inc; + return (clip_update(si,s1)); +} + +int clip_marker_clear(int clip_id) { + clip_info *si = clip_get(clip_id); + if (!si) + return -1; + si->marker_start = 0; + si->marker_end = 0; + if(si->marker_speed) { + si->speed = (si->speed * -1); + si->marker_speed=0; + } + veejay_msg(VEEJAY_MSG_INFO, "Marker cleared (%d - %d) - (speed=%d)", + si->marker_start, si->marker_end, si->speed); + return ( clip_update(si,clip_id)); +} + +int clip_set_marker_start(int clip_id, int marker) +{ + clip_info *si = clip_get(clip_id); + if (!si) + return -1; + if(si->marker_end != 0) { + /* the end frame is set, so it may not be > marker_end */ + if(marker > si->marker_end) return -1; + } + si->marker_start = marker; + return ( clip_update(si,clip_id)); +} + +int clip_set_marker(int clip_id, int start, int end) { + clip_info *si = clip_get(clip_id); + int tmp; + if(!si) return -1; + + if(end == -1 || end > si->last_frame[si->active_render_entry] ) + end = si->last_frame[si->active_render_entry]; + if(start < 0) start = 0; + if(start <= end) { + tmp = si->last_frame[si->active_render_entry] - end; + if( (si->first_frame[si->active_render_entry] + start) > si->last_frame[si->active_render_entry]) { + veejay_msg(VEEJAY_MSG_ERROR, "Invalid start parameter"); + return -1; + } + if( (si->last_frame[si->active_render_entry]-tmp) < si->first_frame[si->active_render_entry]) { + veejay_msg(VEEJAY_MSG_ERROR, "Invalid end parameter"); + return -1; + } + si->marker_start = si->first_frame[si->active_render_entry] + start; + si->marker_end = si->last_frame[si->active_render_entry] - tmp; + si->marker_speed = 0; + veejay_msg(VEEJAY_MSG_INFO, "Clip marker at %d - %d",si->marker_start, + si->marker_end); + } + return ( clip_update( si , clip_id ) ); +} + +int clip_set_marker_end(int clip_id, int marker) +{ + clip_info *si = clip_get(clip_id); + if (!si) + return -1; + if( si->marker_start == 0) return -1; + + /* if (si->speed < 0) { + si->marker_end = si->marker_start; + si->marker_start = marker; + } else { +*/ + + if(si->marker_start > marker) return -1; + si->marker_end = marker; + if(si->speed < 0) { + int swap = si->marker_start; + si->marker_start = marker; + si->marker_end = swap; + } + +// } + return (clip_update(si,clip_id)); +} + +int clip_set_description(int clip_id, char *description) +{ + clip_info *si = clip_get(clip_id); + if (!si) + return -1; + if (!description || strlen(description) <= 0) { + sprintf(si->descr, "%s", "Untitled"); + } else { + sprintf(si->descr, "%s", description); + } + return ( clip_update(si, clip_id)==1 ? 0 : 1); +} + +int clip_get_description(int clip_id, char *description) +{ + clip_info *si; + si = clip_get(clip_id); + if (!si) + return -1; + sprintf(description, "%s", si->descr); + return 0; +} + +/**************************************************************************************************** + * + * clip_get_endFrame(int clip_id) + * + * returns last frame of clip. + * + ****************************************************************************************************/ +int clip_get_endFrame(int clip_id) +{ + clip_info *si = clip_get(clip_id); + if (si) { + if (si->marker_end != 0 && si->marker_start != 0) + return si->marker_end; + else { + return si->last_frame[si->active_render_entry]; + } + } + return -1; +} +/**************************************************************************************************** + * + * clip_del( clip_nr ) + * + * deletes a clip from the hash. returns -1 on error, 1 on success. + * + ****************************************************************************************************/ +int clip_del(int clip_id) +{ + hnode_t *clip_node; + clip_info *si; + si = clip_get(clip_id); + if (!si) + return -1; + + clip_node = hash_lookup(ClipHash, (void *) si->clip_id); + if (clip_node) { + int i; + for(i=0; i < CLIP_MAX_EFFECTS; i++) + { + if (si->effect_chain[i]) + free(si->effect_chain[i]); + } + if (si) + free(si); + /* store freed clip_id */ + avail_num[next_avail_num] = clip_id; + next_avail_num++; + hash_delete(ClipHash, clip_node); + + return 1; + } + + return -1; +} + + +void clip_del_all() +{ + int end = clip_size(); + int i; + for (i = 0; i < end; i++) { + if (clip_exists(i)) { + clip_del(i); + } + } + this_clip_id = 0; +} + +/**************************************************************************************************** + * + * clip_get_effect( clip_nr , position) + * + * returns effect in effect_chain on position X , -1 on error. + * + ****************************************************************************************************/ +int clip_get_effect(int s1, int position) +{ + clip_info *clip = clip_get(s1); + if(position >= CLIP_MAX_EFFECTS || position < 0 ) return -1; + if(clip) { + if(clip->effect_chain[position]->e_flag==0) return -1; + return clip->effect_chain[position]->effect_id; + } + return -1; +} + +int clip_get_effect_any(int s1, int position) { + clip_info *clip = clip_get(s1); + if(position >= CLIP_MAX_EFFECTS || position < 0 ) return -1; + if(clip) { + return clip->effect_chain[position]->effect_id; + } + return -1; +} + +int clip_get_chain_status(int s1, int position) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + return clip->effect_chain[position]->e_flag; +} + + +int clip_get_offset(int s1, int position) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + return clip->effect_chain[position]->frame_offset; +} + +int clip_get_trimmer(int s1, int position) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + return clip->effect_chain[position]->frame_trimmer; +} + +int clip_get_chain_volume(int s1, int position) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + return clip->effect_chain[position]->volume; +} + +int clip_get_chain_audio(int s1, int position) +{ + clip_info *clip = clip_get(s1); + if (clip) { + return clip->effect_chain[position]->a_flag; + } + return -1; +} + +/**************************************************************************************************** + * + * clip_get_looptype + * + * returns the type of loop set on the clip. 0 on no loop, 1 on ping pong + * returns -1 on error. + * + ****************************************************************************************************/ + +int clip_get_looptype(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) { + return clip->looptype; + } + return 0; +} + +int clip_get_playmode(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) { + return clip->playmode; + } + return -1; +} + +/******************** + * get depth: 1 means do what is in underlying clip. + *******************/ +int clip_get_depth(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) + return clip->depth; + return 0; +} + +int clip_set_depth(int s1, int n) +{ + clip_info *clip; + hnode_t *clip_node; + + if (n == 0 || n == 1) { + clip = clip_get(s1); + if (!clip) + return -1; + if (clip->depth == n) + return 1; + clip->depth = n; + clip_node = hnode_create(clip); + if (!clip_node) { + return -1; + } + return 1; + } + return -1; +} +int clip_set_chain_status(int s1, int position, int status) +{ + clip_info *clip; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + clip = clip_get(s1); + if (!clip) + return -1; + clip->effect_chain[position]->e_flag = status; + clip_update(clip,s1); + return 1; +} + +/**************************************************************************************************** + * + * clip_get_speed + * + * returns the playback speed set on the clip. + * returns -1 on error. + * + ****************************************************************************************************/ +int clip_get_render_entry(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) + return clip->active_render_entry; + return 0; +} + +int clip_get_speed(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) + return clip->speed; + return 0; +} + +int clip_get_framedup(int s1) { + clip_info *clip = clip_get(s1); + if(clip) return clip->dup; + return 0; +} + + +int clip_get_effect_status(int s1) +{ + clip_info *clip = clip_get(s1); + if(clip) return clip->effect_toggle; + return 0; +} + +/**************************************************************************************************** + * + * clip_get_effect_arg( clip_nr, position, argnr ) + * + * returns the required argument set on position X in the effect_chain of clip Y. + * returns -1 on error. + ****************************************************************************************************/ +int clip_get_effect_arg(int s1, int position, int argnr) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + if (argnr < 0 || argnr > CLIP_MAX_PARAMETERS) + return -1; + return clip->effect_chain[position]->arg[argnr]; +} + +int clip_get_selected_entry(int s1) +{ + clip_info *clip; + clip = clip_get(s1); + if(!clip) return -1; + return clip->selected_entry; +} + +int clip_get_all_effect_arg(int s1, int position, int *args, int arg_len, int n_frame) +{ + int i; + clip_info *clip; + clip = clip_get(s1); + if( arg_len == 0) + return 1; + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + if (arg_len < 0 || arg_len > CLIP_MAX_PARAMETERS) + return -1; + for (i = 0; i < arg_len; i++) { + args[i] = clip->effect_chain[position]->arg[i]; + } + return i; +} + +/******************************************** + * clip_has_extra_frame. + * return 1 if an effect on the given chain entry + * requires another frame, -1 otherwise. + */ +int clip_has_extra_frame(int s1, int position) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + if (clip->effect_chain[position]->effect_id == -1) + return -1; + if (vj_effect_get_extra_frame + (clip->effect_chain[position]->effect_id) == 1) + return 1; + return -1; +} + +/**************************************************************************************************** + * + * clip_set_effect_arg + * + * sets an argument ARGNR in the chain on position X of clip Y + * returns -1 on error. + * + ****************************************************************************************************/ + +int clip_set_effect_arg(int s1, int position, int argnr, int value) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if (position >= CLIP_MAX_EFFECTS) + return -1; + if (argnr < 0 || argnr > CLIP_MAX_PARAMETERS) + return -1; + clip->effect_chain[position]->arg[argnr] = value; + return ( clip_update(clip,s1)); +} + +int clip_set_selected_entry(int s1, int position) +{ + clip_info *clip = clip_get(s1); + if(!clip) return -1; + if(position< 0 || position >= CLIP_MAX_EFFECTS) return -1; + clip->selected_entry = position; + return (clip_update(clip,s1)); +} + +int clip_set_effect_status(int s1, int status) +{ + clip_info *clip = clip_get(s1); + if(!clip) return -1; + if(status == 1 || status == 0 ) + { + clip->effect_toggle = status; + return ( clip_update(clip,s1)); + } + return -1; +} + +int clip_set_chain_channel(int s1, int position, int input) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + clip->effect_chain[position]->channel = input; + return ( clip_update(clip,s1)); +} + +int clip_is_deleted(int s1) +{ + int i; + for (i = 0; i < next_avail_num; i++) { + if (avail_num[i] == s1) + return 1; + } + return 0; +} + +int clip_set_chain_source(int s1, int position, int input) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + clip->effect_chain[position]->source_type = input; + return (clip_update(clip,s1)); +} + +/**************************************************************************************************** + * + * clip_set_speed + * + * store playback speed in the clip. + * returns -1 on error. + * + ****************************************************************************************************/ + +int clip_set_speed(int s1, int speed) +{ + clip_info *clip = clip_get(s1); + if (!clip) return -1; + clip->speed = speed; + return ( clip_update(clip,s1)); +} + +int clip_set_render_entry(int s1, int entry) +{ + clip_info *clip = clip_get(s1); + if (!clip) return -1; + if( entry < 0 || entry >= CLIP_MAX_RENDER) return -1; + clip->active_render_entry = entry; + return ( clip_update(clip,s1)); +} + +int clip_set_framedup(int s1, int n) { + clip_info *clip = clip_get(s1); + if(!clip) return -1; + clip->dup = n; + return ( clip_update(clip,s1)); +} + +int clip_get_chain_channel(int s1, int position) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + return clip->effect_chain[position]->channel; +} + +int clip_get_chain_source(int s1, int position) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + return clip->effect_chain[position]->source_type; +} + +int clip_get_loops(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) { + return clip->max_loops; + } + return -1; +} +int clip_get_loops2(int s1) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + return clip->max_loops2; +} + +/**************************************************************************************************** + * + * clip_set_looptype + * + * store looptype in the clip. + * returns -1 on error. + * + ****************************************************************************************************/ + +int clip_set_looptype(int s1, int looptype) +{ + clip_info *clip = clip_get(s1); + if(!clip) return -1; + + if (looptype == 0 || looptype == 1 || looptype == 2) { + clip->looptype = looptype; + return ( clip_update(clip,s1)); + } + return -1; +} + +int clip_set_playmode(int s1, int playmode) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + + clip->playmode = playmode; + return ( clip_update(clip,s1)); +} + + + +/************************************************************************************************* + * update start frame + * + *************************************************************************************************/ +int clip_set_startframe(int s1, long frame_num) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if(frame_num < 0) return frame_num = 0; + clip->first_frame[clip->active_render_entry] = frame_num; + return (clip_update(clip,s1)); +} + +int clip_set_endframe(int s1, long frame_num) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if(frame_num < 0) return -1; + clip->last_frame[clip->active_render_entry] = frame_num; + return (clip_update(clip,s1)); +} +int clip_get_next(int s1) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + return clip->next_clip_id; +} +int clip_set_loops(int s1, int nr_of_loops) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + clip->max_loops = nr_of_loops; + return (clip_update(clip,s1)); +} +int clip_set_loops2(int s1, int nr_of_loops) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + clip->max_loops2 = nr_of_loops; + return (clip_update(clip,s1)); +} + +int clip_get_sub_audio(int s1) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + return clip->sub_audio; +} + +int clip_set_sub_audio(int s1, int audio) +{ + clip_info *clip = clip_get(s1); + if(!clip) return -1; + if (audio < 0 && audio > 1) + return -1; + clip->sub_audio = audio; + return (clip_update(clip,s1)); +} + +int clip_get_audio_volume(int s1) +{ + clip_info *clip = clip_get(s1); + if (clip) { + return clip->audio_volume; + } + return -1; +} + +int clip_set_audio_volume(int s1, int volume) +{ + clip_info *clip = clip_get(s1); + if (volume < 0) + volume = 0; + if (volume > 100) + volume = 100; + clip->audio_volume = volume; + return (clip_update(clip,s1)); +} + + +int clip_set_next(int s1, int next_clip_id) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + /* just add, do not verify + on module generation, next clip may not yet be created. + checks in parameter set in libveejayvj.c + */ + clip->next_clip_id = next_clip_id; + return (clip_update(clip,s1)); +} + +/**************************************************************************************************** + * + * + * add a new effect to the chain, returns chain index number on success or -1 if + * the requested clip does not exist. + * + ****************************************************************************************************/ + +int clip_chain_malloc(int s1) +{ + clip_info *clip = clip_get(s1); + int i=0; + int e_id = 0; + int sum =0; + if (!clip) + return -1; + for(i=0; i < CLIP_MAX_EFFECTS; i++) + { + e_id = clip->effect_chain[i]->effect_id; + if(e_id) + { + if(vj_effect_activate(e_id)) + sum++; + } + } + veejay_msg(VEEJAY_MSG_INFO, "Allocated %d effects",sum); + return sum; +} + +int clip_chain_free(int s1) +{ + clip_info *clip = clip_get(s1); + int i=0; + int e_id = 0; + int sum = 0; + if (!clip) + return -1; + for(i=0; i < CLIP_MAX_EFFECTS; i++) + { + e_id = clip->effect_chain[i]->effect_id; + if(e_id!=-1) + { + if(vj_effect_initialized(e_id)) + { + vj_effect_deactivate(e_id); + sum++; + } + } + } + return sum; +} + +int clip_chain_add(int s1, int c, int effect_nr) +{ + int effect_params = 0, i; + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if (c < 0 || c >= CLIP_MAX_EFFECTS) + return -1; + + if ( effect_nr < VJ_IMAGE_EFFECT_MIN ) return -1; + + if ( effect_nr > VJ_IMAGE_EFFECT_MAX && effect_nr < VJ_VIDEO_EFFECT_MIN ) + return -1; + + if ( effect_nr > VJ_VIDEO_EFFECT_MAX ) + return -1; + +/* + if(clip->effect_chain[c]->effect_id != -1 && + clip->effect_chain[c]->effect_id != effect_nr && + vj_effect_initialized( clip->effect_chain[c]->effect_id )) + { + veejay_msg(VEEJAY_MSG_DEBUG, "Effect %s must be freed??", vj_effect_get_description( + clip->effect_chain[c]->effect_id)); + vj_effect_deactivate( clip->effect_chain[c]->effect_id ); + } +*/ + if( clip->effect_chain[c]->effect_id != -1 && clip->effect_chain[c]->effect_id != effect_nr ) + { + //verify if the effect should be discarded + if(vj_effect_initialized( clip->effect_chain[c]->effect_id )) + { + // it is using some memory, see if we can free it ... + int i; + int ok = 1; + for(i=(c+1); i < CLIP_MAX_EFFECTS; i++) + { + if( clip->effect_chain[i]->effect_id == clip->effect_chain[c]->effect_id) ok = 0; + } + // ok, lets get rid of it. + if( ok ) vj_effect_deactivate( clip->effect_chain[c]->effect_id ); + } + } + + + if(!vj_effect_initialized(effect_nr)) + { + veejay_msg(VEEJAY_MSG_DEBUG, "Effect %s must be initialized now", + vj_effect_get_description(effect_nr)); + if(!vj_effect_activate( effect_nr )) return -1; + } + + clip->effect_chain[c]->effect_id = effect_nr; + clip->effect_chain[c]->e_flag = 1; /* effect enabled standard */ + effect_params = vj_effect_get_num_params(effect_nr); + if (effect_params > 0) + { + /* there are parameters, set default values */ + for (i = 0; i < effect_params; i++) + { + int val = vj_effect_get_default(effect_nr, i); + clip->effect_chain[c]->arg[i] = val; + } + } + if (vj_effect_get_extra_frame(effect_nr) == 1) + { + clip->effect_chain[c]->frame_offset = 0; + clip->effect_chain[c]->frame_trimmer = 0; + clip->effect_chain[c]->channel = s1; + clip->effect_chain[c]->source_type = 0; + if(s1 > 1) + { /* there is likely more than 1 sample*/ + int s2 = s1 - 1; + if(clip_exists(s2)) + { + clip->effect_chain[c]->channel = s2; + } + else + { + s2 = s1 + 1; + if(clip_exists(s2)) + clip->effect_chain[c]->channel = s2; + } + } + veejay_msg(VEEJAY_MSG_DEBUG,"Effect %s on entry %d overlaying with clip %d", + vj_effect_get_description(clip->effect_chain[c]->effect_id),c,clip->effect_chain[c]->channel); + } + clip_update(clip,s1); + + return c; /* return position on which it was added */ +} + +int clip_reset_offset(int s1) +{ + clip_info *clip = clip_get(s1); + int i; + if(!clip) return -1; + for(i=0; i < CLIP_MAX_EFFECTS; i++) + { + clip->effect_chain[i]->frame_offset = 0; + } + return ( clip_update(clip,s1)); +} + +int clip_set_offset(int s1, int chain_entry, int frame_offset) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + /* set to zero if frame_offset is greater than clip length */ + //if(frame_offset > (clip->last_frame - clip->first_frame)) frame_offset=0; + clip->effect_chain[chain_entry]->frame_offset = frame_offset; + return (clip_update(clip,s1)); +} + +int clip_set_trimmer(int s1, int chain_entry, int trimmer) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + /* set to zero if frame_offset is greater than clip length */ + if (chain_entry < 0 || chain_entry >= CLIP_MAX_PARAMETERS) + return -1; + if (trimmer > (clip->last_frame[clip->active_render_entry] - clip->first_frame[clip->active_render_entry])) + trimmer = 0; + if (trimmer < 0 ) trimmer = 0; + clip->effect_chain[chain_entry]->frame_trimmer = trimmer; + + return (clip_update(clip,s1)); +} +int clip_set_chain_audio(int s1, int chain_entry, int val) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + if (chain_entry < 0 || chain_entry >= CLIP_MAX_PARAMETERS) + return -1; + clip->effect_chain[chain_entry]->a_flag = val; + return ( clip_update(clip,s1)); +} + +int clip_set_chain_volume(int s1, int chain_entry, int volume) +{ + clip_info *clip = clip_get(s1); + if (!clip) + return -1; + /* set to zero if frame_offset is greater than clip length */ + if (volume < 0) + volume = 100; + if (volume > 100) + volume = 0; + clip->effect_chain[chain_entry]->volume = volume; + return (clip_update(clip,s1)); +} + + + + +/**************************************************************************************************** + * + * clip_chain_clear( clip_nr ) + * + * clear the entire effect chain. + * + ****************************************************************************************************/ + +int clip_chain_clear(int s1) +{ + int i, j; + clip_info *clip = clip_get(s1); + + if (!clip) + return -1; + /* the effect chain is gonna be empty! */ + for (i = 0; i < CLIP_MAX_EFFECTS; i++) { + if(clip->effect_chain[i]->effect_id != -1) + { + if(vj_effect_initialized( clip->effect_chain[i]->effect_id )) + vj_effect_deactivate( clip->effect_chain[i]->effect_id ); + } + clip->effect_chain[i]->effect_id = -1; + clip->effect_chain[i]->frame_offset = -1; + clip->effect_chain[i]->frame_trimmer = 0; + clip->effect_chain[i]->volume = 0; + clip->effect_chain[i]->a_flag = 0; + clip->effect_chain[i]->source_type = 0; + clip->effect_chain[i]->channel = s1; + for (j = 0; j < CLIP_MAX_PARAMETERS; j++) + clip->effect_chain[i]->arg[j] = 0; + } + + return (clip_update(clip,s1)); +} + + +/**************************************************************************************************** + * + * clip_chain_size( clip_nr ) + * + * returns the number of effects in the effect_chain + * + ****************************************************************************************************/ +int clip_chain_size(int s1) +{ + int i, e; + + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + e = 0; + for (i = 0; i < CLIP_MAX_EFFECTS; i++) + if (clip->effect_chain[i]->effect_id != -1) + e++; + return e; +} + +/**************************************************************************************************** + * + * clip_chain_get_free_entry( clip_nr ) + * + * returns last available entry + * + ****************************************************************************************************/ +int clip_chain_get_free_entry(int s1) +{ + int i; + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + for (i = 0; i < CLIP_MAX_EFFECTS; i++) + if (clip->effect_chain[i]->effect_id == -1) + return i; + return -1; +} + + +/**************************************************************************************************** + * + * clip_chain_remove( clip_nr, position ) + * + * Removes an Effect from the chain of clip on entry + * + ****************************************************************************************************/ + +static int _clip_can_free(clip_info *clip, int reserved, int effect_id) +{ + int i; + for(i=0; i < CLIP_MAX_EFFECTS; i++) + { + if(i != reserved && effect_id == clip->effect_chain[i]->effect_id) return 0; + } + return 1; +} + +int clip_chain_remove(int s1, int position) +{ + int j; + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + if (position < 0 || position >= CLIP_MAX_EFFECTS) + return -1; + if(clip->effect_chain[position]->effect_id != -1) + { + if(vj_effect_initialized( clip->effect_chain[position]->effect_id) && + _clip_can_free(clip,position, clip->effect_chain[position]->effect_id)) + vj_effect_deactivate( clip->effect_chain[position]->effect_id); + } + clip->effect_chain[position]->effect_id = -1; + clip->effect_chain[position]->frame_offset = -1; + clip->effect_chain[position]->frame_trimmer = 0; + clip->effect_chain[position]->volume = 0; + clip->effect_chain[position]->a_flag = 0; + clip->effect_chain[position]->source_type = 0; + clip->effect_chain[position]->channel = 0; + for (j = 0; j < CLIP_MAX_PARAMETERS; j++) + clip->effect_chain[position]->arg[j] = 0; + + return (clip_update(clip,s1)); +} + +int clip_set_loop_dec(int s1, int active, int periods) { + clip_info *clip = clip_get(s1); + if(!clip) return -1; + if(periods <=0) return -1; + if(periods > 25) return -1; + clip->loop_dec = active; + clip->loop_periods = periods; + return (clip_update(clip,s1)); +} + +int clip_get_loop_dec(int s1) { + clip_info *clip = clip_get(s1); + if(!clip) return -1; + return clip->loop_dec; +} + +int clip_apply_loop_dec(int s1, double fps) { + clip_info *clip = clip_get(s1); + int inc = (int) fps; + if(!clip) return -1; + if(clip->loop_dec==1) { + if( (clip->first_frame[clip->active_render_entry] + inc) >= clip->last_frame[clip->active_render_entry]) { + clip->first_frame[clip->active_render_entry] = clip->last_frame[clip->active_render_entry]-1; + clip->loop_dec = 0; + } + else { + clip->first_frame[clip->active_render_entry] += (inc / clip->loop_periods); + } + veejay_msg(VEEJAY_MSG_DEBUG, "New starting postions are %ld - %ld", + clip->first_frame[clip->active_render_entry], clip->last_frame[clip->active_render_entry]); + return ( clip_update(clip, s1)); + } + return -1; +} + + +/* print clip status information into an allocated string str*/ +int clip_chain_sprint_status(int s1, int entry, int changed, int r_changed,char *str, + int frame) +{ + clip_info *clip; + clip = clip_get(s1); + if (!clip) + return -1; + /* + fprintf(stderr, + "%d %d %d %d %d %d %ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + frame, + clip->active_render_entry, + r_changed, + s1, + clip->first_frame[clip->active_render_entry], + clip->last_frame[clip->active_render_entry], + clip->speed, + clip->looptype, + clip->max_loops, + clip->max_loops2, + clip->next_clip_id, + clip->depth, + clip->playmode, + clip->audio_volume, + clip->selected_entry, + clip->effect_toggle, + changed, + vj_effect_real_to_sequence(clip->effect_chain[entry]->effect_id), + // effect_id), + clip->effect_chain[entry]->e_flag, + clip->effect_chain[entry]->frame_offset, + clip->effect_chain[entry]->frame_trimmer, + clip->effect_chain[entry]->source_type, + clip->effect_chain[entry]->channel, + this_clip_id - 1); + */ + + + sprintf(str, + "%d %d %d %d %d %d %ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %ld %ld %d %d %d %d %d %d %d %d %d %d %d", +/* 1 */ frame, + clip->active_render_entry, + r_changed, + clip->selected_entry, +/* 5 */ clip->effect_toggle, + s1, + clip->first_frame[clip->active_render_entry], + clip->last_frame[clip->active_render_entry], + clip->speed, + clip->looptype, +/* 11 */ clip->max_loops, + clip->max_loops2, + clip->next_clip_id, + clip->depth, + clip->playmode, + clip->dup, + clip->audio_volume, + 0, + 0, +/*20 */ 0, + clip->encoder_active, + clip->encoder_duration, + clip->encoder_succes_frames, + clip->auto_switch, + changed, + vj_effect_real_to_sequence(clip->effect_chain[entry]->effect_id), + // effect_id), + clip->effect_chain[entry]->e_flag, + clip->effect_chain[entry]->frame_offset, + clip->effect_chain[entry]->frame_trimmer, + clip->effect_chain[entry]->source_type, + clip->effect_chain[entry]->channel, + clip->effect_chain[entry]->a_flag, + clip->effect_chain[entry]->volume, + this_clip_id ); + + return 0; +} + + +#ifdef HAVE_XML2 +/************************************************************************************************* + * + * UTF8toLAT1() + * + * convert an UTF8 string to ISO LATIN 1 string + * + ****************************************************************************************************/ +unsigned char *UTF8toLAT1(unsigned char *in) +{ + int in_size, out_size; + unsigned char *out; + + if (in == NULL) + return (NULL); + + out_size = in_size = (int) strlen(in) + 1; + out = malloc((size_t) out_size); + + if (out == NULL) { + return (NULL); + } + + if (UTF8Toisolat1(out, &out_size, in, &in_size) != 0) { + fprintf(stderr, "Cannot convert %s\n", in); + free(out); + return (NULL); + } + + out = realloc(out, out_size + 1); + out[out_size] = 0; /*null terminating out */ + + return (out); +} + +/************************************************************************************************* + * + * ParseArguments() + * + * Parse the effect arguments using libxml2 + * + ****************************************************************************************************/ +void ParseArguments(xmlDocPtr doc, xmlNodePtr cur, int *arg) +{ + xmlChar *xmlTemp = NULL; + unsigned char *chTemp = NULL; + int argIndex = 0; + if (cur == NULL) + fprintf(stderr, "error parsing arguments\n"); + while (cur != NULL && argIndex < CLIP_MAX_PARAMETERS) { + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_ARGUMENT)) + { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + arg[argIndex] = atoi(chTemp); + argIndex++; + } + if (xmlTemp) + xmlFree(xmlTemp); + if (chTemp) + free(chTemp); + + } + // xmlTemp and chTemp should be freed after use + xmlTemp = NULL; + chTemp = NULL; + cur = cur->next; + } +} + + +/************************************************************************************************* + * + * ParseEffect() + * + * Parse an effect using libxml2 + * + ****************************************************************************************************/ +void ParseEffect(xmlDocPtr doc, xmlNodePtr cur, int dst_clip) +{ + xmlChar *xmlTemp = NULL; + unsigned char *chTemp = NULL; + int effect_id = -1; + int arg[CLIP_MAX_PARAMETERS]; + int i; + int source_type = 0; + int channel = 0; + int frame_trimmer = 0; + int frame_offset = 0; + int e_flag = 0; + int volume = 0; + int a_flag = 0; + int chain_index = 0; + + for (i = 0; i < CLIP_MAX_PARAMETERS; i++) { + arg[i] = 0; + } + + if (cur == NULL) + fprintf(stderr, "Error in parseEffect\n"); + + + while (cur != NULL) { + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTID)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + effect_id = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTPOS)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + chain_index = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_ARGUMENTS)) { + ParseArguments(doc, cur->xmlChildrenNode, arg); + } + + /* add source,channel,trimmer,e_flag */ + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTSOURCE)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + source_type = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTCHANNEL)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + channel = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTTRIMMER)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + frame_trimmer = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTOFFSET)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + frame_offset = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECTACTIVE)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + e_flag = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + + } + + if (!xmlStrcmp + (cur->name, (const xmlChar *) XMLTAG_EFFECTAUDIOFLAG)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + a_flag = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + + } + + if (!xmlStrcmp + (cur->name, (const xmlChar *) XMLTAG_EFFECTAUDIOVOLUME)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + volume = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + // xmlTemp and chTemp should be freed after use + xmlTemp = NULL; + chTemp = NULL; + cur = cur->next; + } + + + if (effect_id != -1) { + int j; + if (clip_chain_add(dst_clip, chain_index, effect_id) == -1) { + fprintf(stderr, "Error parsing effect %d (pos %d)\n", + effect_id, chain_index); + } + + /* load the parameter values */ + for (j = 0; j < vj_effect_get_num_params(effect_id); j++) { + clip_set_effect_arg(dst_clip, chain_index, j, arg[j]); + } + fprintf(stderr, "clip %d %d - E:%d p:%d, source %d channel %d\n", + dst_clip,chain_index,effect_id,j,source_type,channel); + clip_set_chain_channel(dst_clip, chain_index, channel); + clip_set_chain_source(dst_clip, chain_index, source_type); + + /* set other parameters */ + if (a_flag) { + clip_set_chain_audio(dst_clip, chain_index, a_flag); + clip_set_chain_volume(dst_clip, chain_index, volume); + } + + clip_set_chain_status(dst_clip, chain_index, e_flag); + + clip_set_offset(dst_clip, chain_index, frame_offset); + clip_set_trimmer(dst_clip, chain_index, frame_trimmer); + } + +} + +/************************************************************************************************* + * + * ParseEffect() + * + * Parse the effects array + * + ****************************************************************************************************/ +void ParseEffects(xmlDocPtr doc, xmlNodePtr cur, clip_info * skel) +{ + int effectIndex = 0; + while (cur != NULL && effectIndex < CLIP_MAX_EFFECTS) { + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_EFFECT)) { + ParseEffect(doc, cur->xmlChildrenNode, skel->clip_id); + effectIndex++; + } + //effectIndex++; + cur = cur->next; + } +} + +/************************************************************************************************* + * + * ParseClip() + * + * Parse a clip + * + ****************************************************************************************************/ +void ParseClip(xmlDocPtr doc, xmlNodePtr cur, clip_info * skel) +{ + + xmlChar *xmlTemp = NULL; + unsigned char *chTemp = NULL; + + while (cur != NULL) { + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_CLIPID)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + skel->clip_id = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_CHAIN_ENABLED)) + { + xmlTemp = xmlNodeListGetString( doc, cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1( xmlTemp ); + if(chTemp) + { + skel->effect_toggle = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_CLIPDESCR)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + sprintf(skel->descr, "%s", chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_FIRSTFRAME)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_startframe(skel->clip_id, atol(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_VOL)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_audio_volume(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_LASTFRAME)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_endframe(skel->clip_id, atol(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_SPEED)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_speed(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_FRAMEDUP)) { + xmlTemp = xmlNodeListGetString(doc,cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1(xmlTemp); + if(chTemp) + { + clip_set_framedup(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_LOOPTYPE)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_looptype(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_MAXLOOPS)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_loops(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_NEXTCLIP)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_next(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_DEPTH)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_depth(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_PLAYMODE)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_playmode(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name,(const xmlChar *) XMLTAG_FADER_ACTIVE)) { + xmlTemp = xmlNodeListGetString(doc,cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1(xmlTemp); + if(chTemp) { + skel->fader_active = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name,(const xmlChar *) XMLTAG_FADER_VAL)) { + xmlTemp = xmlNodeListGetString(doc,cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1(xmlTemp); + if(chTemp){ + skel->fader_val = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name,(const xmlChar*) XMLTAG_FADER_INC)) { + xmlTemp = xmlNodeListGetString(doc,cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1(xmlTemp); + if(chTemp) { + skel->fader_inc = atof(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + + if (!xmlStrcmp(cur->name,(const xmlChar*) XMLTAG_FADER_DIRECTION)) { + xmlTemp = xmlNodeListGetString(doc,cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1(xmlTemp); + if(chTemp) { + skel->fader_inc = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if(!xmlStrcmp(cur->name,(const xmlChar*) XMLTAG_LASTENTRY)) { + xmlTemp = xmlNodeListGetString(doc,cur->xmlChildrenNode,1); + chTemp = UTF8toLAT1(xmlTemp); + if(chTemp) { + skel->selected_entry = atoi(chTemp); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + /* + if (!xmlStrcmp(cur->name, (const xmlChar *)XMLTAG_VOLUME)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1( xmlTemp ); + if( chTemp ){ + //clip_set_volume(skel->clip_id, atoi(chTemp )); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)XMLTAG_SUBAUDIO)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1( xmlTemp ); + if( chTemp ){ + clip_set_sub_audio(skel->clip_id, atoi(chTemp )); + } + } + */ + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_MARKERSTART)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_marker_start(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + } + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_MARKEREND)) { + xmlTemp = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + chTemp = UTF8toLAT1(xmlTemp); + if (chTemp) { + clip_set_marker_end(skel->clip_id, atoi(chTemp)); + free(chTemp); + } + if(xmlTemp) xmlFree(xmlTemp); + + } + + ParseEffects(doc, cur->xmlChildrenNode, skel); + + // xmlTemp and chTemp should be freed after use + xmlTemp = NULL; + chTemp = NULL; + + cur = cur->next; + } + return; +} + + +/**************************************************************************************************** + * + * clip_readFromFile( filename ) + * + * load clips and effect chain from an xml file. + * + ****************************************************************************************************/ +int clip_readFromFile(char *clipFile) +{ + xmlDocPtr doc; + xmlNodePtr cur; + clip_info *skel; + + /* + * build an XML tree from a the file; + */ + doc = xmlParseFile(clipFile); + if (doc == NULL) { + return (0); + } + + /* + * Check the document is of the right kind + */ + + cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + fprintf(stderr, "Empty cliplist. Nothing to do.\n"); + xmlFreeDoc(doc); + return (0); + } + + if (xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_CLIPS)) { + fprintf(stderr, "This is not a cliplist: %s", + XMLTAG_CLIPS); + xmlFreeDoc(doc); + return (0); + } + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if (!xmlStrcmp(cur->name, (const xmlChar *) XMLTAG_CLIP)) { + skel = clip_skeleton_new(0, 0); + clip_store(skel); + if (skel != NULL) { + ParseClip(doc, cur->xmlChildrenNode, skel); + } + } + cur = cur->next; + } + xmlFreeDoc(doc); + + return (1); +} + +void CreateArguments(xmlNodePtr node, int *arg, int argcount) +{ + int i; + char buffer[100]; + argcount = CLIP_MAX_PARAMETERS; + for (i = 0; i < argcount; i++) { + //if (arg[i]) { + sprintf(buffer, "%d", arg[i]); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_ARGUMENT, + (const xmlChar *) buffer); + //} + } +} + + +void CreateEffect(xmlNodePtr node, clip_eff_chain * effect, int position) +{ + char buffer[100]; + xmlNodePtr childnode; + + sprintf(buffer, "%d", position); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTPOS, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->effect_id); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTID, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->e_flag); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTACTIVE, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->source_type); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTSOURCE, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->channel); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTCHANNEL, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->frame_offset); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTOFFSET, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->frame_trimmer); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTTRIMMER, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->a_flag); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTAUDIOFLAG, + (const xmlChar *) buffer); + + sprintf(buffer, "%d", effect->volume); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTAUDIOVOLUME, + (const xmlChar *) buffer); + + + childnode = + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_ARGUMENTS, NULL); + CreateArguments(childnode, effect->arg, + vj_effect_get_num_params(effect->effect_id)); + + +} + + + + +void CreateEffects(xmlNodePtr node, clip_eff_chain ** effects) +{ + int i; + xmlNodePtr childnode; + + for (i = 0; i < CLIP_MAX_EFFECTS; i++) { + if (effects[i]->effect_id != -1) { + childnode = + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECT, + NULL); + CreateEffect(childnode, effects[i], i); + } + } + +} + +void CreateClip(xmlNodePtr node, clip_info * clip) +{ + char buffer[100]; + xmlNodePtr childnode; + + sprintf(buffer, "%d", clip->clip_id); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_CLIPID, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->effect_toggle); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_CHAIN_ENABLED, + (const xmlChar *) buffer); + + + sprintf(buffer,"%d", clip->active_render_entry); + xmlNewChild(node,NULL,(const xmlChar*) XMLTAG_RENDER_ENTRY,(const xmlChar*)buffer); + sprintf(buffer, "%s", clip->descr); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_CLIPDESCR, + (const xmlChar *) buffer); + sprintf(buffer, "%ld", clip->first_frame[clip->active_render_entry]); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_FIRSTFRAME, + (const xmlChar *) buffer); + sprintf(buffer, "%ld", clip->last_frame[clip->active_render_entry]); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_LASTFRAME, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->speed); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_SPEED, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->dup); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_FRAMEDUP, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->looptype); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_LOOPTYPE, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->max_loops); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_MAXLOOPS, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->next_clip_id); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_NEXTCLIP, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->depth); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_DEPTH, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->playmode); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_PLAYMODE, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->audio_volume); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_VOL, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->marker_start); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_MARKERSTART, + (const xmlChar *) buffer); + sprintf(buffer, "%d", clip->marker_end); + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_MARKEREND, + (const xmlChar *) buffer); + + sprintf(buffer,"%d",clip->fader_active); + xmlNewChild(node,NULL,(const xmlChar *) XMLTAG_FADER_ACTIVE, + (const xmlChar *) buffer); + sprintf(buffer,"%f",clip->fader_inc); + xmlNewChild(node,NULL,(const xmlChar *) XMLTAG_FADER_INC, + (const xmlChar *) buffer); + sprintf(buffer,"%f",clip->fader_val); + xmlNewChild(node,NULL,(const xmlChar *) XMLTAG_FADER_VAL, + (const xmlChar *) buffer); + sprintf(buffer,"%d",clip->fader_direction); + xmlNewChild(node,NULL,(const xmlChar *) XMLTAG_FADER_DIRECTION, + (const xmlChar *) buffer); + sprintf(buffer,"%d",clip->selected_entry); + xmlNewChild(node,NULL,(const xmlChar *) XMLTAG_LASTENTRY, + (const xmlChar *)buffer); + childnode = + xmlNewChild(node, NULL, (const xmlChar *) XMLTAG_EFFECTS, NULL); + + + + CreateEffects(childnode, clip->effect_chain); + +} + +/**************************************************************************************************** + * + * clip_writeToFile( filename ) + * + * writes all clip info to a file. + * + ****************************************************************************************************/ +int clip_writeToFile(char *clipFile) +{ + int i; + clip_info *next_clip; + xmlDocPtr doc; + xmlNodePtr rootnode, childnode; + + doc = xmlNewDoc("1.0"); + rootnode = + xmlNewDocNode(doc, NULL, (const xmlChar *) XMLTAG_CLIPS, NULL); + xmlDocSetRootElement(doc, rootnode); + for (i = 1; i < clip_size(); i++) { + next_clip = clip_get(i); + if (next_clip) { + childnode = + xmlNewChild(rootnode, NULL, + (const xmlChar *) XMLTAG_CLIP, NULL); + CreateClip(childnode, next_clip); + } + } + xmlSaveFormatFile(clipFile, doc, 1); + + xmlFreeDoc(doc); + + return 1; +} +#endif diff --git a/veejay-current/libsample/sampleadm.h b/veejay-current/libsample/sampleadm.h new file mode 100644 index 00000000..f394409c --- /dev/null +++ b/veejay-current/libsample/sampleadm.h @@ -0,0 +1,272 @@ +/* + * Linux VeeJay + * + * Copyright(C)2002 Niels Elburg < nelburg@sourceforge.net> + * + * 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. + */ + +#ifndef CLIPADM_H +#define CLIPADM_H +#include +#include +#include +#include +#include +#ifdef HAVE_XML2 +#include +#include +#endif +#define CLIP_MAX_RENDER 10 /* 10 at most */ +#define CLIP_MAX_CLIPS 16384 /* 4096 clips at most */ + +#define CLIP_MAX_PARAMETERS 10 /* 10 parameters per effect at most */ +#define CLIP_ARG1 0 +#define CLIP_ARG2 1 +#define CLIP_ARG3 2 +#define CLIP_ARG4 3 +#define CLIP_ARG5 4 +#define CLIP_ARG6 5 +#define CLIP_ARG7 6 +#define CLIP_ARG8 7 +#define CLIP_ARG9 8 +#define CLIP_ARG10 9 + +#define CLIP_FREEZE_NONE 0 +#define CLIP_FREEZE_PAUSE 1 +#define CLIP_FREEZE_BLACK 2 + +#define CLIP_RENDER_START 1 +#define CLIP_RENDER_STOP 0 +enum { + CLIP_LOAD = 0, + CLIP_RUN = 1, + CLIP_PEEK = 2, +}; + + + +typedef struct clip_eff_t { + int effect_id; /* effect ID */ + int e_flag; + + int arg[CLIP_MAX_PARAMETERS]; /* array of arguments */ + int frame_offset; + int frame_trimmer; /* sub frame scratcher */ + /* audio settings */ + int a_flag; /* audio enabled/disabled */ + int volume; /* volume of 0-100 of audio */ + int source_type; /* source type to mix with */ + int channel; /* secundary source id */ + int is_rendering; /* is rendering */ +} clip_eff_chain; + + +typedef struct clip_info_t { + int clip_id; /* identifies a unique clip */ + clip_eff_chain *effect_chain[CLIP_MAX_EFFECTS]; /* effect chain */ + long first_frame[CLIP_MAX_RENDER]; /* start of clip */ + long last_frame[CLIP_MAX_RENDER]; /* end of clip */ + char descr[150]; + int speed; /* playback speed */ + int looptype; /* pingpong or loop */ + int max_loops; /* max looops before going to */ + int max_loops2; /* count remaining loops */ + int next_clip_id; /* the next clip */ + int depth; /* clip effect chain render depth */ + int source; /* source or tag */ + int channel; /* which channel (which tag) */ + int playmode; /* various playmodes */ + int playmode_frame; + int sub_audio; /* mix underlying clip yes or no */ + int audio_volume; /* volume setting of this clip */ + int marker_start; + int marker_end; + int dup; /* frame duplicator */ + int active_render_entry; + int loop_dec; + int loop_periods; + int marker_speed; + int fader_active; + int fader_direction; + float fader_val; + float fader_inc; + int encoder_active; + unsigned long sequence_num; + unsigned long rec_total_bytes; + char *encoder_base; + unsigned long encoder_total_frames; + char *encoder_destination; + int encoder_format; +// lav_file_t *encoder_file; + void *encoder_file; + long encoder_duration; /* in seconds */ + long encoder_num_frames; + long encoder_succes_frames; + int encoder_width; + int encoder_height; + int encoder_max_size; + int auto_switch; + int selected_entry; + int effect_toggle; + int offset; +} clip_info; + +#define CLIP_YUV420_BUFSIZE 16 +#define CLIP_MAX_DEPTH 4 +#define CLIP_DEC_BIBBER 1 +#define CLIP_DEC_FREEZE 2 + +extern int clip_chain_malloc(int clip_id); +extern int clip_chain_free(int clip_id); +extern int clip_size(); +extern int clip_verify(); +extern void clip_init(int len); +extern int clip_update(clip_info *clip, int s1); +#ifdef HAVE_XML2 +extern int clip_readFromFile(char *); +extern int clip_writeToFile(char *); +#endif +extern int clip_update_offset(int s1, int nframe); +extern int clip_set_state(int new_state); +extern int clip_get_state(); +extern clip_info *clip_skeleton_new(long startFrame, long endFrame); +extern clip_info *clip_get(int clip_id); +extern int clip_store(clip_info * skel); +extern int clip_is_deleted(int s1); +extern int clip_exists(int clip_id); +extern int clip_del(int clip_id); +extern void clip_del_all(); +extern int clip_get_startFrame(int clip_id); +extern int clip_get_endFrame(int clip_id); +extern int clip_set_marker_start(int clip_id, int marker); +extern int clip_set_marker_end(int clip_id, int marker); +extern int clip_set_startframe(int s1, long frame_num); +extern int clip_set_endframe(int s1, long frame_num); +extern int clip_set_marker(int s1, int start, int end); +extern int clip_get_longest(int clip_id); +extern int clip_get_playmode(int s1); +extern int clip_set_playmode(int s1, int playmode); +extern int clip_get_loops(int s1); +extern int clip_get_loops2(int s1); +extern int clip_get_next(int s1); +extern int clip_get_depth(int s1); +extern int clip_set_depth(int s1, int n); +extern int clip_set_speed(int s1, int speed); +extern int clip_set_framedup(int s1, int n); +extern int clip_get_framedup(int s1); +extern int clip_marker_clear(int clip_id); +extern int clip_set_looptype(int s1, int looptype); +extern int clip_get_speed(int s1); +extern int clip_get_looptype(int s1); +extern int clip_set_loops(int s1, int nr_of_loops); +extern int clip_set_loops2(int s1, int nr); +extern int clip_set_next(int s1, int next_clip_id); +extern int clip_get_chain_source(int clip_id, int position); +extern int clip_set_chain_source(int clip_id, int position, int source); +extern int clip_get_sub_audio(int s1); +extern int clip_set_sub_audio(int s1, int audio); +extern int clip_get_audio_volume(int s1); +extern int clip_set_audio_volume(int s1, int volume); +extern int clip_copy(int s1); +extern int clip_get_effect(int s1, int position); +/* get effect any, even if effect is disabled (required for informational purposes)*/ +extern int clip_get_effect_any(int s1, int position); +extern int clip_get_offset(int s1, int position); + +/* trimmer is usefull for underlying clips in the effect chain. + you can manual adjust the video/audio sync of the underlying clip */ +extern int clip_get_trimmer(int s1, int position); +extern int clip_set_trimmer(int s1, int position, int trimmer); +extern int clip_get_short_info(int clip_id, int *, int *, int *, int *) ; +extern int clip_get_chain_volume(int s1, int position); + +/* set volume of audio data coming to the chain */ +extern int clip_set_chain_volume(int s1, int position, int volume); + +/* whether to mix underlying clip's audio */ +extern int clip_get_chain_audio(int s1, int position); +extern int clip_has_extra_frame(int s1, int position); +/* mix the audio from entry from the effect chain, if any */ +extern int clip_set_chain_audio(int s1, int position, int flag); + +extern int clip_set_chain_status(int s1, int position, int status); +extern int clip_get_chain_status(int s1, int position); + +extern int clip_set_offset(int s1, int position, int frame_offset); +extern int clip_get_effect_arg(int s1, int position, int argnr); +extern int clip_set_effect_arg(int s1, int position, int argnr, int value); + +extern int clip_get_all_effect_arg(int s1, int position, int *args, + int arg_len, int n_elapsed); +extern int clip_chain_remove(int s1, int position); +extern int clip_chain_clear(int s1); +extern int clip_chain_size(int s1); +extern int clip_chain_get_free_entry(int s1); +extern int clip_chain_add(int s1, int c, int effect_nr); + +/* channel depends on source , it select a channel of a certain video source */ +extern int clip_get_chain_channel(int s1, int position); +extern int clip_set_chain_channel(int s1, int position, int channel); + +//int clip_chain_replace(int s1, int position, int effect_id); + +extern int clip_chain_sprint_status(int s1, int entry, int changed, int r_changed, char *str, + int frame); + +extern int clip_set_render_entry(int s1, int entry); +extern int clip_get_render_entry(int s1); + +extern int clip_set_description(int clip_id, char *description); +extern int clip_get_description(int clip_id, char *description); + +extern int clip_entry_is_rendering(int clip_id, int entry); +extern int clip_entry_set_is_rendering(int clip_id, int entry, int value); +extern int clip_get_loop_dec(int s1); +extern int clip_set_loop_dec(int s1, int active, int periods); +extern int clip_apply_loop_dec(int s1, double fps); + +extern int clip_set_manual_fader(int s1, int value ); +extern int clip_apply_fader_inc(int s1); +extern int clip_set_fader_active(int s1, int nframes, int direction); +extern int clip_set_fader_val(int s1, float val); +extern int clip_get_fader_active(int s1); +extern float clip_get_fader_val(int s1); +extern float clip_get_fader_inc(int s1); +extern int clip_get_fader_direction(int s1); +extern int clip_reset_fader(int t1); + +extern int clip_reset_offset(int s1); + +extern int clip_get_effect_status(int s1); +extern int clip_get_selected_entry(int s1); + +extern int clip_set_effect_status(int s1, int status); +extern int clip_set_selected_entry(int s1, int position); + + +#ifdef HAVE_XML2 +extern void CreateClip(xmlNodePtr node, clip_info * clip); +extern void CreateEffects(xmlNodePtr node, clip_eff_chain ** effects); +extern void CreateEffect(xmlNodePtr node, clip_eff_chain * effect, int pos); +extern void CreateArguments(xmlNodePtr node, int *arg, int argcount); +extern void ParseClip(xmlDocPtr doc, xmlNodePtr cur, clip_info * skel); +extern void ParseEffects(xmlDocPtr doc, xmlNodePtr cur, clip_info * skel); +extern void ParseEffect(xmlDocPtr doc, xmlNodePtr cur, int dst_clip); +extern void ParseArguments(xmlDocPtr doc, xmlNodePtr cur, int *arg); +extern unsigned char *UTF8toLAT1(unsigned char *in); +#endif + +#endif