CSC 161 Grinnell College Spring, 2015
 
Imperative Problem Solving and Data Structures
 
 

CSC 161 Module Highlighting Music Composition with Dynamic Data Structures

Summary and Main Topics

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

  1. pointers
  2. dynamic memory allocation
  3. linked lists

Day-by-day Details

Day Topic Preparation In-class Due Date Availability
for
Extra
Credit
Friday, April 3 Supplemental Problem 3   Supplemental Problem 3 (done individually) Friday, April 3  
Friday, April 3 Pointers and Dynamic Memory 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, April 6 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, April 7 Scheme-like Lists in C Scheme-like Lists in C    
Wednesday, April 8 Linked Lists in C Linked Lists in C Monday, April 13  
Friday, April 10 More Linked Lists   Linked Lists for a Movie   Extra credit if submitted by Friday, April 24
Monday, April 13 Program management: header files Lab on Program Management   Extra credit if submitted by Monday, April 27
Tuesday-Wednesday, April 14-15 Project: Music Composition   Project: Music Composition Friday, April 17  

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

Referencing a Note within a Melody

For this project, we will simply number the notes in a melody, 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 the melody.
    1. Setting the sequence of notes to null
    2. Adding a sequence of notes at the end of the current melody
    3. Changing any single note
    4. Deleting a single note
  2. Displaying/playing a melody
    1. Printing a table of the notes (i.e., sequence number, note name, octave, frequency, duration)
    2. Playing the melody on the Scribbler 2
  3. Modifying a melody (Extra Credit)
    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.

As described below, parts of the outline in blue are implemented in the code below.

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 and to print the table of notes for the melody. (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, and 2b in the outline).

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

Extra Credit:
Do one or more of the following:

Grading