Jump to content
IGNORED

Intellivision Music Tracker Library


DZ-Jay

Recommended Posts

Merry Christmas & Happy New Year, everyone!

 

I've been working on a little project for the past several months and it is finally ready to be shared.  Introducing the new and improved Intellivision Music Tracker v1.5.

 

Just what is an "Intellivision Music Tracker"?

It's a software library that allows you to play specially crafted music files in your own Intellivision games.  These files are similar in nature to the classic tracker module (MOD) format employed in many old school platforms.

 

Originally written by Arnauld Chevallier and released to the public domain many years ago, this new version includes many changes, enhancements, bug fixes, and fully comprehensive documentation.

 

This entire project started as part of the demo Voyage: An Intv Journey.  The original idea was to take Arnauld's tracker and enhance it to support the additional sound processor in the ECS, and fix a few latent bugs.  I then took it upon myself to reverse-engineer the entire tracker and document in exhaustive detail the data format in order to add new features to it.  The result is not only a comprehensive user manual and technical guide, but an improved and highly-optimized version of the tracker.

 

Plus it plays some of the best kick-ass drums you've ever heard on an 8-bit sound chip. :)

 

Below is a list of the most important features of the Intellivision Music Tracker:

  • Pattern-based song sequencing, with support for an unlimited number of patterns.
  • Supports up to six independent sound channels, updated and playing simultaneously.
  • Full 6-note polyphony; 5-note polyphony, with one drum channel; or 4-note polyphony, with two drum channels.
  • 64-point user-defined software envelopes.
  • 4-step pitch effects (e.g., arpeggios).
  • Vibrato effects configurable with 3 levels of depth.
  • Simple programmable drum sound synthesizer.
  • Supports up to 85 individual instrument definitions with independent envelopes, and pitch and amplitude modulation effects.
  • Supports over 40 individual drum instrument definitions per song.
  • Global master volume control.
  • Dynamic active channel selection. (New!)

 

And if all of that wasn't enough, in a fantastic twist of fate, this new version includes support for IntyBASIC integration, making it dead-simple to incorporate the tracker in your own IntyBASIC games!

 

The standard distribution includes everything you need:

  • Tracker v1.5 User Manual & Technical Guide.
  • Tracker library and dependency modules.
  • Interface modules for both Assembly Language and IntyBASIC programs.
  • A global music definition file with useful predefined envelopes, effects, and drum sounds.
  • Eight sample songs, including a cool drums solo showcasing the new drum sounds.

 

Along with the standard distribution, I include below an "IntyBASIC SDK" project with an example program illustrating how to use the tracker from IntyBASIC.

 

Current Revision #4 (2021-03-07)

 

 

 

And for those who do not want to build or compile the project to check it out, below are some MP3s exported from the emulator playing the sample songs:

 

 

 

One of my goals in documenting the data format so thoroughly was to enable the creation of MOD-to-Inty or MIDI-to-Inty programs to convert standard music files to the Intellivision Music Tracker format.  I hope others will take inspiration of this project and contribute to this effort.  Let us enrich the musical library of the Intellivision and expand the pool of musicians working on it. :)

 

Many thanks to Arnauld Chevallier for the original software and for his many contributions to this community.

 

Now, go make some cool music!

 

       Cheers!

       -dZ.

 

 

Latest Version Release Notes:

  • NEW: Instrument envelopes now recycle indefinitely, by backtracking a number of sample points from the end. The backtracking offset is configurable with a global constant.
  • FIXED: Optimized the code for size and speed to compensate for additional code brought in by recent enhancements.
  • FIXED: Changed the behaviour of "NULL" events that include instrument changes, to reset the channel counter.  The old behaviour is believed to be a bug.
  • FIXED: Included the latest version of IBN-to-IMT conversion tool.

 

 

UPDATES:

2021-01-10: Updated attachments of library distribution to revision #1.

2021-01-22: Updated attachments of library distribution to revision #2.

2021-02-03: Updated attachments of library distribution to revision #3.

2021-03-07: Updated attachments of library distribution to revision #4.

 

Edited by DZ-Jay
Updated to revision #4
  • Like 9
  • Thanks 1
Link to comment
Share on other sites

Everything you need to know -- from the most simple tracking concepts, to the low-level details of the song data structures -- is described in excruciating detail in the user manual document, complete with charts, illustrations, formulas, and data tables.  And of course, if anybody needs help using the tracker in their programs, or encounters any problems, please let me know. :)

 

    -dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

In case anyone needs additional tools, here is a link to my PHP script "mod2inty":

 

Currently it takes the printout syntax from a tracker program like e.g. OctaMED on the Amiga, and converts a good deal of the data to the above tracker format. I also have an unpublished version with ECS support, simply extended to handle more channels.

 

It is not a complete solution! You will need to do quite a bit of manual work with the automated outcome, like assembling play lists, fixing instrument references etc. If you lack patience, don't try it.

 

Having said that, it helped me a lot while developing the Voyage demo to get musics into the tracker format instead of doing everything by hand.

  • Like 3
Link to comment
Share on other sites

For the curious programmer, here's an example program that illustrates how to use the tracker from IntyBASIC.  It has some boiler-plate created by the IntyBASIC SDK, so you can ignore the "title.bas" part and additional comments.  Notice that it is very easy to do and the "bas-interface.bas" does much of the work for you.

 

Do not be intimidated by the length -- it's got much more comments than code, to describe what it is doing.

 

Spoiler

' =========================================================================
' IntyBASIC SDK Project: Tracker Demo
' -------------------------------------------------------------------------
'     Programmer: James Pujals (DZ-Jay)
'     Created:    2020-12-26
'     Updated:    2020-12-28
'
'     Project automatically generated by INTYNEW.
' -------------------------------------------------------------------------
' History:
' 2020-12-28 - Tracker Demo project created.
' =========================================================================

	OPTION EXPLICIT

	INCLUDE "constants.bas"
	INCLUDE "title.bas"

	' Set up a recurring task to update
	' the tracker on every frame.
	ON FRAME GOSUB UpdateTracker

' =========================================================================
' Game Program
' =========================================================================

	' INITIALIZATION:
	'   Initialize the tracker as part of your
	'   game initialization routine.
	CALL TRKINIT

	' MAIN PROGRAM:
	'
	'   <... YOUR CODE GOES HERE ...>
	'

	' LOAD & PLAY SONG:
	'   At any point after initialization, you may
	'   load a song ...
	'
	'   The routine TRKLOADSONG() is a special
	'   IntyBASIC interface that receives a pointer
	'   to the song data structure via R0.
	'
	'   The song will start playing immediately.
	CALL TRKLOADSONG(VARPTR DRUMS(0))

InfiniteLoop:
	GOTO InfiniteLoop

	' UPDATE TRACKER STATE:
	'   The state of the tracker needs to be updated
	'   once per frame, ideally at the same place
	'   every time.  We use ON FRAME GOSUB in this
	'   example, but all you really need to do is
	'   invoke TRKPLAY at some point from your regular
	'   game engine loop.
	UpdateTracker: PROCEDURE
		CALL TRKPLAY
	END

' =========================================================================
' Game Data
' =========================================================================

	' Include the tracker IntyBASIC Interface library
	' anywhere in your program, along with any other
	' libraries you use.
	INCLUDE "lib/bas-interface.bas"

	' INCLUDE SONGS:
	'   Include any songs to play in your program.
	'
	'   Song are still defined using assembler data
	'   structures and macros, and so must be loaded
	'   as assembly language modules.

	'   Include the standard global music definitions
	ASM INCLUDE "music/global-music.asm"

	'   Include sample song.  Note that assigning a
	'   label before including the song allows us to
	'   address the structure using VARPTR when we
	'   load it.  Otherwise, we would have to dig up
	'   its actual address from the assembler's
	'   output LISTING file.
DRUMS:
	ASM INCLUDE "music/drums-demo.asm"

 

 

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

16 minutes ago, Arnauld said:

Thanks for sharing this. It's great to see an enhanced version and I'll definitely give it a try when I have a chance to.

It is my pleasure to be able to contribute to it.  I think the hardest part was your work on creating the module, I just added a bit of flair on top. :)

 

By far, my biggest contribution is documenting the functionality and the data format in great detail.  I hope that it opens it up to more programmers and musicians.

 

     -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

55 minutes ago, First Spear said:

What an awesome contribution! Not that the date has anything to do with things really, but is a nice way to close-out 2020. 

 

Reviewing the docs now, looking at what it takes to get something going with/for IntyBASIC.

 

Thank you!

 

Sure thing!  Let me know if you need help.  I know that you have quite a few songs in IntyBASIC already.  I'd love to see what you can do with this tracker. ??

 

   dZ.

  • Like 1
Link to comment
Share on other sites

3 hours ago, DZ-Jay said:

Sure thing!  Let me know if you need help.  I know that you have quite a few songs in IntyBASIC already.  I'd love to see what you can do with this tracker. ??

 

   dZ.

The demo songs are pretty complex (the drum demo in particular is fire). Did you "code" them, or did you have another tool to pre-process? 

Link to comment
Share on other sites

9 hours ago, First Spear said:

The demo songs are pretty complex (the drum demo in particular is fire). Did you "code" them, or did you have another tool to pre-process? 

The drums I coded by hand.  However, I did "design" them in Garage Band.  I made the drum track patterns in Garage Band, placing the instruments until it sounded good, then I just imitated it in code.  Garage Band has a neat "key pad" interface were you light up the points were you want a drum to sound, in a horizontal "scale" marked by the beat.  I take the buttons in that horizontal scale and mentally pivot them into rows in the tracker.

 

The songs themselves were tracked originally by @carlsson.  He uses a tracker tool to make the music, then exports it into some sort of notation that then he converts into the code.  He then has to alter it manually.   I then "remixed" them by re-arranging the patterns or altering the instruments.  And of course, adding cooler drums.

 

I can help you track some of your songs if you'd like.  I do it by hand (Christmas Carol's tracks were also composed in Logic then transcribed by hand).  Personally, I've worked with the tracker for so long, that I don't find its format too intimidating.

 

However, if you have your songs in some particular format, we could figure out how to transcribe them in the appropriate one.  Part of my goal was to document the data format in detail to enable this sort of work.  :)

 

For the drum track, it's pretty straight forward:  they are 16-beat patterns, grouped in blocks of 4-beats each for easy reading.  Then each beat is sub-divided into four rows, that's to allow for "triplets" quantization in the hi-hats.

 

It helps that I was a DJ for many years, so I used to program drums and synths using beat boxes and sequencers, so I'm used to thinking of songs and rhythms as patterns.  I just can't play worth crap.

 

    -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

For an example of how I made the drums, consider this Garage Band view:

image.thumb.png.1f13bcc9ea27f4d01f2d1565e887ca89.png

 

Notice that you have 16 blocks across for each instrument.  Let us consider the kick drum (at the very top).  The 16 blocks are packed into four groups of four blocks each.  When you play that track, you will hear the kick drum hit on every beat, which means that every four blocks (one group) represents one beat.

 

(To imagine the beat, notice that the snare hits on every other beat, so you get a nice "boom, kah, boom, kah, ..." rhythm.  The hi-hats fill in the empty spaces in between drum hits.)

 

So, we fashion our tracker pattern in the same way:  Each row will represent one block, so we need 16 rows in total to fill the pattern.  It'll look something like this:

NOTE("--- ---") ; 00  Beat #1
NOTE("--- ---") ; 01
NOTE("--- ---") ; 02
NOTE("--- ---") ; 03

NOTE("--- ---") ; 04  Beat #2
NOTE("--- ---") ; 05
NOTE("--- ---") ; 06
NOTE("--- ---") ; 07

NOTE("--- ---") ; 08  Beat #3
NOTE("--- ---") ; 09
NOTE("--- ---") ; 10
NOTE("--- ---") ; 11

NOTE("--- ---") ; 12  Beat #4
NOTE("--- ---") ; 13
NOTE("--- ---") ; 14
NOTE("--- ---") ; 15

 

Now, we saw in the Garage Band pattern that the kick hits every beat, that is, every fourth block starting with the very first one.  Assuming we already set up our song header and instruments, we'll consider that the kick is drum instrument #1 and that the snare is drum instrument #2.  We then put our kick drums on the beats using "DRM" events, with the NOTE() macro:

NOTE("DRM 1F0") ; 00  Beat #1
NOTE("--- ---") ; 01
NOTE("--- ---") ; 02
NOTE("--- ---") ; 03

NOTE("DRM 1F0") ; 04  Beat #2
NOTE("--- ---") ; 05
NOTE("--- ---") ; 06
NOTE("--- ---") ; 07

NOTE("DRM 1F0") ; 08  Beat #3
NOTE("--- ---") ; 09
NOTE("--- ---") ; 10
NOTE("--- ---") ; 11

NOTE("DRM 1F0") ; 12  Beat #4
NOTE("--- ---") ; 13
NOTE("--- ---") ; 14
NOTE("--- ---") ; 15

 

As per the manual definition of the NOTE() macro, the row string means the following:

  • DRM - this is a drum instrument, not a tone.
  • 1 - this is instrument #1 (from the drums list as stated above).
  • F - Play the instrument at maximum volume (15)
  • 0 - The event lasts one single row, so there is no delay to the next row.

(Actually, that last one is not strictly accurate, but we'll address that one step at a time.  It'll make sense in a second, trust me.)

 

The rest of the blocks are empty in the Garage Band pattern, which essentially means that the drum hit will continue playing until it naturally decays and then drop off until the next beat.  We do the same in our tracker notation by adding "NUL" events.  Because we want the drum instrument to decay naturally, following its original definition, we do not want to alter its volume ourselves.  Therefore, our "null" events essentially say "do not change anything in this row.  Oh, and by the way, keep the volume at maximum."

NOTE("DRM 1F0") ; 00  Beat #1
NOTE("NUL 0F0") ; 01
NOTE("NUL 0F0") ; 02
NOTE("NUL 0F0") ; 03

NOTE("DRM 1F0") ; 04  Beat #2
NOTE("NUL 0F0") ; 05
NOTE("NUL 0F0") ; 06
NOTE("NUL 0F0") ; 07

NOTE("DRM 1F0") ; 08  Beat #3
NOTE("NUL 0F0") ; 09
NOTE("NUL 0F0") ; 10
NOTE("NUL 0F0") ; 11

NOTE("DRM 1F0") ; 12  Beat #4
NOTE("NUL 0F0") ; 13
NOTE("NUL 0F0") ; 14
NOTE("NUL 0F0") ; 15

 

The string in those rows means the following:

  • NUL - This is a "null" event, continue the previous event as normal.
  • 0 - No change in instrument, let the previous one continue.
  • F - Maintain the volume at maximum.
  • 0 - This event lasts a single row, so no delay is needed.  (Again, the same caveat as before applies here ...)

 

And that's our kick drum pattern.  Notice that we "unrolled" it to match the Garage Band pattern, where each row represents one block in the 16-block pattern.  This is why we said that the "0 delay" for each row is inaccurate:  in reality, for our tracker, the kick drum event lasts four rows, right until the next one.  We just "unrolled" the four events into individual rows:  one for the actual event of the drum hit, and three "null" events that just extend it.  We did that purely out of convenience.

 

In reality, our pattern should look like this:

NOTE("DRM 1F3") ; 00  Beat #1

NOTE("DRM 1F3") ; 04  Beat #2

NOTE("DRM 1F3") ; 08  Beat #3

NOTE("DRM 1F3") ; 12  Beat #4

Notice that it is the same one, but compacted.  We are now saying that each drum event lasts four rows (the current event row, plus a delay of 3 rows).

 

And normally, that's how we would write our patterns:  our last step is always to "compact" the events.  In fact, that's how all the demo songs are tracked right now, except the drums.  It uses less ROM because we pack each event row into a single data word (four), while the previous "unrolled" way required one data word per row (16).

 

The power and convenience of unrolling the rows becomes apparent when you want to modify or play with the pattern.  Let's make our drums a bit more funky ...

image.thumb.png.8d1c4404e981ee2e2abd9db21e6a9ee9.png

 

Notice that we added two syncopated kick drum hits.  Those do not fit nicely in our 4-event pattern.  It forces us to figure out between which rows we need to add them, and adjust the length of the events around it.  That's too cumbersome, at least for me, and especially when we're just experimenting to see how it sounds.  We may want to move those kicks around as we continue.

 

But if we were using the unrolled pattern, it's a cinch!  Because every block is represented as a row, we just find the row we need to change, and add a drum event:

NOTE("DRM 1F0") ; 00  Beat #1
NOTE("NUL 0F0") ; 01
NOTE("NUL 0F0") ; 02
NOTE("DRM 1F0") ; 03  <-- New beat

NOTE("DRM 1F0") ; 04  Beat #2
NOTE("NUL 0F0") ; 05
NOTE("DRM 1F0") ; 06  <-- New beat
NOTE("NUL 0F0") ; 07

NOTE("DRM 1F0") ; 08  Beat #3
NOTE("NUL 0F0") ; 09
NOTE("NUL 0F0") ; 10
NOTE("NUL 0F0") ; 11

NOTE("DRM 1F0") ; 12  Beat #4
NOTE("NUL 0F0") ; 13
NOTE("NUL 0F0") ; 14
NOTE("NUL 0F0") ; 15

And we're done!

 

No need to fiddle with the length of events or their positions; we treat the pattern table in the same way we treat it in Garage Band, and maintain them in sync. :)

 

The only problem, as I mentioned before, is that the pattern still takes considerably more ROM space than the "normal" compacted one.

 

I came up with another solution for this:  a new macro family described in the manual as "Automatic Note Packing" or NPK.  With NPK, you define your patterns unrolled like we've done here, and let the macros handle the complexity of detecting event boundaries and compacting your event rows.

 

Translating our unrolled pattern to use NPK is simple, we just add some wrapper macros to enclose the pattern block, and use the NPK.Note() macro instead for our rows:

NPK.Begin(16)
    NPK.Note("DRM 1F0") ; 00  Beat #1
    NPK.Note("NUL 0F0") ; 01
    NPK.Note("NUL 0F0") ; 02
    NPK.Note("DRM 1F0") ; 03  <-- New beat

    NPK.Note("DRM 1F0") ; 04  Beat #2
    NPK.Note("NUL 0F0") ; 05
    NPK.Note("DRM 1F0") ; 06  <-- New beat
    NPK.Note("NUL 0F0") ; 07

    NPK.Note("DRM 1F0") ; 08  Beat #3
    NPK.Note("NUL 0F0") ; 09
    NPK.Note("NUL 0F0") ; 10
    NPK.Note("NUL 0F0") ; 11

    NPK.Note("DRM 1F0") ; 12  Beat #4
    NPK.Note("NUL 0F0") ; 13
    NPK.Note("NUL 0F0") ; 14
    NPK.Note("NUL 0F0") ; 15
 NPK.End

At the top, we tell the NPK sub-system that our pattern is 16 rows in total.  It will tell us if we under- or overrun it, which is convenient.

 

And what about the triplets?  Well, easy:  just change the grain of your rows.  In the above example, each row is equivalent to a block in Garage Band.  To add triplets we just make each row 1/3 of that.  That is, for each block in Garage Band we add three rows.

 

Here's an excerpt from the drums demo track to illustrate (instrument #3 is the closed hi-hat, #4 is the open hi-hat):

@@ph04: NPK.Begin(48)
    NPK.Note("DRM 3E0") ; #00 - 1  Beat #1
    NPK.Note("NUL 0E0") ; #01
    NPK.Note("NUL 0E0") ; #02
    NPK.Note("DRM 3C0") ; #03 - 2  <-- Triple here!
    NPK.Note("DRM 3B0") ; #04
    NPK.Note("DRM 3B0") ; #05
    NPK.Note("DRM 3E0") ; #06 - 3
    NPK.Note("NUL 0E0") ; #07
    NPK.Note("NUL 0E0") ; #08
    NPK.Note("DRM 3B0") ; #09 - 4
    NPK.Note("NUL 0B0") ; #10
    NPK.Note("NUL 0B0") ; #11

    NPK.Note("DRM 3E0") ; #12 - 1  Beat #2
    NPK.Note("NUL 0E0") ; #13
    NPK.Note("NUL 0E0") ; #14
    NPK.Note("DRM 3B0") ; #15 - 2
    NPK.Note("NUL 0B0") ; #16
    NPK.Note("NUL 0B0") ; #17
    NPK.Note("DRM 3E0") ; #18 - 3
    NPK.Note("NUL 0E0") ; #19
    NPK.Note("NUL 0E0") ; #20
    NPK.Note("DRM 3B0") ; #21 - 4
    NPK.Note("NUL 0B0") ; #22
    NPK.Note("NUL 0B0") ; #23

    NPK.Note("DRM 3E0") ; #24 - 1  Beat #3  <-- Triplet here!
    NPK.Note("DRM 3C0") ; #25
    NPK.Note("DRM 3C0") ; #26
    NPK.Note("DRM 3B0") ; #27 - 2
    NPK.Note("NUL 0B0") ; #28
    NPK.Note("NUL 0B0") ; #29
    NPK.Note("DRM 3E0") ; #30 - 3
    NPK.Note("DRM 3C0") ; #31
    NPK.Note("DRM 3C0") ; #32
    NPK.Note("DRM 3B0") ; #33 - 4
    NPK.Note("NUL 0B0") ; #34
    NPK.Note("NUL 0B0") ; #35

    NPK.Note("DRM 3E0") ; #36 - 1  Beat #4
    NPK.Note("NUL 0E0") ; #37
    NPK.Note("NUL 0E0") ; #38
    NPK.Note("DRM 3B0") ; #39 - 2
    NPK.Note("NUL 0B0") ; #40
    NPK.Note("NUL 0B0") ; #41
    NPK.Note("DRM 3E0") ; #42 - 3
    NPK.Note("NUL 0E0") ; #43
    NPK.Note("NUL 0E0") ; #44
    NPK.Note("DRM 3B0") ; #45 - 4
    NPK.Note("NUL 0B0") ; #46
    NPK.Note("NUL 0B0") ; #47
NPK.End

 

I add the comments to keep my place in the pattern.  It's similar to what the "ruler" does in Garage Band.

 

And that, in a (rather large brazilian) nut shell is how I made the drum tracks.  The rest is doing similar patterns for the other instruments or intercalating them in the same patterns.

 

I hope this helps.  Ask any questions. :)

 

     -dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

By the way, you don't need Garage Band to do drums.  There are plenty of similar open source software out there.  And on a pinch, you could "draw" the patterns in a grid on paper -- just like we used to do back in the 80s when megamixing records with tape splices. :)

 

    -dZ.

Link to comment
Share on other sites

12 hours ago, First Spear said:

The demo songs are pretty complex (the drum demo in particular is fire).

Also, I "remixed" the demo tracks to illustrate some cool things you can do with the Intellivision Music Tracker v1.5.  Because it is pattern-based and uses a sequencer to play the patterns, rather than, say, executing procedurally, it affords some really neat effects.

 

Some cool examples demonstrated in the sample tracks are:

(You can download the songs from the first post in the thread.)

 

  • butterfly-remix.mp3 - Layered fade out, starting at around 3:15.  It uses the same patterns as in the rest of the song, but I duplicated them and lowered the volume across the events in the pattern.  Different instruments attenuate at different rates.  This is all static pattern definition, not dynamically faded.

    Dynamic fading can be performed under program control by accessing the master fader variable (see the documentation for more information).  We did this in the "Voyage" demo at the end of each part:  the demo sequencer would periodically increase the master fader attenuation, and when the volume reached zero, the song ended and we start the next part.
     
  • space-music.mp3 - Dance break, starting at around 1:30.  The normal drums, melody, and bass sub-patterns are rearranged in different patterns to break down the track to a drum solo, then re-introducing the bass and melody slowly afterwards.
     
  • voyage-remix.mp3 - Pattern "inter-cut edit" introducing the dance break, starting at around 2:30.  It's a typical DJ remix effect of splicing together patterns in interesting ways, similar to what they do in Dub-Step and very common in megamixing.  Because the patterns are defined by a set of note and drum events, you can just take events from different patterns and put them all together in the same one.

    In this instance, I chose the last pattern of the melody verse with the first pattern of the dance break, repeating the events for drums, bass, and melody of each, and intercalating them into the same pattern in a cool rhythmic effect.  It's a bit involved, but the point is that it is very possible.  This is my favorite effect and it was a lot of fun to create.  You don't hear this sort of thing very often in 8-bit tracks. :)
     
  • voyage-remix.mp3 - Pattern transposition, starting at around 3:15.  The bass line of the melody is actually composed of two separate patterns, one that plays lower notes and the other one that plays higher notes.  I just re-ordered them in the sequence.  Instead of low-to-high, the bass line now goes from high-to-low, which makes it sound like the song has been transposed.  Notice how it changes back to "normal" when the melody is re-introduced again at around 3:36.

 

I want to clarify that most (and perhaps all) of these effects were possible with the original tracker as created by @Arnauld.  However, because it is now properly documented, it is easier to pick at the fringes of its capabilities and discover new ways of using it.  Plus the new "NPK" packing macros make things like the inter-cutting effect simpler and more straightforward by allowing you to unroll the patterns without consuming more ROM.  :)

 

Oh and lastly, here's another cool twist that is enabled by the tracker:  Just change the "drum kit" or "instruments" list used by your song, and it automatically changes its character using the new sounds.  Here's the same "demo-drums" song with the "standard" drum kit.

 

 

Note that the hi-hats sound more like real ones and the TR-808 rimshot "click" is changed to something that evokes a cowbell.  All done by just changing the list of drum instruments that the song uses.  Both the "standard" and "TR-808(ish)" drum sounds are included with the tracker.

 

     -dZ.

 

Edited by DZ-Jay
  • Like 2
Link to comment
Share on other sites

2 hours ago, DZ-Jay said:

By the way, you don't need Garage Band to do drums.  There are plenty of similar open source software out there.  And on a pinch, you could "draw" the patterns in a grid on paper -- just like we used to do back in the 80s when megamixing records with tape splices. :)

 

    -dZ.

One thing that is handy with what I call IBN (IntyBASIC Notation) is "MUSIC GOSUB" which I have in an older song that plays the same sections two times then does a 3rd section and then the original section 2 times and then a 4th section. Would that be done with sequence/repeat that in the doc on page 46?

Link to comment
Share on other sites

19 hours ago, First Spear said:

One thing that is handy with what I call IBN (IntyBASIC Notation) is "MUSIC GOSUB" which I have in an older song that plays the same sections two times then does a 3rd section and then the original section 2 times and then a 4th section. Would that be done with sequence/repeat that in the doc on page 46?

The repeat feature of the sequencer (i.e., adding a negative offset at the end), only serves to backtrack the player a number of patterns.  If you backtrack all the way to the first pattern, it repeats the song from the start, if you backtrack only a few patterns, it will repeat from that point on.

 

In that sense it is not really like "MUSIC GOSUB," because you cannot call it with multiple offsets -- its more like "MUSIC JUMP" that jumps once to the middle of the sequence.  Since there is only one sequence per song, this causes the song to loop a particular segment.

 

However, you can do precisely what you want by altering the sequence.  Think of the song as a series of patterns strung together in a sequence.  So, my song sequence may look like this:

@@sequence:  DECLE 1, 2, 3, 4, 5, 6, 7, 8, -8

 

That means "play pattern #1, followed by pattern #2, followed by pattern #3, and so on, until pattern #8; after which we backtrack to the first pattern and start again."

 

So, let's say that before repeating the entire song, you want to repeat the middle parts first, say, patterns #5 and #6.  Just do precisely that in your sequence:

@@sequence:  DECLE 1, 2, 3, 4, 5, 6, 7, 8, 4, 5, -10

 

Notice that I just added patterns #4 and #5 after pattern #8, and then backtracked 10 patterns to fall back at the beginning again.

 

You can get funkier than that and place patterns in whatever order you want, repeating them as you wish.  The following repeats the middle section, then advances a little bit, then the middle section again, and finally goes back to the beginning:

@@sequence:  DECLE 1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 5, 6, 7, 8, -14

 

Think of "patterns" as musical sections:  the intro, the verse, the drum solo, etc.  Then string them together as you wish to compose your song.  This is one key difference between traditional "trackers" or sequencers such as this one, and the IntyBASIC music player.  It's not better or worse, just a different paradigm.  You must think of your song in terms of a sequence of patterns, and then statically define that sequence; rather than a program where you have to use control flow and other techniques to "execute" the song.

 

That's how I "remixed" the songs:  I re-arranged the patterns in the sequence, repeating some, extending others, putting a drum solo at the beginning, etc.

 

In this sense, the order of the pattern definitions is completely arbitrary -- It's just an "array" of patterns.  What actually matters is how you place their "indices" in the sequence list.

 

Sure, the length of the sequence consumes ROM space, one data word for each entry, but hey! songs are resource-expensive, and the more effects or arrangements you want to add, the more resources it'll consume.  The cool part is that you "set it and forget it":  just like the IntyBASIC music player, once you define your song, the tracker does everything for you, playing notes and synthesizing the sounds in the background while your game plays. :)

 

Does this make sense?

 

    -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

Yeah. Compared to many C64 music programs, those often consist of sequence lists (playlists) which can hold values for next sequence, repeat factor, transpose factor or a jump command - not subroutine but absolute jump. Each sequence then contains the music for 1 track, with instrument changes etc. A good tracker can playback sequences of varying lengths at the same time - i.e. one track could consist of 16 step long sequences repeating over and over while another track has 128 step long sequences.

 

Actually the IntyBASIC approach to music is closer to how SoundTracker does it, since it treats a block of music with all four channels at once instead of dividing it up into individual tracks. While SoundTracker has sequence lists too, if you want to make two blocks of music with the same bass but different melody, you have to copy the entire block (e.g. 64 steps x 4 channels) twice. On 16-bit machines like the Amiga, Atari ST, PC, Mac the memory used is not of any major concern and perhaps the same can be said about Intellivision once you generate those 42 kilodecle cartridges. Also if your music is constantly changing so two seconds of music never sound the same, you don't have the need to be able to repeat the music only on some tracks/voices.

  • Like 1
Link to comment
Share on other sites

On 1/2/2021 at 12:47 PM, DZ-Jay said:

By the way, @First Spear, if you give me one of your songs in "IBN" I can try to track it for you and see what we get.

Hi, @First Spear,

 

I just created a Perl script that translates a song in "IBN" (IntyBASIC Notation) into "IMT" (Intellivision Music Tracker notation).  It tries to discern patterns in the song, and deduplicate and split it accordingly; and, unless given a specific pattern length, will attempt to find the most optimal length (i.e., the one that would result in the smallest ROM data size).

 

It is not perfect, but it is rather functional and practical as a first step.

 

It's also a work in progress, so it doesn't yet support "MUSIC GOSUB" and "MUSIC JUMP"; but I intend to address that as it continues evolving.

 

If you or anybody is interested in trying it out, let me know.  I will publish it publicly a bit later, once I polish it a little bit more. :)

 

    -dZ.

  • Like 1
Link to comment
Share on other sites

Hello everyone,

 

I've updated the distro files on the first post to "revision #1."  This revision includes a bug fix and an enhancement:

  • FIXED:  3-channel support (i.e., disabling "TRK_ENABLED_6_CHN" for non-ECS play) was broken.
  • NEW:  Envelopes now support five speed levels, from 0 to 4.  Level #4 is now the fastest, advancing the envelope on every tick.
  • TYPO:  The table describing the envelope intervals had the levels reversed, showing #0 as fastest and #3 as lowest.

The documentation has been updated with the new envelope information.

 

   Thanks!

   -dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

Darn!  Found a bug in the NPK.Note() macro that breaks a pattern when the first row is a NUL event.  As a consequence, all NUL events at the beginning of the pattern are skipped.  This explains some anomalies I found while tracking the drums demo, but I thought they were just quirks of my patterns.  Oh well.

 

I'll fix it today and provide an update distro.

 

    -dZ.

Link to comment
Share on other sites

I found another issue ... well, more of a potential side-effect.  The tracker does not have any bounds-checking for envelopes, so when it reaches the end of the array, it just continues reading whatever follows (which could be another envelope, or anything else in the data stream).

 

At a glance, it appears that the tracker expects all envelopes to end at zero volume; because when the volume of a channel goes to zero, envelope processing automatically stops.

 

However, if you forget to end an envelope in zero (say, you were trying to make a long, sustaining envelope), then if the note lasts longer than the envelope, it will not work as intended.

 

Note that this only manifests in special circumstances, mostly  when the note is exceedingly long and the envelope is on its highest speed.

 

I am not sure if I should change this behaviour.  I could easily mask the upper bits of the counter to force the envelope to cycle, but I do not know if this is desirable at all.  It could be used for repeating envelopes, though, which are not supported right now and sounds like a useful feature to have.

 

What do you think?

 

     -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

That is something I never had considered when I made music with the tracker. Is it possible to treat the last decle in the sequence of 16 (excluding the speed one) as a jump index where $0000 - $000E defines which step to loop back to and any value $000F or greater (ideally $FFFF) means stop the envelope? Obviously it would break existing songs but it would be rather easy to change the envelope definitions if we know that is what we want.

 

Something else that I've thought about but to be honest I haven't looked into if it already works, is if the drum sounds can be combined with the pitches for an instrument that begins with the drum sound and then plays a pitched normal note. In Goat Tracker on the C64, I would have a waveform table roughly like this:

 

81 C0 <- white noise, fixed frequency

11 A0 <- triangle, fixed frequency

81 90 <- white noise, fixed frequency

41 00 <- pulse width, note pitch

FF 00 <- end waveform, retain the pulse width for remainder of the note

 

The C64 doesn't have the detailed envelope control we have here, but rather relies on the register controlled values for ADSR, while obviously the AY chip doesn't have multiple waveforms (though a few different pulse widths, IIRC). The idea behind the above is of course to be able to combine drums and bass on one channel, using an instrument that strikes the fixed noise/wave first and then plays the note. Quite possibly this tracker already has support for this and I just didn't look into it. If it yet hasn't but it is something that is technically possible to implement, it could be put on a TODO list with other improvements if the format is intended to be modified before too much music has been composed with it.

Link to comment
Share on other sites

2 minutes ago, carlsson said:

That is something I never had considered when I made music with the tracker. Is it possible to treat the last decle in the sequence of 16 (excluding the speed one) as a jump index where $0000 - $000E defines which step to loop back to and any value $000F or greater (ideally $FFFF) means stop the envelope? Obviously it would break existing songs but it would be rather easy to change the envelope definitions if we know that is what we want.

Hmmm ... That is an interesting idea.  The concern I would have, apart from breaking existing songs, is reducing the length of the envelopes themselves.  It is now a 64-point matrix describing the contour of the channel amplitude.

 

Removing the last decle -- which actually means removing the last four points of the matrix.  That may not matter too much at the highest speed (which is 1 tick per point), but the shorter speeds are intended for very long notes, and each point is repeated over a number of ticks.

 

Let me think about this one.  It may be possible to enhance the data format in some way to support jump indices without changing the existing behaviour too much.

 

2 minutes ago, carlsson said:

 

Something else that I've thought about but to be honest I haven't looked into if it already works, is if the drum sounds can be combined with the pitches for an instrument that begins with the drum sound and then plays a pitched normal note. In Goat Tracker on the C64, I would have a waveform table roughly like this:

The tracker treats drums and instruments as separate things, and each is synthesized by its own process.  That limits the way that both can interact, but that goes back to how Arnauld wrote it.

 

One of the enhancements I added was the ability to support drums and instruments in the same channel, but this feature has very strict limitations due to the way that drums and instruments work:

  • A row can contain either a drum event or a note (instrument) event.  This means that drums and notes can alternate in a channel.
  • Only drums use the noise generator, so you cannot layer noise on a note unless you "simulate" it as a drum with tone at the right frequency.
  • A drum sound is not "tuned."  That is, it only plays as defined and has no note associated with it.  This means that if you want to play a drum sound with tone at different pitches, you need a drum definition for each pitch.
  • Drums are only supported in channels A and D -- that is, the channel "A" of each PSG.

The implication of the above is that noise can only be used in channels A and D, and only as part of drum sounds.  This is the standing limitation of the tracker.

 

Part of what I wanted to do as part of the "Tracker 2.0" was to blur the line between drums and instruments and treat them just like a synthesizer would.  However, I can see how this could make the sound synthesis more costly.

 

In my opinion, although not perfect, Arnauld's approach is simple, elegant, and practical:  by limiting the facilities of sound synthesis and dividing them explicitly into drums and instruments, it allows a more targeted, special-purpose implementation for each.

 

As an example of this, consider that the instrument synthesizer doesn't have to deal with changes to the noise and tone enable flags or alterations in pitch other than the deterministic, table-driven features it supports; and likewise, the drum synthesizer doesn't have to deal with procedural alterations to arbitrary pitches, etc.  Each is very flexible within the confines of its specific process of sound generation.

 

There are two alternatives we already have, of course:

  • Streaming PSG channel changes from data, as games like Deep Zone, etc. do; which trades processing speed for storage, and externalizes the complexity of music composition;
  • Forgoing user-customization of instruments sounds complete, like IntyBASIC does; which offers the opposite trade off, at the expense of variety and expression.

 

I think this tracker offers a bit of both, which is -- in my personal opinion -- a good (and perhaps superior) compromise between them.

 

That said, since I use this tracker myself, I intend to continue enhancing it with further capabilities.  So keep the ideas coming!

 

     -dZ.

 

  • Like 1
Link to comment
Share on other sites

Regarding the envelope, it can also be extended with a 17th decle, or maybe the first one specifying speed could have the speed in the LSB and the jump index in the MSB. It would still break existing songs, but not invade the length of existing envelopes.

Edited by carlsson
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...