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_RF64) || (sf->header_type == MUS_RIFF)) && (mus_header_aux_comment_start(0) != 0)) { sf->aux_comment_start = (mus_long_t *)calloc(4, sizeof(mus_long_t)); sf->aux_comment_end = (mus_long_t *)calloc(4, sizeof(mus_long_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 ((sf->header_type == MUS_AIFF) || (sf->header_type == MUS_AIFC)) { int *marker_ids, *marker_positions; sf->markers = mus_header_mark_info(&marker_ids, &marker_positions); if (sf->markers > 0) { sf->marker_ids = (int *)malloc(sf->markers * sizeof(int)); sf->marker_positions = (int *)malloc(sf->markers * sizeof(int)); memcpy((void *)(sf->marker_ids), (void *)marker_ids, sizeof(int) * sf->markers); memcpy((void *)(sf->marker_positions), (void *)marker_positions, sizeof(int) * sf->markers); } } 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(); } return(sf); }
/* Create a soundfile header of the specified type. The usage msg above describes the kind of types you can create. (These are ones that sndlib can write and that can be useful to the average cmix user. There are other kinds of header sndlib can write -- for example, for 24-bit files -- but we want to keep the sfcreate syntax as simple as possible. We allocate a generous comment area, because expanding it after a sound has already been written would mean copying the entire file. (We use comments to store peak stats -- see sys/sndlibsupport.c.) */ int main(int argc, char *argv[]) { int i, fd, result, overwrite_file, old_format, old_header_type=0; int data_location, old_data_location, old_nsamps, old_datum_size; int force = FALSE; struct stat statbuf; /* get name of this program */ progname = strrchr(argv[0], '/'); if (progname == NULL) progname = argv[0]; else progname++; if (argc < 2) usage(); for (i = 1; i < argc; i++) { char *arg = argv[i]; if (arg[0] == '-') { switch (arg[1]) { case 'c': if (++i >= argc) usage(); nchans = atoi(argv[i]); break; case 'r': if (++i >= argc) usage(); srate = atoi(argv[i]); break; case 'i': is_short = TRUE; break; case 'f': is_short = FALSE; break; case 'b': endian = CREATE_BIG_ENDIAN; break; case 'l': endian = CREATE_LITTLE_ENDIAN; break; case 't': if (++i >= argc) usage(); strncpy(format_name, argv[i], FORMAT_NAME_LENGTH - 1); format_name[FORMAT_NAME_LENGTH - 1] = 0; /* ensure termination */ break; case '-': if (strcmp(arg, "--force") == 0) force = TRUE; else usage(); break; default: usage(); } } else sfname = arg; } if (sfname == NULL) usage(); if (check_params()) usage(); old_data_location = old_format = old_datum_size = old_nsamps = 0; /* Test for existing file. If there is one, and we can read and write it, and the force flag is set, then we'll overwrite its header. Note that we slap on a header even if the file doesn't have one already, as would be the case with a raw sound file (or a precious text file...). If there isn't an existing file, we'll create a new one. */ overwrite_file = FALSE; result = stat(sfname, &statbuf); if (result == -1) { if (errno == ENOENT) { /* file doesn't exist, we'll create one */ fd = open(sfname, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd == -1) { fprintf(stderr, "Error creating file \"%s\" (%s)\n", sfname, strerror(errno)); exit(1); } } else { fprintf(stderr, "File \"%s\" exists, but there was an error accessing it (%s)\n", sfname, strerror(errno)); exit(1); } } else { int drastic_change; overwrite_file = TRUE; /* File exists and we could stat it. If it's a regular file, open it and see if it looks like a sound file. */ if (!S_ISREG(statbuf.st_mode)) { fprintf(stderr, "\"%s\" exists, but it's not a regular file.\n", sfname); exit(1); } fd = open(sfname, O_RDWR); if (fd == -1) { fprintf(stderr, "Error opening file (%s)\n", strerror(errno)); exit(1); } if (sndlib_read_header(fd) == -1) { fprintf(stderr, "Error reading header (%s)\n", strerror(errno)); exit(1); } old_header_type = mus_header_type(); old_format = mus_header_format(); if (NOT_A_SOUND_FILE(old_header_type) || INVALID_DATA_FORMAT(old_format)) { fprintf(stderr, "\nWARNING: \"%s\" exists, but doesn't look like " "a sound file.\n\n", sfname); drastic_change = TRUE; old_header_type = MUS_RAW; } else if (old_header_type != header_type || old_format != data_format) drastic_change = TRUE; else drastic_change = FALSE; if (drastic_change && !force) { fprintf(stderr, "%s\n", OVERWRITE_WARNING); exit(1); } old_data_location = mus_header_data_location(); old_nsamps = mus_header_samples(); /* samples, not frames */ old_datum_size = mus_header_data_format_to_bytes_per_sample(); } result = sndlib_write_header(fd, 0, header_type, data_format, srate, nchans, NULL, &data_location); if (result == -1) { fprintf(stderr, "Error writing header (%s)\n", strerror(errno)); exit(1); } /* If we're overwriting a header, we have to fiddle around a bit to get the correct data_size into the header. (These will not likely be the same if we're changing header types.) */ if (overwrite_file) { int loc_byte_diff, datum_size, sound_bytes; /* need to do this again */ if (sndlib_read_header(fd) == -1) { fprintf(stderr, "Error re-reading header (%s)\n", strerror(errno)); exit(1); } if (data_format != old_format && old_header_type != MUS_RAW) if (! FORMATS_SAME_BYTE_ORDER(data_format, old_format)) printf("WARNING: Byte order changed!\n"); datum_size = mus_header_data_format_to_bytes_per_sample(); loc_byte_diff = data_location - old_data_location; /* If the data locations have changed, we're effectively adding or subtracting sound data bytes. Depending on the number of bytes added, this could result in swapped channels or worse. We can't do anything about this, because our scheme for encoding peak stats in the header comment requires a fixed comment allocation in the header. (Otherwise, we could shrink or expand this to make things right.) The best we can do is warn the user. */ if (loc_byte_diff) { if (loc_byte_diff > 0) printf("Losing %d bytes of sound data\n", loc_byte_diff); else if (loc_byte_diff < 0) printf("Gaining %d bytes of sound data\n", -loc_byte_diff); if (loc_byte_diff % datum_size) printf("%s\n", WORD_INTEGRITY_WARNING); else if (loc_byte_diff % (nchans * datum_size)) printf("%s\n", CHANNEL_SWAP_WARNING); /* else got lucky: no shifted words or swapping */ sound_bytes = (old_nsamps * old_datum_size) - loc_byte_diff; } else /* same number of bytes as before */ sound_bytes = old_nsamps * old_datum_size; result = sndlib_set_header_data_size(fd, header_type, sound_bytes); if (result == -1) { fprintf(stderr, "Error updating header\n"); exit(1); } close(fd); } else close(fd); return 0; }
/* ------------------------------------------------------ open_sound_file --- */ int open_sound_file( const char *funcname, // for error messages const char *sfname, // name of sound file to open int *header_type, // Remaining args are pointers to storage for int *data_format, // various bits of file header info. If int *data_location, // pointer is NULL, it will be ignored. double *srate, // Info is undefined on error return (-1). int *nchans, long *nsamps) { // See if file exists and is a regular file or link. struct stat sfst; if (stat(sfname, &sfst) == -1) { rterror(funcname, "\"%s\": %s", sfname, strerror(errno)); return -1; } if (!S_ISREG(sfst.st_mode) && !S_ISLNK(sfst.st_mode)) { rterror(funcname, "\"%s\" is not a regular file or a link.\n", sfname); return -1; } // Open the file and read its header. int fd = sndlib_open_read(sfname); if (fd == -1) { rterror(funcname, "Can't read header from \"%s\" (%s)\n", sfname, strerror(errno)); return -1; } // Now info is available from sndlib query functions. int type = mus_header_type(); if (NOT_A_SOUND_FILE(type)) { rterror(funcname, "\"%s\" is probably not a sound file\n", sfname); sndlib_close(fd, 0, 0, 0, 0); return -1; } int format = mus_header_format(); if (INVALID_DATA_FORMAT(format)) { rterror(funcname, "\"%s\" has invalid sound data format\n", sfname); sndlib_close(fd, 0, 0, 0, 0); return -1; } if (!SUPPORTED_DATA_FORMAT(format)) { rterror(funcname, "Can't open \"%s\": can read only 16-bit integer, " "24-bit integer and 32-bit float files.", sfname); sndlib_close(fd, 0, 0, 0, 0); return -1; } if (header_type) *header_type = type; if (data_format) *data_format = format; if (data_location) *data_location = mus_header_data_location(); if (srate) *srate = (double) mus_header_srate(); if (nchans) *nchans = mus_header_chans(); if (nsamps) *nsamps = mus_header_samples(); return fd; }