# SEQ_BPM

## Defines
#define | SLAVE_CLK_TIMEOUT_DELAY 11000
#define | TIMER_RATE_SLAVE_MODE_US 250
## Functions
s32 | SEQ_BPM_Init (u32 mode)
seq_bpm_mode_t | SEQ_BPM_ModeGet (void)
s32 | SEQ_BPM_ModeSet (seq_bpm_mode_t mode)
float | SEQ_BPM_Get (void)
float | SEQ_BPM_EffectiveGet (void)
s32 | SEQ_BPM_Set (float _bpm)
s32 | SEQ_BPM_PPQN_Get (void)
s32 | SEQ_BPM_PPQN_Set (u16 _ppqn)
u32 | SEQ_BPM_TickGet (void)
s32 | SEQ_BPM_TickSet (u32 tick)
s32 | SEQ_BPM_IsRunning (void)
seq_bpm_run_mode_t | SEQ_BPM_RunModeGet (void)
s32 | SEQ_BPM_IsMaster (void)
s32 | SEQ_BPM_CheckAutoMaster (void)
s32 | SEQ_BPM_NotifyMIDIRx (u8 midi_byte)
s32 | SEQ_BPM_Start (void)
s32 | SEQ_BPM_Cont (void)
s32 | SEQ_BPM_Stop (void)
s32 | SEQ_BPM_ChkReqStop (void)
s32 | SEQ_BPM_ChkReqStart (void)
s32 | SEQ_BPM_ChkReqCont (void)
s32 | SEQ_BPM_ChkReqClk (u32 *bpm_tick_ptr)
s32 | SEQ_BPM_ChkReqSongPos (u16 *song_pos)
u32 | SEQ_BPM_TicksFor_mS (u16 time_ms)

## Detailed Description

Some comments to the way how the bpm_tick is generated:

MASTER MODE

A timer interrupt will be triggered with an interval of: interval = (60 / (bpm * 24)) / (ppqn/24)

It increments bpm_tick, and request the clock event to the sequencer.

The sequencer can check for clock events by calling

        u32 bpm_tick_ptr; // will contain the BPM tick which has to be processed
        while( SEQ_BPM_ChkReqClk(&bpm_tick_ctr) ) {
          // got a new tick
        }

This polling scheme ensures, that no bpm_tick gets lost if the sequencer task is stalled for any reason

Since the *_ChkReq* function is a destructive call, it should only be used by the sequencer task, and not called from any other function.

Similar request flags exist for MIDI Clock Start/Stop/Continue and Song Position

Note:
  these flags have to be polled as well, otherwise clock requests will be held back

SLAVE MODE

In order to reach a higher ppqn resolution (e.g. 384 ppqn) than provided by MIDI (24 ppqn), the incoming clock has to be interpolated in ppqn/24 steps

A timer interrupt with an interval of 250 uS is used to measure the delay between two incoming MIDI clock events. The same interrupt will call the sequencer clock handler with an interval of: interval = 250 uS * measured_delay / (ppqn/24) (e.g. 384 ppqn: 16x interpolated clock)

There is a protection (-> sent_clk_ctr) which ensures, that never more that 16 internal ticks will be triggered between two F8 events to improve the robustness on BPM sweeps or jittering incoming MIDI clock

Note that instead of the timer interrupt, we could also use a HW timer to measure the delay. Problem: currently I don't know how to handle this in the MacOS based emulation...

## Define Documentation

#define SLAVE_CLK_TIMEOUT_DELAY 11000

#define TIMER_RATE_SLAVE_MODE_US 250

## Function Documentation

s32 SEQ_BPM_CheckAutoMaster | ( | void |  | ) |

BPM should be clocked as master or slave? If this function is called in Auto Mode, BPM will switch to Master Clock

Returns:
  1 if we changed from auto slave to master mode

Here is the call graph for this function:

s32 SEQ_BPM_ChkReqClk | ( | u32 * | bpm_tick_ptr | ) |

Returns the bpm_tick which is related to the clock request

Attention:
  ChkReqClk will return 0 as long as Stop/Cont/Start/SongPos haven't been checked to ensure that clocks won't be propagated as long as a position or run state change hasn't been flagged to the sequencer. Accordingly, the sequencer application has to poll all requests as shown in the examples, otherwise it will never receive a clock

Parameters:

  `[out]` | bpm_tick_ptr | a pointer to a u32 value which will get the requested bpm_tick

Returns:
  0 if no clock request (content of bpm_tick invalid)
  1 if clock has been requested (content of bpm_tick valid)

Here is the call graph for this function:

s32 SEQ_BPM_ChkReqCont | ( | void |  | ) |

Has to be called by the sequencer to check for a continue request

Returns:
  0 if no continue request
  1 if continue has been requested

Here is the call graph for this function:

s32 SEQ_BPM_ChkReqSongPos | ( | u16 * | song_pos | ) |

Returns a new song position if requested from external

Parameters:

  `[out]` | song_pos | a pointer to a u32 value which will get the new song position

Returns:
  0 if no song position request (content of song_pos invalid)
  1 if new song position has been requested (content of song_pos valid)

Here is the call graph for this function:

s32 SEQ_BPM_ChkReqStart | ( | void |  | ) |

Has to be called by the sequencer to check for a start request

Returns:
  0 if no start request
  1 if start has been requested

Here is the call graph for this function:

s32 SEQ_BPM_ChkReqStop | ( | void |  | ) |

Has to be called by the sequencer to check for a stop request

Returns:
  0 if no stop request
  1 if stop has been requested

Here is the call graph for this function:

s32 SEQ_BPM_Cont | ( | void |  | ) |

This function continues the BPM generator and forwards this event to the sequencer.

Returns:
  < 0 on errors

Here is the call graph for this function:

float SEQ_BPM_EffectiveGet | ( | void |  | ) |

Returns the effective BPM rate (Master mode: selected BPM, Slave mode: received BPM)

Returns:
  effective bpm rate as float

float SEQ_BPM_Get | ( | void |  | ) |

Queries the current BPM rate

Returns:
  bpm rate as float

s32 SEQ_BPM_Init | ( | u32 | mode | ) |

Initialisation of BPM generator

Parameters:

  `[in]` | mode | currently only mode 0 supported

Returns:
  < 0 if initialisation failed

Here is the call graph for this function:

s32 SEQ_BPM_IsMaster | ( | void |  | ) |

BPM clocked as master or slave?

Returns:
  0: BPM clocked as slave, 1: BPM clocked as master

s32 SEQ_BPM_IsRunning | ( | void |  | ) |

BPM generator running?

Returns:
  0 if generator not running
  1 if start event or clock has been received

seq_bpm_mode_t SEQ_BPM_ModeGet | ( | void |  | ) |

Queries the current run mode

Returns:
  the run mode

s32 SEQ_BPM_ModeSet | ( | seq_bpm_mode_t | mode | ) |

Sets a new run mode

Parameters:

  `[in]` | mode | the run mode

Returns:
  < 0 on errors

s32 SEQ_BPM_NotifyMIDIRx | ( | u8 | midi_byte | ) |

should be called from Direct Rx notification hook in app.c on incoming MIDI bytes see also example code

Returns:
  < 0 on errors

Here is the call graph for this function:

s32 SEQ_BPM_PPQN_Get | ( | void |  | ) |

Queries the current PPQN value

Returns:
  ppqn value

s32 SEQ_BPM_PPQN_Set | ( | u16 | _ppqn | ) |

Sets a new PPQN value

Note:
  after changing PPQN, SEQ_BPM_Set() has to be called again to take over the new value. In other words: set PPQN before BPM

Supported ppqn for slave mode: 24, 48, 96, 192 and 384

seq_bpm_run_mode_t SEQ_BPM_RunModeGet | ( | void |  | ) |

BPM generator running?

Returns:
  SEQ_BPM_RUN_MODE_Off if generator not running
  SEQ_BPM_RUN_MODE_Armed if start event has been received
  SEQ_BPM_RUN_MODE_Clocked if first clock after start has been received and bpm_tick will be incremented

s32 SEQ_BPM_Set | ( | float | _bpm | ) |

Sets a new BPM rate

Parameters:

  `[in]` | _bpm | rate as float

Here is the call graph for this function:

s32 SEQ_BPM_Start | ( | void |  | ) |

This function starts the BPM generator and forwards this event to the sequencer.

Here is the call graph for this function:

s32 SEQ_BPM_Stop | ( | void |  | ) |

This function stops the BPM generator and forwards this event to the sequencer

Returns:
  < 0 on errors

Here is the call graph for this function:

u32 SEQ_BPM_TickGet | ( | void |  | ) |

Queries the current tick value

Returns:
  BPM tick value

s32 SEQ_BPM_TickSet | ( | u32 | tick | ) |

Sets a new BPM tick value

Parameters:

  `[in]` | tick | new BPM tick value

Returns:
  < 0 on errors

u32 SEQ_BPM_TicksFor_mS | ( | u16 | time_ms | ) |

This help function returns the number of BPM ticks for a given time E.g.: SEQ_BPM_TicksFor_mS(50) returns 38 ticks @ 120 BPM 384 ppqn Regardless if BPM generator is clocked in master or slave mode

Parameters:

  `[in]` | time_ms | the time in milliseconds (e.g. 50 corresponds to 50 mS)

Returns:
  the number of BPM ticks