diff options
Diffstat (limited to 'flow/gsl/gslwavechunk.c')
-rw-r--r-- | flow/gsl/gslwavechunk.c | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/flow/gsl/gslwavechunk.c b/flow/gsl/gslwavechunk.c new file mode 100644 index 0000000..06a105f --- /dev/null +++ b/flow/gsl/gslwavechunk.c @@ -0,0 +1,812 @@ +/* GSL - Generic Sound Layer + * Copyright (C) 2001 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "gslwavechunk.h" + +#include "gslcommon.h" +#include "gsldatahandle.h" + +#include <string.h> + + +/* --- macros --- */ +#define PRINT_DEBUG_INFO (0) +#define STATIC_ZERO_SIZE (4096) +#define PBLOCK_SIZE(pad, n_channels) (MAX (2 * (pad), (n_channels) * gsl_get_config ()->wave_chunk_big_pad)) + +#define PHASE_NORM(wchunk) ((GslWaveChunkMem*) (0)) +#define PHASE_NORM_BACKWARD(wchunk) ((GslWaveChunkMem*) (+1)) +#define PHASE_UNDEF(wchunk) ((GslWaveChunkMem*) (+2)) +#define PHASE_HEAD(wchunk) (&(wchunk)->head) +#define PHASE_ENTER(wchunk) (&(wchunk)->enter) +#define PHASE_WRAP(wchunk) (&(wchunk)->wrap) +#define PHASE_PPWRAP(wchunk) (&(wchunk)->ppwrap) +#define PHASE_LEAVE(wchunk) (&(wchunk)->leave) +#define PHASE_TAIL(wchunk) (&(wchunk)->tail) + + +/* --- typedefs & structures --- */ +typedef struct { + GslLong pos; /* input */ + GslLong rel_pos; + GslLong lbound, ubound; /* PHASE_NORM/_BACKWARD */ +} Iter; +typedef struct { + GslLong dir; + GslLong pos; + GslLong loop_count; +} WPos; + + +/* --- variables --- */ +static gfloat static_zero_block[STATIC_ZERO_SIZE] = { 0, }; /* FIXME */ + + +/* --- functions --- */ +static inline void +wpos_step (GslWaveChunk *wchunk, + WPos *wpos) +{ + wpos->pos += wpos->dir; + if (wpos->loop_count) + { + if (wchunk->loop_type == GSL_WAVE_LOOP_PINGPONG) + { + if (wpos->dir < 0 && + wpos->pos == wchunk->loop_first + wpos->dir) + { + wpos->loop_count--; + wpos->dir = -wpos->dir; + wpos->pos = wchunk->loop_first + wpos->dir; + } + else if (wpos->pos == wchunk->loop_last + wpos->dir) + { + wpos->loop_count--; + wpos->dir = -wpos->dir; + wpos->pos = wchunk->loop_last + wpos->dir; + } + } + else + { + if (wpos->pos == wchunk->loop_last + wpos->dir && wpos->loop_count) + { + wpos->loop_count--; + wpos->pos = wchunk->loop_first; + } + } + } +} + +static void +fill_block (GslWaveChunk *wchunk, + gfloat *block, + GslLong offset, + guint length, + gboolean backward, + guint loop_count) +{ + GslLong dcache_length = gsl_data_handle_length (wchunk->dcache->dhandle); + guint i, dnode_length = wchunk->dcache->node_size; + GslDataCacheNode *dnode; + WPos wpos; + + wpos.dir = wchunk->n_channels; + if (backward) + wpos.dir = -wpos.dir; + wpos.pos = offset; + wpos.loop_count = loop_count; + dnode = gsl_data_cache_ref_node (wchunk->dcache, 0, TRUE); + for (i = 0; i < length; i++) + { + GslLong offset = wpos.pos; + + if (offset < 0 || offset >= dcache_length) + block[i] = 0; + else + { + if (offset < dnode->offset || offset >= dnode->offset + dnode_length) + { + gsl_data_cache_unref_node (wchunk->dcache, dnode); + dnode = gsl_data_cache_ref_node (wchunk->dcache, offset, TRUE); + } + block[i] = dnode->data[offset - dnode->offset]; + } + wpos_step (wchunk, &wpos); + } + gsl_data_cache_unref_node (wchunk->dcache, dnode); +} + +static gfloat* +create_block_for_offset (GslWaveChunk *wchunk, + GslLong offset, + guint length) +{ + GslLong padding = wchunk->n_pad_values; + GslLong one = wchunk->n_channels; + GslLong wave_last = wchunk->length - one; + GslLong loop_width = wchunk->loop_last - wchunk->loop_first; + gfloat *mem; + GslLong l, j, k; + + if (wchunk->loop_type != GSL_WAVE_LOOP_PINGPONG) + loop_width += one; + + l = length + 2 * padding; + mem = gsl_new_struct (gfloat, l); + offset -= padding; + j = ((wchunk->wave_length - one - offset) - + (wchunk->pploop_ends_backwards ? wchunk->loop_first : wave_last - wchunk->loop_last)); + if (j >= 0) + { + k = j / loop_width; + /* g_print ("endoffset-setup: j=%ld %%=%ld, k=%ld, k&1=%ld\n", j, j % loop_width, k, k & 1); */ + j %= loop_width; + if (wchunk->loop_type == GSL_WAVE_LOOP_PINGPONG) + { + if (wchunk->pploop_ends_backwards && (k & 1)) + fill_block (wchunk, mem, wchunk->loop_last - j, l, FALSE, k); + else if (wchunk->pploop_ends_backwards) + fill_block (wchunk, mem, wchunk->loop_first + j, l, TRUE, k); + else if (k & 1) + fill_block (wchunk, mem, wchunk->loop_first + j, l, TRUE, k); + else + fill_block (wchunk, mem, wchunk->loop_last - j, l, FALSE, k); + } + else + fill_block (wchunk, mem, wchunk->loop_last - j, l, FALSE, k); + } + else if (wchunk->pploop_ends_backwards) + fill_block (wchunk, mem, wchunk->loop_first + j, l, TRUE, 0); + else + fill_block (wchunk, mem, wchunk->loop_last - j, l, FALSE, 0); + return mem + padding; +} + +static void +setup_pblocks (GslWaveChunk *wchunk) +{ + GslLong padding = wchunk->n_pad_values; + GslLong big_pad = PBLOCK_SIZE (wchunk->n_pad_values, wchunk->n_channels); + GslLong loop_width = wchunk->loop_last - wchunk->loop_first; + GslLong one = wchunk->n_channels; + GslLong loop_duration, wave_last = wchunk->length - one; + gfloat *mem; + guint l; + + if (wchunk->loop_type != GSL_WAVE_LOOP_PINGPONG) + loop_width += one; + loop_duration = loop_width * wchunk->loop_count; + + wchunk->head.start = -padding; + wchunk->head.end = big_pad; + wchunk->head.length = wchunk->head.end - wchunk->head.start + one; + wchunk->tail_start_norm = wave_last - big_pad; + wchunk->tail.start = wchunk->tail_start_norm + loop_duration; + wchunk->tail.end = wchunk->tail.start + big_pad + padding; + wchunk->tail.length = wchunk->tail.end - wchunk->tail.start + one; + if (wchunk->loop_type) + { + wchunk->enter.start = wchunk->loop_last - padding; + wchunk->enter.end = wchunk->loop_last + one + big_pad; + wchunk->wrap.start = loop_width - padding; + wchunk->wrap.end = big_pad; + if (wchunk->loop_type == GSL_WAVE_LOOP_PINGPONG) + { + wchunk->enter.end -= one; + wchunk->wrap.end -= one; + wchunk->ppwrap.start = wchunk->wrap.start; + wchunk->ppwrap.end = wchunk->wrap.end + loop_width; + wchunk->ppwrap.length = wchunk->ppwrap.end - wchunk->ppwrap.start + one; + wchunk->wrap.length = loop_width - wchunk->wrap.start + wchunk->wrap.end + one; + wchunk->wrap.start += loop_width; + } + else + wchunk->wrap.length = loop_width - wchunk->wrap.start + wchunk->wrap.end + one; + wchunk->leave_end_norm = wchunk->loop_last + big_pad; + wchunk->leave.start = wchunk->loop_last + loop_duration - padding; + wchunk->leave.end = wchunk->leave_end_norm + loop_duration; + if (wchunk->mini_loop) + { + wchunk->leave.start -= wchunk->wrap.length + padding; + wchunk->enter.end += wchunk->wrap.length + padding; + } + wchunk->leave.length = wchunk->leave.end - wchunk->leave.start + one; + wchunk->enter.length = wchunk->enter.end - wchunk->enter.start + one; + if (wchunk->pploop_ends_backwards) + { + wchunk->tail.start += wchunk->loop_last - wave_last + wchunk->loop_first; + wchunk->tail.end += wchunk->loop_last - wave_last + wchunk->loop_first; + wchunk->tail_start_norm = 0 + big_pad; + wchunk->leave_end_norm = wchunk->loop_first - big_pad; + } + } + else + { + /* + wchunk->enter.start = wchunk->head.end; + wchunk->enter.end = wchunk->head.end; + wchunk->enter.length = 0; + */ + wchunk->enter.start = wchunk->tail.start; + wchunk->enter.end = wchunk->head.end; + wchunk->enter.length = 0; + wchunk->wrap.start = wchunk->tail.end + 1; + wchunk->wrap.end = wchunk->head.start - 1; + wchunk->wrap.length = 0; + wchunk->ppwrap.start = wchunk->tail.end + 1; + wchunk->ppwrap.end = wchunk->head.start - 1; + wchunk->ppwrap.length = 0; + wchunk->leave.start = wchunk->tail.start; + wchunk->leave.end = wchunk->tail.end; + wchunk->leave_end_norm = 0; + wchunk->leave.length = 0; + } + + l = wchunk->head.length + 2 * padding; + mem = gsl_new_struct (gfloat, l); + fill_block (wchunk, mem, wchunk->head.start - padding, l, FALSE, wchunk->loop_count); + wchunk->head.mem = mem + padding; + if (wchunk->loop_type) + { + l = wchunk->enter.length + 2 * padding; + mem = gsl_new_struct (gfloat, l); + fill_block (wchunk, mem, wchunk->enter.start - padding, l, FALSE, wchunk->loop_count); + wchunk->enter.mem = mem + padding; + if (wchunk->loop_type == GSL_WAVE_LOOP_PINGPONG) + { + wchunk->wrap.mem = create_block_for_offset (wchunk, wchunk->loop_last + one + wchunk->wrap.start, wchunk->wrap.length); + wchunk->ppwrap.mem = create_block_for_offset (wchunk, wchunk->loop_last + one + wchunk->ppwrap.start, wchunk->ppwrap.length); + } + else + { + l = wchunk->wrap.length + 2 * padding; + mem = gsl_new_struct (gfloat, l); + fill_block (wchunk, mem, wchunk->loop_first + wchunk->wrap.start - padding, l, FALSE, wchunk->loop_count - 1); + wchunk->wrap.mem = mem + padding; + } + wchunk->leave.mem = create_block_for_offset (wchunk, wchunk->leave.start, wchunk->leave.length); + } + wchunk->tail.mem = create_block_for_offset (wchunk, wchunk->tail.start, wchunk->tail.length); +} + +static inline GslWaveChunkMem* +wave_identify_offset (GslWaveChunk *wchunk, + Iter *iter) +{ + GslLong pos = iter->pos; + GslLong one = wchunk->n_channels; + + if (pos < wchunk->head.start) /* outside wave boundaries */ + { + iter->lbound = 0; + iter->rel_pos = wchunk->n_pad_values; + iter->ubound = iter->rel_pos + MIN (STATIC_ZERO_SIZE - 2 * wchunk->n_pad_values, wchunk->head.start - pos); + if (PRINT_DEBUG_INFO) + g_print ("PHASE_UNDEF, pre-head %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_UNDEF (wchunk); + } + if (pos > wchunk->tail.end) /* outside wave boundaries */ + { + iter->lbound = 0; + iter->rel_pos = wchunk->n_pad_values; + iter->ubound = iter->rel_pos + MIN (STATIC_ZERO_SIZE - 2 * wchunk->n_pad_values, pos - wchunk->tail.end); + if (PRINT_DEBUG_INFO) + g_print ("PHASE_UNDEF, post-tail %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_UNDEF (wchunk); + } + if (pos <= wchunk->head.end) + { + iter->rel_pos = pos - wchunk->head.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_HEAD %ld %ld %ld\n", wchunk->head.start, iter->rel_pos, wchunk->head.end); + return PHASE_HEAD (wchunk); + } + else if (pos <= wchunk->enter.end) /* before loop */ + { + if (pos >= wchunk->enter.start) + { + iter->rel_pos = pos - wchunk->enter.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_ENTER %ld %ld %ld\n", wchunk->enter.start, iter->rel_pos, wchunk->enter.end); + return PHASE_ENTER (wchunk); + } + iter->rel_pos = pos - wchunk->head.end; + iter->lbound = wchunk->head.end; + iter->ubound = wchunk->enter.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM, pre-enter %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM (wchunk); + } + else if (pos >= wchunk->tail.start) + { + iter->rel_pos = pos - wchunk->tail.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_TAIL %ld %ld %ld\n", wchunk->tail.start, iter->rel_pos, wchunk->tail.end); + return PHASE_TAIL (wchunk); + } + else if (pos >= wchunk->leave.start) /* after loop */ + { + if (pos <= wchunk->leave.end) + { + iter->rel_pos = pos - wchunk->leave.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_LEAVE %ld %ld %ld\n", wchunk->leave.start, iter->rel_pos, wchunk->leave.end); + return PHASE_LEAVE (wchunk); + } + iter->rel_pos = pos - wchunk->leave.end; + if (wchunk->pploop_ends_backwards) + { + iter->lbound = wchunk->tail_start_norm; + iter->ubound = wchunk->leave_end_norm; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM_BACKWARD, post-leave %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM_BACKWARD (wchunk); + } + else + { + iter->lbound = wchunk->leave_end_norm; + iter->ubound = wchunk->tail_start_norm; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM, post-leave %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM (wchunk); + } + } + else if (wchunk->loop_type == GSL_WAVE_LOOP_PINGPONG) /* in ping-pong loop */ + { + guint loop_width = wchunk->loop_last - wchunk->loop_first; + + pos -= wchunk->loop_last + one; + pos %= 2 * loop_width; + if (pos <= wchunk->ppwrap.end) + { + if (pos <= wchunk->wrap.end) + { + iter->rel_pos = wchunk->wrap.length - one - wchunk->wrap.end + pos; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_WRAP %ld %ld %ld\n", wchunk->wrap.start, iter->rel_pos, wchunk->wrap.end); + return PHASE_WRAP (wchunk); + } + if (pos >= wchunk->ppwrap.start) + { + iter->rel_pos = pos - wchunk->ppwrap.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_PPWRAP %ld %ld %ld\n", wchunk->ppwrap.start, iter->rel_pos, wchunk->ppwrap.end); + return PHASE_PPWRAP (wchunk); + } + iter->ubound = wchunk->loop_last - one - wchunk->wrap.end; + iter->lbound = wchunk->loop_last - one - wchunk->ppwrap.start; + iter->rel_pos = pos - wchunk->wrap.end; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM_BACKWARD, pploop %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM_BACKWARD (wchunk); + } + if (pos >= wchunk->wrap.start) + { + iter->rel_pos = pos - wchunk->wrap.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_WRAP %ld %ld %ld\n", wchunk->wrap.start, iter->rel_pos, wchunk->wrap.end); + return PHASE_WRAP (wchunk); + } + iter->rel_pos = pos - wchunk->ppwrap.end; + iter->ubound = wchunk->loop_first + one + wchunk->wrap.start - loop_width; + iter->lbound = wchunk->loop_first + one + wchunk->ppwrap.end - loop_width; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM, pploop %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM (wchunk); + } + else if (wchunk->loop_type == GSL_WAVE_LOOP_JUMP) /* in jump loop */ + { + guint loop_width = wchunk->loop_last - wchunk->loop_first + one; + + pos -= wchunk->loop_last + one; + pos %= loop_width; + if (pos >= wchunk->wrap.start) + { + iter->rel_pos = pos - wchunk->wrap.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_WRAP %ld %ld %ld\n", wchunk->wrap.start, iter->rel_pos, wchunk->wrap.end); + return PHASE_WRAP (wchunk); + } + if (pos <= wchunk->wrap.end) + { + iter->rel_pos = wchunk->wrap.length - one - wchunk->wrap.end + pos; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_WRAP %ld %ld %ld\n", wchunk->wrap.start, iter->rel_pos, wchunk->wrap.end); + return PHASE_WRAP (wchunk); + } + iter->rel_pos = pos - wchunk->wrap.end; + iter->lbound = wchunk->loop_first + wchunk->wrap.end; + iter->ubound = wchunk->loop_first + wchunk->wrap.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM, jloop %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM (wchunk); + } + iter->rel_pos = pos - wchunk->head.end; + iter->lbound = wchunk->head.end; + iter->ubound = wchunk->enter.start; + if (PRINT_DEBUG_INFO) + g_print ("PHASE_NORM, noloop %ld %ld %ld\n", iter->lbound, iter->rel_pos, iter->ubound); + return PHASE_NORM (wchunk); +} + +void +gsl_wave_chunk_use_block (GslWaveChunk *wchunk, + GslWaveChunkBlock *block) +{ + GslWaveChunkMem *phase; + GslLong one; + Iter iter; + gboolean reverse; + + g_return_if_fail (wchunk != NULL); + g_return_if_fail (wchunk->open_count > 0); + g_return_if_fail (block != NULL); + g_return_if_fail (wchunk->dcache != NULL); + g_return_if_fail (block->node == NULL); + g_return_if_fail (block->play_dir == -1 || block->play_dir == +1); + + block->offset /= wchunk->n_channels; + block->offset *= wchunk->n_channels; + + one = wchunk->n_channels; + reverse = block->play_dir < 0; + iter.pos = block->offset; + phase = wave_identify_offset (wchunk, &iter); + + block->is_silent = FALSE; + if (phase <= PHASE_UNDEF (wchunk)) + { + GslDataCacheNode *dnode; + guint offset; + + if (phase == PHASE_UNDEF (wchunk)) + { + block->is_silent = TRUE; + reverse = FALSE; + block->length = (iter.ubound - iter.rel_pos) / wchunk->n_channels; + block->length *= wchunk->n_channels; + g_assert (block->length <= STATIC_ZERO_SIZE - 2 * wchunk->n_pad_values); + block->start = static_zero_block + iter.rel_pos; + } + else + { + GslLong max_length; + + if (phase == PHASE_NORM_BACKWARD (wchunk)) + { + offset = iter.ubound - iter.rel_pos; + reverse = !reverse; + } + else + offset = iter.lbound + iter.rel_pos; + max_length = reverse ? offset - iter.lbound : iter.ubound - offset; + dnode = gsl_data_cache_ref_node (wchunk->dcache, offset, TRUE); /* FIXME: demand_load */ + offset -= dnode->offset; + block->start = dnode->data + offset; + if (reverse) + { + block->length = 1 + offset / wchunk->n_channels; + block->length *= wchunk->n_channels; + } + else + { + block->length = (wchunk->dcache->node_size - offset) / wchunk->n_channels; + block->length *= wchunk->n_channels; + } + block->length = MIN (block->length, max_length); + block->node = dnode; + } + } + else + { + block->start = phase->mem + iter.rel_pos; + if (reverse) + block->length = one + iter.rel_pos; + else + block->length = phase->length - iter.rel_pos; + } + if (reverse) + { + block->dirstride = -wchunk->n_channels; + block->end = block->start - block->length; + } + else + { + block->dirstride = +wchunk->n_channels; + block->end = block->start + block->length; + } + g_assert (block->length > 0); + /* we might want to partly reset this at some point to implement + * truly infinite loops + */ + block->next_offset = block->offset + (block->play_dir > 0 ? block->length : -block->length); +} + +void +gsl_wave_chunk_unuse_block (GslWaveChunk *wchunk, + GslWaveChunkBlock *block) +{ + g_return_if_fail (wchunk != NULL); + g_return_if_fail (block != NULL); + g_return_if_fail (wchunk->dcache != NULL); + + if (block->node) + { + gsl_data_cache_unref_node (wchunk->dcache, block->node); + block->node = NULL; + } +} + +static void +wave_chunk_setup_loop (GslWaveChunk *wchunk) +{ + GslWaveLoopType loop_type = wchunk->requested_loop_type; + GslLong loop_first = wchunk->requested_loop_first; + GslLong loop_last = wchunk->requested_loop_last; + guint loop_count = wchunk->requested_loop_count; + GslLong one, padding, big_pad; + + g_return_if_fail (wchunk->open_count > 0); + + one = wchunk->n_channels; + padding = wchunk->n_pad_values; + big_pad = PBLOCK_SIZE (wchunk->n_pad_values, wchunk->n_channels); + + /* check validity */ + if (loop_count < 1 || loop_first < 0 || loop_last < 0 || wchunk->length < 1) + loop_type = GSL_WAVE_LOOP_NONE; + + /* setup loop types */ + switch (loop_type) + { + case GSL_WAVE_LOOP_JUMP: + loop_first /= wchunk->n_channels; + loop_last /= wchunk->n_channels; + if (loop_last >= wchunk->length || + loop_first >= loop_last) + goto CASE_DONT_LOOP; + wchunk->loop_type = loop_type; + wchunk->loop_first = loop_first * wchunk->n_channels; + wchunk->loop_last = loop_last * wchunk->n_channels; + wchunk->loop_count = (G_MAXINT - wchunk->length) / (wchunk->loop_last - wchunk->loop_first + one); + wchunk->loop_count = MIN (wchunk->loop_count, loop_count); + wchunk->wave_length = wchunk->length + (wchunk->loop_last - wchunk->loop_first + one) * wchunk->loop_count; + break; + case GSL_WAVE_LOOP_PINGPONG: + loop_first /= wchunk->n_channels; + loop_last /= wchunk->n_channels; + if (loop_last >= wchunk->length || + loop_first >= loop_last) + goto CASE_DONT_LOOP; + wchunk->loop_type = loop_type; + wchunk->loop_first = loop_first * wchunk->n_channels; + wchunk->loop_last = loop_last * wchunk->n_channels; + wchunk->loop_count = (G_MAXINT - wchunk->loop_last - one) / (wchunk->loop_last - wchunk->loop_first); + wchunk->loop_count = MIN (wchunk->loop_count, loop_count); + wchunk->wave_length = wchunk->loop_last + one + (wchunk->loop_last - wchunk->loop_first) * wchunk->loop_count; + if (wchunk->loop_count & 1) /* FIXME */ + wchunk->wave_length += wchunk->loop_first; + else + wchunk->wave_length += wchunk->length - one - wchunk->loop_last; + break; + CASE_DONT_LOOP: + loop_type = GSL_WAVE_LOOP_NONE; + case GSL_WAVE_LOOP_NONE: + wchunk->loop_type = loop_type; + wchunk->loop_first = wchunk->length + 1; + wchunk->loop_last = -1; + wchunk->loop_count = 0; + wchunk->wave_length = wchunk->length; + break; + } + wchunk->pploop_ends_backwards = wchunk->loop_type == GSL_WAVE_LOOP_PINGPONG && (wchunk->loop_count & 1); + wchunk->mini_loop = wchunk->loop_type && wchunk->loop_last - wchunk->loop_first < 2 * big_pad + padding; +} + +GslWaveChunk* +gsl_wave_chunk_new (GslDataCache *dcache, + gfloat osc_freq, + gfloat mix_freq, + GslWaveLoopType loop_type, + GslLong loop_first, + GslLong loop_last, + guint loop_count) +{ + GslWaveChunk *wchunk; + + g_return_val_if_fail (dcache != NULL, NULL); + g_return_val_if_fail (osc_freq < mix_freq / 2, NULL); + g_return_val_if_fail (loop_type >= GSL_WAVE_LOOP_NONE && loop_type <= GSL_WAVE_LOOP_PINGPONG, NULL); + + wchunk = gsl_new_struct0 (GslWaveChunk, 1); + wchunk->dcache = gsl_data_cache_ref (dcache); + wchunk->length = 0; + wchunk->n_channels = 0; + wchunk->n_pad_values = 0; + wchunk->wave_length = 0; + wchunk->loop_type = GSL_WAVE_LOOP_NONE; + wchunk->leave_end_norm = 0; + wchunk->tail_start_norm = 0; + wchunk->ref_count = 1; + wchunk->open_count = 0; + wchunk->mix_freq = mix_freq; + wchunk->osc_freq = osc_freq; + wchunk->requested_loop_type = loop_type; + wchunk->requested_loop_first = loop_first; + wchunk->requested_loop_last = loop_last; + wchunk->requested_loop_count = loop_count; + + return wchunk; +} + +GslWaveChunk* +gsl_wave_chunk_ref (GslWaveChunk *wchunk) +{ + g_return_val_if_fail (wchunk != NULL, NULL); + g_return_val_if_fail (wchunk->ref_count > 0, NULL); + + wchunk->ref_count++; + return wchunk; +} + +void +gsl_wave_chunk_unref (GslWaveChunk *wchunk) +{ + g_return_if_fail (wchunk != NULL); + g_return_if_fail (wchunk->ref_count > 0); + + wchunk->ref_count--; + if (wchunk->ref_count == 0) + { + g_return_if_fail (wchunk->open_count == 0); + gsl_data_cache_unref (wchunk->dcache); + gsl_delete_struct (GslWaveChunk, wchunk); + } +} + +GslErrorType +gsl_wave_chunk_open (GslWaveChunk *wchunk) +{ + g_return_val_if_fail (wchunk != NULL, GSL_ERROR_INTERNAL); + g_return_val_if_fail (wchunk->ref_count > 0, GSL_ERROR_INTERNAL); + + if (wchunk->open_count == 0) + { + GslErrorType error; + + error = gsl_data_handle_open (wchunk->dcache->dhandle); + if (error != GSL_ERROR_NONE) + return error; + if (gsl_data_handle_n_values (wchunk->dcache->dhandle) < gsl_data_handle_n_channels (wchunk->dcache->dhandle)) + { + gsl_data_handle_close (wchunk->dcache->dhandle); + return GSL_ERROR_FILE_EMPTY; + } + wchunk->n_channels = gsl_data_handle_n_channels (wchunk->dcache->dhandle); + wchunk->length = gsl_data_handle_n_values (wchunk->dcache->dhandle) / wchunk->n_channels; + wchunk->length *= wchunk->n_channels; + wchunk->n_pad_values = gsl_get_config ()->wave_chunk_padding * wchunk->n_channels; + gsl_data_cache_open (wchunk->dcache); + gsl_data_handle_close (wchunk->dcache->dhandle); + g_return_val_if_fail (wchunk->dcache->padding >= wchunk->n_pad_values, GSL_ERROR_INTERNAL); + wchunk->open_count++; + wchunk->ref_count++; + wave_chunk_setup_loop (wchunk); + setup_pblocks (wchunk); + } + else + wchunk->open_count++; + return GSL_ERROR_NONE; +} + +void +gsl_wave_chunk_close (GslWaveChunk *wchunk) +{ + GslLong padding; + + g_return_if_fail (wchunk != NULL); + g_return_if_fail (wchunk->open_count > 0); + g_return_if_fail (wchunk->ref_count > 0); + + wchunk->open_count--; + if (wchunk->open_count) + return; + + padding = wchunk->n_pad_values; + gsl_data_cache_close (wchunk->dcache); + if (wchunk->head.mem) + gsl_delete_structs (gfloat, wchunk->head.length + 2 * padding, wchunk->head.mem - padding); + memset (&wchunk->head, 0, sizeof (GslWaveChunkMem)); + if (wchunk->enter.mem) + gsl_delete_structs (gfloat, wchunk->enter.length + 2 * padding, wchunk->enter.mem - padding); + memset (&wchunk->enter, 0, sizeof (GslWaveChunkMem)); + if (wchunk->wrap.mem) + gsl_delete_structs (gfloat, wchunk->wrap.length + 2 * padding, wchunk->wrap.mem - padding); + memset (&wchunk->wrap, 0, sizeof (GslWaveChunkMem)); + if (wchunk->ppwrap.mem) + gsl_delete_structs (gfloat, wchunk->ppwrap.length + 2 * padding, wchunk->ppwrap.mem - padding); + memset (&wchunk->ppwrap, 0, sizeof (GslWaveChunkMem)); + if (wchunk->leave.mem) + gsl_delete_structs (gfloat, wchunk->leave.length + 2 * padding, wchunk->leave.mem - padding); + memset (&wchunk->leave, 0, sizeof (GslWaveChunkMem)); + if (wchunk->tail.mem) + gsl_delete_structs (gfloat, wchunk->tail.length + 2 * padding, wchunk->tail.mem - padding); + memset (&wchunk->tail, 0, sizeof (GslWaveChunkMem)); + wchunk->length = 0; + wchunk->n_channels = 0; + wchunk->n_pad_values = 0; + wchunk->wave_length = 0; + wchunk->loop_type = GSL_WAVE_LOOP_NONE; + wchunk->leave_end_norm = 0; + wchunk->tail_start_norm = 0; + gsl_wave_chunk_unref (wchunk); +} + +void +gsl_wave_chunk_debug_block (GslWaveChunk *wchunk, + GslLong offset, + GslLong length, + gfloat *block) +{ + g_return_if_fail (wchunk != NULL); + + fill_block (wchunk, block, offset, length, FALSE, wchunk->loop_count); +} + +GslWaveChunk* +_gsl_wave_chunk_copy (GslWaveChunk *wchunk) +{ + g_return_val_if_fail (wchunk != NULL, NULL); + g_return_val_if_fail (wchunk->ref_count > 0, NULL); + + return gsl_wave_chunk_new (wchunk->dcache, + wchunk->osc_freq, + wchunk->mix_freq, + wchunk->loop_type, + wchunk->loop_first, + wchunk->loop_last, + wchunk->loop_count); +} + +const gchar* +gsl_wave_loop_type_to_string (GslWaveLoopType wave_loop) +{ + g_return_val_if_fail (wave_loop >= GSL_WAVE_LOOP_NONE && wave_loop <= GSL_WAVE_LOOP_PINGPONG, NULL); + + switch (wave_loop) + { + case GSL_WAVE_LOOP_NONE: return "none"; + case GSL_WAVE_LOOP_JUMP: return "jump"; + case GSL_WAVE_LOOP_PINGPONG: return "pingpong"; + default: return NULL; + } +} + +GslWaveLoopType +gsl_wave_loop_type_from_string (const gchar *string) +{ + g_return_val_if_fail (string != NULL, 0); + + while (*string == ' ') + string++; + if (strncasecmp (string, "jump", 4) == 0) + return GSL_WAVE_LOOP_JUMP; + if (strncasecmp (string, "pingpong", 8) == 0) + return GSL_WAVE_LOOP_PINGPONG; + return GSL_WAVE_LOOP_NONE; +} |