AminetAminet
Search:
84701 packages online
About
Recent
Browse
Search
Upload
Setup
Services

mus/play/ptplayer.lha

Mirror:Random
Showing:m68k-amigaosppc-amigaosppc-morphosi386-arosi386-amithlonppc-warpupppc-powerupgeneric
No screenshot available
Short:ProTracker player w/ support for ext. sfx
Author:Frank Wille
Uploader:frank phoenix owl de
Type:mus/play
Version:6.4
Requires:Assembler
Architecture:m68k-amigaos
Date:2024-06-26
Download:http://aminet.net/mus/play/ptplayer.lha - View contents
Readme:http://aminet.net/mus/play/ptplayer.readme
Downloads:4972

While developing the games Sqrxz and Solid Gold I needed a Protracker
player which can insert sound effects from the game into the current song.
I ended up in writing a completely new player based on the original
replayer source which came with ProTracker 2.3.

This player is quite optimized and has some useful features for game
developers:

- Insert external sound effects into the replayed module.

- Can also play sound effects while music is stopped or not even initialized.

- A fast master volume for the replayed music.

- No busy waiting. DMA and repeat pointers are set with timer interrupts.

- Optionally works without timer interrupts at all.

- E8 command can be used as a trigger for your main program.

- Lots of tables for best performance. No multiplications or divisions.


The sound fx system gives you the possibility to play samples on a channel
of your choice or on the channel which the player thinks is the best one.

It may be a channel which is currently not replaying music and/or has
the longest period of inactivity ahead. This has the effect that the
replayed song is often not disturbed at all.

Up to four sound effects can be played at the same time and any of them
has its own priority, which is especially useful when trying to play
several sounds on the same channel. You may for example define that a
shooting-sound has a higher priority than a jumping-sound.

For automatic channel selection you can additionally reserve specific
channels for music only, or define the maximum number of channels which
may be used for sound effects at once.

The master volume is always applied to the music, but does never affect
external sound effects.

NOTE: When playing external sound effects, always make sure the first
word is cleared! It is used for idling when the effect stopped. This
is the same technique as used by the music samples in Amiga trackers.
(Alternatively, refer to the NULL_IS_CLEARED option.)


How to use:


1. Selecting the data model

ptplayer.asm can be configured to support two data models:

a) By default ptplayer.asm assembles into a single code section. The
   player routines set up a local base register to access data.
   This requires a working RS directive, which is provided by many
   assemblers, like e.g. vasm, PhxAss, Devpac, Basm, AsmOne, SNMA.

b) By defining the SDATA symbol, ptplayer.asm assembles into a code
   section and a small-data section (called __MERGED). All player
   routines expect that the base register A4 is set up with the
   small-data base pointer (provided by the linker through _LinkerDB).
   It uses the NEAR directive, which might work with vasm and PhxAss only.


2. Selecting optional features

By default ptplayer builds the CIA-B timer version, for games/demos which
take over the hardware, with all features included:
MINIMAL=0, ENABLE_SAWRECT=1, NULL_IS_CLEARED=0, NO_TIMERS=0,
VBLANK_MUSIC=0 and OSCOMPAT=0.

a) By defining the symbol MINIMAL=1 (defaults to 0) you get just a
   standard Protracker player, without the ability to insert external
   sound effects or to control the master or sample volumes.
   This approximately halves the code size.

b) By defining the symbol ENABLE_SAWRECT=0 (defaults to 1) you may
   disable sawtooth and rectangle vibratos/tremolos. When disabled,
   they are replaced by sine-waves. These wave forms are extremely
   rare and disabling them saves another 2K for the tables.

c) By defining the symbol NULL_IS_CLEARED=1 (defaults to 0) you indicate
   that the memory locations $0 and $1 in your system are zero, so they
   will be used for idle-looping of audio channels. Might be useful when
   dynamically loading new samples.

d) Setting NO_TIMERS=1 disables the use of both CIA-B timers completely,
   which means that the player doesn't depend on level-6 EXTER interrupts
   anymore. NO_TIMERS=1 automatically includes VBLANK_MUSIC=1 (see below).
   With NO_TIMERS set, your main program has to call all music subroutines
   itself. The typical procedure in a VERTB-based main loop would be:
   1. call _mt_music
   2. wait at least 550 ticks, then call _mt_dmaon
   3. wait at least 550 ticks again, then call _mt_setrep
   The last two steps could for example be done by Copper interrupts.
   CAUTION: You should know what you do, if you use this mode! It is up
   to you to take care that calling a ptplayer function is not interrupted
   by another ptplayer function.

e) Define VBLANK_MUSIC=1 (defaults to 0) if you don't want ptplayer
   to set up a CIA-B Timer-A interrupt for music replay, which means
   you cannot set the tempo with the F-command anymore (only the speed)
   and you have to call _mt_music yourself out of your own VBlank
   interrupt handler. Also sound effects will no longer work, when no
   music is playing.
   NOTE: CIA-B Timer-B is still used for enabling Audio DMA and setting
   the loop pointers (unless you define NO_TIMERS=1). So it doesn't free
   the Level-6 interrupt vector. This is just an option if you must
   synchronize your music with your game or demo running in VBlank interrupts.

f) Define OSCOMPAT=1 (defaults to 0) to build a ptplayer-version which
   uses AmigaOS to register CIA interrupts and allocates all audio channels
   via audio.device. The default version is a little bit faster and
   smaller, but may NOT work correctly with AmigaOS alive!
   Note, that _mt_install and _mt_remove take different arguments, when
   OSCOMPAT is set.

You may change these symbols either directly in the source or override
them via the assembler's command line.


3. Common usage (standard version with CIA-B timer interrupts)

a) Install a Level-6 interrupt handler for CIA-B timer interrupts by
   calling _mt_install. Do this once, during program init.

b) For every new MOD file to play initialize it with _mt_init.
   Most common case is to pass a pointer to the MOD in A0 and set A1
   and D0 to zero, which will play the song from the beginning.
   Everything is initialized for replay now, but nothing is played.

c) Set the byte-variable _mt_Enable to non-zero to start the replay.
   Clearing that variable again pauses the song.
   VBLANK_MUSIC=1: You have to call _mt_music yourself out of a 50Hz
   interrupt. _mt_Enable is ignored in this case.

d) Stop playing and set all volumes to zero by calling _mt_end.

e) Finally, in the cleanup routine of your program, remove the Level-6
   interrupt handler again and reset all CIA registers by calling
   _mt_remove.


4. Common usage (NO_TIMERS=1)

a) For every new MOD file to play initialize it with _mt_init.
   Most common case is to pass a pointer to the MOD in A0 and set A1
   and D0 to zero, which will play the song from the beginning.
   Everything is initialized for replay now, but nothing is played.

b) As with VBLANK_MUSIC=1, you have to call _mt_music 50 times per
   second to play the MOD. For example out of a VERTB interrupt.
   Then wait for at least 550 ticks (12 raster lines) and call _mt_dmaon.
   Thereafter wait for at least another 550 ticks and call _mt_setrep.

c) Stop playing and set all volumes to zero by calling _mt_end. Also
   make sure that you don't call _mt_music, _mt_dmaon or _mt_setrep
   anymore.


Exported functions:

Note, that the leading underscore disappears when a symbol is referenced
from C (you can use ptplayer.h to include all prototypes).
CUSTOM is the Amiga custom-chip base address $dff000.

Also note that the arguments for _mt_install and _mt_remove differ,
depending on whether you have built ptplayer with OS-compatibility
enabled (define OSCOMPAT) or not. When calling the OS-compatible
ptplayer routines from C, define __OSCOMPAT before including ptplayer.h.

[OSCOMPAT=0] _mt_install(a6=CUSTOM, a0=VectorBase, d0=PALflag.b)
  Install a CIA-B interrupt for calling _mt_music or mt_sfxonly
  automatically. The music module is replayed via _mt_music when _mt_Enable
  is non-zero. Otherwise the interrupt handler calls mt_sfxonly to play
  sound effects only.
  VectorBase is 0 for 68000, otherwise set it to the CPU's VBR register.
  A non-zero PALflag selects PAL-clock for the CIA timers (NTSC otherwise).

[OSCOMPAT=1] ok = _mt_install()
  Register CIA-B interrupts with AmigaOS for calling mt_music or
  mt_sfxonly. Allocate all Paula audio channels via audio.device.
  The music module is replayed via _mt_music when _mt_Enable is non-zero.
  Otherwise the interrupt handler calls mt_sfxonly to play sound
  effects only.
  Returns true (1) on success, false (0) otherwise.
  You must not call _mt_remove(), when _mt_install() failed!

[OSCOMPAT=0] _mt_remove(a6=CUSTOM)
  Remove the  CIA-B music interrupt, restore the previous handler and
  reset the CIA timer registers to their original values.

[OSCOMPAT=1] _mt_remove()
  Unregister the CIA-B interrupts handlers and deallocate all
  audio channels.

_mt_init(a6=CUSTOM, a0=TrackerModule, a1=Samples|NULL, d0=InitialSongPos.b)
  Initialize a new module.
  Reset speed to 6, tempo to 125 and start at the given song position.
  Master volume is at 64 (maximum).
  When a1 is NULL the samples are assumed to be stored after the patterns,
  which is the usual case.

_mt_end(a6=CUSTOM)
  Stop playing the current module and sound effects.

_mt_soundfx(a6=CUSTOM, a0=SamplePointer,
            d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w)
  Request playing of an external sound effect on the most unused channel.
  This function is for compatibility with the old API only.
  You should call _mt_playfx instead. MINIMAL=0 only.

channelStatus = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer)
  Request playing of a prioritized external sound effect, either on a
  fixed channel or on the most unused one.
  Structure layout of SfxStructure:
    void *sfx_ptr  (pointer to raw sample start in Chip RAM, even address)
    WORD  sfx_len  (sample length in words)
    WORD  sfx_per  (hardware replay period for sample)
    WORD  sfx_vol  (volume 0..64, is unaffected by the song's master volume)
    BYTE  sfx_cha  (0..3 selected replay channel, -1 selects best channel)
    BYTE  sfx_pri  (priority, must be in the range 1..127)
  When multiple samples are assigned to the same channel the lower
  priority sample will be replaced. When priorities are the same, then
  the older sample is replaced.
  The chosen channel is blocked for music until the effect has
  completely been replayed.
  RETURN VALUES: A pointer to a channel-status structure (see ptplayer.h)
  when the sample is scheduled for playing, or NULL when the request was
  ignored.
  NOTE: Remember that sfx_ptr points to raw sample data (no IFF header
  or similar). And always make sure the first two bytes of your sound
  effect sample are zero! Alternatively, refer to NULL_IS_CLEARED.
  MINIMAL=0 only.

_mt_loopfx(a6=CUSTOM, a0=SfxStructurePointer)
  Request playing of a looped sound effect on a fixed channel, which
  will be blocked for music until the effect is stopped (_mt_stopfx).
  It uses the same SfxStructure as _mt_playfx, but the priority is
  ignored. A looped sound effect has always highest priority and will
  replace a previous effect on the same channel. No automatic channel
  selection is possible!
  Also make sure the sample starts with a zero-word, which is used
  for idling when the effect is stopped by _mt_stopfx. This word is
  included in the total length calculation, but excluded when actually
  playing the loop. MINIMAL=0 only.

_mt_stopfx(a6=CUSTOM, d0=Channel.b)
  Immediately stop a currently playing sound effect on a channel (0..3)
  and make it available for music, or other effects, again. This is the
  only way to stop a looped sound effect (_mt_loopfx), besides stopping
  replay completely (_mt_end). MINIMAL=0 only.

_mt_musicmask(a6=CUSTOM, d0=ChannelMask.b)
  Bits set in the mask define which specific channels are reserved
  for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3.
  Additionally a cleared bit prevents any access to the sample pointers
  of this channel.
  When calling _mt_soundfx or _mt_playfx with automatic channel selection
  (sfx_cha=-1) then these masked channels will never be picked.
  The mask defaults to 0. MINIMAL=0 only

_mt_mastervol(a6=CUSTOM, d0=MasterVolume.w)
  Set a master volume from 0 to 64 for all music channels.
  Note that the master volume does not affect the volume of external
  sound effects (which is desired). MINIMAL=0 only.

_mt_samplevol(d0=SampleNumber.w, d1=Volume.b)
  Redefine a sample's volume. May also be done while the song is playing.
  Warning: Does not check arguments for valid range! You must have done
  _mt_init before calling this function!
  The new volume is persistent. Even when the song is restarted.
  MINIMAL=0 only.

_mt_channelmask(a6=CUSTOM, d0=ChannelMask.b)
  Bits cleared in the mask define which specific channels are muted
  for music replay. Clear bit 0 for channel 0, ..., bit 3 for channel 3.
  The mask defaults to all channels unmuted (bits set) and is reset to
  this state on _mt_init and _mt_end. MINIMAL=0 only.

_mt_music(a6=CUSTOM)
  The replayer routine. Can be called from your own VERTB interrupt
  handler when VBLANK_MUSIC or NO_TIMERS is set. Is otherwise called
  automatically by Timer-A interrupts after _mt_install.

_mt_dmaon()
  NO_TIMERS=1 only!
  MUST be called ca. 550 ticks after calling _mt_music. Enables Audio
  DMA to play a new note.

_mt_setrep()
  NO_TIMERS=1 only!
  MUST be called ca. 550 ticks after calling _mt_dmaon. Sets the
  repetition pointers and lengths for looped samples.


Exported byte-sized variables:

_mt_Enable
  Set this byte to non-zero to play music, zero to pause playing.
  Note that you can still play sound effects, while music is stopped.
  It is set to 0 by _mt_install. No effect, when VBLANK_MUSIC or
  NO_TIMERS is set.

_mt_E8Trigger
  This byte reflects the value of the last E8 command.
  It is reset to 0 after _mt_init.

_mt_MusicChannels
  This byte defines the number of channels which should be dedicated
  for playing music. So sound effects will never use more
  than 4 - _mt_MusicChannels channels at once. Defaults to 0.
  MINIMAL=0 only.


There is also a header file for C compilers, called ptplayer.h.
It depends on the SDI_compiler.h header file, which implements
portable macros for defining compiler-specific register arguments.
Get it from Aminet: http://aminet.net/dev/c/SDI_headers.lha


License:

Written by Frank Wille in 2013, 2016 - 2024.
I, the copyright holder of this work, hereby release it into the public
domain. This applies worldwide.

If still in doubt, please read the included file "LICENSE".


FAQ:

- The player doesn't work. I'm hearing no music.

  A: This can have multiple reasons. Most likely is that you didn't
  call _mt_install at all or with a wrong Vector Base pointer (OSCOMPAT=0).
  The Vector Base is 0 for 68000 systems. Otherwise you *must* read
  it from the CPU's VBR register, which is only available in supervisor
  mode. Also don't forget to set _mt_Enable to true to start playing.
  The default version of ptplayer is intended for games/demos which take
  over the hardware and disable the OS. Running with the OS alive may work,
  as the OS doesn't use the CIA-B timers, but it is not recommended.
  If you want the player to use and run under AmigaOS, define OSCOMPAT=1.

- I want to run the player in VERTB interrupt.

  A: You have two options. Either define VBLANK_MUSIC=1 to free CIA-B
  Timer-A and call the music replay routine _mt_music 50 times per
  second yourself (Timer-B is still used for DMA enable and looped
  samples), or define NO_TIMERS=1 to free all timers and don't use any
  level 6 interrupts. In the last case you also have to call _mt_dmaon
  and _mt_setrep after at least 550 ticks to make music replay work.

- I am hearing a high-pitched noise in my music.

  A: Amiga players use the first two bytes of a sample for idle-looping.
  Make sure they are zeroed.
  Alternatively, assemble ptplayer with NULL_IS_CLEARED=1 and make sure
  the bytes at $0 and $1 are zero.

- I am hearing a high-pitched noise when playing an empty sample.
  My first initialized sample is looped or unused, so how can that be?

  A: Check the first two bytes of your first initialized sample.
  These bytes are used by ptplayer for all empty samples.
  Alternatively, assemble ptplayer with NULL_IS_CLEARED=1 and make sure
  the bytes at $0 and $1 are zero.

- I am hearing a high-pitched noise after playing a sound effect.

  A: The same technique is used for playing sound effects as for
  playing instrumental samples. The sound effect idles in the first
  two bytes after being played. So make sure they are zero.

- But I don't want to clear the first two bytes in sound effects.

  A: Then make sure that the bytes at address $0 and $1 are zero, and
  assemble the player with NULL_IS_CLEARED=1.

- I am hearing some clicking noise at the beginning of a sound effect.

  A: Make sure your sample pointer points to the raw sample data, and
  not to any form of file header (e.g. IFF-8SVX, etc.). When loading
  external sound effects you have to parse the file header yourself.


History:

3.0:
- Try not to break channels with sound effects, which are currently
  playing a looped sample.
- Sound effects can also be started when the music is not playing
  (_mt_Enable=0).

3.1:
- Do not trash d2/a2 in _mt_init.
- Unused samples with length 0 are played like a 1-word null-sample,
  for compatibility with other players.
- Make sure all samples start with two zero-bytes.

4.0:
- For better maintenance there is only a single source text now. The
  default is a PC-relative, single-section version, which uses a local
  base register. Small data mode can be enabled by defining SDATA.
- _mt_install also initializes the channel structures, like _mt_init
  does. So it should be possible to play sound effects without loading
  a tracker module.
- New sound effects system, which truly supports playing multiple sound
  effects per frame (e.g. stereo). It also supports priorities and
  channel selection.
- New exported function _mt_playfx which passes a pointer to an sfx
  structure, includes the new parameters like channel and priority.
  The old API through _mt_soundfx is still supported and emulated by
  _mt_playfx.
- New exported function _mt_musicmask defines which specific channels
  are dedicated for music and won't be used for sound effects.
- New exported variable _mt_MusicChannels defines a limit for the
  maximum number of channels to be used for sound effects in parallel.

5.0:
- Only few assemblers support the BASEREG directive, so I decided
  to rework the code and make it more portable. This version is tested
  with Devpac, vasm, PhxAss, Barfly-Asm, SNMA, AsmOne, AsmPro.
- All exported symbols (functions and variables) have got a leading
  underscore now, to make them directly accessible to C programs.
  So in assembler you have to call _mt_init now, while in C you can
  call mt_init.

5.1:
- Included C header file, ptplayer.h, provided by BSzili.
- Fixed bug where other level 6 interrupts could trigger sample replay.
- Eliminated relocations in the fine-tune table, by replacing pointers
  with word-offsets (asman).
- More optimizations in the Timer B interrupt handlers and made it
  PC-relative (asman).
- Include a public domain license.

5.2:
- Make it work with broken mods, which have a sample repeat length of zero.
- Never break looped samples with sound effects, except we have looped
  samples on all four channels at once!
- New variable _mt_SongEnd to automatically stop the song when having
  played the last position. Don't use it! Doesn't work perfectly yet.

5.3:
- No longer clear the first word of each sample for idle looping.
  Either we have a good Amiga tracker MOD with repeat-length one, which
  already cleared that word, or we have a PC tracker MOD with a zero
  repeat length. In the latter case the idle loop will now point to
  address $0. Make sure that the word at this address is cleared!
- Treat samples with a length of one word the same as with zero length
  as a workaround for broken PC trackers.
- Changed APTR to void* in the C headers, for better Kickstart 1.x
  OS header file compatibility.
- Fixed detection of negative fine-tuning (broken due to optimizations
  in V5.1).

6.0:
- _mt_musicmask works as documented now! Sound effects will never play
  on the masked channels. Previously it was rather a hint not to use them.
- Fixed sign-bug in tremolo/vibrato command 7xx (Antiriad/EAB).
- New function _mt_samplevol may be used to redefine a sample's volume.
- _mt_playfx now returns a pointer to the selected channel status structure
  when the sample was scheduled for playing and returns NULL when ignored.
- Wait 576 ticks for audio DMA instead of 496, which fixes issues with
  low notes on a few A1200 configurations. (No, this doesn't harm the
  player's performance, as it is a timer interrupt.)
- Defining the symbol MINIMAL lets you assemble a minimal version of
  the player, without the ability to insert sound effects and without
  master-volume or changing samples volumes.
- Improved interrupt handling, following a suggestion of Ross/EAB.
- Minor optimizations.

6.1:
- Fixed note delay command (EDx), which still played the previous note
  in some situations (Antiriad/EAB).
- Fixed sample-offset command (9) with empty note-field (h0ffman/EAB).
- _mt_mastervol must change the volumes of all channels immediately
  and shouldn't wait for the next sample being played (suggested by
  h0ffman/EAB).
- Symbol ENABLE_SAWRECT may be used to disable sawtooth and rectangle
  waveforms for vibrato and tremolo, which saves memory for their tables
  (suggested by Antiriad).
- Symbol NULL_IS_CLEARED may be used to indicate that the memory locations
  $0 and $1 in your system are zero, so they will be used for idle-looping
  of audio channels (suggested by h0ffman).
- Removed cia.i and custom.i include files and included the required
  symbols directly into the source.
- New function _mt_loopfx for playing looped sound effects (suggested by
  mcgeezer/EAB).
- New function _mt_stopfx for immediately stopping a sound effect.

6.2:
- Fixed master volume again, which must not immediately change a channel's
  volume when it is playing a sound effect (h0ffman/EAB).
- _mt_end also has to stop and reset looped sound effects. Otherwise
  channels may be blocked when starting the next mod.
- Fixed a few more channel state variables, which were not reset during
  _mt_end (e.g. from the funk and glissando command).
- New function mt_channelmask() to mute specific music channels at
  any time (Marek Duda).
- Symbol VBLANK_MUSIC skips CIA Timer-A initialization and lets you call
  _mt_music out of your own VBlank interrupt handler.

6.3:
- MINIMAL version compiles again (broken in V6.2).
- Avoid potential overflow with command 1 (portamento up).
- Muted channels by mt_channelmask() no longer touch sample pointer
  and length registers (AUDxLC, AUDxLEN) (patch by roondar).

6.4:
- Fixed channel selection logic for sound effects, which was broken in
  certain situations (all channels busy or looped samples) since V6.0,
  and selected the worst channel instead.
- E0x command didn't work! The filter was always disabled.
- Defining NO_TIMERS disables the use of CIB-B timers and interrupts
  completely. As with VBLANK_MUSIC you have to call _mt_music 50 times
  per second, followed by _mt_dmaon and _mt_setrep after at least 550 ticks
  each.
- Define OSCOMAPT to build a version which uses AmigaOS to register
  interrupts and allocate audio channels. Patch submitted by Piru.
- Renamed mt_install_cia to mt_install and mt_remove_cia to mt_remove,
  because there are now OS-compliant versions of these routines, which
  additionally do other things, like allocating audio channels.
  The old names remain as an alias for compatibility reasons.


Contents of mus/play/ptplayer.lha
PERMISSION  UID  GID    PACKED    SIZE  RATIO METHOD CRC     STAMP     NAME
---------- ----------- ------- ------- ------ ---------- ------------ ----------
[generic]                  690    1211  57.0% -lh5- 3e9d Jun 28  2018 license
[generic]                23003   95516  24.1% -lh5- 0054 May 24 18:07 ptplayer.asm
[generic]                 2986    8533  35.0% -lh5- 9137 May 23 12:09 ptplayer.h
[generic]                 9127   24105  37.9% -lh5- d748 Jun 25 12:37 ptplayer.readme
---------- ----------- ------- ------- ------ ---------- ------------ ----------
 Total         4 files   35806  129365  27.7%            Jun 26 12:53

Aminet © 1992-2024 Urban Müller and the Aminet team. Aminet contact address: <aminetaminet net>