aboutsummaryrefslogtreecommitdiff
path: root/comp1511/cs_beats/beats.c
diff options
context:
space:
mode:
authorNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-13 18:00:17 +1100
committerNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-13 18:00:17 +1100
commit98cef5e9a772602d42acfcf233838c760424db9a (patch)
tree5277fa1d7cc0a69a0f166fcbf10fd320f345f049 /comp1511/cs_beats/beats.c
initial commit
Diffstat (limited to 'comp1511/cs_beats/beats.c')
-rw-r--r--comp1511/cs_beats/beats.c412
1 files changed, 412 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;
+}