Harmonanal

Quit giggling.

|harmonanal| contains a set of subroutines to construct probability arrays based on harmonic analysis. The harmonic analysis process is very similar to the melodic analysis process, but is more complex. As opposed to |melanalyze|, which constructs probability arrays that allow one to predict the next interval based on the current interval, |harmonanal| constructs probability arrays that allow one to predict the next interval based on the current and previous interval. In technical terms, the melodic arrays allow one to propagate first-order Markov chains, and the harmonic arrays allow one to propagate second-order Markov chains. To construct the harmonic arrays, therefore, one has to look at the current interval, the previous interval, and the interval that came before the previous interval, which I shall call the twoprevious interval.

The harmonic analysis process starts with input modules called |harminputmodule|. Each pair of musical voices (one voice per MIDI channel) will use it's own |harminputmodule|.

Harminputmodule Harmongetint
|harminputmodule| |harmongetint|

The MIDI steams (separated into note number and velocity) of the two voices are input into |harminputmodule| and then into a subpatch called |harmongetint|. It is the job of |harmongetint| to calculate the harmonic interval between the two voices, and to output that interval if and only if both voices are playing simultaneously. First, the interval is calculated: |stripnote| passes only the Note On messages, and the note number of the bottom voice is subtracted from the top voice, yielding the harmonic interval in half-steps. |harmongetint| assumes that the left voice will always be higher in pitch than the right voice; if the voices cross, the analyzed intervals will be inverted (i.e. a perfect fifth becomes a perfect fourth). The |t b f| object makes sure that the interval is recalculated any time the note number of either voice changes. The calculated interval is sent into the left inlet of the |spigot| object, which, depending on the input that it's right inlet has recieved, can either pass or block further transmission of the interval.

To determine whether both voices are playing at any given time, one needs only to multiply their velocity values: if the result is nonzero, then both voices are playing a note. This is convenient, since |spigot| will output the information it receives in it's left inlet only if it has last received a nonzero number in it's right inlet. This is exactly what happens in the patch: the note velocities of both voices are multiplied (with another |t b f| object used to make sure the multiplication is redone if the velocity of either voice changes), and the result is sent into the right inlet of the |spigot|. There is, however, a potential complication. A human keyboard player will often hold several notes simultaneously, even if the player is playing an unaccompanied melody (i.e. playing a melody very legato often means slightly overlapping the beginning a note with the end of the previous note). If this occurs, it is likely that the multiplication object will have last received a velocity of 0 from one of the voices, despite the fact that the voice could still be playing one or more notes. To solve this problem, the |pd notecount| object keeps track of the number of notes that the voice is playing; this count is sent to the multiplication object in place of the actual note velocities.

Once an interval makes it through the |spigot|, it is sent through a network of objects to ensure that it is in the acceptable range. OSCAR recognizes the harmonic intervals from unison through octave, and reduces all other intervals to fit within this range. Once the interval is made to fit within this range, it is output from |harmongetint| and into a sorting mechanism called |sordsort|.

Sordsort  
|sordsort|

|sordsort| (which stands for "second-order sort") works much like |melsort|. The incoming stream of intervals is divided into packets, this time with three intervals in each packet, in the order twoprevious, previous, current. Each incoming interval is included in three packets: first as the current interval, then as previous, and then again as twoprevious, as new intervals are received. Each packet is then sent to the |route| object, which removes the first part of the packet (twoprevious) and sends the rest of the packet out of the outlet that corresponds with the twoprevious interval. The remaining packet is sent out the correct outlet and into a |fosort| object.

Fosort  
|fosort|

|fosort| (first-order sort) essentially repeats the sorting process of |sordsort|. The |route| object removes the first part of each incoming packet and sends the rest of the packet out of the corresponding outlet. |sordsort| and |fosort| work together to route the final part of a packet (the current interval) to the destination specified by the first two parts (the twoprevious and previous intervals). For example, if the interval succession 0, 6, 7 (unision, tritone, perfect fifth) is input, the "7" will be sent through the outlet |s zerosixh| (again, the "s" is a remote send, and the "h" stands for "harmonic") Each of these |s| objects is connected to a |harmanalmod|.

From this point forward, the harmonic analysis process is identical to the melodic analysis process outlined earlier. The central component in the process is |harmanalmod|, which, aside from differently named inlets and outlets, is identical to |melanalmod|. The biggest difference between the two processes is that, because it is looking at the current and two previous intervals, the harmonic analysis process uses one hundred and sixty-nine |harmanalmod| objects, whereas only twenty-five |melanalmod| objects are needed. One |harmanalmod| object is used for each possible harmonic succession; the particular interval succession being tracked by a |harmanalmod| is given in it's initialization argument. |harmanalmod sixseven|, for example, keeps track of the intervals that follow the succession tritone, perfect fifth.

As in |melanalmod|, |harmanalmod| keeps a running count of the total number of intervals that have been received and distributes this count for use throughout the patch. Each incoming interval generates a BANG that is sent to it's corresponding |probcalc|, which counts the number of BANG messages it has recieved and divides this count by the total, yeilding a probability percentage. This percentage is sent to an analysis array. Additional inlets and outlets add the ability to save the state of the analysis module, and to restore this state at a later time.

The harmonic analysis process can be summarized:

      One of the voices connected to a |harminputmodule| plays a note while the other connected voice is playing a note.
      The harmonic inteval between the two voices is calculated by |harmongetint|.
      The new harmonic interval is sent to |sordsort|, which packs the new interval into a package with the previous two intervals.
      The package is sent to a |fosort|. The first third of the package (the twoprevious interval) determines which |fosort| is to be used. The first third of the package is then discarded.
      The package is sent by |fosort| to a |harmanalmod|. The first half of the remaining package (the previous interval) determines which |harmanalmod| is to be used. The first half of the package is then discarded.
      The remaining third of the package (the current interval) is receieved in the correct |harmanalmod|. The |harmanalmod|'s "total" counter is incremented, and the new count is distributed.
      The interval is routed to the |select| object, which sends a BANG to the |probcalc| that corresponds to that interval.
      The counter inside the |probcalc| is incremented.
      All of the |probcalc| objects redo their calculations using the incremented counts, generating new probability percentages.
      The new percentages are sent to the analysis arrays.

At the end of an analysis session, the analysis arrays contain information about the music that has been analyzed. If asked the question, "Given a series of two harmonic intervals, what should the next interval be?" an analysis array would tell you, "Ten percent of the time it should be a perfect fifth, eight percent of the time it should be a minor second. . ."


Email Greg OSCAR Main Page