diff options
Diffstat (limited to 'third_party/resample/sndlib-20/sound.c')
-rw-r--r-- | third_party/resample/sndlib-20/sound.c | 1072 |
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); +} |