Game Boy Advance – MaxMod Frame Interrupt

gameboy_advance_maxmodAls ich mich vor ein paar Monaten etwas mit der Sound-Hardware und dessen Programmierung von einem Game Boy Advance beschäftigt habe, habe ich verschiedene Bibliotheken bzw. fertige Sound-Player gefunden. Im devkitPro Ordner befinden sich insgesamt 4 Beispiele, die man kompilieren kann. Im Februrar bzw. März hatte ich mir BoyScout angeschaut. Ein Tracker mit 4 Kanälen. Diese Software habe ich dann benutzt, um die Musik (wird man wohl so bezeichnen können!) für meine MSP430 Demo “A Dead Pixel” zu erstellen. Die Tracker Software habe ich dann speziell für den MSP430 geschrieben und mich auf BoyScout dabei gestützt. Aber das ist eine andere Geschichte. Mit MaxMod können mod-Files abgespielt werden. In dem vorhandenen MaxMod Beispiel habe ich ein paar Modifikationen vorgenommen. Normalerweise wird die Musik in der Hauptschleife nach einem Frame aktualisiert. Die Funktion zum aktualisieren muss also immer manuell aufgerufen werden. Dies kann natürlich zu Problemen in der Wiedergabe führen, besonders wenn in der Schleife viele Berechnungen durchgeführt werden und somit der genaue Zeitpunkt des Funktionsaufrufs nicht mehr eingehalten werden kann. Hier bietet MaxMod aber eine Problemlösung. Ein Frame-Update kann per Interrupt durchgeführt werden. Somit wird die Funktion bzw. ISR immer nach einem Frame aufgerufen. Der abgeänderte Beispiel-Code:

Die Website von MaxMod scheint nicht mehr online zu sein. Auf archive.org finden sich allerdings noch ein paar Snapshots. Hier also der Link zu der alten MaxMod Seite: MaxMod.org und die komplette Dokumentation: Maxmod Programming Reference

Die Interrupt Service Routine, um die Musik zu aktualisieren:

  1. void updateSongFrame(void)
  2. {
  3.     mmFrame();
  4. }
void updateSongFrame(void)
{
    mmFrame();
}

Die ISR wird über die Funktion void mmSetVBlankHandler( void* function ); bekannt gemacht:

  1. mmSetVBlankHandler(updateSongFrame);
mmSetVBlankHandler(updateSongFrame);

Der komplette Source-Code:

  1. #include <gba.h>
  2. #include <maxmod.h>
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6.  
  7. #include "soundbank.h"
  8. #include "soundbank_bin.h"
  9.  
  10.  
  11. void updateSongFrame(void)
  12. {
  13.     mmFrame();
  14. }
  15.  
  16. int main() {
  17.  
  18.     irqInit();
  19.  
  20.     // Maxmod requires the vblank interrupt to reset sound DMA.
  21.     // Link the VBlank interrupt to mmVBlank, and enable it.
  22.     irqSet( IRQ_VBLANK, mmVBlank );
  23.     
  24.     irqEnable(IRQ_VBLANK);
  25.     
  26.     mmSetVBlankHandler(updateSongFrame);
  27.     
  28.     consoleDemoInit();
  29.  
  30.     // ansi escape sequence to clear screen and home cursor
  31.     // /x1b[line;columnH
  32.     iprintf("\x1b[2J");
  33.  
  34.     // initialise maxmod with soundbank and 8 channels
  35.     mmInitDefault( (mm_addr)soundbank_bin, 8 );
  36.  
  37.     // Start playing module
  38.     //mmStart( MOD_FLATOUTLIES, MM_PLAY_LOOP );
  39.     mmStart( MOD_LOSTINMYDREAMS, MM_PLAY_LOOP );
  40.  
  41.     mm_sound_effect ambulance = {
  42.         { SFX_AMBULANCE } ,            // id
  43.         (int)(1.0f * (1<<10)),    // rate
  44.         0,        // handle
  45.         255,    // volume
  46.         0,        // panning
  47.     };
  48.  
  49.     mm_sound_effect boom = {
  50.         { SFX_BOOM } ,            // id
  51.         (int)(1.0f * (1<<10)),    // rate
  52.         0,        // handle
  53.         255,    // volume
  54.         255,    // panning
  55.     };
  56.  
  57.     // ansi escape sequence to clear screen and home cursor
  58.     // /x1b[line;columnH
  59.     iprintf("\x1b[2J");
  60.  
  61.     // ansi escape sequence to set print co-ordinates
  62.     // /x1b[line;columnH
  63.     iprintf("\x1b[0;8HMaxMod Audio demo");
  64.     iprintf("\x1b[3;0HHold A for ambulance sound");
  65.     iprintf("\x1b[4;0HPress B for boom sound");
  66.     
  67.     // sound effect handle (for cancelling it later)
  68.     mm_sfxhand amb = 0;
  69.  
  70.     do {
  71.  
  72.         int keys_pressed, keys_released;
  73.         
  74.         //VBlankIntrWait();
  75.         //mmFrame();
  76.         scanKeys();
  77.  
  78.         keys_pressed = keysDown();
  79.         keys_released = keysUp();
  80.  
  81.         // Play looping ambulance sound effect out of left speaker if A button is pressed
  82.         if ( keys_pressed & KEY_A ) {
  83.             amb = mmEffectEx(&ambulance);
  84.         }
  85.  
  86.         // stop ambulance sound when A button is released
  87.         if ( keys_released & KEY_A ) {
  88.             mmEffectCancel(amb);
  89.         }
  90.  
  91.         // Play explosion sound effect out of right speaker if B button is pressed
  92.         if ( keys_pressed & KEY_B ) {
  93.             mmEffectEx(&boom);
  94.         }
  95.  
  96.     } while( 1 );
  97. }
#include <gba.h>
#include <maxmod.h>

#include <stdio.h>
#include <stdlib.h>

#include "soundbank.h"
#include "soundbank_bin.h"


void updateSongFrame(void)
{
    mmFrame();
}

int main() {

    irqInit();

    // Maxmod requires the vblank interrupt to reset sound DMA.
    // Link the VBlank interrupt to mmVBlank, and enable it. 
    irqSet( IRQ_VBLANK, mmVBlank );
    
    irqEnable(IRQ_VBLANK);
    
    mmSetVBlankHandler(updateSongFrame);
    
    consoleDemoInit();

    // ansi escape sequence to clear screen and home cursor
    // /x1b[line;columnH
    iprintf("\x1b[2J");

    // initialise maxmod with soundbank and 8 channels
    mmInitDefault( (mm_addr)soundbank_bin, 8 );

    // Start playing module
    //mmStart( MOD_FLATOUTLIES, MM_PLAY_LOOP );
    mmStart( MOD_LOSTINMYDREAMS, MM_PLAY_LOOP );

    mm_sound_effect ambulance = {
        { SFX_AMBULANCE } ,            // id
        (int)(1.0f * (1<<10)),    // rate
        0,        // handle
        255,    // volume
        0,        // panning
    };

    mm_sound_effect boom = {
        { SFX_BOOM } ,            // id
        (int)(1.0f * (1<<10)),    // rate
        0,        // handle
        255,    // volume
        255,    // panning
    };

    // ansi escape sequence to clear screen and home cursor
    // /x1b[line;columnH
    iprintf("\x1b[2J");

    // ansi escape sequence to set print co-ordinates
    // /x1b[line;columnH
    iprintf("\x1b[0;8HMaxMod Audio demo");
    iprintf("\x1b[3;0HHold A for ambulance sound");
    iprintf("\x1b[4;0HPress B for boom sound");
    
    // sound effect handle (for cancelling it later)
    mm_sfxhand amb = 0;

    do {

        int keys_pressed, keys_released;
        
        //VBlankIntrWait();
        //mmFrame();
        scanKeys();

        keys_pressed = keysDown();
        keys_released = keysUp();

        // Play looping ambulance sound effect out of left speaker if A button is pressed
        if ( keys_pressed & KEY_A ) {
            amb = mmEffectEx(&ambulance);
        }

        // stop ambulance sound when A button is released
        if ( keys_released & KEY_A ) {
            mmEffectCancel(amb);
        }

        // Play explosion sound effect out of right speaker if B button is pressed
        if ( keys_pressed & KEY_B ) {
            mmEffectEx(&boom);
        }

    } while( 1 );
}

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.