Copyright is a legal “intellectual property” that gives owner exclusive rights to “copy, distribute, display, or perform” their own creative works. Copyright was developed in the 15th and 16th centuries in response to the printing press. Before the printing press, it was prohibitively time-consuming and expensive to copy a book—you had to do it by-hand! With the rise of the printing press, there were concerns that the work of an author would be copied and distributed without their consent, thus robbing them of the benefits—monetary and social—of developing that work. Copyright law was, therefore, developed to protect creators and incentive them to go through the effort of developing new work.
While copyright law was originally intended to protect book authors from the perils of the printing press, it has extended to virtually all creative industries, including music. For example, if you download a song from the Internet, e.g., from a torrent, you are likely violating the copyright of the artist and/or publishing company for that song. However, copyright law has had other chilling effects on the music industry. In, perhaps, the most infamous court case of its kind, Bright Tunes Music v. Harrisongs Music, the judge found that Beatles member George Harrison had “unconsciously copied” The Chiffons song He’s So Fine in his song My Sweet Lord. Similar cases of questionable quality have come up over the years, most recently between pop artist Katy Perry and Christian rap artist Flame. In that case, the judge ordered Perry and her associates to pay Flame 2.78 million dollars in damages although the ruling was eventually overturned.
In response to these copyright cases, lawyer Damien Rihel and technologist Noah Rubin took an extreme measure: they created a computer program to enumerate all possible melodies under a set of constraints (eight note songs drawn from a diatonic scale) and write them to disk. By writing them to disk, they, in theory, have infringed on the copyright of every existing song and copyrighted any song that has yet to be written!
For this mini-project, we will follow in Rihel and Rubin’s footsteps and write a small recursive program capable of enumerating all the songs of a given length drawn from a set of notes. We will briefly then reflect on the nature of computers, copyright, and creativity.
For this mini-project, write your code and reflection in a file called all-songs.scm
.
For all the functions you write in this mini-project, make sure you:
Finally, do not worry about providing test suites for functions that produce musical compositions as we do not have a meaningful way to test compositions for equality!
As a warm-up to writing a function that produces all combinations of musical notes of a certain size, we’ll focus on the case where our compositions only have 2 notes. From there we’ll apply the technique developed in this part and generalize it to the case where the number of notes in the composition is arbitrary.
First, write a recursive function, (all-list2 v lst)
that takes a value v
and a list of values and creates a list of all possible lists of size 2 where the first element is v
and the second element is a value from lst
.
For example:
(all-list2 "q" (range 5))
> (list (list "q" 0) (list "q" 1) (list "q" 2) (list "q" 3) (list "q" 4))
(all-list2 "q" null)
> null
Begin by using these examples to derive the recursive decomposition:
To generate all the lists of size two where the first element is
v
and the second element is drawn from a listlst
:
- When
lst
is empty, …- When
lst
is non-empty, …
Remember that in your recursive decomposition, you assume you can recursively “do the operation” on the tail of the list you are recursively decomposing.
Use this assumption to write out what to do in the non-empty case.
After you are done, create an appropriate docstring for all-list2
and implementation from this decomposition.
Next, use all-list2
to write a recursive function, (cartesian-product l1 l2)
that takes two lists and produces the Cartesian Product of the elements drawn from lists l1
and l2
.
The Cartesian Product of two lists is a list that contains all the possible lists of size 2 (list x y)
where x
is drawn from l1
and y
is drawn from l2
.
(cartesian-product (range 3) (list "a" "b"))
> (list (list 0 "a") (list 0 "b")
(list 1 "a") (list 1 "b")
(list 2 "a") (list 2 "b"))
(cartesian-product null null)
> null
Again, begin with the recursive decomposition for this function.
Like append
, we have a choice of two lists to perform recursion over; let’s choose the first:
To form all pairs of values whose first component is drawn from
l1
and second component is drawn froml2
:
- When
l1
is empty, …- When
l2
is non-empty, …
When design your recursive decomposition, ask yourself: “what is the “recursive assumption” that I can utilize in writing the recursive case?”
After you have completed your recursive design, make sure to write a docstring and implementation for cartesian-product
.
When you go to implementation, you should use all-list2
to perform the work necessary in the non-empty case and combine this work with the work done in the recursive call with append
.
Finally, use cartesian-product
and a (small) list pipeline to write a function (all-two-note-songs notes)
that takes a list of notes (as MIDI note values) as input and produces all the possible two note songs drawn from the list of provided notes
.
The duration of the notes should be quarter notes qn
.
For this function, think about what you should pass to cartesian-product
to get the “effect” of all combinations of two notes drawn from notes
and then how to transform that collection into a list of compositions using map
.
Demonstrate that all-two-note-songs
works by defining a list called two-note-example
containing all 2 note songs drawn from the notes C (MIDI note 60) and A (MIDI note 69).
You should have $2 \times 2 = 4$ compositions in your resulting list.
In part 1, you wrote a function that generates all two-note melodies drawn from a collection of notes.
Now, we will generalize these functions to write a function to generate all n
-note melodies for any natural number n
.
While we will follow the same pattern of implementation from part 1, we will not reuse implementation, i.e., these functions will not call on all-list2
or cartesian-product
.
First, implement a recursive function (cons-all x lsts)
that takes a single value x
and a list of lists, lsts
, and returns lsts
but with x
added to the front of every list.
(cons-all 0 (list (list 1 2)
(list 3 4 5)
(list 6 7)))
> (list (list 0 1 2)
(list 0 3 4 5)
(list 0 6 7))
(cons-all 0 null)
> null
Now, use cons-all
to implement (combinations lsts)
that returns a list of lists.
For each list returned in the result, the i
th element of the list is drawn from i
th list of lsts
.
The result, therefore, contains all the ways we can combine the elements of the lists found in lsts
.
(combinations (list (list 1 2)
(list 3 4 5)
(list 6 7)))
> (list
(list 1 3 6) (list 1 3 7)
(list 1 4 6) (list 1 4 7)
(list 1 5 6) (list 1 5 7)
(list 2 3 6) (list 2 3 7)
(list 2 4 6) (list 2 4 7)
(list 2 5 6) (list 2 5 7))
(combinations null)
> (list null)
Note that when given the empty list, combinations
produces a list containing null
, not null
itself!
This is a little bit strange, but intuitively, there is one way to form a valid list when we are given nothing—just null
itself!
Also note that if the list passed to combinations
only contains two lists, then the function returns the same output as cartesian-product
!
(combinations (list (range 3)
(list "a" "b")))
> (list (list 0 "a") (list 0 "b")
(list 1 "a") (list 1 "b")
(list 2 "a") (list 2 "b"))
(combinations (list null null))
> null
To implement combinations
, you need to be very careful about types!
You are manipulating three levels of objects here:
In your recursive decomposition, be clear about which of three types you are manipulating at every step in your reasoning.
You will likely find it useful to also try the recursive decomposition out on concrete examples to get a sense of how to proceed. Choose a number of small examples, say 3–5 of them, stick to what our recursive decomposition gets you, i.e., a case analysis in terms of empty/non-emptiness, and once you have written then out, see if you can generalize the pattern of behavior you see in them
Finally, use combinations
to write a function (all-songs n notes)
that takes a non-negative number n
and a list of notes (as MIDI note values) as input and produces all the possible songs of n
notes drawn from the list of provided notes
.
The duration of the notes should be quarter notes qn
.
You may implement this function with either recursion or a list transformation pipeline.
Demonstrate that all-songs
works by defining a list called five-note-example
containing all 5 note songs drawn from the notes C (MIDI note 60), B♭ (MIDI note 58), and F (MIDI note 65).
Once you have finished your all-songs
implementation, let’s reflect on what we have done.
You hopefully noticed that your recursive implementation of all-songs
is simple and elegant (although, perhaps, difficult to have derived in the first place).
But yet, it is capable of generating, literally, all the songs, according to the opinion of Rihel and Rubin.
The fact that such a simple thing is capable of so much calls into question why we value music in the first place.
Computers can play music—we wrote all that in the first two mini-projects—and now we can compose music (“all the music”!) as well.
Of course, this does not apply only to music; other creative endeavours such as writing, art, and dance are, to varying degrees, already being challenged by computation.
In a few paragraphs (at most one page of text), respond to the following prompt:
Do you think music should still be valued in light of modern-day computation’s ability to “do it all?” If so, what do you personally value about music in spite of this assignment? If not, why do you feel that music has lost its value?
You should leave your response as a comment at the bottom of all-songs.scm
.
In formatting your text, feel free to use line breaks within a paragraph to avoid making lines that are too long, e.g., favor this style of formatting:
; Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sit amet
; porta massa. Proin fermentum mi eu magna tristique, nec gravida sapien luctus.
; Vivamus ut tincidunt dolor, a ultrices velit. Curabitur volutpat augue mollis
; dictum maximus. Maecenas magna enim, euismod a mauris sed, convallis blandit
; dui. Suspendisse ut nisl vitae tellus malesuada vehicula. Cras tincidunt
; commodo libero, ut lacinia sem. Donec sit amet convallis ante. Vivamus cursus
; nec massa eget finibus. Integer mattis ac mi ac blandit. Cras ligula felis,
; rhoncus sit amet facilisis non, gravida quis justo.
versus this style of formatting:
; Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sit amet porta massa. Proin fermentum mi eu magna tristique, nec gravida sapien luctus. Vivamus ut tincidunt dolor, a ultrices velit. Curabitur volutpat augue mollis dictum maximus. Maecenas magna enim, euismod a mauris sed, convallis blandit dui. Suspendisse ut nisl vitae tellus malesuada vehicula. Cras tincidunt commodo libero, ut lacinia sem. Donec sit amet convallis ante. Vivamus cursus nec massa eget finibus. Integer mattis ac mi ac blandit. Cras ligula felis, rhoncus sit amet facilisis non, gravida quis justo.
Where all the text of a paragraph appears on a single line.
This is, of course, not a writing course, so we are not scrutinizing your response to the degree we would if this were Tutorial. But we will be checking that (a) you actually answer the prompt at hand and (b) your response demonstrates that you have put some genuine thought about the nature of computation in relation to music.
all-songs.scm
(all-list2 v l)
(cartesian-product l1 l2)
(all-two-note-songs notes)
two-note-example
(cons-all v lsts)
(combinations lsts)
(all-songs n notes)
five-note-example