summaryrefslogtreecommitdiff
path: root/third_party/resample/sndlib-20/sound.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/resample/sndlib-20/sound.c')
-rw-r--r--third_party/resample/sndlib-20/sound.c1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/third_party/resample/sndlib-20/sound.c b/third_party/resample/sndlib-20/sound.c
new file mode 100644
index 00000000..8443dcaa
--- /dev/null
+++ b/third_party/resample/sndlib-20/sound.c
@@ -0,0 +1,1072 @@
+/* sound.c */
+
+#include <mus-config.h>
+
+#if USE_SND
+ #include "snd.h"
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdarg.h>
+
+#if (defined(HAVE_LIBC_H) && (!defined(HAVE_UNISTD_H)))
+ #include <libc.h>
+#else
+ #if (!(defined(_MSC_VER)))
+ #include <unistd.h>
+ #endif
+ #if HAVE_STRING_H
+ #include <string.h>
+ #endif
+#endif
+
+#include "_sndlib.h"
+#include "sndlib-strings.h"
+
+static mus_error_handler_t *mus_error_handler = NULL;
+
+mus_error_handler_t *mus_error_set_handler(mus_error_handler_t *new_error_handler)
+{
+ mus_error_handler_t *old_handler;
+ old_handler = mus_error_handler;
+ mus_error_handler = new_error_handler;
+ return(old_handler);
+}
+
+#define MUS_ERROR_BUFFER_SIZE 1024
+static char *mus_error_buffer = NULL;
+
+int mus_error(int error, const char *format, ...)
+{
+ va_list ap;
+ if (format == NULL) return(MUS_ERROR); /* else bus error in Mac OSX */
+ if (mus_error_buffer == NULL)
+ mus_error_buffer = (char *)CALLOC(MUS_ERROR_BUFFER_SIZE, sizeof(char));
+ va_start(ap, format);
+#if HAVE_VSNPRINTF
+ vsnprintf(mus_error_buffer, MUS_ERROR_BUFFER_SIZE, format, ap);
+#else
+ vsprintf(mus_error_buffer, format, ap);
+#endif
+ va_end(ap);
+ if (mus_error_handler)
+ (*mus_error_handler)(error, mus_error_buffer);
+ else
+ {
+ fprintf(stderr, mus_error_buffer);
+ fputc('\n', stderr);
+ }
+ return(MUS_ERROR);
+}
+
+static mus_print_handler_t *mus_print_handler = NULL;
+
+mus_print_handler_t *mus_print_set_handler(mus_print_handler_t *new_print_handler)
+{
+ mus_print_handler_t *old_handler;
+ old_handler = mus_print_handler;
+ mus_print_handler = new_print_handler;
+ return(old_handler);
+}
+
+void mus_print(const char *format, ...)
+{
+ va_list ap;
+ if (mus_error_buffer == NULL)
+ mus_error_buffer = (char *)CALLOC(MUS_ERROR_BUFFER_SIZE, sizeof(char));
+ if (mus_print_handler)
+ {
+ va_start(ap, format);
+#if HAVE_VSNPRINTF
+ vsnprintf(mus_error_buffer, MUS_ERROR_BUFFER_SIZE, format, ap);
+#else
+ vsprintf(mus_error_buffer, format, ap);
+#endif
+ va_end(ap);
+ (*mus_print_handler)(mus_error_buffer);
+ }
+ else
+ {
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ }
+}
+
+static const char *mus_initial_error_names[] = {
+ "no error", "no frequency method", "no phase method", "null gen arg to method", "no length method",
+ "no free method", "no describe method", "no data method", "no scaler method",
+ "memory allocation failed", "unstable two pole error",
+ "can't open file", "no sample input", "no sample output",
+ "no such channel", "no file name provided", "no location method", "no channel method",
+ "no such fft window", "unsupported data format", "header read failed",
+ "unsupported header type", "file descriptors not initialized", "not a sound file", "file closed", "write error",
+ "header write failed", "can't open temp file", "interrupted", "bad envelope",
+
+ "audio channels not available", "audio srate not available", "audio format not available",
+ "no audio input available", "audio configuration not available",
+ "no audio lines available", "audio write error", "audio size not available", "audio device not available",
+ "can't close audio", "can't open audio", "audio read error", "audio amp not available",
+ "can't write audio", "can't read audio", "no audio read permission",
+ "can't close file", "arg out of range",
+
+ "midi open error", "midi read error", "midi write error", "midi close error", "midi init error", "midi misc error",
+
+ "no channels method", "no hop method", "no width method", "no file-name method", "no ramp method", "no run method",
+ "no increment method", "no offset method",
+ "no xcoeff method", "no ycoeff method", "no xcoeffs method", "no ycoeffs method",
+};
+
+static char **mus_error_names = NULL;
+static int mus_error_names_size = 0;
+
+static int mus_error_tag = MUS_INITIAL_ERROR_TAG;
+int mus_make_error(char *error_name)
+{
+ int new_error, err, len, i;
+ new_error = mus_error_tag++;
+ err = new_error - MUS_INITIAL_ERROR_TAG;
+ if (error_name)
+ {
+ if (err >= mus_error_names_size)
+ {
+ if (mus_error_names_size == 0)
+ {
+ mus_error_names_size = 8;
+ mus_error_names = (char **)CALLOC(mus_error_names_size, sizeof(char *));
+ }
+ else
+ {
+ len = mus_error_names_size;
+ mus_error_names_size += 8;
+ mus_error_names = (char **)REALLOC(mus_error_names, mus_error_names_size * sizeof(char *));
+ for (i = len; i < mus_error_names_size; i++) mus_error_names[i] = NULL;
+ }
+ }
+ len = strlen(error_name);
+ mus_error_names[err] = (char *)CALLOC(len + 1, sizeof(char));
+ strcpy(mus_error_names[err], error_name);
+ }
+ return(new_error);
+}
+
+const char *mus_error_type_to_string(int err)
+{
+ if (err >= 0)
+ {
+ if (err < MUS_INITIAL_ERROR_TAG)
+ return(mus_initial_error_names[err]);
+ else
+ {
+ err -= MUS_INITIAL_ERROR_TAG;
+ if ((mus_error_names) && (err < mus_error_names_size))
+ return(mus_error_names[err]);
+ }
+ }
+ return("unknown mus error");
+}
+
+static void default_mus_error(int ignore, char *msg)
+{
+ /* default error handler simply prints the error message */
+ fprintf(stderr, msg);
+}
+
+static time_t local_file_write_date(const char *filename)
+{
+ struct stat statbuf;
+ int err;
+ err = stat(filename, &statbuf);
+ if (err < 0) return((time_t)0);
+ return((time_t)(statbuf.st_mtime));
+}
+
+static bool sndlib_initialized = false;
+
+int mus_sound_initialize(void)
+{
+ int err = MUS_NO_ERROR;
+ if (!sndlib_initialized)
+ {
+ sndlib_initialized = true;
+ mus_error_handler = default_mus_error;
+ err = mus_header_initialize();
+ if (err == MUS_NO_ERROR)
+ err = mus_audio_initialize();
+ return(err);
+ }
+ return(MUS_NO_ERROR);
+}
+
+int mus_sample_bits(void)
+{
+ /* this to check for inconsistent loads */
+#if SNDLIB_USE_FLOATS
+ return(sizeof(Float));
+#else
+ return(MUS_SAMPLE_BITS);
+#endif
+}
+
+typedef struct {
+ char *file_name; /* full path -- everything is keyed to this name */
+ int table_pos;
+ off_t *aux_comment_start, *aux_comment_end;
+ int *loop_modes, *loop_starts, *loop_ends;
+ int markers, base_detune, base_note;
+ int *marker_ids, *marker_positions;
+ off_t samples, true_file_length;
+ off_t data_location;
+ int srate, chans, header_type, data_format, original_sound_format, datum_size;
+ off_t comment_start, comment_end;
+ int type_specifier, bits_per_sample, block_align, fact_samples;
+ time_t write_date;
+ mus_sample_t *maxamps;
+ off_t *maxtimes;
+} sound_file;
+
+static int sound_table_size = 0;
+static sound_file **sound_table = NULL;
+static sound_file *previous_sf = NULL; /* memoized search */
+static int previous_freed_sf = -1;
+
+static void free_sound_file(sound_file *sf)
+{
+ previous_sf = NULL;
+ if (sf)
+ {
+ sound_table[sf->table_pos] = NULL;
+ previous_freed_sf = sf->table_pos;
+ if (sf->aux_comment_start) FREE(sf->aux_comment_start);
+ if (sf->aux_comment_end) FREE(sf->aux_comment_end);
+ if (sf->file_name) FREE(sf->file_name);
+ if (sf->loop_modes) FREE(sf->loop_modes);
+ if (sf->loop_starts) FREE(sf->loop_starts);
+ if (sf->loop_ends) FREE(sf->loop_ends);
+ if (sf->marker_ids) FREE(sf->marker_ids);
+ if (sf->marker_positions) FREE(sf->marker_positions);
+ if (sf->maxamps) FREE(sf->maxamps);
+ if (sf->maxtimes) FREE(sf->maxtimes);
+ FREE(sf);
+ }
+}
+
+static sound_file *add_to_sound_table(const char *name)
+{
+ int i, pos;
+ pos = previous_freed_sf;
+ if (pos == -1)
+ {
+ for (i = 0; i < sound_table_size; i++)
+ if (sound_table[i] == NULL)
+ {
+ pos = i;
+ break;
+ }
+ if (pos == -1)
+ {
+ pos = sound_table_size;
+ sound_table_size += 16;
+ if (sound_table == NULL)
+ sound_table = (sound_file **)CALLOC(sound_table_size, sizeof(sound_file *));
+ else
+ {
+ sound_table = (sound_file **)REALLOC(sound_table, sound_table_size * sizeof(sound_file *));
+ for (i = pos; i < sound_table_size; i++) sound_table[i] = NULL;
+ }
+ }
+ }
+ else previous_freed_sf = -1;
+ sound_table[pos] = (sound_file *)CALLOC(1, sizeof(sound_file));
+ sound_table[pos]->table_pos = pos;
+ sound_table[pos]->file_name = (char *)CALLOC(strlen(name) + 1, sizeof(char));
+ strcpy(sound_table[pos]->file_name, name);
+ return(sound_table[pos]);
+}
+
+int mus_sound_prune(void)
+{
+ int i, pruned = 0;
+ for (i = 0; i < sound_table_size; i++)
+ if ((sound_table[i]) &&
+ (!(mus_file_probe(sound_table[i]->file_name))))
+ {
+ free_sound_file(sound_table[i]);
+ sound_table[i] = NULL;
+ pruned++;
+ }
+ return(pruned);
+}
+
+int mus_sound_forget(const char *name)
+{
+ int i, len;
+ bool free_name = false;
+ char *short_name = NULL;
+ if (name == NULL) return(MUS_ERROR);
+ if (name[0] == '/')
+ {
+ len = strlen(name);
+ for (i = 0; i < len; i++)
+ if (name[i] == '/')
+ short_name = (char *)(name + i + 1);
+ }
+ else
+ {
+ short_name = mus_expand_filename(name);
+ free_name = true;
+ }
+ previous_sf = NULL;
+ if (name)
+ for (i = 0; i < sound_table_size; i++)
+ if ((sound_table[i]) &&
+ ((strcmp(name, sound_table[i]->file_name) == 0) ||
+ ((short_name) &&
+ (strcmp(short_name, sound_table[i]->file_name) == 0))))
+ {
+ free_sound_file(sound_table[i]);
+ sound_table[i] = NULL;
+ }
+ if (free_name) FREE(short_name);
+ return(MUS_NO_ERROR);
+}
+
+static sound_file *check_write_date(const char *name, sound_file *sf)
+{
+ if (sf)
+ {
+ time_t date;
+ date = local_file_write_date(name);
+ if (date == sf->write_date)
+ return(sf);
+ else
+ {
+ if ((sf->header_type == MUS_RAW) && (mus_header_no_header(name)))
+ {
+ int chan;
+ off_t data_size;
+ /* sound has changed since we last read it, but it has no header, so
+ * the only sensible thing to check is the new length (i.e. caller
+ * has set other fields by hand)
+ */
+ sf->write_date = date;
+ chan = mus_file_open_read(name);
+ data_size = lseek(chan, 0L, SEEK_END);
+ sf->true_file_length = data_size;
+ sf->samples = mus_bytes_to_samples(sf->data_format, data_size);
+ CLOSE(chan, name);
+ return(sf);
+ }
+ /* otherwise our data base is out-of-date, so clear it out */
+ free_sound_file(sf);
+ }
+ }
+ return(NULL);
+}
+
+static sound_file *find_sound_file(const char *name)
+{
+ int i;
+ /* perhaps we already have the needed data... (90% hit rate here) */
+ if ((previous_sf) &&
+ (strcmp(previous_sf->file_name, name) == 0) &&
+ (previous_sf->write_date == local_file_write_date(name)))
+ return(previous_sf);
+ if (name)
+ for (i = 0; i < sound_table_size; i++)
+ if ((sound_table[i]) &&
+ (strcmp(name, sound_table[i]->file_name) == 0))
+ {
+ previous_sf = check_write_date(name, sound_table[i]);
+ return(previous_sf);
+ }
+ return(NULL);
+}
+
+static void display_sound_file_entry(FILE *fp, const char *name, sound_file *sf)
+{
+ int i, lim;
+ time_t date;
+ char timestr[64];
+ char *comment;
+ date = sf->write_date;
+ if (date != 0)
+ {
+#if HAVE_STRFTIME
+ strftime(timestr, 64, "%a %d-%b-%Y %H:%M:%S", localtime(&date));
+#else
+ sprintf(timestr, "%d", (int)date);
+#endif
+ }
+ else sprintf(timestr, "(date cleared)");
+ fprintf(fp, " %s: %s, chans: %d, srate: %d, type: %s, format: %s, samps: " OFF_TD,
+ name,
+ timestr,
+ sf->chans,
+ sf->srate,
+ mus_header_type_name(sf->header_type),
+ mus_data_format_name(sf->data_format),
+ sf->samples);
+ if (sf->loop_modes)
+ {
+ if (sf->loop_modes[0] != 0)
+ fprintf(fp, ", loop mode %d: %d to %d", sf->loop_modes[0], sf->loop_starts[0], sf->loop_ends[0]);
+ if (sf->loop_modes[1] != 0)
+ fprintf(fp, ", loop mode %d: %d to %d, ", sf->loop_modes[1], sf->loop_starts[1], sf->loop_ends[1]);
+ fprintf(fp, ", base: %d, detune: %d", sf->base_note, sf->base_detune);
+ }
+ if (sf->maxamps)
+ {
+ lim = sf->chans;
+ if (lim > 0)
+ {
+ if (lim > 64)
+ lim = 64;
+ for (i = 0; i < lim; i++)
+ {
+ if (i > 1) fprintf(fp, ", ");
+ fprintf(fp, " %.3f at %.3f ",
+ MUS_SAMPLE_TO_FLOAT(sf->maxamps[i]),
+ (sf->srate > 0.0) ? (float)((double)(sf->maxtimes[i]) / (double)(sf->srate)) : (float)(sf->maxtimes[i]));
+ }
+ }
+ }
+ if (mus_file_probe(name))
+ {
+ comment = mus_sound_comment(name);
+ if (comment)
+ {
+ fprintf(fp, "\n comment: %s", comment);
+ FREE(comment);
+ }
+ }
+ else fprintf(fp, " [defunct]");
+ fprintf(fp, "\n");
+}
+
+void mus_sound_report_cache(FILE *fp)
+{
+ sound_file *sf;
+ int entries = 0;
+ int i;
+ fprintf(fp, "sound table:\n");
+ for (i = 0; i < sound_table_size; i++)
+ {
+ sf = sound_table[i];
+ if (sf)
+ {
+ display_sound_file_entry(fp, sf->file_name, sf);
+ entries++;
+ }
+ }
+ fprintf(fp, "\nentries: %d\n", entries);
+ fflush(fp);
+}
+
+static sound_file *fill_sf_record(const char *name, sound_file *sf)
+{
+ int i;
+ sf->data_location = mus_header_data_location();
+ sf->samples = mus_header_samples();
+ sf->data_format = mus_header_format();
+ sf->srate = mus_header_srate();
+ /* if (sf->srate < 0) sf->srate = 0; */
+ sf->chans = mus_header_chans();
+ /* if (sf->chans < 0) sf->chans = 0; */
+ sf->datum_size = mus_bytes_per_sample(sf->data_format);
+ sf->header_type = mus_header_type();
+ sf->original_sound_format = mus_header_original_format();
+ sf->true_file_length = mus_header_true_length();
+ sf->comment_start = mus_header_comment_start();
+ sf->comment_end = mus_header_comment_end();
+ if (((sf->header_type == MUS_AIFC) ||
+ (sf->header_type == MUS_AIFF) ||
+ (sf->header_type == MUS_RIFF)) &&
+ (mus_header_aux_comment_start(0) != 0))
+
+ {
+ sf->aux_comment_start = (off_t *)CALLOC(4, sizeof(off_t));
+ sf->aux_comment_end = (off_t *)CALLOC(4, sizeof(off_t));
+ for (i = 0; i < 4; i++)
+ {
+ sf->aux_comment_start[i] = mus_header_aux_comment_start(i);
+ sf->aux_comment_end[i] = mus_header_aux_comment_end(i);
+ }
+ }
+ sf->type_specifier = mus_header_type_specifier();
+ sf->bits_per_sample = mus_header_bits_per_sample();
+ sf->fact_samples = mus_header_fact_samples();
+ sf->block_align = mus_header_block_align();
+ sf->write_date = local_file_write_date(name);
+ if (mus_header_loop_mode(0) > 0)
+ {
+ sf->loop_modes = (int *)CALLOC(2, sizeof(int));
+ sf->loop_starts = (int *)CALLOC(2, sizeof(int));
+ sf->loop_ends = (int *)CALLOC(2, sizeof(int));
+ for (i = 0; i < 2; i++)
+ {
+ sf->loop_modes[i] = mus_header_loop_mode(i);
+ if ((sf->header_type == MUS_AIFF) ||
+ (sf->header_type == MUS_AIFC))
+ {
+ sf->loop_starts[i] = mus_header_mark_position(mus_header_loop_start(i));
+ sf->loop_ends[i] = mus_header_mark_position(mus_header_loop_end(i));
+ }
+ else
+ {
+ sf->loop_starts[i] = mus_header_loop_start(i);
+ sf->loop_ends[i] = mus_header_loop_end(i);
+ }
+ }
+ sf->base_detune = mus_header_base_detune();
+ sf->base_note = mus_header_base_note();
+ }
+ previous_sf = sf;
+ return(sf);
+}
+
+static sound_file *read_sound_file_header(const char *name)
+{
+ mus_sound_initialize();
+ if (mus_header_read(name) != MUS_ERROR)
+ return(fill_sf_record(name, add_to_sound_table(name)));
+ return(NULL);
+}
+
+static sound_file *getsf(const char *arg)
+{
+ sound_file *sf = NULL;
+ if (arg == NULL) return(NULL);
+ sf = find_sound_file(arg);
+ if (sf) return(sf);
+ return(read_sound_file_header(arg));
+}
+
+#define MUS_SF(Filename, Expression) \
+ sound_file *sf; \
+ sf = getsf(Filename); \
+ if (sf) return(Expression); \
+ return(MUS_ERROR)
+
+off_t mus_sound_samples(const char *arg) {MUS_SF(arg, sf->samples);}
+off_t mus_sound_frames(const char *arg) {MUS_SF(arg, (sf->chans > 0) ? (sf->samples / sf->chans) : 0);}
+int mus_sound_datum_size(const char *arg) {MUS_SF(arg, sf->datum_size);}
+off_t mus_sound_data_location(const char *arg) {MUS_SF(arg, sf->data_location);}
+int mus_sound_chans(const char *arg) {MUS_SF(arg, sf->chans);}
+int mus_sound_srate(const char *arg) {MUS_SF(arg, sf->srate);}
+int mus_sound_header_type(const char *arg) {MUS_SF(arg, sf->header_type);}
+int mus_sound_data_format(const char *arg) {MUS_SF(arg, sf->data_format);}
+int mus_sound_original_format(const char *arg) {MUS_SF(arg, sf->original_sound_format);}
+off_t mus_sound_comment_start(const char *arg) {MUS_SF(arg, sf->comment_start);}
+off_t mus_sound_comment_end(const char *arg) {MUS_SF(arg, sf->comment_end);}
+off_t mus_sound_length(const char *arg) {MUS_SF(arg, sf->true_file_length);}
+int mus_sound_fact_samples(const char *arg) {MUS_SF(arg, sf->fact_samples);}
+time_t mus_sound_write_date(const char *arg) {MUS_SF(arg, sf->write_date);}
+int mus_sound_type_specifier(const char *arg) {MUS_SF(arg, sf->type_specifier);}
+int mus_sound_block_align(const char *arg) {MUS_SF(arg, sf->block_align);}
+int mus_sound_bits_per_sample(const char *arg) {MUS_SF(arg, sf->bits_per_sample);}
+
+float mus_sound_duration(const char *arg)
+{
+ float val = -1.0;
+ sound_file *sf;
+ sf = getsf(arg);
+ if (sf)
+ {
+ if ((sf->chans > 0) && (sf->srate > 0))
+ val = (float)((double)(sf->samples) / ((float)(sf->chans) * (float)(sf->srate)));
+ else val = 0.0;
+ }
+ return(val);
+}
+
+int *mus_sound_loop_info(const char *arg)
+{
+ sound_file *sf;
+ int *info;
+ sf = getsf(arg);
+ if ((sf) && (sf->loop_modes))
+ {
+ info = (int *)CALLOC(MUS_LOOP_INFO_SIZE, sizeof(int));
+ if (sf->loop_modes[0] != 0)
+ {
+ info[0] = sf->loop_starts[0];
+ info[1] = sf->loop_ends[0];
+ info[6] = sf->loop_modes[0];
+ }
+ if (sf->loop_modes[1] != 0)
+ {
+ info[2] = sf->loop_starts[1];
+ info[3] = sf->loop_ends[1];
+ info[7] = sf->loop_modes[1];
+ }
+ info[4] = sf->base_note;
+ info[5] = sf->base_detune;
+ return(info);
+ }
+ else return(NULL);
+}
+
+void mus_sound_set_loop_info(const char *arg, int *loop)
+{
+ sound_file *sf;
+ sf = getsf(arg);
+ if (sf)
+ {
+ if (sf->loop_modes == NULL)
+ {
+ sf->loop_modes = (int *)CALLOC(2, sizeof(int));
+ sf->loop_starts = (int *)CALLOC(2, sizeof(int));
+ sf->loop_ends = (int *)CALLOC(2, sizeof(int));
+ }
+ sf->loop_modes[0] = loop[6];
+ if (loop[6] != 0)
+ {
+ sf->loop_starts[0] = loop[0];
+ sf->loop_ends[0] = loop[1];
+ }
+ else
+ {
+ sf->loop_starts[0] = 0;
+ sf->loop_ends[0] = 0;
+ }
+ sf->loop_modes[1] = loop[7];
+ if (loop[7] != 0)
+ {
+ sf->loop_starts[1] = loop[2];
+ sf->loop_ends[1] = loop[3];
+ }
+ else
+ {
+ sf->loop_starts[1] = 0;
+ sf->loop_ends[1] = 0;
+ }
+ sf->base_note = loop[4];
+ sf->base_detune = loop[5];
+ }
+}
+
+char *mus_sound_comment(const char *name)
+{
+ off_t start, end, len;
+ int fd, full_len; /* comment string lengths */
+ char *sc = NULL, *auxcom;
+ sound_file *sf = NULL;
+ sf = getsf(name);
+ if (sf == NULL) return(NULL);
+ start = mus_sound_comment_start(name);
+ end = mus_sound_comment_end(name);
+ if (end == 0)
+ {
+ if (sf->aux_comment_start)
+ {
+ if (mus_sound_header_type(name) == MUS_RIFF)
+ return(mus_header_riff_aux_comment(name,
+ sf->aux_comment_start,
+ sf->aux_comment_end));
+ if ((mus_sound_header_type(name) == MUS_AIFF) ||
+ (mus_sound_header_type(name) == MUS_AIFC))
+ return(mus_header_aiff_aux_comment(name,
+ sf->aux_comment_start,
+ sf->aux_comment_end));
+ }
+ return(NULL);
+ }
+ if (end > mus_sound_length(name)) return(NULL);
+ len = end - start + 1;
+ if (len > 0)
+ {
+ /* open and get the comment */
+ size_t bytes;
+ fd = mus_file_open_read(name);
+ if (fd == -1) return(NULL);
+ lseek(fd, start, SEEK_SET);
+ sc = (char *)CALLOC(len + 1, sizeof(char));
+ bytes = read(fd, sc, len);
+ CLOSE(fd, name);
+ if (((mus_sound_header_type(name) == MUS_AIFF) ||
+ (mus_sound_header_type(name) == MUS_AIFC)) &&
+ (sf->aux_comment_start) &&
+ (bytes != 0))
+ {
+ auxcom = mus_header_aiff_aux_comment(name,
+ sf->aux_comment_start,
+ sf->aux_comment_end);
+ if (auxcom)
+ {
+ full_len = strlen(auxcom) + strlen(sc) + 2;
+ sc = (char *)REALLOC(sc, full_len * sizeof(char));
+ strcat(sc, "\n");
+ strcat(sc, auxcom);
+ }
+ }
+ }
+ return(sc);
+}
+
+int mus_sound_open_input(const char *arg)
+{
+ int fd = -1;
+ if (!(mus_file_probe(arg)))
+ mus_error(MUS_CANT_OPEN_FILE, S_mus_sound_open_input " can't open %s: %s", arg, STRERROR(errno));
+ else
+ {
+ sound_file *sf = NULL;
+ mus_sound_initialize();
+ sf = find_sound_file(arg);
+ if (!sf)
+ sf = read_sound_file_header(arg);
+ if (sf)
+ {
+ fd = mus_file_open_read(arg);
+ mus_file_open_descriptors(fd, arg, sf->data_format, sf->datum_size, sf->data_location, sf->chans, sf->header_type);
+ lseek(fd, sf->data_location, SEEK_SET);
+ }
+ }
+ return(fd);
+}
+
+int mus_sound_open_output(const char *arg, int srate, int chans, int data_format, int header_type, const char *comment)
+{
+ int fd = MUS_ERROR, err, comlen = 0;
+ if (comment) comlen = strlen(comment);
+ mus_sound_initialize();
+ mus_sound_forget(arg);
+ err = mus_header_write(arg, header_type, srate, chans, 0, 0, data_format, comment, comlen);
+ if (err != MUS_ERROR)
+ {
+ fd = mus_file_open_write(arg);
+ if (fd != -1)
+ mus_file_open_descriptors(fd,
+ arg,
+ data_format,
+ mus_bytes_per_sample(data_format),
+ mus_header_data_location(),
+ chans,
+ header_type);
+ }
+ return(fd);
+}
+
+int mus_sound_reopen_output(const char *arg, int chans, int format, int type, off_t data_loc)
+{
+ int fd;
+ mus_sound_initialize();
+ fd = mus_file_reopen_write(arg);
+ if (fd != -1)
+ mus_file_open_descriptors(fd, arg, format, mus_bytes_per_sample(format), data_loc, chans, type);
+ return(fd);
+}
+
+int mus_sound_close_input(int fd)
+{
+ return(mus_file_close(fd)); /* this closes the clm file descriptors */
+}
+
+int mus_sound_close_output(int fd, off_t bytes_of_data)
+{
+ char *name;
+ name = mus_file_fd_name(fd);
+ if (name)
+ {
+ int err = MUS_ERROR, old_type;
+ char *fname;
+ fname = strdup(name); /* strdup defined, if necessary, in io.c */
+ old_type = mus_file_header_type(fd);
+ err = mus_file_close(fd); /* this frees the original fd->name, so we copied above */
+ /* fd is NULL now */
+ mus_sound_forget(fname);
+ mus_header_change_data_size(fname, old_type, bytes_of_data);
+ free(fname);
+ return(err);
+ }
+ return(MUS_ERROR);
+}
+
+typedef enum {SF_CHANS, SF_SRATE, SF_TYPE, SF_FORMAT, SF_LOCATION, SF_SIZE} sf_field_t;
+
+static int mus_sound_set_field(const char *arg, sf_field_t field, int val)
+{
+ sound_file *sf;
+ sf = getsf(arg);
+ if (sf)
+ {
+ switch (field)
+ {
+ case SF_CHANS: sf->chans = val; break;
+ case SF_SRATE: sf->srate = val; break;
+ case SF_TYPE: sf->header_type = val; break;
+ case SF_FORMAT: sf->data_format = val; sf->datum_size = mus_bytes_per_sample(val); break;
+ default: return(MUS_ERROR); break;
+ }
+ return(MUS_NO_ERROR);
+ }
+ return(MUS_ERROR);
+}
+
+static int mus_sound_set_off_t_field(const char *arg, sf_field_t field, off_t val)
+{
+ sound_file *sf;
+ sf = getsf(arg);
+ if (sf)
+ {
+ switch (field)
+ {
+ case SF_SIZE: sf->samples = val; break;
+ case SF_LOCATION: sf->data_location = val; break;
+ default: return(MUS_ERROR); break;
+ }
+ return(MUS_NO_ERROR);
+ }
+ return(MUS_ERROR);
+}
+
+int mus_sound_set_chans(const char *arg, int val) {return(mus_sound_set_field(arg, SF_CHANS, val));}
+int mus_sound_set_srate(const char *arg, int val) {return(mus_sound_set_field(arg, SF_SRATE, val));}
+int mus_sound_set_header_type(const char *arg, int val) {return(mus_sound_set_field(arg, SF_TYPE, val));}
+int mus_sound_set_data_format(const char *arg, int val) {return(mus_sound_set_field(arg, SF_FORMAT, val));}
+int mus_sound_set_data_location(const char *arg, off_t val) {return(mus_sound_set_off_t_field(arg, SF_LOCATION, val));}
+int mus_sound_set_samples(const char *arg, off_t val) {return(mus_sound_set_off_t_field(arg, SF_SIZE, val));}
+
+int mus_sound_override_header(const char *arg, int srate, int chans, int format, int type, off_t location, off_t size)
+{
+ sound_file *sf;
+ /* perhaps once a header has been over-ridden, we should not reset the relevant fields upon re-read? */
+ sf = getsf(arg);
+ if (sf)
+ {
+ if (location != -1) sf->data_location = location;
+ if (size != -1) sf->samples = size;
+ if (format != -1)
+ {
+ sf->data_format = format;
+ sf->datum_size = mus_bytes_per_sample(format);
+ }
+ if (srate != -1) sf->srate = srate;
+ if (chans != -1) sf->chans = chans;
+ if (type != -1) sf->header_type = type;
+ return(MUS_NO_ERROR);
+ }
+ return(MUS_ERROR);
+}
+
+bool mus_sound_maxamp_exists(const char *ifile)
+{
+ sound_file *sf;
+ bool val = false;
+ sf = getsf(ifile);
+ val = ((sf) && (sf->maxamps));
+ return(val);
+}
+
+off_t mus_sound_maxamps(const char *ifile, int chans, mus_sample_t *vals, off_t *times)
+{
+ int ifd, ichans, chn, j;
+ int i, bufnum;
+ off_t n, frames, curframes;
+ mus_sample_t abs_samp;
+ mus_sample_t *buffer, *samp;
+ off_t *time;
+ mus_sample_t **ibufs;
+ sound_file *sf;
+ sf = getsf(ifile);
+ if (sf->chans <= 0) return(MUS_ERROR);
+ if ((sf) && (sf->maxamps))
+ {
+ if (chans > sf->chans) ichans = sf->chans; else ichans = chans;
+ for (chn = 0; chn < ichans; chn++)
+ {
+ times[chn] = sf->maxtimes[chn];
+ vals[chn] = sf->maxamps[chn];
+ }
+ frames = sf->samples / sf->chans;
+ return(frames);
+ }
+ ifd = mus_sound_open_input(ifile);
+ if (ifd == MUS_ERROR) return(MUS_ERROR);
+ ichans = mus_sound_chans(ifile);
+ frames = mus_sound_frames(ifile);
+ if (frames == 0)
+ {
+ mus_sound_close_input(ifd);
+ return(0);
+ }
+ mus_file_seek_frame(ifd, 0);
+ ibufs = (mus_sample_t **)CALLOC(ichans, sizeof(mus_sample_t *));
+ bufnum = 8192;
+ for (j = 0; j < ichans; j++)
+ ibufs[j] = (mus_sample_t *)CALLOC(bufnum, sizeof(mus_sample_t));
+ time = (off_t *)CALLOC(ichans, sizeof(off_t));
+ samp = (mus_sample_t *)CALLOC(ichans, sizeof(mus_sample_t));
+ for (n = 0; n < frames; n += bufnum)
+ {
+ if ((n + bufnum) < frames)
+ curframes = bufnum;
+ else curframes = (frames - n);
+ mus_file_read(ifd, 0, curframes - 1, ichans, ibufs);
+ for (chn = 0; chn < ichans; chn++)
+ {
+ buffer = (mus_sample_t *)(ibufs[chn]);
+ for (i = 0; i < curframes; i++)
+ {
+ abs_samp = mus_sample_abs(buffer[i]);
+ if (abs_samp > samp[chn])
+ {
+ time[chn] = i + n;
+ samp[chn] = abs_samp;
+ }
+ }
+ }
+ }
+ mus_sound_close_input(ifd);
+ mus_sound_set_maxamps(ifile, ichans, samp, time); /* save the complete set */
+ if (ichans > chans) ichans = chans;
+ for (chn = 0; chn < ichans; chn++)
+ {
+ times[chn] = time[chn];
+ vals[chn] = samp[chn];
+ }
+ FREE(time);
+ FREE(samp);
+ for (j = 0; j < ichans; j++) FREE(ibufs[j]);
+ FREE(ibufs);
+ return(frames);
+}
+
+int mus_sound_set_maxamps(const char *ifile, int chans, mus_sample_t *vals, off_t *times)
+{
+ sound_file *sf;
+ sf = getsf(ifile);
+ if (sf)
+ {
+ int i, ichans = 0;
+ if (sf->maxamps)
+ {
+ if (chans > sf->chans) ichans = sf->chans; else ichans = chans;
+ for (i = 0; i < ichans; i++)
+ {
+ sf->maxtimes[i] = times[i];
+ sf->maxamps[i] = vals[i];
+ }
+ }
+ else
+ {
+ ichans = mus_sound_chans(ifile);
+ if (sf->maxamps == NULL)
+ {
+ sf->maxamps = (mus_sample_t *)CALLOC(ichans, sizeof(mus_sample_t));
+ sf->maxtimes = (off_t *)CALLOC(ichans, sizeof(off_t));
+ }
+ if (ichans > chans) ichans = chans;
+ for (i = 0; i < ichans; i++)
+ {
+ sf->maxtimes[i] = times[i];
+ sf->maxamps[i] = vals[i];
+ }
+ }
+ return(MUS_NO_ERROR);
+ }
+ return(MUS_ERROR);
+}
+
+int mus_file_to_array(const char *filename, int chan, int start, int samples, mus_sample_t *array)
+{
+ int ifd, chans, total_read;
+ mus_sample_t **bufs;
+ ifd = mus_sound_open_input(filename);
+ if (ifd == MUS_ERROR) return(MUS_ERROR);
+ chans = mus_sound_chans(filename);
+ if (chan >= chans)
+ {
+ mus_sound_close_input(ifd);
+ return(mus_error(MUS_NO_SUCH_CHANNEL, "mus_file_to_array can't read %s channel %d (file has %d chans)", filename, chan, chans));
+ }
+ bufs = (mus_sample_t **)CALLOC(chans, sizeof(mus_sample_t *));
+ bufs[chan] = array;
+ mus_file_seek_frame(ifd, start);
+ total_read = mus_file_read_any(ifd, 0, chans, samples, bufs, bufs);
+ mus_sound_close_input(ifd);
+ FREE(bufs);
+ return(total_read);
+}
+
+char *mus_array_to_file_with_error(const char *filename, mus_sample_t *ddata, int len, int srate, int channels)
+{
+ /* put ddata into a sound file, taking byte order into account */
+ /* assume ddata is interleaved already if more than one channel */
+ int fd, err = MUS_NO_ERROR;
+ mus_sample_t *bufs[1];
+ mus_sound_forget(filename);
+ fd = mus_file_create(filename);
+ if (fd == -1)
+ return("mus_array_to_file can't create output file");
+ err = mus_file_open_descriptors(fd, filename,
+ MUS_OUT_FORMAT,
+ mus_bytes_per_sample(MUS_OUT_FORMAT),
+ 28, channels, MUS_NEXT);
+ if (err != MUS_ERROR)
+ {
+ err = mus_header_write_next_header(fd, srate, channels, 28, len /* out chans = 1?? */, MUS_OUT_FORMAT, NULL, 0);
+ if (err != MUS_ERROR)
+ {
+ bufs[0] = ddata;
+ err = mus_file_write(fd, 0, len - 1, 1, bufs); /* 1 = chans?? */
+ }
+ }
+ mus_file_close(fd);
+ if (err == MUS_ERROR)
+ return("mus_array_to_file write error");
+ return(NULL);
+}
+
+int mus_array_to_file(const char *filename, mus_sample_t *ddata, int len, int srate, int channels)
+{
+ char *errmsg;
+ errmsg = mus_array_to_file_with_error(filename, ddata, len, srate, channels);
+ if (errmsg)
+ return(mus_error(MUS_CANT_OPEN_FILE, errmsg));
+ return(MUS_NO_ERROR);
+}
+
+int mus_file_to_float_array(const char *filename, int chan, off_t start, int samples, Float *array)
+{
+#if SNDLIB_USE_FLOATS
+ return(mus_file_to_array(filename, chan, start, samples, array));
+#else
+ mus_sample_t *idata;
+ int i, len;
+ idata = (mus_sample_t *)CALLOC(samples, sizeof(mus_sample_t));
+ len = mus_file_to_array(filename, chan, start, samples, idata);
+ if (len != -1)
+ for (i = 0; i < samples; i++)
+ array[i] = MUS_SAMPLE_TO_FLOAT(idata[i]);
+ FREE(idata);
+ return(len);
+#endif
+}
+
+int mus_float_array_to_file(const char *filename, Float *ddata, int len, int srate, int channels)
+{
+ char *errmsg;
+#if SNDLIB_USE_FLOATS
+ errmsg = mus_array_to_file_with_error(filename, ddata, len, srate, channels);
+#else
+ mus_sample_t *idata;
+ int i;
+ idata = (mus_sample_t *)CALLOC(len, sizeof(mus_sample_t));
+ for (i = 0; i < len; i++)
+ idata[i] = MUS_FLOAT_TO_SAMPLE(ddata[i]);
+ errmsg = mus_array_to_file_with_error(filename, idata, len, srate, channels);
+ FREE(idata);
+#endif
+ if (errmsg)
+ return(mus_error(MUS_CANT_OPEN_FILE, errmsg));
+ return(MUS_NO_ERROR);
+}