Using a song in production, using Rasm

BREAKING NEWS: Do NOT use Rasm 1.5 or 1.6, it is buggy! Use the 1.7 or higher, or any version below 1.5!

First, we will study the simplest case : using Rasm. Don't worry, using other assemblers will be covered in the next part. However, you must read this part first to understand how the player works, then you can switch to the next part where we'll take care of your assembler problem.

So! I consider four files were generated:

  • Grenade.asm (the song)
  • Grenade_playerconfig.asm (the optional config file for optimization)
  • SoundEffects.asm (the sound effects).
  • SoundEffects_playerconfig.asm (the optional config file for the optimization of the sound effects).

We will use the AKG player, but any other would work the same way.

You could open the players/playerAkg/sources/tester folder and pick up the tester that would fit, and that would be it! But as it is a tutorial, I'll explain everything from scratch.

Let's make a code

Our goal is to make our own code to play the song.

Its structure will be very simple:

  1. Initialize the code
  2. Initialize the song
  3. Wait for the frame flyback (explained later)
  4. Play the song
  5. Go back to 2!

Initialization

We will start with a ORG to define where our code starts, and various initialization, which are platform depend. You can check the testers directly on how this is done, but we'll have a quick overview here.

On CPC, we can start as low as #1000 (or even lower, but watch out for your Basic program!), and we "kill" the system by putting the DI : RET instructions in #38. This will prevent the system from messing with our code.

    ;CPC:
    org #1000
    di
    ld hl,#c9fb   ;DI, RET
    ld (#38),hl

On MSX, you need to declare a small header for the BLOAD command to work, it's easy (more info here):

    ;MSX:
    org #b000
    db #fe     ;Declares a binary header.
    dw TesterStart    ;Start of the tester, just after this header.
    dw TesterEnd      ;End of the tester.
    dw TesterStart    ;Execution address.
TesterStart           ;Starts our real code here.

Don't forget to add the TesterEnd label at the very end of the source, even as we start adding more code (do not put anything after!).

On Spectrum/Pentagon, it seems we only have to do this:

    ;Spectrum/Pentagon:
    org #8000
    di

Initialize the music

Now that this technicality is done, we can initialize the music. All the players work the same way: we call the "init" method of the player and gives it the address of the music.

One question we might ask is: where are the music and the player? Don't worry, we'll include them later.

    ld hl,Music            ;The address of the music.
    ld a,0                 ;What subsong to play (starts at 0)?
    call PLY_AKG_Init      ;Call the init method of the player. 

(Yes, "ld a,0" can be optimized to xor a, I didn't want to scare the beginners). The subsong "0" is in fact the "1" in the AT2 editor. In assembler, we think in "index", which starts at 0. If later, you want to play another subsong (for example, a "game over" subsong), simply call the PLY_AKG_Init method again with the corresponding subsong index, and there you go.

This "init" method is always named the same way in every player, only the "AKG" part changes. For example:

  • For the AKY player, the init method will be PLY_AKY_Init
  • For the Lightweight player, PLY_LW_Init
  • and so on.

The only difference between players is that AKY doesn't manage subsongs (there is only one), so there is no need to set A. To know that, simply check the testers of these players to check how to use them.

...So, we have initialized the player. Now it knows about where the music is, and what subsong to play. Note that this is a mandatory step!

Initialize the sound effects

Maybe you want some sound effects? Now is the right time to initialize them too. Just like with the music, it is a mandatory step, and it must be done once. Simply call the init method of the SFXs, giving it the address of the sound effects:

    ld hl,SoundEffects         ;Address of the sound effects.
    call PLY_AKG_InitSoundEffects

Once again, we will include the sound effects later, so don't worry if the program doesn't compile yet.

Frame flyback

I told before about this "frame flyback" stuff. On most retro machines, music, but also animations, are synchronized with the frame flyback. This happens 50 or 60 times per seconds (thus, 50/60 Hz). At that moment, the electron canon that displays the image on the screen is at the top, and will move towards the bottom to display a frame.

We synchronize the music and sound effect to the moment the canon is at the top. By doing this, we can assure the music is played at constant rate, and at a decent one (the faster, the more notes we can play!). 99% on the songs are expected to be played at 50 Hz, but you could also want a song that plays at 100, 150, or even 300 Hz! On the opposite, 25 or 12.5 Hz are also possible and can still give good results. Just know that AT2 can handle all these! But for now, let's stick to our good old 50 Hz.

Waiting for the frame flyback is once again system dependent:

Sync
    ;CPC:
    ld b,#f5
    in a,(c)
    rra
    jr nc,Sync + 2
    ei
    nop
    halt
    halt
    di

    ;MSX / SPECTRUM / PENTAGON:
    ei
    nop
    halt
    di

Please only use the snippet your platform requires. After this, we can finally play one frame of our song.

Play the music

This is very simple:

    call PLY_AKG_Play

That's it! As the player has been already initialized, this is the only thing to do. No need of any parameter.

Note that we only played one frame of the music. Now we need to create a loop for the music to be played ad vitam eternam. This is simply done by adding a jump to the frame flyback code, that we (cleverly) marked with a "sync" label:

    jp Sync

That's it! We have a music that plays. We could stop here, but maybe you want, at some point, to stop the music.

Stopping the music

This is done as simply as playing it:

    call PLY_AKG_Stop

Where to call this? Well, you could add a keyboard test after the "play", and if a certain key is pressed, go to a subroutine that makes the "stop" call. This is very platform-specific, so I won't enter in these details. You should be able to do it by yourself!

Please note that once the music is stopped, you should NOT make a call to the Play method. The Stop method only cuts the PSG registers, thus stopping all sound. But it does NOT prevent the player from playing if called again.

Play the sound effects

What about adding sound effects? If you have initialized them, as seen above, playing one sound effect is simple. All has been explained here, but here a little summing up anyway. Call this to play the first sound:

    ld a,1 ;Sound effect number (>=1)
    ld c,0 ;channel (0-2)
    ld b,0 ;Inverted volume (0-16)
    call PLY_AKG_PlaySoundEffect

When to call this? This is highly dependent on your system, but you could make a keyboard test to play a sound.

Note: calling this method will only program the playing of a sound effect. It is the PLY_AKG_Play that will play it along with the music, frame by frame.

Stopping a sound effect

This is done by calling a simple method, plus the channel where the sound effect is. So this does not actually stop one specific sound effect, but any sound effect that is on the selected channel.

    ld a,0   ;Channel (0-2)
    call PLY_AKG_StopSoundEffectFromChannel

Declare the players and music

Last part! And probably where all the subtleties are. Our code does not compile yet because we didn't include the player, music, and sound effects files! Let's do that, then. Remember, we have already generated all these files in the previous part of this tutorial.

At the bottom of the source, include the music:

    include "Grenade.asm"
    include "Grenade_playerconfig.asm"   ;Optional.

Note that we also included the player configuration file. You can do it anywhere (or not at all, this is optional!) but it must be put before the player, else the player can not know how to configure itself, and will use default parameters (so no optimizations will be performed, which is a shame!).

Now include the sound effects, if you're using them:

    include "SoundEffects.asm"
    include "SoundEffects_playerconfig.asm"   ;Optional.

Once again, we used the player configuration of the sound effects, for more optimization.

Finally, let's include the player. But warning! AT2 is multi-platform, so the player must be told what platform to use. CPCers are lucky, they don't have to do anything, as CPC is default. Other platforms must declare their identity. This is done with a single line, don't worry!

Also, we need to indicate another flag if you want to include the sound effect support. Once again, a single line is needed.

    ;Selects the hardware target:
    ;PLY_AKG_HARDWARE_CPC = 1   ;Declare CPC platform. Default, not needed!
    PLY_AKG_HARDWARE_MSX = 1    ;Declare MSX platform.
    ;PLY_AKG_HARDWARE_SPECTRUM = 1    ;... and so on.
    ;PLY_AKG_HARDWARE_PENTAGON = 1

    ;Sound effect support? Then uncomment this:
    ;PLY_AKG_MANAGE_SOUND_EFFECTS = 1

    include "PlayerAkg.asm"

Note that unused lines must be deleted or commented. Do NOT put 0 instead of 1, it will not work! The players test the variables presence, not their value!

And that's it!! If you did things right (!), you should hear music (and sound effects if you plugged things right).

What order?

I included the music before the player, but this is not mandatory at all. It is actually more intuitive to, instead, include the player first, then the music and the sound effects. If you do so, remember to include the player configuration files before the player, which may be counter-intuitive, because the music/sound effect files they are related to will be included later. Like this:

    PLY_AKG_HARDWARE_MSX = 1 
    PLY_AKG_MANAGE_SOUND_EFFECTS = 1

    ;Always include the player configuration files BEFORE the player. 
    include "Grenade_playerconfig.asm"
    include "SoundEffects_playerconfig.asm"  
 
    ;This is the player.
    include "PlayerAkg.asm" 
  
    ;Music and sound effects.
    include "Grenade.asm" 
    include "SoundEffects.asm" 

This should get you started!

You can assemble your code by typing:

rasm <MySourceFile.asm> -o Test

(of source, replace MySourceFile.asm with the name of the source you just created. "Test" is the base name of the generated file. In our case, it will generate Test.bin).

And of course this should assemble without any error.

Quick testing with an emulator

You can inject this code directly in an emulator for testing, for example. On CPC, open Winape, pause it (F7). The debugger opens. At the bottom, select "Any" in Memory. In the Monitor just above (where all the hex numbers are), right-click and select Goto... Enter 1000 (this is our ORG value). Then right-click again on the monitor and Load. Select your Test.bin file. Click on Play in Winape, and under Basic, type call &1000. You should hear some music!

What now?

If you use Rasm, you can stop reading here. If you are using another assembler, then go on to the next part!