/* sound.c */ #include #if USE_SND #include "snd.h" #endif #include #include #include #include #include #include #include #include #include #if (defined(HAVE_LIBC_H) && (!defined(HAVE_UNISTD_H))) #include #else #if (!(defined(_MSC_VER))) #include #endif #if HAVE_STRING_H #include #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); }