/* * Reads audio data as decimal integers from standard input and outputs * the intervals corresponding to tracks. Tracks are maximal intervals * of samples that contain no long runs of low values. The starting * and ending point of each track is written to standard output. */ #include #include #include "track_list.h" // the functions we will pass to track_list_process // it is important that all these functions have the same type // (return type + types and order of arguments) void write_track(int i, int start, int end, const int *samples, void *); void write_endpoints(int i, int start, int end, const int *samples, void *); void add_lengths(int i, int start, int end, const int *samples, void *tot); /* states for the FSM: * GAP = in the gap between tracks * TRACK = in a track * ZEROS = in a run of zeros (or low values at or below the threshold) */ typedef enum {GAP, TRACK, ZEROS} state; int main(int argc, char **argv) { // the current state of the FSM state curr = GAP; // the maximum value to count as silence const int threshold = 5; // the sample rate of the audio being read const int sample_rate = 44100; // the minimum length of a gap between tracks, in seconds and samples const double min_gap_sec = 0.0001; const int min_gap_samples = (int)(sample_rate * min_gap_sec); // the starting and ending points of the most recent track read or being read int start, end; // the starting and ending points of all the tracks track_list *l; l = track_list_create(); const int SAMPLES_INITIAL_SIZE = 4; // make the array int *samples = malloc(sizeof(int) * SAMPLES_INITIAL_SIZE); int samples_capacity = SAMPLES_INITIAL_SIZE; // the index of the currently read sample int count = 0; FILE *in; if (argc < 2) { // no filename given, read from standard input instead in = stdin; } else { in = fopen(argv[1], "r"); // mode string "w" for writing (with replacement); "w+" for append } if (in == NULL) { fprintf(stderr, "Error opening %s\n", argv[1]); return 1; } // the current sample value int value; while (fscanf(in, "%d", &value) > 0) { switch (curr) { case GAP: if (abs(value) > threshold) { // end of gap; now reading track so remember start start = count; curr = TRACK; } break; case TRACK: if (abs(value) <= threshold) { // read a low value, start counting consecutive low values // and record possible end of current track end = count - 1; curr = ZEROS; } break; case ZEROS: if (abs(value) > threshold) { // read a high value; just a pause in the track so read some more curr = TRACK; } else if (count - end >= min_gap_samples) { // read enough consecutive low values to consider track over; // output interval for last track and continue to read through gap curr = GAP; track_list_add(l, start, end); } } if (count == samples_capacity) { int *bigger = malloc((count + 1) * sizeof(int) ); if (bigger == NULL) { fprintf(stderr, "%s: out of memory\n", argv[0]); return 1; } for (int i = 0; i < count; i++) { bigger[i] = samples[i]; } free(samples); samples = bigger; samples_capacity += 1; } samples[count] = value; count++; } // output interval for last track if input ended in the middle of a track if (curr == TRACK) { end = count - 1; } if (curr == TRACK || curr == ZEROS) { // this section of code has been repeated -- a good candidate // to make into a function! track_list_add(l, start, end); } // write each track to standard output using write_track track_list_process(l, write_track, samples, NULL); // write just the endpoints of each track using write_endpoints track_list_process(l, write_endpoints, NULL, NULL); // add up the total length of the tracks using add_lengths int tot = 0; // we will pass this to process so it can pass it to add_lengths track_list_process(l, add_lengths, samples, &tot); printf("total length %d\n", tot); // clean up dynamically allocated memory free(samples); track_list_destroy(l); fclose(in); } /** * Writes the given interval from the given array to standard output. * The interval is given by the indices of the endpoints. The output * format is a header followed by all the values in the interval * on separate lines. * * @param i the index of the track * @param start an inndex into samples * @param end an index into samples >= start * @param samples an array integers */ void write_track(int i, int start, int end, const int *samples, void *dummy) { printf("=== TRACK %d ===\n", i); printf("%d\n", end - start + 1); for (int s = start; s <= end; s++) { printf("%d\n", samples[s]); } } void write_endpoints(int i, int start, int end, const int *samples, void *dummy) { printf("Track %d: %d-%d\n", i, start, end); } void add_lengths(int i, int start, int end, const int *samples, void *tot) { // must cast from void * in order to do anything useful with tot int *total = tot; *total += (end - start) + 1; }