/** Move the current position to the location clicked. * The music must be in the paused/stopped state. * When we resume in playPause, we start at the currentPulseTime. * So, set the currentPulseTime to the position clicked. */ private void MoveToClicked(object sender, MouseEventArgs evt) { if (midifile == null || sheet == null) { return; } if (playstate != MidiPlayState.Paused && playstate != MidiPlayState.Stopped) { return; } playstate = MidiPlayState.Paused; /* Remove any highlighted notes */ lock (sheet) { sheet.ShadeNotes(-10, (int)currentPulseTime, false); piano.ShadeNotes(-10, (int)currentPulseTime); currentPulseTime = sheet.PulseTimeForPoint(evt.Location); prevPulseTime = currentPulseTime - midifile.Time.Measure; if (currentPulseTime > midifile.TotalPulses) { currentPulseTime -= midifile.Time.Measure; } sheet.ShadeNotes((int)currentPulseTime, (int)prevPulseTime, false); piano.ShadeNotes((int)currentPulseTime, (int)prevPulseTime); } }
/** Fast forward the midi music by one measure. * The music must be in the paused/stopped state. * When we resume in playPause, we start at the currentPulseTime. * So to fast forward, just increase the currentPulseTime, * and re-shade the sheet music. */ private void FastForward(object sender, EventArgs evt) { if (midifile == null || sheet == null) { return; } if (playstate != MidiPlayState.Paused && playstate != MidiPlayState.Stopped) { return; } playstate = MidiPlayState.Paused; /* Remove any highlighted notes */ sheet.ShadeNotes(-10, (int)currentPulseTime, false); piano.ShadeNotes(-10, (int)currentPulseTime); prevPulseTime = currentPulseTime; currentPulseTime += midifile.Time.Measure; if (currentPulseTime > midifile.TotalPulses) { currentPulseTime -= midifile.Time.Measure; } sheet.ShadeNotes((int)currentPulseTime, (int)prevPulseTime, false); piano.ShadeNotes((int)currentPulseTime, (int)prevPulseTime); }
/** The "Play Measures in a Loop" feature is enabled, and we've reached * the last measure. Stop the sound, unshade the music, and then * start playing again. */ private void RestartPlayMeasuresInLoop() { timer.Stop(); playstate = MidiPlayState.Stopped; StopSound(); sheet.ShadeNotes(-10, (int)prevPulseTime, false); piano.ShadeNotes(-10, (int)prevPulseTime); currentPulseTime = -1; prevPulseTime = 0; //System.Threading.Thread.Sleep(300); PlayPause(null, null); }
/** Perform the actual stop, by stopping the sound, * removing any shading, and clearing the state. */ void DoStop() { playstate = MidiPlayState.Stopped; StopSound(); // Remove all shading by redrawing the music // JV: set shading instead of invalidating, removes flicker sheet.ShadeNotes(-10, (int)currentPulseTime, false); piano.ShadeNotes(-10, (int)currentPulseTime); startPulseTime = 0; currentPulseTime = 0; prevPulseTime = 0; playButton.Image = playImage; playTip.SetToolTip(playButton, "Play"); return; }
/** The callback for the Stop button. * If playing, initiate a stop and wait for the timer to finish. * Then do the actual stop. */ public void Stop(object sender, EventArgs args) { if (midifile == null || sheet == null || playstate == MidiPlayState.Stopped) { return; } if (playstate == MidiPlayState.Pausing || playstate == MidiPlayState.Stopping || playstate == MidiPlayState.Playing) { /* Wait for timer to finish */ playstate = MidiPlayState.Stopping; System.Threading.Thread.Sleep(300); DoStop(); } else if (playstate == MidiPlayState.Paused) { DoStop(); } }
/** The callback for the timer. If the midi is still playing, * update the currentPulseTime and shade the sheet music. * If a stop or pause has been initiated (by someone clicking * the stop or pause button), then stop the timer. */ void TimerCallback(object sender, EventArgs args) { if (midifile == null || sheet == null) { timer.Stop(); playstate = MidiPlayState.Stopped; return; } else if (playstate == MidiPlayState.Stopped || playstate == MidiPlayState.Paused) { /* This case should never happen */ timer.Stop(); return; } else if (playstate == MidiPlayState.Stopping) { timer.Stop(); return; } else if (playstate == MidiPlayState.Playing) { TimeSpan diff = DateTime.Now.TimeOfDay.Subtract(startTime); long msec = diff.Minutes * 60 * 1000 + diff.Seconds * 1000 + diff.Milliseconds; prevPulseTime = currentPulseTime; currentPulseTime = startPulseTime + msec * pulsesPerMsec; /* If we're playing in a loop, stop and restart */ if (options.playMeasuresInLoop) { double nearEndTime = currentPulseTime + pulsesPerMsec * 10; int measure = (int)(nearEndTime / midifile.Time.Measure); if (measure > options.playMeasuresInLoopEnd) { RestartPlayMeasuresInLoop(); return; } } /* Stop if we've reached the end of the song */ if (currentPulseTime > midifile.TotalPulses) { timer.Stop(); DoStop(); return; } sheet.ShadeNotes((int)currentPulseTime, (int)prevPulseTime, true); piano.ShadeNotes((int)currentPulseTime, (int)prevPulseTime); return; } else if (playstate == MidiPlayState.Pausing) { timer.Stop(); TimeSpan diff = DateTime.Now.TimeOfDay.Subtract(startTime); long msec = diff.Minutes * 60 * 1000 + diff.Seconds * 1000 + diff.Milliseconds; StopSound(); prevPulseTime = currentPulseTime; currentPulseTime = startPulseTime + msec * pulsesPerMsec; sheet.ShadeNotes((int)currentPulseTime, (int)prevPulseTime, false); piano.ShadeNotes((int)currentPulseTime, (int)prevPulseTime); playstate = MidiPlayState.Paused; playButton.Image = playImage; playTip.SetToolTip(playButton, "Play"); return; } }
/** The callback for the play/pause button (a single button). * If we're stopped or pause, then play the midi file. * If we're currently playing, then initiate a pause. * (The actual pause is done when the timer is invoked). */ private void PlayPause(object sender, EventArgs args) { if (midifile == null || sheet == null || numberTracks() == 0) { return; } else if (playstate == MidiPlayState.Stopping || playstate == MidiPlayState.Pausing) { return; } else if (playstate == MidiPlayState.Playing) { playstate = MidiPlayState.Pausing; return; } else if (playstate == MidiPlayState.Stopped || playstate == MidiPlayState.Paused) { /* The startPulseTime is the pulse time of the midi file when * we first start playing the music. It's used during shading. */ if (options.playMeasuresInLoop) { /* If we're playing measures in a loop, make sure the * currentPulseTime is somewhere inside the loop measures. */ int measure = (int)(currentPulseTime / midifile.Time.Measure); if ((measure < options.playMeasuresInLoopStart) || (measure > options.playMeasuresInLoopEnd)) { currentPulseTime = options.playMeasuresInLoopStart * midifile.Time.Measure; } startPulseTime = currentPulseTime; options.pauseTime = (int)(currentPulseTime - options.shifttime); } else if (playstate == MidiPlayState.Paused) { startPulseTime = currentPulseTime; options.pauseTime = (int)(currentPulseTime - options.shifttime); } else { options.pauseTime = 0; startPulseTime = options.shifttime; currentPulseTime = options.shifttime; prevPulseTime = options.shifttime - midifile.Time.Quarter; } if (Type.GetType("Mono.Runtime") != null) { SkipLeadingSilence(); } CreateMidiFile(); playstate = MidiPlayState.Playing; Volume.SetVolume(volumeBar.Value); PlaySound(tempSoundFile); startTime = DateTime.Now.TimeOfDay; timer.Start(); playButton.Image = pauseImage; playTip.SetToolTip(playButton, "Pause"); sheet.ShadeNotes((int)currentPulseTime, (int)prevPulseTime, true); piano.ShadeNotes((int)currentPulseTime, (int)prevPulseTime); return; } }
/** Create a new MidiPlayer, displaying the play/stop buttons, the * speed bar, and volume bar. The midifile and sheetmusic are initially null. */ public MidiPlayer() { this.Font = new Font("Arial", 10, FontStyle.Bold); loadButtonImages(); int buttonheight = this.Font.Height * 2; this.midifile = null; this.options = null; this.sheet = null; playstate = MidiPlayState.Stopped; startTime = DateTime.Now.TimeOfDay; startPulseTime = 0; currentPulseTime = 0; prevPulseTime = -10; errormsg = new StringBuilder(256); ToolTip tip; /* Create the rewind button */ rewindButton = new Button(); rewindButton.Parent = this; rewindButton.Image = rewindImage; rewindButton.ImageAlign = ContentAlignment.MiddleCenter; rewindButton.Size = new Size(buttonheight, buttonheight); rewindButton.Location = new Point(buttonheight / 2, buttonheight / 2); rewindButton.Click += new EventHandler(Rewind); tip = new ToolTip(); tip.SetToolTip(rewindButton, Strings.rewind); /* Create the play button */ playButton = new Button(); playButton.Parent = this; playButton.Image = playImage; playButton.ImageAlign = ContentAlignment.MiddleCenter; playButton.Size = new Size(buttonheight, buttonheight); playButton.Location = new Point(buttonheight / 2, buttonheight / 2); playButton.Location = new Point(rewindButton.Location.X + rewindButton.Width + buttonheight / 2, rewindButton.Location.Y); playButton.Click += new EventHandler(PlayPause); playTip = new ToolTip(); playTip.SetToolTip(playButton, Strings.play); /* Create the stop button */ stopButton = new Button(); stopButton.Parent = this; stopButton.Image = stopImage; stopButton.ImageAlign = ContentAlignment.MiddleCenter; stopButton.Size = new Size(buttonheight, buttonheight); stopButton.Location = new Point(playButton.Location.X + playButton.Width + buttonheight / 2, playButton.Location.Y); stopButton.Click += new EventHandler(Stop); tip = new ToolTip(); tip.SetToolTip(stopButton, Strings.stop); /* Create the fastFwd button */ fastFwdButton = new Button(); fastFwdButton.Parent = this; fastFwdButton.Image = fastFwdImage; fastFwdButton.ImageAlign = ContentAlignment.MiddleCenter; fastFwdButton.Size = new Size(buttonheight, buttonheight); fastFwdButton.Location = new Point(stopButton.Location.X + stopButton.Width + buttonheight / 2, stopButton.Location.Y); fastFwdButton.Click += new EventHandler(FastForward); tip = new ToolTip(); tip.SetToolTip(fastFwdButton, Strings.fastForward); /* Create the Speed bar */ speedLabel = new Label(); speedLabel.Parent = this; speedLabel.Text = Strings.speed + " : 100%"; speedLabel.TextAlign = ContentAlignment.MiddleLeft; speedLabel.Height = buttonheight; speedLabel.Width = buttonheight * 3; speedLabel.Location = new Point(fastFwdButton.Location.X + fastFwdButton.Width + buttonheight / 2, fastFwdButton.Location.Y); speedBar = new TrackBar(); speedBar.Parent = this; speedBar.Minimum = 1; speedBar.Maximum = 150; speedBar.TickFrequency = 10; speedBar.TickStyle = TickStyle.BottomRight; speedBar.LargeChange = 10; speedBar.Value = 100; speedBar.Width = buttonheight * 6; speedBar.Location = new Point(speedLabel.Location.X + speedLabel.Width + 2, speedLabel.Location.Y); speedBar.Scroll += new EventHandler(SpeedBarChanged); tip = new ToolTip(); tip.SetToolTip(speedBar, Strings.speed); /* Create the Volume bar */ Label volumeLabel = new Label(); volumeLabel.Parent = this; volumeLabel.Image = volumeImage; volumeLabel.ImageAlign = ContentAlignment.MiddleRight; volumeLabel.Height = buttonheight; volumeLabel.Width = buttonheight * 2; volumeLabel.Location = new Point(speedBar.Location.X + speedBar.Width + buttonheight / 2, speedBar.Location.Y); volumeBar = new TrackBar(); volumeBar.Parent = this; volumeBar.Minimum = 1; volumeBar.Maximum = 100; volumeBar.TickFrequency = 10; volumeBar.TickStyle = TickStyle.BottomRight; volumeBar.LargeChange = 10; volumeBar.Value = Volume.GetVolume(); volumeBar.Width = buttonheight * 6; volumeBar.Location = new Point(volumeLabel.Location.X + volumeLabel.Width + 2, volumeLabel.Location.Y); volumeBar.Scroll += new EventHandler(ChangeVolume); tip = new ToolTip(); tip.SetToolTip(volumeBar, Strings.volume); Height = buttonheight * 2; /* Initialize the timer used for playback, but don't start * the timer yet (enabled = false). */ timer = new Timer(); timer.Enabled = false; timer.Interval = 100; /* 100 millisec */ timer.Tick += new EventHandler(TimerCallback); tempSoundFile = ""; }