public UmlNote GetOrCreateNote(NoteLocation location) { if (!_notes.TryGetValue(location, out var note)) { _notes[location] = note = new UmlNote(); } return(note); }
//------------------------------------------------------------------------------------------------------------------------------- // Handle the drag drop and resize location of the notes //------------------------------------------------------------------------------------------------------------------------------- private void inApp_LocationChanged(object sender, EventArgs e) { foreach (var note in Notes) { if (note.InApplication != null) { NoteLocation ln = adjustLocation(note); note.Left = ln.X; note.Top = ln.Y; } } }
public bool BeginPlaySong(Song s, bool pal, int startNote) { song = s; famitrackerTempo = song.UsesFamiTrackerTempo; famitrackerSpeed = song.FamitrackerSpeed; palPlayback = pal; playPosition = startNote; playLocation = new NoteLocation(0, 0); frameNumber = 0; famitrackerTempoCounter = 0; channelStates = CreateChannelStates(this, song.Project, apuIndex, song.Project.ExpansionNumN163Channels, palPlayback); NesApu.InitAndReset(apuIndex, sampleRate, palPlayback, tndMode, song.Project.ExpansionAudioMask, song.Project.ExpansionNumN163Channels, dmcCallback); ResetFamiStudioTempo(); UpdateChannelsMuting(); //Debug.WriteLine($"START SEEKING!!"); if (startNote != 0) { seeking = true; NesApu.StartSeeking(apuIndex); AdvanceChannels(); UpdateChannels(); UpdateTempo(); while (playLocation.ToAbsoluteNoteIndex(song) < startNote - 1) { if (!PlaySongFrameInternal(true)) { break; } } NesApu.StopSeeking(apuIndex); seeking = false; } else { AdvanceChannels(); UpdateChannels(); UpdateTempo(); } playPosition = playLocation.ToAbsoluteNoteIndex(song); UpdateBeat(); EndFrame(); return(true); }
public UmlNote AddNote(NoteLocation location, string noteText, IUmlFill background = null) { noteText = noteText?.Trim(); if (string.IsNullOrEmpty(noteText)) { return(null); } var note = GetOrCreateNote(location); if (background != null) { note.Background = background; } note.Text += "\n" + noteText; return(note); }
//------------------------------------------------------------------------------------------------------------------------------- // check if the specified location (X, Y) is already used by another note //------------------------------------------------------------------------------------------------------------------------------- private bool isLocationAlreadyUsed(NoteLocation location, UINotifier note) { foreach (var p in Notes) { if (p.Left == location.X && p.Top == location.Y) { if (note.InApplication != null && p.ID == note.ID) { return(false); } return(true); } } return(false); }
//------------------------------------------------------------------------------------------------------------------------------- // Find a valid position for the note into the note area: // 1. Inside the Screen (support multiple screens) // 2. Inside the father application (if specified) //------------------------------------------------------------------------------------------------------------------------------- private NoteLocation adjustLocation(UINotifier note) { Rectangle notesArea; int nColumn = 0, xShift = 25; // Custom note overlay // x_Shift = Width + 5; // Full visible note (no overlay) bool add = false; if (InApplication != null && InApplication.WindowState == FormWindowState.Normal) // Get the available notes area, based on the type of note location { notesArea = InApplication.Bounds; } else { notesArea = new Rectangle(Screen.GetWorkingArea(note).Left, Screen.GetWorkingArea(note).Top, Screen.GetWorkingArea(note).Width, Screen.GetWorkingArea(note).Height); } int nMaxRows = notesArea.Height / Height; // Max number of rows in the available space int nMaxColumns = notesArea.Width / xShift; // Max number of columns in the available space noteLocation = new NoteLocation(notesArea.Width + // Initial Position X notesArea.Left - Width, notesArea.Height + // Initial Position Y notesArea.Top - Height); while (nMaxRows > 0 && !add) // Check the latest available position (no overlap) { for (int nRow = 1; nRow <= nMaxRows; nRow++) { noteLocation.Y = notesArea.Height + notesArea.Top - Height * nRow; if (!isLocationAlreadyUsed(noteLocation, note)) { add = true; break; } if (nRow == nMaxRows) // X shift if no more column space { nColumn++; nRow = 0; noteLocation.X = notesArea.Width + notesArea.Left - Width - xShift * nColumn; } if (nColumn >= nMaxColumns) // Last exit condition: the screen is full of note { add = true; break; } } } noteLocation.initialLocation = new Point(noteLocation.X, // Init the initial Location, for drag & drop noteLocation.Y); return(noteLocation); }
//------------------------------------------------------------------------------------------------------------------------------- // Create the Note and handle its location //------------------------------------------------------------------------------------------------------------------------------- private void setNotifier(string description, UINotifierType noteType, string title, bool isUpdate = false) { Title = title; Description = description; _uiNotifierType = noteType; noteTitle.Text = title; // Fill the UINotifier data title noteContent.Text = description; // Fill the UINotifier data description noteDate.Text = DateTime.Now + ""; // Fill the UINotifier data Timestamp #region ADJUST COLORS switch (noteType) { case UINotifierType.ERROR: icon.Symbol = 61527; icon.SymbolColor = UIStyles.Red.ButtonFillColor; LeaveColor = UIStyles.Red.ButtonFillColor; HoverColor = UIStyles.Red.ButtonFillHoverColor; break; case UINotifierType.INFO: icon.Symbol = 61530; icon.SymbolColor = UIStyles.Blue.ButtonFillColor; LeaveColor = UIStyles.Blue.ButtonFillColor; HoverColor = UIStyles.Blue.ButtonFillHoverColor; break; case UINotifierType.WARNING: icon.Symbol = 61553; icon.SymbolColor = UIStyles.Orange.ButtonFillColor; LeaveColor = UIStyles.Orange.ButtonFillColor; HoverColor = UIStyles.Orange.ButtonFillHoverColor; break; case UINotifierType.OK: icon.Symbol = 61528; icon.SymbolColor = UIStyles.Green.ButtonFillColor; LeaveColor = UIStyles.Green.ButtonFillColor; HoverColor = UIStyles.Green.ButtonFillHoverColor; break; } buttonClose.BackColor = LeaveColor; // Init colors buttonMenu.BackColor = LeaveColor; noteTitle.BackColor = LeaveColor; buttonClose.MouseHover += (s, e) => // Mouse hover { buttonClose.BackColor = HoverColor; buttonMenu.BackColor = HoverColor; noteTitle.BackColor = HoverColor; }; buttonMenu.MouseHover += (s, e) => { buttonMenu.BackColor = HoverColor; buttonClose.BackColor = HoverColor; noteTitle.BackColor = HoverColor; }; noteTitle.MouseHover += (s, e) => { buttonMenu.BackColor = HoverColor; buttonClose.BackColor = HoverColor; noteTitle.BackColor = HoverColor; }; buttonClose.MouseLeave += (s, e) => // Mouse leave { buttonClose.BackColor = LeaveColor; buttonMenu.BackColor = LeaveColor; noteTitle.BackColor = LeaveColor; }; buttonMenu.MouseLeave += (s, e) => { buttonMenu.BackColor = LeaveColor; buttonClose.BackColor = LeaveColor; noteTitle.BackColor = LeaveColor; }; noteTitle.MouseLeave += (s, e) => { buttonMenu.BackColor = LeaveColor; buttonClose.BackColor = LeaveColor; noteTitle.BackColor = LeaveColor; }; #endregion ADJUST COLORS #region DIALOG NOTE if (IsDialog) { Button ok_button = new Button(); // Dialog note comes with a simple Ok button ok_button.FlatStyle = FlatStyle.Flat; ok_button.BackColor = LeaveColor; ok_button.ForeColor = Color.White; Size = new Size(Size.Width, // Resize the note to contain the button Size.Height + 50); ok_button.Size = new Size(120, 40); ok_button.Location = new Point(Size.Width / 2 - ok_button.Size.Width / 2, Size.Height - 50); ok_button.Text = UILocalize.OK; ok_button.Click += onOkButtonClick; Controls.Add(ok_button); noteDate.Location = new Point(noteDate.Location.X, // Shift down the date location noteDate.Location.Y + 44); noteLocation = new NoteLocation(Left, Top); // Default Center Location } #endregion DIALOG NOTE #region NOTE LOCATION if (!IsDialog && !isUpdate) { NoteLocation location = adjustLocation(this); // Set the note location Left = location.X; // UINotifier position X Top = location.Y; // UINotifier position Y } #endregion NOTE LOCATION }
public void Advance(Song song, NoteLocation location, ref int famitrackerSpeed) { // When advancing row, if there was a delayed note, play it immediately. That's how FamiTracker does it. if (delayedNote != null) { PlayNote(delayedNote, delayedNoteSlidePitch, delayedNoteSlideStep); delayedNote = null; delayedNoteCounter = 0; } var channel = song.GetChannelByType(channelType); var pattern = channel.PatternInstances[location.PatternIndex]; if (pattern == null) { return; } pattern.Notes.TryGetValue(location.NoteIndex, out var newNote); var needClone = true; // Generate a release note if the release counter reaches zero. if (releaseCounter > 0 && --releaseCounter == 0 && (newNote == null || !newNote.IsMusicalOrStop)) { newNote = newNote == null ? new Note() : newNote.Clone(); newNote.Value = Note.NoteRelease; needClone = false; } // Generate a stop note if the stop counter reaches zero. if (durationCounter > 0 && --durationCounter == 0 && (newNote == null || !newNote.IsMusicalOrStop)) { newNote = newNote == null ? new Note() : newNote.Clone(); newNote.Value = Note.NoteStop; needClone = false; } if (newNote != null) { // We dont delay speed effects. This is not what FamiTracker does, but I dont care. // There is a special place in hell for people who delay speed effect. if (newNote.HasSpeed) { famitrackerSpeed = newNote.Speed; } // Slide params needs to be computed right away since we wont have access to the play position/channel later. int noteSlidePitch = 0; int noteSlideStep = 0; if (newNote.IsMusical) { if (newNote.IsSlideNote) { channel.ComputeSlideNoteParams(newNote, location, famitrackerSpeed, noteTable, palPlayback, true, out noteSlidePitch, out noteSlideStep, out _); } if (newNote.HasRelease) { releaseCounter = newNote.Release; } durationCounter = newNote.Duration; } // Store note for later if delayed. if (newNote.HasNoteDelay) { delayedNote = newNote; delayedNoteCounter = newNote.NoteDelay + 1; delayedNoteSlidePitch = noteSlidePitch; delayedNoteSlideStep = noteSlideStep; return; } PlayNote(newNote, noteSlidePitch, noteSlideStep, needClone); } }
private void CreateSlideNotes(Song s, Dictionary <Pattern, RowFxData[, ]> patternFxData) { var processedPatterns = new HashSet <Pattern>(); // Convert slide notes + portamento to our format. foreach (var c in s.Channels) { if (!c.SupportsSlideNotes) { continue; } var songSpeed = s.FamitrackerSpeed; var lastNoteInstrument = (Instrument)null; var lastNoteArpeggio = (Arpeggio)null; var lastNoteValue = (byte)Note.NoteInvalid; var portamentoSpeed = 0; var slideSpeed = 0; var slideShift = c.IsN163WaveChannel ? 2 : 0; var slideSign = c.IsN163WaveChannel || c.IsFdsWaveChannel || c.IsVrc7FmChannel ? -1 : 1; // Inverted channels. for (int p = 0; p < s.Length; p++) { var pattern = c.PatternInstances[p]; if (pattern == null) { continue; } var patternLen = s.GetPatternLength(p); for (var it = pattern.GetDenseNoteIterator(0, patternLen); !it.Done; it.Next()) { var location = new NoteLocation(p, it.CurrentTime); var note = it.CurrentNote; // Look for speed changes. s.ApplySpeedEffectAt(location, ref songSpeed); if (!patternFxData.ContainsKey(pattern) || processedPatterns.Contains(pattern)) { continue; } var fxData = patternFxData[pattern]; var slideTarget = 0; for (int i = 0; i < fxData.GetLength(1); i++) { var fx = fxData[location.NoteIndex, i]; if (fx.param != 0) { // When the effect it turned on, we need to add a note. if ((fx.fx == Effect_PortaUp || fx.fx == Effect_PortaDown || fx.fx == Effect_SlideUp || fx.fx == Effect_SlideDown) && lastNoteValue >= Note.MusicalNoteMin && lastNoteValue <= Note.MusicalNoteMax && (note == null || !note.IsValid)) { if (note == null) { note = pattern.GetOrCreateNoteAt(location.NoteIndex); it.Resync(); } note.Value = lastNoteValue; note.Instrument = lastNoteInstrument; note.Arpeggio = lastNoteArpeggio; note.HasAttack = false; } } if (fx.fx == Effect_PortaUp) { // If we have a Qxx/Rxx on the same row as a 1xx/2xx, things get weird. if (slideTarget == 0) { slideSpeed = (-fx.param * slideSign) << slideShift; } } if (fx.fx == Effect_PortaDown) { // If we have a Qxx/Rxx on the same row as a 1xx/2xx, things get weird. if (slideTarget == 0) { slideSpeed = (fx.param * slideSign) << slideShift; } } if (fx.fx == Effect_Portamento) { portamentoSpeed = fx.param; } if (fx.fx == Effect_SlideUp && note != null && note.IsMusical) { slideTarget = Utils.Clamp(note.Value + (fx.param & 0xf), Note.MusicalNoteMin, Note.MusicalNoteMax); slideSpeed = (-((fx.param >> 4) * 2 + 1)) << slideShift; } if (fx.fx == Effect_SlideDown && note != null && note.IsMusical) { slideTarget = Utils.Clamp(note.Value - (fx.param & 0xf), Note.MusicalNoteMin, Note.MusicalNoteMax); slideSpeed = (((fx.param >> 4) * 2 + 1)) << slideShift; } } // Create a slide note. if (note != null && !note.IsSlideNote) { if (note.IsMusical) { var slideSource = note.Value; var noteTable = NesApu.GetNoteTableForChannelType(c.Type, s.Project.PalMode, s.Project.ExpansionNumChannels); var pitchLimit = NesApu.GetPitchLimitForChannelType(c.Type); // If we have a new note with auto-portamento enabled, we need to // swap the notes since our slide notes work backward compared to // FamiTracker. if (portamentoSpeed != 0) { // Ignore notes with no attack since we created them to handle a previous slide. if (note.HasAttack && lastNoteValue >= Note.MusicalNoteMin && lastNoteValue <= Note.MusicalNoteMax) { slideSpeed = portamentoSpeed; slideTarget = note.Value; slideSource = lastNoteValue; note.Value = lastNoteValue; } } // Our implementation of VRC7 pitches is quite different from FamiTracker. // Compensate for larger pitches in higher octaves by shifting. We cant shift by // a large amount because the period is 9-bit and FamiTracker is restricted to // this for slides (octave never changes). var octaveSlideShift = c.IsVrc7FmChannel && note.Value >= 12 ? 1 : 0; // 3xx/Qxy/Rxy : We know which note we are sliding to and the speed, but we // don't know how many frames it will take to get there. if (slideTarget != 0) { // Advance in the song until we have the correct number of frames. var numFrames = Math.Max(1, Math.Abs((noteTable[slideSource] - noteTable[slideTarget]) / (slideSpeed << octaveSlideShift))); note.SlideNoteTarget = (byte)slideTarget; // TODO: Here we consider if the start note has a delay, but ignore the end note. It might have one too. var nextLocation = location; s.AdvanceNumberOfFrames(ref nextLocation, numFrames, note.HasNoteDelay ? -note.NoteDelay : 0, songSpeed, s.Project.PalMode); // Still to see if there is a note between the current one and the // next note, this could append if you add a note before the slide // is supposed to finish. if (FindNextSlideEffect(c, location, out var nextLocation2, patternFxData)) { nextLocation = NoteLocation.Min(nextLocation, nextLocation2); // If the slide is interrupted by another slide effect, we will not reach // the final target, but rather some intermediate note. Let's do our best // to interpolate and figure out the best note. var numFramesUntilNextSlide = s.CountFramesBetween(location, nextLocation, songSpeed, s.Project.PalMode); var ratio = Utils.Clamp(numFramesUntilNextSlide / numFrames, 0.0f, 1.0f); var intermediatePitch = (int)Math.Round(Utils.Lerp(noteTable[slideSource], noteTable[slideTarget], ratio)); slideTarget = FindBestMatchingNote(noteTable, intermediatePitch, Math.Sign(slideSpeed)); note.SlideNoteTarget = (byte)slideTarget; } if (nextLocation.PatternIndex < s.Length) { // Add an extra note with no attack to stop the slide. var nextPattern = c.PatternInstances[nextLocation.PatternIndex]; if (!nextPattern.Notes.TryGetValue(nextLocation.NoteIndex, out var nextNote) || !nextNote.IsValid) { nextNote = nextPattern.GetOrCreateNoteAt(nextLocation.NoteIndex); nextNote.Instrument = note.Instrument; nextNote.Value = (byte)slideTarget; nextNote.HasAttack = false; it.Resync(); } else if (nextNote != null && nextNote.IsRelease) { Log.LogMessage(LogSeverity.Warning, $"A slide note ends on a release note. This is currently unsupported and will require manual correction. {GetPatternString(nextPattern, nextLocation.NoteIndex)}"); } } // 3xx, Qxx and Rxx stops when its done. slideSpeed = 0; } // 1xx/2xy : We know the speed at which we are sliding, but need to figure out what makes it stop. else if (slideSpeed != 0 && FindNextSlideEffect(c, location, out var nextLocation, patternFxData)) { // See how many frames until the slide stops. var numFrames = (int)Math.Round(s.CountFramesBetween(location, nextLocation, songSpeed, s.Project.PalMode)); // TODO: Here we consider if the start note has a delay, but ignore the end note. It might have one too. numFrames = Math.Max(1, numFrames - (note.HasNoteDelay ? note.NoteDelay : 0)); // Compute the pitch delta and find the closest target note. var newNotePitch = Utils.Clamp(noteTable[slideSource] + numFrames * (slideSpeed << octaveSlideShift), 0, pitchLimit); var newNote = FindBestMatchingNote(noteTable, newNotePitch, Math.Sign(slideSpeed)); note.SlideNoteTarget = (byte)newNote; // If the FX was turned off, we need to add an extra note. var nextPattern = c.PatternInstances[nextLocation.PatternIndex]; if (!nextPattern.Notes.TryGetValue(nextLocation.NoteIndex, out var nextNote) || !nextNote.IsValid) { nextNote = nextPattern.GetOrCreateNoteAt(nextLocation.NoteIndex); nextNote.Instrument = note.Instrument; nextNote.Value = (byte)newNote; nextNote.HasAttack = false; it.Resync(); } else if (nextNote != null && nextNote.IsRelease) { Log.LogMessage(LogSeverity.Warning, $"A slide note ends on a release note. This is currently unsupported and will require manual correction. {GetPatternString(nextPattern, nextLocation.NoteIndex)}"); } } } } if (note != null && (note.IsMusical || note.IsStop)) { lastNoteValue = note.IsSlideNote ? note.SlideNoteTarget : note.Value; lastNoteInstrument = note.Instrument; lastNoteArpeggio = note.Arpeggio; } } processedPatterns.Add(pattern); } } }
private bool FindNextSlideEffect(Channel channel, NoteLocation location, out NoteLocation nextLocation, Dictionary <Pattern, RowFxData[, ]> patternFxData) { nextLocation = NoteLocation.Invalid; var pattern = channel.PatternInstances[location.PatternIndex]; if (pattern == null || !patternFxData.ContainsKey(pattern)) { return(false); } var patternLen = channel.Song.GetPatternLength(location.PatternIndex); var fxData = patternFxData[pattern]; for (var it = pattern.GetDenseNoteIterator(location.NoteIndex + 1, patternLen); !it.Done; it.Next()) { var time = it.CurrentTime; var note = it.CurrentNote; var fxChanged = false; for (int i = 0; i < fxData.GetLength(1); i++) { var fx = fxData[time, i]; if (fx.fx == Effect_PortaUp || fx.fx == Effect_PortaDown || fx.fx == Effect_Portamento || fx.fx == Effect_SlideUp || fx.fx == Effect_SlideDown) { fxChanged = true; break; } } if (note != null && note.IsValid || fxChanged) { nextLocation.PatternIndex = location.PatternIndex; nextLocation.NoteIndex = time; return(true); } } for (int p = location.PatternIndex + 1; p < channel.Song.Length; p++) { pattern = channel.PatternInstances[p]; patternLen = channel.Song.GetPatternLength(p); if (pattern != null && patternFxData.ContainsKey(pattern)) { fxData = patternFxData[pattern]; for (var it = pattern.GetDenseNoteIterator(0, patternLen); !it.Done; it.Next()) { var time = it.CurrentTime; var note = it.CurrentNote; var fxChanged = false; for (int i = 0; i < fxData.GetLength(1); i++) { var fx = fxData[time, i]; if (fx.fx == Effect_PortaUp || fx.fx == Effect_PortaDown || fx.fx == Effect_Portamento || fx.fx == Effect_SlideUp || fx.fx == Effect_SlideDown) { fxChanged = true; break; } } if (note != null && note.IsValid || fxChanged) { nextLocation.PatternIndex = p; nextLocation.NoteIndex = time; return(true); } } } } return(false); }
//------------------------------------------------------------------------------------------------------------------------------- // Create the Note and handle its location //------------------------------------------------------------------------------------------------------------------------------- private void setNotifier(string description, Type noteType, string title, bool isUpdate = false) { this.title = title; this.description = description; this.type = noteType; noteTitle.Text = title; // Fill the Notifier data title noteContent.Text = description; // Fill the Notifier data description noteDate.Text = DateTime.Now + ""; // Fill the Notifier data Timestamp #region ADJUST COLORS switch (noteType) { case Type.ERROR: icon.Image = global::Notify.Properties.Resources.ko; Leave = Color.FromArgb(200, 60, 70); Hover = Color.FromArgb(240, 80, 90); break; case Type.INFO: icon.Image = global::Notify.Properties.Resources.info; Leave = Color.FromArgb(90, 140, 230); Hover = Color.FromArgb(110, 160, 250); break; case Type.WARNING: icon.Image = global::Notify.Properties.Resources.warning; Leave = Color.FromArgb(200, 200, 80); Hover = Color.FromArgb(220, 220, 80); break; case Type.OK: icon.Image = global::Notify.Properties.Resources.ok; Leave = Color.FromArgb(80, 200, 130); Hover = Color.FromArgb(80, 240, 130); break; } buttonClose.BackColor = Leave; // Init colors buttonMenu.BackColor = Leave; noteTitle.BackColor = Leave; this.buttonClose.MouseHover += (s, e) => // Mouse hover { this.buttonClose.BackColor = Hover; this.buttonMenu.BackColor = Hover; this.noteTitle.BackColor = Hover; }; this.buttonMenu.MouseHover += (s, e) => { this.buttonMenu.BackColor = Hover; this.buttonClose.BackColor = Hover; this.noteTitle.BackColor = Hover; }; this.noteTitle.MouseHover += (s, e) => { this.buttonMenu.BackColor = Hover; this.buttonClose.BackColor = Hover; this.noteTitle.BackColor = Hover; }; this.buttonClose.MouseLeave += (s, e) => // Mouse leave { this.buttonClose.BackColor = Leave; this.buttonMenu.BackColor = Leave; this.noteTitle.BackColor = Leave; }; this.buttonMenu.MouseLeave += (s, e) => { this.buttonMenu.BackColor = Leave; this.buttonClose.BackColor = Leave; this.noteTitle.BackColor = Leave; }; this.noteTitle.MouseLeave += (s, e) => { this.buttonMenu.BackColor = Leave; this.buttonClose.BackColor = Leave; this.noteTitle.BackColor = Leave; }; #endregion #region DIALOG NOTE if (isDialog) { Button ok_button = new Button(); // Dialog note comes with a simple Ok button ok_button.FlatStyle = FlatStyle.Flat; ok_button.BackColor = Leave; ok_button.ForeColor = Color.White; Size = new Size(Size.Width, // Resize the note to contain the button Size.Height + 50); ok_button.Size = new Size(120, 40); ok_button.Location = new Point(Size.Width / 2 - ok_button.Size.Width / 2, Size.Height - 50); ok_button.Text = DialogResult.OK.ToString(); ok_button.Click += onOkButtonClick; Controls.Add(ok_button); noteDate.Location = new Point(noteDate.Location.X, // Shift down the date location noteDate.Location.Y + 44); noteLocation = new NoteLocation(Left, Top); // Default Center Location } #endregion #region NOTE LOCATION if (!isDialog && !isUpdate) { NoteLocation location = adjustLocation(this); // Set the note location Left = location.X; // Notifier position X Top = location.Y; // Notifier position Y } #endregion }
private void RenderTimecode(RenderGraphics g) { var frame = App.CurrentFrame; var famitrackerTempo = App.Project != null && App.Project.UsesFamiTrackerTempo; var zeroSizeX = g.MeasureString("0", ThemeBase.FontHuge); var colonSizeX = g.MeasureString(":", ThemeBase.FontHuge); var timeCodeSizeY = Height - timecodePosY * 2; var textColor = App.IsRecording ? theme.DarkRedFillBrush : theme.LightGreyFillBrush2; g.FillAndDrawRectangle(timecodePosX, timecodePosY, timecodePosX + timecodeSizeX, Height - timecodePosY, theme.BlackBrush, theme.LightGreyFillBrush2); if (Settings.TimeFormat == 0 || famitrackerTempo) // MM:SS:mmm cant be used with FamiTracker tempo. { var location = NoteLocation.FromAbsoluteNoteIndex(App.Song, frame); var numPatternDigits = Utils.NumDecimalDigits(App.Song.Length - 1); var numNoteDigits = Utils.NumDecimalDigits(App.Song.GetPatternLength(location.PatternIndex) - 1); var patternString = location.PatternIndex.ToString("D" + numPatternDigits); var noteString = location.NoteIndex.ToString("D" + numNoteDigits); var charPosX = timecodePosX + timecodeSizeX / 2 - ((numPatternDigits + numNoteDigits) * zeroSizeX + colonSizeX) / 2; for (int i = 0; i < numPatternDigits; i++, charPosX += zeroSizeX) { g.DrawText(patternString[i].ToString(), ThemeBase.FontHuge, charPosX, 2, textColor, zeroSizeX); } g.DrawText(":", ThemeBase.FontHuge, charPosX, 2, textColor, colonSizeX); charPosX += colonSizeX; for (int i = 0; i < numNoteDigits; i++, charPosX += zeroSizeX) { g.DrawText(noteString[i].ToString(), ThemeBase.FontHuge, charPosX, 2, textColor, zeroSizeX); } } else { TimeSpan time = App.CurrentTime; var minutesString = time.Minutes.ToString("D2"); var secondsString = time.Seconds.ToString("D2"); var millisecondsString = time.Milliseconds.ToString("D3"); // 00:00:000 var charPosX = timecodePosX + timecodeSizeX / 2 - (7 * zeroSizeX + 2 * colonSizeX) / 2; for (int i = 0; i < 2; i++, charPosX += zeroSizeX) { g.DrawText(minutesString[i].ToString(), ThemeBase.FontHuge, charPosX, 2, textColor, zeroSizeX); } g.DrawText(":", ThemeBase.FontHuge, charPosX, 2, textColor, colonSizeX); charPosX += colonSizeX; for (int i = 0; i < 2; i++, charPosX += zeroSizeX) { g.DrawText(secondsString[i].ToString(), ThemeBase.FontHuge, charPosX, 2, textColor, zeroSizeX); } g.DrawText(":", ThemeBase.FontHuge, charPosX, 2, textColor, colonSizeX); charPosX += colonSizeX; for (int i = 0; i < 3; i++, charPosX += zeroSizeX) { g.DrawText(millisecondsString[i].ToString(), ThemeBase.FontHuge, charPosX, 2, textColor, zeroSizeX); } } }
public void ShowNotes() { string line = NoteDuration + " " + NoteLocation.ToString() + " " + RawNote + " " + octave + " " + intensity + " " + pan; Trace.WriteLine(line); }