diff options
Diffstat (limited to 'flow/gsl/gslloader-gslwave.c')
| -rw-r--r-- | flow/gsl/gslloader-gslwave.c | 701 | 
1 files changed, 701 insertions, 0 deletions
diff --git a/flow/gsl/gslloader-gslwave.c b/flow/gsl/gslloader-gslwave.c new file mode 100644 index 0000000..e851a12 --- /dev/null +++ b/flow/gsl/gslloader-gslwave.c @@ -0,0 +1,701 @@ +/* GSL - Generic Sound Layer + * Copyright (C) 2001, 2002 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 "gslloader.h" + +#include "gsldatahandle.h" +#include "gslmath.h" + +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + + +#define	GSL_DEBUG_LOADER	g_message + +#define parse_or_return(scanner, token) { guint _t = (token); \ +                                          if (g_scanner_get_next_token (scanner) != _t) \ +                                            return _t; \ +                                        } + + +/* --- token types --- */ +typedef enum +{ +  /* wave tokens */ +  GSL_WAVE_TOKEN_WAVE           = 512, +  GSL_WAVE_TOKEN_CHUNK, +  GSL_WAVE_TOKEN_NAME, +  GSL_WAVE_TOKEN_BYTE_ORDER, +  GSL_WAVE_TOKEN_FORMAT, +  GSL_WAVE_TOKEN_N_CHANNELS, +  GSL_WAVE_TOKEN_MIX_FREQ, +  GSL_WAVE_TOKEN_OSC_FREQ, +  GSL_WAVE_TOKEN_MIDI_NOTE, +  GSL_WAVE_TOKEN_FILE, +  GSL_WAVE_TOKEN_INDEX, +  GSL_WAVE_TOKEN_BOFFSET, +  GSL_WAVE_TOKEN_N_VALUES, +  GSL_WAVE_TOKEN_LOOP_TYPE, +  GSL_WAVE_TOKEN_LOOP_START, +  GSL_WAVE_TOKEN_LOOP_END, +  GSL_WAVE_TOKEN_LOOP_COUNT, +  GSL_WAVE_TOKEN_LAST_FIELD, +  /* data tokens */ +  GSL_WAVE_TOKEN_BIG_ENDIAN     = 768, +  GSL_WAVE_TOKEN_BIG, +  GSL_WAVE_TOKEN_LITTLE_ENDIAN, +  GSL_WAVE_TOKEN_LITTLE, +  GSL_WAVE_TOKEN_SIGNED_8, +  GSL_WAVE_TOKEN_SIGNED_12, +  GSL_WAVE_TOKEN_SIGNED_16, +  GSL_WAVE_TOKEN_UNSIGNED_8, +  GSL_WAVE_TOKEN_UNSIGNED_12, +  GSL_WAVE_TOKEN_UNSIGNED_16, +  GSL_WAVE_TOKEN_FLOAT, +  GSL_WAVE_TOKEN_NONE, +  GSL_WAVE_TOKEN_JUMP, +  GSL_WAVE_TOKEN_PINGPONG, +  GSL_WAVE_TOKEN_LAST_DATA +} GslWaveTokenType; + + +/* --- structures --- */ +typedef struct +{ +  GslWaveFileInfo wfi; +  gchar          *cwd; +} FileInfo; + +typedef struct +{ +  GslWaveDsc        wdsc; +  GslWaveFormatType format; +  guint		    byte_order; +  gfloat	    dfl_mix_freq; +} WaveDsc; + + +/* --- tokens --- */ +static const char *wave_tokens_512[] = { +  "wave",       "chunk",        "name",         "byte_order", +  "format",     "n_channels",   "mix_freq",     "osc_freq", +  "midi_note",  "file",         "index",	"boffset", +  "n_values",	"loop_type",	"loop_start",	"loop_end", +  "loop_count", +}; +static const char *wave_tokens_768[] = { +  "big_endian", "big",          "little_endian", "little", +  "signed_8",   "signed_12",    "signed_16", +  "unsigned_8", "unsigned_12",  "unsigned_16", +  "float",	"none",		"jump",		 "pingpong", +}; + + +/* --- functions --- */ +static const gchar* +gsl_wave_token (GslWaveTokenType token) +{ +  if (token >= 768) +    { +      token -= 768; +      return token > sizeof (wave_tokens_768) / sizeof (wave_tokens_768[0]) ? NULL : wave_tokens_768[token]; +    } +  else +    { +      token -= 512; +      return token > sizeof (wave_tokens_512) / sizeof (wave_tokens_512[0]) ? NULL : wave_tokens_512[token]; +    } +} + +static GTokenType +gslwave_skip_rest_statement (GScanner *scanner, +			     guint     level) +{ +  g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + +  while (level) +    { +      g_scanner_get_next_token (scanner); +      switch (scanner->token) +	{ +	case G_TOKEN_EOF: case G_TOKEN_ERROR:                   return '}'; +	case '(': case '{': case '[':           level++;        break; +	case ')': case '}': case ']':           level--;        break; +	default:                                                break; +	} +    } + +  return G_TOKEN_NONE; +} + +static GslWaveFileInfo* +gslwave_load_file_info (gpointer      data, +			const gchar  *_file_name, +			GslErrorType *error_p) +{ +  FileInfo *fi = NULL; +  gboolean in_wave = FALSE, abort = FALSE; +  GslRing *wave_names = NULL; +  GScanner *scanner; +  gchar *cwd, *file_name; +  gint fd; +  guint i; + +  if (g_path_is_absolute (_file_name)) +    { +      gchar *p = strrchr (_file_name, G_DIR_SEPARATOR); + +      g_assert (p != NULL); +      cwd = g_strndup (_file_name, p - _file_name + 1); +      file_name = g_strdup (_file_name); +    } +  else +    { +      cwd = g_get_current_dir (); +      file_name = g_strdup_printf ("%s%c%s", cwd, G_DIR_SEPARATOR, _file_name); +    } + +  fd = open (file_name, O_RDONLY); +  if (fd < 0) +    { +      *error_p = GSL_ERROR_OPEN_FAILED; +      g_free (cwd); +      g_free (file_name); +      return NULL; +    } + +  scanner = g_scanner_new (NULL); +  scanner->config->symbol_2_token = TRUE; +  g_scanner_scope_add_symbol (scanner, 0, "wave", GUINT_TO_POINTER (GSL_WAVE_TOKEN_WAVE)); +  g_scanner_scope_add_symbol (scanner, 0, "name", GUINT_TO_POINTER (GSL_WAVE_TOKEN_NAME)); +  g_scanner_input_file (scanner, fd); +  while (!abort) +    { +      g_scanner_get_next_token (scanner); +      switch (scanner->token) +	{ +	case GSL_WAVE_TOKEN_WAVE: +	  if (g_scanner_peek_next_token (scanner) == '{') +	    { +	      g_scanner_get_next_token (scanner); /* eat '{' */ +	      in_wave = TRUE; +	    } +	  break; +	case '{': +	  if (gslwave_skip_rest_statement (scanner, 1) != G_TOKEN_NONE) +	    abort = TRUE; +	  break; +	case GSL_WAVE_TOKEN_NAME: +	  if (in_wave && g_scanner_peek_next_token (scanner) == '=') +	    { +	      g_scanner_get_next_token (scanner); /* eat '=' */ +	      if (g_scanner_peek_next_token (scanner) == G_TOKEN_STRING) +		{ +		  gchar *wave_name; +		   +		  g_scanner_get_next_token (scanner); /* eat string */ +		  wave_name = g_strdup (scanner->value.v_string); +		  if (gslwave_skip_rest_statement (scanner, 1) == G_TOKEN_NONE) +		    { +		      in_wave = FALSE; +		      wave_names = gsl_ring_append (wave_names, wave_name); +		    } +		  else +		    { +		      g_free (wave_name); +		      abort = TRUE; +		    } +		} +	    } +	  break; +	default: +	  if (scanner->token == G_TOKEN_EOF || scanner->token == G_TOKEN_ERROR) +	    abort = TRUE; +	  break; +	} +    } +  g_scanner_destroy (scanner); +  close (fd); + +  if (wave_names) +    { +      GslRing *ring; + +      fi = gsl_new_struct0 (FileInfo, 1); +      fi->wfi.n_waves = gsl_ring_length (wave_names); +      fi->wfi.waves = g_malloc0 (sizeof (fi->wfi.waves[0]) * fi->wfi.n_waves); +      for (i = 0, ring = wave_names; i < fi->wfi.n_waves; i++, ring = ring->next) +	fi->wfi.waves[i].name = ring->data; +      gsl_ring_free (wave_names); +      fi->cwd = cwd; +    } +  else +    g_free (cwd); +  g_free (file_name); + +  /* FIXME: empty wave error? */ + +  return fi ? &fi->wfi : NULL; +} + +static void +gslwave_free_file_info (gpointer         data, +			GslWaveFileInfo *file_info) +{ +  FileInfo *fi = (FileInfo*) file_info; +  guint i; + +  for (i = 0; i < fi->wfi.n_waves; i++) +    g_free (fi->wfi.waves[i].name); +  g_free (fi->wfi.waves); +  g_free (fi->cwd); +  gsl_delete_struct (FileInfo, fi); +} + +static guint +gslwave_parse_chunk_dsc (GScanner        *scanner, +			 GslWaveChunkDsc *chunk) +{ +  parse_or_return (scanner, '{'); +  do +    switch (g_scanner_get_next_token (scanner)) +      { +      case '}': +	return G_TOKEN_NONE; +      default: +	return '}'; +      case GSL_WAVE_TOKEN_FILE: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_STRING); +	g_free (chunk->loader_data1);	/* file_name */ +	chunk->loader_data1 = g_strdup (scanner->value.v_string); +	break; +      case GSL_WAVE_TOKEN_INDEX: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_STRING); +	g_free (chunk->loader_data2);	/* wave_name */ +	chunk->loader_data2 = g_strdup (scanner->value.v_string); +	break; +      case GSL_WAVE_TOKEN_MIX_FREQ: +	parse_or_return (scanner, '='); +	switch (g_scanner_get_next_token (scanner)) +	  { +	  case G_TOKEN_FLOAT:	chunk->mix_freq = scanner->value.v_float;	break; +	  case G_TOKEN_INT:	chunk->mix_freq = scanner->value.v_int;		break; +	  default:		return G_TOKEN_FLOAT; +	  } +	break; +      case GSL_WAVE_TOKEN_OSC_FREQ: +	parse_or_return (scanner, '='); +	switch (g_scanner_get_next_token (scanner)) +	  { +	  case G_TOKEN_FLOAT:	chunk->osc_freq = scanner->value.v_float;	break; +	  case G_TOKEN_INT:	chunk->osc_freq = scanner->value.v_int;		break; +	  default:		return G_TOKEN_FLOAT; +	  } +	break; +      case GSL_WAVE_TOKEN_MIDI_NOTE: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	chunk->osc_freq = gsl_temp_freq (gsl_get_config ()->kammer_freq, +					 scanner->value.v_int - gsl_get_config ()->midi_kammer_note); +	break; +      case GSL_WAVE_TOKEN_BOFFSET: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	chunk->loader_offset = scanner->value.v_int;	/* byte_offset */ +	break; +      case GSL_WAVE_TOKEN_N_VALUES: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	chunk->loader_length = scanner->value.v_int;	/* n_values */ +	break; +      case GSL_WAVE_TOKEN_LOOP_TYPE: +	parse_or_return (scanner, '='); +	switch (g_scanner_get_next_token (scanner)) +	  { +	  case GSL_WAVE_TOKEN_NONE:	chunk->loop_type = GSL_WAVE_LOOP_NONE;		break; +	  case GSL_WAVE_TOKEN_JUMP:	chunk->loop_type = GSL_WAVE_LOOP_JUMP;		break; +	  case GSL_WAVE_TOKEN_PINGPONG:	chunk->loop_type = GSL_WAVE_LOOP_PINGPONG;	break; +	  default:			return GSL_WAVE_TOKEN_JUMP; +	  } +	break; +      case GSL_WAVE_TOKEN_LOOP_START: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	chunk->loop_start = scanner->value.v_int; +	break; +      case GSL_WAVE_TOKEN_LOOP_END: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	chunk->loop_end = scanner->value.v_int; +	break; +      case GSL_WAVE_TOKEN_LOOP_COUNT: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	chunk->loop_count = scanner->value.v_int; +	break; +      } +  while (TRUE); +} + +static guint +gslwave_parse_wave_dsc (GScanner    *scanner, +			WaveDsc     *dsc, +			const gchar *wave_name) +{ +  parse_or_return (scanner, '{'); +  do +    switch (g_scanner_get_next_token (scanner)) +      { +	guint i, token; +      case '}': +	return G_TOKEN_NONE; +      default: +	return '}'; +      case GSL_WAVE_TOKEN_NAME: +	if (dsc->wdsc.name) +	  return '}'; +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_STRING); +	if (wave_name) +	  { +	    if (strcmp (wave_name, scanner->value.v_string) == 0) +	      dsc->wdsc.name = g_strdup (scanner->value.v_string); +	    else +	      return gslwave_skip_rest_statement (scanner, 1); +	  } +	else +	  dsc->wdsc.name = g_strdup (scanner->value.v_string); +	break; +      case GSL_WAVE_TOKEN_CHUNK: +	if (g_scanner_peek_next_token (scanner) != '{') +	  parse_or_return (scanner, '{'); +	i = dsc->wdsc.n_chunks++; +	dsc->wdsc.chunks = g_realloc (dsc->wdsc.chunks, sizeof (dsc->wdsc.chunks[0]) * dsc->wdsc.n_chunks); +	memset (dsc->wdsc.chunks + i, 0, sizeof (dsc->wdsc.chunks[0]) * 1); +	dsc->wdsc.chunks[i].mix_freq = dsc->dfl_mix_freq; +	dsc->wdsc.chunks[i].osc_freq = dsc->dfl_mix_freq;	/* we check this later */ +	dsc->wdsc.chunks[i].loop_type = GSL_WAVE_LOOP_JUMP; +	dsc->wdsc.chunks[i].loop_start = GSL_MAXLONG; +	dsc->wdsc.chunks[i].loop_end = -1; +	dsc->wdsc.chunks[i].loop_count = 1000000; /* FIXME */ +	dsc->wdsc.chunks[i].loader_offset = 0;			/* offset in bytes */ +	dsc->wdsc.chunks[i].loader_length = 0;			/* length in n_values */ +	dsc->wdsc.chunks[i].loader_data1 = NULL;		/* file_name */ +	dsc->wdsc.chunks[i].loader_data2 = NULL;		/* wave_name */ +	token = gslwave_parse_chunk_dsc (scanner, dsc->wdsc.chunks + i); +	if (token != G_TOKEN_NONE) +	  return token; +	if (dsc->wdsc.chunks[i].loop_end < dsc->wdsc.chunks[i].loop_start) +	  { +	    dsc->wdsc.chunks[i].loop_type = GSL_WAVE_LOOP_NONE; +	    dsc->wdsc.chunks[i].loop_start = 0; +	    dsc->wdsc.chunks[i].loop_end = 0; +	    dsc->wdsc.chunks[i].loop_count = 0; +	  } +	if (dsc->wdsc.chunks[i].osc_freq >= dsc->wdsc.chunks[i].mix_freq / 2.) +	  g_scanner_error (scanner, "wave chunk \"%s\" mixing frequency is invalid: mix_freq=%f osc_freq=%f", +			   dsc->wdsc.chunks[i].loader_data1 ? (gchar*) dsc->wdsc.chunks[i].loader_data1 : "", +			   dsc->wdsc.chunks[i].mix_freq, +			   dsc->wdsc.chunks[i].osc_freq); +	break; +      case GSL_WAVE_TOKEN_BYTE_ORDER: +	parse_or_return (scanner, '='); +	token = g_scanner_get_next_token (scanner); +	switch (token) +	  { +	  case GSL_WAVE_TOKEN_LITTLE_ENDIAN: +	  case GSL_WAVE_TOKEN_LITTLE:		dsc->byte_order = G_LITTLE_ENDIAN; break; +	  case GSL_WAVE_TOKEN_BIG_ENDIAN: +	  case GSL_WAVE_TOKEN_BIG:		dsc->byte_order = G_BIG_ENDIAN;    break; +	  default:				return GSL_WAVE_TOKEN_LITTLE_ENDIAN; +	  } +	break; +      case GSL_WAVE_TOKEN_FORMAT: +	parse_or_return (scanner, '='); +	token = g_scanner_get_next_token (scanner); +	switch (token) +	  { +	  case GSL_WAVE_TOKEN_SIGNED_8:		dsc->format = GSL_WAVE_FORMAT_SIGNED_8;    break; +	  case GSL_WAVE_TOKEN_SIGNED_12:	dsc->format = GSL_WAVE_FORMAT_SIGNED_12;   break; +	  case GSL_WAVE_TOKEN_SIGNED_16:	dsc->format = GSL_WAVE_FORMAT_SIGNED_16;   break; +	  case GSL_WAVE_TOKEN_UNSIGNED_8:	dsc->format = GSL_WAVE_FORMAT_UNSIGNED_8;  break; +	  case GSL_WAVE_TOKEN_UNSIGNED_12:	dsc->format = GSL_WAVE_FORMAT_UNSIGNED_12; break; +	  case GSL_WAVE_TOKEN_UNSIGNED_16:	dsc->format = GSL_WAVE_FORMAT_UNSIGNED_16; break; +	  case GSL_WAVE_TOKEN_FLOAT:		dsc->format = GSL_WAVE_FORMAT_FLOAT;	   break; +	  default:				return GSL_WAVE_TOKEN_SIGNED_16; +	  } +	break; +      case GSL_WAVE_TOKEN_N_CHANNELS: +	parse_or_return (scanner, '='); +	parse_or_return (scanner, G_TOKEN_INT); +	dsc->wdsc.n_channels = scanner->value.v_int; +	if (dsc->wdsc.n_channels < 1) +	  return G_TOKEN_INT; +	break; +      case GSL_WAVE_TOKEN_MIX_FREQ: +	parse_or_return (scanner, '='); +	switch (g_scanner_get_next_token (scanner)) +	  { +	  case G_TOKEN_FLOAT:   dsc->dfl_mix_freq = scanner->value.v_float;    break; +	  case G_TOKEN_INT:     dsc->dfl_mix_freq = scanner->value.v_int;      break; +	  default:		return G_TOKEN_FLOAT; +	  } +	break; +      } +  while (TRUE); +} + +static void +gslwave_wave_dsc_free (WaveDsc *dsc) +{ +  guint i; + +  for (i = 0; i < dsc->wdsc.n_chunks; i++) +    { +      g_free (dsc->wdsc.chunks[i].loader_data1); /* file_name */ +      g_free (dsc->wdsc.chunks[i].loader_data2); /* wave_name */ +    } +  g_free (dsc->wdsc.chunks); +  g_free (dsc->wdsc.name); +  gsl_delete_struct (WaveDsc, dsc); +} + +static GslWaveDsc* +gslwave_load_wave_dsc (gpointer         data, +		       GslWaveFileInfo *file_info, +		       guint            nth_wave, +		       GslErrorType    *error_p) +{ +  GScanner *scanner; +  WaveDsc *dsc; +  guint token, i; +  gint fd; + +  fd = open (file_info->file_name, O_RDONLY); +  if (fd < 0) +    { +      *error_p = GSL_ERROR_OPEN_FAILED; +      return NULL; +    } + +  scanner = g_scanner_new (NULL); +  scanner->config->symbol_2_token = TRUE; +  scanner->input_name = file_info->file_name; +  g_scanner_input_file (scanner, fd); +  for (i = GSL_WAVE_TOKEN_WAVE; i < GSL_WAVE_TOKEN_LAST_FIELD; i++) +    g_scanner_scope_add_symbol (scanner, 0, gsl_wave_token (i), GUINT_TO_POINTER (i)); +  for (i = GSL_WAVE_TOKEN_BIG_ENDIAN; i < GSL_WAVE_TOKEN_LAST_DATA; i++) +    g_scanner_scope_add_symbol (scanner, 0, gsl_wave_token (i), GUINT_TO_POINTER (i)); + + continue_scanning: +  dsc = gsl_new_struct0 (WaveDsc, 1); +  dsc->wdsc.name = NULL; +  dsc->wdsc.n_chunks = 0; +  dsc->wdsc.chunks = NULL; +  dsc->wdsc.n_channels = 1; +  dsc->format = GSL_WAVE_FORMAT_SIGNED_16; +  dsc->byte_order = G_LITTLE_ENDIAN; +  dsc->dfl_mix_freq = 44100; +  if (g_scanner_get_next_token (scanner) != GSL_WAVE_TOKEN_WAVE) +    token = GSL_WAVE_TOKEN_WAVE; +  else +    token = gslwave_parse_wave_dsc (scanner, dsc, file_info->waves[nth_wave].name); +  if (token != G_TOKEN_NONE || scanner->parse_errors) +    { +      gslwave_wave_dsc_free (dsc); +      dsc = NULL; +      if (!scanner->parse_errors) +	g_scanner_unexp_token (scanner, token, "identifier", "keyword", NULL, "discarding wave", TRUE); /* FIXME */ +    } +  else +    { +      if (dsc->wdsc.n_chunks && dsc->wdsc.name) +	{ +	  /* found the correctly named wave and parsed it */ +	} +      else +	{ +	  /* got invalid/wrong wave */ +	  gslwave_wave_dsc_free (dsc); +	  dsc = NULL; +	  goto continue_scanning;	/* next attempt */ +	} +    } +  g_scanner_destroy (scanner); +  close (fd); + +  return dsc ? &dsc->wdsc : NULL; +} + +static void +gslwave_free_wave_dsc (gpointer    data, +		       GslWaveDsc *wave_dsc) +{ +  WaveDsc *dsc = (WaveDsc*) wave_dsc; + +  gslwave_wave_dsc_free (dsc); +} + +static GslDataHandle* +gslwave_load_singlechunk_wave (GslWaveFileInfo *fi, +			       const gchar     *wave_name, +			       GslErrorType    *error_p) +{ +  GslWaveDsc *wdsc; +  guint i; + +  if (fi->n_waves == 1 && !wave_name) +    i = 0; +  else if (!wave_name) +    { +      /* don't know which wave to pick */ +      *error_p = GSL_ERROR_FORMAT_INVALID; +      return NULL; +    } +  else /* find named wave */ +    for (i = 0; i < fi->n_waves; i++) +      if (strcmp (fi->waves[i].name, wave_name) == 0) +	break; +  if (i >= fi->n_waves) +    { +      *error_p = GSL_ERROR_NOT_FOUND; +      return NULL; +    } + +  wdsc = gsl_wave_dsc_load (fi, i, error_p); +  if (!wdsc) +    return NULL; + +  if (wdsc->n_chunks == 1) +    { +      GslDataHandle *dhandle = gsl_wave_handle_create (wdsc, 0, error_p); + +      gsl_wave_dsc_free (wdsc); +      return dhandle; +    } + +  /* this is ridiculous, letting the chunk of a wave +   * point to a wave with multiple chunks... +   */ +  gsl_wave_dsc_free (wdsc); +  *error_p = GSL_ERROR_FORMAT_INVALID; +  return NULL; +} + +static GslDataHandle* +gslwave_create_chunk_handle (gpointer      data, +			     GslWaveDsc   *wave_dsc, +			     guint         nth_chunk, +			     GslErrorType *error_p) +{ +  WaveDsc *dsc = (WaveDsc*) wave_dsc; +  FileInfo *fi = (FileInfo*) dsc->wdsc.file_info; +  GslWaveChunkDsc *chunk = wave_dsc->chunks + nth_chunk; + +  if (chunk->loader_data1)	/* file_name */ +    { +      GslDataHandle *dhandle; +      GslWaveFileInfo *cfi; +      gchar *string; + + +      /* construct chunk file name from (hopefully) relative path +       */ +      if (g_path_is_absolute (chunk->loader_data1)) +	string = g_strdup (chunk->loader_data1); +      else +	string = g_strdup_printf ("%s%c%s", fi->cwd, G_DIR_SEPARATOR, (char*) chunk->loader_data1); + + +      /* first, try to load the chunk via registered loaders +       */ +      cfi = gsl_wave_file_info_load (string, error_p); +      if (cfi) +	{ +	  /* FIXME: there's a potential attack here, in letting a single chunk +	   * wave's chunk point to its own wave. this'll trigger recursions until +	   * stack overflow +	   */ +	  dhandle = gslwave_load_singlechunk_wave (cfi, +						   chunk->loader_data2,	/* wave_name */ +						   error_p); +	  gsl_wave_file_info_unref (cfi); +	  g_free (string); +	  return dhandle; +	} + + +      /* didn't work, assume it's a raw sample +       */ +      if (chunk->loader_data2)	/* wave_name */ +	{ +	  /* raw samples don't give names to their data */ +	  *error_p = GSL_ERROR_NOT_FOUND; +	  g_free (string); +	  return NULL; +	} +      dhandle = gsl_wave_handle_new (string,	/* file_name */ +				     dsc->wdsc.n_channels, +				     dsc->format, +				     dsc->byte_order, +				     chunk->loader_offset,	/* byte_offset */ +				     chunk->loader_length > 0	/* n_values */ +				     ? chunk->loader_length +				     : -1); +      *error_p = dhandle ? GSL_ERROR_NONE : GSL_ERROR_IO; +      g_free (string); +      return dhandle; +    }	 +  else +    { +      /* no file_name specified */ +      *error_p = GSL_ERROR_NOT_FOUND; +      return NULL; +    } +} + +void +_gsl_init_loader_gslwave (void) +{ +  static const gchar *file_exts[] = { "gslwave", NULL, }; +  static const gchar *mime_types[] = { "audio/x-gslwave", NULL, }; +  static const gchar *magics[] = { "0 string #GslWave", NULL, }; +  static GslLoader loader = { +    "GslWave", +    file_exts, +    mime_types, +    magics, +    0,  /* priority */ +    NULL, +    gslwave_load_file_info, +    gslwave_free_file_info, +    gslwave_load_wave_dsc, +    gslwave_free_wave_dsc, +    gslwave_create_chunk_handle, +  }; +  static gboolean initialized = FALSE; + +  g_assert (initialized == FALSE); +  initialized = TRUE; + +  gsl_loader_register (&loader); +}  | 
