Safe Haskell | Safe-Inferred |
---|
Master control for playing blocks.
Here's how it works:
- Find the relevant block to play.
- Deriver generates a performable score and an inverse tempo map.
- The score is preprocessed by adding the current absolute time to it and skipping notes based on the start offset.
- Create a
Transport.PlayControl
to tell players when to stop, and aTransport.ActivePlayers
to track the number of players still running. Start players for each kind of backend:player_thread
for MIDI and im-via-MIDI, one for SC if appropriate, and an audio streaming thread if play_im_direct is enabled. - The ActivePlayers and tempo map are passed to
play_monitor_thread
, which uses the tempo map to display the play position in the various blocks. It stops when it runs out of tempo map (which corresponds with running off the end of the score), or when the PlayControl goes to Stop. It's not synchronized to the play threads in any way, but of course they are both working from the same score. - A stop from the user sets
Transport.stop_player
. All the players and the play_monitor_thread are polling it and will quit. - Another thread is watching the ActivePlayers. It sent Transport.Playing to the responder when they started, and will send Transport.Stopped when they go to zero. This control's the UI's idea of whether it's playing or not. If it's playing, it still has the PlayControl, so it won't accept another play, but will accept a stop. So player threads should only exit when there's nothing left for them to cancel, so MIDI and OSC, being scheduled in advance, will hang around even after they scheduled their last message. If a player never sends Transport.player_stopped, ActivePlayers will never go to 0, and we get stuck. So they should do the stop in a finally block.
The im backend complicates things a bit. See NOTE [play-im].
repeat_at, the play speed multiplier, and the negative start adjustment also complicate things.
Synopsis
- modify_play_multiplier :: Cmd.M m => (RealTime.RealTime -> RealTime.RealTime) -> m ()
- cmd_context_stop :: Cmd.CmdT IO Bool
- cmd_stop :: Cmd.CmdT IO Cmd.Status
- stop :: [Transport.PlayControl] -> Cmd.CmdT IO ()
- stop_im :: Cmd.CmdT IO ()
- im_addr :: Cmd.M m => m (Maybe.Maybe Patch.Addr)
- has_im :: Cmd.M m => m Bool
- set_previous_play :: Cmd.M m => Text -> Cmd.CmdId Cmd.PlayArgs -> m ()
- local_block :: Cmd.M m => m Cmd.PlayArgs
- local_selection :: Cmd.M m => m Cmd.PlayArgs
- local_previous :: Cmd.M m => m Cmd.PlayArgs
- local_top :: Cmd.M m => m Cmd.PlayArgs
- local_from :: Cmd.M m => Id.BlockId -> Id.TrackId -> TrackTime -> m Cmd.PlayArgs
- root_block :: Cmd.M m => m Cmd.PlayArgs
- root_from_root_selection :: Cmd.M m => m Cmd.PlayArgs
- root_selection :: Cmd.M m => m Cmd.PlayArgs
- root_previous :: Cmd.M m => m Cmd.PlayArgs
- root_top :: Cmd.M m => m Cmd.PlayArgs
- top_of_block :: Cmd.M m => m (Id.BlockId, Id.TrackId, TrackTime)
- root_from :: Cmd.M m => Id.BlockId -> Id.TrackId -> TrackTime -> m Cmd.PlayArgs
- maybe_root_from :: Cmd.M m => Id.BlockId -> Id.TrackId -> ScoreTime -> m (Maybe.Maybe Cmd.PlayArgs)
- from_score :: Cmd.M m => Id.BlockId -> Maybe.Maybe Id.TrackId -> ScoreTime -> Maybe.Maybe ScoreTime -> m Cmd.PlayArgs
- get_realtime :: Cmd.M m => Id.BlockId -> Id.BlockId -> Maybe.Maybe Id.TrackId -> ScoreTime -> m RealTime.RealTime
- get_performance :: Cmd.M m => Id.BlockId -> m Performance
- write_logs :: Cmd.M m => Id.BlockId -> Performance -> m ()
- record_cache_stats :: Cmd.M m => [Log.Msg] -> m ()
- extract_cache_stats :: (Log.Msg -> Maybe.Maybe k) -> [Log.Msg] -> ([(Text, [k])], [(k, Int)])
- get_block_id :: Log.Msg -> Maybe.Maybe Id.BlockId
- get_track_id :: Log.Msg -> Maybe.Maybe (Id.BlockId, Id.TrackId)
- from_realtime :: Cmd.M m => Id.BlockId -> Maybe.Maybe RealTime.RealTime -> RealTime.RealTime -> m Cmd.PlayArgs
- get_adjust0 :: RealTime.RealTime -> Bool -> [LEvent.LEvent Midi.WriteMessage] -> Vector.Vector Score.Event -> RealTime.RealTime
- lookup_im_config :: Map ScoreT.Instrument UiConfig.Allocation -> Either (Maybe.Maybe Text) (Set ScoreT.Instrument, Maybe.Maybe Patch.Addr)
- im_play_msgs :: FilePath -> Id.BlockId -> Set ScoreT.Instrument -> RealTime.RealTime -> Patch.Addr -> [LEvent.LEvent Midi.WriteMessage]
- merge_midi :: [LEvent.LEvent Midi.WriteMessage] -> [LEvent.LEvent Midi.WriteMessage] -> [LEvent.LEvent Midi.WriteMessage]
- merge_until :: Ord k => (a -> k) -> [a] -> [a] -> [a]
- generate_mtc :: Maybe.Maybe Cmd.SyncConfig -> RealTime.RealTime -> [Midi.WriteMessage]
- lookup_current_performance :: Cmd.M m => Id.BlockId -> m (Maybe.Maybe Performance)
- gets :: Cmd.M m => (Cmd.PlayState -> a) -> m a
Documentation
modify_play_multiplier :: Cmd.M m => (RealTime.RealTime -> RealTime.RealTime) -> m () Source #
stop
cmd_context_stop :: Cmd.CmdT IO Bool Source #
Context sensitive stop that stops whatever is going on. First it stops realtime play, then step play, and then it just sends all notes off. If it does the last one, it returns False in case you want to go stop something else.
im_addr :: Cmd.M m => m (Maybe.Maybe Patch.Addr) Source #
play
set_previous_play :: Cmd.M m => Text -> Cmd.CmdId Cmd.PlayArgs -> m () Source #
local_block :: Cmd.M m => m Cmd.PlayArgs Source #
Play the local block from its beginning.
local_selection :: Cmd.M m => m Cmd.PlayArgs Source #
Start playing from the point selection on the local block. If the selection is a range, loop that range forever.
local_previous :: Cmd.M m => m Cmd.PlayArgs Source #
Play the current block's performance from the previous
Cmd.state_play_step
.
local_top :: Cmd.M m => m Cmd.PlayArgs Source #
Play the current block's performance from the top of the window.
local_from :: Cmd.M m => Id.BlockId -> Id.TrackId -> TrackTime -> m Cmd.PlayArgs Source #
root_block :: Cmd.M m => m Cmd.PlayArgs Source #
Play the root block from its beginning.
root_from_root_selection :: Cmd.M m => m Cmd.PlayArgs Source #
Play the root performance from the selection on the root block. This is useful to manually set a point to start playing.
root_selection :: Cmd.M m => m Cmd.PlayArgs Source #
The same as local_selection
, but use the root performance.
root_previous :: Cmd.M m => m Cmd.PlayArgs Source #
Find the previous step on the focused block, get its RealTime, and play
from the root at that RealTime. If this block isn't linked from the root,
then fall back on local_previous
.
root_top :: Cmd.M m => m Cmd.PlayArgs Source #
Like root_previous
, but play from the top of the selected block.
top_of_block :: Cmd.M m => m (Id.BlockId, Id.TrackId, TrackTime) Source #
root_from :: Cmd.M m => Id.BlockId -> Id.TrackId -> TrackTime -> m Cmd.PlayArgs Source #
maybe_root_from :: Cmd.M m => Id.BlockId -> Id.TrackId -> ScoreTime -> m (Maybe.Maybe Cmd.PlayArgs) Source #
:: Cmd.M m | |
=> Id.BlockId | |
-> Maybe.Maybe Id.TrackId | Track to play from. Since different tracks can have different tempos, a track is needed to convert to RealTime. If not given, use the first track that has tempo information. |
-> ScoreTime | Convert to RealTime and start playing from this time. |
-> Maybe.Maybe ScoreTime | |
-> m Cmd.PlayArgs |
:: Cmd.M m | |
=> Id.BlockId | Lookup realtime according to the performance of this block. |
-> Id.BlockId | Lookup realtime at the position (TrackId, ScoreTime) within this block. |
-> Maybe.Maybe Id.TrackId | |
-> ScoreTime | |
-> m RealTime.RealTime |
get_performance :: Cmd.M m => Id.BlockId -> m Performance Source #
write_logs :: Cmd.M m => Id.BlockId -> Performance -> m () Source #
record_cache_stats :: Cmd.M m => [Log.Msg] -> m () Source #
Summarize the cache stats and emit them as global status msgs.
The output looks like
~C: [34 / 6742] bid bid bid... || ~X control damage: [104] bid bid ... || ~X trock block damage: [1] bid
This means that 34 blocks were cached, totally 6742 events. 104 blocks
were not cached due to control damage, and 1 more due to track block damage.
The reasons are from find_generator_cache
. They keys are
prefixed with a tilde to make them sort last in the logview status line.
cache_stats
gives a more complete summary.
:: (Log.Msg -> Maybe.Maybe k) | |
-> [Log.Msg] | |
-> ([(Text, [k])], [(k, Int)]) | (cache misses, cache hits): ([(because, [key])], [(key, cached_vals)]) |
get_block_id :: Log.Msg -> Maybe.Maybe Id.BlockId Source #
Get block cache stats.
get_track_id :: Log.Msg -> Maybe.Maybe (Id.BlockId, Id.TrackId) Source #
Get track cache stats.
from_realtime :: Cmd.M m => Id.BlockId -> Maybe.Maybe RealTime.RealTime -> RealTime.RealTime -> m Cmd.PlayArgs Source #
Play the performance of the given block starting from the given time.
get_adjust0 :: RealTime.RealTime -> Bool -> [LEvent.LEvent Midi.WriteMessage] -> Vector.Vector Score.Event -> RealTime.RealTime Source #
lookup_im_config :: Map ScoreT.Instrument UiConfig.Allocation -> Either (Maybe.Maybe Text) (Set ScoreT.Instrument, Maybe.Maybe Patch.Addr) Source #
im_play_msgs :: FilePath -> Id.BlockId -> Set ScoreT.Instrument -> RealTime.RealTime -> Patch.Addr -> [LEvent.LEvent Midi.WriteMessage] Source #
merge_midi :: [LEvent.LEvent Midi.WriteMessage] -> [LEvent.LEvent Midi.WriteMessage] -> [LEvent.LEvent Midi.WriteMessage] Source #
Merge a finite list of notes with an infinite list of MTC.
merge_until :: Ord k => (a -> k) -> [a] -> [a] -> [a] Source #
Merge until the leftmost list runs out.
lookup_current_performance :: Cmd.M m => Id.BlockId -> m (Maybe.Maybe Performance) Source #
implementation
gets :: Cmd.M m => (Cmd.PlayState -> a) -> m a Source #