CSC 161 Grinnell College Fall, 2012
 
Imperative Problem Solving and Data Structures
 
 

CSC 161 Module 5: Music Composition with Dynamic Data Structures

Summary and Main Topics

The purpose of this module is to cover pointers, and their use in common self referential data structures, such as linked lists, in detail. The main topics covered are:

  1. pointers
  2. linked lists

Day-by-day Details

Day Topic Preparation In-class Due Date Availability
for
Extra
Credit
Friday, November 9 Pointers Review:
  • King:
    • Review: Chapter 11
    • Read: Chapter 12
  • K&R:
    • Review: 5.1-5.2
    • Read: 5.3-5.6
malloc-example.c
list-example.c
array-list.c
   
Monday, November 12 Supplemental Problem 4   Supplemental Problem 4 (done individually) Monday, November 12 Discussion of solutions  
Monday, November 12 Pointers
  • King:
    • Review: Chapter 11
    • Read: Chapter 12
  • K&R:
    • Review: 5.1-5.2
    • Read: 5.3-5.6
  • scribbler-movie.c
Pointers Lab    
Tuesday, November 13 Linked Lists Scheme-like Linked Lists Lab Monday, November 19  
Wednesday, November 14 More Linked Lists   Linked Lists for a Movie   Linked Lists with Actions: extra credit if submitted by Wednesday, November 28
Friday, November 16 Program management: header files lab on program management   Tuesday, December 4
Monday, November 19 Project: Music Composition   Project: Music Composition   Extra credit if turned in by Friday, December 7
Tuesday, November 20
Wednesday, November 21 Hour Test 3 Covers through Module 5 Lab on "More Linked Lists"    

Project: Music Composition

Introduction

When developing a song, a composer may have an initial idea for a melody, including pitches (frequencies) and durations (length of the pitch). After writing down some first ideas, the composer likely will want to edit the melody: altering some notes, adding new notes, removing others, reordering notes, etc.

Of course, a full melody-composition program would require extensive development and is far beyond the scope of what might be done for a CSC 161 project. Instead, this project provides some basic capabilities for developing a melody and suggests ideas for a more extensive system.

Picking Data Structures

In considering how to store a single note, the natural choice is a struct with fields for pitch and frequency. The choice for an entire melody requires some analysis.

The need for flexibility suggests a linked structure be used. To use an array, we would need to determine how long the array should be. Further, insertion within the array would likely require moving many existing notes over to make room for the new addition. Deletion of a note would likely require moving many existing notes toward the start to take up the place of the deleted note. Linked lists do not have size or ordering constraints, so linked lists seem better suited to the storage of note information than arrays.

For this project, the simplest approach for storing a melody would be a singly-linked list, as discussed in several labs in this module. Other, more sophisticated list structures are beyond the scope of this project.

Specifying Notes

When writing a melody, a composer likely would want to identify the name of a note and its octave. For example, see Piano key frequencies on Wikipedia for some details.

Following this approach, file pitch.h defines a pitch as a struct with name, octave, and pitch information.

Specifying Notes on the Piano

Using the struct pitch in file pitch.h , it is natural to define the notes on a piano as an array with an entry for each of the 88 keys on the piano. Further, we only need one copy of this array. For this reason, we place use file scale.c To define the array of pitches corresponding to all keys on a piano. This array can be compiled once and then used where necessary.

When using the scale array, a single index gives a structure with full information about any note on the piano. For example, here are several entries from the array:

index note
name
alternative
name
octave frequency comment
39 "C" "" 4 262 (middle C)
40 "Df" "Cs" 4 277 (D flat or C sharp)
48 "A" "" 4 440 ("Concert A" used for tuning)

As this table suggests, given an array index, one can quickly retrieve name, octave, and frequency information.

With this notation, the first 5 notes of Spirit Song (from the Getting Started Module) might be represented as follows:

index name octave frequency duration
62 "B" 5 988 0.75
60 "A" 5 880 0.25
58 "G" 5 783 1
55 "E" 5 659 0.75
56 "F" 5 698 0.75

Since the index provides information about name and octave, we need store only the array index to obtain the rest of the note information.

Technical Note

Within a music composition program, there is no need to have multiple copies of the scale array, but we may want to refer to that array in several places. Thus, the main declaration of the array will be in a file scale.c which we will define and compile separately.

In order to reference this array in other parts of the program, we place the line

extern struct pitch scale[88];

in any implementation files that may need to use this array. This will allow the compiler to know that a scale array will be supplied separately where ever it is needed.

Storage of a Note

Putting the above pieces together in the context of a composer creating a melody, a single note will contain just an array index and a duration. For a linked list of melody notes, a node could use these declarations:

typedef struct noteNode * noteNode_t;

typedef struct noteNode {
  int arrInd;
  double duration;
  noteNode_t * next;
} noteNode;

In reviewing this framework, a noteNode is a struct with three fields: an array index frequency, a duration, and a pointer to the next node in the melody. Also, the type nodeNode_t refers to a pointer to a noteNode. For convenience, these declarations are contained in the header file noteNode.h.

Melody Development: a Musical Phrase

One reasonable element of music composition involves the use of a musical phrase, separate from the melody. The idea is that a composer might develop a basic melody, and then want to insert a new phrase (or series of notes) into the phrase at various points. Pragmatically, this means that the composer might develop/edit both a main melody and a separate phrase and periodically insert the phrase into the melody. Common editing techniques might apply to both the melody and the phrase, but an insertion operation of the phrase into the melody also should be possible.

Referencing a Note within a Melody or a Phrase

In a professional, music-editing context, a composer might work with parts of a melody or phrase in many ways (e.g., by pointing to a note on the screen, by moving backward and forward from a given note, etc.). However, many of these approaches require considerable sophistication within a computer program—well beyond the scope of our CSC 161 project.

For this project, we will simply number the notes in a melody or phrase, with the first note having index 1. Thus, the first five notes in a melody would be notes 1, 2, 3, 4, and 5. Also, once a note is inserted within a melody, numbering will start fresh with the first number being 1. For example, suppose a melody has 5 notes and we insert 3 notes before the last one. The new melody will have 8 notes, and the last note (formerly number 5 in the original melody) now has number 8.

Musical Composition Functions for this Project

Altogether, this project involves the following operations:

  1. Editing either the melody or a separate musical phrase.
    1. Setting the sequence of notes to null
    2. Adding a sequence of notes at the end of the current melody or phrase
    3. Changing any single note
    4. Deleting a single note
    5. Inserting a new note after a specified note.
  2. Displaying/playing a melody or a separate musical phrase
    1. Printing a table of the notes (i.e., sequence number, note name, octave, frequency, duration)
    2. Playing the melody or phrase (i.e., on the Scribbler 2)
  3. Modifying a melody or a separate musical phrase
    1. Transposing each note in the sequence up or down by a specified number of half steps.
    2. Inverting each note in a sequence, so that if the original melody goes up a certain number of half steps, the inverted melody goes does the same number of half steps.
  4. Inserting the separate musical phrase into the melody following a specified note.

Project Organization

Work on this project falls into approximately three main categories:

Following the reading on program management, these elements could be placed within the following file structure:

files for the composer project

As illustrated in this diagram, noteNodes are used for the linked list (noteSeq.h). Similarly, information about note pitches in pitch.h is used in the user's composerMain program and within linked list processing (in noteSeq.c). However, information about note pitches in pitch.h may or may not be used in the definition of various linked-list operations (in noteSeq.h). An array of notes on a piano (in scale.c) is used in the implementation of note operations (in noteSeq.c and maybe noteSeq.h) and possibly in the user composerMain program. Linked list operations (defined in noteSeq.h) are used by the user composerMain.c program, but the details of implementation of those operations (given in noteSeq.c) are not needed by the user.

Getting Started

To begin this project, code is available in the various files mentioned above to create a simple melody or separate musical phrase and to print the table of notes for the melody or phrase. (These are operations 1a, 1b, and 2a in the outline above. For further clarification, the outline displays these operations in blue.)

Existing files:

Work for this Project

Expand the initial files to complete the operations identified above (steps 1c, 1d, 1e, 2b, 3a, 3b, and 4 in the outline).

Define a Makefile to compile and link the various parts of your program.

Grading

This project will be worth 30 points, based on the following rubric:

Extra Credit: