aboutsummaryrefslogtreecommitdiff
path: root/comp1511/cs_beats
diff options
context:
space:
mode:
Diffstat (limited to 'comp1511/cs_beats')
-rw-r--r--comp1511/cs_beats/beats.c412
-rw-r--r--comp1511/cs_beats/test_beats.c190
2 files changed, 602 insertions, 0 deletions
diff --git a/comp1511/cs_beats/beats.c b/comp1511/cs_beats/beats.c
new file mode 100644
index 0000000..2f26898
--- /dev/null
+++ b/comp1511/cs_beats/beats.c
@@ -0,0 +1,412 @@
+// 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 <assert.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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;
+}
diff --git a/comp1511/cs_beats/test_beats.c b/comp1511/cs_beats/test_beats.c
new file mode 100644
index 0000000..d1eb793
--- /dev/null
+++ b/comp1511/cs_beats/test_beats.c
@@ -0,0 +1,190 @@
+// Assignment 2 20T1 COMP1511: CS bEats
+// test_beats.c
+//
+// This program was written by YOUR-NAME-HERE (z5555555)
+// on INSERT-DATE-HERE
+//
+// Version 1.0.0: Assignment released.
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "beats.h"
+#include "test_beats.h"
+
+// Test function for `add_note_to_beat`
+int test_add_note_to_beat(void) {
+ // Test 1: Rejecting negative inputs.
+ Beat test_beat = create_beat();
+ if (add_note_to_beat(test_beat, -1, -1) != INVALID_OCTAVE) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ if (add_note_to_beat(test_beat, -1, 0) != INVALID_OCTAVE) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ if (add_note_to_beat(test_beat, 1, -1) != INVALID_KEY) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 2: Rejecting invalid pitch notes.
+ if (add_note_to_beat(test_beat, 1, 1) != VALID_NOTE) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ if (add_note_to_beat(test_beat, 1, 0) != NOT_HIGHEST_NOTE) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 3: Accepting valid inputs.
+ if (add_note_to_beat(test_beat, 1, 2) != VALID_NOTE) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ return MEETS_SPEC;
+}
+
+// Test function for `count_notes_in_octave` this is broken
+int test_count_notes_in_octave(void) {
+ // Test 1: Counting invalid octaves.
+ Beat test_beat = create_beat();
+ add_note_to_beat(test_beat, 1, 1);
+ if (count_notes_in_octave(test_beat, -1) != 0) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ if (count_notes_in_octave(test_beat, 11) != 0) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 2: Counting valid octaves.
+ if (count_notes_in_octave(test_beat, 1) != 1) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ if (count_notes_in_octave(test_beat, 1) == 0) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 3: Counting without any notes
+ struct beat *empty_beat = create_beat();
+ if (count_notes_in_octave(empty_beat, 0) != 0) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 4: Counting the correct amount
+ add_note_to_beat(test_beat, 2, 1);
+ add_note_to_beat(test_beat, 2, 2);
+ if (count_notes_in_octave(test_beat, 2) != 2) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ add_note_to_beat(test_beat, 2, 3);
+ if (count_notes_in_octave(test_beat, 2) != 3) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ return MEETS_SPEC;
+}
+
+// These struct definitions are required for the following tests.
+// They access the fields directly instead of using other functions as tests,
+// which could be considered unsafe as they are not guaranteed to be correct.
+// We only want to test a single function, not other functions indirectly.
+struct track {
+ struct beat *head;
+ struct beat *cur;
+};
+
+struct beat {
+ struct note *notes;
+ struct beat *next;
+};
+
+// Test function for `add_beat_to_track`
+int test_add_beat_to_track(void) {
+ // Test 1: Test if the beat gets added correctly.
+ struct track *test_track = create_track();
+ struct beat *test_beat = create_beat();
+ add_beat_to_track(test_track, test_beat);
+ if (test_track->head == NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 2: Ensure the beat is a shallow copy.
+ if (test_track->head != test_beat) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 3: Test if the expected fields are NULL.
+ if (test_track->cur != NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ if (test_track->head->notes != NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 4: Ensure adding muliple beats works correctly.
+ struct beat *test_beat2 = create_beat();
+ add_beat_to_track(test_track, test_beat2);
+ if (test_track->head != test_beat2) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 5: Ensure the last field is still NULL.
+ if (test_track->head->next->next != NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ return MEETS_SPEC;
+}
+
+// Test function for `remove_selected_beat`
+int test_remove_selected_beat(void) {
+ // Test 1: Ensure the correct return type on a track with no selected beat.
+ struct track *test_track = create_track();
+ struct beat *test_beat = create_beat();
+ add_beat_to_track(test_track, test_beat);
+ if (remove_selected_beat(test_track) != TRACK_STOPPED) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 2: Ensure that no beat has been deleted after the previous test.
+ if (test_track->head == NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 3: Ensure the track is still playing after the attempted removal.
+ if (test_track->cur != NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 4: Ensure the correct return type on a track with a selected beat.
+ select_next_beat(test_track);
+ if (remove_selected_beat(test_track) != TRACK_STOPPED) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 5: Ensure the beat has been deleted correct after the previous test.
+ if (test_track->head != NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 6: Ensure the track is not playing after beat removal.
+ if (test_track->cur != NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ return MEETS_SPEC;
+}
+
+// Another struct definition which is required for testing below.
+// This can probably be considered bad practice as struct defintions should not
+// be simply copied across multiple files, but compared to the alternative
+// of invoking potentially broken functions, this is preferable.
+struct note {
+ int oct;
+ int key;
+ struct note *next;
+};
+
+// Test function for `add_musical_note_to_beat`
+int test_add_musical_note_to_beat(void) {
+ // Test 1: Ensure the beat is added to an empty beat.
+ struct beat *test_beat = create_beat();
+ add_musical_note_to_beat(test_beat, "2C#");
+ if (test_beat->notes == NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 2: Ensure the beat has the correct pitch.
+ if (test_beat->notes->oct != 2 || test_beat->notes->key != 4) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 3: Ensure a higher beat is added when appropriate.
+ add_musical_note_to_beat(test_beat, "2C##");
+ if (test_beat->notes->next == NULL) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ // Test 4: Ensure that a lower beat is added when appropriate.
+ add_musical_note_to_beat(test_beat, "2C");
+ if (test_beat->notes->oct != 2 || test_beat->notes->key != 3) {
+ return DOES_NOT_MEET_SPEC;
+ }
+ return MEETS_SPEC;
+}