/*================================================================== * SwamiSamplelib.c - Swami Wavetable object * * Swami * Copyright (C) 1999-2003 Josh Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net *==================================================================*/ #include #include #include #include "SwamiSamplelib.h" #include "SwamiConfig.h" #include "SwamiObject.h" #include "i18n.h" /* size of sample data copy buffer */ #define COPY_BUFFER_SIZE (64 * 1024) /* --- signals and properties --- */ enum { LAST_SIGNAL }; enum { PROP_0, }; /* --- private function prototypes --- */ static void swami_samplelib_class_init (SwamiSamplelibClass *klass); static void swami_samplelib_init (SwamiSamplelib *samplelib); static gboolean samplelib_okay (SwamiSamplelibHandle *handle); /* --- private data --- */ // static guint samplelib_signals[LAST_SIGNAL] = { 0 }; /* --- functions --- */ GType swami_samplelib_get_type (void) { static GType item_type = 0; if (!item_type) { static const GTypeInfo item_info = { sizeof (SwamiSamplelibClass), NULL, NULL, (GClassInitFunc) swami_samplelib_class_init, NULL, NULL, sizeof (SwamiSamplelib), 0, (GInstanceInitFunc) swami_samplelib_init, }; item_type = g_type_register_static (G_TYPE_OBJECT, "SwamiSamplelib", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } static void swami_samplelib_class_init (SwamiSamplelibClass *klass) { } static void swami_samplelib_init (SwamiSamplelib *samplelib) { } /** * swami_samplelib_new: * * Create a new samplelib object * * Returns: New Swami Samplelib object */ SwamiSamplelib * swami_samplelib_new (void) { return (SWAMI_SAMPLELIB (g_object_new (SWAMI_TYPE_SAMPLELIB, NULL))); } /** * swami_samplelib_load_sampledata: * @handle: Swami samplelib handle * @left: OUTPUT: Created sample data object for left channel (stereo) * or mono data. Can be NULL if sample is stereo, in which case left channel * will be ignored. * @right: OUTPUT: Created sample data object for right channel (stereo). * Not used for mono data and its value is irrelevant. Can be NULL for * stereo data in which case the right channel will be ignored. * * Load a sample file into new sample data structures * * Returns: SWAMI_OK on success, SWAMI_FAIL otherwise */ int swami_samplelib_load_sampledata (SwamiSamplelibHandle *handle, IPSampleData **left, IPSampleData **right) { SwamiObject *swami; IPSampleData *right_data = NULL, *left_data = NULL; IPSampleStore *right_store, *left_store; gint16 *buf = NULL; int channels, max_size, size, ofs, i, samples; if (left) *left = NULL; if (right) *right = NULL; g_return_val_if_fail (handle != NULL, SWAMI_FAIL); g_return_val_if_fail (handle->samplelib != NULL, SWAMI_FAIL); g_return_val_if_fail (SWAMI_IS_SAMPLELIB (handle->samplelib), SWAMI_FAIL); channels = handle->params.channels; /* no data to store? (bad bad programmer really) */ if ((channels == 1 && !left) || (channels == 2 && !left && !right)) return (SWAMI_OK); swami = SWAMI_OBJECT (g_object_get_data (G_OBJECT (handle->samplelib), "SwamiObject")); /* sample max size is in megabytes */ max_size = swami_config_get_int ("swami", "sample_max_size", NULL) * 1024 * 1024; if (handle->size * channels * 2 > max_size) { g_critical ("The requested sample exceeds the maximum allowable" " size of %d bytes (set in preferences)", max_size); return (SWAMI_FAIL); } if (left) { if (!(left_data = instp_sample_data_new ())) goto _error; instp_sample_data_set_size (left_data, handle->size); if (!(left_store = instp_sample_store_new (left_data, IPSAMPLE_METHOD_BUFFER))) goto _error; if (instp_sample_store_alloc (left_data, left_store) != INSTP_OK) goto _error; } if (right && channels == 2) { if (!(right_data = instp_sample_data_new ())) goto _error; instp_sample_data_set_size (right_data, handle->size); if (!(right_store = instp_sample_store_new (right_data, IPSAMPLE_METHOD_BUFFER))) goto _error; if (instp_sample_store_alloc (right_data, right_store) != INSTP_OK) goto _error; } /* allocate 2 times the needed size for stereo data, so it can be split */ buf = g_malloc (COPY_BUFFER_SIZE * channels * 2); /* use the same size buffer (in bytes) for mono or stereo data */ size = COPY_BUFFER_SIZE / channels; ofs = 0; while (ofs < handle->size) { if (handle->size - ofs < size) /* check for last partial fragment */ size = handle->size - ofs; if (swami_samplelib_read (handle, size, buf) < size) goto _error; /* does data need to be de-interleaved? */ if (channels == 2) { samples = size * 2; for (i = 0; i < samples; i++) { if (i & 1) buf[samples + (i >> 1)] = buf[i]; else buf[i >> 1] = buf[i]; } } if (left_data && instp_sample_store_write (left_data, left_store, ofs, size, buf) != INSTP_OK) goto _error; if (right_data && instp_sample_store_write (right_data, right_store, ofs, size, &buf[samples]) != INSTP_OK) goto _error; ofs += size; } if (left_data) *left = left_data; if (right_data) *right = right_data; if (buf) g_free (buf); return (SWAMI_OK); _error: /* FIXME! */ // if (left_data) instp_sample_data_destroy (left_data); // if (right_data) instp_sample_data_destroy (right_data); if (buf) g_free (buf); return (SWAMI_FAIL); } /** * swami_samplelib_save_sampledata: * @handle: Swami samplelib handle * @left: Sample data object for left channel (stereo) or mono data. * @right: Sample data object for right channel (stereo). Should be %NULL * for mono data. * * Save sample data to a sample file. * * Returns: SWAMI_OK on success, SWAMI_FAIL otherwise */ int swami_samplelib_save_sampledata (SwamiSamplelibHandle *handle, IPSampleData *left, IPSampleData *right) { gint16 *buf = NULL; int channels, frames, samples, ofs, i; IPSampleStore *right_store, *left_store; g_return_val_if_fail (handle != NULL, SWAMI_FAIL); g_return_val_if_fail (handle->samplelib != NULL, SWAMI_FAIL); g_return_val_if_fail (SWAMI_IS_SAMPLELIB (handle->samplelib), SWAMI_FAIL); g_return_val_if_fail (INSTP_IS_SAMPLE_DATA (left), SWAMI_FAIL); channels = handle->params.channels; g_return_val_if_fail (channels == 1 || channels == 2, SWAMI_FAIL); g_return_val_if_fail (channels != 2 || INSTP_IS_SAMPLE_DATA (right), SWAMI_FAIL); left_store = instp_sample_data_find_store (left, 0, IPSAMPLE_STORE_FIND_FASTEST | IPSAMPLE_STORE_FIND_READABLE); if (channels == 2) right_store = instp_sample_data_find_store (right, 0, IPSAMPLE_STORE_FIND_FASTEST | IPSAMPLE_STORE_FIND_READABLE); if (!left_store || (channels == 2 && !right_store)) { g_critical ("Cannot save sample, missing sample store"); return (SWAMI_FAIL); } if (channels == 2 && left->size != right->size) { g_critical ("Stereo sample pair differ in size, cannot save"); return (SWAMI_FAIL); } /* allocate 2 times needed size for stereo data, so it can be interleaved */ buf = g_malloc (COPY_BUFFER_SIZE * channels * 2); /* use the same size buffer (in bytes) for mono or stereo data */ frames = COPY_BUFFER_SIZE / channels; ofs = 0; while (ofs < left->size) { if (left->size - ofs < frames) /* check for last partial fragment */ frames = left->size - ofs; if (channels == 1) { if (instp_sample_store_read (left, left_store, ofs, frames, buf) != INSTP_OK) goto _error; } else /* stereo */ { if (instp_sample_store_read (left, left_store, ofs, frames, &buf[frames*2]) != INSTP_OK) goto _error; if (instp_sample_store_read (right, right_store, ofs, frames, &buf[frames*3]) != INSTP_OK) goto _error; samples = frames * 2; /* interleave the data */ for (i = 0; i < samples; i++) { if (i & 1) buf[i] = buf[frames*3 + (i >> 1)]; else buf[i] = buf[frames*2 + (i >> 1)]; } } if (swami_samplelib_write (handle, frames, buf) < frames) goto _error; ofs += frames; } g_free (buf); return (SWAMI_OK); _error: g_free (buf); return (SWAMI_FAIL); } /** * swami_samplelib_init_sample: * @handle: Handle of opened sample file in read mode. * @sample: Sample to set parameters of to match the file. * * Initialize sample parameters from an open sample file */ void swami_samplelib_init_sample (SwamiSamplelibHandle *handle, IPSample *sample) { SwamiSamplelibParams *params; g_return_if_fail (handle != NULL); g_return_if_fail (handle->samplelib != NULL); g_return_if_fail (SWAMI_IS_SAMPLELIB (handle->samplelib)); g_return_if_fail (sample != NULL); params = &handle->params; if (params->loop_type == SWAMI_AUDIO_LOOP_NONE) { sample->loopstart = 8; sample->loopend = handle->size - 8; } else { sample->loopstart = params->loop_start; sample->loopend = params->loop_end; } if (params->rate >= IPSAMPLE_RATE_MIN && params->rate <= IPSAMPLE_RATE_MAX) sample->samplerate = params->rate; else sample->samplerate = 44100; sample->origpitch = params->root_note; sample->pitchadj = params->fine_tune; } /** * swami_samplelib_set_params_default: * @params: The parameter structure to initialize to 44100hz sampling * rate, 1 channel (mono), no looping, root note of 60, and no fine tuning. * * Set a params structure to default values */ void swami_samplelib_set_params_default (SwamiSamplelibParams *params) { g_return_if_fail (params != NULL); params->file_type = SWAMI_SAMPLELIB_TYPE_AIFF; params->rate = 44100; params->channels = 1; params->loop_type = SWAMI_AUDIO_LOOP_NONE; params->root_note = 60; params->fine_tune = 0; params->width = 16; params->signd = TRUE; params->lendian = TRUE; } /** * swami_samplelib_set_params_from_sample: * @sample: Sample to initialize @params with * @params: Sample lib param structure to initialize * * Sets a sample library parameter structure to the format of the @sample. * Note that if the sample is linked the @params will be set to stereo. */ void swami_samplelib_set_params_from_sample (IPSample *sample, SwamiSamplelibParams *params) { g_return_if_fail (INSTP_IS_SAMPLE (sample)); g_return_if_fail (params != NULL); params->file_type = SWAMI_SAMPLELIB_TYPE_AIFF; params->rate = sample->samplerate; params->channels = sample->linked ? 2 : 1; params->loop_type = SWAMI_AUDIO_LOOP_NORMAL; params->loop_start = sample->loopstart; params->loop_end = sample->loopend; params->root_note = sample->origpitch; params->fine_tune = sample->pitchadj; params->width = 16; params->signd = TRUE; params->lendian = TRUE; } /** * swami_samplelib_open: * @samplelib: Swami Samplelib object * @filename: Path and name of file to open * @mode: File access mode 'r' for read only, 'w' for write only * @params: For write access mode only. Determines parameters of * written file. * * Open a sample file * * Returns: The new handle or NULL on error. */ SwamiSamplelibHandle * swami_samplelib_open (SwamiSamplelib *samplelib, char *filename, char mode, SwamiSamplelibParams *params) { SwamiSamplelibHandle *handle; SwamiSamplelibClass *oclass; g_return_val_if_fail (samplelib != NULL, NULL); g_return_val_if_fail (SWAMI_IS_SAMPLELIB (samplelib), NULL); g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (mode != 'w' || params != NULL, NULL); handle = g_malloc0 (sizeof (SwamiSamplelibHandle)); handle->samplelib = samplelib; handle->filename = filename; handle->mode = (mode == 'w') ? 'w' : 'r'; if (params) handle->params = *params; else swami_samplelib_set_params_default (&handle->params); oclass = SWAMI_SAMPLELIB_CLASS (G_OBJECT_GET_CLASS (samplelib)); if ((*oclass->open) (handle) != SWAMI_OK) { g_free (handle); return (NULL); } if (!samplelib_okay (handle)) /* check if file parameters are okay */ { swami_samplelib_close (handle); return (NULL); } return (handle); } /* FIXME! */ /* check if a sample file handle's parameters are sane */ static gboolean samplelib_okay (SwamiSamplelibHandle *handle) { if (handle->mode == 'r') { if (handle->size < IPSAMPLE_LENGTH_MIN) { g_critical (_("Sample is too small (%d samples)"), handle->size); return (FALSE); } } return (TRUE); } /** * swami_samplelib_close: * @handle: Swami samplelib handle * * Close a sample file */ void swami_samplelib_close (SwamiSamplelibHandle *handle) { SwamiSamplelibClass *oclass; g_return_if_fail (handle != NULL); g_return_if_fail (handle->samplelib != NULL); g_return_if_fail (SWAMI_IS_SAMPLELIB (handle->samplelib)); oclass = SWAMI_SAMPLELIB_CLASS (G_OBJECT_GET_CLASS (handle->samplelib)); (*oclass->close) (handle); g_free (handle); } /** * swami_samplelib_read: * @handle: Swami Samplelib handle returned by the open_file function * @frames: Number of sample frames to read * @buf: Buffer is filled with samples. Should be (2 * size * channels). * If the sample is stereo it is stored in left followed by right channel * order. * * Read sample data from an opened file * * Returns: The number of frames successfully read. Could be less than the * amount requested if an end of file occurs or error. * Use #swami_samplelib_is_eof to determine which occured. */ int swami_samplelib_read (SwamiSamplelibHandle *handle, int frames, gint16 *buf) { SwamiSamplelibClass *oclass; g_return_val_if_fail (handle != NULL, 0); g_return_val_if_fail (handle->samplelib != NULL, 0); g_return_val_if_fail (frames > 0, 0); g_return_val_if_fail (buf != NULL, 0); oclass = SWAMI_SAMPLELIB_CLASS (G_OBJECT_GET_CLASS (handle->samplelib)); return ((*oclass->read) (handle, frames, buf)); } /** * swami_samplelib_write: * @handle: Swami Samplelib handle returned by the open_file function * @frames: Number of sample frames to write * @buf: Buffer is saved to sample file. Should be * (2 * frames * channels) bytes. If the sample is more than one channel the * samples should be stored in left-right interleaved fashion. * * Write samples to an opened file * * Returns: The number of frames successfully written. Could be less than the * amount requested if an error occurs. */ int swami_samplelib_write (SwamiSamplelibHandle *handle, int frames, const gint16 *buf) { SwamiSamplelibClass *oclass; g_return_val_if_fail (handle != NULL, 0); g_return_val_if_fail (handle->samplelib != NULL, 0); g_return_val_if_fail (frames > 0, 0); g_return_val_if_fail (buf != NULL, 0); oclass = SWAMI_SAMPLELIB_CLASS (G_OBJECT_GET_CLASS (handle->samplelib)); return ((*oclass->write) (handle, frames, buf)); } /** * swami_samplelib_is_eof: * @handle: Swami Samplelib handle returned by the open_file function * * Checks for end of file status of file opened with @handle. * * Returns: TRUE if end of file, FALSE otherwise */ gboolean swami_samplelib_is_eof (SwamiSamplelibHandle *handle) { g_return_val_if_fail (handle != NULL, TRUE); return (handle->eof_flag); } /** * swami_samplelib_type_ext: * @type: Sample file type * * Get extension (not including '.') for a given sample type. * * Returns: File extension, which is static and should not be modified or * freed. */ char * swami_samplelib_type_ext (SwamiSamplelibType type) { switch (type) { case SWAMI_SAMPLELIB_TYPE_AIFF: return ("aiff"); case SWAMI_SAMPLELIB_TYPE_WAVE: return ("wav"); case SWAMI_SAMPLELIB_TYPE_AU: return ("au"); case SWAMI_SAMPLELIB_TYPE_RAW: return ("raw"); default: return (""); } }