Daqarta
Data AcQuisition And Real-Time Analysis
Scope - Spectrum - Spectrogram - Signal Generator
Software for Windows
Science with your Sound Card!
The following is from the Daqarta Help system:

Features:

Oscilloscope

Spectrum Analyzer

8-Channel
Signal Generator

(Absolutely FREE!)

Spectrogram

Pitch Tracker

Pitch-to-MIDI

DaqMusiq Generator
(Free Music... Forever!)

Engine Simulator

LCR Meter

Remote Operation

DC Measurements

True RMS Voltmeter

Sound Level Meter

Frequency Counter
    Period
    Event
    Spectral Event

    Temperature
    Pressure
    MHz Frequencies

Data Logger

Waveform Averager

Histogram

Post-Stimulus Time
Histogram (PSTH)

THD Meter

IMD Meter

Precision Phase Meter

Pulse Meter

Macro System

Multi-Trace Arrays

Trigger Controls

Auto-Calibration

Spectral Peak Track

Spectrum Limit Testing

Direct-to-Disk Recording

Accessibility

Applications:

Frequency response

Distortion measurement

Speech and music

Microphone calibration

Loudspeaker test

Auditory phenomena

Musical instrument tuning

Animal sound

Evoked potentials

Rotating machinery

Automotive

Product test

Contact us about
your application!

Music_from_Anything Mini-App

Introduction:

The Music_from_Anything macro mini-app creates music from a wide variety of sources, including nothing at all. If you don't like the default results, you can adjust parameters and repeat the performance using your preferences.

No musical talent or training is required; all you need is a "try it and see" outlook.

You can record the performance as a MIDI file that can be played elsewhere on any standard player. You can make a special gift, using a special photo or poem text as a source.

Or use a personal message in your own words, or even a song, either by recording and using the sound file, or directly from a live microphone source. (Try spectrogram mode for any of these.)

Or have a competition with friends to see who can turn any agreed-upon source into the best musical production.

To run Music_from_Anything, hit the F8 key followed by the unshifted m key. Note that this macro ID is case-sensitive because the Multi_Meters macro uses an uppercase M as its ID.

Alternatively, hit CTRL+F8 to open the Macro Dialog and double-click on Music_from_Anything in the Macro List.

Some of the "anythings" to use can only be selected with the macro dialog open, by holding the ALT, CTRL, or SHIFT keys down while you double click on Music_from_Anything, or, if you have previously selected it, while you hit the macro Run button. See the Raw Data Sources section for details.

The basic strategy used by Music_from_Anything is to extract 1024 data points from the source, then manipulate those and use them to control a MIDI script that generates the actual musical performance. The raw extracted points are automatically saved to a file that allows you to repeat a performance, but with different parameters controlling the data manipulation.

There are more than 100 adjustable parameters in the main Music_from_Anything macro, many of which have 6 or 8 voice-specific characters. You don't need to change any of these parameters, but you are encouraged to explore. They can make the difference between an unsatisfying performance and one you enjoy. See the Parameter Adjustments subtopic for details.


Quick Start Reference:

Music_from_Anything needs 1024 data points to operate on. The way you start it determines where they come from. It first looks at existing conditions:

ALT key down       Mouse-Drawn Source
CTRL key down      Clipboard Image Source
SHIFT key down     Use Prior Source data
Input active       Use Live Source data
Generator active   Use Live Source data
File Open          Use as if Live Source

For any of the Live Source cases, it extracts data from the current waveform, Spectrum, or Spectrogram display mode.

Otherwise, Music_from_Anything opens a file select dialog that shows all file types present in Daqarta - User_Data. You can change the default *.* to *.WAV or *.JPG or whatever you want, as well as navigate to another folder.

Any file type is acceptable, but there is special handling for types that Music_from_Anything understands: Sound Files (WAV, DQA), Image Files (BMP, JPG), and Weaving Drafts (WIF, DTX). Any Unknown File Types are treated as "just bytes" without regard to their original use.

If you hit ESC or Cancel to skip file selection, or the selected file doesn't load for whatever reason, Music_from_Anything next looks at the Daqarta Notes area for any text you may have entered there. If it finds anything at all, whatever is there is repeated as needed to get the minimum of 1024 characters. If starting with less than 10 characters, the date and time are appended first to get more text to work with.

If there is nothing in the Notes area, Music_from_Anything checks to see if you have entered a "seed" value in Field1 (the space below Label 1) and uses that in a pseudo-random number generator that creates the needed 1024 points.

If there is nothing in Field1, a truly random value is used as the initial seed for the generator.


How It Works:

As noted in the Introduction, Music_from_Anything extracts 1024 data points from a specified source, then manipulates them to control a MIDI script that generates the actual musical performance.

The MIDI script uses up to 8 tonal voices plus 6 percussion voices. The data for each tonal voice includes two arrays of 20 values each, which become the notes to be played. A pattern length is chosen, and a block of that many notes is selected from each of the two arrays. These patterns form two different themes, phrases, or motifs of the song, played in a default AABABA sequence to form one verse.

Additional C and D patterns can be derived from A and B, and all four used in any combination, up to 20 per verse.

The particular values in the arrays are provided by the source data, but the actual notes to be played are obtained by scaling and shifting the array of original values using parameters you can adjust.

Nothing about any of the data sources causes the notes to form a musical series; the "secret sauce" is that after all other manipulations, the notes are constrained to a selected musical scale of only specific notes in each octave. Notes that do not match any of the scale notes are shifted to the nearest matching note.

In principle there are 2048 possible scales (12-bit numbers, one bit for each note of the octave, with the 'C' bit always set). But in reality there are less than 500 that are named (presumably those that at least one composer somewhere once found to be useful), and only 42 that are used by Music_from_Anything.

To appreciate the importance of scales in music, try playing random notes on a keyboard; that almost always sounds horrible (at least if the notes are truly random). The "all notes" scale is called a Chromatic scale. Now try playing only white keys (a Major scale), and the result is a marked improvement. Next try playing only the black keys (Pentatonic Major) and suddenly you can do no wrong; everything always sounds musical. (It's a traditional scale used in folk styles of the British Isles, American Mountaineers, Native Americans, and Oriental music.)

The 42 scales used here were selected from the larger set by listening tests with random note series. Pentatonic Major is there along with Major, while Chromatic is omitted. The rest did not necessarily give pleasing results under all tests, but they at least gave interesting results on some of the tests. (See GlossyScales List of Selected Musical Scales.)

Besides general musicality, scales also give a certain "feel" to music, like "bluesy" or "jazzy". Many of the selected scales are from traditional ethnic music styles. (Chinese, Japanese, Eskimo, Scottish, and 21 Indian Raga scales.)

The note series for each of the 8 tonal voices in Music_from_Anything are always independent, though they all use the same scale. By default this is selected from the data, but see the Scale subtopic below to find out how to force a particular scale.

The voices are independent in other ways: The default settings use data-selected instruments, pattern lengths, and starting times. An individual voice may run at a tempo that is slower than the overall Tempo setting by an integer multiple. After each voice completes its song, it starts over with a new instrument, a new pattern length, a new subset of notes, and a new tempo factor.

The sound can be best described as a "jam session" with a very big band, where 8 random players jam at a time. They stay with the beat and scale, but otherwise play whatever they want, for an arbitrary amount of time. After each player's bit ends, another player steps up.

This is definitely not "conventional music"... but it can be exhilarating, soothing, pleasant, interesting, boring, incomprehensible, or sometimes intolerable... just like conventional music.

With the default settings, your performance will typically start with a percussion-only lead-in, with 6 independent percussion instruments coming in at different times (but on the beat). Then the 8 tonal voices will come in, again at different times but on the beat. The performace will last about 1 minute.

That's with the default setup, but you have considerable control via the parameters in the main Music_from_Anything macro listing. You can omit selected voices to thin out the forest of sound. You can set the amount of overlap in note ranges so that each voice handles a certain portion of the total range.

If you want to create a more-conventional song you can align the voices to have the same verse lengths and starting times, and you can assign your choice of instruments to each voice. You can thus create standard song structures with separate bass, rhythm, and lead melody parts.

One thing you can not do is insert a particular series of notes. Like playing poker, you are dealt a certain hand (by the data source), and if you don't like it you can fold (use another source for the next hand), or get a few new cards (adjust parameters), but you can't force the dealer to give you a specific hand.

Some simple things can be done without modifying the main macro, like toggling selected voices off and on during the performance. That's a great way to decide which ones need to be removed later using macro parameters.

You can also toggle the performance off, then use Daqarta Fields to change the total duration, or force a certain scale, or set whether there will be percussion accompaniment, then restart.


Run-Time Display:

The above is an image of a typical running Music_from_Anything performance, scaled down to fit here. The colored horizontal lines are tonal voices, with vertical position indicating the note. The Y axis of the display area is marked to show the C notes of each octave. (See Standard Musical Note Frequencies for actual frequencies.) Although not very conspicuous in this reduced image, the background consists of horizontal black and grey lines that correspond to the black and white keys of a piano, showing each note played.

The display scrolls continuously from right to left during the performance. If it appears jerky, with gaps in the sound, it means that Daqarta is competing for Windows resources (time and memory) with some other program, even those that may be minimized to the system tray. Try closing them one at a time to find the worst offenders. See Jerky or Flickering Scrolling Display.

The length of each colored line shows the duration of the note, as indicated by the seconds marked on the X axis. This duration should be regarded as "how long the key was held down", which is not necessarily how long the sound lasts; for example, it's accurate for continuous-tone instruments like brass, woodwinds, and organs, but not for plucked or struck instruments like guitars, pianos, or timpani which decay.

The colors indicate which of the 8 tonal voices played each note, as shown by the colored squares of the Pitch-to-MIDI dialog at the right, along with the specific instrument. These instruments will change from time to time with the default setup.

Note that you can open specific Help for any Daqarta control by right-clicking it at any time.

The vertical lines are percussion voices. There are 6 such voices, denoted A-F, whose colors and instruments are shown in a separate dialog invoked by the button below Voice 8 in the main dialog. The vertical positions and lengths are fixed, with voice A at the top and voice F at the bottom.

Since a percussion voice typically has a very brief duration, the horizontal position of each line shows only the onset time. You may notice that some of the lines (like orange and yellow here) appear to be misaligned; that's due to a deliberate (but optional) random lag that is added to simulate human playing, instead of machine perfection. (The tonal voices also have this random lag applied, but it's hard to see in the image.)

The top percussion voice A, in red, shows optional drum rolls that have been enabled for this voice.

The yellow tonal voice 3 shows occasional chords as vertical stacks of 3 or 4 notes. Chords have been enabled only for this voice by default, but you can use them on any and all voices. Although not shown here, you can also elect to use arpeggio/strum to stagger the onset times of each note in the chord as in a strummed guitar; you can even select strumming up, down, or alternating.

The elapsed time (17 seconds here) is shown at the upper left of the Pitch-to-MIDI dialog, next to the Pitch-to-MIDI On/Off button. To the right of that is the MIDI Record button; see its Help topic by right-clicking for details.

The big yellow banner with "The Crying Mayfly In The Country Of Frivolity" shows the song name. It is derived from the raw data used to create the performance, and does not change when you use the same data with different parameters. You can drag this name bar around to any position you prefer.

The name's title bar is "Source: Random Seed h8BF74DCB". That value also appears in field1 under the Seed label below the song name. You can re-create this exact same performance by entering that seed number there before running Music_from_Anything. (Don't forget the leading 'h'.) The entry will vanish momentarily; then when prompted for a file name, just hit the Escape key or click Cancel. The seed value will reappear and performance will begin.

If you instead leave Field1 blank but likewise don't choose a file, a random Seed will be assigned and you'll get a completely different performance.

You'll notice other labels and fields to the right of the Seed: Time, BPM (Tempo), Scale, and Perc Prob % (Percussion Probability) Those can be changed without re-running Music_from_Anything, by first toggling the Pitch-to-MIDI On/Off button off (if the performance hasn't ended on its own yet), changing the desired field, and toggling the performance back on.


Raw Data Sources:

Music_from_Anything accepts any sort of file data, live sound data, live mouse-drawn paths, image data that has been copied to the Windows Clipboard, text entered or pasted into Daqarta's Notes area, or a "seed" number entered into Field1 and used to generate random data. If you don't choose to use any of these, a randomly-chosen seed will be entered.

You can use any file as a source, as long as it is big enough to provide 1024 data points. If Music_from_Anything understands the file type, then it uses a type-specfic strategy to select the points. For example, if it is an image file (BMP or JPG), it only extracts data from the image area and not the header. If it is a WAV file it only looks at the waveform data. If it is a weaving draft (WIF or DTX), it does a drawdown analysis. Otherwise, if the file has no standard header (such as a TXT or CSV text file) or is not recognized (such as PDF, DOC, GIF, PNG, etc) then it is treated as "just bytes" and 1024 evenly-spaced points are selected.

For sound data, whether live input (including in Pause mode) or from a file opened by Daqarta, Music_from_Anything will use the current waveform, Spectrum, or Spectrogram mode for point extraction. Note that "live" also includes Generator output, ongoing or Paused.


Live Source:

When Music_from_Anything starts up, it checks to see if there is a live source, either the sound card Input or the Generator output. It also checks to see if a file has been opened by Daqarta. If any of these is true, even if in Pause mode, it calls the _Music_Live subroutine to acquire 1024 data points.

The subroutine uses the current display mode: waveform, Spectrum, or Spectrogram when acquiring data. For waveform and Spectrum, one screen of data represents 1024 samples of raw data per input or output. All the active channels are summed together; for waveform mode that sum becomes the 1024 music data points.

For Spectrum mode things are a bit more involved. Althogh the spectrum for each channel is created from 1024 raw samples, only 512 magnitude values result. Furthermore, those are evenly spread over the entire spectrum, which for the default 48000 Hz sample rate is 0 to 24000 Hz. Most speech and music energy is at the lower end of that range, so by default _Music_Live only takes the bottom 3 kHz... only 64 of those 512 points. So to get 1024 they are simply copied over 8 times to fill the array. See the _Music_Live macro listing for how to use wider (6, 12, or 24 kHz) frequency ranges.

For Spectrogram mode the 1024 data points are read directly from the pixel colors on the screen, not from any data buffers. Thus, it makes a huge difference where you have the display sensitivity set, relative to how loud the sound is while the Spectrogram is being created. If the sensitivity is low (PgDn), the screen will be mostly black except where the sound is loud. If it is high (PgUp) then background noise may fill the screen even in supposed silence. Neither of these are intrinsically "bad", musically, but they may not be what you want. A completely black screen, in particular, leads to pretty boring performances... as you might expect. If you want the music to reflect the input signal, you should adjust the sensitivity as desired before starting Music_from_Anything.

Also, from the time a live Spectrogram input goes on, it takes a few seconds for the screen to be filled. So be sure to wait before starting Music_from_Anything.

Note that for an open file that is longer than 1024 samples, you can scroll to a point of interest before starting Music_from_Anything. For Spectrogram mode you can adjust the sensitivity while keeping the same part of the file in view, which will then be used by _Music_Live.


File Source:

If Music_from_Anything doesn't find any active sources when it starts (or a CTRL, ALT, or SHIFT key to select other options), it opens a file select dialog. By default it shows all file types in Daqarta - User_Data, but you can change the *.* to *.WAV or *.JPG or whatever you want, as well as navigate to another folder.

You can change the default file types by editing this line near the start of Music_from_Anything:

Str0="*.*"        ;File types to show

The file types that Music_from_Anything has specific support for are WAV and DQA sound files, BMP and JPG image files, and WIF and DTX weaving draft files. All other types including TXT and DAT are handled as "Unknown".


Sound File Source (WAV, DQA):

If the file type is WAV or DQA, the _Music_WAV subroutine reads the file header information to locate the sound data area and its size. It then passes that to _Music_DAT, which treats the data area the same as unknown file data and reads 1024 evenly-spaced 16-bit values from the data area.


Image File Source (BMP, JPG):

BMP files are handled by the _Music_BMP subroutine, which passes the size of the data area and its location to the _Music_DAT subroutine for extraction of 1024 evenly-spaced 16-bit data values.

JPG files are handled by the _Music_JPG subroutine, which has to locate the data area by skipping optional sections one by one until it finds the data. Although the image data is in a compressed form, no decompression is done; it is just passed to _Music_DAT for extracting the required 1024 points.


Weaving Draft File Source (WIF, DTX):

WIF and DTX weaving draft files don't hold any sort of image or map of the woven material, just information about the loom setup to weave that material. This information is in plain text, and consists of lists of which color of thread goes where, and which warp (vertical) threads will be lifted when a treadle is pressed, so the shuttle carrying the weft (horizontal) thread will pass under it.

The _Music_WIF and _Music_DTX subroutines locate, parse, read, and process this information to create a "drawdown", essentially an image of the material. However, rather than create the whole image, which could be quite large, only the locations of the 1024 points needed for Music_from_Anything are computed as needed.


Unknown File Source:

If Music_from_Anything doesn't find a specific format that it can handle, the _Music_DAT subroutine is called to extract 1024 evenly-spaced 16-bit data values from the entire file.


Mouse-Drawn Source:

If you hold down the ALT key while double-clicking Music_from_Anything in the macro list, or selecting it first and holding ALT while clicking the Run button, the _Music_Mouse subroutine is called.

This subroutine presents a black screen, and shows the message "Draw 1024 points in display area". You can click the mouse button to select individual points, or hold the button down and drag to draw continuous lines. A counter shows the total points as you go; when you hit 1024 the music performance starts immediately.

The X and Y positions and color number of each point are summed to get the value used for music creation. By default, the drawing is in "rainbow" mode, with smoothly-changing color as you draw. If you want to draw in colors you select, comment out this line in _Music_Mouse to skip rainbow mode:

Pix#Dr=5               ;Rainbow mode on, step=5

In that case, you select the drawing color by clicking on it in the vertical color bar at the right edge of the display. You can change colors as often as you want during the drawing.

If you just want to play with the drawing system, note that you can run _Music_Mouse independently of Music_from_Anything.


Clipboard Image Source:

If you hold down the CTRL key while double-clicking Music_from_Anything in the macro list, or selecting it first and holding CTRL while clicking the Run button, the _Music_Clipboard subroutine is called.

_Music_Clipboard expects an image to have been previously copied to the Windows Clipboard. If it doesn't find one, or finds one with less than 8 bits per pixel, it aborts Music_from_Anything with the message:

"No bitmap in Clipboard, or unsupported format"

Otherwise, it extracts 1024 evenly-spaced points from the image and uses them for music creation.

Using the clipboard may be more convenient than loading the same image as a file. You can use a separate image-viewing program to scroll through your images, then copy the one you want to the clipboard. You can crop out unwanted parts before the copy.


Notes Text Source:

If you don't select one of the above options, and you escape or cancel out of the file open dialog, Music_from_Anything calls the _Music_NoFile subroutine. This first looks to see if there is anything in Daqarta's Notes area. If so, it uses that text to create the 1024 data points required for music generation. If there are less than 1024 text characters (counting spaces and newlines) it repeats the text as needed. If there are less than 10 characters, it first appends the date and time to get more text to work with.

With this mode you can make music from a name, phrase, or poem. (If you have a long poem or other text already in a file, it may be easier to just open the file; Daqarta's Notes section only holds 2047 characters.)


Random Seed Source:

If _Music_NoFile doesn't find anything in the Notes area (see above), it checks to see if there is anything in Daqarta's Field1. If so, it uses that value as a random seed to create 1024 random values for music generation.

Note that the initial Field1 entry vanishes when you are presented with the file open dialog; if you escape or cancel it then reappears. Otherwise, if you use a file as a source then Field1 remains empty.

When Field1 reappears, it is always shown in hexadecimal format regardless of how it was originally entered.

If a short text string is entered instead of a number, the first 4 characters are treated like hex digits according to their ASCII values. For example, MaryLou becomes h4D617279.

Field1 persists if used, so if you run Music_from_Anything again you'll get the same performance.


No Source:

If _Music_NoFile doesn't find anything in the Notes area or in Field1 (see above), it proceeds to create a unique random seed and enters it into Field1. Then it uses that seed to create 1024 random values for music generation, as above in Random Seed Source.

Since Field1 persists if used, if you want to get a different Music_from_Anything performance on the each run you'll need to clear Field1 each time.


Prior Source:

Regardless of the source you choose, you can repeat it on the next run of Music_from_Anything by holding down the SHIFT key while double-clicking Music_from_Anything, or while clicking the Run button if Music_from_Anything is currently selected.

(Note that you must have the Macro dialog open for this to work. You can't hit the F8 key followed by SHIFT+M because the Multi_Meters macro uses an uppercase M as its ID.)

This will use the exact same 1024 data points and show the same title as the prior run, but you can change parameters in the main Music_from_Anything macro (see Parameter Adjustments below) to modify the performance as desired. You can repeat this process as many times as desired until you are satisfied with the results, as long as you don't run Music_from_Anything using any other source between times. (The raw source data and title are saved in a file called Music_Save.BUF, which is overwritten on every run. As long as you keep using the SHIFT method, the data will be the same.)

This approach is especially useful for .WIF or .DTX weaving draft files, which take a long time to analyze; repeated runs are immediate.

It's also the only way to repeat live input sources exactly, whether they are spoken words, music, or mouse-drawn creations.

Note that you can change the Auto-Backup parameter from the default Qa=1 to Qa=0. In that case everything behaves as above, except that you will be prompted for the file to save to or load from. The default will still be Music_Save, but you can change it on a case-by-case basis.

This allows you to easily switch between different data sources, whether they be repeatable but slow-loading weaving files, or transitory live sources.

It's up to you to pick meaningful names that you will remember. You may want to start all names with Music_Save but append a descriptor, so they will all appear in group in the file save or open dialogs.


Parameter Adjustments:

The main Music_from_Anything macro is essentially just a long list of parameter values that you can change, followed by a call to the _Music_Proc subroutine that actually does the work of acquiring data, processing it according to the parameters, and invoking a special MIDI script to create the music.

To change parameters, click on Music_from_Anything to select without running it, then click the Edit button. Edit the parameters you want to change, then click the Save Macro button at the bottom.

TIP: If you are hesitant about making changes and later being unable to get back to the original setup, there is a simple solution. First proceed as above, but only edit the macro name and ID, not the code. For example, change the ID to '0' and the name to 'MyMusic'. When you click Save Macro you will be prompted with:

 Macro ID/Name changed.  Save as New?
 (No = Rename existing.)

Click 'Yes' to save as a new macro, which will now appear near the top of the macro list where it's easy to find. Now edit that macro; it will still invoke the same subroutines as the original so it runs the same (except for your changes). You can use this method to make multiple versions for comparison.

Trial and Personal users: After the trial expires, you will no longer be able to save the changed macro file for use in future sessions. You can still make changes within the session, but they'll be gone when you next run Daqarta. But there is a fairly simple workaround: Open Music_from_Anything for editing and click the Copy button near the top. Then open Windows Notepad (or your choice of text editor) and paste (CTRL+Y) the macro text there. Save to an ordinary text file. Edit as desired, naming the changed text files as desired.

Now the next time you open Daqarta, you can copy the text from the editor into the Windows Clipboard, then click the New button in the macro dialog and paste it with your choice of name and ID.


Things To Try:

The following suggestions present ideas that may improve a performance, as well as address some typical issues with Music_from_Anything.


Thinning Out The Field:

The default performance may sound too "busy" or musically crowded, from too many simultaneous voices covering the same wide note range. The first thing to try is toggling different instruments off during the performance, which actually toggles that whole voice output off. It will stay off even when the instrument changes due to an update at the end of the original pattern cycle, until you manually toggle it back on or toggle Pitch-to-MIDI off and back on.

By playing around with various combinations, you can decide on a smaller field of instruments. You might want to do this after changing the ranges, below.

When you have the field you want, use Voice Enables to disable the ones you don't want, so you won't have to toggle them off manually for each performance.


Non-Overlapping Voice Ranges:

By default, all voices have the same wide note range, so they are musically stepping on each other. You can reduce or eliminate that by reducing Ur=100 percent overlap factor under Note Ranges; if you reduce it to 0 each range will be confined to 1/8 of the total range given by the Un to UN parameters.

However, the overlap factor assumes all voices are active, so if you thin out the field (see above) so that (say) only odd-numbered voices are active, you may want more overlap so that each one that is active gets 1/4 of the total. See Note Ranges for the formula. (Answer: Ur=14.)


Selected Instruments:

When thinning out the field, you may select voices with a melody you like, but the instrument is not the right choice. In that case, use the Set Instruments option to set whatever you like.

Alternatively, even it the field is not thinned, you might want to experiment with things like assigning the active voices to a set of percussive tonal instruments or some other subset of the full 128-instrument General MIDI set.

One approach to assigning specific instruments, especially if you've chosen non-overlapping ranges, is to use conventional arrangements. For example, use bass instruments for the lowest ranges, combined with (say) guitar, piano, or strings for the next-highest ranges to act as a rhythm track. You may want to throw in some chords for either or both of these ranges.

Then for midrange and higher leading lines, something with some punch like electric guitar, brass, saxophone, synth lead... or maybe human-sounding voices like Choir Aahs, Voice Oohs, Synth Voice... or soft string ensembles... or just take a chance!


Conventional Music:

Conventional voice ranges and instrument arrangements won't transform the default performance into "conventional" music. For that, the first thing you'll want is to synchronize the individual voices so there is some sort of structure. The simplest way to get started with this is to choose one voice to be the "Lead" voice in Str7[610]="????????" under Verse Construction, by replacing its ? with L. Change the other voices to p, h, or a to synchronize to the Lead pattern length, hold times pattern, or all including number of verses.

Note that these sync operations will be undone by updates with random values after each voice completes is cycle, unless you go to Tonal Voice Update Enables and either change Str7[19]="U" to Str7[19]="u" or even to Str7[19]="0" to block all updates.

In addition, under Intro and Fade-Out Delays change Str7[9]="0" to Str7[9]="I" to synchronize the intro delays to multiples of the Lead length.


Resolving A Never-Ending Intro:

A typical issue with the default setup is that you may find a great initial sound, but it seems like an endless intro that never actually resolves into anything.

One approach to fixing this is to play with the 4 Verse Construction blocks A, B, C, and D. The default uses only A and B in an AABABA sequence. You can derive C and D blocks from A and B respectively using Note Pattern Block Modification. This allows you to shift the note range of an individual block up or down in pitch, or invert the note range of all or part of a block, or to slide the position of the note pattern earlier or later in the block, or even reverse the note sequence of the block from the original A or B sequence.

Besides using the additional C and D blocks, you can get creative with the sequence; it can contain up to 20 blocks in any order.


Getting A Satisfying Ending:

Besides having the music "go somewhere", it eventually needs to come to an end. The default scheme, being based on multiple unsynchronized patterns, takes the only feasible approach by simply fading away. But if you are using a "conventional music" scheme with all patterns in sync, there are other alternatives.

One way is to have the final notes jump up an octave (or, less common, drop down). Note Pattern Block Modification allows you to shift up or down by an octave, which is 12 MIDI notes (semitones). An "L" is a 12-note shift up (where "A" is plus 1 note), while a lowercase "l" is a 12-note shift down (where "a" is minus 1 note). This affects the entire block, so you'd need to save that block for the very end of the sequence pattern. Go to Verse Construction and change Str7[540]="22222222" to Str7[540]="11111111" so there is only one run through the sequence. To get the effect of multiple verses, you'd include them directly in the block pattern. For example, if you apply this shift to block C as an end for the default AABABA sequence, you can get 2 normal verses plus the final one with the ending block via AABAAABAAABC.

You might want to slide the pattern around using the Offset controls to find the most satisfying sequence, such as one where the last few notes are rising. (Or falling, if you are shifting down an octave.)

Instead of shifting all the notes in the pattern up or down, another approach is to try an inverted note sequence. For example, if the A pattern ends in 3 descending notes, you can change the C block to invert those 3 to ascending. If the pattern is (say) 10 notes long (0-9) and you want to invert the last 3 (7-9), you use an "H" instead of "0" in the C block for each voice you want this to apply to.

With multiple voices in the performance, you can choose to apply the same inversion to all of them, or you can analyze the endings of each voice and decide whether to invert or not, and from what point.


Voice Map Parameters:

Many parameters are quoted strings, such as these two under Voice Enables:

Str7[0]="11111111"      ;Active tonal Voice 1-8 bitmap
Str7[430]="xx111111"    ;Active Percussion voice A-F bitmap

Each character in the string applies to a particular voice, but it's important to note that the numbering (or lettering, for percussion voices) starts from the right end, as shown below:

Str7[0]="11111111"      ;Active tonal Voice 1-8 bitmap
;        87654321        Voice numbers

Str7[430]="xx111111"    ;Active Percussion voice A-F bitmap
;          xxFEDCBA      Percussion voice letters

Also note that since there are only 6 percussion voices, there are two "x" characters at the left end as placeholders.

Some voice-mapped parameters use "?" to indicate that the default data-controlled value should be used, versus a digit or letter to set a specific value:

Str7[530]="????????"    ;Tonal intro delay 0-Z = 0-35, ?=Ut,UT

Max and Min Parameters:

Note that many parameters are controlled by "max" and "min" limits rather thant specific values, unless you set both limits the same. The actual value used for the performance is selected based on the relevant value from the 1024-point data array. For example, if the max is UV=80 and the min is Uv=48, and the data value is 123 out of a possible UZ peak data array value of, say, 255, the chosen value is found by multiplying the data value by (UV-Uv)/UZ + Uv to get (123 * (80-48)/255 + 48) = 63 (rounded down).


Random Update Effects:

Many of the parameters that are set initially from the 1024 source data points, can be updated after each song cycle of a tonal or percussion voice. A pseudo-random number generator provides the raw data in this case, which is then scaled to the parameter-set limits just as the source data was.

Because the generator is pseudo-random, the performance will repeat exactly every time you play it. The pseudo-random generator behaves just like dealing cards from a well-shuffled deck of 4.29 billion cards. Each time you start the performance, it uses the same deck and starts at the same point, as determined by a "seed" value. From then on, the cards always come out in the same sequence.

You can change which parameters, in which voices, will get these updates, via the Tonal Voice Update Enables and Percussion Update Enables.

But note what happens when you deal a "player" in or out by enabling or disabling its updates: If you cut a player out, for example, the pseudo-random dealer deals those same cards to other players, which could be different features in the same voice, or could be other voices in turn. In other words, changing even one enable state changes every update after that, for the rest of the performance.

This can be good or bad: By mixing things up, you might get a performance you like better. But if you already had one that you really liked and just wanted to add a little tweak, you might be unpleasantly surprised.


Auto-Backup:

Qa=1            ;1=Auto-Backup, 0=manual file select

This parameter controls the behavior of the automatic data backup system. With the default setting, every source you send through Music_from_Anything is captured to a backup file that allows you to re-use it with different parameters on subsequent runs. (See the Prior Source topic, above, for more details.) This can be important for weaving files, which take a long time to analyze, or for live input sources that can't be repeated.

Changing this parameter to Qa=0 doesn't stop the backups, it just allows you to choose another name than the default Music_Save.BUF. That way you can have multiple files for different weaves or sources.


Search String:

Str0="*.*"             ;File types to show

This is the search string for the File Open dialog. It will show everything in the Daqarta - User_Data folder by default. If you are working with one file type, such as .JPG, just change it to Str0="*.JPG". That will save you having to do that manually in the File Open dialog every time. You will still need to navigate to the target folder, however.


Play Duration:

Field2="1:00"    ;Play duration (1 minute)

You can set this as long as you like. Note that if you use a single colon as above, it will be interpreted as minutes and seconds. If you want it to be treated as hours and minutes, append an 'a' or use a dual colon format like 1:00:00.

Alternatively, you can enter the desired number of seconds, without a colon.

Entering "-1" instead of a time will cause the performance to last about 16 years.

This value will be shown in the "Time" field (Field2). The Time value is only considered at the start of the performance. If you enter a new value while it is playing, that value is ignored unless it is a -1 to extend the performance indefinitely. Otherwise, you can stop the performance by toggling Pitch-to-MIDI off, then enter a new value and toggle Pitch-to-MIDI back on. The performance will start over and run for the new duration. Or you can wait for the performance to stop after the original duration, then set a new value and start it again. You do not need to re-run Music_from_Anything to change duration.

Str7[29]="0"         ;"D" sets overall Duration to Lead voice

If the "0" is replaced with "D", this overrides the Time field and sets the duration so that the performance will end after a selected voice (the Lead voice) ends. (See Verse Construction for details on setting the Lead voice.) This duration is computed at the start of the performance and replaces the Field2 entry.

The duration is the Lead pattern length times hold beats times the number of verses times the AABABA sequence count, plus its intro delay.

If no Lead voice has been set, the duration will be that of the voice whose duration is longest.

Just as described previously, any value you enter into Field2 during play is ignored, except -1 to extend the performance indefinitely. The difference here is that if you stop the performance (or let it end) and enter a new time, it will again be replaced when the performance is started... you have to change the "D" back to "0" in Music_from_Anything and re-run to change to a specific duration.


Tempo:

Field3=" "          ;BPM (default 200-300 range from data)

The default Tempo is in the 200-300 BPM range, with the specific value chosen by averaging two independent values from the 1024-sample data array. The averaging process tends to keep the tempo near the middle of the range.

You can force a specific tempo by entering it in Field3, such as Field3="245". You will also be able to change the tempo later, by stopping the performance and entering a new value.

You can keep the data-driven process but change the range by entering the limits as the integer and fraction parts of a number, such as Field3="150.250" for a range of 150-250 BPM. Note that the order of the limits does not matter, but the fraction term must have exactly 3 digits. If it is less than 100, use leading digits as in Field3="70.090".


Scale:

Field4=" "      ;Scale = Auto unless 2048 to 4095

A big secret of Music_from_Anything (as well as the related GlossyTracks MIDI setup) is the use of a scale to keep things coherently musical. See How It Works for comments about this. The default " " tells Music_from_Anything to choose a scale based on the source data, but you can force a particular scale by entering its number instead. The default will choose one of the 42 selected GlossyScales, which are highly recommended. If you are more adventuresome, AllScales has 532 to choose from. You can even invent your own following the format used for these lists.

Before you change the scale here, however, you can try some quick tests during the performance. Just enter the desired scale number into Field4, labeled Scale (<4096), and restart the performance by clicking Pitch-to-MIDI off and back on. Try 2708 (Pentatonic Major) if in doubt.


Percussion Probability:

Field5="80"     ;Percussion probability, percent

By default there is an 80 percent probability that percussion will be used, as set by Field5="80". You can set it to zero or 100 percent to be certain of the outcome, or anything in between. An empty Field5=" " is considered 0%.

The probability is not dependent on any source data; it is determined in the _MusicData.DQM MIDI script that Music_from_Anything invokes, choosing a random value that has Field5 percent probability of being above 0.

You can stop a performance by toggling Pitch-to-MIDI off (or wait for it to end on its own), then enter a new value and toggle Pitch-to-MIDI back on. The performance will start over with the new percussion probability.


Voice Enables:

;Voice enables:
Str7[0]="11111111"          ;Active tonal Voice 1-8 bitmap
Str7[430]="xx111111"        ;Active percussion voice A-F bitmap

These two parameters are "maps" of which tonal and percussion voices will be used for the performance. They are all active by default; to disable a voice, replace its "1" with "0".

These settings affect the initial voices, but you can change things during the performance by toggling voices off or on in the Pitch-to-MIDI dialog or the Percussion Setup dialog (opened via the "Percussion" button at the lower left of the Pitch-to-MIDI dialog.)

The all-voice default may have too much "sonic clutter" for your taste. To find a better selection you can leave all voices active initially, and experimentally toggle certain voices off during the performance to see what sounds best.


Intro and Fade-Out Delays:

;Intro and fade-out delays:
Ut=10           ;Min tonal intro delay
UT=50           ;Max tonal intro delay
Qt=0            ;Min percussion intro delay
QT=10           ;Max percussion intro delay
Str7[530]="????????"    ;Tonal intro delay 0-Z = 0-35, ?=Ut,UT
Str7[580]="xx??????"    ;Perc intro delay 0-Z = 0-35, ?=Qt,QT
Str7[9]="0"     ;"I" syncs Intro delays with Lead voice
UF=5            ;Final fade-out, seconds

The individual tonal and percussion voices start at different times by default, with adjustable ranges for each type. Using the default values, the percussion voices A-F will come in first, over a range from Qt=0 to QT=10 beats, followed by the tonal voices 1-8 over a range from Ut=10 to UT=50 beats. You can set all these values to 0 to have everything start at once, or set (say) Ut and UT to the same value so that all tonal voices start at the same time after the percussion voices start, or whatever you like.

You can optionally set specific voices to start at specific times by changing Str7[530]="????????" and/or Str7[580]="xx??????" to assign the desired times to each voice. As described under Voice Map Parameters, tonal voice 1 is at the right end of Str7[530] and percussion voice A is at the right end of Str7[580]. At any voice position, you can enter a digit from 0-9 for delays of 0-9 beats, or an uppercase letter from A-Z for 10-35 beats. These values will override the range limits for those voices, while all voices with ? will continue to use their range limits.

If the "0" is replaced by "I" in Str7[9]="0", all voices will be synced to the Lead tonal voice, if any, set via Str7[610] under Verse Construction. In this case, all delay values change from counts of individual beats to counts of pattern lengths, so that the percussion and tonal voices come in on multiples of the Lead voice (Note Pattern length times Tonal Hold Beats, if any). In that case the above values will probably be longer than you want. Try reducing them by a factor of 5 or 10.

At the end of the Play Duration (default 1 minute), as shown by the elapsed time readout to the left of the Pitch-to-MIDI On/Off button, the volumes of the individual tonal and all precussion voices fade out over a period of UF=5 seconds, followed by a brief period of silence before the performance is automatically toggled off.


Volume:

;Volume (Level):
Uv=48           ;Min tonal voice Volume
UV=80           ;Max tonal voice Volume
Qm=48           ;Min Percusion Volume
QM=64           ;Max Percussion Volume

Uv and UV set the limits of the randomly-selected volume (Voice Level) for each tonal voice 1-8. These are shown in the Level column at the right side of the Pitch-to-MIDI dialog, and change when they are updated during the performance.

All 6 percussion instruments share one overall volume (Percussion Level), the limits of which are set by Qm and QM.

Relative levels between instruments are also affected by their velocity values, controlled by the Velocity Map as well as the data.

Of course, the overall volume is also controlled by the main volume control slider dialog (F9 key), which is mirrored by the MIDI Volume control at the lower left of the Pitch-to-MIDI dialog.


Note Ranges:

;Note ranges:
Un=28           ;Min note
UN=80           ;Max note
Ur=100          ;Percent voice note range overlap

Musical notes here use the MIDI system, which assigns integers in the range of 0 to 127 to the standard equal-tempered frequencies used by Western music. (See Standard Musical Note Frequencies for the note names, MIDI numbers, and frequencies of an ordinary piano.)

Each tonal voice 1-8 has it own series of notes derived from the 1024-point data array. By default the notes range from Un=28 (E1, 41.203 Hz) to UN=80 (G#5, 830.609 Hz), and because Ur=100 all voices share the same range.

Setting Ur=0 forces each voice into 1/8 of the total range (rounded as needed), and values in between 0 and 100 produce varying overlap with adjacent voices. Voice 1 is the lowest and Voice 8 the highest, but that also depends upon the particular data array values: Voice 1 could be dealt a run of high values, and Voice 8 a run of low values, but this is not likely.

You can set voices so that their ranges would overlap, but disable adjacent voices (see Voice Enables, above) so the ones that remain active will overlap less or not at all.

The fraction K of the total range that will be assigned to each voice (active or not) is given by:

K=1/8 + 7/8 * Ur/100

So if you have 4 voices (all odd or all even numbers) and you want K to be 1/4, the second term needs to resolve to 1/8, so Ur/100 must be 1/7, or Ur=14. (Actually closer to 14.3, but Ur is an integer.)

Additionally, you can apply different shifts up or down to all the notes in each voice; see Note Pattern Block Modification, below.


Note Patterns:

;Note patterns:
UL=7            ;Min note pattern length (1 min)
UU=11           ;Max note pattern length (20 max)
Str7[520]="????????"    ;Set pattern lengths 1-K = 1-20, ?=UL,UU
Str7[160]="0_8_8_8_8_8_88880_4_4_6_6_6_8888"   ;Velocity map

The 1024-point data array includes 40 notes for each voice, 20 in an A block and 20 in a B block, from which a subset of each is chosen for actual playing. The subsets both have the same length, chosen to be between UL=7 and UU=11. These subsets are called "note patterns" here, but musically each can be considered a "theme", "phrase", or "motif" from which the overall melody or verse is constructed.

Str7[520]="????????" allows you to set specific initial pattern lengths for any tonal voice. You can enter a digit from 1-9, or uppercase letters A-K for 10-20. (Anything outside this will be limited to the 1-20 range.) Voices with "?" set will use the data-driven UL to UU values.

There are also C and D blocks derived from the A and B blocks, respectively, by shifting their notes up or down in pitch, inverting the note range of all or part of a block, and/or rotating the blocks left or right so that a different series of notes will be within a pattern. See the Note Pattern Block Modification discussion, below.

In addition to blocks A-D, there is a separate block of 20 velocity values for each voice, which shares the same pattern length as above. These values control the loudness of the individual notes at the corresponding point in each pattern. However, since the data may provide a wide range of values, whereas most music has little volume change between notes, the data values are scaled and processed by a Velocity Map that translates each value to a more limited range. The default map for Music_from_Anything is given by:

Str7[160]="0_8_8_8_8_8_88880_4_4_6_6_6_8888"    ;Velocity map

The left half of the map (16 positions) is used for tonal voice notes, while the right 16 are used for percussion. Note velocity values are scaled to be in the range of 0-15; when the Nth note of a pattern is to be played, the Nth scaled velocity value is read and used as an index into the map. Suppose the value is 4; the map character at position 4 (where the 0th is the "0" on the left) is "8", which indicates that the note is to be sounded at full loudness. The "0" means silence, a musical rest, with the note value ignored.

The "_" characters are not spacers, they are sustains; if a pattern note ends up paired with a sustain, that note is ignored and the prior note just continues until the next non-sustain note.

Looking at the first 16 characters in the map, any given note has a 1/16 chance of drawing a "0", a 6/16 chance of a sustain, and a 9/16 chance of a full-loudness "8".


Tonal Hold Beats:

;Tonal Hold beats:
UH=-4                  ;Negative Hold Beats limit (random bias)
Uh=4                   ;Positive Hold limit
Str7[360]="????????"   ;Set Hold Beats (?=UH,Uh)

Hold Beats sets the number of beats a note is to be held. It is effectively the same as slowing the Tempo by this factor, except it works on an individual Voice Setup.

This applies to all notes, including sustains. If Hold Beats is 3, all single notes are 3 beats long, whereas a note that is followed by one sustain (see above) and would thus normally be two beats long, is now multplied by the Hold Beats value and becomes 6 beats long.

The UH and Uh limits are for the default random scheme used in the _MusicData.DQM MIDI setup that is called to create the music. For each voice, it chooses a random value between UH and Uh to set its internal Hold Beats control. However, the control only accepts values from 1 to 127, so 0 or negative values are limited to 1. This provides a simple random bias: The random generator produces a uniform distribution of values; using the -4 to 4 default range the possible outcomes are -4, -3, -2, -1, 0, 1, 2, 3, 4. So 2/3 of the time (6/9) it will end up as 1, and 1/3 of the time it will be either 2, 3, or 4. See Controlling Probabilities under the Random Values topic.

If you want the same Hold Beats for all voices, set both UH and Uh to the desired value.

If you prefer specific Hold Beats for each voice, the Str7[360] parameter overrides the random selection. The default "????????" string indicates that all voices will use the random values. If you instead enter a digit from 0-9 at any position, that value will always be used for Hold Beats for the relevant voice. Again, note that Voice 1 is on the right end and Voice 8 on the left, as described under Voice Map Parameters.


Note or Percussion Lag:

;Note or Percussion Lag:
Ug=2            ;Max lag (limit 15)

When multiple humans play music together, they naturally have minor variations in timing throughout the performance. A note might come a bit early or late compared to an imaginary (or real) metronome, or to the average timing of the other musicians. This lack of "perfection" contributes to performaces sounding human-like, instead of robotic.

The lag setting causes the notes of a tonal voice, or the hits of a percussion voice, to start slightly behind the beat. Since each voice can have a different lag, a voice can appear to lead if its lag is less than the average of all voices.

The actual lag will be set by a data value for each voice, scaled to fall in the range of 0 to the Ug value. That lag will persist for one complete cycle of that voice, and will then be updated with a random value over the same range.

The amount of lag is in units of one Trace Update interval, which is 10 msec by default. This is about the threshold for detection of timing differences in normal music.

This is not really a true approximation of human timing looseness, which can vary from beat to beat for the same voice, but nevertheless it's something to experiment with to make a performance less machine-like.


Set Instruments:

;Set instruments, 255 = use data file value:
Str7[100]#b=255            ;Voice 1 inst
Str7[101]#b=255            ;Voice 2 inst
Str7[102]#b=255            ;Voice 3 inst
Str7[103]#b=255            ;Voice 4 inst
Str7[104]#b=255            ;Voice 5 inst
Str7[105]#b=255            ;Voice 6 inst
Str7[106]#b=255            ;Voice 7 inst
Str7[107]#b=255            ;Voice 8 inst
Str7[108]#b=255            ;Perc A inst (27-87 valid, 0 disables)
Str7[109]#b=255            ;Perc B inst
Str7[110]#b=255            ;Perc C inst
Str7[111]#b=255            ;Perc D inst
Str7[112]#b=255            ;Perc E inst
Str7[113]#b=255            ;Perc F inst

If you want to change a default instrument from that set by the source data, enter the desired instrument number for the relevant voice. The default 255 values are not valid instruments numbers so Music_from_Anything ignores them and chooses values based on the data.

General MIDI Instrument Sounds shows the numbers for each of the 128 tonal instruments for Voices 1 to 8. Note that some of listed instruments will not appear in a Music_from_Anything performance unless you force them here. That's because they are excluded in the GlossyInst.TXT file that it works from. That file is used by the GlossyTracks.DQM MIDI Setup and was created from listening tests to choose the most musically-useful instruments... ones that wouldn't mess up an otherwise good performance. But you may want to experiment with these, at least for fun if not for good music:

    109  ;Bagpipe

    111  ;Shanai

    119  ;Reverse Cymbal
    120  ;Guitar Fret Noise
    121  ;Breath Noise
    122  ;Seashore
    123  ;Bird Tweet
    124  ;Telephone Ring
    125  ;Helicopter
    126  ;Applause
    127  ;Gunshot

General MIDI Percussion Sounds lists the possible instruments for percussion voices A-F. Again, a couple of these won't be used unless you set them explicitly, since they are excluded in the GlossyPerc.TXT file:

    32   ;Click 1

    72   ;Long Whistle

Notice from the list that the General MIDI standard doesn't provide for percussion instrument numbers below 27 or above 87, and the default MIDI synthesizer in Windows ignores them. If you try to enter one of these, Music_from_Anything will ignore your entry and choose an instrument from GlossyPerc.TXT based on the data.


Chords:

;Chords
Str7[120]="00000400"        ;Chord enable voice bitmap
Str7[130]="00000200"        ;Initial chord position bitmap
Str7[140]="00000000"        ;Arpeggio enable/count bitmap
Str7[150]="00000000"        ;Arpeggio direction bitmap
Str7[200]="MMMMMMmmmm777S56................"    ;Chord map

Any or all of the tonal voices can include chords. Str7[120]="00000400" is a map that enables chords for each voice position that holds a non-zero character. Voice 1 is on the right, so the "4" enables chords for Voice 3.

The "4" value itself specifies that a chord should sound on every 4th note played. Valid characters are "0"-"9", "A"-"Z", and "a"-"z"; anything else is considered "0" for no chords. For the moment we will consider only numerals.

The 1024-point source data array includes 20 values for each of the tonal voices 1-8 to be used for chords. These operate like velocity values, in that they don't directly specify chord parameters. Instead, they are scaled to the 0-31 range and used to select one of 32 characters from a Chord Map.

When Str7[120] contains a numeral "1" to "9" to specify a chord repeat count, the raw chord data in the array is scaled to a 0-15 range and used to select one of the first 16 characters in the Chord Map:

Str7[200]="MMMMMMmmmm777S56................"    ;Chord map

Notice that these 16 are all alphanumerics, which encode valid chords. The Chord Pattern Characters table shows 60 different chord characters, plus the names of the chords they refer to and the added notes that will be played along with the root (in semitones above the root). The root is the note that would otherwise be played alone, if no chord was used.

The characters used in this default Chord Map are:

Symbol  Name         Added Notes
  M    Major          4, 7
  m    minor          3, 7
  7    7th            4, 7, 10
  S    M7             4, 7, 11
  5    5th (power)    7
  6    M6             4, 7, 9

If a letter is used instead of a numeral (like "4") in Str7[120], the rules change: The letters "A"-"Z" are converted to numbers 1-26, and "a"-"e" to 27-31. These no longer select chord spacing, but instead specify that the raw data be scaled to use different ranges of the 32-character Chord Map. The 0th character is always included, plus additional characters up to the letter-derived number. So "A" means scale the data so that either "M" or "m" is selected (map characters 0 and 1), while "e" scales the data to select any of the 32 map characters... including the "." dots in the second half, which mean omit the chord and only play the original note.

So if you use "A" = 1 through "O" = 15, then there will be a chord for every note played, one of the initial 0-15 characters in the map. As you go to using more of the map, the probability of a non-chord increases, up to 50% with "e".

Letters "f"-"z" work in the opposite direction, always including character 31 with increasingly less of the lower end. "f" uses 1-31, "g" uses 2-31, and so on up to "z" using 21-31. Note that with the current map, anything above "u" = 16-31 would be pointless, since everything would be mapped to "." for no chord. These options are present to allow a wide variety of other chord maps.

Str7[130]="00000200" maps the starting position for the chord for each voice. Valid characters are "0" through "9"; anything else is regarded as "0". Here the "2" specifies that the chord in Voice 3 should start on note 2 (counting from 0), so together with the above "4" for every 4th note, the first 2 notes (0 and 1) would have no chord, then a chord on note 2, then skip 3 notes and play a chord on note 6 and every 4th note thereafter in the voice pattern.

The starting position also applies to the non-counted letter-based map range options; it simply forces earlier notes than the start to be played without chords.


Arpeggio or Strum for Chords:

Str7[140]="00000000"        ;Arpeggio enable/count bitmap
Str7[150]="00000000"        ;Arpeggio direction bitmap

Arpeggio is where the notes of the chord are sounded one by one instead of all at once. This is what happens naturally when a guitar chord is strummed, and like the guitar example you can strum up or down, or alternate.

A character "1"-"9" or "A"-"F" in Str7[140]="00000000" enables arpeggio for any voice that is already enabled for chords in Str7[120]. The character sets the delay between the notes of the chord, in multiples of the Trace Update interval (10 msec default). "1" (10 msec between note starts) is a very rapid strum, "9" (90 msec) is a slow strum. "A"-"F" are 100-150 msec.

When the root note interval ends, all the chord components end as well. With arpeggio, this means that the later notes have shorter durations, which can give a rather interesting "blip" effect. With long delays, the root may end before some of the components even get started. These delays may be useful for root notes with sustain, either due to a velocity sustain character following the root, or due to the use of Hold Beats to slow down the entire voice.

Str7[150]="00000000" sets the direction of the arpeggio or strum. Here "up" and "down" refer to ascending or descending pitch. When a standard guitar is strummed physically downward the pitch ascends, which would be called "up".

    "0" = Up only
    "1" = Down only
    "2" = Up, then alternate
    "3" = Down, then alternate

Verse Construction:

;Tonal Verse Construction:
Str7[240]="AABABA"         ;Pattern sequence, 20 max, ABCD
Str7[540]="22222222"       ;Tonal Verses, 0-Z = 0-35
Str7[550]="44444444"       ;Tonal Sustain, 0-Z = 0-35
Str7[560]="44444444"       ;Tonal Rest, 0-Z = 0-35, &=indefinite
Str7[610]="????????"       ;Tonal sync: L, a, h, p, or ?=data

Each voice plays a cycle consisting of one or more verses, and each verse is made up of shorter segments called note patterns A, B, C, and/or D. The default verse uses only A and B, and only 6 of these total, as set by Str7[240]="AABABA". But a verse can use up to 20 total, with any mix of the four letters, in any order.

Conventional song structures often have two different segments that are combined to make each verse, with the combination described as "AABA" or "ABBA" or whatever. Those segments are typically quite a bit longer than the pattern lengths used here, even if the upper limit of 20 notes is used in the pattern. To emulate a conventional structure like "AABA" using these shorter segments, consider combining 2 or more patterns in various combinations. For example use "ABAB" as the conventional "A" section, and "CDCD" as the "B". Then use Str7[240]="ABABABABCDCDABAB" to emulate a conventional "AABA" verse:

    ABAB ABAB CDCD ABAB
     A    A    B    A

Str7[540]="22222222" sets the number of verses per cycle, which are played one immediately after the other. You can change any and all voices to a different number of verses by changing its default character in the voice map. Digits 0-9 set 0-9 verses, while uppercase letters A-Z set 10-35 verses. Note that if you set 0, the voice is disabled.

Str7[550]="44444444" sets a sustain value for the final note after all verses are over. The "sustained" note might only be obvious if the instrument has natural sustain, like an organ or a horn, while a plucked or struck string like a piano will decay away much more quickly. As with the number of verses, you can set values from 0 (no sustain) to 35 beats.

After the sustain, if any, Str7[560]="44444444" sets the number of rest (silence) beats before the voice is updated to begin a new cycle. Like the number of verses or sustain beats, you can set the rest from 0 to 35 beats. But here there is an additional option: "&" sets the rest for that voice to last indefinitely. (It goes on & on & on...) This allows for variety in performance endings, such as voices that drop out one by one.

Str7[610]="????????" sets voices to synchronize with a selected Lead voice, designated by setting its character to "L".

Other voices can be synchronized in various ways with the Lead voice. A voice set to "p" will have its pattern length forced to match the Lead pattern length. An "h" forces both the pattern length and hold beats to match those of the Lead, while "a" forces pattern length, hold beats, and number of verses to match the Lead.

Note: If you set more than one "L", only the lowest, rightmost voice is used; other "L" voices behave if they were still set to "?". On the other hand, if you don't set any voice to "L" but you do set one or more to "a", the lowest of those will become the Lead voice.

Besides establishing a reference for "p", "h", and "a" voice synchronization, the Lead is also the voice that sets the overall performance time when Str7[29]="D" is set in Play Duration.

Likewise, setting Str7[9]="I" will cause Intro delays to become multiples of the Lead pattern length times hold beats.


Note Pattern Block Modification:

;Note pattern block shift, offset, and invert:
Str7[280]="00000000"       ;Pattern A note shift
Str7[290]="00000000"       ;Pattern B note shift
Str7[300]="00000000"       ;Pattern C note shift
Str7[310]="00000000"       ;Pattern D note shift
Str7[320]="00000000"       ;Pattern A offset
Str7[330]="00000000"       ;Pattern B offset
Str7[340]="00000000"       ;Pattern C offset
Str7[350]="00000000"       ;Pattern D offset
Str7[480]="00000000"       ;Pattern A invert
Str7[490]="00000000"       ;Pattern B invert
Str7[500]="00000000"       ;Pattern C invert
Str7[510]="00000000"       ;Pattern D invert
Str7[630]="????????"       ;A=copy block A to B, B= B to A

The Str7[240]="AABABA" pattern sequence was discussed previously under Verse Construction. Here, we discuss ways to modify the individual ABCD patterns for each tonal voice, from those derived strictly from the 1024-point source data.

Each of the ABCD patterns is extracted from a separate 20-value note array or block, four for each voice. The A and B blocks are supplied by the source data, while C and D are derived from A and B, respectively. They default to exact copies unless modified by the methods here, though you may choose to modify A and B while leaving C and D in original form.

The initial note patterns for the voice are taken from the start of each array, according to the pattern length derived from the raw data via the UL and UU parameters, or set manually via Str7[520]="????????", as discussed in the Note Patterns section. Subsequent updates, if enabled, can extract patterns of different lengths from different parts of each note array. Hence, modifying the arrays will affect the entire performance.

The first modification approach is to shift all the notes in the 20-value note array up or down in pitch, which will likewise shift any chosen note pattern subset.

Str7[280]="00000000" shifts the notes of the A array for any voice, when the default "0" at its position is replaced by a letter. (Voice 1 is at the right end.) Uppercase letters "A"-"Z" shift all notes up by 1-26, while lowercase letters "a"-"z" shift all notes down by 1-26. The units here are "MIDI note numbers", which are identical to musical semitones. So uppercase "L" will shift everything up by 12 semitones, which is one octave, while lowercase "l" will shift down by an octave.

Please note that these shifts are applied to the raw note values, prior to the application of the selected Scale. That means that the final played notes will always conform to the scale, but some notes might not be shifted by the amount specified. Some might not even be shifted at all. Consider the Raga Valaji scale (2198) with the format C^^^E^^G^Ab^ where ^ marks unused notes. An initial E that is shifted down by one note will still be nearer to the original than to C, so will be rounded back to E when the scale is applied. The same would be true if it had been shifted up by one note. Since different notes will usually be affected differently by the scale application, the overall melody will change.

The second modification approach is to change the position and/or order of the notes in an array. The Offset control Str7[320]="00000000" can slide the note sequence of the A block of any voice to the right, by replacing the default "0" at the voice position with a letter. Uppercase "A"-"Z" slide by 1-26 positions. This is actually a "rotate" since any notes that move off the upper end of the array will slide in at the bottom.

Lowercase "a"-"z" also slide/rotate right by 1-26 positions, but first reverse the sequence of the entire array. Since the raw data only contains 20 positions, you might wonder about the need to move more than 19. But, as noted above, this is actually a rotation. This provides a way to reverse the sequence (which requires a lowercase letter) without also sliding it anywhere: just rotate by 20 positions. (That's a "t".)

The third modification approach inverts the note range over all or part of an array; high notes become low notes and ascending sequences become descending, and vice-versa.

Str7[480]="00000000" inverts notes in the A array for any voice when the default "0" at its position is replaced by a letter. Uppercase letters "A"-"T" encode positions 0-19, and invert the array starting from that position. So "A" inverts everything, "B" leaves the initial note as-is but inverts the rest, and "T" inverts only the last note.

Conversely, lowercase letters "a"-"t" encode the same positions, but invert the array up to and including the given position. So "a" inverts only the initial note, "b" inverts the first 2 notes, and "t" inverts everything. (Equivalent to "A" above.)

Str7[630]="????????" modifies the original A and B blocks by copying one over the other. An "A" at any voice position will copy the A block to the B block, giving two identical A blocks. "B" does the reverse, giving two B blocks.

You might want do this to keep the A and B patterns (and hence the C and D patterns) more similar, differing only in the note shift, offset, and invert modifications discussed above.


Tonal Voice Update Enables:

;Tonal voice update enables:
Str7[19]="U"               ;Enable updates. "u" omits pattern and hold
Str7[10]="11111111"        ;Instrument update enables
Str7[20]="11111111"        ;Note hold update enables
Str7[30]="11111111"        ;Volume update enables
Str7[40]="11111111"        ;Note lag update enables
Str7[50]="11111111"        ;Note pattern length update enables
Str7[60]="11111111"        ;Pattern offset bias update enables
Str7[70]="11111111"        ;Note velocity update enables
Str7[80]="11111111"        ;Pattern A update enables
Str7[90]="11111111"        ;Pattern B update enables
Str7[260]="11111111"       ;Pattern C update enables
Str7[270]="11111111"       ;Pattern D update enables

Str7[19]="U" specifies that after each tonal voice cycle ends, the next cycle begins with new random parameters. If the "U" is replaced with "u", the note hold beats and pattern length are locked to their original settings, but all other parameters will be updated. If "0" is used instead, all updates are blocked.

However, parameters enabled by the "U" or "u" can still be disabled by setting the voice character of a specific parameter to "0" from its default of "1".

Note that the "U", "u", or "0" update setting also applies to percussion voice updates.

Updates choose values randomly, within whatever limits were set initially for that value.

Instruments are chosen randomly from the GlossyInst.TXT list; there are no separate range limits. See Set Instruments, above.

Note hold beats are updated with a random value in the range set by UH=-4 and Uh=4 (or whatever values you choose), using the same biased random scheme as for the original data-driven setting.

Volume limits Uv=48 to UV=80 will cause an update to choose a random value within this range. (As opposed to the initial data-driven value scaled to be within that range.)

Note lag, note pattern length, and note velocity behave the same with their respective limits.

Pattern offset has no user-adjustable limit; the initial offset is 0, meaning that the pattern starts at the beginning of the array, and updates are limited to keep the pattern within the specified 20-note A-D array. You can choose to disable pattern length updates, then let pattern offset simply move that "window" around the array.

Pattern update enables A-D are 0 or 1 toggles that enable the pattern length and offset to be applied to these patterns.


Percussion Parameters:

;Percussion parameters:
Up=4            ;Min Perc pattern length (1 min)
UP=9            ;Max Perc pattern length (20 max)
QH=-4           ;Negative Perc Hold limit (random bias)
Qh=4            ;Positive Perc Hold limit
Str7[380]="xx?????0"   ;Set Perc Holds (?=random QH,Qh)
Str7[570]="xx??????"   ;Perc Pattern lengths, 1-K = 1-20, ?=Up,UP
Str7[590]="xx444444"   ;Perc repeat count (verses), 0-Z = 0-35
Str7[600]="xx000000"   ;Perc Rest, 0-Z = 0-35, &=indefinite
Str7[39]="0"           ;"S" = Tonal Sequence count cycle
Str7[620]="xx??????"   ;Perc sync: a, h, p, or ?=data

Percussion voices don't have notes like tonal voices, but they do have velocities. Velocity (and hence loudness) differences between hits are typically much more common than those between notes in tonal music.

Each of the six percussion voices has its own 20-value velocity array derived from the 1024-point data array. By default, each voice uses its own subset pattern of its array, with a length that can vary from Up=4 to UP=9.

As with tonal voices, the velocity data is scaled to a 0-15 range, but shifted up to 16-31 to select from the upper half of the common Velocity Map that it shares with the tonal voices that use the lower half:

Str7[160]="0_8_8_8_8_8_88880_4_4_6_6_6_8888"    ;Velocity map

When the Nth hit of a pattern is to be played, the Nth scaled velocity value is read and used as an index into the map. Suppose the scaled value is 8; it is shifted up to 16+8 to point to the map character at position 24, which is a "6" to indicate that the hit should have a velocity of 96 out of 127. (See the Velocity Pattern Character Table.)

As with the tonal voices, a "0" in the map means silence, so there is no hit. The "_" underscore characters are sustains, but since percussion instruments don't have much natural sustain these mostly act like spacers or rests.

In addition, the percussion pattern can be "stretched" by by a Hold factor, such that it slows down the tempo of the voice by that factor. Similar to tonal voices (see Tonal Hold Beats, above), a negative "bias" parameter QH=-4 is used with a positive upper limit Qh=4 to allow most voices to have no hold (actually a hold of 1) and thus run at full tempo, with a probability of 2/3, while 1/3 of the time it will be either 2, 3, or 4. The more negative you make Qh, the greater the chance of a negative result and the more probable the full-tempo rate.

If you prefer specific hold settings for any voice, the Str7[380] parameter overrides the initial data-derived selection. A digit from 0-9 at any position means that value will be used for Hold for the relevant voice. Again, note that Voice A is on the right end. The default "???????0" string indicates that all voices will use the data-derived values except voice A. That's because voice A has a default drum roll set which was judged to sound better without holds.

Str7[570]="xx??????" allows you to set specific initial pattern lengths for any percussion voice. You can enter a digit from 1-9, or uppercase letters A-K for 10-20. (Anything outside this will be limited to the 1-20 range.) Voices with "?" set will use the data-driven Up to UP values.

Str7[590]="xx444444" indicates that the percussion pattern will be repeated 4 times per initial or update cycle. You can enter any digit from 0-9 or letters A-Z for 10-35. A "0" will disable that voice, the same as entering a "0" in that position of the Str7[430] Voice Enables voice map.

After all pattern repeats, there can be an optional rest (silence) before the overall cycle is begun again. Str7[600]="xx000000" specifies the number of rest beats via digits 0-9 or A-Z for 0-35 beats. An ampersand (&) specifies an indefinite rest. (It goes on & on & on...) This allows for variety in performance endings, such as voices that drop out one by one when their cycles end.

After each percussion voice ends its cycle, new pattern length and hold values are chosen for the next cycle; this requires that updates be enabled via the same Str7[19]="U" or "u" used for tonal voice update enables, as well as specific percussion update enables.

If the "0" in Str7[39]="0" is replaced with "S", the overall cycle length is multipied by the tonal pattern sequence count, which is the number of characters in the Str7[240]="AABABA" sequence that defines a tonal voice verse. (See Verse Construction.) If this is activated, a percussion voice update can still take place at the end of each normal cycle (pattern length times hold beats times repeats), with the rest coming only after all cycles in the sequence. This allows a percussion voice to match the duration of a tonal voice.

Str7[620]="xx??????" sets percussion voices to synchronize with a selected Lead tonal voice, selected in the tonal Verse Construction section. A percussion voice set to "p" will have its pattern length forced to match the Lead pattern length. An "h" forces both the pattern length and hold beats to match those of the Lead, while "a" forces pattern length, hold beats, and number of repeats to match the Lead.


Percussion Rolls (Drum Rolls) :

;Percussion rolls:
Str7[390]="xx000003"       ;1st Perc Roll rate
Str7[400]="xx000000"       ;Perc Roll beat for above voice
Str7[410]="xx000000"       ;2nd Perc Roll rate
Str7[420]="xx000000"       ;2nd Perc Roll beat for above voice

Music_from_Anything allows up to two percussion roll (drum roll) setups per percussion voice. Each roll setup requires two separate values, one for the roll rate, and one for the beat of the percussion pattern that will have the roll applied.

Str7[390]="xx000003" specifies that percussion voice A (on the right end) will have a repeat period of 40 msec per hit. This period is computed by adding one to the entered numeral and multiplying by the Trace Update interval, which is 10 msec by default. The numerals "1"-"9" would thus yield periods of 20-100 msec. But you can continue on with "A"-"W" to get periods up to 330 msec. A full list is given below, but keep in mind that a change in the Trace Update interval would change the timing below by the same factor.

  Char    Factor   msec   hits/sec   BPM
    1      2       20      50       3000
    2      3       30      33.3     2000
    3      4       40      25       1500
    4      5       50      20       1200
    5      6       60      16.7     1000
    6      7       70      14.3      857
    7      8       80      12.5      750
    8      9       90      11.1      666
    9     10      100      10.00     600
    A     11      110       9.09     545
    B     12      120       8.33     500
    C     13      130       7.69     462
    D     14      140       7.14     429
    E     15      150       6.66     400
    F     16      160       6.25     375
    G     17      170       5.88     353
    H     18      180       5.56     333
    I     19      190       5.26     316
    J     20      200       5.00     300
    K     21      210       4.76     286
    L     22      220       4.55     273
    M     23      230       4.35     261
    N     24      240       4.17     250
    O     25      250       4.00     240
    P     26      260       3.85     231
    Q     27      270       3.70     222
    R     28      280       3.57     214
    S     29      290       3.45     207
    T     30      300       3.33     200
    U     31      310       3.23     194
    V     32      320       3.13     188
    W     33      330       3.03     182

Str7[400]="xx000000" specifies that percussion voice A will have the roll on position 0 of its hit pattern. This position, or 1 or 2, are usually safe choices, since the percussion pattern length is typically more than that. If you pick a position greater than the length (less one, since positions start at 0), there will be no percussion on that voice.

Str7[410] and Str7[420] set the roll period and position of an optional second roll for a voice. The settings behave exactly like those for the first roll, except that if you attempt to set the second roll to the same position as the first it will be ignored.

A roll causes a special value to be set in the velocity pattern for a voice. That means that if the velocity pattern is later updated with random values (which will never include special roll values), the roll will be lost for the rest of the performance.


Percussion Update Enables:

;Percussion update enables:
Str7[370]="xx111111"       ;Perc Hold update enables
Str7[440]="xx111111"       ;Perc Instrument update enables
Str7[450]="xx111111"       ;Perc lag update enables
Str7[460]="xx111111"       ;Perc Pattern length update enables
Str7[470]="xx111111"       ;Perc Velocity update enables
Qe=1                       ;Perc Volume update enable

After each percussion voice cycle ends, the next cycle begins with new random parameters, as long as updates are enabled via the same Str7[19]="U" or "u" used for tonal voice update enables.

However, parameters enabled by the "U" or "u" can still be disabled by setting the voice character of a specific parameter to "0" from its default of "1".

Updates choose values randomly, within whatever limits were set initially for that value.

Str7[370]="xx111111" enables percussion hold values to be updated, using the same QH and Qh biased random values set under Percussion Parameters.

Str7[440]="xx111111" enables Percussion instruments to be chosen randomly from the GlossyPerc.TXT list; there are no separate range limits. See Set Instruments, above.

Str7[450]="xx111111" enables a new random lag between 0 and Ug as described under Note or Percussion Lag .

Str7[460]="xx111111" likewise enables Percussion pattern length changes between Up and UP limits, as discussed under Percussion Parameters.

Str7[470]="xx111111" enables Percussion velocity updates, but with an extra option. The "1" as shown here allows the velocity pattern to be copied from a random location in the original 20-value data array, while keeping the array itself unchanged. That means that future updates during the performance may repeat portions of the original pattern.

Alternatively, a "?" can be used to replace the entire array with random values in the 16-31 range, so they will point to the upper half of the velocity map as noted in Percussion Parameters.

Note that using a "?" here will definitely eliminate any Percussion Rolls on that voice as soon as it is updated. That's because rolls use special velocity values that will never be generated randomly, so any replacement values will be normal percussion beats. However, the "1" option does not change the original values, it just chooses a different section of the array. That means that the roll may still appear after the update, though perhaps on a different beat. Or it may not appear until some later update.

Pattern length updates do not affect the roll settings, though, so you can still allow the length to change; however, if a roll position is later than a shorter updated length, it will not sound. It will be restored if a subsequent update to a longer length brings it back into the pattern range.

Qe=1 enables a random Volume to be chosen between Qm=48 and QM=64, as opposed to the initial data-driven value scaled to be within that range. Note that there is only a single volume setting that applies to all percussion voices, but only the Percussion A voice updates can change it.


Variable, Array, and String Usage:

The following summary of variable, array, and string usage is intended to aid in understanding the macro script operation. It can also help in modifying the script, including avoiding accidental re-use of critical variables.

This list includes only variables set in the initial Music_from_Anything macro and saved for use by the _Music_Proc subroutine while it acquires all the source data. It then restores the saved variables, and uses a few more that appear in this list, to process that data for use by the _MusicData.DQM MIDI script.

The source acquisition subroutines are thus free to use these variables (and any others) as desired, since the critical settings are later restored. The listed variables should not be changed in _Music_Proc after the restore line:

Buf7#Br=9    ;Restore all variables from Buf7 block 9

Otherwise, changed values may be passed on to the MIDI script with unintended consequences. Variables marked 'Work variable' are not passed to the MIDI script, but should be changed only after studying _Music_Proc to make sure you understand their usage. It would be safer, however, to pick a variable that's not on the list.

Variables:
QH      Negative Perc Hold limit (random bias)
QI      Tonal instruments in GlossyInst.TXT
QM      Max Perc Volume
QN      Work variable
QP      Percussion instruments in GlossyPerc.TXT
QR      Work variable
QS      Scales in GlossyScales.TXT
QT      Max common delay
QV      Max voice 2-8 intro delay + cycle beats
Qa      Auto-Backup toggle
Qe      Perc Volume update enable
Qh      Positive Perc Hold limit
Qm      Min Perc Volume
Qn      Work variable
Qp      Lock pattern lengths to Voice 1
Qq      Number of blocks in ABCD sequence
Qt      Min common delay
UA      Work variable
UB      Work variable
UC      Work variable
UD      Work variable
UF      Fade-out, secs
UH      Negative note Hold limit (random bias)
UI      Work variable - index
UJ      Work variable - index
UK      Work variable - index
UL      Min pattern length
UN      Max note
UP      Max Perc pattern length
UT      Max added intro delay
UU      Max pattern length
UV      Max volume (Level)
UW      Work variable
UX      Work variable
UY      Work variable
UZ      Peak data value
Ub      1st all-track voice if no Lead
Ug      Max lag
Uh      Positive note Hold limit
Um      Lead voice
Un      Min note
Up      Min Perc pattern length
Ur      Percent voice note range overlap
Ut      Min tonal intro delay
Uv      Min volume (Level)

Arrays:
Buf0   Main data array
Buf1   GlossyInst, GlossyPerc, and GlossyScales lists

Strings:
Str0   File search string, also source name
Str7   [0]-[620] = Misc voice maps and settings
Str7   [700]-[1980] = Note patterns A-D for voices 1-8

Music_from_Anything Macro Listing:

Although this is the main macro, all it really does is specify user preferences that are then passed to the _Music_Proc subroutine for processing and sending to the MIDI script.

;<Help=H4929
Str0#B=-1              ;Unlink Str0 from any BufN

;Adjustable parameters:
Qa=1                   ;1=Auto-Backup, 0=manual file select
Str0="*.*"             ;File types to show
Field2="1:00"          ;Play duration (1 minute)
Str7[29]="0"           ;"D" sets overall Duration to Lead voice
Field3=" "             ;Tempo BPM (default 200-300 range from data)
Field4=" "             ;Scale = Auto unless 2048 to 4095
Field5="80"            ;Percussion probability, percent

;Voice enables:
Str7[0]="11111111"     ;Active tonal Voice 1-8 bitmap
Str7[430]="xx111111"   ;Active Percussion voice A-F bitmap

;Intro and fade-out delays:
Ut=10              ;Min tonal intro delay
UT=50              ;Max tonal intro delay
Qt=0               ;Min percussion intro delay
QT=10              ;Max percussion intro delay
Str7[530]="????????"   ;Tonal intro delay 0-Z = 0-35, ?=Ut,UT
Str7[580]="xx??????"   ;Perc intro delay 0-Z = 0-35, ?=Qt,QT
Str7[9]="0"        ;"I" syncs Intro delays with Lead voice
UF=5               ;Final fade-out, seconds

;Volume (Level):
Uv=48                  ;Min tonal voice Volume
UV=80                  ;Max tonal voice Volume
Qm=48                  ;Min Percussion Volume
QM=64                  ;Max Percusion Volume

;Note ranges:
Un=28                  ;Min pattern note (0-127)
UN=80                  ;Max note
Ur=100                 ;Percent voice note range overlap

;Note patterns:
UL=7                   ;Min note pattern length (1 min)
UU=11                  ;Max note pattern length (20 max)
Str7[520]="????????"   ;Set pattern lengths 1-K = 1-20, ?=UL,UU
Str7[160]="0_8_8_8_8_8_88880_4_4_6_6_6_8888"   ;Velocity map

;Tonal Hold beats:
UH=-4                  ;Negative Note Hold limit (random bias)
Uh=4                   ;Positive Note Hold limit
Str7[360]="????????"   ;Set Hold Beats (?=random UH,Uh)

;Note or Percussion Lag:
Ug=2                   ;Max random Note Lag (limit 15)

;Set Instruments, 255 = use data file value:
Str7[100]#b=255        ;Voice 1 inst
Str7[101]#b=255        ;Voice 2 inst
Str7[102]#b=255        ;Voice 3 inst
Str7[103]#b=255        ;Voice 4 inst
Str7[104]#b=255        ;Voice 5 inst
Str7[105]#b=255        ;Voice 6 inst
Str7[106]#b=255        ;Voice 7 inst
Str7[107]#b=255        ;Voice 8 inst
Str7[108]#b=255        ;Perc A percussion inst (27-87 valid, 0 disables)
Str7[109]#b=255        ;Perc B inst
Str7[110]#b=255        ;Perc C inst
Str7[111]#b=255        ;Perc D inst
Str7[112]#b=255        ;Perc E inst
Str7[113]#b=255        ;Perc F inst

;Chords:
Str7[120]="00000400"   ;Chord enable voice bitmap
Str7[130]="00000200"   ;Initial chord position bitmap
Str7[140]="00000000"   ;Arpeggio enable/count bitmap
Str7[150]="00000000"   ;Arpeggio direction bitmap
Str7[200]="MMMMMMmmmm777S56................"   ;Chord map

;Tonal Verse Construction:
Str7[240]="AABABA"     ;Pattern sequence, 20 max, ABCD
Str7[540]="22222222"   ;Tonal Verses, 0-Z = 0-35
Str7[550]="44444444"   ;Tonal Sustain, 0-Z = 0-35
Str7[560]="44444444"   ;Tonal Rest, 0-Z = 0-35, &=indefinite
Str7[610]="????????"   ;Tonal sync: L, a, h, p, or ?=data

;Note pattern block shift, offset, and invert:
Str7[280]="00000000"   ;Block A note shift
Str7[290]="00000000"   ;Block B note shift
Str7[300]="00000000"   ;Block C note shift
Str7[310]="00000000"   ;Block D note shift
Str7[320]="00000000"   ;Block A offset
Str7[330]="00000000"   ;Block B offset
Str7[340]="00000000"   ;Block C offset
Str7[350]="00000000"   ;Block D offset
Str7[480]="00000000"   ;Block A invert
Str7[490]="00000000"   ;Block B invert
Str7[500]="00000000"   ;Block C invert
Str7[510]="00000000"   ;Block D invert
Str7[630]="????????"   ;A=copy block A to B, B= B to A

;Tonal voice update enables:
Str7[19]="U"           ;Enable updates. "u" omits pattern and hold
Str7[10]="11111111"    ;Instrument update enables
Str7[20]="11111111"    ;Note Hold update enables
Str7[30]="11111111"    ;Volume update enables
Str7[40]="11111111"    ;Note lag update enables
Str7[50]="11111111"    ;Note pattern length update enables
Str7[60]="11111111"    ;Pattern offset bias update enables
Str7[70]="11111111"    ;Note velocity update enables
Str7[80]="11111111"    ;Pattern A update enables
Str7[90]="11111111"    ;Pattern B update enables
Str7[260]="11111111"   ;Pattern C update enables
Str7[270]="11111111"   ;Pattern D update enables

;Percussion parameters:
Up=4                   ;Min Perc pattern length (1 min)
UP=9                   ;Max Perc pattern length (20 max)
QH=-4                  ;Negative Perc Hold limit (random bias)
Qh=4                   ;Positive Perc Hold limit
Str7[380]="xx?????0"   ;Set Perc Holds (?=QH,Qh)
Str7[570]="xx??????"   ;Perc Pattern lengths, 1-K = 1-35, ?=Up,UP
Str7[590]="xx444444"   ;Perc repeat count (verses), 0-Z = 0-35
Str7[600]="xx000000"   ;Perc Rest, 0-Z = 0-35, &=indefinite
Str7[39]="0"           ;"S" = tonal Sequence count cycle
Str7[620]="xx??????"   ;Perc sync: a, h, p, or ?=data

;Percussion rolls:
Str7[390]="xx000003"   ;1st Perc Roll rate (0=off, 1-8 = 20-90 msec)
Str7[400]="xx000000"   ;Perc Roll beat for above voice
Str7[410]="xx000000"   ;2nd Perc Roll rate
Str7[420]="xx000000"   ;2nd Perc Roll beat for above voice

;Percussion update enables:
Str7[370]="xx111111"   ;Perc Hold update enables
Str7[440]="xx111111"   ;Perc Instrument update enables
Str7[450]="xx111111"   ;Perc lag update enables
Str7[460]="xx111111"   ;Perc Pattern length update enables
Str7[470]="xx111111"   ;Perc Velocity update enables
Qe=1                   ;Perc Volume update enable

@_Music_Proc           ;Acquire data, process, run MIDI script

_Music_Proc Macro Subroutine Listing:

This routine gets input data from a chosen source by calling its specific subroutine, processes it to be compatible with the MIDI script using preferences set at the start of the main Music_from_Anything macro, then sets up and runs the script.

;<Help=H4929

Buf7=                  ;Clear Buf7
Buf7#Bs=9              ;Save all variables to Buf7 block 9
U1=Field1              ;Save possible seed
Field1=                ;Clear for now

QF=0                   ;Flag, set to 1 when we have data
QK=Key?$               ;Get key shift states
IF.QK=2                ;ALT key (only) down?
    @_Music_Mouse          ;Get mouse-drawn image data
    QF=1                   ;Got mouse data
ENDIF.

IF.QK=1                ;CTRL key (only) down?
    @_Music_Clipboard      ;Get BMP image data from clipboard
    QF=2
ENDIF.

IF.QK=4                ;SHIFT key (only) down?
    Buf2#N=2               ;Get prior saved data if so
    IF.Qa=1
        A.Buf2="<Load:Music_Save"  ;Auto-loads Buf2 data, Buf3
    ELSE.
        Buf2="<Load:Music_Save"    ;Prompts for file name
    ENDIF.
    UL=Buf2?L              ;Bytes loaded
    IF.UL=0                ;No file loaded?
        Msg="No Music_from_Anything save file."
        LoopBreak=-1           ;Abort macro
    ENDIF.
    Str1=Buf3[66](a)       ;Get file ID to Str1 string array
    IF.Str1=!"MusicAny"    ;Not one of ours?
        Msg="Not a Music_from_Anything save file."
        LoopBreak=-1           ;Abort macro
    ENDIF.
    Str0=Buf3[0](aB)       ;Restore filename
    UZ=Buf3[64]            ;Restore peak data value
    UR=Buf3[65]            ;Possible rand seed if prior NoFile
    IF.UR=!0
        Field1="h"+UR(h)       ;Restore seed if present
    ENDIF.
    QF=3
ENDIF.

E.IF.Input=            ;Input enabled?
    IF.Input=1             ;If so, is it active?
    AND.QF=0               ;and no prior data?
        @_Music_Live           ;Get waveform, Spectrum, or Sgram data
        QF=4
    ENDIF.
ENDIF.

IF.Gen=1               ;Generator active?
AND.QF=0               ;and no prior data?
    @_Music_Live            ;Get waveform, Spectrum, or Sgram data
    QF=5
ENDIF.

IF.Open=>0             ;File open?
AND.QF=0               ;and no prior data?
    GetFilePath=1          ;Get filename for label
    @_Music_Live           ;Get waveform, Spectrum, or Sgram data
    QF=6
ENDIF.

IF.QF=0                ;No data so far?
    Buf0=                  ;Clear Buf0
    Buf0#N=1               ;Read 1 channel
    Buf0#WU=16             ;Unsigned 16-bit data
    Buf0#H=0               ;Start at beginning of file
    Buf0="<LoadDAT:" + Str0    ;Load file to Buf0
    QL=Buf0?B              ;File bytes

    IF.Posn?f=0            ;No file selected?
    OR.QL=0                ;Or file empty?
        @_Music_NoFile         ;Use Notes, or random data
        QF=6
    ELSE.                  ;Else file opened OK
        GetFilePath=1          ;Get filename for label

        IF.Buf0[0]=hD8FF       ;FF, D8 for JPG file?
            @_Music_JPG            ;Extract raw file data
            QF=8
        ENDIF.

        IF.Buf0[0]="MB"        ;'BM' for BMP?
            @_Music_BMP            ;Extract bitmap data
            QF=9
        ENDIF.

        IF.Buf0[0]="IR"        ;'RI' for RIFF WAVE file?
        AND.Buf0[1]="FF"       ;'FF'
        AND.Buf0[4]="AW"       ;'WA'
        AND.Buf0[5]="EV"       ;'VE'
            @_Music_WAV            ;Extract waveform data
            QF=10
        ENDIF.

        IF.Buf0[0]="W["        ;'[WIF]' weaving draft?
        AND.Buf0[1]="FI"
        AND.(Buf0[2]&hFF="]"
            @_Music_WIF            ;Extract drawdown data
            QF=11
        ENDIF.

        IF.Buf0[0]="@@"        ;'@@StartDTX' weaving draft?
        AND.Buf0[1]="tS"
        AND.(Buf0[2]="ra"
        AND.(Buf0[3]="Dt"
        AND.(Buf0[4]="XT"
            @_Music_DTX            ;Extract drawdown data
            QF=12
        ENDIF.

        IF.QF=0                ;Unknown file type?
            UO=0                   ;Assume no header
            @_Music_DAT            ;Extract arbitrary data
            QF=13
        ENDIF.
    ENDIF.
ENDIF.

IF.QF=!3                   ;If this is not Music_Prior
    Buf3=                      ;Clear Buf3
    Buf3[0]#aB=Str0            ;Save filename/title
    Buf3[64]=UZ                ;Save peak data value
    IF.QF=7                    ;Is this a NoFile?
        Buf3[65]=UR                ;Save possible seed
    ENDIF.
    Buf3[66]#a="MusicAny"      ;Save file ID
    Buf2#N=2                   ;Save Buf2 and Buf3
    IF.Qa=1
        A.Buf2="<Save:Music_Save"  ;Auto-save data
    ELSE.
        Buf2="<Save:Music_Save"    ;Prompts for save file name
    ENDIF.
ENDIF.

Buf0="<=B2"            ;Copy Buf2 to Buf0
Buf7[0]=UZ             ;Save peak file value during variable restore
Buf7#Br=9              ;Restore all variables from Buf7 block 9
UZ=Buf7[0]             ;Restore peak file value

;Load files of MIDI instruments and scales:
Msg=                   ;Clear message
Buf1#L=0               ;Load file to start of Buf1
A.Buf1="<LoadTXT:MIDI_SetupGlossyInst"    ;Load tonal instrument list
QI=Buf1?L              ;Get samples (number of instruments) loaded
IF.QI=0                ;Nothing there?
    Msg="Unable to load MIDI_SetupGlossyInst.TXT"
    LoopBreak=-1           ;Exit
ENDIF.
Buf1#K=1               ;Keep Buf1 data while we load more
Buf1#SO=200            ;Load to start offset 200
A.Buf1="<LoadTXT:MIDI_SetupGlossyPerc"    ;Load percussion instrument list
QP=Buf1?L              ;Get samples (number of instruments) loaded
IF.QP=0                ;Nothing there?
    Msg="Unable to load MIDI_SetupGlossyPerc.TXT"
    LoopBreak=-1           ;Exit
ENDIF.
Buf1#K=1               ;Keep Buf1 data while we load more
Buf1#SO=400            ;Load to start offset 400
A.Buf1="<LoadTXT:MIDI_SetupGlossyScales"  ;Load musical scales list
QS=Buf1?L              ;Get number of scales loaded
IF.QS=0                ;Nothing there?
    Msg="Unable to load MIDI_SetupGlossyScales.TXT"
    LoopBreak=-1           ;Exit
ENDIF.

;Bitmapped sync/update options:
Qp=0                   ;Default
IF.Str7?b[9]="I"       ;Intro delay sync to Lead voice
    Qp=Qp | 4
END.
UA=Str7?b[19]          ;Update control; "U" = allow all
IF.UA="u"              ;Update all but Pattern and Hold
    Qp=Qp | 16
ENDIF.
IF.UA="0"              ;Block all updates
    Qp=Qp | 8
ENDIF.
IF.Str7[29]="D"        ;Duration set to Lead voice
    Qp=Qp | 32
ENDIF.
IF.Str7[39]="S"        ;Perc uses Sequence count
    Qp=Qp | 128
ENDIF.

;Adjust note range:
IF.Ur=>100             ;Percent note range overlap > 100% ?
    Ur=1                   ;Convert to unity
ELSE.
    Ur=Ur/100          ;Else convert to fraction
ENDIF.
QR=(UN-Un)/8 + Ur * 7/8 * (UN-Un)  ;Note range / voice

UU=lim(UU,1,20)        ;Limit max pattern length to 1-20 range
UL=lim(UL,1,UU)        ;Limit min pattern length to UU-max range

Um=0                   ;No 'L' lead sync voice found yet
Ub=0                   ;No 'a' all-track voice in case no Lead

;Adjust parameter values as percent of full range UZ:
UI=0                                   ;Tonal voice number (less 1)
WHILE.UI=<8                            ;Do voices 0-7 (Voice 1-8)
    Str7[7-UI]#b=Str7?b[7-UI]-"0"          ;Enable string to binary
    UX=Buf0[UI*100] * (QI-1) / UZ          ;Instrument index 0-116
    UX=Buf1[UX]                            ;Instrument number from list
    UY=Str7?b[UI + 100]                    ;Chosen inst, if any
    IF.UY=<128                             ;0-127 range?
        UX=UY                                  ;Use as instrument if so
    ENDIF.
    Buf0[UI * 100]=UX
    UY=Str7?b[367-UI] - "0"                ;0-9 forced Hold Beats
    IF.UY=>9                               ;>9 for random?
        UX=Buf0[UI*100+1] * (Uh-UH)/UZ + UH    ;Hold -4 to +4 if so
    ELSE.                                  ;Else forced 1-9
        UX=UY                                  ;Value to set
    ENDIF.
    IF.UX=<1
        UX=1
    ENDIF.
    Buf0[UI*100+1]=UX                      ;Set Hold Beats
    UX=Buf0[UI*100+2] * (UV-Uv)/UZ + Uv    ;Level 48-80
    Buf0[UI*100+2]=UX

    UA=Str7?b[527-UI]                      ;Forced Pattern char
    IF.UA="?"                              ;Random?
        UA=Buf0[UI*100+3] * (UU-UL)/UZ + UL    ;Pattern 7-11, etc
    ELSE.                                  ;Else force value
        UA=-UA                                 ;Neg mode for _Music_Alpha
        @_Music_Alpha                          ;UA = 0-35 for "0-9, A-Z"
        UA=lim(UA,1,20)
    ENDIF.
    Buf0[UI*100+3]=UA                      ;Set Pattern length

    UA=Str7?b[537-UI]                      ;Forced Intro char?
    IF.UA="?"
        UA=Buf0[UI*100+4] * (UT-Ut)/UZ +Ut     ;Intro delay
    ELSE.
        UA=-UA
        @_Music_Alpha                          ;UA = 0-35 for "0-9, A-Z"
    ENDIF.
    Buf0[UI*100+4]=UA                      ;Set Intro delay

    UX=Buf0[UI*100+5] * Ug/UZ              ;Initial Note Lag
    Buf0[UI*100+5]=UX

    UA=-Str7?b[547-UI]                     ;Verses char
    @_Music_Alpha                          ;UA = 0-35 for "0-9, A-Z"
    Buf0[UI*100+6]=UA                      ;Set Verses
    IF.UA=0                                ;If no Verses,
        Str7[7-UI]#b=0                         ;Disable voice
    ENDIF.

    UA=-Str7?b[557-UI]                     ;Sustain char
    @_Music_Alpha                          ;UA = 0-35 for "0-9, A-Z"
    Buf0[UI*100+7]=UA                      ;Set Sustain

    UA=Str7?b[567-UI]                      ;Rest char
    IF.UA="&"                              ;Indefinite?
        UA=2G                                  ;Very large value
    ELSE.                                  ;Else specific length
        UA=-UA                                 ;Neg _Music_Alpha mode
        @_Music_Alpha                          ;UA = 0-35 for "0-9, A-Z"
    ENDIF.
    Buf0[UI*100+8]=UA                      ;Set Rest

    UA=Str7?b[617-UI]                      ;Sync map char
    IF.UA="L"                              ;Lead voice?
        IF.Um=0                                ;None set yet?
            Um=UI+1                                ;Set to voice num 1-8
        ENDIF.
    ELSE.
        IF.UA="a"                              ;All-track voice?
            IF.Ub=0                                ;None set yet?
                Un=UI+1                                ;Set to voice num 1-8
            ENDIF.
        ENDIF.
    ENDIF.

;Adjust note velocities for voice UI:
    UJ=0
    WHILE.UJ=<20                           ;20 Velocity values
        UX=Buf0[UI*100+UJ+20] % 15             ;0-15 in Velocity Map
        Buf0[UI*100+UJ+20]=UX                  ;Save value
        UJ=UJ+1
    WEND.

;Adjust note patterns for voice UI:
    Qn=Un + UI/7 * (UN - QR - Un)          ;New Un min note
    QN=Qn + QR                             ;New UN max note
    UJ=0
    WHILE.UJ=<40                           ;20 pattern 1, 20 pattern 2
        UX=Buf0[UI*100+UJ+40] * (QN-Qn)/UZ +Qn     ;Adjust range re: UZ
        Buf0[UI*100+UJ+40]=UX                      ;Save value
        UJ=UJ+1
    WEND.

;Copy note block A to B, or B to A, for voice UI:
    UA=Str7?b[637-UI]                      ;Copy block char
    UJ=0
    IF.UA="A"                              ;Copy A to B?
        WHILE.UJ=<20                           ;20 block values
            UX=Buf0[UI*100+UJ+40]                  ;A source
            Buf0[UI*100+UJ+60]=UX                  ;B dest
            UJ=UJ+1
        WEND.
    ELSE.
        IF.UA="B"                              ;Copy B to A?
            WHILE.UJ=<20                           ;20 block values
                UX=Buf0[UI*100+UJ+60]                  ;B source
                Buf0[UI*100+UJ+40]=UX                  ;A dest
                UJ=UJ+1
             WEND.
        ENDIF.
    ENDIF.

;Get chord data for voice UI:
    UX=0
    UY=Str7?b[127-UI]              ;Chord enable/count char for voice
    IF.UY=<"0"                     ;Invalid if less than "0"
    OR.UY=>"z"                     ;Or above "z"
        UX=127                         ;No chord, root note only
    ELSE.                          ;Valid chars are 0-9, A-Z, a-z
        IF.UY=>"9"                     ;Between "9" and "A"?
        AND.UY=<"A"
            UX=127                         ;No chord, root note only
        ELSE.                          ;Else "A" to "z"
            IF.UY=>"Z"                     ;Between "Z" and "a"?
            AND.UY=<"a"
                UX=127                         ;No chord, root note only
            ENDIF.
        ENDIF.
    ENDIF.

    IF.UY=>"0"                     ;Do nothing if "0"
    AND.UX=0                       ;Or "no chord" char 127
        UW=Str7?b[137-UI] - "0"        ;Else get chord start posn
        IF.UW=<0                       ;Limit to 0-9
        OR.UW=>9
            UW=0
        ENDIF.
        UJ=0
        WHILE.UJ=<UW                   ;No chord for this many notes
            Buf0[UI*100+UJ+80]=127         ;"No chord" value
            UJ=UJ+1
        WEND.

        IF.UY=<="9"                    ;Chord count "1"-"9" ?
            UY=UY-"0"                      ;Convert char to count 1-9
            UY=UY-1                        ;Nulls between chords
            UK=0                           ;Null counter
            WHILE.UJ=<20                   ;20 chord values
                UX=Buf0[UI*100+UJ+80] * 15/UZ  ;Adjust 1st chord re: UZ
                Buf0[UI*100+UJ+80]=UX          ;Save it
                UJ=UJ+1                        ;Count it
                WHILE.UK=<UY                   ;Nulls between chords
                AND.UJ=<20                     ;Or until all 20 done
                    Buf0[UI*100+UJ+80]=127         ;Null chord
                    UK=UK+1                        ;Next null
                    UJ=UJ+1                        ;Next chord posn
                WEND.
                UK=0
            WEND.
        ELSE.                          ;Else alpha "A"-"Z" or "a"-"z"
            IF.UY=<="Z"                    ;"A"-"Z" range?
                UY=UY-"A" + 1                  ;UY = 1-26
            ELSE.                          ;Else "a"-"z" range
                UY=UY-"a" + 27                 ;UY = 27-52
            ENDIF.
            IF.UY=<32                      ;Use 0-1, 0-2, ... 0-31 chords
                WHILE.UJ=<20                   ;20 chord values
                    UX=Buf0[UI*100+UJ+80] * UY/UZ  ;Adjust value re: UZ
                    Buf0[UI*100+UJ+80]=UX          ;Save value
                    UJ=UJ+1
                WEND.
            ELSE.                      ;Else scale 1-31, 2-31, 3-31, etc
                UY=UY-31                   ;Min value 1->21
                UW=31-UY                   ;Range 30->10
                WHILE.UJ=<20               ;20 chord values
                    UX=Buf0[UI*100+UJ+80] * UW/UZ + UY
                    Buf0[UI*100+UJ+80]=UX
                    UJ=UJ+1
                WEND.
            ENDIF.
        ENDIF.
    ELSE.                      ;Else disable chords if invalid enable/count
        Str7[127-UI]="0"
    ENDIF.

;Convert arpeggio strings to binary:
    UA=-Str7?b[147-UI]         ;"0"-"9", "A"-"F"
    @_Music_Alpha              ;0-15
    Str7[147-UI]#b=UA          ;Arpeggio count to binary
    Str7[157-UI]#b=Str7?b[157-UI]-"0"  ;Arpeggio direction to binary

;Apply invert and note shift to patterns for voice UI and copy to Str7:
    UJ=0
    WHILE.UJ=<4                ;4 patterns (A,B,C,D) per voice
        UA=Str7?b[287 + UJ*10 - UI]    ;Note shift 0, A-Z, a-z
        @_Music_Alpha              ;A-Z = 1-26, a-z = -1 to -26, else 0
        UB=UA                      ;Copy for later use
        UA=Str7?b[487 + UJ*10 - UI]    ;Note invert 0, A-Z, a-z
        @_Music_Alpha              ;A-Z = 1-26, a-z = -1 to -26, else 0
        UC=0                       ;Assume uppercase, disable lower test
        IF.UA=0                    ;"0" ?
            UA=26                      ;Disable upper test
        ENDIF.
        IF.UA=<0                   ;Lowercase?
            UC=-UA                     ;Set UC
            UA=26                      ;Disable upper test
        ENDIF.
        UK=0
        WHILE.UK=<20               ;20 notes per pattern
            UX=Buf0[UI*100 +(UJ&1)*20 +40 +UK]
            IF.UK=>UA-2                ;Invert remainder of block?
            OR.UK=<UC                  ;Invert beginning of block?
                UX=QN-UX+Qn                ;Invert note in range
            ENDIF.
            UX=UX+UB                   ;Shift note up or down
            UX=lim(UX,0,127)           ;Limit to 0-127 note range
            UD=700 + UI*160 +UJ*40     ;40-byte repeated pattern
            Str7[UD+UK]#b=UX           ;1st copy
            Str7[UD +UK + 20]#b=UX     ;2nd copy
            UK=UK+1
        WEND.
    UJ=UJ+1
    WEND.

;Apply offset/direction to note patterns in Str7:
    UJ=0
    WHILE.UJ=<4                ;4 patterns (A,B,C,D) per voice
        UA=Str7?b[327 + UJ*10 - UI]     ;Pattern offset 0, A-Z, a-z
        @_Music_Alpha              ;A-Z = 1-26, a-z = -1 to -26, else 0
        UD=700 + UI*160 +UJ*40     ;Start of 40-byte pattern
        IF.UA=<0                   ;Reverse order?
            UA=-UA                     ;Negate offset value
            Str7[UD]=Str7[UD+39,UD+20] ;Top 20 to 0, reversed
            Str7[UD+20]=Str7[UD,UD+19] ;Bottom 20 to top
        ENDIF.
        UA=UA%20                   ;UA MOD 20 = 0-19
        IF.UA=>0                   ;0 = no change
            Str7[UD]=Str7[UD+UA,UD+UA+19]  ;20 from UA to 0
            Str7[UD+20]=Str7[UD,UD+19]     ;Bottom 20 to top
        ENDIF.
        UJ=UJ+1
    WEND.
    UI=UI+1
WEND.

Qq=0                       ;Count chars in AABABA sequence
WHILE.Qq=<20               ;20 chars max
    UX=Str7?b[240+Qq]          ;Get char
    IF.UX=0                    ;End of string?
    OR.UX=<"A"                 ;Not A,B,C,D?
    OR.UX=>"D"
        LoopBreak=2                ;Stop count
    ENDIF.
    UX=UX - "A" + 1            ;A=1 to D=4
    Str7[240+Qq]#b=UX          ;Save back to original posn
    Qq=Qq+1
WEND.

IF.Um=0         ;No sync Lead voice?
    Um=Ub           ;Use first all-track voice, if any
ENDIF.

;Set selected voice sync:
IF.Um=!0                       ;Lead voice set?
    UI=Um-1                        ;Lead voice 1-8 to index 0-7
    UX=Buf0[UI*100+3]              ;Pattern length
    UY=Buf0[UI*100+1]              ;Hold beats
    UB=Buf0[UI*100+6]              ;Verses
    UI=0                           ;Voices 1-8
    WHILE.UI=<8
        IF.UI=!Um-1                    ;Do nothing for lead voice
            UA=Str7?b[617-UI]              ;Sync map char
            IF.UA="p"                      ;Sync pattern only?
                Buf0[UI*100+3]=UX              ;Force pattern length
            ENDIF.
            IF.UA="h"                      ;Sync hold & pattern?
                Buf0[UI*100+3]=UX              ;Force pattern length
                Buf0[UI*100+1]=UY              ;Force Hold beats
            ENDIF.
            IF.UA="a"                      ;Sync all?
                Buf0[UI*100+3]=UX              ;Force pattern length
                Buf0[UI*100+1]=UY              ;Force Hold beats
                Buf0[UI*100+6]=UB              ;Force verses
            ENDIF.
        ENDIF.
    UI=UI+1
    WEND.
ENDIF.

IF.Qp&4=4                      ;Align intro delays to Lead
AND.Um=!0                      ;If Lead is set
    UY=UX*UY                       ;Total Lead beats per pattern
    UI=0                           ;Align all 8 tonal voices
    WHILE.UI=<8
        IF.UI=!Um-1                    ;Do nothing for lead voice
            UX=Buf0[UI*100+4]              ;Intro delay factor
            UX=UX*UY                       ;Integer pattern multiple
            Buf0[UI*100+4]=UX
        ENDIF.
        UI=UI+1
    WEND.
ENDIF.

;Find Lead voice intro delay + cycle beats, else max of any voice
QV=0                       ;Max total delay
UI=0                       ;Assume no Lead, test all voices
UJ=8                       ;Max voice to test
IF.Um=!0                   ;Lead voice set?
    UI=Um-1                    ;Start with Lead
    UJ=Um                      ;Stop after that
ENDIF.
WHILE.UI=<UJ
    UX=Buf0[UI*100+1]          ;Hold beats
    UY=Buf0[UI*100+3]          ;Pattern length
    UA=Buf0[UI*100+4]          ;Intro delay
    UB=Buf0[UI*100+6]          ;Verses
    UA=UA + UX * UY * UB * Qq  ;Total delay to end of voice cycle
    IF.UA=>QV                  ;Bigger than max so far?
        QV=UA                      ;New max
    ENDIF.
    UI=UI+1
WEND.

UP=lim(UP,1,20)                ;Max perc pattern length
Up=lim(Up,1,UP)                ;Min perc pattern length

;Get and adjust 6 percussion voices (A-F):
UI=0
WHILE.UI=<6
    Str7[437-UI]#b=Str7?b[437-UI]-"0"  ;Enable string to binary
    UX=Buf0[UI*30+800] * (QP-1)/UZ     ;Perc inst index
    UX=Buf1[200 + UX]                  ;Valid inst from list
    UY=Str7?b[UI + 108]                ;Start preference
    IF.UY=<88                          ;Valid 27-87 range?
    AND.UY=>26
        UX=UY                              ;Use pref inst if so
    ENDIF.
    Buf0[UI*30+800]=UX

    UA=Str7?b[577-UI]                  ;Forced Pattern char
    IF.UA="?"                          ;Data-driven?
        UA=Buf0[UI*30+801] * (UP-Up)/UZ +Up    ;Pattern Up,UP
    ELSE.                              ;Else force value
        UA=-UA                             ;Neg _Music_Alpha mode
        @_Music_Alpha                      ;UA = 0-35 for "0-9, A-Z"
        UA=lim(UA,1,20)                    ;Limit to 1-20 range
    ENDIF.
    Buf0[UI*30+801]=UA                 ;Set Pattern length

    UX=Buf0[UI*30+802] * Ug/UZ         ;Pattern lag
    Buf0[UI*30+802]=UX

    UA=Str7?b[587-UI]                  ;Forced Intro delay char
    IF.UA="?"                          ;Data-driven?
        UA=Buf0[UI*30+803] * (QT-Qt)/UZ -Qt    ;Perc Intro delay
    ELSE.                              ;Else use entered value
        UA=-UA
        @_Music_Alpha                      ;UA = 0-35 for "0-9, A-Z"
    ENDIF.
    Buf0[UI*30+803]=UA                 ;Set Intro delay

    UY=Str7?b[387-UI] - "0"            ;0-9 forced Perc Hold beats
    IF.UY=>9                           ;>9 (?, etc) for data-driven?
        UX=Buf0[UI*30+804] * (Qh-QH)/UZ + QH   ;Hold -4 to +4, etc
    ELSE.                              ;Else forced 1-9
        UX=UY                              ;Use entered value
    ENDIF.
    IF.UX=<1                           ;Eliminate neg bias
        UX=1
    ENDIF.
    Buf0[UI*30+804]=UX                 ;Set Perc Hold beats

    UA=-Str7?b[597-UI]                 ;"Verse" count (repeats)
    @_Music_Alpha                      ;UA = 0-35 for "0-9, A-Z"
    Buf0[UI*30+805]=UA                 ;Set repeat count
    IF.UA=0                            ;If no repeats,
        Str7[437-UI]#b=0                   ;Disable voice
    ENDIF.

    UA=Str7?b[607-UI]                  ;Rest beats char
    IF.UA="&"                          ;Indefinite?
        UA=2G                              ;Very large value
    ELSE.                              ;Else use directly
        UA=-UA
        @_Music_Alpha                  ;UA = 0-35 for "0-9, A-Z"
    ENDIF.
    Buf0[UI*30+806]=UA                 ;Set rest beats

;Adjust percussion velocity values:
    UJ=0
    WHILE.UJ=<20                           ;20 Velocity values
        UX=Buf0[UI*30+UJ+810] % 15 +16         ;16-31 in Velocity Map
        Buf0[UI*30+UJ+810]=UX                  ;Save value
        UJ=UJ+1
    WEND.

;Set Perc Roll:
    UA=-Str7?b[397-UI]                     ;NEG 1st Roll period
    @_Music_Alpha                          ;0-32 for "0"-"9" and "A"-"W"
    IF.UA=>32
        UA=32
    ENDIF.
    IF.UA=>0                               ;Use Roll 1-32 on this Perc?
        UY=((UA-1) & 31) << 2              ;1-32 -> 0-31 SHL 2 => 2-33 ticks
        UA=-Str7?b[407-UI]                 ;NEG Beat posn for roll, 0-19
        @_Music_Alpha                      ;0-19 for "0"-"9" and "A"-"J"
        IF.UA=>19
            UA=19
        ENDIF.
        UD=Buf0[UI*30+UA+810]              ;Orig vel 16-31
        UD=((UD - 16) >> 2) + UY +128      ;Orig 16-31 -> 0-3 + UY +MSB
        Buf0[UI*30+UA+810]=UD
    ENDIF.

    UA=-Str7?b[417-UI]                     ;NEG 2nd Roll period
    @_Music_Alpha                          ;0-32 for "0"-"9" and "A"-"Y"
    IF.UA=>32
        UA=32
    ENDIF.
    IF.UA=>0                               ;Use Roll 1-32 on this Perc?
        UY=((UA-1) & 31) << 2              ;1-32 -> 0-31 SHL 2 => 2-33 ticks
        UA=-Str7?b[427-UI]                 ;NEG Beat posn for roll, 0-19
        @_Music_Alpha                      ;0-19 for "0"-"9" and "A"-"J"
        IF.UA=>19
            UA=19
        ENDIF.
        UD=Buf0[UI*30+UA+810]              ;Orig vel 16-31
        IF.UD=<128                         ;No MSB set from 1st Roll?
            UD=((UD - 16) >> 2) + UY +128      ;Orig 16-31 -> 0-3 + UY +MSB
            Buf0[UI*30+UA+810]=UD
        ENDIF.
    ENDIF.

    UI=UI+1
WEND.

;Set selected Perc sync to Lead tonal voice:
IF.Um=!0                       ;Lead voice set?
    UI=Um-1                        ;Lead voice 1-8 to index 0-7
    UX=Buf0[UI*100+3]              ;Pattern length
    UY=Buf0[UI*100+1]              ;Hold beats
    UB=Buf0[UI*100+6]              ;Verses
    UI=0                           ;Perc Voices A-F
    WHILE.UI=<6
        UA=Str7?b[627-UI]              ;Sync map char
        IF.UA="p"                      ;Sync pattern only?
            Buf0[UI*100+3]=UX              ;Force pattern length
        ENDIF.
        IF.UA="h"                      ;Sync hold & pattern?
            Buf0[UI*100+3]=UX              ;Force pattern length
            Buf0[UI*100+1]=UY              ;Force Hold beats
        ENDIF.
        IF.UA="a"                      ;Sync all?
            Buf0[UI*100+3]=UX              ;Force pattern length
            Buf0[UI*100+1]=UY              ;Force Hold beats
            Buf0[UI*100+6]=UB              ;Force verses
        ENDIF.
        UI=UI+1
    WEND.
ENDIF.

IF.Qp&4=4                  ;Align intro beats to tonal Lead voice
AND.Um=!0                  ;Only if Lead is set
    UY=UX*UY                   ;Total beats per pattern
    UI=0
    WHILE.UI=<6                ;Align all perc voices
        UX=Buf0[UI*30+803]         ;Orig. intro delay
        UX=UX*UY                   ;Integer pattern multiple
        Buf0[UI*30+803]=UX         ;New delay
        UI=UI+1
    WEND.
ENDIF.

;Global values:
Buf0[1020]=h474C424D           ;"GLBM" signature
X=Buf0[1021]/UZ +(Buf0[1016]/UZ)<<8 +(Buf0[1017]/UZ)<<16_  ;Get random seed
  +(Buf0[1018]/UZ)<<24             ;Sum of 4 raw source values
IF.X=>2^31                     ;Needed to prevent overflow
    X=X-2^32
ENDIF.
UX=Buf0?U # X                  ;XOR with sum of all data
Buf0[1021]=UX                  ;Random seed

IF.Field3=0                    ;Field 3 empty?
    UA=200                         ;Use 200-300 BPM default if so
    UD=300
ELSE.                          ;Else use int.frac as limits
    UA=Field3                      ;Integer = min
    UD=(Field3 - UA)*1000          ;Fraction = max
ENDIF.
IF.UD=0                        ;User entry of integer only?
    UX=UA                          ;Use that for BPM
ELSE.                          ;Else triangular dist with UA and UD
    UX=(Buf0[1022] + Buf0[1019])/2 * (UD-UA)/UZ +UA
ENDIF.
Buf0[1022]=UX                  ;BPM
Field3=UX                      ;Show actual value used

IF.Field4=<2048                ;User Scale outside 2048-4095 range?
OR.Field4=>4095
    UX=Buf0[1023] * (QS-1)/UZ      ;Auto-Scale from data if so
ELSE.
    UX=Field4                  ;Else use Scale directly
ENDIF.
Buf0[1023]=UX                  ;Set musical Scale (shown by DQM)

;Static values from start of main Music_from_Anything:
Buf0[809]=Qm           ;Min Perc volume
Buf0[839]=QM           ;Max perc volume
Buf0[869]=Qe           ;Perc volume update enable
Buf0[929]=Qp           ;Pattern length lock
Buf0[959]=UF           ;Fade-out, secs
Buf0[980]=UL           ;Min Voice pattern length
Buf0[981]=UU           ;Max Voice length
Buf0[982]=UH           ;Neg Hold limit (bias)
Buf0[983]=Uh           ;Pos Hold limit
Buf0[984]=UV           ;Max Level
Buf0[985]=Uv           ;Min Level
Buf0[986]=Up           ;Min Perc pattern length
Buf0[987]=UP           ;Max Perc length
Buf0[988]=Ug           ;Max note/perc lag
Buf0[989]=Qq           ;Number of blocks in ABCD sequence
Buf0[990]=QI-1         ;Max tonal Instruments index
Buf0[991]=QP-1         ;Max Perc instrument index
Buf0[992]=QV           ;Max Voice intro delay + cycle beats
Buf0[995]=QH           ;Neg Perc Hold limit (bias)
Buf0[996]=Qh           ;Pos Perc Hold limit

Close=                 ;Close any Open files
Gen=0                  ;Turn off Generator
E.IF.Input=            ;If Input is enabled,
    Input=0                ;Force it off
ENDIF.

Trig=0                 ;No trigger
UserUnits=0            ;User Units off
SgramDlg=1             ;Open Spectogram dialog
PitchMIDI=0            ;Pitch-to-MIDI off for now
Decimate=0             ;No sample decimation
Sgram=1                ;Spectrogram/Pitch Track on
TraceUpdate=10         ;10 msec display updates (default)
PitchShow=3            ;Show MIDI notes in voice color
PitchWide=1            ;Draw MIDI notes with wide lines
PitchYaxis=0           ;Show Y axis as note letters and octaves
PitchSteps=1           ;Quantized display to show exact notes
PitchMIDIdlg=1         ;Show Pitch-to-MIDI dialog
XpandMax=2531.25       ;Set expanded vertical (frequency) axis range
XpandMin=46.875
Xpand=1                ;Toggle expanded vertical axis on

A.LoadDQM="_MusicData" ;Load special MIDI script
SmplRate#M=24000       ;Reduce sample rate for display only
PitchMIDI=1            ;Start Pitch-to-MIDI running special script
PitchTrk=1             ;Start MIDI performance
Msg=                   ;Clear message

_Music_Alpha Macro Subroutine Listing:

This routine is used by _Music_Proc to convert uppercase letters A-Z to numbers 1-26, and lowercase letters a-z to -1 to -26. Used for arpeggio with chords, for percussion rolls, and for shift and offset/direction to note patterns,

;<Help=H4929
;On entry, UA=ASCII char
;Returns 1-26 for "A"-"Z", -1 to -26 for "a" to "z", else 0
;If UA is negative, returns 0-35 for "0"-"9" and "A"-"Z"

IF.UA=>0               ;Positive?
    IF.UA=<"A"             ;Outside of overall range?
    OR.UA=>"z"
        UA=0                   ;Set 0
        LoopBreak=0            ;Return to caller
    ENDIF.
    IF.UA=>"Z"             ;Between "Z" and "a"?
    AND.UA=<"a"
        UA=0                   ;Set 0
        LoopBreak=0            ;Return to caller
    ENDIF.
    IF.UA=<"Z"             ;Uppercase?
        UA=UA-"A"+1            ;Set +1 to +26
    ELSE.                  ;Else lowercase
        UA=-(UA-"a"+1)         ;Set -1 to -26
    ENDIF.
ELSE.                  ;Else 0 or negative
    UA=-UA
    IF.UA=<"0"
    OR.UA=>"Z"
        UA=0
        LoopBreak=0
    ENDIF.
    IF.UA=>"9"
    AND.UA=<"A"
        UA=0
        LoopBreak=0
    ENDIF.
    IF.UA=<="9"
        UA=UA-"0"
    ELSE.
        UA=UA-"A" + 10
    ENDIF.
ENDIF.

_Music_Mouse Macro Subroutine Listing:

This subroutine is called by _Music_Proc if the ALT key is down when you start Music_from_Anything. It gets input in the form of lines and points drawn by the user on the display area, and uses them to fill a 1024-point data array.

;<Help=H4929

Sgram=1                ;Clear Sgram display
PitchTrk=0             ;No Pitch Track... plain black screen
Msg="<D(120,0)"        ;Message position
Msg="Draw 1024 points in display area"     ;Prompt message
Mtr0="<D(0,0)"         ;Point counter meter position
Mtr0="<F(50)"          ;Point counter font size
Buf2=                  ;Clear output buffer
UB=0                   ;Initial mouse click flag
UI=0                   ;Output point counter
QX=0                   ;Default prior X posn
QY=0                   ;Default prior Y posn
Pix#Dw=10              ;Line width 10 pixels
Pix#Dc=128             ;Initial line color
Pix#Dr=5               ;Rainbow mode on, step=5
Pix#D=1                ;Smooth line drawing (0=point trail)
WHILE.UI=<1024         ;Wait for 1024 total points
    IF.Posn?m=1            ;Mouse button down?
        IF.UB=0                ;Initial click?
            UB=1                   ;Flag the click
            UC=Pix?c               ;Get color, if in color column
            IF.UC=>0               ;Was it?
                Pix#Dc=UC              ;Set as current color
             ENDIF.
        ELSE.                  ;Not initial click
            UX=Pix?x               ;Get X position
            UY=Pix?y               ;Get Y position
            IF.UX=>=0              ;-1 if outside trace area
            AND.UX=!QX             ;Changed posn?
            OR.UY=!QY
                QX=UX                  ;Save new for next change test
                QY=UY
                UC=Pix?D               ;Get color just used
                Buf2[UI]=(UX + UY + UC) & 255  ;Save sum as music point
                Mtr0=UI                ;Show the point number
                WaitTrace=             ;Wait one TraceUpdate (10 msec)
                UI=UI+1                ;Next music point
            ENDIF.
        ENDIF.
    ELSE.                  ;Else mouse button up
        UB=0                   ;Reset flag
    ENDIF.
WEND.
Pix#D=0                ;Line drawing mode off
Msg=                   ;Remove line drawing prompt
Mtr0=                  ;Remove point count
Str0="Mouse points"    ;Label title for music performance

UZ=Buf2?p              ;Positive peak

_Music_Clipboard Macro Subroutine Listing:

This subroutine is called by _Music_Proc if the CTRL key is down when you start Music_from_Anything. It gets data from an image that you have previously copied to the Windows Clipboard from some other program such as Paint.

After finding the size of the clipboard image, it reads 1024 equally-spaced 16-bit values to be used in music creation. These values are not treated as specific pixels; the bitmap may have 8, 16, 24, or 32 bits per pixel, but this code ignores that. (However, images with less than 8 bits per pixel are rejected.) So if you have a 256-color image with 8 bits per pixel, then two adjacent 0-255 pixel color indexes will be concatenated to form each 16-bit value. If you have a 24-bit image then the 16-bit value will only include two of the three bytes making up a pixel color.

;<Help=H4929

Sgram=1                ;Force Spectrogram mode
US=Pix?p               ;Open Clipboard, read BMP size
IF.US=0                ;Size = 0?
    Msg="No bitmap in Clipboard, or unsupported format"
    Pix#p=0                ;Close clipboard
    LoopBreak=-1           ;Exit Music_from_Anything
ENDIF.
S=US / (1025 * 2)      ;Step size, words
Buf2=                  ;Clear output buffer
UI=0
WHILE.UI=<1024         ;Get 1024 equal-spaced values
    QL=int(S*(UI+1))*2     ;Point to the data at step S
    UX=Pix?w[QL]           ;Get word value at that location
    Buf2[UI]=UX            ;Save as output value
    UI=UI+1                ;Next step
WEND.
Pix#p=0                ;Close clipboard

UZ=Buf2?p              ;Positive peak in output buffer

Str0="Pasted from Clipboard"   ;Title of MIDI performance

_Music_Live Macro Subroutine Listing:

This subroutine is called by _Music_Proc if the Input or Generator is active or a file is open when you start Music_from_Anything. 1024 data points are acquired according to the current display mode: waveform, Spectrum, or Spectrogram.

All data is acquired from the equivalent of a single "screen shot" of the display area. If you have an open file, you should scroll to the region you want to use. With live signals, start Music_from_Anything when you see something you want to convert.

For waveform mode, the 1024 points of each each waveform are summed across all active channels, including Input and Generator channels.

For Spectrum mode, the 512-point linear magnitude spectra of all active channels are summed, then the vertical range is compressed logarithmically. Since most audio sounds are in the lower frequencies of the spectrum, the bottom 3 kHz region is replicated as needed to get 1024 total values. You can choose the region by modifying the following line of code:

    UN=4       ;1=24k, 2=12k, 3=6k, 4=3k freq range

For Spectrogram mode the 1024 data points are acquired by reading colors of a grid of pixels directly from the display. For live signals you will thus want to wait after starting the Spectrogram until you have a full screen of data; otherwise the pixels will be mostly black.

;<Help=H4929

Buf0=              ;Clear work buffers
Buf1=
Buf2=
Buf3=
Str0=              ;Clear string array for label

UC=Disp?m          ;Display-channel bitmap
IF.Sgram=0         ;If no Spectrogram
AND.Spect=0        ;And no Spectrum this must be waveform
    IF.UC&1=1          ;Channel 0 (Left In) active?
        Buf0="<=W0"        ;Waveform to Buf0
    ENDIF.
    IF.UC&2=1          ;Channel 1 (Right In) active?
        Buf1="<=W1"        ;Waveform to Buf1
    ENDIF.
    IF.UC&4=1          ;Channel 2 (Left Out) active?
        Buf2="<=W2"        ;Waveform to Buf2
    ENDIF.
    IF.UC&8=1          ;Channel 3 (Right Out) active?
        Buf3="<=W3"        ;Waveform to Buf3
    ENDIF.
    E.IF.Pause=        ;Pause enabled?
        Pause=1            ;Set Pause if so
    ENDIF.
    Buf2="<+B0"        ;Add chan 0 to chan 2
    Buf2="<+B1"        ;Add chan 1 to sum
    Buf2="<+B3"        ;Add chan 3
    IF.Open=>0         ;Open file data?
        Str0="Waveform of " + FileName?N   ;Label with file name
    ELSE.
        Str0="Live wave, " +d +b +t        ;Label with date & time
    ENDIF.
ENDIF.

IF.Spect=1         ;Spectrum?
    UN=4               ;1=24k, 2=12k, 3=6k, 4=3k freq range
    IF.UC&1=1          ;Channel 0 (Left In) active?
        Buf0="<mS0"        ;Magnitude spectrum to Buf0
    ENDIF.
    IF.UC&2=1          ;Channel 1 (Right In) active?
        Buf1="<mS1"        ;Magnitude spectrum to Buf1
    ENDIF.
    IF.UC&4=1          ;Channel 2 (Left Out) active?
        Buf2="<mS2"        ;Magnitude spectrum to Buf2
    ENDIF.
    IF.UC&8=1          ;Channel 3 (Right Out) active?
        Buf3="<mS3"        ;Magnitude spectrum to Buf3
    ENDIF.
    E.IF.Pause=        ;Pause enabled?
        Pause=1            ;Set Pause if so
    ENDIF.
    Buf2="<+B0"        ;Add chan 0 to chan 2
    Buf2="<+B1"        ;Add chan 1 to sum
    Buf2="<+B3"        ;Add chan 3
    Buf2="<+(1)"       ;Prevent log(0) error
    Buf2="<L(1)"       ;Base-2 logarithm
    Buf2="<*(100)"     ;x100
    UK=2^UN            ;2,4,8,16 chunks of spectrum
    UL=1024/UK         ;512, 256, 128, 64 points each
    UI=0
    WHILE.UI=<UL       ;Copy UL points to fill Buf2
        UX=Buf2[UI]        ;Get point
        UJ=1
        WHILE.UJ=<UK       ;Copy to all UK chunks of Buf2
            Buf2[UL*UJ+UI]=UX  ;Set UIth point in UJth chunk
            UJ=UJ+1
        WEND.
        UI=UI+1
    WEND.
    IF.Open=>0             ;Open file data?
        Str0="Spectrum of " + FileName?N   ;Label with file name
    ELSE.
        Str0="Live Spectrum, " +d +b +t    ;Label with date & time
    ENDIF.
ENDIF.

IF.Sgram=1         ;Spectrogram?
    Msg="Acquiring Spectrogram data..."
    UI=0               ;Data point counter, 0-1023
    QX=14              ;Initial X value
    QY=1               ;Initial Y value
    WHILE.QX=<498      ;44 horizontal points, 11 pixels apart
        WHILE.QY=<244      ;23 vertical points, 11 pixels apart
            Buf2[UI]=Pix[QX,QY]    ;Pixel color index 0-255
            QY=QY+11           ;Next vertical pixel
            UI=UI+1            ;Count the data point
        WEND.             ;Do all 23 points in column
        QY=1              ;Start next column at Y=1
        QX=QX+11          ;Next column X=11 rightward
    WEND.              ;Do all 44 columns for 23x44=1012 points
    WHILE.UI=<1024     ;12 more points to make 1024
        Buf2[UI]=Pix[QX,QY]    ;All in 45th column
        QY=QY+11          ;Next vertical pixel
        UI=UI+1           ;Count the data point
    WEND.
    IF.Open=>0         ;Open file data?
        Str0="Spectrogram of " + FileName?N    ;Label with file name
    ELSE.
        Str0="Live Spectrogram, " +d +b +t     ;Label with date & time
    ENDIF.
ENDIF.

UX=Buf2?n          ;Negative peak of Buf2
Buf2="<-(UX)"      ;Shift up so neg peak is at 0
UZ=Buf2?p          ;Positive peak

_Music_NoFile Macro Subroutine Listing:

This subroutine is called by _Music_Proc if there is nothing else selected: No live signal, no file open, no ALT for mouse, no CTRL for Clipboard, no SHIFT for saved prior data, and you decline to load a file when prompted.

_Music_NoFile checks to see if there is anything in Notes. If not, it checks to see if there is a value in Field1. If so, that value is used as a seed for the random generator, and used to generate the 1024 values. If there is nothing in Field1, the random generator is randomized (if it hasn't been done yet in the current session) and a random seed is obtained and used as above.

Alternatively, if there is anything at all in Notes, but it is less than 10 characters, the current date and time are appended. Then, if there are less than 1024 characters, whatever is there is copied repeatedly to reach that total.

;<Help=H4929

UR=0                ;No random seed yet
Buf2="<NoteS"       ;Copy string from Notes
UL=Buf2?L           ;Number of characters copied
IF.UL=0             ;Nothing in Notes?
    UR=U1              ;Possible seed (saved initial Field1)
    IF.UR=0            ;No seed entered?
        IF.Posn?9=0        ;Did we randomize on prior run?
            Randomize=1        ;Do it now if not
            Posn#9=1           ;Flag so we don't repeat
        ENDIF.
        X=rand(0,2^32-1)   ;Random 64-bit value 0 to 2^32-1
        IF.X=>2^31         ;Convert unsigned to signed
            X=X-2^32           ;-2^31 to +2^31-1
        ENDIF.             ;Now it fits in 32-bit integer
        UR=X               ;Integer random value
    ENDIF.
    Posn#r=UR              ;Set seed of 32-bit random generator
    Field1="h"+UR(h)       ;Show it in Field1
    Buf2="<r(0,255)"       ;Fill Buf2 with random values 0-255
    Str0="Random Seed h" + UR(h)   ;Label includes seed
ELSE.               ;Else something in Notes
    IF.UL=<10          ;Pad short strings with date & time
        Notes=Notes + " " +d +" " +t
        Buf2="<NoteS"      ;Copy back to Buf2
        UL=Buf2?L          ;New size of Notes
    ENDIF.

    UJ=UL              ;Repeat the above pattern to fill buffer
    WHILE.UJ=<1024
        UI=UJ % UL         ;UI counts from 0 to UL-1, then repeats
        Buf2[UJ]=Buf2[UI]  ;Copy value from 0 to UL-1 to higher point
        UJ=UJ+1            ;Repeat until we hit 1024
    WEND.

    Str0=Notes         ;Copy entire Notes to Str0
    Str0=Str0[0,48] + "..." +z     ;Truncate and insert null.
ENDIF.

UZ=Buf2?p          ;Peak value in Buf2

_Music_DAT Macro Subroutine Listing:

_Music_DAT is called by _Music_Proc if a file is selected that Music_from_Anything doesn't understand. That might be because it lacks a header, such as a .TXT file or one of Daqarta's own .DAT files, or because the header is something Music_from_Anything doesn't accept, such as a .GIF file.

In these cases, _Music_DAT first checks to see that file size QL is at least 2048 bytes, then it sets the starting point for reading the file to UO bytes, which defaults to 0. It computes a step size such that the file will be evenly covered by 1024 steps, excluding small borders at the start and end. The file is then traversed in a loop by computing the target of each step, opening the file at that point, and reading a 16-bit value there. This allows it to handle extremely large files, of arbitrary size.

_Music_DAT is also called by _Music_JPG, _Music_BMP, and _Music_WAV after each has validated the file from its header info, determined the size QL of the actual data (without the header), and set UO to skip over the header and start at the data area.

_Music_Proc sets Buf0#WU=16 to read 16-bit unsigned data before calling _Music_DAT directly; that's also used for .JPG and .BMP images, but is set to Buf0#W=16 for .WAV files to read 16-bit signed data.

;<Help=H4929

;DAT = No header, or unsupported file type.
;   QL = file size, UO = 0

;JPG, BMP, and WAV:
;   QL = size of data area, UO = size of header to skip

IF.QL=<2048            ;File too small?
    Msg="File less than 2048 bytes"
    LoopBreak=-1       ;Abort
ENDIF.
Buf0#H=UO              ;Start of data area (after header?)
S=QL / (1025 * 2)      ;Step size to get 1024 words

Buf2=                  ;Clear Buf2
UI=0
WHILE.UI=<1024         ;Get 1024 points
    QL=int(S*(UI+1))       ;File pointer for this point
    Buf0#L=QL              ;Load up to this many bytes
    Buf0#N=1               ;Single buffer only
       A.Buf0="<LoadDAT:" + FileName   ;Open the file
    Buf2[UI]=Buf0[0]       ;0th byte of Buf0 is UIth point
    UI=UI+1                ;Next data point
WEND.

UX=Buf2?n              ;Negative peak, if any
Buf2="<-(UX)"          ;Shift up so neg peak is at 0
UZ=Buf2?p              ;Positive peak of unipolar data

Str0=FileName?N        ;Get file name for MIDI titlebar label

_Music_JPG Macro Subroutine Listing:

This subroutine is called by _Music_Proc if you select a .JPG image file type when prompted by Music_from_Anything. JPG files use sophisticated compression methods to reduce file size. _Music_JPG makes no attempt to uncompress the data; it just locates the data area and calls _Music_DAT to read 1024 points from the compressed data. However, the JPG header does not contain a pointer to the data area; it contains many optional sections that must be skipped over until the data area is found.

;<Help=H4929

;On entry, Buf0 has been loaded with the first 1024 file
;words. The first word has been validated as FF, D8 for JPG
;file

Str1=                          ;Clear Str1 string array
Str1[0]="1010100000000000"     ;C0-CF
Str1[16]="3333333330110200"    ;D0-DF
Str1[32]="1111111111111111"    ;E0-EF
Str1[48]="0000000000000010"    ;F0-FF

;Assume next 2 bytes are FF,E0, etc and get following size
UA=Buf0[2]             ;Size of section data to skip, incl size
UO=bswp2(UA)           ;Big-endian size to binary
UO=UO + 4              ;Offset incl FF,D8 and FF,E0, etc

WHILE.UO=<QL           ;Read while less than file size
    Buf0#N=1               ;Load single Buf0
    Buf0#WU=16             ;Unsigned 16-bit data
    Buf0#H=UO              ;Start at offset
    A.Buf0="<LoadDAT:" + FileName  ;Load Buf0 with up to 1024 words

    UX=Buf0[0]             ;Get FF,xx ID (xxFF in hex)
    UX=int(UX>>8)          ;Get ID byte
    QN=UX - hC0            ;Anything less than hC0 is error
    IF.QN=<0
        Msg="FF," +UX(h) +" not valid ID at " +UO
        LoopBreak=-1           ;Abort
    ELSE.
        U1=Str1?b[QN] - h30
        IF.U1=0
            Msg="FF," +UX(h) +" not valid ID at " +UO
            LoopBreak=-1       ;Abort
        ENDIF.
        IF.U1=1                ;Get size of block to skip
            UA=Buf0[1]             ;Size of data to skip, incl size
            QB=bswp2(UA)           ;Big-endian size to binary
            UO=UO + QB + 2         ;Offset incl FF,xx
            IF.UX=hDA              ;Start Of Scan?
                LoopBreak=2            ;Exit WHILE if so
            ENDIF.
        ENDIF.
        IF.U1=2                ;FF,DD = skip 4 bytes
            UO=UO + 6              ;Offset incl FF,DD
        ENDIF.
        IF.U1=3                ;FF,D0-D7 or FF,FF = Do Nothing
            UO=UO+2                ;Just skip this ID
        ENDIF.
    ENDIF.
WEND.

IF.UX=!hDA             ;No Start Of Scan ID?
    Msg="Error in JPG header"
    LoopBreak=-1           ;Abort
ENDIF.

;Raw data is assumed to run from UO to (QL-2)
QL=QL-UO-2             ;Size of data area
@_Music_DAT            ;Read 1024 equal-spaced points

_Music_BMP Macro Subroutine Listing:

This subroutine is called by _Music_Proc if you select a .BMP (bitmap image) file type when prompted by Music_from_Anything. The code reads the start byte of the bitmap from the file header and sets UO, then subtracts that from the total file size QL and calls _Music_DAT to do everything else.

_Music_DAT normally handles headerless files or files that Music_from_Anything doesn't understand. It just treats everything as raw data and extracts 1024 points spaced evenly throughout. Howver, it skips UO bytes before it starts; that value is normally set to 0 for an unknown file type, but here it is set to skip the BMP header and start in the actual bitmap data area, which holds QL bytes.

;<Help=H4929

;File size in QL from _Music_Proc caller
UO=Buf0[5] + Buf0[6]<<16   ;File offset to bitmap
QL=QL-UO                   ;Data size
@_Music_DAT                ;Extract 1024 points

_Music_WAV Macro Subroutine Listing:

This subroutine is called by _Music_Proc if you select a .WAV (waveform) file type when prompted by Music_from_Anything. The first part of the header has been validated by _Music_Proc before calling this, which does further validation and aborts if that fails. Note that it supports standard .WAV files but not multi-channel files that use the WAVEFORMATEXTENSIBLE format.

If validated, the file read mode is set to 16-bit signed binary, and the data size QL and the header size (offset to the data) UO are computed. Then _Music_DAT is called to extract 1024 evenly-spaced data points, skipping over the UO header bytes.

;<Help=H4929

IF.Buf0[6]=!h6D66      ;Not 'fm' ?
OR.Buf0[7]=!h2074      ;Not 't ' ?
OR.Buf0[10]=!1         ;Not WAVE_FORMAT_PCM tag?
    Msg="Not a valid WAV file"
    LoopBreak=-1           ;Abort
ELSE.                  ;Else valid WAV file
    QF=Buf0[8]         ;fmt size, bytes
    Buf0#W=16      ;ASSUME 16-bit signed binary
    QD=QF/2 + 12       ;Data size ptr
    QL=Buf0[QD] + Buf0[QD+1]<<16   ;Data size in BYTES
    UO=2 * (QD+2)      ;Offset to data, bytes
    @_Music_DAT        ;Extract 1024 points
ENDIF.

_Music_WIF Macro Subroutine Listing:

This subroutine is called by _Music_Proc if you select a .WIF (weaving information) file type when prompted by Music_from_Anything. The header has already been validated as starting with [WIF].

A .WIF file consists of plain ASCII text with section headers defined by brackets, such as [WARP] and [WEFT]. There are 8 such sections that are of interest to Music_from_Anything, plus a [CONTENTS] section that lists the sections to follow, without brackets. Sections may appear in any order in the file, and not necessarily the order they appear in [CONTENTS]. These are the relevant sections:

[WARP]
[WEFT]
[WARP COLORS]
[WEFT COLORS]
[THREADING]
[TIEUP]
[TREADLING]
[LIFTPLAN]

Some sections are mutually exclusive: A .WIF file can have either [TIEUP] and [TREADLING], or a single [LIFTPLAN] that is equivalent to both.

The file search scheme loads Buf0 after setting the load width to 64 bits per sample, which packs 8 characters into each of the 1024 buffer index positions, or up to 8192 per load. The buffer is "mapped" to Str0, which means that Str0 acts as an 8192-character string that can be searched or read (but not written) as needed to extract data.

It locates the [CONTENTS] section and searches within it for the 8 sections it wants. Then it searches for each of those sections in the file. If the file is longer than 8192 bytes, the search will advance the file pointer a little less than 8192 bytes at a time in case a section search was midway through the header at the end of the prior 8192. For example, if it got as far as [TREA while looking for [TREADLING], the search will fail at the end of the buffer. But on the next load it will start anew looking for the same target, only slightly earlier than where it left off so it will be sure to find the start of the name.

Whenever it finds a section header, it reloads at a point just ahead of the start of the header. This insures that the entire contents of that section will be in the buffer for processing.

As sections are found, their contents are analyzed and stored in separate buffers for subsequent use.

The [WARP] section has a Color entry that gives the default color number; this is used to fill all values in Buf1, which will become the list of colors for each warp thread.

The [WARP COLORS] section is a list of specific warp threads and their color numbers; these update Buf3. Any threads not included in the list will remain at the default color.

Similarly, the [WEFT] and [WEFT COLORS] sections are used to fill Buf2 with the colors for each weft "pick" (shuttle pass).

The [THREADING] section has an entry for each warp thread, telling which shaft number will lift it. This information is saved in Buf3, where the index is the warp thread number and its value is the shaft number. Buf3[0] is used to hold the total number of threads.

Since more than one shaft can be tied to each treadle, the [TIEUP] section has an entry for each treadle, listing which shafts will be activated when that treadle is pressed. This information is stored in Buf4, with the index holding the treadle and the value stored as a bitmap of shafts activated by that treadle. (Bitmaps are limited to 32 bits, hence the loom can't have more than 32 shafts.)

The [TREADLING] section is a list giving the treadle number or numbers used for each pick. This information is used along with the tieup data in Buf4 to create a "liftplan" in Buf5, where each index is a pick number whose value is a bitmap of all the shafts activated on that pick.

Some .WIF files skip the [TIEUP] and [TREADLING] sections and instead include a [LIFTPLAN] section that has an entry for each pick, followed by a list of all the shafts that are activated for that pick. In this case, Buf5 bitmaps will be created directly from the shaft list for each pick.

When all required sections are complete, a "drawdown" can be created that is essentially an idealized image of the weave, showing each thread that is on top at any given location; this is essentially a "pixel" of that thread's color. For music creation we don't need a visible image, but we do need to extract the same information. Here we work through the weave incrementally, extracting 1024 points as needed by _Music_Proc to drive the music creation.

Each point is derived from 7 adjacent pixels. A 7-bit bitmap value is obtained from the states of each pixel: 1 if the warp thread is showing, 0 if the weft thread. This is determined by the binary AND of the Buf3 bitmap of the shaft used for the warp thread, and the Buf5 bitmap of the shafts activated for the weft pick. If the Buf3 shaft bit is not also present in the Buf5 shaft map, then that shaft is not raised on that pick so the weft color will be on top and visible.

At the same time, the low 2 bits of the color numbers of each visible thread are summed together, giving a total of 0 to 21. (If both bits are set for a pixel, the value is 3, and if all 7 pixels have that value the total is 21.) Then the bitmap and the color total are summed to get one of the 1024 needed output points.

Each point thus takes 7 drawdown pixels. The total number of pixels is the product of the warp threads and weft threads, so if this is more than 7 * 1024 then the output points are spaced evenly throughout the fabric. If there are less pixels than that, then the 7-pixel output points are adjacent and the extraction works through to the end of the fabric and then wraps back to the start and continues extracting.

;<Help=H4929

;On entry, QL holds file size in bytes.
Msg="Analyzing WIF file"
Buf0=                          ;Clear work buffers
Buf1=
Buf2=
Buf3=
Buf4=
Buf5=
Buf6=
Str1=                          ;Clear string array
Str1[0]="WARP"                 ;Section names to search for
Str1[1*20]="WEFT"              ;No brackets in [CONTENTS] list
Str1[2*20]="WARP COLORS"
Str1[3*20]="WEFT COLORS"
Str1[4*20]="THREADING"
Str1[5*20]="TIEUP"
Str1[6*20]="TREADLING"
Str1[7*20]="LIFTPLAN"
Buf0#H=0                       ;No header offset
Buf0#W=64                      ;Load as packed raw text
Buf0#L=0                       ;Load starting at beginning
Buf0#N=1                       ;Single Buf0 load
A.Buf0="<LoadDAT:" + FileName  ;Fill Buf0 with up to 8192 chars
Str0#B=0                       ;Map Buf0 to Str0 for ease of access

;First search contents to see what sections will be used:
Str0#F="[CONTENTS]"            ;Search Str0 for contents string
QC=Str0?P +2                   ;Point to 2 beyond end of string
Str0[QC]#F="["                 ;Search for start of next section
QD=Str0?P                      ;Point to the '['
US=0                           ;Section index 0-7
WHILE.US=<8                    ;8 possible target strings
    Str0[QC,QD]#F=Str1[20*US]      ;Search contents for string US
    UX=Str0?F                      ;Search status
    IF.UX=<0                       ;Neg = success!
        Str1[200 + 20*US]="1"          ;Flag = present in contents
    ELSE.
        Str1[200 + 20*US]="0"      ;Else not present
    ENDIF.
    US=US+1                        ;Next target to search
WEND.

;Next search the entire file for each of the sections found above.
;Since the file may be longer than 8192 chars, may take multiple passes.
US=0                           ;Section index 0-7
WHILE.US=<8                    ;Scan for each
    IF.Str1[200 + 20*US]="1"       ;Present in contents?
        UF=0                           ;File load start pointer
        WHILE.UF=<(QL/8 - 4)           ;Search entire file, if needed
            Buf0#L=UF                      ;Load starting here
            Buf0#H=0                       ;No header
            Buf0#W=64                      ;Pack 8 chars / Buf0 index
            Buf0#N=1                       ;Single Buf0 load
            A.Buf0="<LoadDAT:" + FileName  ;Fill Buf0 with up to 8192 chars
            Str0#F="[" + Str1[US*20] +"]"  ;Search for US [section]
            QF=Str0?F                      ;Search status
            IF.QF=<0                       ;Found the string?
                LoopBreak=2                    ;End file scan if so
            ENDIF.
            UF=UF+1000                     ;Else advance 8000 chars
        WEND.                          ;Repeat until section found
        IF.QF=>0                       ;Not found?
            Msg="[" + Str1[US*20] +"] section not found!"
            LoopBreak=-1                   ;Abort
        ENDIF.
        UA=Str0?P                      ;Ptr past string in Str0
        UF=UF + UA/8 - 4               ;File ptr before string
        Buf0#L=UF                      ;Load there
        Buf0#H=0                       ;No header
        Buf0#W=64                      ;Pack 8 chars / Buf0 index
        Buf0#N=1                       ;Single Buf0 load
        A.Buf0="<LoadDAT:" + FileName  ;Fill Buf0 with up to 8192 chars
        Str0#F="[" + Str1[US*20] +"]"  ;Search for same section
        QF=Str0?F                      ;Seach status
        UA=Str0?P                      ;Ptr past section header

;Retrieve and save the contents of each section for subsequent use:
        IF.US=0                        ;[WARP] section
            Msg="Analyzing WIF file"+n+"WARP default color"
            Str0[UA]#F="Color"             ;Search for color ID
            UA=Str0?P                      ;Ptr past ID
            UX=Str0?V                      ;Read default color number
            Buf1="<=(UX)"                  ;Fill Buf1 with WARP color num
        ENDIF.

        IF.US=1                        ;[WEFT] section
            Msg="Analyzing WIF file"+n+"WEFT default color"
            Str0[UA]#F="Color"             ;Search for color ID
            UA=Str0?P                      ;Ptr past ID
            UX=Str0?V                      ;Read default color number
            Buf2="<=(UX)"                  ;Fill Buf2 with WEFT color num
        ENDIF.

;Get colors for each warp thread into Buf1:
        IF.US=2                        ;[WARP COLORS] section
            Msg="Analyzing WIF file"+n+"WARP COLORS"
            QJ=1                           ;Buffer index / thread number
            WHILE.QJ=!0                    ;Do all entries in list
                QJ=Str0?V                      ;Get thread number
                IF.QJ=0                        ;0 = past end of section
                OR.QJ=>1023                    ; or past Buf1 limit?
                    LoopBreak=2                    ;Done with section
                ENDIF.
                QX=Str0?V                      ;Read color number
                Buf1[QJ]=QX                    ;Save color # at thread #
            WEND.
        ENDIF.

;Get colors for each weft thread into Buf2:
        IF.US=3                        ;[WEFT COLORS] section
            Msg="Analyzing WIF file"+n+"WEFT COLORS"
            QJ=1                           ;Buffer index / thread number
            WHILE.QJ=!0                    ;Do all entries in list
                QJ=Str0?V                      ;Get pick number
                IF.QJ=0                        ;0 = past end of section
                OR.QJ=>1023                    ; or past Buf2 limit?
                    LoopBreak=2                    ;Done with section
                ENDIF.
                QX=Str0?V                      ;Read color number
                Buf2[QJ]=QX                    ;Save color # at pick #
            WEND.
        ENDIF.

;Get bitmap of shaft number for each warp thread into Buf3:
        IF.US=4                        ;[THREADING] section
            Msg="Analyzing WIF file"+n+"THREADING"
            QJ=1                           ;Buffer index / thread number
            WHILE.QJ=!0                    ;Do all entries in list
                QJ=Str0?V                      ;Get thread number
                IF.QJ=0                        ;0 = past end of section
                OR.QJ=>1023                    ; or past Buf3 limit?
                    LoopBreak=2                    ;Done with section
                ENDIF.
                QC=QJ                          ;Save thread number
                QX=Str0?V                      ;Get shaft number
                QW=2^(QX-1)                    ;Convert to binary
                Buf3[QJ]=QW                    ;Save shaft # at thread #
            WEND.                          ;Next thread entry
            Buf3[0]=QC                     ;Thread count to 0th index
        ENDIF.

;Get shaft numbers that will be activated by each treadle into Buf4:
        IF.US=5                        ;[TIEUP] section
            Msg="Analyzing WIF file"+n+"TIEUP"
            QJ=1                           ;Treadle number
            WHILE.QJ=!0                    ;Do all treadles in list
                QJ=Str0?V                      ;Get treadle number
                IF.QJ=0                        ;0 = past end of section
                    LoopBreak=2                    ;Done with section
                ENDIF.
                QX=1                           ;Shaft number
                QW=0                           ;Accumulate shaft bits
                WHILE.QX=!0                    ;Do all shafts
                    QX=Str0?V                      ;Get shaft number
                    QE=Str0?T                      ;Terminator char
                    IF.QX=0                        ;0 = past last shaft
                        LoopBreak=2                    ;Done with treadle
                    ENDIF.
                    QW=QW | (2^(QX-1))             ;Add shaft bit to accum
                    IF.QE=13                       ;Carriage return next?
                        LoopBreak=2                    ;Done with treadle
                    ENDIF.
                WEND.                          ;Do all shafts for treadle
                Buf4[QJ]=QW                    ;Shaft map to treadle num
            WEND.                          ;Next treadle line in list
        ENDIF.

;Get liftplan bitmap of active shafts for each weft pick into Buf5:
        IF.US=6                        ;[TREADLING] section
            Msg="Analyzing WIF file"+n+"TREADLING"
            QJ=1                           ;Pick (shuttle throw) number
            WHILE.QJ=!0                    ;Do all picks in list
                QJ=Str0?V                      ;Get pick number
                IF.QJ=0                        ;0 = past end of section
                OR.QJ=>1023                    ; or past Buf5 limit?
                    LoopBreak=2                    ;Done with section
                ENDIF.
                QC=QJ                          ;Save pick number
                QX=1                           ;Treadle number
                QW=0                           ;Shaft bit accumulator
                WHILE.QX=!0                    ;Do all treadles on line
                    QX=Str0?V                      ;Get treadle number
                    QE=Str0?T                      ;Terminator char
                    IF.QX=0                        ;0 = end of line
                        LoopBreak=2                    ;Done with line
                    ENDIF.
                    QW=QW | Buf4[QX]               ;Add treadle shaft(s)
                    IF.QE=13                       ;Carriage return?
                        LoopBreak=2                    ;Done with treadles
                    ENDIF.
                WEND.                          ;Next treadle in line
                Buf5[QJ]=QW                    ;Save liftplan at pick num
            WEND.
            Buf5[0]=QC                     ;Save pick count at 0th index
        ENDIF.

;Instead of separate TIEUP and TREADLING, get direct bitmap of active
; shafts for each weft pick into Buf5:
        IF.US=7                        ;[LIFTPLAN] section
            Msg="Analyzing WIF file"+n+"LIFTPLAN"
            QJ=1                           ;Pick (shuttle throw) number
            WHILE.QJ=!0                    ;Do all picks in list
                QJ=Str0?V                      ;Get pick number
                IF.QJ=0                        ;0 = past last line in section
                    LoopBreak=2                    ;Done with section
                ENDIF.
                QC=QJ                          ;Save pick number
                QX=1                           ;Shaft number
                QW=0                           ;Shaft bit accumulator
                WHILE.QX=!0                    ;Do all shafts in line
                    QX=Str0?V                      ;Get shaft number
                    QE=Str0?T                      ;Terminator char
                    IF.QX=0                        ;0 = end of line
                        LoopBreak=2                    ;Done with pick line
                    ENDIF.
                    QW=QW | (2^(QX-1))             ;Add shaft bit to accum
                    IF.QE=13                       ;Carriage return?
                        LoopBreak=2                    ;Done with pick line
                    ENDIF.
                WEND.                          ;Next shaft on pick line
                Buf5[QJ]=QW                    ;Save shaft map at pick num
            WEND.                          ;Next pick line
            Buf5[0]=QC                     ;Save pick count at 0th index
        ENDIF.                         ;End LIFTPLAN section
    ENDIF.                         ;End section present in contents
    US=US+1                        ;Next section
WEND.

;Create the drawdown 7 pixels at a time for each output point:
Msg="Drawdown Array: " +n +"0% Done"
UI=0                           ;Output array pixel index
Q3=Buf3[0]                     ;Warp count
Q5=Buf5[0]                     ;Weft count
QN=int(Q3 * Q5 / 1024)         ;We need 7x1024 pixels
IF.QN=>7                       ;Enough to skip pixels between 7-bits?
    QN=QN-7                        ;Skip this many if so
ELSE.
    QN=0                           ;Else don't skip, just continue
ENDIF.
QC=1                           ;Initial warp index
QR=1                           ;Initial weft index
QJ=102                         ;About 10% of 1024 points
QQ=10                          ;Percent done (at first use)
WHILE.UI=<1024                 ;Do all 1024 output points
    IF.UI=QJ                       ;At next 10% done point?
        Msg="Drawdown Array: " +n +QQ(0.0) + "% Done"
        QQ=QQ+10                       ;Next percent done value
        QJ=QJ+102                      ;Point number at next 10%
    ENDIF.
    Q7=0                           ;7-bit output value
    Q8=0                           ;Color component
    U7=0                           ;Count 7 bits per output
    WHILE.U7=<8
        QX=Buf3[QC] & Buf5[QR]         ;Drawdown pixel
        IF.QX=0                        ;Weft thread showing?
            QZ=Buf2[QR]                    ;Use weft color for this pick
        ELSE.                          ;Else warp showing
            QZ=Buf1[QC]                    ;Use warp color for this thread
            QX=1
        ENDIF.
        Q7=Q7<<1 + QX                  ;Build 7- bit binary
        Q8=Q8 + (QZ & 3)               ;Sum uses low 2 color bits
        QC=QC+1                        ;Next warp index
        IF.QC=>Q3                      ;Past end of warp?
            QC=QC % Q3                     ;Wrap to new line
            QR=QR+1                        ;Next weft line
            IF.QR=>Q5                      ;Line past end of weft?
                QR=1                           ;Back to start if so
            ENDIF.
        ENDIF.
        U7=U7+1                        ;Next bit of 7-bit bitmap
    WEND.
    Buf6[UI]=Q7 + Q8               ;Output point = bitmap + color sum
    QC=QC+QN
    IF.QC=>Q3                      ;Past end of warp?
        QC=QC % Q3                     ;Wrap to new line
        QR=QR+1                        ;New line
        IF.QR=>Q5                      ;Line past end of weft?
            QR=1                           ;Back to start if so
        ENDIF.
    ENDIF.
    UI=UI+1                        ;Next output point
WEND.

Buf2="<=B6"                    ;Copy Buf6 to Buf2
Str0#B=-1                      ;Unmap Str0 from Buf0
UZ=Buf2?p                      ;Positive peak

Str0=FileName?N                ;Save file name for music title

_Music_DTX Macro Subroutine Listing:

This subroutine is called by _Music_Proc if you select a .DTX weaving draft file type when prompted by Music_from_Anything. The header has already been validated as starting with @@StartDTX.

A .DTX file consists of plain ASCII text with section headers defined by @@, such as @@Warp Colors and @@Weft Colors. There are 6 such sections that are of interest to Music_from_Anything:

@@Warp Colors
@@Weft Colors
@@Threading
@@Tieup
@@Treadling
@@Liftplan

Some sections are mutually exclusive: A .DTX file can have either @@Tieup and @@Treadling, or a single @@Liftplan that is equivalent to both.

The file search scheme loads Buf0 after setting the load width to 64 bits per sample, which packs 8 characters into each of the 1024 buffer index positions, or up to 8192 per load. The buffer is "mapped" to Str0, which means that Str0 acts as an 8192-character string that can be searched or read (but not written) as needed to extract data.

Unlike the above _Music_WIF subroutine, _Music_DTX ignores its @@Contents section and searches the file for each of the target sections by name. If the file is longer than 8192 bytes, the search will advance the file pointer a little less than 8192 bytes at a time in case a section search was midway through the header at the end of the prior 8192. For example, if it got as far as @@Trea while looking for @@Treadling, the search will fail at the end of the buffer. But on the next load it will start anew looking for the same target, only slightly earlier than where it left off so it will be sure to find the start of the name.

Whenever it finds a section header, it reloads at a point just ahead of the start of the header. This insures that the entire contents of that section will be in the buffer for processing.

As sections are found, their contents are analyzed and stored in separate buffers for subsequent use.

The @@Warp Colors section is a list of warp threads and their color numbers. It is used to build Buf3 such that each index is a warp thread number, and the value at that index is the color number of that warp thread.

Similarly, the @@Weft Colors section is used to fill Buf2 with the colors for each weft "pick" (shuttle pass).

The @@Threading section has an entry for each warp thread, telling which shaft number will lift it. This information is saved in Buf3, where the index is the warp thread number and its value is the shaft number. Buf3[0] is used to hold the total number of threads.

Since more than one shaft can be tied to each treadle, the @@Tieup section has an entry for each treadle, consisting of a bitmap of which shafts will be activated when that treadle is pressed. This information is stored in Buf4, with the index holding the treadle and the value holding the shaft bitmaps. (Bitmaps are limited to 32 bits, hence the loom can't have more than 32 shafts.)

The @@Treadling section is a list giving the treadle number or numbers used for each pick. This information is used along with the tieup data in Buf4 to create a "liftplan" in Buf5, where each index is a pick number whose value is a bitmap of all the shafts activated on that pick.

Some .DTX files skip the @@Tieup and @@Treadling sections and instead include a @@Liftplan section that has an entry for each pick, consisting of a bitmap of which shafts that are activated for that pick. In this case, Buf5 bitmaps will be created directly from the shaft list for each pick.

When all required sections are complete, a "drawdown" can be created that is essentially an idealized image of the weave, showing each thread that is on top at any given location; this is essentially a "pixel" of that thread's color. For music creation we don't need a visible image, but we do need to extract the same information. Here we work through the weave incrementally, extracting 1024 points as needed by _Music_Proc to drive the music creation.

Each point is derived from 7 adjacent pixels. A 7-bit bitmap value is obtained from the states of each pixel: 1 if the warp thread is showing, 0 if the weft thread. This is determined by the binary AND of the Buf3 bitmap of the shaft used for the warp thread, and the Buf5 bitmap of the shafts activated for the weft pick. If the Buf3 shaft bit is not also present in the Buf5 shaft map, then that shaft is not raised on that pick so the weft color will be on top and visible.

At the same time, the low 2 bits of the color numbers of each visible thread are summed together, giving a total of 0 to 21. (If both bits are set for a pixel, the value is 3, and if all 7 pixels have that value the total is 21.) Then the bitmap and the color total are summed to get one of the 1024 needed output points.

Each point thus takes 7 drawdown pixels. The total number of pixels is the product of the warp threads and weft threads, so if this is more than 7 * 1024 then the output points are spaced evenly throughout the fabric. If there are less pixels than that, then the 7-pixel output points are adjacent and the extraction works through to the end of the fabric and then wraps back to the start and continues extracting.

;<Help=H4929

;On entry, QL holds file size in bytes.
Msg="Analyzing DTX file"
Buf0=                          ;Clear work buffers
Buf1=
Buf2=
Buf3=
Buf4=
Buf5=
Buf6=
Str1=
Str1[0]="@@Warp Colors"        ;Section names to search for
Str1[1*20]="@@Weft Colors"
Str1[2*20]="@@Threading"
Str1[3*20]="@@Tieup"
Str1[4*20]="@@Treadling"
Str1[5*20]="@@Liftplan"

Buf0#H=0                       ;No header offset
Buf0#W=64                      ;Load as packed raw text
Buf0#L=0                       ;Load starting at beginning
Buf0#N=1                       ;Single Buf0 load
A.Buf0="<LoadDAT:" + FileName  ;Fill Buf0 with up to 8192 chars
Str0#B=0                       ;Map Buf0 to Str0 for ease of access

;Search the entire file for each of the target sections.
;Since the file may be longer than 8192 chars, may take multiple passes.
US=0                           ;Section index 0-5
WHILE.US=<6                    ;Search for all 6 sections
    UF=0                           ;File load start pointer
    WHILE.UF=<(QL/8 - 4)           ;Search entire file, if needed
        Buf0#L=UF                      ;Load starting here
        Buf0#H=0                       ;No header
        Buf0#W=64                      ;Pack 8 chars / Buf0 index
        Buf0#N=1                       ;Single Buf0 load
        A.Buf0="<LoadDAT:" + FileName  ;Fill Buf0 with up to 8192 chars
        Str0#F=Str1[US*20]             ;Search for section US
        QF=Str0?F                      ;Search status
        IF.QF=<0                       ;Found the string?
            LoopBreak=2                    ;End file scan if so
        ENDIF.
        UF=UF+1000                     ;Else advance 8000 chars
    WEND.                          ;Repeat until section found
    UA=Str0?P                      ;Ptr past string in Str0
    UF=UF + UA/8 - 4               ;File ptr before string
    Buf0#L=UF                      ;Load there
    Buf0#H=0                       ;No header
    Buf0#W=64                      ;Pack 8 chars / Buf0 index
    Buf0#N=1                       ;Single Buf0 load
    A.Buf0="<LoadDAT:" + FileName  ;Fill Buf0 with up to 8192 chars
    Str0#F=Str1[US*20]             ;Search for same section
    QF=Str0?F                      ;Seach status
    UA=Str0?P                      ;Ptr past section header

;Retrieve and save the contents of each section for subsequent use:
;Get colors for each warp thread into Buf1:
    IF.US=0                        ;@@Warp Colors
        IF.QF=<0                       ;Found section?
            Msg="Analyzing DTX file"+n+"Warp Colors"
            QJ=1                           ;Thread number
            UI=0                           ;Buffer index
            WHILE.QJ=!0                    ;Do all entries in list
                QX=Str0?V                      ;Get color number
                QJ=Str0?T                      ;Char after end
                IF.QJ="@"                      ;Start of next section?
                    LoopBreak=2                    ;Done here if so
                ENDIF.
                UI=UI+1                        ;Next buffer index
                Buf1[UI]=QX                    ;Save color # at thread #
            WEND.                          ;Do all entries in list
            Buf1[0]=UI                     ;0th index =  warp thread count
        ENDIF.
    ENDIF.

;Get colors for each weft thread into Buf2:
    IF.US=1                        ;@@Weft Colors
        IF.QF=<0                       ;Found section?
            Msg="Analyzing DTX file"+n+"Weft Colors"
            QJ=1                           ;Pick number
            UI=0                           ;Buffer index
            WHILE.QJ=!0                    ;Do all entries in list
                QX=Str0?V                      ;Get color number
                QJ=Str0?T                      ;Char after end
                IF.QJ="@"                      ;Start of next section?
                    LoopBreak=2                    ;Done here if so
                ENDIF.
                UI=UI+1                        ;Next buffer index
                Buf2[UI]=QX                    ;Save color # at pick #
            WEND.                          ;Do all entries in list
            Buf2[0]=UI                     ;0th index =  weft thread count
        ENDIF.
    ENDIF.

;Get bitmap of shaft number for each warp thread into Buf3:
    IF.US=2                        ;@@Threading
        IF.QF=<0                       ;Found section?
            Msg="Analyzing DTX file"+n+"Threading"
            QJ=1                           ;Thread number
            UI=0                           ;Buffer index
            WHILE.QJ=!0                    ;Do all entries in list
                QX=Str0?V                      ;Get shaft number
                QJ=Str0?T                      ;Char after end
                IF.QJ="@"                      ;Start of next section?
                    LoopBreak=2                    ;Done here if so
                ENDIF.
                UI=UI+1                        ;Next buffer index
                Buf3[UI]=QX                    ;Save shaft # at thread #
            WEND.                          ;Do all entries in list
            Buf3[0]=UI                     ;0th index =  thread count
        ENDIF.
    ENDIF.

;Get shaft numbers that will be activated by each treadle into Buf4:
    IF.US=3                        ;@@Tieup
        IF.QF=<0                       ;Found section?
            Msg="Analyzing DTX file"+n+"Tieup"
            QJ=1                           ;Treadle number
            UI=0                           ;Buffer index
            WHILE.QJ=!0                    ;Do all treadles in list
                QX=Str0?B                      ;Tieup bitmap for treadle
                QJ=Str0?T                      ;Terminator char
                IF.QJ="@"                      ;Start of next section?
                    LoopBreak=2                    ;Done here if so
                ENDIF.
                UI=UI+1                        ;Treadle number
                Buf4[UI]=QX                    ;Save tieup at treadle num
            WEND.                          ;Next treadle
            Buf4[0]=UI                     ;0th index = treadle count
        ENDIF.
    ENDIF.

;Get liftplan bitmap of active shafts for each weft pick into Buf5:
    IF.US=4                        ;@@Treadling
        IF.QF=<0                       ;Found section"
            Msg="Analyzing DTX file"+n+"Treadling"
            QJ=0                           ;Terminator (dummy default)
            UI=1                           ;Pick (shuttle throw) number
            WHILE.QJ=!"@"                  ;Do until start of next section
                QX=Str0?V                      ;Get treadle
                QJ=Str0?T                      ;Get terminator
                IF.QJ="@"                      ;Start of next section
                    LoopBreak=2                    ;Done here if so
                ENDIF.
                Buf5[UI]=Buf4[QX]              ;Save liftplan for pick num
                UI=UI+1                        ;Next pick number
            WEND.                          ;Do all picks
            Buf5[0]=UI-1                   ;0th index = pick count
        ENDIF.
    ENDIF.

;Instead of separate @@Tieup and @@Treadling, get direct bitmap of active
; shafts for each weft pick into Buf5:
    IF.US=5                        ;@@Liftplan
        IF.QF=<0                       ;Section found?
            Msg="Analyzing DTX file"+n+"Liftplan"
            QJ=1                           ;Dummy terminator
            UI=0                           ;Pick number after inc
            WHILE.QJ=!"@"                  ;Do until start of next section
                QX=Str0?B                      ;Get liftplan for pick
                QJ=Str0?T                      ;Terminator char
                IF.QJ="@"                      ;Start of next section?
                    LoopBreak=2                    ;Done here if so
                ENDIF.
                UI=UI+1                        ;Pick number
                Buf5[UI]=QX                    ;Save liftplan at pick num
            WEND.                          ;Do all picks
            Buf5[0]=UI                     ;0th index = pick count
        ENDIF.
    ENDIF.
    US=US+1                        ;Next section
WEND.

QX=Buf1?U                      ;Sum of all warp colors
QY=Buf2?U                      ;Sum of all weft colors
IF.QX=0                        ;If both empty,
AND.QY=0
    Buf1="<=(1)"                   ;Fill warp colors with 1s
ENDIF.

;Create the drawdown 7 pixels at a time for each output point:
Msg="Drawdown Array: " +n +"0% Done"
UI=0                           ;Output array pixel index
Q3=Buf3[0]                     ;Warp count
Q5=Buf5[0]                     ;Weft count
QN=int(Q3 * Q5 / 1024)         ;We need 7x1024 pixels
IF.QN=>7                       ;Enough to skip pixels between 7-bits?
    QN=QN-7                        ;Skip this many if so
ELSE.
    QN=0                           ;Else don't skip, just continue
ENDIF.
QC=1                           ;Initial warp index
QR=1                           ;Initial weft index
QJ=102                         ;About 10% of 1024 points
QQ=10                          ;Percent done (at first use)
WHILE.UI=<1024                 ;Do all 1024 output points
    IF.UI=QJ                       ;At next 10% done point?
        Msg="Drawdown Array: " +n +QQ(0.0) + "% Done"
        QQ=QQ+10                       ;Next percent done value
        QJ=QJ+102                      ;Point number at next 10%
    ENDIF.
    Q7=0                           ;7-bit output value
    Q8=0                           ;Color component
    U7=0                           ;Count 7 bits per output
    WHILE.U7=<8
        QX=Buf3[QC] & Buf5[QR]         ;Drawdown pixel
        IF.QX=0                        ;Weft thread showing?
            QZ=Buf2[QR]                    ;Use weft color for this pick
        ELSE.                          ;Else warp showing
            QZ=Buf1[QC]                    ;Use warp color for this thread
            QX=1
        ENDIF.
        Q7=Q7<<1 + QX                  ;Build 7- bit binary
        Q8=Q8 + (QZ & 3)               ;Sum uses low 2 color bits
        QC=QC+1                        ;Next warp index
        IF.QC=>Q3                      ;Past end of warp?
            QC=QC % Q3                     ;Wrap to new line
            QR=QR+1                        ;Next weft line
            IF.QR=>Q5                      ;Line past end of weft?
                QR=1                           ;Back to start if so
            ENDIF.
        ENDIF.
        U7=U7+1
    WEND.
    Buf6[UI]=Q7 + Q8               ;Output point = bitmap + color sum
    QC=QC+QN
    IF.QC=>Q3                      ;Past end of warp?
        QC=QC % Q3                     ;Wrap to new line
        QR=QR+1                        ;New line
        IF.QR=>Q5                      ;Line past end of weft?
            QR=1                           ;Back to start if so
        ENDIF.
    ENDIF.
    UI=UI+1                        ;Next output point
WEND.

Buf2="<=B6"                    ;Copy Buf6 to Buf2
Str0#B=-1                      ;Unmap Str0 from Buf0
UZ=Buf2?p                      ;Positive peak

Str0=FileName?N                ;Save file name for music title

_MusicData.DQM MIDI Script Listing:

After the source data has been acquired and processed by _Music_Proc, this MIDI setup is loaded into the Pitch-to-MIDI dialog and toggled on to start playing.

The script contains 9 separate sections that all run concurrently in a multi-tasking environment: Tonal Voices 1-8, plus Percussion. All tasks are run once each millisecond. The actual order of execution begins with Percussion, then Voice 1, then Voices 2-8 in sequence. This order means that certain initial setup operations take place in Percussion and Voice 1 that are not needed in Voices 2-8, so their scripts are shorter.


Voice 1:

[F4<=0                      ;Field4 empty?
    UX=Bm0.1023                 ;Scale index from Buf0
    [UX>2047 S9=UX              ;Set all 9 voice Scales if OK
    |    S9=Bm1.<UX+400>            ;Else set scales from data
    ]
|                           ;Else get Scale from Field4
    UX=F4
    [UX>4095 UX=4095]           ;Limit if out of range
    S9=UX                       ;Set all voices
]
F4=S1                       ;Scale num to Field4
oRS=S1                      ;Show Scale name
ors=S1                      ;Show Scale pattern
UI=Bm0.990                  ;Max Instrument from GlossyInst

[F3<=0                      ;Field 3 empty?
    TB=Bm0.1022                 ;Tempo BPM from file data
|                           ;Else get Tempo from Field3
    UX=F3
    [UX>600 UX=600]             ;Limit to 100-600 BPM
    [UX<100 UX=100]
    TB=UX                       ;Set Tempo
]

[F2<=0                      ;Field2 empty?
    UD=TB                       ;Performance duration in beats if so
| UD=F2*TB/60]              ;Else use Field2 as Duration
UF=Bm0.959                  ;Fade-out duration, secs
UF=UF/60*TB                 ;Fade-out duration, beats

UL=Bm0.980                  ;Min Voice Note Pattern length
UU=Bm0.981                  ;Max Voice length
UH=Bm0.982                  ;Neg Hold Beats limit (bias)
Uh=Bm0.983                  ;Pos Hold limit
UV=Bm0.984                  ;Max Volume
Uv=Bm0.985                  ;Min volume
Ug=Bm0.988                  ;Max Note or Percussion Lag
Bs7.1981=1                  ;State flags for Voices 1-8
Bs7.1982=1
Bs7.1983=1
Bs7.1984=1
Bs7.1985=1
Bs7.1986=1
Bs7.1987=1
Bs7.1988=1

U1=Bm0.4                    ;Voice 1 intro delay
U2=Bm0.104
U3=Bm0.204
U4=Bm0.304
U5=Bm0.404
U6=Bm0.504
U7=Bm0.604
U8=Bm0.704                  ;Voice 8 intro delay

QV=Bm0.992                  ;Lead voice intro delay + cycle beats
[Qp&32=32 UD=QV QQ=UD*60/TB F2t=QQ]    ;New play duration, show in Field2

{!                     ;Infinite loop
;Wait if elapsed beats mB less than delay UI, else wait if final fade;
;else toggle voice X1 on if enabled and state is active:
[mB<U1 W=1 | [Qf>0 W=1 | X1=Bs7.<7>&Bs7.1981
[Q1=0                       ;Initial pass?
    I1=Bm0.0                    ;Instrument number
    H1=Bm0.1                    ;Hold Beats
    L1=Bm0.2                    ;Volume (Level)
    U1=Bm0.3                    ;Pattern length
    g1=Bm0.5                    ;Note lag
    BC1V.0=m0(20,39)            ;Copy Velocity 0-19
    BC1V.20=m0(20,39)           ;Duplicate copy to 20-39

    BC11.0=s7(700,739)          ;Buf1 (A) notes 0-39
    BS11.0=U1-1                 ;Set pointers

    BC12.0=s7(740,779)          ;Buf2 (B) notes 0-39
    BS12.0=U1-1                 ;Set pointers

    BC13.0=s7(780,819)          ;Buf3 (C) notes 0-39
    BS13.0=U1-1                 ;Set pointers

    BC14.0=s7(820,859)          ;Buf4 (D) notes 0-39
    BS14.0=U1-1                 ;Set pointers

;If chords enabled, copy to Chords buffer:
    [Bs7.127!="0"  BC1C.0=m0(80,99)
        BC1C.20=m0(80,99)]      ;Second copy
    A1=Bs7.147                  ;Arpeggio enable
    UB=Bs7.157                  ;Get direction and alternate
    AD1=UB&1                    ;Set arpeggio direction
    AA1=UB>>1&1                 ;Set arpeggio alternate
    Q1=1
|                          ;Not init, get random updates if enabled
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.27="1"  H1=?(UH,Uh)]      ;Random Hold
            [Bs7.57="1"  U1=?(UL,UU)]      ;Random pattern length
        ]
        [Bs7.17="1"  I1=Bm1.?(0,UI)]   ;Random instrument from Buf1 list
        [Bs7.37="1"  L1=?(Uv,UV)]      ;Random volume/level
        [Bs7.47="1"  g1=?r(0,Ug)]      ;Random lag
        [Bs7.67="1"  UB=?(0,19)        ;Bias range
            [Bs7.87="1"   BS11.UB=UB+U1-1)]    ;Update A Buf1
            [Bs7.97="1"   BS12.UB=UB+U1-1)]    ;Update B Buf3
            [Bs7.267="1"  BS13.UB=UB+U1-1)]    ;Update C Buf3
            [Bs7.277="1"  BS14.UB=UB+U1-1)]    ;Update D Buf4
        ]                              ;End bias update IF
        [Bs7.77="1"  Bf1V.0=(?(0,15),40)]   ;Update velocity
    ]                          ;End Qp&8 IF
]                          ;End initial / update IF
V1.0="8"                   ;Force initial velocity to max
U1=U1*H1                   ;Total length = pattern * hold
{Bm0.6                      ;Do Qv sequence repeats (AABABA, etc)
    Bs0.110=0                  ;Sequence item number
    {Qq                        ;Do all Qq items in sequence
        UC=Bs0.110                 ;Get sequence item num
        i1=1<Bs7.240+UC>           ;Select buf 1-4 as input
        Bs0.110=UC+1               ;Next sequence item
        W=U1                       ;Wait for one sequence
    }                          ;End of sequence loop
}                          ;End of sequence repeat loop
Bs7.1981=X1                ;Get current voice button state
s1=1                       ;Set sustain on
X1=0                       ;Set state off
W=Bm0.7                    ;Wait for sustain
s1=0                       ;Sustain off
[Qf=1 W=1M | W=Bm0.8]      ;Wait for final fade, else rest before repeat
X1=1                       ;Voice back on
]                          ;End active voice IF
]                          ;End initial delay IF
}                          ;End infinite loop

Voice 2:

[mB<U2 W=1 |  [Qf>0 W=1 | X2=Bs7.<6>&Bs7.1982
[Q2=0                                ;Initial pass?
    I2=Bm0.100
    H2=Bm0.101
    L2=Bm0.102
    U2=Bm0.103
    g2=Bm0.105
    BC2V.0=m0(120,139)            ;Velocity 0-19
    BC2V.20=m0(120,139)        ;Copy to 20-39

    BC21.0=s7(860,899)        ;Buf1 (A) notes 0-39
    BS21.0=U2-1

    BC22.0=s7(900,939)        ;Buf2 (B) notes 0-39
    BS22.0=U2-1

    BC23.0=s7(940,979)        ;Buf3 (C) notes 0-39
    BS23.0=U2-1

    BC24.0=s7(980,1019)            ;Buf4 (D) notes 0-39
    BS24.0=U2-1

    [Bs7.126!="0"
        BC2C.20=m0(180,199)]
    A2=Bs7.146
    UB=Bs7.156
    AD2=UB&1
    AA2=UB>>1&1
    Q2=1
|                            ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.26="1" H2=?(UH,Uh)]
            [Bs7.56="1"  U2=?(UL,UU)]
        ]
        [Bs7.16="1" I2=Bm1.?(0,UI)]
        [Bs7.36="1"  L2=?(Uv,UV)]
        [Bs7.46="1"  g2=?r(0,Ug)]
        [Bs7.66="1"  UB=?(0,19)    ;Bias range
            [Bs7.86="1"   BS21.UB=UB+U2-1)]
            [Bs7.96="1"   BS22.UB=UB+U2-1)]
            [Bs7.266="1"  BS23.UB=UB+U2-1)]
            [Bs7.276="1"  BS24.UB=UB+U2-1)]
        ]
        [s7.76="1"  Bf2V.0=(?(0,15),40)]
    ]
]
V2.0="8"
U2=U2*H2
{Bm0.106
    Bs0.111=0
    {Qq
        UC=Bs0.111
        i2=2<Bs7.240+UC>
        Bs0.111=UC+1
        W=U2
    }
}
Bs7.1982=X2
s2=1
X2=0
W=Bm0.107
s2=0
[Qf=1 W=1M | W=Bm0.108]
X2=1
]
]

Voice 3:

[mB<U3 W=1 |  [Qf>0 W=1 | X3=Bs7.<5>&Bs7.1983
[Q3=0                                ;Initial pass?
    I3=Bm0.200
    H3=Bm0.201
    L3=Bm0.202
    U3=Bm0.203
    g3=Bm0.205
    BC3V.0=m0(220,239)            ;Velocity 0-19
    BC3V.20=m0(220,239)        ;Copy to 20-39

    BC31.0=s7(1020,1059)        ;Buf1 (A) notes 0-39
    BS31.0=U3-1

    BC32.0=s7(1060,1099)        ;Buf2 (B) notes 0-39
    BS32.0=U3-1

    BC33.0=s7(1100,1139)        ;Buf3 (C) notes 0-39
    BS33.0=U3-1

    BC34.0=s7(1140,1179)        ;Buf4 (D) notes 0-39
    BS34.0=U3-1

    [Bs7.125!="0"  BC3C.0=m0(280,299)
        BC3C.20=m0(280,299)]
    A3=Bs7.145
    UB=Bs7.155
    AD3=UB&1
    AA3=UB>>1&1
    Q3=1
|                            ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.25="1"  H3=?(UH,Uh)]
            [Bs7.55="1"  U3=?(UL,UU)]
        ]
        [Bs7.15="1"  I3=Bm1.?(0,UI)]
        [Bs7.35="1"  L3=?(Uv,UV)]
        [Bs7.45="1"  g3=?r(0,Ug)]
        [Bs7.65="1"  UB=?(0,19)    ;Bias range
            [Bs7.85="1"   BS31.UB=UB+U3-1)]
            [Bs7.95="1"   BS32.UB=UB+U3-1)]
            [Bs7.265="1"  BS33.UB=UB+U3-1)]
            [Bs7.275="1"  BS34.UB=UB+U3-1)]
        ]
        [Bs7.75="1"  Bf3V.0=(?(0,15),40)]
    ]
]
V3.0="8"
U3=U3*H3
{Bm0.206
    Bs0.112=0
    {Qq
        UC=Bs0.112
        i3=3<Bs7.240+UC>
        Bs0.112=UC+1
        W=U3
    }
}
Bs7.1983=X3
s3=1
X3=0
W=Bm0.207
s3=0
[Qf=1 W=1M | W=Bm0.208]
X3=1
]
]

Voice 4:

[mB<U4 W=1 |  [Qf>0 W=1 | X4=Bs7.<4>&Bs7.1984
[Q4=0                                ;Initial pass?
    I4=Bm0.300
    H4=Bm0.301
    L4=Bm0.302
    U4=Bm0.303
    g4=Bm0.305
    BC4V.0=m0(320,339)            ;Velocity  0-19
    BC4V.20=m0(320,339)        ;Copy to 20-39

    BC41.0=s7(1180,1219        ;Buf1 (A) notes 0-39
    BS41.0=U4-1

    BC42.0=s7(1220,1259)        ;Buf2 (B) notes 0-39
    BS42.0=U4-1

    BC43.0=s7(1260,1299)        ;Buf3 (C) notes 0-39
    BS43.0=U4-1

    BC44.0=s7(1300,1339)        ;Buf4 (D) notes 0-39
    BS44.0=U4-1

    [Bs7.124!="0"  BC4C.0=m0(380,399)
        BC4C.20=m0(380,399)]
    A4=Bs7.144
    UB=Bs7.154
    AD4=UB&1
    AA4=UB>>1&1
    Q4=1
|                            ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.24="1"  H4=?(UH,Uh)]
            [Bs7.54="1"  U4=?(UL,UU)]
        ]
        [Bs7.14="1"  I4=Bm1.?(0,UI)]
        [Bs7.34="1"  L4=?(Uv,UV)]
        [Bs7.44="1"  g4=?r(0,Ug)]
        [Bs7.64="1"  UB=?(0,19)    ;Bias range
            [Bs7.84="1"   BS41.UB=UB+U4-1)]
            [Bs7.94="1"   BS42.UB=UB+U4-1)]
            [Bs7.264="1"  BS43.UB=UB+U4-1)]
            [Bs7.274="1"  BS44.UB=UB+U4-1)]
        ]
        [Bs7.74="1"  Bf4V.0=(?(0,15),40)]
    ]
]
V4.0="8"
U4=U4*H4
{Bm0.306
    Bs0.113=0
    {Qq
        UC=Bs0.113
        i4=4<Bs7.240+UC>
        Bs0.113=UC+1
        W=U4
    }
}
Bs7.1984=X4
s4=1
X4=0
W=Bm0.307
s4=0
[Qf=1 W=1M | W=Bm0.308]
X4=1
]
]

Voice 5:

[mB<U5 W=1 |  [Qf>0 W=1 | X5=Bs7.<3>&Bs7.1985
[Q5=0                                ;Initial pass?
    I5=Bm0.400
    H5=Bm0.401
    L5=Bm0.402
    U5=Bm0.403
    g5=Bm0.405
    BC5V.0=m0(420,439)            ;Velocity 0-19
    BC5V.20=m0(420,439)        ;Copy to 20-39

    BC51.0=s7(1340,1379)        ;Buf1 (A) notes 0-39
    BS51.0=U5-1

    BC52.0=s7(1380,1419)        ;Buf2 (B) notes 0-39
    BS52.0=U5-1

    BC53.0=s7(1420,1459)        ;Buf3 (C) notes 0-39
    BS53.0=U5-1

    BC54.0=s7(1460,1499)        ;Buf4 (D) notes 0-19
    BS54.0=U5-1

    [Bs7.123!="0"  BC5C.0=m0(480,499)
        BC5C.20=m0(480,499)]
    A5=Bs7.143
    UB=Bs7.153
    AD5=UB&1
    AA5=UB>>1&1
    Q5=1
|                        ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.23="1"  H5=?(UH,Uh)]
            [Bs7.53="1"  U5=?(UL,UU)]
        ]
        [Bs7.13="1"  I5=Bm1.?(0,UI)]
        [Bs7.33="1"  L5=?(Uv,UV)]
        [Bs7.43="1"  g5=?r(0,Ug)]
        [Bs7.63="1"  UB=?(0,19)    ;Bias range
            [Bs7.83="1"   BS51.UB=UB+U5-1)]
            [Bs7.93="1"   BS52.UB=UB+U5-1)]
            [Bs7.263="1"  BS53.UB=UB+U5-1)]
            [Bs7.273="1"  BS54.UB=UB+U5-1)]
        ]
        [Bs7.73="1"  Bf5V.0=(?(0,15),40)]
    ]
]
V5.0="8"
U5=U5*H5
{Bm0.406
    Bs0.114=0
    {Qq
        UC=Bs0.114
        i5=5<Bs7.240+UC>
        Bs0.114=UC+1
        W=U5
    }
}
Bs7.1985=X5
s5=1
X5=0
W=Bm0.407
s5=0
[Qf=1 W=1M | W=Bm0.408]
X5=1
]
]

Voice 6:

[mB<U6 W=1 |  [Qf>0 W=1 | X6=Bs7.<2>&Bs7.1986
[Q6=0                                ;Initial pass?
    I6=Bm0.500
    H6=Bm0.501
    L6=Bm0.502
    U6=Bm0.503
    g6=Bm0.505
    BC6V.0=m0(520,539)            ;Velocity 0-19
    BC6V.20=m0(520,539)        ;Copy to 20-39

    BC61.0=s7(1500,1539)        ;Buf1 (A) notes 0-39
    BS61.0=U6-1

    BC62.0=s7(1540,1579)        ;Buf2 (B) notes 0-39
    BS62.0=U6-1

    BC63.0=s7(1580,1619)        ;Buf3 (C) notes 0-39
    BS63.0=U6-1

    BC64.0=s7(1620,1659)        ;Buf4 (D) notes 0-39
    BS64.0=U6-1

    [Bs7.122!="0"  BC6C.0=m0(580,599)
        BC6C.20=m0(580,599)]
    A6=Bs7.142
    UB=Bs7.152
    AD6=UB&1
    AA6=UB>>1&1
    Q6=1
|                            ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.22="1"  H6=?(UH,Uh)]
            [Bs7.52="1"  U6=?(UL,UU)]
        ]
        [Bs7.12="1"  I6=Bm1.?(0,UI)]
        [Bs7.32="1"  L6=?(Uv,UV)]
        [Bs7.42="1"  g6=?r(0,Ug)]
        [Bs7.62="1"  UB=?(0,19)
            [Bs7.82="1"   BS61.UB=UB+U6-1)]
            [Bs7.92="1"   BS62.UB=UB+U6-1)]
            [Bs7.262="1"  BS63.UB=UB+U6-1)]
            [Bs7.272="1"  BS64.UB=UB+U6-1)]
        ]
        [Bs7.72="1"  Bf6V.0=(?(0,15),40)]
    ]
]
V6.0="8"
U6=U6*H6
{Bm0.506
    Bs0.115=0
    {Qq
        UC=Bs0.115
        i6=6<Bs7.240+UC>
        Bs0.115=UC+1
        W=U6
    }
}
Bs7.1986=X6
s6=1
X6=0
W=Bm0.507
s6=0
[Qf=1 W=1M | W=Bm0.508]
X6=1
]
]

Voice 7:

[mB<U7 W=1 |  [Qf>0 W=1 | X7=Bs7.<1>&Bs7.1987
[Q7=0                                ;Initial pass?
    I7=Bm0.600
    H7=Bm0.601
    L7=Bm0.602
    U7=Bm0.603
    g7=Bm0.605
    BC7V.0=m0(620,639)            ;Velocity to 0-19
    BC7V.20=m0(620,639)        ;Copy to 20-39

    BC71.0=s7(1660,1699)        ;Buf1 (A) notes 0-39
    BS71.0=U7-1

    BC72.0=s7(1700,1739)        ;Buf2 (B) notes 0-39
    BS72.0=U7-1

    BC73.0=s7(1740,1779)        ;Buf3 (C) notes 0-39
    BS73.0=U7-1

    BC74.0=s7(1780,1819)        ;Buf4 (D) notes 0-39
    BS74.0=U7-1

    [Bs7.121!="0"  BC7C.0=m0(680,699)
        BC7C.20=m0(680,699)]
    A7=Bs7.141
    UB=Bs7.151
    AD7=UB&1
    AA7=UB>>1&1
    Q7=1
|                        ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.21="1"  H7=?(UH,Uh)]
            [Bs7.51="1"  U7=?(UL,UU)]
        ]
        [Bs7.11="1"  I7=Bm1.?(0,UI)]
        [Bs7.31="1"  L7=?(Uv,UV)]
        [Bs7.41="1"  g7=?r(0,Ug)]
        [Bs7.61="1"  UB=?(0,19)
            [Bs7.81="1"   BS71.UB=UB+U7-1)]
            [Bs7.91="1"   BS72.UB=UB+U7-1)]
            [Bs7.261="1"  BS73.UB=UB+U7-1)]
            [Bs7.271="1"  BS74.UB=UB+U7-1)]
        ]
        [Bs7.71="1"  Bf7V.0=(?(0,15),40)]
    ]
]
V7.0="8"
U7=U7*H7
{Bm0.606
    Bs0.116=0
    {Qq
        UC=Bs0.116
        i7=7<Bs7.240+UC>
        Bs0.116=UC+1
        W=U7
    }
}
Bs7.1987=X7
s7=1
X7=0
W=Bm0.607
s7=0
[Qf=1 W=1M | W=Bm0.608]
X7=1
]
]

Voice 8:

[mB<U8 W=1 |  [Qf>0 W=1 | X8=Bs7.<0>&Bs7.1988
[Q8=0                                ;Initial pass?
    I8=Bm0.700
    H8=Bm0.701
    L8=Bm0.702
    U8=Bm0.703
    g8=Bm0.705
    BC8V.0=m0(720,739)            ;Velocity 0-19
    BC8V.20=m0(720,739)        ;Copy to 20-39

    BC81.0=s7(1820,1859)        ;Buf1 (A) notes 0-39
    BS81.0=U8-1

    BC82.0=s7(1860,1899)        ;Buf2 (B) notes 0-39
    BS82.0=U8-1

    BC83.0=s7(1900,1939)        ;Buf3 (C) notes 0-39
    BS83.0=U8-1

    BC84.0=s7(1940,1979)        ;Buf4 (C) notes 0-39
    BS84.0=U8-1

    [Bs7.120!="0"  BC8C.0=m0(780,799)
        BC8C.20=m0(780,799)]
    A8=Bs7.140
    UB=Bs7.150
    AD8=UB&1
    AA8=UB>>1&1
    Q8=1
|                        ;Not init, get random updates
    [Qp&8!=8                    ;No updates if Qp bit 3 set
        [Qp&16!=16                    ;No Hold or Pattern updates if bit 4
            [Bs7.20="1"  H8=?(UH,Uh)]
            [Bs7.50="1"  U8=?(UL,UU)]
        ]
        [Bs7.10="1"  I8=Bm1.?(0,UI)]
        [Bs7.30="1"  L8=?(Uv,UV)]
        [Bs7.40="1"  g8=?r(0,Ug)]
        [Bs7.60="1"  UB=?(0,19)
            [Bs7.80="1"   BS81.UB=UB+U8-1)]
            [Bs7.90="1"   BS82.UB=UB+U8-1)]
            [Bs7.260="1"  BS83.UB=UB+U8-1)]
            [Bs7.270="1"  BS84.UB=UB+U8-1)]
        ]
        [Bs7.70="1"  Bf8V.0=(?(0,15),40)]
    ]
]
V8.0="8"
U8=U8*H8
{Bm0.706
    Bs0.117=0
    {Qq
        UC=Bs0.117
        i8=8<Bs7.240+UC>
        Bs0.117=UC+1
        W=U8
    }
}
Bs7.1988=X8
s8=1
X8=0
W=Bm0.707
s8=0
[Qf=1 W=1M | W=Bm0.708]
X8=1
]
]

Percussion:

UX=Bm0.1020                ;Get signature
UX!=h474C42D               ;No match?
    oM="Run Music_from_Anything macro."
    X0=20                      ;Shut down, keep Msg
]

Bv=s7.160                  ;Load velocity map
Bc=s7.200                  ;Load chord map
FL1="Seed"                 ;Set Label1-Label6
FL2="Time (1:00)"
FL3="BPM (100-600)"
FL4="Scale (<4096)"
FL5="Perc P% (80)"
FL6=""

?s=Bm0.1021                ;Random seed
[F1!=0 ?s=F1]              ;Use Field1 instead if set

?x=0                       ;Set pseudo-random mode
oMx="<<Source: " +Str0     ;Show source string in Msg title
oM="<F(34)"                ;Msg font
oM="<v(10)"                ;Msg vertical white space
oM="<B(256,256,128)"       ;Msg backround color
oMT=s                      ;Show Song Title in Msg
UX=F5                      ;Get Percussion probability %
XP=?(UX-99,UX)             ;Biased random percussion state
QP=XP                      ;Save Percussion button state
Qm=Bm0.809                 ;Min perc volume
QM=Bm0.839                 ;Max perc volume
Qe=Bm0.869                 ;Perc volume update enable
LP=?(Qm,QM)                ;Set perc volume
XP=0                       ;Percussion off for now
UJ=Bm0.991                 ;Max GlossyPerc index re:200

QA=Bm0.803                 ;Perc A intro delay
QB=Bm0.833
QC=Bm0.863
QD=Bm0.893
QE=Bm0.923
QF=Bm0.953                 ;Intro delay for Perc F
Qp=Bm0.929                 ;Sync/update options
Up=Bm0.986                 ;Min perc pattern length
UP=Bm0.987                 ;Max perc pattern length
Qq=Bm0.989                 ;Number of AABABA chars
QH=Bm0.995                 ;Min (neg bias) perc hold beats
Qh=Bm0.996                 ;Max perc hold beats

[Qp&128=128 Qs=Qq | Qs=0]  ;Set perc to use AABABA sequence count

Bs7.1990=0                 ;Perc A first run flag
Bs7.1991=0
Bs7.1992=0
Bs7.1993=0
Bs7.1994=0
Bs7.1995=0                 ;Perc F

Bs7.1996=0                 ;Perc A Rest flag
Bs7.1997=0
Bs7.1998=0
Bs7.1999=0
Bs7.2000=0
Bs7.2001=0                 ;Perc F Rest flag

{!                        ;Infinite loop
W=1                        ;Wait one beat
UX=S1                      ;Get Scale for tonal Voice 1
S9=UX                      ;Set all Scales to match
oRS=S1                     ;Show scale on right
ors=S1                     ;Show scale pattern below it
[QX=0 XP=QP QX=1]          ;Percussion back on after 1st beat
QA=QA-1                    ;Perc A countdown
[QA<=0                     ;If 0 or less...
    [Bs7.1990=0                ;First pass?  Use source data:
        Bs7.1990=1                 ;Don't repeat this
        IA=Bm0.800                 ;Perc A instrument
        Ua=Bm0.801                 ;Perc A pattern length
        gA=Bm0.802                 ;Perc A hit lag
        HA=Bm0.804                 ;Perc A hold beats
        BCAV.0=m0(810,810+Ua-1)    ;Copy source velocity pattern
        [IA!=0 XA=Bs7.437]         ;If valid inst, Perc A on if enabled
    |                          ;Else random updates if enabled
        [Qp&8!=8                   ;No updates if Qp bit 3 set
            [Qp&16!=16                  ;No Hold or Pattern updates if 6
                [Bs7.377="1"  HA=?(QH,Qh)]             ;Perc Hold
                [Bs7.467="1"  Ua=?(Up,UP)]             ;Pattern length
            ]                                      ;Qp&16 IF done
            [Bs7.447="1"  IA=Bm1.?(200,200+UJ)]    ;Inst from list
            [Bs7.477="?" BfAV.0=(?(16,31),Ua)      ;Random velocities?
            | [Bs7.477="1"  QU=?(0,19-Ua)          ;Else random pattern shift
                BCAV.0=m0(810+QU,810+QU+Ua-1)]
            ]                                      ;Velocity IF done
            [Bs7.457="1"  gA=?r(0,Ug)]             ;Perc hit lag
            [Qe!=0 LP=?(Qm,QM)]                    ;Volume update
        ]
    ]                              ;Qp&8 IF done
    [Bm0.810<128    VA.0="8"]      ;Max vel on 1st hit unless Roll
    [Bs7.1996=0                    ;No Rest flag?
        QQ=Bm1.390                     ;Sequence counter
        [QQ=0                          ;Counted down to 0?
            Bm1.390=Qs                     ;Reset sequence counter
            QQ=Bm0.806                     ;Check Rest beats
            [QQ=0                          ;If none
                QA=Ua*Bm0.805                  ;Normal cycle
            |                              ;Else Rest
                XA=0                           ;Voice off
                QA=QQ                          ;Set Rest beats
                Bs7.1996=1                     ;Flag = Rest
            ]
        |                              ;Else sequence count
            QQ=QQ-1                        ;Count down
            Bm1.390=QQ                     ;Update counter
            QA=Ua*Bm0.805                  ;Normal cycle
        ]
    |                              ;Else flag = Rest just ended
        Bs7.1996=0                     ;Clear flag
        XA=Bs7.437                     ;Back on (if enabled)
        QA=Ua*Bm0.805                  ;Normal cycle
        Bm1.390=Qs                     ;Reset sequence counter
    ]
]

QB=QB-1
[QB<=0
    [Bs7.1991=0
        Bs7.1991=1
        IB=Bm0.830
        Ub=Bm0.831
        gB=Bm0.832
        HB=Bm0.834
        BCBV.0=m0(840,840+Ub-1)
        [IB!=0 XB=Bs7.436]
    |
        [Qp&8!=8                   ;No updates if Qp bit 3 set
            [Qp&16!=16                  ;No Hold or Pattern updates if 6
                [Bs7.376="1"  HB=?(QH,Qh)]
                [Bs7.466="1"  Ub=?(Up,UP)]
            ]
            [Bs7.446="1"  IB=Bm1.?(200,200+UJ)]
            [Bs7.476="?" BfBV.0=(?(16,31),Ub)
            | [Bs7.476="1"  QU=?(0,19-Ub)
                BCBV.0=m0(840+QU,840+QU+Ub-1)]
            ]
            [Bs7.456="1"  gB=?r(0,Ug)]
        ]
    ]
    [Bm0.840<128    VB.0="8"]
    [Bs7.1997=0
        QQ=Bm1.391
        [QQ=0
            Bm1.391=Qs
            QQ=Bm0.836
            [QQ=0
                QB=Ub*Bm0.835
            |
                XB=0
                QB=QQ
                Bs7.1997=1
            ]
        |
            QQ=QQ-1
            Bm1.391=QQ
            QB=Ub*Bm0.835
        ]
    |
        Bs7.1997=0
        XB=Bs7.436
        QB=Ub*Bm0.835
        Bm1.391=Qs
    ]
]

QC=QC-1
[QC<=0
    [Bs7.1992=0
        Bs7.1992=1
        IC=Bm0.860
        Uc=Bm0.861
        gC=Bm0.862
        HC=Bm0.864
        BCCV.0=m0(870,870+Uc-1)
        [IC!=0 XC=Bs7.435]
    |
        [Qp&8!=8                   ;No updates if Qp bit 3 set
            [Qp&16!=16                  ;No Hold or Pattern updates if 6
                [Bs7.375="1"  HC=?(QH,Qh)]
                [Bs7.465="1"  Uc=?(Up,UP)]
            ]
            [Bs7.445="1"  IC=Bm1.?(200,200+UJ)]
            [Bs7.475="?" BfCV.0=(?(16,31),Uc)
            | [Bs7.475="1"  QU=?(0,19-Uc)
                BCCV.0=m0(870+QU,870+QU+Uc-1)]
            ]
           [Bs7.455="1"  gC=?r(0,Ug)]
        ]
    ]
    [Bm0.870<128    VC.0="8"]
    [Bs7.1998=0
        QQ=Bm1.392
        [QQ=0
            Bm1.392=Qs
            QQ=Bm0.866
            [QQ=0
                QC=Uc*Bm0.865
            |
                XC=0
                QC=QQ
                Bs7.1998=1
            ]
        |
            QQ=QQ-1
            Bm1.392=QQ
            QC=Uc*Bm0.865
        ]
    |
        Bs7.1998=0
        XC=Bs7.435
        QC=Uc*Bm0.865
        Bm1.392=Qs
    ]
]

QD=QD-1
[QD<=0
    [Bs7.1993=0
        Bs7.1993=1
        ID=Bm0.890
        Ud=Bm0.891
        gD=Bm0.892
        HD=Bm0.894
        BCDV.0=m0(900,900+Ud-1)
        [ID!=0 XD=Bs7.434]
    |
        [Qp&8!=8                   ;No updates if Qp bit 3 set
            [Qp&16!=16                  ;No Hold or Pattern updates if 6
                [Bs7.374="1"  HD=?(QH,Qh)]
                [Bs7.464="1"  Ud=?(Up,UP)]
            ]
            [Bs7.444="1"  ID=Bm1.?(200,200+UJ)]
            [Bs7.474="?" BfDV.0=(?(16,31),Ud)
            | [Bs7.474="1"  QU=?(0,19-Ud)
                BCDV.0=m0(900+QU,900+QU+Ud-1)]
            ]
            [Bs7.454="1"  gD=?r(0,Ug)]
        ]
    ]
    [Bm0.900<128    VD.0="8"]
    [Bs7.1999=0
        QQ=Bm1.393
        [QQ=0
            Bm1.393=Qs
            QQ=Bm0.896
            [QQ=0
                QD=Ud*Bm0.895
            |
                XD=0
                QD=QQ
                Bs7.1999=1
            ]
        |
            QQ=QQ-1
            Bm1.393=QQ
            QD=Ud*Bm0.895
        ]
    |
        Bs7.1999=0
        XD=Bs7.434
        QD=Ud*Bm0.895
        Bm1.393=Qs
    ]
]

QE=QE-1
[QE<=0
    [Bs7.1994=0
        Bs7.1994=1
        IE=Bm0.920
        Ue=Bm0.921
        gE=Bm0.922
        HE=Bm0.924
        BCEV.0=m0(930,930+Ue-1)
        [IE!=0 XE=Bs7.433]
    |
        [Qp&8!=8                   ;No updates if Qp bit 3 set
            [Qp&16!=16                  ;No Hold or Pattern updates if 6
                [Bs7.373="1"  HE=?(QH,Qh)]
                [Bs7.463="1"  Ue=?(Up,UP)]
            ]
            [Bs7.443="1"  IE=Bm1.?(200,200+UJ)]
            [Bs7.473="?" BfEV.0=(?(16,31),Ue)
            | [Bs7.473="1"  QU=?(0,19-Ue)
                BCEV.0=m0(930+QU,930+QU+Ue-1)]
            ]
            [Bs7.453="1"  gE=?r(0,Ug)]
        ]
    ]
    [Bm0.930<128    VE.0="8"]
    [Bs7.2000=0
        QQ=Bm1.394
        [QQ=0
            Bm1.394=Qs
            QQ=Bm0.926
            [QQ=0
                QE=Ue*Bm0.925
            |
                XE=0
                QE=QQ
                Bs7.2000=1
            ]
        |
            QQ=QQ-1
            Bm1.394=QQ
            QE=Ue*Bm0.925
        ]
    |
        Bs7.2000=0
        XE=Bs7.433
        QE=Ue*Bm0.925
        Bm1.394=Qs
    ]
]

QF=QF-1
[QF<=0
    [Bs7.1995=0
        Bs7.1995=1
        IF=Bm0.950
        Uf=Bm0.951
        gF=Bm0.952
        HF=Bm0.954
        BCFV.0=m0(960,960+Uf-1)
        [IF!=0 XF=Bs7.432]
    |
        [Qp&8!=8                   ;No updates if Qp bit 3 set
            [Qp&16!=16                  ;No Hold or Pattern updates if 6
                [Bs7.372="1"  HF=?(QH,Qh)]
                [Bs7.462="1"  Uf=?(Up,UP)]
            ]
            [Bs7.442="1"  IF=Bm1.?(200,200+UJ)]
            [Bs7.472="?" BfFV.0=(?(16,31),Uf)
            | [Bs7.472="1"  QU=?(0,19-Uf)
                BCFV.0=m0(960+QU,960+QU+Uf-1)]
            ]
            [Bs7.452="1"  gF=?r(0,Ug)]
        ]
    ]
    [Bm0.960<128    VF.0="8"]
    [Bs7.2001=0
        QQ=Bm1.395
        [QQ=0
            Bm1.395=Qs
            QQ=Bm0.956
            [QQ=0
                QF=Uf*Bm0.955
            |
                XF=0
                QF=QQ
                Bs7.2001=1
            ]
        |
            QQ=QQ-1
            Bm1.395=QQ
            QF=Uf*Bm0.955
        ]
    |
        Bs7.2001=0
        XF=Bs7.432
        QF=Uf*Bm0.955
        Bm1.395=Qs
    ]
]

[F2<0 UD=3G]           ;If Time Field2 neg, set max duration
[mB>UD                 ;Beat count past duration?
    [Qf=0                  ;Fade not started yet?
        Qf=1                   ;Start now
        Bs0.101=L1             ;Current volumes, Voices 1-8
        Bs0.102=L2
        Bs0.103=L3
        Bs0.104=L4
        Bs0.105=L5
        Bs0.106=L6
        Bs0.107=L7
        Bs0.108=L8
        Qk=LP                  ;Current percussion volume
        Qe=UF                  ;Set fade duration counter
    |                      ;Else fade in progress
        L1=Bs0.<101>*Qe/UF     ;Scale down Voices 1-8 volume
        L2=Bs0.<102>*Qe/UF
        L3=Bs0.<103>*Qe/UF
        L4=Bs0.<104>*Qe/UF
        L5=Bs0.<105>*Qe/UF
        L6=Bs0.<106>*Qe/UF
        L7=Bs0.<107>*Qe/UF
        L8=Bs0.<108>*Qe/UF
        LP=Qk*Qe/UF            ;Scale down Perc volume
        Qe=Qe-1                ;Reduce fade counter
        [Qe=0 W=10 X0=0]       ;If now 0, wait 10 beats and exit
    ]
]                      ;Done fade IF
}                     ;Infinite loop

See also Macro Examples and Mini-Apps, GlossyTracks.DQM MIDI Setup, "Glossy" MIDI Setup Family, Example MIDI Setup Files, MIDI Setup Files, Musical Frontiers, DaqMusiq, KaleidoSynth, Pitch-to-MIDI dialog, Pitch Track Toolbox - Overview, Spectrogram / Pitch Track Controls, Spectrogram / Pitch Track (Sgram/PT)

GO:

Questions? Comments? Contact us!

We respond to ALL inquiries, typically within 24 hrs.
INTERSTELLAR RESEARCH:
Over 35 Years of Innovative Instrumentation
© Copyright 2007 - 2023 by Interstellar Research
All rights reserved