// Assignment 2 20T1 COMP1511: CS bEats // beats.c // // The purpose of this program is to categorise music input. // // Version 1.0.0: Assignment released. // Version 1.0.1: Fix default return value of add_musical_note_to_beat. #include #include #include #include #include #include "beats.h" struct track { struct beat *head; struct beat *cur; }; struct beat { struct note *notes; struct beat *next; }; struct note { int oct; int key; struct note *next; }; //////////////////////////////////////////////////////////////////////// // User Defined Functions // //////////////////////////////////////////////////////////////////////// // Returns a value representing the pitch. static int get_pitch(const int octave, const int key) { return (octave * 12) + (key); } // Return a malloced beat with fields initialised. struct beat *create_beat() { Beat new_beat = malloc(sizeof(struct beat)); assert(new_beat != NULL); new_beat->next = NULL; new_beat->notes = NULL; return new_beat; } // Returns the tail of a note linked list. static struct note *get_note_tail(struct note *nhead) { if (nhead == NULL) { return NULL; } struct note *ret = nhead; while (ret->next != NULL) { ret = ret->next; } return ret; } // Returns the tail of a beat linked list. static struct beat *get_beat_tail(struct beat *bhead) { if (bhead == NULL) { return NULL; } struct beat *ret = bhead; while (ret->next != NULL) { ret = ret->next; } return ret; } // Returns a malloced Note with fields initialised. static struct note *create_note() { struct note *ret = malloc(sizeof(struct note)); assert(ret != NULL); ret->oct = 0; ret->key = 0; ret->next = NULL; return ret; } // Inserts a note into a linked list of notes before the target note. static void insert_note_before(struct beat *beat, struct note *note, struct note *target) { if (beat->notes == target) { beat->notes = note; note->next = target; return; } for (struct note *i = beat->notes; i != NULL; i = i->next) { if (i->next == target) { i->next = note; note->next = target; return; } } } //////////////////////////////////////////////////////////////////////// // Stage 1 Functions // //////////////////////////////////////////////////////////////////////// // Add a note to the end of a beat. int add_note_to_beat(struct beat *const beat, const int oct, const int key) { if (oct < 0 || oct >= 10) { return INVALID_OCTAVE; } if (key < 0 || key >= 12) { return INVALID_KEY; } struct note *ntail = get_note_tail(beat->notes); if (ntail != NULL) { if (get_pitch(oct, key) <= get_pitch(ntail->oct, ntail->key)) { return NOT_HIGHEST_NOTE; } } struct note *new_note = create_note(); new_note->oct = oct; new_note->key = key; new_note->next = NULL; if (ntail == NULL) { beat->notes = new_note; } else { ntail->next = new_note; } return VALID_NOTE; } // Print the contents of a beat. void print_beat(struct beat *const beat) { if (beat == NULL) { return; } if (beat->notes == NULL) { printf("\n"); return; } for (struct note *i = beat->notes; i != NULL; i = i->next) { printf("%d ", i->oct); if (i->key >= 10) { printf("%d", i->key % 12); } else { printf("%d%d", i->key / 12, i->key % 12); } if (i->next) { printf(" | "); } } printf("\n"); } // Count the number of notes in a beat that are in a given octave. int count_notes_in_octave(struct beat *const beat, int oct) { if (beat->notes == NULL) { return 0; } int notes = 0; for (struct note *i = beat->notes; i != NULL; i = i->next) { if (i->oct == oct) { ++notes; } } return notes; } //////////////////////////////////////////////////////////////////////// // Stage 2 Functions // //////////////////////////////////////////////////////////////////////// // Return a malloced track with fields initialized. struct track *create_track(void) { struct track *ret = malloc(sizeof(struct track)); ret->head = NULL; ret->cur = NULL; return ret; } // Add a beat after the current beat in a track. void add_beat_to_track(struct track *track, struct beat *beat) { if (track->cur != NULL) { // this behaviour may be unwanted if (track->cur->next != NULL) { beat->next = track->cur->next; track->cur->next = beat; } else { track->cur->next = beat; } } else { if (track->head == NULL) { track->head = beat; } else { beat->next = track->head; track->head = beat; } } } // Set a track's current beat to the next beat. int select_next_beat(struct track *track) { if (track->cur == NULL) { if (track->head == NULL) { return TRACK_STOPPED; } else { track->cur = track->head; return TRACK_PLAYING; } } else { if (track->cur->next == NULL) { track->cur = NULL; return TRACK_STOPPED; } else { track->cur = track->cur->next; return TRACK_PLAYING; } } } // Print the contents of a track. void print_track(struct track *track) { if (track->head == NULL) { return; } int count = 1; for (struct beat *i = track->head; i != NULL; i = i->next) { if (i == track->cur) { printf(">"); } else { printf(" "); } printf("[%d] ", count); print_beat(i); ++count; } } // Count beats after the current beat in a track. int count_beats_left_in_track(struct track *track) { int count = 0; if (track == NULL || track->head == NULL) { return count; } struct beat *i = track->cur; if (i == NULL) { i = track->head; } for (; i != NULL; i = i->next) { ++count; } if (track->cur) { --count; } return count; } //////////////////////////////////////////////////////////////////////// // Stage 3 Functions // //////////////////////////////////////////////////////////////////////// // Free the memory of a beat, and any memory it points to. void free_beat(struct beat *beat) { if (beat == NULL) { return; } if (beat->notes != NULL) { struct note *i = beat->notes; struct note *forwards = i->next; while (i != NULL) { forwards = i->next; free(i); i = forwards; } } free(beat); } // Free the memory of a track, and any memory it points to. void free_track(struct track *track) { if (track == NULL) { return; } if (track->head != NULL) { struct beat *i = track->head; struct beat *forwards = i->next; while (i != NULL) { forwards = i->next; free_beat(i); i = forwards; } } free(track); } // Remove the currently selected beat from a track. int remove_selected_beat(struct track *track) { if (track->cur == NULL) { return TRACK_STOPPED; } struct beat *backwards = NULL; struct beat *forwards = NULL; for (struct beat *i = track->head; i != NULL; i = i->next) { if (i == track->cur) { track->head = i->next; forwards = i->next; free_beat(track->cur); break; } else if (i->next == track->cur) { backwards = i; forwards = i->next->next; free_beat(track->cur); backwards->next = forwards; break; } } if (forwards == NULL) { track->cur = NULL; return TRACK_STOPPED; } else { track->cur = forwards; return TRACK_PLAYING; } } //////////////////////////////////////////////////////////////////////// // Stage 4 Functions // //////////////////////////////////////////////////////////////////////// // Add note to beat, given its 'musical notation'. int add_musical_note_to_beat(Beat beat, char *string) { int oct = string[0] - '0'; if (oct < 0 || oct > 9) { return INVALID_MUSICAL_NOTE; } int key = 0; switch (string[1]) { case 'A': key = 0; break; case 'B': key = 2; break; case 'C': key = 3; break; case 'D': key = 5; break; case 'E': key = 7; break; case 'F': key = 8; break; case 'G': key = 10; break; default: return INVALID_MUSICAL_NOTE; } for (char *c = string + 2; *c != '\0'; ++c) { if (*c == '#') { ++key; } else { return INVALID_MUSICAL_NOTE; } } int pitch = get_pitch(oct, key); oct = pitch / 12; key = pitch % 12; struct note *tail = get_note_tail(beat->notes); if (tail == NULL || get_pitch(tail->oct, tail->key) < pitch) { add_note_to_beat(beat, oct, key); return VALID_NOTE; } struct note *higher = NULL; for (struct note *i = beat->notes; i != NULL; i = i->next) { int iter_pitch = get_pitch(i->oct, i->key); if (iter_pitch == pitch) { return INVALID_MUSICAL_NOTE; } else if (iter_pitch > pitch) { higher = i; break; } } struct note *note = create_note(); note->oct = oct; note->key = key; insert_note_before(beat, note, higher); return VALID_NOTE; } //////////////////////////////////////////////////////////////////////// // Stage 5 Functions // //////////////////////////////////////////////////////////////////////// // Cut a range of beats to the end of a track. void cut_range_to_end(Track track, int range_length) { printf("cut_range_to_end not implemented yet."); return; } // Reverse a list of beats within a range of a track. int reverse_range(Track track, int range_length) { printf("reverse_range not implemented yet."); return 0; }