Jump to content
Mr SQL

EXPERIMENTAL MUSIC AND FX TECHNIQUES IN BASIC

Recommended Posts

This thread discusses interesting music and FX in BREAKOUT 2002 LASERBEAMS

 

The BASIC listing for the game is here: BREAKOUT2002_LASERBEAMS_NTSC.txt


Steps to Get the BASIC program running in the IDE for this discussion -

 

This BASIC has an experimental sound engine and is close enough to bB that bB programmers will have an easy time programming with it and will like the new features - the codebase is free and anyone is welcome to port existing features and ideas to bB as well like the recent dynamic sprite kernel.

 

Here's a screenshot of the simple IDE which is already installed on Windows:

BREAKOUT2002_LASERBEAMS_BASIC_PROGRAM_IN_THE_IDE.thumb.JPG.cf5e07521f9d6ce89e13f2b8dabf81e3.JPG

 

SuperCharger BASIC and setup instructions are available for download here and there's one final step (thank you RT!) necessary to create a c:\vwbasic\Stella subfolder like this:

Optionally_turning_Z26_into_Stella_for_the_IDE_emu.JPG.a174eb266a6220e4a831f3f945c43152.JPG

You must place a copy of Stella.exe in this subfolder, or you can use another emulator like Z26.exe and it's DLL's in the folder and rename it as Stella.exe - that's what I did on the machine in the screenshot. It doesn't matter what version of Stella you use, but you'll want to use the latest Z26 if you prefer Z26. Any other Atari emu that can handle the SuperCharger format will work as well. Make sure the emu is configured to launch full screen and that you do not have a dual display active.

 

We're going to use the online bB Atari Music composition page by RT as well:

 

https://www.randomterrain.com/atari-2600-memories-batari-basic-music-toy.html

 

This thread is just to talk about music theory chiptune composition and interactive Fx programming in BASIC, if anyone has setup issues with the IDE please PM me and I will help you via PM - you should be able to load the .bas or .txt BASIC program in one tab and the compiler in another and click Play to compile and lauch the game in the emu.

 

Inspiration and the ADSR Envelope

 

Here are a couple of really incredible music videos by KK/DMA, Flush , KK/DMA and Shadow/Noice for inspiration,

 

and an awesome sound digitizer written by Tjoppen with a Popcorn cover by Esposta - one of my favorites! 


That last one pushes the TIA so hard that no video is possible unless the ARM or the Pitfall II chip is leveraged, but our techniques are all related - the key to shaping an ADSR envelope to any degree is punching the volume at regular intervals. We can do this even at 60, 30 or 15x per second, but the lower the frequency the less of an effect that is exerted on the selected waveform and frequency.

 

There was a thread recently about the two TIA channels sounding slightly different with the same inputs that is very related here as the twin oscillators have a slight variance in volume/power causing a different ADSR where none is intended. The experimental sound engine in this BASIC is already shaping the ADSR envelope very differently for both oscillators, here's the first few lines of the chiptune definition in the code:

 

chiptunes
4,19,4,19,8
4,17,4,17,8
4,15,4,15,8
4,14,4,14,16

 

The last value in each line is a shared duration in frames (or every two frames) and a maximum of 51 entries are available in the table (short chiptunes but we can remix them!)

 

RT's awsome composition page will be used to find and hear the base waveform and frequency for the notes you want, these are the second and third values that appear in the copy/paste box at the bottom of the page when you click on an available note from any of the waveform keyboards, for example:

 

 8,6,18
   8

 

Ignore the initial 8 and take the "6,18" this is the waveform and frequency for the first voice/oscillator. The experimental sound engine expects this information for both oscillators and a shared duration, so you will want to select a sound for the second voice/oscillator and set a shared duration. 

 

Very different sounds on both channels from the same inputs:

 

If we choose exactly the same waveform and frequency "6,18" and set a shared duration of 15 we will experience a very different sound from the same oscillators:

 

chiptunes
6,18,6,18,15

 

Why? The ADSR envelopes are now very different:

 

The experimental sound engine punches the volume down from 15 (using the duration) every other frame in this example to flare one oscillator while fading the other punching the volume up every other frame from 0 to 15 shaping the envelopes differently.

 

Tweaking the envelope and applying other Fx:

 

This game doesn't allow a linear fade or flare but instead uses a vibratto effect on the ADSR; down up down up down down, and up down up down up up, instead of moving linearly - this also extends our tempo; instead of 15 representing 15 or 30 frames (two modes available) 15 becomes 60 or 120 frames (one or two seconds sustained) and the ADSR can become more interesting for our limited punches.

 

The LASERBEAM theme - Overriding one channel AND the sustain value:

 

The game starts out with the lasers activated (unless you hold down the button to turn them off) and uses an algorithm to create a different Harmony overriding one of the channels and modifies the melody on the other channel/oscillator via the shared sustain.

 

The Balls theme - Overriding one channel and the sustain value:

 

If you have the lasers off and are listening to the main harmony chiptune (or when they run out) you can perform either a "sonic roll" through a contiguous line of bricks or find yourself bouncing quickly back and forth through a narrow corridor between rows you will hear the Ball's theme directly replacing the harmony (or is it replacing the melody? need to check which channel) or mixing in at evenly spaced alternating intervals and modifing the other channel/oscillator via the shared sustain. In this instance it doesn't use an algorithm to generate melodious music but reads it from a table. 

The Balls theme is in it's own table and is also composed with RT's awesome bB music page!    

 

The Balls theme and LASERBEAM theme are arpeggios and canons initiated by the gameplay events, these musical structures can have a compelling and captivating effect in tandem with gameplay and visuals. 

 

There's a lot going on with the Experimental music engine with opposing ADSR shaping and these other Fx techniques used together and the complex musical structures created from music tables and algorithms mingling with the gameplay and revising the Harmony and Melody in the chiptunes.

 

Looking forward to ideas and discussion! :)

 

Here's another thread with a fun experimental sound engine I put together for a demo at an Expo in the 80's with my voice digitized in the Fx: 
Computer_Expo_Book_Signing_1987.thumb.jpg.2939e39d2d6f2ebfe8aa9638d696e812.jpg

 

Update: I found the first note in the Balls theme arpeggio was getting detuned by a bug, fixed it in the BASIC code before sharing it today - recompile this game and see the melodious improvement from correcting just one note in an Arpeggio! All participating notes must be in tune for the Arpeggios to work properly.
 
 

Share this post


Link to post
Share on other sites

Color Fx techniques and Ideas

 

The sphere version (ROM here) adds color cycling to the ball player sprite for lazy sideways rotation to complement the forward rotation:

 

BREAKOUT2002_LASERBEAMS_NTSC_R2.txt 

 

Loading Sprites upside down for the forward roll animation

The forward roll rotation animation is comprised of 4 frames, like Assembly and batari BASIC we can flip the sprite sideways, but SuperCharger BASIC has an additional keyword (defined in the manual) for loading sprites upside down! This allows a single sprite image to turn into four frames which is what is happening with the Ball's forward roll which seems smooth because it is at a fast 30 FPS despite the large increments:

 

Normal sprite 

Normal reversed sprite

upside down reversed sprite

unside down sprite

 

Cycling the ball player sprite colors for sidways rotation animation

Cycling the sprites colors at a slower rate adds sideways rotation creating the illusion of a sphere with both forward and sidways animation at different rates. 

 

Program architecture - Runing SuperCharger BASIC in both vertical blanks:

 

architecture.jpg.f1f0ad568d6a215a3251dabb7f7507be.jpg

 

New concept - two game loops:

Note there are two gameloops which run in the top and bottom vertical blanks - batari BASIC only has one of these, but the extra vertical blank can run additional code instead of having to call draw screen - this is important because you can get up to twice as much time to do stuff. 

 

New concept - kitchen sink loop:

What is this? Well SuperCharger BASIC also allows you to use an entire frame to run code which is a lot of time, this be done intermittently like with SuperCharger Space Invaders, or constantly like with this game, resulting in a 30 Hz display.

 

New concept - 30 Hz displays and motion blur reduction:

A 30 Hz display can be used to optimize motion blur reduction from panning the camera object and requires careful selection of the palette to minimize flicker.
this game features smoothed jaggies; pulsed movement that is visibly smoothed.  

 

The forward roll on the ball is a pure 30 fps and smooth, the sideways color cycling animation is at 7.5 fps, but the transitions are at 30 Hz pulses.

 

The PAL palette does not play nice algorithmically and needs a lookup table for flicker optimization if someone wants to create a PAL version!

 

Edited by Mr SQL

Share this post


Link to post
Share on other sites

This is interesting. I've been trying to learn the audio to make sound effects for my game so I am working on a project and want to make more expressive sounds for purpose of a drum/rhythm machine. I've been playing around with original code and sample programs but can't determine which is best yet. 

 

I had thought data tables might be the best option but I am trying to learn those too and cannot quite figure them out yet.

 

I'm hoping to come up with something good so I can post the project but I want to learn all this stuff first. The Atari audio is very interesting. 

  • Like 1

Share this post


Link to post
Share on other sites
13 hours ago, KevKelley said:

This is interesting. I've been trying to learn the audio to make sound effects for my game so I am working on a project and want to make more expressive sounds for purpose of a drum/rhythm machine. I've been playing around with original code and sample programs but can't determine which is best yet. 

 

I had thought data tables might be the best option but I am trying to learn those too and cannot quite figure them out yet.

 

I'm hoping to come up with something good so I can post the project but I want to learn all this stuff first. The Atari audio is very interesting. 

Very cool you are experimenting with expressive sound and an Atari drum/rythm machine - I am looking forward to seeing your project! :)

 

I think data tables are fantastic, there are so many purposes for them. We can have data tables for the musical score, but then may also have
more data tables to change the sound or use an engine for that (more algorithms), and maybe mix other algorithms into the sound too.

 

Here's an inspiring pure algorithmic TIA tune written with the BASIC programming cart; a lot can be added with algorithms from shaping effects to entire chiptunes:


 

Share this post


Link to post
Share on other sites

My thoughts were to use a data table to essentially set up the ADSR for a sound. I don't know if that is possible but from everything I read it sounded possible. In my sample program I wrote I couldn't get them to work, but I am learning (I was trying to learn data tables for my Bag Boy game). I'm hoping me playing around with all this helps me with Bag Boy. Sound is one of my weak spots (out of many) that I hope to fix. Currently in the game I used some random sounds to give the game  some variation. I want to try and get some cool arcade sounds. I love those classic game sounds.

Share this post


Link to post
Share on other sites

Speaking of music, I made this using Visual batari Basic:

 

https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#ex_sound_with_bg_music

 

The "music" plays on one channel while the sound effects play on the other. The only problem is that repeated sections of the "music" are wasting ROM, so I hoped that Sprybug's tool would fix that, but it isn't finished yet:

 

 

Share this post


Link to post
Share on other sites
10 hours ago, KevKelley said:

My thoughts were to use a data table to essentially set up the ADSR for a sound. I don't know if that is possible but from everything I read it sounded possible. In my sample program I wrote I couldn't get them to work, but I am learning (I was trying to learn data tables for my Bag Boy game). I'm hoping me playing around with all this helps me with Bag Boy. Sound is one of my weak spots (out of many) that I hope to fix. Currently in the game I used some random sounds to give the game  some variation. I want to try and get some cool arcade sounds. I love those classic game sounds.

I think you are on the right track! To get your sample programs to work try avoiding calling drawscreen multiple times because you need to update the TIA registers regularly, experiment with updating them every frame or every 2nd or 3rd frame from your data table and you will hear different shaping effect.

 

What happens when drawscreen gets called is that the next frame occurs and with a variable number of these calls (depending upon the program path) there is no way to evenly disperse the register updates to shape the sound envelope.

 

A music engine that is called from the other vertical blank every frame could handle it transparently, I checked out SpryBug's music engine RT linked which is very cool and has flare and fade features which may allow shaping the envelope when drawscreen calls can be avoided in the game programs.

 

Ideas to avoid calls to drawscreen - one way to do this is with a framecounter variable where different frames handle different loads instead of load balancing via drawscreen calls; then the main loop is always able to update the TIA sound registers every 1st, 2nd or 3rd frame.  

 

8 hours ago, Random Terrain said:

Speaking of music, I made this using Visual batari Basic:

 

https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#ex_sound_with_bg_music

 

The "music" plays on one channel while the sound effects play on the other. The only problem is that repeated sections of the "music" are wasting ROM, so I hoped that Sprybug's tool would fix that, but it isn't finished yet:

 

 

Wow awesome soundtrack and really cool game RT! I was playing that for a few minutes battling the AI - I like the Smiley face character and the game is challenging :)

What really made me keep playing was the immersive soundtrack I wanted to keep listening to with more ear candy whenever I hit the enemy - very well done!

 

Smiley face Chase (what I named it on my Haromony) is an excellent game and a fantastic example of the concept of a balanced melodious soundtrack complementing the gameplay and interactive Fx with the use of both channels.

 

And I really like what Sprybug is doing with his engine and the idea for patterns! We have limited space and being able to sequence a block of musical measures or a single measure is key to making longer musical compositions. The patterns concept better explains what I was trying to convey to @MemberAtarian regarding his sound digitizer - building extended musical compositions via the use of patterns has tremendous potential for digital sound samples by breaking them up into smaller segments we can sequence.  I used a similar concept on my demo at the 80's Expo to sequence the Max Headroom effect, patterns work with vocals too.

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Thanks! Right now I have a simple program that will activate a sound depending on the direction. I am still trying to figure out data tables but for now the basis of my program comes from a RT sample program until I figure more things out (I like the puzzle of learning before I run to the forums with a question). 

 

I was wondering one thing, if visuals are not important, what are the negative impacts of too many cycles? I had made an edit to my code and while the sound seemed to work well. 

Share this post


Link to post
Share on other sites

If visuals are not important there are no negative impacts, the digitized audio in Quadrun and in MemberAtarian's sound digitizer require updating the TIA registers so frequently there is no time for the display.  Video Chess uses all the cycles thinking and only changes the background color but doesn't render the board.

 

I wonder if a pulsed sound digitizer could be written that uses all the cycles but runs every alternate frame to allow a 30 Hz display and digitized sound.

 

Share this post


Link to post
Share on other sites

Hey, saw the link to my topic.  I just wanted to let you know that I did implement a new sound system on my game, however because of RAM limitations, I only had 3 bytes and a bit to my disposal and what I wanted to do originally required a bit more than that.  So I decided to take the typical method and streamline it a little.  This method actually will reduce the size of data in your music sequence.  For example, I cleared up 1K of space using this method, for the Base Area music in Zed.

 

The original data method for music is like this:

First line is channel 0

AUDC value, AUDF value, AUDV value

Next line is the same except for channel 1

And the last number is the duration in frames (or game cycles in my case), that this gets played, or the 255 terminator.

 

Well, I quickly figured that there was a lot of wasted space going on here, when all you want to do is, for example, just change the volume level, but keep the same C and F value going, because there's no reason to change the registers.  The old way we had to repeat it, thus wasting bytes.  But with this method, we can cut out the C and F, if there going to be the same, and just alter the volume instead.

 

What we do is, if we're repeating the same C and F and just want to lower the volume, we'd replace the first value with a volume level above 15.  The Atari's volume registers go from 0-15.  If we go higher than that, then we just tell the program that this is just a volume change and don't bother with C and F.  Then we subtract the volume level by 16 to get the true volume.  Example, 16=0, 17=1, 18=2, etc.  After that we skip to the next channel read and see if it's doing the same thing on channel 1 or not that we just did with channel 0.

 

To get an idea of what I did, here's an example from my new Base Area music data:

 

Quote

BaseMusic
   sdata BaseMusicData=q
  8,4,26,4,6,20,3
  22,18,3
  20,17,3
  8,4,19,4,6,13,3
  22,18,3
  20,17,3
  8,4,21,2,1,31,12
  23,4,6,20,3
  23,20,3
  22,2,1,31,9
  20,17,3

 

And so on.  This is the first measure of the song.

First line we have all the info, since it's the beginning.  Channel 0 C,F,V, Channel 1 C,F,V, duration

Line 2, is just read as Channel 0 volume 6, Channel 1 volume 2, duration

Line 8 is setting just the volume on channel 0, while setting up the new sound for channel 1. Channel 0 volume 7, Channel 1 C,F,V, duration

And you can also do the reverse as well (Channel 0 C,F,V, Channel 1 volume X, duration)

And a 16 you can use as silence on the volume change

 

After doing this on the Base Area song, I ended up saving 1K of space!

The bit is used to detect if a sound effect (thus canceling the music for channel 0), had just finished playing and to silence channel 0, until it can load up the next new sound on that channel.

 

Here's my code on it:

Quote

 rem Soundtrack routines
GetMusic
 if Enemy{5} then goto GetMusicBossBattle bank14
   rem  *  Check for end of current note
   p=p-1
   if p>0 then return otherbank
   rem  *  Retrieve channel 0 data
   temp4 = sread(BaseMusicData) 
  if temp4=255 then p = 1 : gosub BaseMusic:goto GetMusic
  if temp4>15 then VOBM
   Music_Bit=1
   temp5 = sread(BaseMusicData)
   temp6 = sread(BaseMusicData)
   rem  *  Check for end of data
   rem  *  Play channel 0
   if Sound_Effect  then C1BM
   AUDV0 = temp4
   AUDC0 = temp5
   AUDF0 = temp6
   goto C1BM
VOBM
   if Sound_Effect  then C1BM
  if !Zed_Flags{7} then temp4=16 
  temp4=temp4-16
   AUDV0=temp4
C1BM
   rem  *  Retrieve channel 1 data
   temp4 = sread(BaseMusicData)
 if temp4>15 then temp4=temp4-16:AUDV1=temp4:goto LengthBM
   temp5 = sread(BaseMusicData)
   temp6 = sread(BaseMusicData)
   rem  *  Play channel 1
   AUDV1 = temp4
   AUDC1 = temp5
   AUDF1 = temp6
   rem  *  Set duration
LengthBM
   p = sread(BaseMusicData)
  return otherbank

 

Music_Bit is a defined bit I made from Zed_Flags{7} (Notice I had to use this later because there is a bug when conditionally checking a not on a bit that's been defined, so I have to resort to using the original name), and Sound_Effect is a dim I did for the Sound_Effect value to play in the Sound Effect routine.  Checking this to see if it isn't zero means that a Sound Effect is currently playing.  Music Bit being 0 means that a sound effect finished playing and to silence this channel until the next sound register can get loaded up (this is set in the Sound Effect playing routine separately after it plays a sound and sets this bit to 0).  Notice that it sets the bit to 1 once it loads up an actual sound.  It does this regardless if the bit is 1 or 0.  This makes the music program play normally again.

 

So, I use p as my duration, q and r as my sdata pointer, v for my sound effect pointer (if you use this method), and a bit to determine if we should silence channel 0 after a sound effect until we can load up the next note.

  • Like 2

Share this post


Link to post
Share on other sites

Very cool! I like the data compression technniques using values beyond 15 to change how your sound engine processes the sound, and how much data is required. 

 

This kind of controlled sound processing offers a big edge over the simpler uniform sound engine in bB that processes every line the same way, because the engine becomes programmable via the musical score - thanks for providing an excellent example of this :)

 

The experimental sound engine in BREAKOUT2002 achieves data compression by utilizing a shared duration for the waveform and frequency in the musical score.

 

It also uses data compression to shape the envelope differently for the channels by flaring one and fading the other by using the shared duration to seed the algorithm such that these identical waveforms and frequencies produce distinctly different sounds:

 

chiptunes

6,18,6,18,15

 

This engine has a tiny footprint to run in a small space like the SuperCharger where 6K is the limit for everything, but the details on the design ideas may be interesting -

 

It supports patterns via an index on the musical score:

 

chiptunes
4,19,4,19,8
4,17,4,17,8
4,15,4,15,8
4,14,4,14,15
4,19,4,19,8
0,0,0,0,0
4,14,4,9,32
6,30,6,30,8
0,0,0,0,0
 

The sound engine repeats the first tune when it encounters a line of zeros, and the index can also be set to point to any line to play back one or all of the measures in any pattern below the main chiptune. whenever it encounters a line of zeros, it returns to repeating the first chiptune.

 

Interrupts and overrides for Fx on one or both channels are handled in BREAKOUT2002 by hooking the sound engines shadow registers in conjunction with the TIA registers to change either the shared duration or to override one or both channels from an algorithm or Fx data tables, and to change the pattern index.

 

Share this post


Link to post
Share on other sites

There are some questions in my head.
First, "Sound_Bit" and "Sound_Effect" are both full bytes? Those should be just one bit. One big lack of bB is that you can't define bits by names. 
On the other hand, you don't have to use temp4-temp6, you can directly load values into the CPU registers. The only reason you need loading first is if you are measuring if the value is #255, so you reached the end.
In my opinion, you should do it so if you load zero for AUDV, you won't need any AUDC and AUDF because it will be silent anyway.

Loading_Channel0
    temp4=sread(BaseMusicData)
    if temp4=255 then goto BaseMusic
    if temp4=0 then AUDV0=0 else AUDV0=temp4: AUDC0=sread(BaseMusicData): AUDF0=sread(BaseMusicData)
Load_Duration0
    Duration=sread(BaseMusicData)

 

[...]

 

BaseMusicData

    0

    16

    8, 4, 3

    4

    8, 4, 6

    8

    255

   If you won't change the channel during the music, you can set it on constant.

Loading_Channel0
    temp4=sread(BaseMusicData)
    if temp4=255 then goto BaseMusic
    if temp4=0 then AUDV0=0 else AUDV0=temp4: AUDC0=4: AUDF0=sread(BaseMusicData)
Load_Duration0
    Duration=sread(BaseMusicData)

 

[...]

 

BaseMusicData

    0

    16

    8, 3

    4

    8, 6

    8

    255


   And if you know a little about how the AUDV and AUDC registers work, you can set them on the same byte, so add the value of AUDV to the value of AUDC*16. So you you want AUDV as 8 and AUDC as 4, 8+4*16=72.
 

Loading_Channel0
    temp4=sread(BaseMusicData)
    if temp4=255 then goto BaseMusic
    if temp4=0 then AUDV0=0: goto Load_Duration0

    AUDV0=temp4

    AUDC0=temp4/16

    AUDF0=sread(BaseMusicData)
Load_Duration0
    Duration=sread(BaseMusicData)

[...]

 

BaseMusicData

    0

    16

    72, 3

    4

    72, 6

    8

    255

Share this post


Link to post
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.

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...