Wednesday, December 22, 2021

Polymeter Phase diagram with Convergences

Version 1.0.4 of the Polymeter MIDI Sequencer features a couple of cool enhancements to the Phase bar, both accessible via its context menu. The first is that the phase diagram can now indicate convergences slightly before and after they occur. The participating orbits are increasingly highlighted as the convergence approaches, and gradually return to normal as the convergence recedes. This is useful because the planets often move fast enough that it can be hard to tell when they’re lined up. Also the planets can appear lined up when they’re actually not, unlike the convergence indicator which is foolproof. And, because the highlighting changes both the width and the color of each orbit in proportion to its proximity to convergence, it’s very satisfying to watch. The second feature is that the Phase bar can now go full screen, without caption bar or borders, which is similarly visually pleasing. Music: "Atunwi" from "Polymeter" (Mental Groove). More info: https://victimofleisure.github.io/

 


 

Thursday, June 11, 2020

Polymeter 0.0.31 adds offset modulation and voice raising

Version 0.0.31 of Polymeter introduces a new feature called Offset modulation. This new modulation type allows a track's time offset to be changed by another track. This is useful for making the track periodically slow down and then catch up again, which helps emulate the feel of a live performer. Offset modulation is customizable per track, unlike tempo modulation which is global and affects all tracks the same. An offset modulator track's step values are interpreted as unsigned delays from 0 to 127 MIDI ticks. Note that by default this scheme only allows events to be delayed, not made earlier. To make events earlier, set the target track's Offset parameter to a negative value, e.g. -64, which is roughly an eighth note using the default time division of 120 PPQ. Because offset modulation is added to the track offset, this shifts the offset modulation range to -64 to +63 ticks, so that the target track's events can be made up to an eighth note early or late. For offset modulation to work properly you may need to set the "Note Overlaps" Master property to "Prevent."

Version 0.0.31 also enhances voicing modulation. In previous versions, voicing modulation could only lower voices an octave, but not raise them an octave. This was sufficient for common chord voicings such as drop 2 and drop 3, but more esoteric chord voicings exist that require some voices to be dropped and others raised. Consequently the voicing value can now be negative, in which case the specified voice is raised an octave instead of lowered an octave. Yes, it's inverted which is a bit annoying, but since lowering is by far the more common case it gets to be positive. In either case the numbering is still one-origin relative to the top voice, i.e. the voice with the highest pitch. An example for a tetrachord:

Voicing modulation value Result
2 2nd voice from the top is lowered an octave
3 3rd voice from the top is lowered an octave
-2 2nd voice from the top is raised an octave
-3 3rd voice from the top is raised an octave

Lowering or raising multiple voices is achieved by assigning multiple voicing modulators to the target track.

Offset modulation demo files are available here.

Sunday, April 19, 2020

Optimizations in sequencer core

There's an updated version, 0.0.30.001, which will process complex modulation setups significantly faster, due to some sorely-needed optimizations in the sequencer core. If you occasionally get the error "Sequencer Callback took too long" this version may help. Basically the sequencer was doing three dumb things:

  1. Muted tracks were processing their modulators, pointlessly.
  2. Modulator tracks were processing their modulators, pointlessly.
  3. If a mute modulator evaluated true, the track's remaining modulators were evaluated, pointlessly.

All three of these were wasting precious time in the callback thread for no good reason. Large projects often develop subtle flaws like this, due to iterative design over long periods of time. Periodic performance checks and code reviews are a must.

Friday, April 17, 2020

Polymeter 0.0.30 adds synchronization via MIDI clock

Polymeter 0.0.30 optionally transmits MIDI clock, allowing you to synchronize your DAW or hardware sequencer with Polymeter. This was by far the most-requested new feature. The Start, Continue, Stop and Song Position messages are also sent as usual. Note that this feature must be enabled, via Options/Send MIDI Clock.

MIDI clock is old school, but it’s widely supported and works pretty well. There are some limitations, all involving the starting position. 1) Playback must start on a sixteenth note boundary; if you try to start from a position that isn’t evenly divisible by a sixteenth note, you’ll get a warning message and your starting position will be quantized to the nearest sixteenth note. 2) Playback can’t start from a negative position; this is MIDI’s limitation, not mine! 3) Similarly, playback can’t start from a position later than 16,383 sixteenth notes; again, not my fault. And 4) once playback is started, skipping forward or backward in the sequence will cause loss of synchronization; that’s because the MIDI clock specification doesn’t support position change during playback, alas.

This version adds some other handy features. For one, the ability to assign multiple modulators at once (via Insert Group on the Modulation bar’s context menu). This is especially useful with scale and chord modulation. And, the Step Values bar now supports multi-step editing; if the edited step is within a contiguous range of selected items, the track’s corresponding steps are filled with the new value. This is an easier way to set all of a track’s steps to some value, as opposed to using the Velocity pane.

Finally, this version supports assigning custom colors of tracks in the Track view. The color picker is in the main toolbar. This feature is disabled by default, but you can enable it via Options/Show Track Colors.

Thursday, April 9, 2020

Polymeter 0.0.29 adds Chord Modulation and mapping to steps

Polymeter 0.0.29 introduces a major new feature called Chord Modulation, explained below. There are many other additions and improvements, including the ability to remotely control each individual step of a track. The release notes are here.

Chord Modulation

A scale is a subset of the chromatic scale. Each scale tone is a note, expressed as an offset in semitones relative to some base note. Assuming a base note of C, zero is C, one is Db, two is D, three is Eb, and so forth.

A chord is a subset of a scale. A chord can contain any number of tones, up to and including all of the tones of the parent scale. Chords are created by modulating a note track with a combination of scale, chord, and index modulators. Voicing modulators may optionally be added also.

Without a chord, index modulation accesses the tones of the current scale. The index modulation picks a scale tone, and the scale tone maps to a note. For example, suppose a note track has seven scale modulators, constituting a C major scale: C, D, E, F, G, A, B. The index modulation is interpreted as a zero-based index into that scale, ranging from zero to six. In other words, if the index modulation is zero the C is picked, if it’s one the D is picked, if it’s two the E is picked, and so on.

With a chord, index modulation accesses the tones of the current chord. This adds an extra level of mapping. The index modulation picks a chord tone, the chord tone map to a scale tone, and the scale tone maps to a note. Suppose the track has four chord modulators, which output 0, 2, 4, and 6 respectively. These correspond to the first, third, fifth, and seventh notes of the current scale. So if the scale is C major as before, the resulting chord is C major seventh.

Pitches:

Index 0 1 2 3 4 5 6 7 8 9 10 11
Note C Db D Eb E F Gb G Ab A Bb B

Scale:

Index 0 1 2 3 4 5 6
Scale Tone 0 2 4 5 7 9 11
Note C D E F G A B

Chord:

Index 0 1 2 3
Chord Tone 0 2 4 6
Scale Tone 0 4 7 11
Note C E G B

In this example, when accessing the scale, indices 0, 1, 2 map to the notes C, D, E but when accessing the chord, the same indices map to the notes C E G. The chord gives the indices a different meaning.

Notice that three things are being distinguished: the subset of notes (the scale), the subset of scale tones (the chord), and which tone is picked (the index). All three of these entities are distinguished from the rhythm, which is determined by the note track. So we have an orthogonal system in which each axis (scale, chord, ordering, and rhythm) is independent of the others.

A demo file that makes use of chord modulation is attached to this post. The demo file uses chord modulation to implement the "Numbers" system of tonal palettes popularized by my ex-teacher, noted tenor saxophonist Jerry Bergonzi. This system was also implemented in my previous project ChordEase. The Numbers system is explained here.

Mapping to Track Steps

Why would you want to want to map a controller to a track step? I’m glad you asked that question. For one thing, it means you can control the tempo. Here’s how.

Create a track, change its type to Tempo, set its Length to one, and set its Duration to 100. The duration in this case is repurposed as the range of tempo scaling, as a percentage. The 100 means the tempo control ranges from half time to double time. Create a mapping for whatever controller you’re using. Within the mapping, set the Output Event to Step, the Output Control to zero, and the Track to your tempo track. Note that for Step mapping, Output Control is the zero-based index of the target step.

Start the sequencer. Your controller should now control the tempo. Zero gives you half time, 127 gives you double time, and at 64 the base tempo is unaffected.

There’s lots of other fun stuff you can do with step mapping. For example, you could make an empty track, and then map each of its steps to a key on a keyboard. Pressing and holding a note on the keyboard sets the corresponding step in your pattern. How hard you hit the key determines the step’s velocity. Playing a chord on the keyboard gives you a rhythm.
Try doing this for two different tracks, each with a different prime length. For example one could have a length of five and the other a length of seven. Create two sets of step mappings, one for each track, on different ranges of the keyboard. Now you can dynamically create two rhythms, one with each hand, and they’re in polymeter. Pretty cool!

Step mapping will most likely be useful for controlling the behavior modulator tracks, for example range modulation, voicing modulation, and so on.

Wednesday, April 8, 2020

Docking bars to tabbed panes: visible but inactive

Re the issue with not being able to dock Piano and MIDI bars to a tabbed pane: I looked into that some more. It's a non-trivial issue with pros and cons that need consideration. But the short form is this: Currently, keeping many of the bars in tabbed panes may make the UI sluggish. This is especially true of the Step Values bar!

When a bar is docked to a tabbed pane, Windows considers it "visible" even if it's not the active tab, in which case it's not actually visible to the user. In my current implementation, such "visible but inactive" bars receive document updates (affecting performance), though they don't get "painted".

It seems reasonable that hidden bars shouldn't consume any CPU cycles. When a bar gets "shown" my code assumes the bar is "dirty" (meaning it wasn't being updated while it was "hidden"), and updates it. But there's a catch to that work-saving strategy: bars that show real-time events (e.g. the Piano and MIDI event bars) will miss events that occur while they're hidden. This is why when you show the Piano bar, it doesn't display notes that were already in progress by the time you showed the bar; it missed those events.

It's also why I had to disable tabbed docking for Piano and MIDI event bars. Those bars have big impact, because they oblige the sequencer to send a "monitoring" copy of the entire output MIDI sequence to the main thread. If those bars were allowed to tab-dock (or auto-hide), they would drag performance even though they might not be visible to the user. This seemed wrong to me, so I disallowed it.

It's a messy situation with competing design goals. On the one hand, it's nice if bars don't consume resources while they're "hidden". On the other hand, it's annoying that when you show the Piano or MIDI event bars, events that occurred while they were hidden are missing.

Friday, March 27, 2020

Polymeter 0.0.28 adds mapping and scales

Polymeter 0.0.28 adds two major new features: mapping, and scales. It also allows step values to be edited numerically, as opposed to by dragging bars in the Velocities pane.

Mapping

The mapping feature supports both translation and mapping to parameters. An input MIDI message can be translated to any other MIDI message, and/or mapped to any track parameter except track name.

An input MIDI message can have multiple mappings, i.e. it can be translated to multiple messages, or mapped to multiple track parameters, or to the same parameter in multiple tracks, or any combination of these. The reverse is also true: different input MIDI messages can be translated to the same output message, or mapped to the same track parameter.

Translation facilitates tweaking synthesizer parameters with a control surface. It’s likely that different songs will use different synthesizers and different modulation schemes. By setting up appropriate translations in Polymeter, the user can avoid reprogramming the control surface for each song. The control surface can always output the same messages; only the mappings need to change, and the mapping details are conveniently stored in the song’s Polymeter document. More importantly, since Polymeter can record the MIDI input as well as the dubs, a performance can be recorded as events and then completely reconstructed afterwards.

Mapping to track parameters is particularly interesting for parameters that affect the rhythmic feel, such as Offset and Swing. The Range Start parameter is another fruitful target. For some track parameters, the mapping’s range may need to be adjusted for optimal results; see below.

Mapping is managed from the new Mapping dockable bar, displayed via View/Docking Windows/Mapping. Mappings can be inserted, deleted, copied, cut, pasted, and reordered via dragging. Mappings can also be sorted by left-clicking the appropriate item in the column header.

The mappings are displayed in a grid control, similar to the track view. Mappings are selected by left-clicking in the # column. Multiple mappings can be selected with Ctrl+click or Shift+click as usual. To edit a mapping property, left-click it. Depending on the column, either a drop list or an edit box is displayed. To set a value in multiple mappings at once, select multiple mappings, and then edit the desired property within one of them.

Each mapping consists of input and output event properties, a data range, and a track selection. The input properties include the input event type (note on, control, etc.), the input MIDI channel, and the note or control number if appropriate. These properties can entered manually, or learned from the MIDI input (see below).

The output properties start with the output event type, which may be either a MIDI message (note on, control, etc.) or a track property. If the output event is a MIDI message, the mapping is a translation, in which case the output MIDI channel and the note/control number are also relevant. Otherwise, the mapping targets a track property, in which case the MIDI channel and note/control number don’t matter, but the track selection is required.

To map a MIDI message to the same parameter in multiple tracks, create multiple mappings, one for each target track. It’s easiest to do this via copy and paste: create a mapping for one of the tracks first, and when it’s set up correctly, clone it as many times as needed, and finally edit the copies to target different tracks.

The range parameters (start value and end value) allow the input MIDI data to be shifted or scaled to any integer range. If the start is bigger than end, the control direction is reversed, in which case turning the knob up makes the value smaller.

The app also supports MIDI "learn" mode, i.e. the input messages can be learned from the MIDI input device. To do this, select a mapping in the Mapping bar, and then enable learn mode via Tools/MIDI Learn, or via the keyboard shortcut Ctrl+Shift+M, or via the Mapping bar’s context menu. The background color of the selected mapping changes to green, indicating that this mapping will be learned. Now twiddle the intended knob on the MIDI input device, and the mapping’s input properties will snap to the appropriate values for that knob. To learn other mappings, select them one at a time and twiddle their intended knobs. After all mappings have been learned, disable MIDI learn mode.

Scales

The scales feature consists of three interrelated modulation types: Scale, Index, and Voicing. The primary purpose of scale modulation is to decouple pitch selection from rhythm. The rhythm comes from the target track’s steps, the pitch set comes from the scale and voicing modulation, and the pitch ordering comes from the index modulation.

When a track has one or more scale modulators, the track doesn’t get its note in the usual way. Instead, it creates a dynamic scale, consisting of the current step values of each of the scale modulators. But which scale tone is used? This determined by index modulation.

Index modulation selects one of the tones in the current scale. A track that uses a scale should also have at least one index modulator. If it has multiple index modulators, they’re summed. The resulting value is interpreted as a zero-origin index into the scale. If the index exceeds the number of scale tones in either direction, it’s wrapped around as needed.

For example, suppose the target track has four scale modulators, and their current steps contain 0, 4, 7, and 11 respectively. This constitutes a Cmaj7 chord. If the target track’s index modulator outputs 0, the first scale tone (C in this case) is selected; if it outputs 1, the second scale tone (E in this case) is selected, and so on.

The range track parameter also works differently in this case. The range if any is applied to the entire scale BEFORE the indexing operation, i.e. before the scale tone is selected.

The target track can still be note modulated. In this case the note shift is added to all of the scale tones, before range modulation and indexing.

Scale modulators can also have note modulators. These are applied first as the dynamic scale is being built.

Voicing

The scale can also be sorted and optionally converted into a drop voicing, by applying one or more voicing modulators to the target track. The voicing modulator’s steps are interpreted as follows: If the step value is one or more, it selects a scale tone to be shifted down an octave (dropped). The numbering scheme is one-origin and starts from the highest-pitched scale tone, i.e. a value of 1 drops the highest scale tone, a value of 2 drops the second-highest scale tone, and so on, as shown below:

0 or below: The scale is sorted, but no voices are dropped.
1: The highest scale tone is shifted down an octave.
2: The second-highest scale tone is shifted down an octave.
3: The third-highest scale tone is shifted down an octave.
And so on. If a voicing modulator exceeds the number of tones in the scale, it’s ignored.

Multiple voices can be dropped by applying multiple voicing modulators to the track. For example, to create a drop 2/4 voicing, apply two voicing modulators, one outputting 2 and the other outputting 4. Many other combinations are possible.

After any voice drops are applied, the scale tones are sorted again, and only then is a scale tone selected, based on the current index modulation.

Step Values

The Step Values bar lets you edit step values numerically, using a grid view. In some case this may be more convenient than dragging velocity bars in the Velocities pane. It’s particularly useful for editing note modulator tracks and similar cases.

To show or hide the Step Values bar, use View/Docking Windows/Step Values. Step values are shown for the selected tracks only. Note that selecting a large number of tracks at once may cause the Step Values bar to perform poorly. It’s recommended that the Step Values bar be hidden when it’s not needed.

To edit a step, left-click it, edit the value or type a new one, and then press one of the following keys:

Enter: Saves the edit. Escape: Discards the edit. Tab: Saves the edit and moves to the next column. Shift+Tab: Saves the edit and moves to the previous column. Ctrl+Down: Saves the edit and moves to the next row. Ctrl+Up: Saves the edit and moves to the previous row.

Various display formats can be selected using the bar’s context menu. If Signed is enabled, the steps are interpreted as signed values from −64 to +64, otherwise they’re considered unsigned values. If Notes is enabled, the steps are displayed as notes, with or without octave information, depending on whether Octaves is enabled. If Ties is enabled, the tie bit (the MSB) is included, otherwise it’s masked off; this is an advanced feature, as is Hexadecimal display.

Correct offset modulation for non-modulator tracks

In previous versions offset modulation was signed for modulators and unsigned for non-modulators, because the latter case was implemented v...