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.
813 lines
26 KiB
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;
|
|
}
|