You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/flow/gsl/gslwavechunk.c

813 lines
26 KiB

/* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, 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;
}