Assignment 2 - CS Beats

version: 1.5 last updated: 2021-04-19 20:00:00

Introduction

Making music using computers is something that's been with us for many years. Even the Commodore 64 from 1982 had the ability to synthesise sounds and as a programmable computer, it was one of the first tools for making digital music that was publicly available. Since then, the advent of MIDI (also in the early 1980s) and more advanced digital synthesisers allowed the digital music boom of the 1990s, starting in dance music and spreading to many other genres.

If we want to look at it a certain way, we could say that Computer Science is historically responsible for the rise of dubstep.

The assignment is split into 5 stages -- three core stages, and two extension stages. Each stage will have different marks weighting to other stages; but every stage is worth marks. This is very similar to how Assignment 1 was marked.

What is 'CS Beats'

For this assignment, you will be implementing a program that follows some of the ideas of digital music composition and production. You will also be writing some of your own automated testing to verify that your code works.

You will be writing a tool in C that allows us to put together representations of musical "tracks". Each track is divided into "beats" and each beat can contain a list of "notes".

While we won't necessarily be making genuine music (Daft Punk is safe from our challenge to their supremacy), we will be learning a lot about putting sequences of structures together in linked lists.

You don't need to have studied any music to complete this assignment, though students who have studied music may be familiar with some of the terms we use to describe concepts.

What's Provided?

Supplied Code

You can run 1511 setup_cs_beats to copy all of the files to your CSE account. You can also download all the files as a zip file here.

There are three types of files we have in CS Beats:

  • Main Files (ending in main.c) contain code written for you to test your code. You don't need to read them, though it may be useful if you're curious how the assignment works. They do all the input and output for you. You cannot change a main file. Each of these files contains a main() function and only one of them will be used at a time.
  • Header Files (ending in beats.h) contain defined constants, and function prototypes. They also contain lots of comments that explain what functions should do. If you are confused about what a function should do, read the header file. You cannot change a header file.
  • Implementation Files (ending in beats.c) contain stubs of functions you are meant to complete. These are the only functions you can change. You don't need to scanf anywhere. You also don't need to print anything, except where you're explicitly told to.
Here is a detailed description of each file.
beats.h
contains declarations for all functions you need to implement for this assignment. It also contains all the hash defines that you need to return for this assignment. You cannot change beats.h
beats.c
contains stubs for all the functions you need to implement for this assignment. This file does not contain a main function. You will need to compile it along with either main.c or ext_main.c. Put all your CS Beats code in beats.c.
main.c
contains a main function and other functions that allow you to interactively test the functions you implement in beats.c You cannot change main.c

You can also link and copy the supplied files into your CSE account using commands like these:

mkdir ass2
cd ass2
cp -n /web/cs1511/21T1/activities/cs_beats/beats.c .
ln -s /web/cs1511/21T1/activities/cs_beats/main.c .
ln -s /web/cs1511/21T1/activities/cs_beats/beats.h .

The ln commands create symbolic links to a file in class account, so you are always using the latest version.

How to compile CS Beats

To compile you should compile only the beats.c and main.c files. This version will let you interactively call the functions you have written. Autotests have been written to test your code in this way. Use the following command:

  dcc -o beats beats.c main.c

Reference implementation

A reference implementation 1511 cs_beats_ref is available to help you understand the assignment.

  1511 cs_beats_ref
============================[ CS bEats ]============================
Welcome to CS bEats! Type '?' to see help.
====================================================================

[001] Beat Being Constructed:
[001]: ?
============================[ Help ]============================
  ?
    Show this help screen
  q
    Quit this program.

---------------------------[ Stage 1 ]---------------------------
  a <octave_number> <note_number>
    Add a note to the current beat.
  p
    Print the beat you are constructing (happens automatically).
  c <octave>
    Count the number of notes in the beat you are constructing that
    are in the given octave.

---------------------------[ Stage 2 ]---------------------------
  A
    Adds the beat you are building to the track, after the currently
    selected beat. If there is no selected beat, the beat you are
    building becomes the first beat. The beat you are building is
    cleared, ready to build another beat.
  P
    Print the whole track, beat by beat.
  >
    Move the currently selected beat to the next beat.
    Stop the track if the currently selected is the last beat.
    If the track is stopped, this command sets the currently selected
    beat to the first beat.
  C
    Count the number of beats left in the track.

---------------------------[ Stage 3 ]---------------------------
  R
    Remove the currently selected beat, if there is one.

[002]: q

Your beats.c (compiled with the supplied file main.c) should match the behaviour of the reference implementation.

Provision of a reference implementation is a common method to provide an operational specification, and it's something you will likely need to do outside UNSW.

If you discover what you believe to be a bug in the reference implementation, report it in the class forum. We may fix the bug or indicate that you do not need to match the reference implementation's behaviour in this case.

The Tasks

For this assignment, we are asking you to implement a small music composition tool. The task is split into 3 stages; and then an extension section with two parts. Each stage is not weighted the same. The Extension component is worth marks -- you cannot get full marks without completing it.

New -- Reflection #4

Before you start the assignment, consider starting your Reflection 4 early. While you'll only have to do it during week 10, it contains an optional section to help you plan for your second assignment. You can write this section first, and come back to the rest of the reflection next week!

Find the reflection instructions here.

Core -- Stage 1

You can run the autotests for Stage 1 by running the following command:

1511 autotest-stage 01 cs_beats

For stage one, there are two structs you will need to be familiar with:

  • struct beat, which represents a particular "time" within the music you have written. It contains a list of Notes, as well as a pointer to another struct beat, which for this stage will remain NULL.
  • struct note, which represents a particular note within the music you have written. It contains an "octave" and a "key", which are both positive integers that describe the pitch of each note (notes can have a pitch that is higher or lower than other notes). It also contains a next pointer, which either points to another struct note in the same Beat, or to NULL.
If you look in beats.h you will discover this definition: typedef struct beat *Beat

In other words the type Beat is the same as struct beat *.

struct beat is defined at the top of beats.c.

struct note is not given a typedef, and will never be referred to outside your beats.c.

The provided main function, located in main.c, automatically creates an empty beat when you run it. It calls the create_beat function, which has been written for you. You can then use the 'a' command to add a new Note. The 'a' command adds a Note to the end of a Beat's list of Notes, if the values it is given are valid. See beats.h for a full description of which values are valid.

'a' Command: Add A Note

Usage: a <octave> <note>

Examples:

  • a 3 10
  • a 7 0

Description: Add a Note to the beat that is being constructed, by the main function. The new Note has the specified octave and key.

Comments: The Note you add must be valid according to the rules in beats.h

When you are given a Beat created by create_beat, it will look like this:

After adding a Note with Octave 3, Key 11, the Beat would look like this:

After adding a Note with Octave 4, Key 2, the Beat would look like this:


When you compile and run the provided code for the first time, you will see the following:

dcc -o beats main.c beats.c
./beats
============================[ CS bEats ]============================
Welcome to CS bEats! Type '?' to see help.
====================================================================

[001] Beat Being Constructed: print_beat not implemented yet.
[001]: a 1 1
add_note_to_beat not implemented yet.

[002] Beat Being Constructed: print_beat not implemented yet.
[002]: q

When you run the a command, this function in beats.c is called:

// Add a note to the end of a beat.
int add_note_to_beat(Beat beat, int octave, int key) {
    printf("add_note_to_beat not implemented yet.\n");
    return VALID_NOTE;
}

After every command you give, the print_beat function will be called

// Print the contents of a beat.
void print_beat(Beat beat) {
    printf("print_beat not implemented yet.\n");
    return;
}

Your first task is to implement add_note_to_beat and print_beat. They should follow the specification given in beats.h.

// Add a new Note to the Beat, if it's a valid octave & key, and if
// it's larger than the Notes in the Beat. Otherwise, return an
// error code.
//
// New Notes are only allowed to be added to a Beat if:
/// - They have a valid octave. 
//    An octave is valid if it is a non-negative integer less than 10. 
//    For example:
//      - -1 is negative, so not a valid octave.
//      - 10 is not less than 10, so not a valid octave.
//      - 0, 1, ..., 8, 9 are all valid octaves.
//  - They have a valid key. 
//    A key is valid if it is a non-negative integer less than 12. 
//    For example:
//      - -1 is negative, so not a valid key.
//      - 12 is not less than 12, so not a valid key.
//      - 0, 1, ..., 10, 11 are all valid keys.
//  - The new Note is higher than the Notes in the beat, that is:
//    - The new Note is not a lower octave than any Note in the Beat.
//      For example, in a beat with one Note with octave 2, and note 5:
//        - You could not add a note with octave 1 and note 6,
//          since the Beat already has a higher octave.
//        - You could add a note with octave 2 and note 6, since
//          the Beat's highest octave is 2.
//    - If the octaves are the same, the new Note's key must be higher.
//      For example, in a beat with one note; octave 2, and key 5
//        - You could not add a Note with octave 2 and key 4,
//          since there is a Note with octave 2 and a higher note (5).
//        - You could not add a Note with octave 2 and key 5,
//          since there is already a Note with octave 2 and key 5.
//        - You could add a note with octave 2 and key 6, since
//          there is no Note with octave 2, and a higher key.
//
// The new Note should be added to the end of the Beat's
// list of notes. This means that you should add the Note
// directly after the Note that was added the last time
// `add_note_to_beat` was called.
//
// If a Note exists in the Beat already, you should not add it again. 
// For instance:
// If a Beat contains the Note with octave 3 and key 11, you should
// not add another Note with the same octave and key. You should return
// `NOT_HIGHEST_NOTE`.
//
// Notes in a Beat should always be sorted in ascending order, first by
// octave, then by key. 
// For instance, "3 10" comes before "3 11" which comes before "4 0".
//
// `add_note_to_beat` will be passed:
// - `beat`, a pointer to a Beat, created with `create_beat`. You
//   are guaranteed `beat` will not be NULL.
// - `octave`, an int that may or may not be a valid octave.
// - `key`, an int that may or may not be a valid note.
//
// `add_note_to_beat` should return one of the following #defines from beats.h:
// - `INVALID_OCTAVE` if the octave is not valid.
// - `INVALID_KEY` if the octave is valid, but the key is not.
// - `NOT_HIGHEST_NOTE` if the key is valid, but is not the
//    highest note in the Beat.
// - `VALID_NOTE` otherwise.
//
int add_note_to_beat(Beat beat, int octave, int key);
// Print out the beat passed to the function.
//
// print_beat should print out all the Notes in the given Beat on one line.
// Each Beat consists of two numbers, the octave then the key.
// They will be separated by the characters " | " and the line will end in
// a newline ('\n').
// The notes will be printed out in ascending order, which is the same
// order that the list should be in.
//
// For a Beat containing no notes, `print_beat` should print an empty line:
// "\n"
//
// For a Beat containing a note with octave 0, and note 9,
// `print_beat` should print:
// "0 09\n"
//
// For a Beat containing:
//   - A Note with octave 0, and note 10.
//   - A Note with octave 2, and note 10.
// `print_beat` should print:
// "0 10 | 2 10\n"
//
// For a beat containing:
//   - A Note with octave 1, and note 10.
//   - A Note with octave 2, and note 10.
//   - A Note with octave 3, and note 10.
// "1 10 | 2 10 | 3 10\n"
//
// `print_beat` will be passed:
// - `beat`, a pointer to a Beat, created with `create_beat`, and possibly
//   with notes added by `add_note_to_beat`. You are guaranteed `beat`
//   will not be NULL.
//
// `print_beat` will not return a value, but should print to stdout.
void print_beat(Beat beat);

Beats Tip #0: Octaves and Keys

In music, the word "Octave" refers to a group of notes that are similarly high or low. In modern western muscial theory, there are 12 keys in any octave. Notes that are the same key, but in a different octave, sound very similar.

This is not that different from thinking about numbers. You could treat the Octaves like 10s and the keys like 1s, except there is a limit to the number of Octaves we are using and we have 12 keys instead of 10 digits.

Throughout the spec, you'll see the words octave and key a lot, but you don't need to know anything about music to get this working.

Hint: your code should check that key is non-negative, and less than 12, and that octave is a non-negative single digit integer.

Beats Tip #1: Malloc

Malloc, the function to allocate memory, can return NULL. Normally, this would mean that your computer (or your user) has run out of the memory it's able or allowed to give you. For instance, you can't malloc a 32 trillion integer array, because your computer probably doesn't have 32 trillion integers worth of memory.

While it's standard practice to check for malloc returning NULL, we do not require this, and we will not test you on this case. If you want to check, you are free to choose whatever behaviour you would like if the allocation fails.


When those functions are successfully implemented, you should see this:

dcc -o beats main.c beats.c
./beats
============================[ CS bEats ]============================
Welcome to CS bEats! Type '?' to see help.
====================================================================

[001] Beat Being Constructed:
[001]: a 1 1
Added note successfully!

[002] Beat Being Constructed: 1 01
[002]: q

You are also being asked to complete another function as part of Stage One, count_beats_in_octave, which takes a Beat, and tells you how many Notes are in a particular octave. See beats.h for full details.

'c' Command: Count Notes in an Octave

Usage: c <octave>

Examples: c -1; or c 7

Description: Return the number of Notes in the Beat being constructed that are in the given octave.

// Count the number of notes in a beat that are in a given octave.
int count_notes_in_octave(Beat beat, int octave) {
    printf("count_notes_in_octave not implemented yet.\n");
    return 0;
}

See beats.h for information on each function.

Use this interactive explorer to see how Stage 1 should behave:

X

Core -- Stage 2

You can run the autotests for Stage 2 by running the following command:

1511 autotest-stage 02 cs_beats

For this stage, there's an additional struct to be aware of: struct track. This struct contains a list of Beats. Each Beat now either points to the Beat that comes after it, or NULL if it is the last Beat in the Track.

The main function in main.c calls create_track. It returns a struct track, which can be represented like this:

You can then call the add_beat_to_track function using the 'A' (capital A) command. If you had constructed a Beat containing two Notes, it might look like this:

'A' Command: Add A Beat to the Track

Usage: A

Description: Add a Beat to the Track after the current Beat, or as the first Beat if there is no currently selected Beat in the Track.

Comments: This function does not change the currently selected Beat.

You can then call the select_next_beat function using the '>' (right angle bracket, or greater than sign) command. We say a track that has a selected Beat (like this one now does) is 'playing'. This now 'playing' Track might be represented like this:

'>' Command: Select the Next Beat in the Track

Usage: >

Description: Select the Beat after the currently selected Beat in the Track, or stop the Track and deselect any Beats if this is the last Beat in the Track, or select the first Beat if the Track is stopped.

Calling the add_beat_to_track function will now insert the Beat after the currently selected beat.

Note that inserting doesn't change the currently selected beat, so we can insert multiple beats after the same beat, like so:

At this point, we could call count_beats_left_in_track, using the 'C' command, which would tell us that there are 2 beats left in the track.

'C' Command: Count Beats Left In Track

Usage: C

Description: Return the number of Beats after the current Beat.

Calling the print_track function on this Track, using the 'P' (capital P) command would show:

>[1] 3 11 | 4 02
 [2]
 [3] 4 01

Converted to commands, the above process could be described as:

dcc -o beats main.c beats.c
./beats
============================[ CS bEats ]============================
Welcome to CS bEats! Type '?' to see help.
====================================================================

[001] Beat Being Constructed:
[001]: a 3 11
Added note successfully!

[002] Beat Being Constructed: 3 11
[002]: a 4 2
Added note successfully!

[003] Beat Being Constructed: 3 11 | 4 02
[003]: A

[004] Beat Being Constructed:
[004]: P
 [1] 3 11 | 4 02

[005] Beat Being Constructed:
[005]: >
Moved to next Beat.

[006] Beat Being Constructed:
[006]: P
>[1] 3 11 | 4 02

[007] Beat Being Constructed:
[007]: a 4 1
Added note successfully!

[008] Beat Being Constructed: 4 01
[008]: A

[009] Beat Being Constructed:
[009]: P
>[1] 3 11 | 4 02
 [2] 4 01

[010] Beat Being Constructed:
[010]: A

[011] Beat Being Constructed:
[011]: P
>[1] 3 11 | 4 02
 [2]
 [3] 4 01

[012] Beat Being Constructed:
[012]: q

For Stage 2, you will need to implement the following functions in beats.c:
// Return a malloced track with fields initialized.
Track create_track(void) {
    // Note: there is no fprintf in this function, as the
    // Stage 1 autotests call create_track but expect it to return NULL
    // (so this function should do nothing in Stage 1).

    return NULL;
}
// Add a beat after the current beat in a track.
void add_beat_to_track(Track track, Beat beat) {
    printf("add_beat_to_track not implemented yet.\n");
    return;
}
// Set a track's current beat to the next beat.
int select_next_beat(Track track) {
    printf("select_next_beat not implemented yet.\n");
    return TRACK_STOPPED;
}
// Print the contents of a track.
void print_track(Track track) {
    printf("print_track not implemented yet.\n");
    return;
}
// Count beats after the current beat in a track.
int count_beats_left_in_track(Track track) {
    printf("count_beats_left_in_track not implemented yet.\n");
    return 0;
}

Again, see beats.h for information on what each function should do, and use 1511 cs_beats_ref to see what the correct behaviour is.

Core -- Stage 3

You can run the autotests for Stage 3 by running the following command:

1511 autotest-stage 03 cs_beats

For this stage, we need to free memory and remove beats from the track!

For this, you need to complete the functions:

// Free the memory of a beat, and any memory it points to.
void free_beat(Beat beat) {
    // Note: there is no printf in this function, as the
    // Stage 1 & 2 autotests call free_beat but don't check whether
    // the memory has been freed (so this function should do nothing in
    // Stage 1 & 2, rather than print an error).
    return;
}
// Free the memory of a track, and any memory it points to.
void free_track(Track track) {
    // Note: there is no printf in this function, as the
    // Stage 1 & 2 autotests call free_track but don't check whether
    // the memory has been freed (so this function should do nothing in
    // Stage 1 & 2, rather than print an error).
    return;
}
// Remove the currently selected beat from a track.
int remove_selected_beat(Track track) {
    printf("remove_selected_beat not implemented yet.");
    return TRACK_STOPPED;
}

You can use the following commands:

'R' Command: Remove the Currently Selected Beat

Usage: R

Description: Remove the currently selected beat, if it exists.

Again, see beats.h for information on what each function should do, and use 1511 cs_beats_ref to see what the correct behaviour is.

Extension

Once you have completed Stage 3 of Assignment 2, you can submit the following Extension Work. Before you do, you should ensure that the following is true:

  • None of your functions should be longer than 100 lines.
  • Your code should pass 1511 style with no issues. If it has any issues, you should discuss them with a tutor before proceeding.
  • All your normal labs should be complete.
  • While not essential, you should make sure that other Uni work you have is prioritized over this work.

COMP1511 reserves the right to disqualify your extension work if you do not meet the above requirements. Working on extension work is not an excuse for failure to submit your regular assignment, nor is it grounds for extension. Plagiarism of extension work will be particularly penalized.

The intent of extension work is to expose you to different parts of the software development experience. The major differences between regular work and extension work are:

  1. Extension work does not provide you a detailed specification -- we expect you to determine what the correct behaviour is from the sample solution.
  2. Extension work also requires you to test and document your own work -- this is very similar to how software developers at companies are expected to act.

While it is extension work, you must still follow the COMP1511 style guide, and your program must compile and run on CSE machines, using dcc.

Supplied Code

You can run 1511 setup_cs_beats_ext to copy all of the files to your CSE account.

Here is a detailed description of each file.

ext_beats.h
contains declarations for all functions you need to implement for the extension assignment. It also contains every function declaration you need for the regular assignment. You should use it instead of beats.h. You cannot change ext_beats.h
ext_beats.c
contains stubs for all the functions you need to implement for the extension This file does not contain a main function. You will need to compile it along with ext_main.c. Note that you should copy over the code from beats.c into this file, as it only contains funciton stubs for the extension work. Put all your CS Beats Extension code in ext_beats.c.
ext_main.c
contains a main function and other functions that allow you to interactively test the functions you implement in ext_beats.c You cannot change ext_main.c
ext_save.h
contains a description of functions that allow you to save and load a string. You will need these functions for Stage 5. You cannot change ext_save.h
ext_save.c
contains functions that allow you to save and load a string. You will need these functions for Stage 5, however you should not rely on how they are implemented. You cannot change ext_save.c
cs_beats_docs.txt
Is a text file that you will write test cases in, as well as documentation. Put all your tests and documentation in this file..

You can also link and copy the supplied files into your CSE account using commands like these:

mkdir ass2
cd ass2
cp -n /web/cs1511/21T1/activities/cs_beats_ext/ext_beats.c .
ln -s /web/cs1511/21T1/activities/cs_beats_ext/ext_main.c .
ln -s /web/cs1511/21T1/activities/cs_beats_ext/ext_beats.h .
ln -s /web/cs1511/21T1/activities/cs_beats_ext/ext_save.h .
ln -s /web/cs1511/21T1/activities/cs_beats_ext/ext_save.c .

The ln commands create symbolic links to a file in class account, so you are always using the latest version.

How to compile CS Beats

To compile you should compile the .c files that start with ext_. This version will let you interactively call the functions you have written. Autotests have been written to test your code in this way. Use the following command:

  dcc -o ext_beats ext_beats.c ext_main.c ext_save.c

Reference implementation

A reference implementation 1511 cs_beats_ext is available to help you understand the assignment.

Marking

There are two extensions provided for this assignment. Each extension asks you to implement more complicated functionality. The second extension is more complicated than the first. The two extensions will not be tested within the same test case. We require you to submit three things:

  1. For each extension you complete, a description of the work you have done, around 100 words long.
  2. For each extension you complete, two test cases which you have written to test the functionality of your extension work.
  3. One .c file which contains the code for your extension work. This will be a seperate file to your beats.c

Extension activities will be marked in three stages:

Qualification Stage: Before the due date, you must ensure your extension work "qualifies" to be marked. To "qualify", you must:

  1. For each extension you complete, have at least 100 words to explain what changes you have made to your regular submission.
  2. For each extension you complete, you must have written two test cases. These test cases will be given as input to both your program and the sample solution. Each of your test cases must produce the same output from the sample solution as from your code.
  3. For each extension you complete, your code must pass the two given "qualification tests". These tests will be run when you submit your code, so you will know if you pass them before the due date.

Peer-Testing Stage: At this stage, after the due date, a subset of student tests will be run against every qualified extension submission. Marks will be awarded based on the percentage of tests you pass. In other words, other student's tests will be run against your own code.

Test Evaluation Stage: At this stage, after the due date, every test will be run against a subset of qualified extension submissions. If you didn't get full marks during Peer-Testing, you can make up for it by writing tests that other students do not pass. In other words, even if you fail some other tests, if you write a test that catches other people out, you can still get full marks!

Extension -- Stage 4: Merging Notes

Music often has interesting rhythms, which you can now explore by merging beats. In this extension, you will be told to merge beats_to_merge beats into merged_beats beats. You will need to merge everything from the currently selected beat, up to the beat beats_to_merge after the selected beat. After merging, there should be at most merged_beats number of new beats. You will need to use the reference solution to discover exactly how this merging works.

As an example, if beats_to_merge = 4 and merged_beats = 2; and the beats in the Track are {1 01 -> X} -> {1 02 -> X} -> {1 03 -> X} -> {1 04 -> X}. The result of merging those four beats would be {1 01 -> 1 02 -> X} -> {1 03 -> 1 04 -> X} -> X

'M' Command: Merge Beats

Usage: M <beats_to_merge> <merged_beats>

Examples:

  • M 3 1
  • M 4 2

Description: Replace the current beat, and beats_to_merge - 1 beats after it, with merged_beats new beats. For exact details on how merging works, investigate the reference solution...

To make this problem easier, for 50% of the marks available, you will only need to merge multiple beats into exactly one beat. Specifically, in 50% of test cases, merged_beats will be equal to 1.

When writing your tests, you will need to ensure that your first test only checks merging beats into one beat. Your second test can merge any number of beats into any other number of beats.

You can access the reference solution for merging notes with:

1511 cs_beats_merge_ext

You can access the reference solution for merging notes into exactly one note with:

1511 cs_beats_merge_one_ext
New:

Extension work is submitted differently to regular work. To test your extension work, run:

 1511 autotest ass2_cs_beats_ext ext_beats.c cs_beats_docs.txt 

To submit your work, run:

 give cs1511 ass2_cs_beats_ext  ext_beats.c cs_beats_docs.txt 

Extension -- Stage 5: Save and Load Track

Most programs have a way of saving your work, so you can work on multiple projects at once, and you don't have to worry about your dog pulling out the power cable. In order to save your next master-work, in this stage you will implement saving and restoring.

To help you in this, we have provided you the save_string and load_string functions in ext_save.c and ext_save.h. You must use these functions (i.e. you may not write your own versions of these functions to do the same thing); and you should not rely on their behaviour (we might change the implementation during testing).

Your program should save the current Track using the save_string function; and be able to restore the current Track, using the load_string function, even after the program has been restarted. To get any marks for this stage, you must implement both functions (i.e. there are no partial marks for just saving).

Note: Due to an error in our autotests, it was previously not possible to load tracks that hadn't been saved when writing your own tests. It is now possible to test that case, and your program should be able to test it.

'S' Command: Save Track

Usage: S <save_name>

Examples:

  • S new_creation
  • S new_creation_fixed_2_final

Description: Call save_string to store the current state of the program.

'L' Command: Load Track

Usage: L <save_name>

Examples:

  • L new_creation
  • L new_creation_fixed_2_final

Description: Call load_string and replace the current Track with one identical to the saved Track.

Note: the sample solution saves Tracks in a certain way, but we have deliberately made it difficult to understand by using not very advanced encryption techniques. You do not need to save Tracks in the same way that the sample solution does!. We strongly encourage you to design a system for saving a Track yourself.

You can access the reference solution for saving notes with:

1511 cs_beats_save_ext
New:

Extension work is submitted differently to regular work. To test your extension work, run:

 1511 autotest ass2_cs_beats_ext ext_beats.c cs_beats_docs.txt 

To submit your work, run:

 give cs1511 ass2_cs_beats_ext ext_beats.c cs_beats_docs.txt 

Frequently Asked Questions

  • Can I share my test cases? You are welcome to discuss ways of breaking the reference solution; but you should not share the two test cases you submit with other people, or submit copies of other people's tests. If you want to submit tests with a similar idea, that's fine.
  • Do I have to check for memory leaks? Yes.
  • Can I rely on the functionality of saving things. Yes; everything in save.h can be taken as reliable. We just don't want you to rely on save.c.
  • Can I use string.h Yes; an earlier version of the code said this was not allowed; however it since we didn't enforce it we're going to allow you to use string.h.
  • What do I do if I receive an invalid name when saving a string? You can pretend that all names are valid. When you pass an invalid name to save_string
  • , it is just ignored; and as such any calls to load_string return NULL.
  • Is there a maximum number of nodes in my tests? No, but tests have a maximum size of 100000 characters. Thus, you are limited in how much you can create in that space.

Other Useful Tools

File Redirection

Every time you run your application, instead of re-typing all the details of each given Beat you are adding to the Track, you can store the input for your application in a separate file, and run it using:

./beats < test1.in > test1.out

Then check that the new (created by >) file test1.out contains the correct information given the input file.

A example test1.in might be

a 1 0
p
And the output would be:
============================[ CS bEats ]============================
Welcome to CS bEats! Type '?' to see help.
====================================================================

[001] Beat Being Constructed:
[001]: Added note successfully!

[002] Beat Being Constructed: 1 00
[002]: 1 00

[003] Beat Being Constructed: 1 00
[003]: 

Synthesizer by CSE

This assignment comes with a ready-built synthesizer, which allows you to turn your track into an actual sound file you can share with your friends, and with the course.

You can run the synthesizer with the command:

1511 cs_synth input.beats output.wav

The synthesizer takes input in the form of a .beats file. A beats file looks like this:

100
[1] 1 10 | 1 11
[2] 2 10 | 2 11

The first line is the speed, in beats per minute. 60 is very slow, 300 is very fast. The rest of the file looks the same as the output of calling print_track.

You are able to automatically generate one by using the 1511 cs_beats_synth command to have access to the S command (to save a beats file), or by replacing your main.c file with the synth_main.c file.

You can see the code for the synthesizer by running:

1511 setup_synth

This gives you four files:

synth_main.c
This file can replace your main.c. It lets you use the S command to save a .beats file (which you can then transform into a sound file).
make_wav.c
This is a piece of code that writes a wav file (which is a type of music format).
make_wav.h
This exposes the functions from make_wav.c.
synth.c
This conatins a main function that you can compile to turn a .beats file into a .wav file. This has all the logic for making sound, so this is probably what you would need to change if you want to change something.

You can compile the synthesizer by running:

dcc -o cs_synth synth.c make_wav.c -lm
./cs_synth input.beats output.wav

If you are using VLab or SSH, there is no easy way to play sound. To play sound, you can compile and upload your track to the internet. Use the following command to do that:

1511 upload_track input.beats
Configuring your ~/public_html/beats folder.
You can now see your submissions at https://web.cse.unsw.edu.au/~ZID/beats/gallery/

You can then go to that URL to see your tracks. You can also share that link around to your friends!

Hints

One useful trick is that you do not need to include the numbers on each line of the .beats file. If you manually edit the .beats, you can ignore the numbers on the left hand side of the ].

Another is that if the octave is between 10 and 19, it is treated as an octave between 0 - 9, but it will be treated as shorter than other notes.

The "click" you may hear in the outputted music file is an unfortunate bug in how physics works. We may release updates to the main file if we can figure out how to implement it better. If you have suggestions -- let Tom know!

Assessment

Attribution of Work

This is an individual assignment. The work you submit must be your own work and only your work apart from exceptions below. Joint work is not permitted.

You may use small amounts (< 10 lines) of general purpose code (not specific to the assignment) obtained from site such as Stack Overflow or other publically available resources. You should attribute clearly the source of this code in a comment with it.

You are not permitted to request help with the assignment apart from in the course forum, help sessions or from course lecturers or tutors.

Do not provide or show your assignment work to any other person (including by posting it on the forum) apart from the teaching staff of COMP1511.

The work you submit must otherwise be entirely your own work. Submission of work partially or completely derived from any other person or jointly written with any other person is not permitted. The penalties for such an offence may include negative marks, automatic failure of the course and possibly other academic discipline. Assignment submissions will be examined both automatically and manually for such submissions.

Relevant scholarship authorities will be informed if students holding scholarships are involved in an incident of plagiarism or other misconduct. If you knowingly provide or show your assignment work to another person for any reason, and work derived from it is submitted you may be penalised, even if the work was submitted without your knowledge or consent. This may apply even if your work is submitted by a third party unknown to you.

Note, you will not be penalised if your work is taken without your consent or knowledge.

Submission of Work

You are required to test and/or submit intermediate versions of your assignment. Every time you work on the assignment and make some progress you should copy your work to your CSE account and submit it using the give command, or autotest it using 1511 autotest

It is fine if intermediate versions do not compile or otherwise fail submission tests.

Only the final submitted version of your assignment will be marked. You must give when you have completed your assignment.

If you would like to retrieve your old work, you can go to: View Autotests/Submissions/Marking and click the yellow bubble next to ass2_cs_beats. This will show you a timeline of all the code you have submitted recently.

You submit your work like this:

give cs1511 ass2_cs_beats beats.c

The only file you should modify is beats.c. It is not possible to add new files, or modify the other files we have given you. If you believe you need to modify those files, you have misunderstood the specification. You should not modify your copies of those files in any way.

Assessment Scheme

This assignment will contribute 25% to your final mark.

65% of the marks for this assignment will be based on the performance of the code you write in cs_beats.c

15% of the marks for this assignment will come from hand marking of the readability of the C you have written. These marks will be awarded on the basis of clarity, commenting, elegance and style. In other words, your tutor will assess how easy it is for a human to read and understand your program. The utility 1511 style can help with this!

20% of the marks for this assignment will be based on the performance of any code or tests you write for the Extension component.

This is summarised in the table below:

Core Extension
Performance Style Activity 1 Activity 2
65% 15% 10% 10%
Worth 25% of course mark

Marks for your performance will be allocated roughly according to the below scheme.

100% for Performance Completely working implementation of Stages 1-3
80% for Performance Completely working implementation of Stages 1 and 2
65% for Performance Completely working implementation of Stage 1
50% for Performance Partially working implementation of Stage 1 -- everything except counting notes in octave.

Marks for your style will be allocated roughly according to the scheme below

100% for Style Perfect style
90% for Style Great style, almost all style characteristics perfect.
80% for Style Good style, one or two style characteristics not well done.
70% for Style Good style, a few style characteristics not well done.
60% for Style OK style, an attempt at most style characteristics.
<= 50% for Style An attempt at style.

For the extension, your work Must Qualify to receive marks. If it does not qualify (i.e. it does not pass the autotests for that extension activity) it will not receive marks. If it does qualify, it will be marked based on the percentage of other students' tests it passes. You can get additional marks for the number of other students' code that you break. The scaling of the marks for those code-breaking tests will be determined after the due date.

Note that the following penalties apply to your total mark for plagiarism:
0 for the assignmentKnowingly providing your work to anyone and it is subsequently submitted (by anyone).
0 for the assignmentSubmitting any other person's work. This includes joint work.
0 FL for COMP1511Paying another person to complete work. Submitting another person's work without their consent.

The following is an indicative list of characteristics of your program that will be assessed, though your program will be assessed wholistically so other charactersitics may be assessed too:

  • Header Commenting.
  • Consistent, sensible indenting.
  • Using Blank Lines & Whitespace.
  • Using constants.
  • Decomposing code into functions where relevent (especially after Stage 2).
  • Using comments effectively (at least at top of functions, and not containing irrelevant info).

The course staff may vary the assessment scheme after inspecting the assignment submissions but it will remain broadly similar to the description above.

Due Date

This assignment is due 23 April 2021 20:00:00

If your assignment is submitted after this date, each hour it is late reduces the maximum mark it can achieve by 2%. For example if an assignment worth 78% was submitted 5 hours late, the late submission would have no effect. If the same assignment was submitted 15 hours late it would be awarded 70%, the maximum mark it can achieve at that time.

Change Log

Version 1.0
(2021-04-07 22:30:00)
  • Assignment draft released.
Version 1.1
(2021-04-07 09:30:00)
  • Assignment officially released
  • Autotests released
  • Fixed main.c to recognise // as a comment in input.
Version 1.2
(2021-04-07 09:30:00)
  • Minor typo fixes.
  • Extension text for merging updated to be simpler.
Version 1.3
(2021-04-15 19:30:00)
  • Update extension autotests.
Version 1.4
(2021-04-19 00:30:00)
  • Answer some questions about Extension work.
Version 1.5
(2021-04-19 20:00:00)
  • Update ext_main.c such that testing a non-extant save doesn't cause an autotest error.

Credits

Created by Tom Kunc.

Credits to Marc Chee, Tammy Zhong, Dean Wunder, Ollie Xue and Tammy Zhong for this assignment.