Percussive Pattern Algorithms

I'm not done with drum modules just yet.  I'm determined to build something better than GGrewve.  Sure, GGrewve works great, but since I've had such great success with drums in the past, why not go even further?  Just because something works doesn't mean you have to leave it alone.  After brainstorming with PowerPoint for several hours, I think I have come up with some winning ideas.

Click for the PowerPoint.

Much like the GGrewve engine, this new percussive analysis revolve around grammar segmentation.  Unlike GGrewve, this engine will also "stratify" the pattern by pads for even more flexible analysis and resynthesis.  The actual analysis engines may include Markov and a new engine by the name of Multi-State Analysis that I'm in the process of writing.

Below are the slides in image form in case I should ever need them on a computer without Office;

ChromoRhythm: Mediocre Results

Though I haven't fully fleshed out the ChromoRhythm data structure, I'm already getting the unfortunate feeling that the drum module simply isn't going to be able to stand up to GGrewve.  It's too unstructured to bring any coherence to a drum channel without extensive use of a complicated fitness function and a large population of randomly mutated DNA strands, which results in unnecessarily high run times.

In short, Chromo's genetic algorithm engine isn't looking like a good choice for drum beats.  The engine, even in early stages, consumes too much time and produces mediocre results.

I'm not yet ready to give up on finding a superior alternative to GGrewve.  I may abandon this particular genetic algorithm approach for the moment and focus on a flexible stochastic system implemented on top of a grammar foundation.

As I like to remind myself, one of the greatest benefits of my style of research lies in my ability to immediately turn around when I sense a dead end.  Without a concrete aim or hypothesis, other than the overall goal of creating an infinite music system, I am completely free to try whatever methods I choose, creating and abandoning modules as necessary for maximum productivity.  Killing a module within two days of creating it isn't a bad thing.  It means more experience, more knowledge of what doesn't work, and more opportunity to find out what does work.

ChromoRhythm

ChromoRhythm is a new generative drum module that combines many of the recent ideas I've had surrounding percussive generation.  One of the main features of ChromoRhythm will be the genetic (chromosome-based) configuration storage system, which will facilitate some degree of dynamic mutation of the drum styles.  Though I have yet to decide how the evolutionary fitness function will be implemented and whether it will be subjective or objective, I believe that the ability to convert information between chromosome-like data structures and configuration variables will enable a degree of variability unknown to previous drum modules.

Naturally, ChromoRhythm will be measured against GGrewve, since GGrewve is the current standard in drum pattern generation (and an excellent standard, at that).  Though I doubt that ChromoRhythm will ever be able to surpass GGrewve in human-feeling beats, the new data paradigm employed by the engine should allow it to trump GGrewve in terms of dynamic playing and variability of style.

Many of the details of Chromo are still being worked out.  Here are a few things that are already coded or conceptually established:

  • DNA data structure consists of a long string of numbers
  • Internal function converts between DNA strings (for use with the generalized XIAS evolutionary engine) and global configuration variable states
  • Engine uses a "base style" for each individual drum, as specified by the DNA
  • Engine layers "variations" on top of the base style for each individual drum, as specified by the DNA
  • Engine adjusts playing dynamics based on movement intensity as well as coordination accenting instructions; DNA specifies the degree and exact parameters of the dynamics

One of the main questions upon which the development of ChromoRhythm is hinging concerns the fitness function.  I see two ways of doing it:

Objective Fitness Function - ChromoRhythm takes a drummer, randomly mutates the DNA of the drummer, then runs a fitness function on the mutations.  The fitness function is the first-ever "ear module" employed in the development of mGen, and mimics a user that listens to the beat.  The fitness function assigns points based on how well the drummer lines up with the coordination instructions, how effectively the dynamics change the style, and how pleasing the beat is in general.  The XIAS evolutionary engine then evolves the drummer mutations to the next generation and repeats the process for a set number of generations (or, alternatively, until the population reaches a specified mean fitness value).

Subjective Fitness Function - ChromoRhythm creates a pool of potential drummers to begin with.  The user selects an option in the interface that makes Chromo generate a channel for each potential drummer during the composition of a single piece.  The user then listens to the piece, muting all but one channel, and assigning points to the drummers based on how much he or she likes the style.  The XIAS evolutionary engine then evolves the drummer pool, as detailed above.

One may see that these two methods have very little in common - in fact, the first details a method of evolving substyles of a single drummer configuration, while the second involves evolving overarching drummer configurations.  Perhaps, then, both of these methods can be combined.

Needless to say, the logistics of implementing a genetic algorithm aren't at all simple.  Conceptualizing the process takes far longer than actually implementing it.  Determining what exactly should be evolved, how populations should be treated, which individual should be played, and how fitness points should be assigned will all heavily affect the output of ChromoRhythm.

Object System 2

As I mentioned in the last post, the new Markov engine in XIAS really required a new data structure system to run efficiently.  It required a data structure with the intuitive read/write functions of Object System but the powerful substructure storage system of GDS.  Enter Object System 2.

In developing the second version of Object System, the primary goal was to take a write statement like "movement.progression.chord1=Amin7" and make it correspond to a storage address rather than just a variable name.  In other words, instead of just being stored as a variable with the name movement.progression.chord1, it would be stored as the variable chord1 (having the value Amin7) within the substructure progression as part of a larger structure movement.  This kind of structural nesting provides significant improvements in access times for large data structures that have been properly segmented.

On top of a true substructure storage system like that of GDS, Object System 2 has the powerful ability to nest an infinite number of substructures, as opposed to the 10-sublevel limit imposed by GDS.  Furthermore, OS2 uses only 3 string replacement commands to encode a substructure, while GDS uses around 20.  In short, OS2 now has all the power of GDS, all the ease-of-use of OS, and a significant efficiency boost over both.

To prove the benefits of OS2, I've replaced all of the XIAS calls to OS1 with identical calls to OS2 and graphed the performance results.  While the two data systems perform almost equally for low-order Markov analyses, the runtime for OS1 quickly gets out of hand for high-level analyses.  For a 10th level analysis, OS2 is more than twice as fast as OS1.  This is to be expected, since the performance gains are magnified as the data structures get larger and OS2's substructure system saves the CPU expensive string parsing loops.

Generalized Markov Engine

Picking XIAS up again, I'm now working on developing a flexible and lightweight Markov engine to give the XIAS library stochastic abilities.

Potential features for the Markov engine:

  • Hierarchical storing of event probabilities for fast access time
    • Will require hefty upgrade of Object System data structure
    • Similar to GDS
    • Improve access times for complex data structures
  • Overlapping of orders
    • Markov chains of different orders can have different "weights" that contribute to the overall probability of an event occurring
    • Gives the user the ability to control how deeply the engine considers the past states when calculating probabilities
  • Arbitrary event-handling
    • Events don't have to take any particular structure - in fact, events could even have other data encoded into them to further enhance the predictive capabilities of the engine when outside variables may be influencing the future

Unfortunately, as mentioned in the notes above, making the engine efficient is going to require an overhaul of the Object System data structure created recently. It will require the ability to store substructures as single elements of larger structures to speed up access times (which I'm quite certain would get out of hand quickly for large systems in which many events contribute to the weightings). This ability will bring OS closer to the functionality of GDS. The main difference, however, will lie in the syntax. I still intend for OS to have easy, OOP-like syntax (Parent.sublevel1.sublevel2 ... variable = blah). All previous OS code will need to function after the rewrite.

When the Markov engine is finished, XIAS will boast a truly wide range of algorithms. I can only imagine the hybrid possibilities.

Here's what I'm thinking: a genetic algorithm that dynamically evolves Markov probability spaces that are based on an underlying grammatical system analysis, wherein the words of the grammar come from a fractal cutting engine whose parameters are fed by an L-system. Talk about one hybrid algorithm to rule them all!

Spirit

Spirit is a new generative module designed to provide lead parts. Spirit operates on a hybrid algorithm with a paradigm similar to that of the partial idea stream idea that I brainstormed a few months back. It also exploits the ease-of-use of the new XIAS library.

Three separate "streams" power Spirit: control, config, and duration. The control stream tells Spirit how to behave - that is, whether to use arpeggiation, single notes, or chords. The configuration stream provides details that determine individual pitches (or words, in the case of the control stream requesting a grammar engine). Finally, the duration stream is exactly what one would expect - duration information. Separating the duration stream from the streams that control pitch allows the module to uphold a degree of rhythmic consistency. This is similar to the way that I separate the melodic and rhythmic seed strings in Fraccut to make rhythm and pitch somewhat independent.

Right now Spirit is making effective use of both the L-system and grammar engine features of XIAS.  Great - and unique - results have already been obtained even with a very rough version that made use of only three control types and three config types.  Sample 11 on the sample page showcases this early version of Spirit and promises much more to come!

Some long-term goals for Spirit:

  • Develop "character"  - distinct styles that vary with configuration
    • Restricted grammars
    • Beat emphasis
    • Markov data (as a supplemental "weight" in decision-making)
    • Global variables
    • Restrict algorithm types among configurations
    • Production rules (L-system)
  • Make effective use of all algorithms available in the XIAS engine (perhaps with the exception of the evolutionary engine, as it seems inapplicable)
  • Strictly obey the instructions of the coordination module
  • Achieve a level of creativity above that of any other generative modules
  • Achieve a level of coherence above that of any other generative modules

Coordination Module Preliminary Tests

After months of deliberation over how best to go about constructing the data structures and standards for coordination modules, I finally just jumped out on a bridge and created a basic foundation as well as the first ever coordination module.

Here's what I'm presently planning for the coordination module data standard:

  • Accents
    • Strong
      • [Comma-delimited list of non-relative times]
    • Weak
      • [Comma-delimited list of non-relative times]
  • Style
    • [Normal, Smooth, Sharp]
  • Tempo
    • [Integer value]

I'm still very unsure of whether or not this structure will adequately coordinate the generative modules.  No doubt, developing the full standards for coordination modules will require a great deal more time than it took to develop standards for the other core modules.  Coordination is such an abstract thing, yet it is so absolutely necessary.

Preliminary test with an experimental coordination module (details will be given on the plugin if I decide to keep it as a permanent addition to the plugin library) actually impressed me quite a bit!  The only part of the data structure that I've actually implemented is the strong accent list.  The addition of this one small string of information to the main compositional block, however, actually makes a noticeable difference in composition quality because of the accented beats.  It effectively increases the coherence of the generative modules.

Presently, an experimental post-processing module is the only plugin making use of the accent information provided by the coordination module.  The post-processing module simply pumps up the velocity of any generative module notes that occur on a strong beat.  The generative modules still pay no attention to note placement.  Yet, even this quick and dirty little method of utilizing the coordination module's data makes a difference!

The birth of coordination represents another huge step forward for mGen - a step that will, with a great deal of work and persistence, bridge the gap between mGen's rigid, lifeless computer music and the emotional, dynamic music of true human musicians.

Below is a slice of a composition taken from one of the visualization tools I made.  It shows accenting: lighter blocks represent notes with higher velocities.  This shows the coordination module in action (though in this picture, it appears that the coordination module chose a very boring accent pattern - only on beat 1 of each measure).

I3: Post-Processing and Finishing Touches

After a week of heavy development, Interface3 has effectively reached maximum potential - there's virtually nothing left to improve upon.  Perhaps the only thing remaining is a preferences window.

I3 now has a post-processing bank and full post-processing capabilities, as well as a new tile that controls the composition name.  Finally, I3 gained the ability to set default states for plugins by directly manipulating the plugin configuration files.  This means that users waste less time configuring plugins that they use frequently with the same state - instead, they can just set that state as the default, and I3 will automatically load the configuration when a new instance of the plugin is loaded into a tile.  This simple yet elegant feature has already saved me a great deal of time.

A final screenshot of I3:

30.000

Today is a landmark day in the development of mGen.  The combined program hit 30,000 lines of code - a massive accomplishment!

With each new landmark, I look back on previous work, including interfaces, plugins, compositions, and assisted composition to see how much improvement has really taken place.  Indeed, reviewing the recent compositions and new interfaces, there's not a single doubt in my mind that mGen has improved immensely since 20,000 lines.

I look forward to many more tens of thousands of lines of code.