static Xen g_mus_sound_preload(Xen file) { #define H_mus_sound_preload "(" S_mus_sound_preload " filename): save filename's data in memory (faster opens and so on)." char *str; Xen_check_type(Xen_is_string(file), file, 1, S_mus_sound_preload, "a string"); str = mus_expand_filename(Xen_string_to_C_string(file)); if (str) { int ifd; ifd = mus_sound_open_input(str); if (ifd != MUS_ERROR) { int i, chans; mus_float_t **bufs; mus_long_t framples; chans = mus_sound_chans(str); framples = mus_sound_framples(str) + 8; /* + 8 for readers than wander off the end */ bufs = (mus_float_t **)malloc(chans * sizeof(mus_float_t *)); for (i = 0; i < chans; i++) bufs[i] = (mus_float_t *)malloc(framples * sizeof(mus_float_t)); mus_file_seek_frample(ifd, 0); mus_file_read_file(ifd, 0, chans, framples, bufs); mus_sound_set_saved_data(str, bufs); mus_sound_close_input(ifd); } free(str); } return(file); }
mus_long_t mus_file_to_array(const char *filename, int chan, mus_long_t start, mus_long_t samples, mus_float_t *array) { int ifd, chans; mus_long_t total_read; mus_float_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_float_t **)calloc(chans, sizeof(mus_float_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); }
static Xen g_file_to_array(Xen filename, Xen chan, Xen start, Xen samples, Xen data) { #define H_file_to_array "(" S_file_to_array " filename chan start samples data): read the sound file \ 'filename' placing samples from channel 'chan' into the " S_vct " 'data' starting in the file \ at frample 'start' and reading 'samples' samples altogether." int chn, chans; mus_long_t samps; vct *v; const char *name = NULL; Xen_check_type(Xen_is_string(filename), filename, 1, S_file_to_array, "a string"); Xen_check_type(Xen_is_integer(chan), chan, 2, S_file_to_array, "an integer"); Xen_check_type(Xen_is_llong(start), start, 3, S_file_to_array, "an integer"); Xen_check_type(Xen_is_llong(samples), samples, 4, S_file_to_array, "an integer"); Xen_check_type((mus_is_vct(data)), data, 5, S_file_to_array, "a " S_vct); name = Xen_string_to_C_string(filename); if (!(mus_file_probe(name))) Xen_error(NO_SUCH_FILE, Xen_list_3(C_string_to_Xen_string(S_file_to_array ": ~S ~A"), filename, C_string_to_Xen_string(STRERROR(errno)))); v = Xen_to_vct(data); samps = Xen_llong_to_C_llong(samples); if (samps <= 0) Xen_out_of_range_error(S_file_to_array, 4, samples, "samples <= 0?"); if (samps > mus_vct_length(v)) samps = mus_vct_length(v); chn = Xen_integer_to_C_int(chan); chans = mus_sound_chans(name); if ((chn < 0) || (chn > chans)) Xen_error(NO_SUCH_CHANNEL, Xen_list_4(C_string_to_Xen_string(S_file_to_array ": invalid chan: ~A, ~S has ~A chans"), chan, filename, C_int_to_Xen_integer(chans))); if (chans <= 0) Xen_error(BAD_HEADER, Xen_list_2(C_string_to_Xen_string(S_file_to_array ": ~S chans <= 0"), filename)); mus_file_to_float_array(name, chn, Xen_llong_to_C_llong(start), samps, mus_vct_data(v)); return(data); }
static Xen g_mus_sound_set_maxamp(Xen file, Xen vals) { int chans; char *filename; Xen_check_type(Xen_is_string(file), file, 1, S_set S_mus_sound_maxamp, "a string"); Xen_check_type(Xen_is_list(vals), vals, 2, S_set S_mus_sound_maxamp, "a list"); filename = mus_expand_filename(Xen_string_to_C_string(file)); chans = mus_sound_chans(filename); /* presumably any actual error here will be trapped via mus-error (raised in mus_header_read via read_sound_file_header), * so chans <= 0 is actually in the header? */ if (chans > 0) { Xen lst; int i, j, len; mus_float_t *mvals; mus_long_t *times; len = Xen_list_length(vals); if (len < (chans * 2)) Xen_wrong_type_arg_error(S_set S_mus_sound_maxamp, 2, vals, "max amp list length must = 2 * chans"); if (len > chans * 2) len = chans * 2; mvals = (mus_float_t *)calloc(chans, sizeof(mus_float_t)); times = (mus_long_t *)calloc(chans, sizeof(mus_long_t)); for (i = 0, j = 0, lst = Xen_copy_arg(vals); i < len; i += 2, j++, lst = Xen_cddr(lst)) { times[j] = Xen_llong_to_C_llong(Xen_car(lst)); mvals[j] = Xen_real_to_C_double(Xen_cadr(lst)); } fprintf(stderr, "set in g_mus_sound_set_maxamp\n"); mus_sound_set_maxamps(filename, chans, mvals, times); free(mvals); free(times); if (filename) free(filename); } else { if (filename) free(filename); Xen_error(BAD_HEADER, Xen_list_1(C_string_to_Xen_string(S_set S_mus_sound_maxamp ": chans <= 0"))); } return(vals); }
Xen g_mus_sound_maxamp(Xen file) { #define H_mus_sound_maxamp "(" S_mus_sound_maxamp " filename): maxamps in sound (a list of paired amps (floats) and locations (samples))" int chans; char *filename; Xen res = Xen_empty_list; Xen_check_type(Xen_is_string(file), file, 1, S_mus_sound_maxamp, "a string"); filename = mus_expand_filename(Xen_string_to_C_string(file)); chans = mus_sound_chans(filename); if (chans > 0) { mus_long_t rtn; mus_float_t *vals; mus_long_t *times; vals = (mus_float_t *)calloc(chans, sizeof(mus_float_t)); times = (mus_long_t *)calloc(chans, sizeof(mus_long_t)); rtn = mus_sound_maxamps(filename, chans, vals, times); if (rtn != MUS_ERROR) { int i; for (i = chans - 1; i >= 0; i--) res = Xen_cons(C_llong_to_Xen_llong(times[i]), Xen_cons(C_double_to_Xen_real(vals[i]), res)); } free(vals); free(times); if (filename) free(filename); } else { if (filename) free(filename); Xen_error(BAD_HEADER, Xen_list_1(C_string_to_Xen_string(S_mus_sound_maxamp ": chans <= 0"))); } return(res); }
/* ATS_SOUND *tracker (ANARGS *anargs, char *soundfile) * partial tracking function * anargs: pointer to analysis parameters * soundfile: path to input file * returns an ATS_SOUND with data issued from analysis */ ATS_SOUND *tracker (ANARGS *anargs, char *soundfile, char *resfile) { int fd, M_2, first_point, filptr, n_partials = 0; int frame_n, k, sflen, *win_samps, peaks_size, tracks_size = 0; int i, frame, i_tmp; float *window, norm, sfdur, f_tmp; /* declare structures and buffers */ ATS_SOUND *sound = NULL; ATS_PEAK *peaks, *tracks = NULL, cpy_peak; ATS_FRAME *ana_frames = NULL, *unmatched_peaks = NULL; mus_sample_t **bufs; ATS_FFT fft; #ifdef FFTW fftw_plan plan; FILE *fftw_wisdom_file; #endif /* open input file we get srate and total_samps in file in anargs */ if ((fd = mus_sound_open_input(soundfile))== -1) { fprintf(stderr, "%s: %s\n", soundfile, strerror(errno)); return(NULL); } /* warn about multi-channel sound files */ if (mus_sound_chans(soundfile) > 1) { fprintf(stderr, "Error: file has %d channels, must be mono!\n", mus_sound_chans(soundfile)); return(NULL); } fprintf(stderr, "tracking...\n"); /* get sample rate and # of frames from file header */ anargs->srate = mus_sound_srate(soundfile); sflen = mus_sound_frames(soundfile); sfdur = (float)sflen/anargs->srate; /* check analysis parameters */ /* check start time */ if( !(anargs->start >= 0.0 && anargs->start < sfdur) ){ fprintf(stderr, "Warning: start %f out of bounds, corrected to 0.0\n", anargs->start); anargs->start = (float)0.0; } /* check duration */ if(anargs->duration == ATSA_DUR) { anargs->duration = sfdur - anargs->start; } f_tmp = anargs->duration + anargs->start; if( !(anargs->duration > 0.0 && f_tmp <= sfdur) ){ fprintf(stderr, "Warning: duration %f out of bounds, limited to file duration\n", anargs->duration); anargs->duration = sfdur - anargs->start; } /* print time bounds */ fprintf(stderr, "start: %f duration: %f file dur: %f\n", anargs->start, anargs->duration , sfdur); /* check lowest frequency */ if( !(anargs->lowest_freq > 0.0 && anargs->lowest_freq < anargs->highest_freq)){ fprintf(stderr, "Warning: lowest freq. %f out of bounds, forced to default: %f\n", anargs->lowest_freq, ATSA_LFREQ); anargs->lowest_freq = ATSA_LFREQ; } /* check highest frequency */ if( !(anargs->highest_freq > anargs->lowest_freq && anargs->highest_freq <= anargs->srate * 0.5 )){ fprintf(stderr, "Warning: highest freq. %f out of bounds, forced to default: %f\n", anargs->highest_freq, ATSA_HFREQ); anargs->highest_freq = ATSA_HFREQ; } /* frequency deviation */ if( !(anargs->freq_dev > 0.0 && anargs->freq_dev < 1.0) ){ fprintf(stderr, "Warning: freq. dev. %f out of bounds, should be > 0.0 and <= 1.0, forced to default: %f\n", anargs->freq_dev, ATSA_FREQDEV); anargs->freq_dev = ATSA_FREQDEV; } /* window cycles */ if( !(anargs->win_cycles >= 1 && anargs->win_cycles <= 8) ){ fprintf(stderr, "Warning: windows cycles %d out of bounds, should be between 1 and 8, forced to default: %d\n", anargs->win_cycles, ATSA_WCYCLES); anargs->win_cycles = ATSA_WCYCLES; } /* window type */ if( !(anargs->win_type >= 0 && anargs->win_type <= 3) ){ fprintf(stderr, "Warning: window type %d out of bounds, should be between 0 and 3, forced to default: %d\n", anargs->win_type, ATSA_WTYPE); anargs->win_type = ATSA_WTYPE; } /* hop size */ if( !(anargs->hop_size > 0.0 && anargs->hop_size <= 1.0) ){ fprintf(stderr, "Warning: hop size %f out of bounds, should be > 0.0 and <= 1.0, forced to default: %f\n", anargs->hop_size, ATSA_HSIZE); anargs->hop_size = ATSA_HSIZE; } /* lowest mag */ if( !(anargs->lowest_mag <= 0.0) ){ fprintf(stderr, "Warning: lowest magnitude %f out of bounds, should be >= 0.0 and <= 1.0, forced to default: %f\n", anargs->lowest_mag, ATSA_LMAG); anargs->lowest_mag = ATSA_LMAG; } /* set some values before checking next set of parameters */ anargs->first_smp = (int)floor(anargs->start * (float)anargs->srate); anargs->total_samps = (int)floor(anargs->duration * (float)anargs->srate); /* fundamental cycles */ anargs->cycle_smp = (int)floor((double)anargs->win_cycles * (double)anargs->srate / (double)anargs->lowest_freq); /* window size */ anargs->win_size = (anargs->cycle_smp % 2 == 0) ? anargs->cycle_smp+1 : anargs->cycle_smp; /* calculate hop samples */ anargs->hop_smp = floor( (float)anargs->win_size * anargs->hop_size ); /* compute total number of frames */ anargs->frames = compute_frames(anargs); /* check that we have enough frames for the analysis */ if( !(anargs->frames >= ATSA_MFRAMES) ){ fprintf(stderr, "Error: %d frames are not enough for analysis, nead at least %d\n", anargs->frames , ATSA_MFRAMES); return(NULL); } /* check other user parameters */ /* track length */ if( !(anargs->track_len >= 1 && anargs->track_len < anargs->frames) ){ i_tmp = (ATSA_TRKLEN < anargs->frames) ? ATSA_TRKLEN : anargs->frames-1; fprintf(stderr, "Warning: track length %d out of bounds, forced to: %d\n", anargs->track_len , i_tmp); anargs->track_len = i_tmp; } /* min. segment length */ if( !(anargs->min_seg_len >= 1 && anargs->min_seg_len < anargs->frames) ){ i_tmp = (ATSA_MSEGLEN < anargs->frames) ? ATSA_MSEGLEN : anargs->frames-1; fprintf(stderr, "Warning: min. segment length %d out of bounds, forced to: %d\n", anargs->min_seg_len, i_tmp); anargs->min_seg_len = i_tmp; } /* min. gap length */ if( !(anargs->min_gap_len >= 0 && anargs->min_gap_len < anargs->frames) ){ i_tmp = (ATSA_MGAPLEN < anargs->frames) ? ATSA_MGAPLEN : anargs->frames-1; fprintf(stderr, "Warning: min. gap length %d out of bounds, forced to: %d\n", anargs->min_gap_len, i_tmp); anargs->min_gap_len = i_tmp; } /* SMR threshold */ if( !(anargs->SMR_thres >= 0.0 && anargs->SMR_thres < ATSA_MAX_DB_SPL) ){ fprintf(stderr, "Warning: SMR threshold %f out of bounds, shoul be >= 0.0 and < %f dB SPL, forced to default: %f\n", anargs->SMR_thres, ATSA_MAX_DB_SPL, ATSA_SMRTHRES); anargs->SMR_thres = ATSA_SMRTHRES; } /* min. seg. SMR */ if( !(anargs->min_seg_SMR >= anargs->SMR_thres && anargs->min_seg_SMR < ATSA_MAX_DB_SPL) ){ fprintf(stderr, "Warning: min. seg. SMR %f out of bounds, shoul be >= %f and < %f dB SPL, forced to default: %f\n", anargs->min_seg_SMR, anargs->SMR_thres, ATSA_MAX_DB_SPL, ATSA_MSEGSMR); anargs->min_seg_SMR = ATSA_MSEGSMR; } /* last peak contibution */ if( !(anargs->last_peak_cont >= 0.0 && anargs->last_peak_cont <= 1.0) ){ fprintf(stderr, "Warning: last peak contibution %f out of bounds, should be >= 0.0 and <= 1.0, forced to default: %f\n", anargs->last_peak_cont, ATSA_LPKCONT); anargs->last_peak_cont = ATSA_LPKCONT; } /* SMR cont. */ if( !(anargs->SMR_cont >= 0.0 && anargs->SMR_cont <= 1.0) ){ fprintf(stderr, "Warning: SMR contibution %f out of bounds, should be >= 0.0 and <= 1.0, forced to default: %f\n", anargs->SMR_cont, ATSA_SMRCONT); anargs->SMR_cont = ATSA_SMRCONT; } /* continue computing parameters */ /* fft size */ anargs->fft_size = ppp2(2*anargs->win_size); /* allocate memory for sound, we read the whole sound in memory */ bufs = (mus_sample_t **)malloc(sizeof(mus_sample_t*)); bufs[0] = (mus_sample_t *)malloc(sflen * sizeof(mus_sample_t)); /* bufs = malloc(sizeof(mus_sample_t*)); bufs[0] = malloc(sflen * sizeof(mus_sample_t)); */ /* make our window */ window = make_window(anargs->win_type, anargs->win_size); /* get window norm */ norm = window_norm(window, anargs->win_size); /* fft mag for computing frequencies */ anargs->fft_mag = (double)anargs->srate / (double)anargs->fft_size; /* lowest fft bin for analysis */ anargs->lowest_bin = floor( anargs->lowest_freq / anargs->fft_mag ); /* highest fft bin for analisis */ anargs->highest_bin = floor( anargs->highest_freq / anargs->fft_mag ); /* allocate an array analysis frames in memory */ ana_frames = (ATS_FRAME *)malloc(anargs->frames * sizeof(ATS_FRAME)); /* alocate memory to store mid-point window sample numbers */ win_samps = (int *)malloc(anargs->frames * sizeof(int)); /* center point of window */ M_2 = floor((anargs->win_size - 1) / 2); /* first point in fft buffer to write */ first_point = anargs->fft_size - M_2; /* half a window from first sample */ filptr = anargs->first_smp - M_2; /* read sound into memory */ mus_sound_read(fd, 0, sflen-1, 1, bufs); /* make our fft-struct */ fft.size = anargs->fft_size; fft.rate = anargs->srate; #ifdef FFTW fft.data = fftw_malloc(sizeof(fftw_complex) * fft.size); if(fftw_import_system_wisdom()) fprintf(stderr, "system wisdom loaded!\n"); else fprintf(stderr, "cannot locate system wisdom!\n"); if((fftw_wisdom_file = fopen("ats-wisdom", "r")) != NULL) { fftw_import_wisdom_from_file(fftw_wisdom_file); fprintf(stderr, "ats-wisdom loaded!\n"); fclose(fftw_wisdom_file); } else fprintf(stderr, "cannot locate ats-wisdom!\n"); plan = fftw_plan_dft_1d(fft.size, fft.data, fft.data, FFTW_FORWARD, FFTW_PATIENT); #else fft.fdr = (double *)malloc(anargs->fft_size * sizeof(double)); fft.fdi = (double *)malloc(anargs->fft_size * sizeof(double)); #endif /* main loop */ for (frame_n=0; frame_n<anargs->frames; frame_n++) { /* clear fft arrays */ #ifdef FFTW for(k=0; k<fft.size; k++) fft.data[k][0] = fft.data[k][1] = 0.0f; #else for(k=0; k<fft.size; k++) fft.fdr[k] = fft.fdi[k] = 0.0f; #endif /* multiply by window */ for (k=0; k<anargs->win_size; k++) { if ((filptr >= 0) && (filptr < sflen)) #ifdef FFTW fft.data[(k+first_point)%fft.size][0] = window[k] * MUS_SAMPLE_TO_FLOAT(bufs[0][filptr]); #else fft.fdr[(k+first_point)%anargs->fft_size] = window[k] * MUS_SAMPLE_TO_FLOAT(bufs[0][filptr]); #endif filptr++; } /* we keep sample numbers of window midpoints in win_samps array */ win_samps[frame_n] = filptr - M_2 - 1; /* move file pointer back */ filptr = filptr - anargs->win_size + anargs->hop_smp; /* take the fft */ #ifdef FFTW fftw_execute(plan); #else fft_slow(fft.fdr, fft.fdi, fft.size, 1); #endif /* peak detection */ peaks_size = 0; peaks = peak_detection(&fft, anargs->lowest_bin, anargs->highest_bin, anargs->lowest_mag, norm, &peaks_size); /* peak tracking */ if (peaks != NULL) { /* evaluate peaks SMR (masking curves) */ evaluate_smr(peaks, peaks_size); if (frame_n) { /* initialize or update tracks */ if ((tracks = update_tracks(tracks, &tracks_size, anargs->track_len, frame_n, ana_frames, anargs->last_peak_cont)) != NULL) { /* do peak matching */ unmatched_peaks = peak_tracking(tracks, &tracks_size, peaks, &peaks_size, anargs->freq_dev, 2.0 * anargs->SMR_cont, &n_partials); /* kill unmatched peaks from previous frame */ if(unmatched_peaks[0].peaks != NULL) { for(k=0; k<unmatched_peaks[0].n_peaks; k++) { cpy_peak = unmatched_peaks[0].peaks[k]; cpy_peak.amp = cpy_peak.smr = 0.0; peaks = push_peak(&cpy_peak, peaks, &peaks_size); } free(unmatched_peaks[0].peaks); } /* give birth to peaks from new frame */ if(unmatched_peaks[1].peaks != NULL) { for(k=0; k<unmatched_peaks[1].n_peaks; k++) { tracks = push_peak(&unmatched_peaks[1].peaks[k], tracks, &tracks_size); unmatched_peaks[1].peaks[k].amp = unmatched_peaks[1].peaks[k].smr = 0.0; ana_frames[frame_n-1].peaks = push_peak(&unmatched_peaks[1].peaks[k], ana_frames[frame_n-1].peaks, &ana_frames[frame_n-1].n_peaks); } free(unmatched_peaks[1].peaks); } } else { /* give number to all peaks */ qsort(peaks, peaks_size, sizeof(ATS_PEAK), peak_frq_inc); for(k=0; k<peaks_size; k++) peaks[k].track = n_partials++; } } else { /* give number to all peaks */ qsort(peaks, peaks_size, sizeof(ATS_PEAK), peak_frq_inc); for(k=0; k<peaks_size; k++) peaks[k].track = n_partials++; } /* attach peaks to ana_frames */ ana_frames[frame_n].peaks = peaks; ana_frames[frame_n].n_peaks = n_partials; ana_frames[frame_n].time = (double)(win_samps[frame_n] - anargs->first_smp) / (double)anargs->srate; /* free memory */ free(unmatched_peaks); } else { /* if no peaks found, initialize empty frame */ ana_frames[frame_n].peaks = NULL; ana_frames[frame_n].n_peaks = 0; ana_frames[frame_n].time = (double)(win_samps[frame_n] - anargs->first_smp) / (double)anargs->srate; } } /* free up some memory */ free(window); free(tracks); #ifdef FFTW fftw_destroy_plan(plan); fftw_free(fft.data); #else free(fft.fdr); free(fft.fdi); #endif /* init sound */ fprintf(stderr, "Initializing ATS data..."); sound = (ATS_SOUND *)malloc(sizeof(ATS_SOUND)); init_sound(sound, anargs->srate, (int)(anargs->hop_size * anargs->win_size), anargs->win_size, anargs->frames, anargs->duration, n_partials, ((anargs->type == 3 || anargs->type == 4) ? 1 : 0)); /* store values from frames into the arrays */ for(k=0; k<n_partials; k++) { for(frame=0; frame<sound->frames; frame++) { sound->time[k][frame] = ana_frames[frame].time; for(i=0; i<ana_frames[frame].n_peaks; i++) if(ana_frames[frame].peaks[i].track == k) { sound->amp[k][frame] = ana_frames[frame].peaks[i].amp; sound->frq[k][frame] = ana_frames[frame].peaks[i].frq; sound->pha[k][frame] = ana_frames[frame].peaks[i].pha; sound->smr[k][frame] = ana_frames[frame].peaks[i].smr; } } } fprintf(stderr, "done!\n"); /* free up ana_frames memory */ /* first, free all peaks in each slot of ana_frames... */ for (k=0; k<anargs->frames; k++) free(ana_frames[k].peaks); /* ...then free ana_frames */ free(ana_frames); /* optimize sound */ optimize_sound(anargs, sound); /* compute residual */ if( anargs->type == 3 || anargs->type == 4 ) { fprintf(stderr, "Computing residual..."); compute_residual(bufs, sflen, resfile, sound, win_samps, anargs->srate); fprintf(stderr, "done!\n"); } /* free the rest of the memory */ free(win_samps); free(bufs[0]); free(bufs); /* analyze residual */ if( anargs->type == 3 || anargs->type == 4 ) { fprintf(stderr, "Analyzing residual..."); residual_analysis(ATSA_RES_FILE, sound); fprintf(stderr, "done!\n"); } #ifdef FFTW fftw_wisdom_file = fopen("ats-wisdom", "w"); fftw_export_wisdom_to_file(fftw_wisdom_file); fclose(fftw_wisdom_file); #endif fprintf(stderr, "tracking completed.\n"); return(sound); }
static int main_not_alsa(int argc, char *argv[]) { int fd, afd, i, j, n, k, chans, srate; off_t frames, m; mus_sample_t **bufs; OutSample *obuf; int use_multi_card_code = 0, use_volume = 0; int afd0, afd1, buffer_size = BUFFER_SIZE, curframes, sample_size, out_chans, outbytes; mus_sample_t **qbufs; short *obuf0, *obuf1; char *name = NULL; off_t start = 0, end = 0; double begin_time = 0.0, end_time = 0.0, volume = 1.0; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-describe") == 0) { mus_audio_describe(); exit(0); } else { if (strcmp(argv[i], "-buffers") == 0) { set_buffers(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-bufsize") == 0) { buffer_size = atoi(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-start") == 0) { begin_time = atof(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-end") == 0) { end_time = atof(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-volume") == 0) { volume = atof(argv[i + 1]); use_volume = 1; i++; } else name = argv[i]; }}}}}} if (name == NULL) { printf("usage: sndplay file [-start 1.0] [-end 1.0] [-bufsize %d] [-buffers 2x12] [-volume 1.0] [-describe]\n", BUFFER_SIZE); exit(0); } afd = -1; afd0 = -1; afd1 = -1; if (!(MUS_HEADER_TYPE_OK(mus_sound_header_type(name)))) { fprintf(stderr, "can't play %s (header type: %s?)\n", name, mus_header_type_name(mus_header_type())); exit(0); } if (!(MUS_DATA_FORMAT_OK(mus_sound_data_format(name)))) { fprintf(stderr, "can't play %s (data format: %s (%s)?)\n", name, mus_data_format_name(mus_sound_data_format(name)), mus_header_original_format_name(mus_sound_original_format(name), mus_sound_header_type(name))); exit(0); } fd = mus_sound_open_input(name); if (fd != -1) { chans = mus_sound_chans(name); if (chans > 2) { float val[8]; mus_audio_mixer_read(MUS_AUDIO_DEFAULT, MUS_AUDIO_CHANNEL, 0, val); if (val[0] < chans) { if (mus_audio_systems() > 1) use_multi_card_code = 1; /* I suppose we could count up all the channels here */ else { fprintf(stderr, "%s has %d channels, but we can only handle %d\n", name, chans, (int)(val[0])); exit(1); } } } out_chans = chans; srate = mus_sound_srate(name); frames = mus_sound_frames(name); sample_size = mus_bytes_per_sample(MUS_AUDIO_COMPATIBLE_FORMAT); start = (off_t)(begin_time * srate); if (start > 0) mus_file_seek_frame(fd, start); if (end_time > 0.0) end = (off_t)(end_time * srate); else end = frames; if ((end - start) < frames) frames = end - start; if (!use_multi_card_code) { bufs = (mus_sample_t **)calloc(chans, sizeof(mus_sample_t *)); for (i = 0; i < chans; i++) bufs[i] = (mus_sample_t *)calloc(buffer_size, sizeof(mus_sample_t)); obuf = (OutSample *)calloc(buffer_size * out_chans, sizeof(OutSample)); outbytes = buffer_size * out_chans * sample_size; for (m = 0; m < frames; m += buffer_size) { if ((m + buffer_size) <= frames) curframes = buffer_size; else curframes = frames - m; mus_file_read(fd, 0, curframes - 1, chans, bufs); /* some systems are happier if we read the file before opening the dac */ /* at this point the data is in separate arrays of mus_sample_t's */ if (use_volume) set_volume(bufs, chans, curframes, volume); if (chans == 1) { for (k = 0; k < curframes; k++) obuf[k] = MUS_CONVERT(bufs[0][k]); } else { if (chans == 2) { for (k = 0, n = 0; k < curframes; k++, n += 2) { obuf[n] = MUS_CONVERT(bufs[0][k]); obuf[n + 1] = MUS_CONVERT(bufs[1][k]); } } else { for (k = 0, j = 0; k < curframes; k++, j += chans) { for (n = 0; n < chans; n++) obuf[j + n] = MUS_CONVERT(bufs[n][k]); } } } if (afd == -1) { #if defined(MUS_LINUX) && defined(PPC) afd = mus_audio_open_output(MUS_AUDIO_DEFAULT, srate, chans, MUS_AUDIO_COMPATIBLE_FORMAT, 0); #else afd = mus_audio_open_output(MUS_AUDIO_DEFAULT, srate, out_chans, MUS_AUDIO_COMPATIBLE_FORMAT, outbytes); #endif if (afd == -1) break; } outbytes = curframes * out_chans * sample_size; mus_audio_write(afd, (char *)obuf, outbytes); } if (afd != -1) mus_audio_close(afd); mus_sound_close_input(fd); for (i = 0; i < chans; i++) free(bufs[i]); free(bufs); free(obuf); } else { /* code is essentially the same as above, but since this is supposed * to be a working example of sndlib, I didn't want the basic stuff * to be complicated by one special case. * * in my test case, I was using a Sound Blaster and an Ensoniq clone. * they had slightly different start-up latencies (not really audible), * and the Ensoniq's dac was running ca. 1 sample per second faster than * the SB's, so by 5 minutes into a sound, the stereo pairs had drifted * .01 seconds apart -- this is probably acceptable in many cases. * In a second test, with two Ensoniq's in one machine, they started * together and drifted apart at about 1 sample per 8 seconds -- about * .001 secs apart after 5 minutes. ("Ensoniq" was SoundWave Pro PCI * from SIIG Inc -- some sort of clone.) */ buffer_size = 256; /* 128 probably better */ outbytes = buffer_size * 2 * 2; qbufs = (mus_sample_t **)calloc(chans, sizeof(mus_sample_t *)); for (i = 0; i < chans; i++) qbufs[i] = (mus_sample_t *)calloc(buffer_size, sizeof(mus_sample_t)); obuf0 = (short *)calloc(buffer_size * 2, sizeof(short)); obuf1 = (short *)calloc(buffer_size * 2, sizeof(short)); for (m = 0; m < frames; m += buffer_size) { if ((m + buffer_size) <= frames) curframes = buffer_size; else curframes = frames - m; mus_file_read(fd, 0, curframes - 1, chans, qbufs); if (use_volume) set_volume(qbufs, chans, curframes, volume); for (k = 0, n = 0; k < buffer_size; k++, n += 2) { obuf0[n] = MUS_SAMPLE_TO_SHORT(qbufs[0][k]); obuf0[n + 1] = MUS_SAMPLE_TO_SHORT(qbufs[1][k]); obuf1[n] = MUS_SAMPLE_TO_SHORT(qbufs[2][k]); obuf1[n + 1] = MUS_SAMPLE_TO_SHORT(qbufs[3][k]); } if (afd0 == -1) { afd0 = mus_audio_open_output(MUS_AUDIO_PACK_SYSTEM(0) | MUS_AUDIO_DEFAULT, srate, 2, MUS_AUDIO_COMPATIBLE_FORMAT, outbytes); afd1 = mus_audio_open_output(MUS_AUDIO_PACK_SYSTEM(1) | MUS_AUDIO_DEFAULT, srate, 2, MUS_AUDIO_COMPATIBLE_FORMAT, outbytes); if ((afd0 == -1) || (afd1 == -1)) break; } mus_audio_write(afd0, (char *)obuf0, outbytes); mus_audio_write(afd1, (char *)obuf1, outbytes); } mus_audio_close(afd0); mus_audio_close(afd1); mus_sound_close_input(fd); for (i = 0; i < chans; i++) free(qbufs[i]); free(qbufs); free(obuf0); free(obuf1); } } return(0); }
static int main_alsa(int argc, char *argv[]) { int fd, i, chans, srate; off_t frames, ioff; mus_sample_t **read_bufs; int afd[MAX_SLOTS]; short *out_buf[MAX_SLOTS]; float val[MAX_SLOTS]; int ival[MAX_SLOTS]; int afd0, afd1; char *name; int base, curframes; int allocated; int out_devs[MAX_SLOTS]; int out_chans[MAX_SLOTS]; int out_format[MAX_SLOTS]; int out_bytes[MAX_SLOTS]; int samples_per_chan; int last_device; int devices[MAX_SLOTS]; int available_chans[MAX_SLOTS]; int min_chans[MAX_SLOTS]; int max_chans[MAX_SLOTS]; int alloc_chans; off_t start = 0, end = 0; double begin_time = 0.0, end_time = 0.0, volume = 1.0; int use_volume = 0; /* -describe => call mus_audio_describe and exit * -buffers axb => set OSS fragment numbers */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-describe") == 0) { mus_audio_describe(); exit(0); } else { if (strcmp(argv[i], "-buffers") == 0) { set_buffers(argv[i+1]); i++; } else { if (strcmp(argv[i], "-start") == 0) { begin_time = atof(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-end") == 0) { end_time = atof(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-volume") == 0) { volume = atof(argv[i + 1]); use_volume = 1; i++; } else name = argv[i]; }}}}} afd0 = -1; afd1 = -1; if (!(MUS_HEADER_TYPE_OK(mus_sound_header_type(name)))) { fprintf(stderr, "can't play %s (header type: %s?)\n", name, mus_header_type_name(mus_header_type())); exit(0); } if (!(MUS_DATA_FORMAT_OK(mus_sound_data_format(name)))) { fprintf(stderr, "can't play %s (data format: %s (%s)?)\n", name, mus_data_format_name(mus_sound_data_format(name)), mus_header_original_format_name(mus_sound_original_format(name), mus_sound_header_type(name))); exit(0); } fd = mus_sound_open_input(name); if (fd != -1) { /* try to select proper device */ float dir; int cards, card; int sysdev, devs, dev, d, i, im; int first_samples_per_chan = -1; cards = mus_audio_systems(); /* deselect all devices */ for (d = 0; d < MAX_SLOTS; d++) { out_devs[d] = -1; afd[d] = -1; } /* Scan all cards and build a list of available output devices. This is evil because it second guesses the intentions of the user, best would be to have a command line parameter that points to the device or devices to be used. For things to work under alsa .5 and multichannel cards it has to be here. Hopefully the need will go away (in alsa .6 it will definitely not be needed). */ i = 0; im = 0; for (card = 0; card < cards; card++) { /* get the list of all available devices */ mus_audio_mixer_read(MUS_AUDIO_PACK_SYSTEM(card), MUS_AUDIO_PORT, MAX_SLOTS, val); devs = (int)(val[0]); for (d = 0; d < devs; d++) { dev = (int)(val[d + 1]); sysdev = MUS_AUDIO_PACK_SYSTEM(card)|dev; mus_audio_mixer_read(sysdev, MUS_AUDIO_DIRECTION, 0, &dir); /* only consider output devices */ if ((int)dir == 0) { float ch[4]; /* get the number of channels the device supports */ mus_audio_mixer_read(sysdev, MUS_AUDIO_CHANNEL, 4, ch); available_chans[i] = (int)(ch[0]); if ((int)ch[2] != 0) /* alsa also sets min and max channels */ { min_chans[i] = (int)(ch[1]); max_chans[i] = (int)(ch[2]); } if (max_chans[i] > max_chans[im]) im = i; /* find out what format we can use with the device */ out_format[i] = mus_audio_compatible_format(sysdev); /* find out what buffer size the device wants */ mus_audio_mixer_read(sysdev, MUS_AUDIO_SAMPLES_PER_CHANNEL, 2, ch); samples_per_chan = (int)ch[0]; /* skip device if it has different buffer size, all must match */ if (first_samples_per_chan == -1) first_samples_per_chan = samples_per_chan; else if (samples_per_chan != first_samples_per_chan) continue; devices[i++] = sysdev; if (i >= MAX_SLOTS) goto NO_MORE_DEVICES; } } } NO_MORE_DEVICES: last_device = i; chans = mus_sound_chans(name); allocated = 0; if (available_chans[im] >= chans) { /* the widest device is wide enough to play all channels so we use it */ out_devs[allocated] = im; out_chans[allocated] = chans; if (chans < min_chans[im]) out_chans[allocated] = min_chans[im]; alloc_chans = out_chans[allocated]; allocated++; } else { alloc_chans = 0; if (use_one_device == 0) { /* allocate devices until all channels can be played */ for (i = 0; i < last_device; i++) { out_devs[allocated] = i; out_chans[allocated] = available_chans[i]; alloc_chans += available_chans[out_devs[allocated]]; allocated++; if (alloc_chans >= chans) break; } } if (alloc_chans < chans) { /* FOR NOW, fail the program, not enough channels... */ fprintf(stderr, "not enough channels, %d available, %d needed\n", available_chans[0], chans); exit(1); /* either not enough channels found or have to use just one device and the widest can't do it, so fold all of them into whatever the first device can do */ allocated = 0; out_devs[allocated] = 0; out_chans[allocated] = available_chans[0]; alloc_chans = out_chans[allocated]; allocated++; } } srate = mus_sound_srate(name); frames = mus_sound_frames(name); base = 0; start = (off_t)(begin_time * srate); if (start > 0) mus_file_seek_frame(fd, start); if (end_time > 0.0) end = (off_t)(end_time * srate); else end = frames; if ((end - start) < frames) frames = end - start; /* allocate the list of read buffers, each buffer will hold one channel of the input soundfile, each sample is going to be mus_sample_t */ read_bufs = (mus_sample_t **)calloc(alloc_chans, sizeof(mus_sample_t *)); for (d = 0; d < allocated; d++) { int dev = out_devs[d]; for (i = 0; i < out_chans[d]; i++) read_bufs[base + i] = (mus_sample_t *)calloc(samples_per_chan, sizeof(mus_sample_t)); base += out_chans[d]; out_bytes[dev] = samples_per_chan * out_chans[d] * mus_bytes_per_sample(out_format[dev]); out_buf[dev] = (short *)calloc(out_bytes[dev], 1); } for (ioff = 0; ioff < frames; ioff += samples_per_chan) { mus_sample_t **dev_bufs = read_bufs; if ((ioff + samples_per_chan) <= frames) curframes = samples_per_chan; else { curframes = frames - ioff; for (d = 0; d < allocated; d++) { int f, dev = out_devs[d]; #if 1 /* try to kludge around an ALSA bug... */ for (f = 0; f < chans; f++) memset(read_bufs[f], 0, samples_per_chan * sizeof(mus_sample_t)); #endif out_bytes[dev] = curframes * out_chans[d] * mus_bytes_per_sample(out_format[dev]); } } mus_file_read(fd, 0, curframes - 1, chans, read_bufs); if (use_volume) set_volume(read_bufs, chans, curframes, volume); /* some systems are happier if we read the file before opening the dac */ /* at this point the data is in separate arrays of mus_sample_t */ for (d = 0; d < allocated; d++) { int dev = out_devs[d]; mus_file_write_buffer(out_format[dev], 0, curframes - 1, out_chans[d], dev_bufs, (char *)(out_buf[dev]), 0); if (afd[dev] == -1) { #if defined(PPC) afd[dev] = mus_audio_open_output(devices[dev], srate, out_chans[d], out_format[dev], 0); #else afd[dev] = mus_audio_open_output(devices[dev], srate, out_chans[d], out_format[dev], out_bytes[dev]); #endif if (afd[dev] == -1) break; } mus_audio_write(afd[dev], (char *)out_buf[dev], out_bytes[dev]); dev_bufs += out_chans[d]; } } for (d = 0; d < allocated; d++) { int dev = out_devs[d]; if (afd[dev] != -1) mus_audio_close(afd[dev]); } mus_sound_close_input(fd); for (i = 0; i < alloc_chans; i++) free(read_bufs[i]); free(read_bufs); for (d = 0; d < allocated; d++) { int dev = out_devs[d]; free(out_buf[dev]); } } return(0); }
mus_long_t mus_sound_maxamps(const char *ifile, int chans, mus_float_t *vals, mus_long_t *times) { mus_long_t frames; int i, ichans, chn; sound_file *sf; sf = get_sf(ifile); if (sf->chans <= 0) return(MUS_ERROR); if ((sf) && (sf->maxamps)) { if (chans > sf->maxamps_size) ichans = sf->maxamps_size; 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); } { int j, bufnum, ifd; mus_long_t n, curframes; mus_float_t *buffer, *samp; mus_long_t *time; mus_float_t **ibufs; 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_float_t **)calloc(ichans, sizeof(mus_float_t *)); bufnum = 8192; for (j = 0; j < ichans; j++) ibufs[j] = (mus_float_t *)calloc(bufnum, sizeof(mus_float_t)); time = (mus_long_t *)calloc(ichans, sizeof(mus_long_t)); samp = (mus_float_t *)calloc(ichans, sizeof(mus_float_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_float_t *)(ibufs[chn]); for (i = 0; i < curframes; i++) { mus_float_t abs_samp; abs_samp = fabs(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 main(int argc, char *argv[]) { int fd, afd, i, j, n, k, chans, srate; mus_long_t frames, m; mus_sample_t **bufs; OutSample *obuf; int buffer_size = BUFFER_SIZE, curframes, sample_size, out_chans, outbytes; char *name = NULL; mus_long_t start = 0, end = 0; double begin_time = 0.0, end_time = 0.0; int mutate = 1, include_mutate = 0; if (argc == 1) { printf("usage: sndplay file [-start 1.0] [-end 1.0] [-bufsize %d] [-buffers 2x12] [-describe]\n", BUFFER_SIZE); exit(0); } mus_sound_initialize(); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-buffers") == 0) { #if (HAVE_OSS || HAVE_ALSA) static char x_string[2] = {'x','\0'}; char *arg; int a, b; arg = strtok(argv[i + 1], x_string); a = atoi(arg); arg = strtok(NULL, x_string); b = atoi(arg); mus_oss_set_buffers(a, b); #endif i++; } else { if (strcmp(argv[i], "-bufsize") == 0) { buffer_size = atoi(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-start") == 0) { begin_time = atof(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-end") == 0) { end_time = atof(argv[i + 1]); i++; } else { if (strcmp(argv[i], "-mutable") == 0) { mutate = atoi(argv[i + 1]); include_mutate = 1; i++; } else name = argv[i]; }}}}} if (name == NULL) { printf("usage: sndplay file [-start 1.0] [-end 1.0] [-bufsize %d] [-buffers 2x12] [-mutable 1]\n", BUFFER_SIZE); exit(0); } afd = -1; if (!(mus_header_type_p(mus_sound_header_type(name)))) { fprintf(stderr, "can't play %s (header type: %s?)\n", name, mus_header_type_name(mus_header_type())); exit(0); } if (!(mus_data_format_p(mus_sound_data_format(name)))) { fprintf(stderr, "can't play %s (data format: %s (%s)?)\n", name, mus_data_format_name(mus_sound_data_format(name)), mus_header_original_format_name(mus_sound_original_format(name), mus_sound_header_type(name))); exit(0); } fd = mus_sound_open_input(name); if (fd != -1) { chans = mus_sound_chans(name); if (chans > 2) { int available_chans; available_chans = mus_audio_device_channels(MUS_AUDIO_DEFAULT); if (available_chans < chans) { fprintf(stderr, "%s has %d channels, but we can only handle %d\n", name, chans, available_chans); exit(1); } } out_chans = chans; srate = mus_sound_srate(name); frames = mus_sound_frames(name); sample_size = mus_bytes_per_sample(MUS_AUDIO_COMPATIBLE_FORMAT); start = (mus_long_t)(begin_time * srate); if (start > 0) mus_file_seek_frame(fd, start); if (end_time > 0.0) end = (mus_long_t)(end_time * srate); else end = frames; if ((end - start) < frames) frames = end - start; bufs = (mus_sample_t **)calloc(chans, sizeof(mus_sample_t *)); for (i = 0; i < chans; i++) bufs[i] = (mus_sample_t *)calloc(buffer_size, sizeof(mus_sample_t)); obuf = (OutSample *)calloc(buffer_size * out_chans, sizeof(OutSample)); outbytes = buffer_size * out_chans * sample_size; for (m = 0; m < frames; m += buffer_size) { if ((m + buffer_size) <= frames) curframes = buffer_size; else curframes = frames - m; mus_file_read(fd, 0, curframes - 1, chans, bufs); /* some systems are happier if we read the file before opening the dac */ /* at this point the data is in separate arrays of mus_sample_t's */ if (chans == 1) { for (k = 0; k < curframes; k++) obuf[k] = MUS_CONVERT(bufs[0][k]); } else { if (chans == 2) { for (k = 0, n = 0; k < curframes; k++, n += 2) { obuf[n] = MUS_CONVERT(bufs[0][k]); obuf[n + 1] = MUS_CONVERT(bufs[1][k]); } } else { for (k = 0, j = 0; k < curframes; k++, j += chans) { for (n = 0; n < chans; n++) obuf[j + n] = MUS_CONVERT(bufs[n][k]); } } } #if MUS_MAC_OSX if (include_mutate == 1) mus_audio_output_properties_mutable(mutate); #endif if (afd == -1) { afd = mus_audio_open_output(MUS_AUDIO_DEFAULT, srate, out_chans, MUS_AUDIO_COMPATIBLE_FORMAT, outbytes); if (afd == -1) break; } outbytes = curframes * out_chans * sample_size; mus_audio_write(afd, (char *)obuf, outbytes); } if (afd != -1) mus_audio_close(afd); mus_sound_close_input(fd); for (i = 0; i < chans; i++) free(bufs[i]); free(bufs); free(obuf); } return(0); }
int main(int argc, char *argv[]) { int chans, srate, ctr; mus_sample_t samp_type; mus_header_t type; mus_long_t samples; float length = 0.0; time_t date; int *loops = NULL; char *comment, *header_name; char *samp_type_info = NULL, *samp_type_name, *ampstr = NULL; char timestr[64]; if (argc == 1) {printf("usage: sndinfo file\n"); exit(0);} mus_sound_initialize(); for (ctr = 1; ctr < argc; ctr++) { if (mus_file_probe(argv[ctr])) /* see if it exists */ { date = mus_sound_write_date(argv[ctr]); srate = mus_sound_srate(argv[ctr]); if (srate == MUS_ERROR) { fprintf(stdout, "%s: not a sound file?\n", argv[ctr]); continue; } chans = mus_sound_chans(argv[ctr]); samples = mus_sound_samples(argv[ctr]); comment = mus_sound_comment(argv[ctr]); if ((chans > 0) && (srate > 0)) length = (float)((double)samples / (double)(chans * srate)); loops = mus_sound_loop_info(argv[ctr]); type = mus_sound_header_type(argv[ctr]); header_name = (char *)mus_header_type_name(type); samp_type = mus_sound_sample_type(argv[ctr]); if (samp_type != MUS_UNKNOWN_SAMPLE) samp_type_info = (char *)mus_sample_type_name(samp_type); else { int orig_type; if (samp_type_info == NULL) samp_type_info = (char *)calloc(64, sizeof(char)); orig_type = mus_sound_original_sample_type(argv[ctr]); samp_type_name = (char *)mus_header_original_sample_type_name(orig_type, type); if (samp_type_name) snprintf(samp_type_info, 64, "%d (%s)", orig_type, samp_type_name); else snprintf(samp_type_info, 64, "%d", orig_type); } fprintf(stdout, "%s:\n srate: %d\n chans: %d\n length: %f", argv[ctr], srate, chans, length); if (length < 10.0) { int samps; samps = mus_sound_framples(argv[ctr]); fprintf(stdout, " (%d sample%s)", samps, (samps != 1) ? "s" : ""); } fprintf(stdout, "\n"); fprintf(stdout, " header type: %s\n sample type: %s\n ", header_name, samp_type_info); strftime(timestr, 64, "%a %d-%b-%Y %H:%M %Z", localtime(&date)); fprintf(stdout, "written: %s", timestr); if ((chans > 0) && (mus_sound_maxamp_exists(argv[ctr]))) { ampstr = display_maxamps(argv[ctr], chans); if (ampstr) fprintf(stdout, "%s", ampstr); } fprintf(stdout, "\n"); if (comment) fprintf(stdout, " comment: %s\n", comment); if (loops) { fprintf(stdout, " loop: %d to %d\n", loops[0], loops[1]); if (loops[2] != 0) fprintf(stdout, " loop: %d to %d\n", loops[2], loops[3]); if (loops[0] != 0) fprintf(stdout, " base: %d, detune: %d\n", loops[4], loops[5]); } } else fprintf(stderr, "%s: %s\n", argv[ctr], strerror(errno)); if (ctr < argc - 1) fprintf(stdout, "\n"); } return(0); }