Assignment 2 - Beats by CSE

version: 1.1.0 last updated: 2020-04-13 23:30: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. Each stage will have different marks weighting to other stages.

What is 'Beats by CSE'

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.

We will also be learning about how to write our own tests (very much like the autotests you've been using in COMP1511) so that we can verify whether our work matches the specification.

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 test_beats.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
test_beats.h
contains the declarations for functions which serve as small challenges to help you think about testing beats.c You cannot change test_beats.h
test_beats.c
contains a main function and other functions which are small challenges to help you think about testing test_beats.c Only put testing code in test_beats.c.
test_main.c
contains a main function and other functions that allow you to run the tests you create in test_beats.c You cannot change test_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/20T1/activities/cs_beats/beats.c .
cp -n /web/cs1511/20T1/activities/cs_beats/test_beats.c .
ln -s /web/cs1511/20T1/activities/cs_beats/main.c .
ln -s /web/cs1511/20T1/activities/cs_beats/test_main.c .
ln -s /web/cs1511/20T1/activities/cs_beats/beats.h .
ln -s /web/cs1511/20T1/activities/cs_beats/test_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

There are two ways you can compile CS Beats, depending on which functionality you would like:

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

  dcc -o beats beats.c main.c

To compile the test version you should compile the test_beats.c, beats.c and test_main.c files. This version will run all the tests you have created in test_beats.c. Use the following command:

  dcc -o test_beats test_beats.c beats.c test_main.c

Reference implementation

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

  1511 cs_beats
============================[ 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.

---------------------------[ Stage 4 ]---------------------------
  m <musical_note>
    Add the musical note to the beat being built.

---------------------------[ Stage 5 ]---------------------------
  X <range_length>
    Cut `range_length` notes, starting at the currently selected note, and
    paste them at the end of the track.
  V <range_length>
    Reverse the order of `range_length` notes, starting from the currently
    selected note.
[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 5 stages; each stage is not weighted the same.

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 note.

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

Now you have completed this stage, you should add tests to test_beats.c! There are autotests for this stage that run your test_beats.c code against a broken solution - see if you can figure out from the test description and autotests what is broken! See the Testing section for info.

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 to see what the correct behaviour is.

Now you have completed this stage, you should add tests to test_beats.c! There are autotests for this stage that run your test_beats.c code against a broken solution - see if you can figure out from the test description and autotests what is broken! See the Testing section for info.

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 exit).
    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 to see what the correct behaviour is.

Now you have completed this stage, you should add tests to test_beats.c! There are autotests for this stage that run your test_beats.c code against a broken solution - see if you can figure out from the test description and autotests what is broken! See the Testing section for info.

Stage 4

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

1511 autotest-stage 04 cs_beats

Musicians tend to use letters to represent their keys (for instance, 3 represents the key a musician would call C). In this stage, you will translate from a musical key into our version of a key.

// Add note to beat, given its 'musical notation'.
int add_musical_note_to_beat(Beat beat, char *musical_notation) {
    printf("add_musical_note_to_beat not implemented yet.");
    return VALID_NOTE;
}

You can use the following command to try out the above function:

'm' Command: Add a Musical Note

Usage: M <musical_note>.

Example: M 4G## or 3A.

Description: Add a Musical Note to the end of the Beat.

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

Now you have completed this stage, you should add tests to test_beats.c! There are autotests for this stage that run your test_beats.c code against a broken solution - see if you can figure out from the test description and autotests what is broken! See the Testing section for info.

Stage 5

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

1511 autotest-stage 05 cs_beats

In this stage, there are two challenges, both of which involve cutting ranges of beats - cutting a range of beats to the end of the track, and reversing a range of beats!

// 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;
}

You can use the following commands to try out the above functions:

'X' Command: Cut Range to End of Track

Usage: X <range_size>.

Example: X 2

Description: Take a range of Beats from a Track, and cut it to the end of the Track.

'V' Command: Reverse Range

Usage: V <range_size>.

Example: V 2

Description: Take a range of Beats and reverse their order.

Beats Tip #2: Reversing a Range Hint: beats.h says that you should do nothing if the range is less than one, but if the range is one, reversing a range will have no actual effect.

Errata: A previous version of beats.h implied that for reverse_range, you should return the number of beats you reversed. The sample solution, and tests, actually returned the number of beats you reversed, excluding the current beat. beats.h has now been updated to clarify this, and you should follow the behaviour of the tests and sample solution.

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

Testing

New: You can get access to the program we use to test your code by running 1511 setup_test_objects

In C compilation, a .o file is like a .c file that only machines can read. This means that after running the above command, you can test your tests against our program by compiling with the following command: dcc -o test_beats broken_main.o broken_beats.o test_beats.c.

To run our tests, you should run the command ./test_beats 1, where 1 is replaced by the test number (from 1 to 7).

As you implement functions in beats.c, you should add tests to test_beats.c .

These test functions will ask you to use the functions described in beats.h and test them. This means you will need to identify likely ways they could fail, and write your own code to check whether they have failed in that way. You can use the other functions from beats.h to detect whether a particular function is broken. For instance, you can use the return value of a function, and check that it does in fact return the correct return value. You can also use other functions to verify a function has worked correctly.

There are autotests which compile your test code against broken solutions, and makes sure that you detect they are broken. These tests are not exhaustive, and we will use more tests during marking.

You do not need to pass all these tests to get full marks for testing in the assignment, but if you fail these tests you will need to ensure your code would pick up other errors.

You do not need to have completed any parts of the assignment to write tests for it. That is, you could have only completed Stage 1, and you can still get full marks for the testing part of this assignment.

Both Stage 1 tests have been partially implemented for you to demonstrate how this should work. You should add at least two more tests to each of those Stage 1 functions, and tests to each other function in test_beats.c.

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!

Hall of Fame

You have three options for what to do to participate in the Hall of Fame. The Hall of Fame will be open for submissions until the first day of Term 2, so you are welcome to work on your code after Assignment 2 is complete.

  • Modify the Assignment: You can edit all the provided files to add more functionality. If you are keen to do this, talk to your tutor about your options, but your challenge should be equivalent to or harder than Stage 5 of this assignment.
  • Modify the Synthesizer: You can edit all the provided synthesizer code to add new types of sounds, or add functionality.
  • Create a track: You can submit a file that describes a Track to us, and we will host it on our website. The best tracks will be featured in the hall of fame. Submissions will be open shortly after the start of the assignment.

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 penalized, 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 penalized if your work is taken without your consent or knowledge.

Submission of Work

You are required to 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 below.

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.

All these intermediate versions of your work will be placed in a git repo and made available to you via a web interface at this URL, replace z5555555 with your zID:

https://gitlab.cse.unsw.edu.au/z5555555/20T1-comp1511-ass2_cs_beats/commits/master

This will allow you to retrieve earlier versions of your code if needed.

You submit your work like this:

give cs1511 ass2_cs_beats beats.c test_beats.c

It is not possible to submit your own beats.h, test_beats.h, test_main.c or main.c. You should not modify your copies of those files in any way.

Assessment Scheme

This assignment will contribute 25% to your final mark.

70% of the marks for this assignment will be based on the performance of the functions you write in beats.c.

10% of the marks for this assignment will come from the test_beats.c file. We will examine your own test cases to assess how many distinct cases they test, and whether they meet the descriptions in test_beats.h.

20% of the marks for assignment 2 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.

Here is an indicative marking scheme.

100 (HD)Completely working implementation of Stages 1-5; with beautiful code and all test_beats.c.
85 (HD)Completely working implementation of Stages 1-4; with very readable code and a test suite with good coverage in test_beats.c.
75 (DN)Completely working implementation of Stages 1-3; with readable code and a test suite with reasonable coverage in test_beats.c.
65 (CR)Completely working implementation of Stages 1 and 2; with understandable code and a test suite that touches all stage 1-2 functions in test_beats.c.
50 (PS)Completely working implementation of Stage 1; with an attempt at readable code, and some tests added to test_beats.c.
40-50Partially working implementation of Stage 1.
-70%Knowingly providing your work to anyone and it is subsequently submitted (by anyone).
-70%Submitting 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 lecturer 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 Sunday 26 April 2020 18: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
(2020-04-05 20:30:00)
  • Assignment released.
Version 1.0.1
(2020-04-12 13:30:00)
  • Added hint re reversing a linked list of size 1.
Version 1.1.0
(2020-04-13 23:30:00)
  • Added some clarifications and additions, described in the WebCMS notice.

Credits

Created by Tom Kunc and Marc Chee and with contributions from Liz Willer, Dean Wunder, and Tammy Zhong