
                   -------------------------
                    MUSE - The Music Engine
                   -------------------------

                              v02

                       by thefox//aspekt
                        thefox@aspekt.fi
                     http://kkfos.aspekt.fi


What is MUSE
------------

    MUSE is a NES sound engine that can be used in games and demos. Music
    data is exported from the NES music tracker that I've made, called
    Pornotracker. MUSE supports all features of Pornotracker, except
    auto delay(*) and everything that's based on raw PCM (PCM samples,
    PCM pulse and PCM saw -- DPCM works).

    Features:
        * Note slides
        * Volume slides
        * Glissando
        * Arpeggio
        * Vibrato
        * Tremolo
        * Vibrato depth and note slide speeds are independent of the
        note frequency
        * Note slides and arpeggio interact perfectly (you can have an
        instrument with an arpeggio envelope, and apply a note slide
        to it)
        * Sweep trick for setting the hibyte of pulse period without
        resetting phase
        * NTSC/PAL speed/frequency compensation
        * Global volume setting for volume fades, can be set to affect
        both music and sound effects, or just music
        * Sync events for synchronizing visuals to audio, among other
        things
        * Pausing of music and sound effects individually

    For the time being MUSE is distributed as a relocatable ".byte blob",
    which was generated with CC65. This is done mainly for compatibility
    reasons.

    Pornotracker can be downloaded from
    http://kkfos.aspekt.fi/projects/nes/tools/pornotracker/

    ---

    (*) Auto delay can be used in sound effects, but not in songs.


How to use
----------

    First of all, select a correct library (.h/.inc file) for your
    assembler from the "lib" directory. Include it somewhere in your
    project. Note: it has to be aligned to a 256 byte page.

    You can also grab muse-flags.h (for CA65) or muse-flags.inc (for
    ASM6/NESASM/others) from the "include" directory for flag definitions
    to use with MUSE_setFlags.

    You have to define two labels:

        MUSE_ZEROPAGE
            Points to 7 bytes of zeropage memory.

        MUSE_RAM
            Points to 256 bytes of (normal) memory.

    Make the songs and sound effects in Pornotracker, save each of them in
    separate files. Convert them using Pornotracker command line switches,
    for example:

        pornotracker.exe -s song1.pom song2.pom -e eff1.pom eff2.pom
        -o snd-data.s

    If you're using NESASM, add -nesasm switch in the beginning:

        pornotracker.exe -nesasm -s song1.pom song2.pom -e eff1.pom
        eff2.pom -o snd-data.asm

    This will generate 6 files:

        snd-data.s
            Contains general data and instruments.
        snd-data-dpcm.s
            Contains the DPCM sample data.
        snd-data-song1.s
            Contains data for the first song.
        snd-data-song2.s
            Contains data for the second song.
        snd-data-eff1.s
            Contains data for the first effect.
        snd-data-eff2.s
            Contains data for the second effect.

    Instruments and DPCM samples are optimized so that if song1.pom and
    song2.pom use the same instruments (or DPCM samples), they will be
    included only once.

    Now you can include the files in your project in whatever way you
    want. You can include different songs in different banks as long as
    you make sure they're banked in when calling the MUSE functions. Also
    the DPCM sample data should be banked in at all times if a song with
    DPCM samples is playing.

    *-dpcm.s should be included somewhere in the memory area $C000-FFFF,
    aligned to 64 bytes.

    Call MUSE_init with the label from the general data file, in this
    case it's called snd_data:

        lda #<snd_data
        ldx #>snd_data
        jsr MUSE_init

    Call MUSE_startMusic to load a song:

        ; Load the second song (song2.pom).
        lda #1
        jsr MUSE_startMusic

    By default, MUSE_init sets the music on pause, so that MUSE_update
    can be called even if no song is loaded. We need to unpause it:

        ; No flags set. You could specify other flags like NTSC_MODE
        here as needed.
        lda #0
        jsr MUSE_setFlags

    Now call MUSE_update once per frame to hear the music.

    Call MUSE_startSfx to start sound effects:

        ; Play sound effect 0 (eff1.pom)...
        lda #0
        ; ...on channel 2.
        ldx #2
        jsr MUSE_startSfx

    Make sure to read "Thread safety" if your code has a separate NMI
    and main thread.

    Look in the "examples" directory for simple examples for ASM6 and
    NESASM assemblers, and a bit more complex example for CA65.


Sound effects
-------------

    4 sound effect channels are available. The sound effects are processed
    in reverse order, so channel 0 has the highest priority. For example,
    if sound effects on both channels 1 and 0 use the first pulse channel,
    channel 0 takes priority.

    Sound effects are done with Pornotracker like any other song. Note:
    Auto delay effect can be used for sound effects, but not for normal
    songs.

    Effects can also loop. You have to add the effect EEE somewhere in
    the song to identify the sound effect as a looping one.


Functions
---------

    Note: You should not rely on any registers (A, X, Y) being saved
    when calling these functions.

    MUSE_init
    ---------
    A, X = Pointer to data

    Call this with a pointer in A, X to the general data generated by
    Pornotracker (the label is based on the output filename specified
    with the -o switch).

    Example:
        lda #<sound_data
        ldx #>sound_data
        jsr MUSE_init

    Music updates are on pause by default after calling this function. You
    need to use MUSE_setFlags to unpause it. You should not unpause and
    call MUSE_update unless you have loaded a song with MUSE_startMusic
    first.


    MUSE_update
    -----------
    No parameters.

    Call this once per frame to update the music and sound effects.


    MUSE_startMusic
    ---------------
    A = Song number

    Call to start playing a song.


    MUSE_startSfx
    -------------
    A = Effect number
    X = Effect channel 0..3

    Call to start a sound effect on the specified channel.


    MUSE_stopSfx
    ------------
    X = Effect channel 0..3

    Call to stop any sound effect possibly playing on a channel.


    MUSE_isSfxPlaying
    -----------------
    X = Effect channel 0..3

    Sets the zero flag if a sound effect is NOT playing on a channel. A
    is also set to 0.

    Example:
        ldx #2
        jsr MUSE_isSfxPlaying
        beq sfx_not_playing
            ; Sound effect is playing on channel 2.
        sfx_not_playing:

    MUSE_setVolume
    --------------
    A = Volume 0..15

    Sets the global volume. All channel volumes are multiplied by this
    volume. Can be used for fades. GLOBAL_VOL_BEFORE_SFX flag can be
    set with MUSE_setFlags if you don't want the global volume to affect
    sound effects.


    MUSE_setFlags
    -------------
    No parameters.

    Sets various flags. These are defined in muse-flags.h for CA65 and
    muse-flags.inc for other assemblers.

        MUSE_Flags::PAUSE_MUSIC / MUSE_PAUSE_MUSIC
            If set, music updates are paused.

        MUSE_Flags::PAUSE_SFX / MUSE_PAUSE_SFX
            If set, sound effect updates are paused.

        MUSE_Flags::PAUSE / MUSE_PAUSE
            If set, pauses both music and sound effects.

        MUSE_Flags::GLOBAL_VOL_BEFORE_SFX / MUSE_GLOBAL_VOL_BEFORE_SFX
            If set, global volume (MUSE_setVolume) doesn't apply to
            sound effects.

        MUSE_Flags::NTSC_MODE / MUSE_NTSC_MODE
            If set, NTSC/PAL differences are compensated for.


    MUSE_getSyncEvent
    -----------------
    No parameters.

    Returns a sync event in A, or a negative value if there's no sync
    event pending. Sync events are triggered by the effect EEy in
    Pornotracker, where y is a number 0..15. Calling the function also
    clears the sync event to $FF.

    Sync events can be used for synchronizing visuals to audio, e.g. to
    trigger scene changes in cutscenes.

    If you want to handle sync events you should call this function once
    after each call to MUSE_update. There can be only one sync event
    per one MUSE_update.

    Example:
        jsr MUSE_getSyncEvent
        bmi no_sync_event
          ; Do something with the value in A.
        no_sync_event:
        ; If MUSE_getSyncEvent is called here, it'll always return a
        negative value.


Thread safety
-------------

    The functions aren't thread safe, they cannot be safely called from
    the NMI thread if another MUSE function might be running in the main
    thread. If you have all of your code in the NMI, or are just using
    NMI to signal the start of the vblank for the main loop, you don't
    need to worry about this.

    One way to get around this is to have all your MUSE related calls in
    the NMI thread, like you would do with PPU updates. Reserve a couple
    of bytes from RAM to signal the NMI thread when to start a new song,
    sound effect, or what flags to set with MUSE_setFlags.


Resource usage
--------------

    MUSE uses about 5 % CPU on average (depending on the complexity
    of the song and number of sound effects played simultaneously),
    256 bytes of normal RAM, 7 bytes of zero page and about 8 KB of ROM.

    The ROM has to be aligned to a 256 byte page.


Version history
---------------

    v02 (2011-09-19)
    Fixed some bugs from the library. Songs need to be converted again
    with Pornotracker v1.4.1.

    v01 (2011-09-14)
    Initial release.
