Skip to content

psg_mml is a sound middleware for PSG sound source.

License

Notifications You must be signed in to change notification settings

nyannkov/psg_mml

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

psg_mml

English | 日本語

psg_mml is a sound middleware for PSG sound source.

Features

  • PSG control using MML.
  • MML supports not only chord music generation commands, but also noise generation, volume control by envelope, frequency modulation, etc.
  • A single PSG can play different sets of MML simultaneously (e.g., play sound effects while background music is playing).

Examples

This is a sample program that plays "Yurikago no uta" after turning on the power.

Details

API reference

API Name Summary
psg_mml_init Initializes the specified slot.
psg_mml_deinit Terminates the specified slot.
psg_mml_load_text Loads MML into the specified slot.
psg_mml_decode Decodes the loaded MML.
psg_mml_periodic_control Controls PSG by reading the decoding information of the loaded MML.
psg_mml_play_start Start playing the loaded MML.
psg_mml_play_restart Restarts the loaded MML from the beginning.
psg_mml_play_stop Stop playing the loaded MML.
psg_mml_get_play_state Gets the play state of the loaded MML.

psg_mml_init

Initializes the specified slot.

About slots

This middleware controls PSG by registering MML in objects called slots. There are two slots, which are numbered 0 and 1 respectively. Slot 0 is usually used to simply play a melody. For the behavior when using two slots, see the description of the setting value PSG_MML_SHARE_SLOT0_DRIVER.

psg_mml_t psg_mml_init(
    uint8_t slot
  , uint16_t tick_hz
  , void (*p_write)(uint8_t addr, uint8_t data)
  );
Parameters Description
slot Specifies the slot number to initialize, in the range of 0 to 1.
tick_hz Specifies the call rate of the function psg_mml_periodic_control(). The unit is Hz. The reciprocal of this value is the performance time resolution.
p_write Specifies a pointer to a function that writes data to the PSG. Note that the write function is executed within psg_mml_init(), so the PSG must be initialized before executing this function.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.

NOTE: Periodic execution of the function psg_mml_periodic_control() must be stopped while this function is running.

psg_mml_deinit

Terminates the specified slot.

NOTE: Periodic execution of the function psg_mml_periodic_control() must be stopped while this function is running.

psg_mml_t psg_mml_deinit(
    uint8_t slot
    );
Parameters Description
slot Specify the slot number to perform termination processing in the range of 0 to 1.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.

psg_mml_periodic_control

Acquires MML decoding information from FIFO and controls PSG based on that information. This function should be called periodically at the rate of tick_hz after executing the initialization function psg_mml_init().

void psg_mml_periodic_control(void); 

NOTE: Other psg_mml functions must not be executed while psg_mml_periodic_control() is being executed.

psg_mml_load_text

Load the MML to play.

psg_mml_t  psg_mml_load_text(
      uint8_t slot
    , const char *p_mml_text
    , uint16_t flags
    );
Parameters Description
slot Specify the slot number from 0 to 1 to load the MML.
p_mml_text Specifies a pointer to the MML text to load.
flags Specifies options for MML decoding.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.
PSG_MML_NOT_INITIALIZED The specified slot has not been initialized.
PSG_MML_TEXT_NULL The pointer to MML text is NULL.
PSG_MML_TEXT_EMPTY The MML text is empty.
PSG_MML_TEXT_OVER_LENGTH The MML text exceeds the readable size.

About flags

bit Field Description
15-1 - Reserved.
0 RH_LEN Switches the default value when the R,H command omits the note length specification.
- 0: The default value for note length is 4.
- 1: The default note length is the value specified with the L command.

psg_mml_decode

Decode the loaded MML. The decoded result is sent to psg_mml_periodic_control() through FIFO. This function does not decode the MML at once, but decodes the MML of each channel one instruction at a time and then returns the result. Since the position of the MML text to be decoded next is held in the slot, MML decoding can be performed one by one by repeatedly executing this function. After decoding all MML, this function returns PSG_MML_DECODE_END.

psg_mml_t  psg_mml_decode(
    uint8_t slot
    );
Parameters Description
slot Specifies the slot number from 0 to 1 in which to decode MML.
Return values Conditions
PSG_MML_SUCCESS Decoding of loaded MML is partially complete.
PSG_MML_DECODE_END All decoding of loaded MML is complete.
PSG_MML_DECODE_QUEUE_IS_FULL No room in the FIFO, MML decoding was not performed.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.
PSG_MML_NOT_INITIALIZED The specified slot has not been initialized.
PSG_MML_INTERNAL_ERROR An internal error occurred.

psg_mml_play_start

Starts playing the loaded MML.

psg_mml_t  psg_mml_play_start(
      uint8_t slot
    , uint16_t num_predecode
    );
Parameters Description
slot Specifies the slot number from 0 to 1 where the performance will start.
num_predecode Specifies the number of times to decode before starting the performance. Adjust this value if the performance is interrupted immediately after this function is executed.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.
PSG_MML_NOT_INITIALIZED The specified slot has not been initialized.
PSG_MML_INTERNAL_ERROR n internal error occurred.

psg_mml_play_restart

Play the loaded MML again from the beginning.

psg_mml_t  psg_mml_play_restart(
      uint8_t slot
    , uint16_t num_predecode
    );
Parameters Description
slot Specifies the slot number from 0 to 1 where the performance will start.
num_predecode Specifies the number of times to decode before starting the performance. Adjust this value if the performance is interrupted immediately after this function is executed.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.
PSG_MML_NOT_INITIALIZED The specified slot has not been initialized.
PSG_MML_INTERNAL_ERROR n internal error occurred.

psg_mml_play_stop

Stops MML performance.

psg_mml_t  psg_mml_play_stop(
    uint8_t slot
    );
Parameters Description
slot Specifies the slot number in the range of 0 to 1 where the performance will stop.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.
PSG_MML_NOT_INITIALIZED The specified slot has not been initialized.

psg_mml_get_play_state

Gets the playing status of MML. The playing state is stored in the variable pointed to by parameter p_out.

psg_mml_t  psg_mml_get_play_state(
      uint8_t slot
    , PSG_MML_PLAY_STATE_t *p_out
    );
Parameters Description
slot Specify the slot number from 0 to 1 to get the playing status.
p_out Specifies a pointer to a variable that stores the current playing state.
Return values Conditions
PSG_MML_SUCCESS The function completed successfully.
PSG_MML_OUT_OF_SLOT An out-of-range slot is specified.
PSG_MML_NOT_INITIALIZED The specified slot has not been initialized.

PSG_MML_PLAY_STATE_t

Enumerator Description
E_PSG_MML_PLAY_STATE_STOP Not playing.
E_PSG_MML_PLAY_STATE_PLAY Playing.
E_PSG_MML_PLAY_STATE_END Finished playing.

Configuration

The following configuration constants and macros can be written in psg_mml_conf.h. psg_mml_conf_template.h is provided as a template for this header. Please edit the contents of this template according to your environment and rename the file name to psg_mml_conf.h. Each setting value and macro are described here.

PSG_MML_SLOT_TOTAL_NUM

Sets the total number of slots to be implemented. The value can be selected from 1 or 2. The default for this constant is 2.

PSG_MML_FIFO_SCALE

You can change the length of the FIFO that stores the MML decoding information. The default for this constant is 8. The FIFO of each music channel can contain at least the number of notes and rests specified by this value.

PSG_MML_SHARE_SLOT0_DRIVER

NOTE: This setting value is valid only when PSG_MML_SLOT_TOTAL_NUM is 2.

Sets the behavior of slots 0 and 1 as true/false. The default for this constant is true. The behavior of slots 0 and 1 for each value is as follows

true:

Slots 0 and 1 are played in conjunction. If a single PSG is shared by slots 0 and 1, the build is performed with this setting value. With this setting, the three PSG music channels (channels A, B and C) are preferentially assigned to slot 1 for playing. For example, if you are playing background music with 3 chords in slot 0 and want to generate a single note sound effect in slot 1, you would assign music channel C to slot 1 and play the rest of the music channels A, B, and C in slot 0. The remaining music channels A and B are assigned to slot 0 (temporarily, slot 0 is assigned to 2 chords, and slot 1 is assigned to 2 chords). The MML for each comma-separated part is assigned to music channels A, B, and C in order from left to right in slot 0. Conversely, in slot 1, the MML for each part separated by commas is assigned to music channels C, B, and A. Therefore, when playing background music in slot 0, it is recommended to write the parts that can be temporarily silenced on the right side of the comma delimiter.

false:

Slots 0 and 1 are not linked and are played individually. Select this mode if you want to assign PSGs to slots 0 and 1 individually.

PSG_MML_USE_TP_TABLE

This setting selects how tp is determined from the note number. The default is 1, which refers to the tp_table when the PSG system clock is 2 MHz.

Setting this value to 2 will refer to the tp_table defined by the user. Set this value when the system clock of the PSG used is not 2 MHz and another tp_table needs to be referenced. The tp_table is referenced via psg_mml_usr_tp_table.h. The template for this file is template/psg_mml_usr_tp_table_template.h.

If this value is set to 0, tp will not be determined by tp_table, and tp will be calculated from the note number and PSG system frequency. The system frequency of PSG is referred to the value of the constant PSG_MML_FSCLOCK_001HZ.

PSG_MML_FSCLOCK_001HZ

Specifies the system frequency of PSG. The default is 200,000,000 (=2 MHz, unit: 0.01 Hz). This value is referenced only when the value of PSG_MML_USE_TP_TABLE is 0.

PSG_MML_DISABLE_PERIODIC_CONTROL()

This macro is executed when entering the section where the start of execution of psg_mml_periodic_control() is prohibited. If the execution of psg_mml_periodic_control() is running in a different thread than other functions, it is necessary to code processing to suppress the start of execution in this macro. The default for this macro is empty.

Note that the function that executes this macro ends after executing the macro PSG_MML_ENABLE_PERIODIC_CONTROL() described later. Therefore, it does not continue to suppress the psg_mml_periodic_control() function after the function processing ends.

PSG_MML_ENABLE_PERIODIC_CONTROL()

This macro is executed when exiting the section that prohibits the start of execution of psg_mml_periodic_control(). The default for this macro is empty. If the suppression processing is written in the macro PSG_MML_DISABLE_PERIODIC_CONTROL(), it is necessary to write the suppression release processing in this macro.

PSG_MML_HOOK_START_PERIODIC_CONTROL()

This macro function is executed when the psg_mml_periodic_control() function starts executing. The default for this macro function is empty.

PSG_MML_HOOK_END_PERIODIC_CONTROL()

This macro function is executed at the end of execution of the psg_mml_periodic_control() function. The default for this macro function is empty.

PSG_MML_ASSERT(TF)

Macro for debugging. The default for this macro is empty. This macro runs on conditional validation within a local function.

MML instructions

This section describes each MML command used in psg_mml.

Command name Summary
A-G Outputs the sound of the specified note name.
N Specify the note number and output the sound.
R Insert a rest.
T Sets the tempo.
L Sets the length of the note.
V Sets the volume.
S Switch to envelope generator volume control.
M Specifies the envelope frequency.
O Specifies the octave of the note specified by A-G.
Q Specify the note gate time.
H Outputs noise sound.
I Sets the frequency of the noise.
< Decrease the octave value set by O-command.
> Increase the octave value set by O-command.
, MML is concatenated into a chord.
& Plays like slurs and ties.
[] Specify the loop section.
$E Set ON/OFF of volume control by software envelope.
$A Specifies the attack time of the software envelope.
$H Specifies the hold time of the software envelope.
$D Specifies the software envelope decay time.
$S Specifies the sustain time of the software envelope.
$F Specifies the fade time of the software envelope.
$R Specifies the release time of the software envelope.
$M Sets the software LFO mode.
$J Specifies the modulation depth of the software LFO.
$L Sets the modulation frequency of the software LFO.
$T Specifies the time from the start of sound output until the software LFO operates.
$B Bias the frequency of the output sound.
$P Smoothly increases or decreases the frequency of the output sound until output stops.

Basic command

A-G [<accidental>] [<length>] [<dot>]

Outputs the sound of the specified note name.

Values Description
<accidental> Specify the accidental symbols with "#", "+", and "-". "#" and "+" correspond to semitone up, and "-" correspond to semitone down.
<length> Specify the length of the sound in the range from 0 to 64. 1 represents a whole note, 4 represents a quarter note. If you omit <length>, the note length will be the value specified by the L command. It is also possible to specify 0 as the length of the note. In this case, no sound is generated. This value is primarily used for the final tone of a slur (&) string.
<dot> Specify the dot with ".". If you write one dot, the length of the sound will be 1.5 times (=1+0.5). If you write two, the length of the sound will be 1.75 times (= 1 + 0.5 + 0.25). Up to 10 dots can be described.

Example:

C#2.

N <note-number> [<dot>]

Specify the note number and output the sound. The note length follows the value specified with the L command.

Values Description
<note-number> Specify the note number value in the range from 0 to 95. For example, N36 corresponds to O4C and N52 corresponds to O5E. However, N0 is treated as a rest, not O1C.
<dot> Specifies a dot. The dot effect is the same as for A-G.

Example:

L4O4CEG.R8 N36N40N43.R8

R [<length>] [<dot>]

Insert a rest.

Values Description
<length> Specify the length of the rest in the range of 1 to 64. 1 represents a whole rest, 4 represents a quarter note rest. If this value is omitted, the rest length is determined by the value of the argument flags of the function psg_mml_load_mml() as follows:
- If the 0th bit of flags is 0, the rest length is 4 (quarter rest).
- If the 0th bit of flags is 1, the length of the rest will be the value specified by the L command.
<dot> Specifies a dot. The dot effect is the same as for A-G.

Example:

L4
CR8 C16R16 CR
CR8 C16R16 CR

H [<length>] [<dot>]

Outputs a noise sound with the frequency set by the I command.

Values Description
<length> Specifies the length of the noise in the range 1 to 64. If this value is omitted, the length of the noise is determined by the value of the argument flags of the function psg_mml_load_mml(), just like the R command.
<dot> Specifies a dot. The dot effect is the same as for A-G.

Example:

HR8H16R16HR HR8H16R16HR

T <tempo>

Sets the tempo. Tempo defaults to 120 bpm.

Values Description
<tempo> Specify a tempo value in the range 32 to 255. When performing using multiple tone channels, tempo settings must be written in the MML for each channel.

Example:

T120CDE2T100CDE2,
T120EGB2T100EGB2

L <length> [<dot>]

Sets the length of the sound when <length> is omitted in the A-G command. This value defaults to 4. This value can also be applied to the R and H commands depending on the value of the argument flags of the psg_mml_load_mml() function.

Values Description
<length> Specify the length of the sound from 1-64. 1 represents a whole note, 4 represents a quarter note.
<dot> Specifies a dot. The dot effect is the same as for A-G.

Example:

L4CDE2L16CDECDECDE

V <volume>

Sets the volume of tones and noises. Volume defaults to 15. Note that the V command and S command operate exclusively, and the volume setting follows the command executed later.

Values Description
<volume> Specifies the volume from 0 to 15.

Example:

V15A V13A V10A

S <shape>

Switch to volume control with an envelope generator. This value defaults to OFF. Executing this command turns on the volume control by the envelope generator. If you want to turn off this volume control, execute the V command.

Values Description
<shape> Specifies the shape of the envelope in the range 0-15. Please refer to the PSG datasheet for the shape of the envelope for each value.

Example:

V15CDER4
L4S0M3000CDER4
V15CDER4

M <frequency>

Specifies the envelope frequency. Envelope frequency defaults to 0.

Values Description
<frequency> Specifies the envelope frequency (EP) in the range 0 to 65535.

Example:

L4
S0M5000CDER4
S0M1000CDER4

O <octave>

Specifies the octave of the note specified by A-G. Octave defaults to 4.

Values Description
<octave> Specify an octave in the range 1 to 8.

Example:

L4
O4 CDE2
O5 CDE2
O3 CDE2

Q <gate-time>

Specifies the note gate time. The default value is 8 (8/8=100%).

Values Description
<gate-time> Specify the gate time in the range of 1 to 8. For example, if the gate time is set to 3, the note duration will be 3/8 and the remaining 5/8 will be muted.

Example:

L16CCCCCCCC
R4
Q4CCCCCCCC

I <frequency>

Sets the noise frequency (NP) generated by the H command. The noise frequency defaults to 16.

Values Description
<frequency> Specifies the noise frequency in the range 0 to 31.

Example:

I0H4 I8H4

<(LT)

Lowers the octave specified by the O command.

Example:

L4O5C<BA

>(GT)

Raises the octave specified by the O command.

Example:

L4AB>C

,(COMMA)

MML of each part can be concatenated with commas. Since PSG has 3 tone channels, up to 3 MMLs can be concatenated.

Example:

T120L4O4CEG,
T120L4O4EGB,
T120L4O4GB>D

& [<octave-settings>]

By connecting A-G commands with &, you can perform slurs and ties.

Values Description
<octave-settings> You can use the O command, > and < to set the octave. The octave value set here will continue even after the performance of slurs and ties.

Example:

A2R2 A4&A4R2 A2&>A0R2 A2&<A0R2

[ [<loop-number>] ... ]

Loop playback of MML in []. Loops can be nested up to 5 levels. Loop symbols after the 6th row are ignored.

Values Description
<loop-number> Specify the loop count in the range from 0 to 255. However, if 0 is specified, it will be an infinite loop. If this value is omitted, the loop count will be 1.

Example:

[3
  [2
    L4CDER4
  ]
  L4GEDCDEDR4
]

Software envelope generator

In addition to PSG's built-in envelope generator, psg_mml also supports volume control using software envelopes. This feature can only be enabled if the volume is controlled with the V command rather than the S command. The shape of the envelope is set with 6 parameters (AHDSFR method). This envelope volume control can be set independently for each channel.

$E <enabled>

Sets ON/OFF of volume control by software envelope. This value defaults to OFF.

Values Description
<enabled> Set in the range from 0 to 1. 0 corresponds to OFF and 1 to ON.

Example:

V15L4O4
$A0$H100$D100$S90$F2000$R0

$E0
CDE2

$E1
CDE2

$A <attack>

Specifies the rise time of the sound (the arrival time from 0 to the volume set by the V command).

Values Description
<attack> Specify in the range from 0 to 10000. The unit is ms. If 0 is specified, sound rise processing is not performed, and sound is output at the volume set with the V command.

$H <hold>

Sets the retention time of the volume level after attack.

Values Description
<hold> Specify in the range from 0 to 10000. The unit is ms. If 0 is specified, Decay processing will start immediately.

$D <decay>

Sets the time it takes for the volume to reach the Sustain value after Hold.

Values Description
<decay> Specify in the range from 0 to 10000. The unit is ms. If 0 is specified, the volume is immediately set to the Sustain value and fade processing is started.

$S <sustain>

Sets the target volume level for Decay processing.

Values Description
<sustain> Specify in the range from 0 to 100. The unit is %. Corresponds to the percentage of the volume specified by the V command.

$F <fade>

Specifies the time from the Sustain value to 0 after the Decay process is complete.

Values Description
<fade> Specify in the range from 0 to 10000. The unit is ms. However, if 0 is specified, the volume will keep the Sustain value.

$R <release>

Specifies the time to change the output volume from the current value to 0 after note-off.

Values Description
<release> Specify in the range from 0 to 10000. The unit is ms. However, if 0 is specified, the volume is immediately set to 0.

Software LFO

psg_mml has a software LFO. By enabling this function, you can output a sound with vibrato.

$M <mode>

Sets ON/OFF for the software LFO. This value defaults to OFF.

Values Description
<mode> Sets the software LFO mode from 0 to 1. 0 corresponds to OFF and 1 to ON (modulated by triangular wave).

Example:

V15L4O4
$J4$L80$T8

$M0
CDE2

$M1
CDE2

$J <depth>

Sets the modulation depth. Modulation depth defaults to 0.

Values Description
<depth> Specifies the modulation depth in the range 0 to 360. If the modulation depth is n, the frequency of the sound varies from 2^(-n/360) times to 2^(n/360) times depending on the modulation function.

$L <low-frequency>

Sets the modulation frequency. The default modulation frequency is 40 ( = 4.0 Hz).

Values Description
<low-frequency> Specifies the modulation frequency in the range 0 to 200. The unit is 0.1 Hz.

$T <delay> [<dot>]

Specifies the time from the start of sound output until the LFO operates. This value defaults to 0.

Values Description
<delay> Specifies the delay time of the LFO in the range from 0 to 64. If delay is non-zero, it uses the same calculations as the L command to determine the delay time. For example, if the delay is set to 4, the LFO operation will start after the time of one quarter note has passed after the start of sounding. If delay is 0, the delay time will be 0.
<dot> Specifies a dot. The dot effect is the same as for A-G.

Other commands

$B <bias>

Bias the frequency of the output sound. The default value for bias is 0.

Values Description
<bias> Specify the bias in the range (-2880) to 2880. If the bias value is n, the frequency of the output sound will be the value multiplied by 2^(n/360).

Example1:

V15L4O4
CDE2
$B30 CDE2
$B60 CDE2
$B360 CDE2

Example2:

V15L1O4
AA
,
V15L1O4
A$B1A

$P <pitchbend-level>

Smoothly increases or decreases the frequency of the output sound to the value specified by pitchbend-level until the output stops. This value defaults to 0.

Values Description
<pitchbend-level> Specifies the pitch bend level in the range from (-2880) to 2880. If the specified value is n, the frequency of the output sound changes smoothly up to the final value multiplied by 2^(n/360).

Example:

V15L16O4
$P-360 CDEFGAB>C<
$P360 CDEFGAB>C<

License

MIT License.