// This file is part of Gopher2600. // // Gopher2600 is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Gopher2600 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Gopher2600. If not, see . package tracker import ( "fmt" "github.com/jetsetilly/gopher2600/debugger/govern" "github.com/jetsetilly/gopher2600/environment" "github.com/jetsetilly/gopher2600/hardware" "github.com/jetsetilly/gopher2600/hardware/television" "github.com/jetsetilly/gopher2600/logger" "github.com/jetsetilly/gopher2600/rewind" ) const replayLabel = environment.Label("tracker_replay") // create replay emulation if it has not been created already func (tr *Tracker) createReplayEmulation(mixer television.AudioMixer) error { if tr.replayEmulation != nil { return nil } tv, err := television.NewTelevision(tr.tv.GetSpecID()) if err != nil { return fmt.Errorf("tracker: create replay emulation: %w", err) } tv.AddAudioMixer(mixer) tr.replayEmulation, err = hardware.NewVCS(replayLabel, tv, nil, nil) if err != nil { return fmt.Errorf("tracker: create replay emulation: %w", err) } tr.replayEmulation.TIA.Audio.SetTracker(tr) return nil } // Replay audio from start to end indexes // // The onEnd argument is a function to run when the replay has concluded func (tr *Tracker) Replay(start int, end int, mixer television.AudioMixer, onEnd func()) { // the replay will run even if the master emulation is running. this may // cause audible issues with the hardware audio mixing tr.createReplayEmulation(mixer) tr.crit.section.Lock() defer tr.crit.section.Unlock() startState := tr.rewind.GetState(tr.crit.Entries[start].Coords.Frame) if startState == nil { logger.Logf(logger.Allow, "tracker", "replay: can't find rewind state for frame %d", tr.crit.Entries[start].Coords.Frame) return } rewind.Plumb(tr.replayEmulation, startState, true) // get copy of end frame number so that we don't have to acquire the // criticial section in the replay emulation endFrame := tr.crit.Entries[end].Coords.Frame go func() { defer onEnd() err := tr.replayEmulation.Run(func() (govern.State, error) { if tr.replayEmulation.TV.GetCoords().Frame > endFrame { return govern.Ending, nil } return govern.Running, nil }) if err != nil { logger.Logf(logger.Allow, "tracker", "replay: %v", err.Error()) } }() }