/* 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "gslloader.h" #include "gsldatahandle.h" #include "gslmath.h" #include #include #include #include #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); }