![Image description](https://gitlab.com/torihino/roboplay/-/raw/master/roboplay.png)

# RoboPlay
RoboPlay is a multiformat music replayer for the MSX platform using the Yamaha YMF278B OPL4, PSG and SCC soundchips. MSX-DOS2 is needed.

It is written in C using [SDCC](http://sdcc.sourceforge.net/) and the [FUSION-C](https://www.msx.org/news/development/en/fusion-c-is-available) library.

Copyright © 2020 RoboSoft Inc.
This work is free. You can redistribute it and/or modify it under the
terms of the Do What The Fuck You Want To Public License, Version 2,
as published by Sam Hocevar. See the COPYING file for more details

Currently the following file formats are supported:

For OPL4:
- SOP: Note OPL3 Music Sequencer 
- D00: EdLib packed module
- DRO: DOSBox Raw OPL v0.1 and v2.0
- RAW: Rdos Adlib Capture
- MOD: Amiga Module (up to 24 channels)
- VGM: Video Game Music
- IMF: iD Software Music Format (for .WLF extension 700Hz replay is used)
- MBM: MoonBlaster 1.4 (inlcuding PSG drums, ADPCM sample support, USER and EDIT files)
- MUS: FAC SoundTracker 1.0, 2.0 and PRO (stereo replay, ADPCM sample support)
- PRO: Tyfoon Pro-Tracker (stereo enhanced replay)
- MID: Standard MIDI file (using WaveTable or FM)
- MWM: MoonBlaster for MoonSound Wave (USER and EDIT files)
- MFM: MoonBlaster for MoonSound FM (USER, EDIT and RAW files)
- RAD: Reality ADlib Tracker 1.x (stereo enhanced replay)

For SCC:
- SBM: SCC Blaffer NT (including PSG drums)
- SNG: SCC Musixx

# Installation
RoboPlay consists of the main RoboPlay application and a separate player plugin (.PLY) per supported file format.

The main application together with all player files can be placed on any folder location, just make sure all players are located in the same folder as the main application. Add the location of the main application to the search path to be able to start it from anywhere.

# Usage
To start the player, start the main application followed by optionally the name of the player file and the song file to play.

> **ROBOPLAY** [player.PLY] song.EXT    or **ROBOPLAY** name.M3U [/R]

The player name is optional, when no player is provided the file extension of the song is used for selecting which player to use
For example:
- **ROBOPLAY SOP.PLY TEST.SOP** will play the song TEST.SOP using SOP.PLY as the player.
- **ROBOPLAY TEST.SOP** will play the song TEST.SOP using SOP.PLY as the player
- **ROBOPLAY SOP.PLY TEST.RAW** will play the song TEST.RAW using SOP.PLY as the player

When a M3U file is supplied, the /R option can be used to randomize selection of the songs.

During replay use **ESC** to stop replaying. When the song contains multiple sub-songs, use **LEFT** and **RIGHT** to go to previous or next subsong. When using a M3U file use **SPACE** to go to the next song.

# Players

## ROBO_PLAYER_INTERFACE
All players use the same header in which the ROBO_PLAYER_INTERFACE is declared. This interface is used to plug-in the player into the main application

The following methods need to implemented by a player and are called from the RoboPlay framework:
>* *bool load(char** *fileName)*
>Called when a song file needs to be loaded
>* *bool update()*
>Called every interrupt cycle to play the next song event
>* *void rewind(int8_t subsong)*
>Reset the song to it's initial state and select the provided subsong (when applicable)
>* *float get_refresh();*
>Provide the frequency (in Hz) of the update calls

>* *uint8_t get_subsongs()*
> Return the number of sub-songs. 

>* *char** *get_player_info()*
> Return information about the player
>* *char** *get_title()*
> Return the title of the song
>* *char** *get_author()*
> Return the author of the song
>* *char** *get_description()*
> Return the description of the song

Next to this the interface also provides a number of methods towards the RoboPlay framework which can be used by the player:
>* *open(char** *name)*
>Open a file to read
>* *uint_16_t read(void* *buf, uint16_t nbytes)*
>Read *nbytes* bytes from the opened file. Returns the actual number of bytes read
>* *void close()*
>Close the opened file
>* *boolean exists(char* *name)*
>Check if a specific file exists

>* *uint8_t get_segment()*
>Get a new allocated memory segment. Segments are always a block of 16KB and always located on Page 2 (*0x8000 - 0xBFFF*). Returns the index number of the segment
>* *void set_segment(uint8_t segment)*
>Set a memory segment active on Page 2

>* *void update_refresh()*
>Force a get_refresh call from the framework to set a new refresh rate.

>* *void set_opl_mode(ROBO_PLAYER_OPL_MODE mode)*
>Select OPL2, OPL3 or OPL4 mode.

>* *write_opl_fm_1(uint8_t reg, uint8_t value)*
>Write value *value* to register *addr* using the first OPL port (*0xC4*). Register 2, 3 and 4 will be ignored.
>* *write_opl_fm_2(uint8_t reg, uint8_t value)*
>Write value *value* to register *reg* using the second OPL port (*0xC6*). Register 5 will be ignored.

>* *write_wave(uint8_t reg, uint8_t value)*
>Write value *value* to register *reg* using the wave OPL port (*0x7E*)
>* *write_wave_data(uint8_t value, uint16_t size)*
>Write block of data to the wave OPL data port (*0x7F*)

>* *read_opl_status()*
>Read *value* of the OPL status register
>* *read_wave_register(uint8_t reg)*
>Read *value* from register *reg* using the wave OPL port (*0x7E*)


## Makefile
When a new player is added, place it in the *players* subfolder and add the name of the player to the line starting with *PLAYERS*:
> PLAYERS = 

All players are linked together with the same assembly file (*player.s*) which provides the binary player headers as well as the entry points used for the ROBO_PLAYER_INTERFACE. 
When one or more of the mandatory interface methods are not impmented by the player this will result in link errors, acting sort of as a virtual base class.

## Memory layout
* Main application of the framework uses Page 0 (*0x000 - 0x3FFF*)
* Players are located starting from Page 1 (*0x4000 - 0x7FFF*)
* Song data can be freely located on every location in Page 1 or higher not yet in use. When more memory is needed this can be allocated using *get_segment* and set using *set_segment*. This will **always** be on Page 2.
