Ejemplo n.º 1
0
        private void ComputeChannelsScroll(VideoFrameMetadata[] frames, int channelMask, int numVisibleNotes)
        {
            var numFrames   = frames.Length;
            var numChannels = frames[0].channelNotes.Length;

            for (int c = 0; c < numChannels; c++)
            {
                if ((channelMask & (1 << c)) == 0)
                {
                    continue;
                }

                // Go through all the frames and split them in segments.
                // A segment is a section of the song where all the notes fit in the view.
                var segments       = new List <ScrollSegment>();
                var currentSegment = (ScrollSegment)null;
                var minOverallNote = int.MaxValue;
                var maxOverallNote = int.MinValue;

                for (int f = 0; f < numFrames; f++)
                {
                    var frame = frames[f];
                    var note  = frame.channelNotes[c];

                    if (frame.scroll == null)
                    {
                        frame.scroll = new float[numChannels];
                    }

                    if (note.IsMusical)
                    {
                        if (currentSegment == null)
                        {
                            currentSegment = new ScrollSegment();
                            segments.Add(currentSegment);
                        }

                        // If its the start of a new pattern and we've been not moving for ~10 sec, let's start a new segment.
                        bool forceNewSegment = frame.playNote == 0 && (f - currentSegment.startFrame) > 600;

                        var minNoteValue = note.Value - 1;
                        var maxNoteValue = note.Value + 1;

                        // Only consider slides if they arent too large.
                        if (note.IsSlideNote && Math.Abs(note.SlideNoteTarget - note.Value) < numVisibleNotes / 2)
                        {
                            minNoteValue = Math.Min(note.Value, note.SlideNoteTarget) - 1;
                            maxNoteValue = Math.Max(note.Value, note.SlideNoteTarget) + 1;
                        }

                        // Only consider arpeggios if they are not too big.
                        if (note.IsArpeggio && note.Arpeggio.GetChordMinMaxOffset(out var minArp, out var maxArp) && maxArp - minArp < numVisibleNotes / 2)
                        {
                            minNoteValue = note.Value + minArp;
                            maxNoteValue = note.Value + maxArp;
                        }

                        minOverallNote = Math.Min(minOverallNote, minNoteValue);
                        maxOverallNote = Math.Max(maxOverallNote, maxNoteValue);

                        var newMinNote = Math.Min(currentSegment.minNote, minNoteValue);
                        var newMaxNote = Math.Max(currentSegment.maxNote, maxNoteValue);

                        // If we cant fit the next note in the view, start a new segment.
                        if (forceNewSegment || newMaxNote - newMinNote + 1 > numVisibleNotes)
                        {
                            currentSegment.endFrame   = f;
                            currentSegment            = new ScrollSegment();
                            currentSegment.startFrame = f;
                            segments.Add(currentSegment);

                            currentSegment.minNote = minNoteValue;
                            currentSegment.maxNote = maxNoteValue;
                        }
                        else
                        {
                            currentSegment.minNote = newMinNote;
                            currentSegment.maxNote = newMaxNote;
                        }
                    }
                }

                // Not a single notes in this channel...
                if (currentSegment == null)
                {
                    currentSegment         = new ScrollSegment();
                    currentSegment.minNote = Note.FromFriendlyName("C4");
                    currentSegment.maxNote = currentSegment.minNote;
                    segments.Add(currentSegment);
                }

                currentSegment.endFrame = numFrames;

                // Remove very small segments, these make the camera move too fast, looks bad.
                var shortestAllowedSegment = SegmentTransitionNumFrames * 2;

                bool removed = false;
                do
                {
                    var sortedSegment = new List <ScrollSegment>(segments);

                    sortedSegment.Sort((s1, s2) => s1.NumFrames.CompareTo(s2.NumFrames));

                    if (sortedSegment[0].NumFrames >= shortestAllowedSegment)
                    {
                        break;
                    }

                    for (int s = 0; s < sortedSegment.Count; s++)
                    {
                        var seg = sortedSegment[s];

                        if (seg.NumFrames >= shortestAllowedSegment)
                        {
                            break;
                        }

                        var thisSegmentIndex = segments.IndexOf(seg);

                        // Segment is too short, see if we can merge with previous/next one.
                        var mergeSegmentIndex  = -1;
                        var mergeSegmentLength = -1;
                        if (thisSegmentIndex > 0)
                        {
                            mergeSegmentIndex  = thisSegmentIndex - 1;
                            mergeSegmentLength = segments[thisSegmentIndex - 1].NumFrames;
                        }
                        if (thisSegmentIndex != segments.Count - 1 && segments[thisSegmentIndex + 1].NumFrames > mergeSegmentLength)
                        {
                            mergeSegmentIndex  = thisSegmentIndex + 1;
                            mergeSegmentLength = segments[thisSegmentIndex + 1].NumFrames;
                        }
                        if (mergeSegmentIndex >= 0)
                        {
                            // Merge.
                            var mergeSeg = segments[mergeSegmentIndex];
                            mergeSeg.startFrame = Math.Min(mergeSeg.startFrame, seg.startFrame);
                            mergeSeg.endFrame   = Math.Max(mergeSeg.endFrame, seg.endFrame);
                            segments.RemoveAt(thisSegmentIndex);
                            removed = true;
                            break;
                        }
                    }
                }while (removed);

                // Build the actually scrolling data.
                var minScroll = (float)Math.Ceiling(Note.MusicalNoteMin + numVisibleNotes * 0.5f);
                var maxScroll = (float)Math.Floor(Note.MusicalNoteMax - numVisibleNotes * 0.5f);

                Debug.Assert(maxScroll >= minScroll);

                foreach (var segment in segments)
                {
                    segment.scroll = Utils.Clamp(segment.minNote + (segment.maxNote - segment.minNote) * 0.5f, minScroll, maxScroll);
                }

                for (var s = 0; s < segments.Count; s++)
                {
                    var segment0 = segments[s + 0];
                    var segment1 = s == segments.Count - 1 ? null : segments[s + 1];

                    for (int f = segment0.startFrame; f < segment0.endFrame - (segment1 == null ? 0 : SegmentTransitionNumFrames); f++)
                    {
                        frames[f].scroll[c] = segment0.scroll;
                    }

                    if (segment1 != null)
                    {
                        // Smooth transition to next segment.
                        for (int f = segment0.endFrame - SegmentTransitionNumFrames, a = 0; f < segment0.endFrame; f++, a++)
                        {
                            var lerp = a / (float)SegmentTransitionNumFrames;
                            frames[f].scroll[c] = Utils.Lerp(segment0.scroll, segment1.scroll, Utils.SmootherStep(lerp));
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        private void ReadInstrument2A03(Instrument instrument, int instIdx, ref int idx)
        {
            ReadCommonEnvelopes(instrument, instIdx, ref idx, envelopes);

            for (int i = 0; i < OctaveRange; ++i)
            {
                for (int j = 0; j < 12; ++j)
                {
                    var index = bytes[idx++];
                    var pitch = bytes[idx++];

                    if (blockVersion > 5)
                    {
                        idx++; // sample delta
                    }
                    if (index > 0 && pitch != 0)
                    {
                        var sample = samples[index - 1];
                        var note   = i * 12 + j + 1;
                        if (sample != null && sample.ProcessedData != null)
                        {
                            if (project.NoteSupportsDPCM(note))
                            {
                                if (project.GetDPCMMapping(note) == null)
                                {
                                    project.MapDPCMSample(note, sample, pitch & 0x0f, (pitch & 0x80) != 0);
                                }
                                else
                                {
                                    Log.LogMessage(LogSeverity.Warning, $"Multiple instruments assigning DPCM samples to key {Note.GetFriendlyName(note)}. Only the first one will be assigned, others will be loaded, but unassigned.");
                                }
                            }
                            else
                            {
                                Log.LogMessage(LogSeverity.Warning, $"DPCM sample assigned to key {Note.GetFriendlyName(note)}. FamiStudio only supports DPCM samples on keys {Note.GetFriendlyName(Note.DPCMNoteMin + 1)} to {Note.GetFriendlyName(Note.DPCMNoteMax)}.");
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 3
0
        public void PlayNote(Note newNote)
        {
            if (!newNote.HasFinePitch)
            {
                newNote.FinePitch = 0;
            }

            if (newNote.IsRelease)
            {
                // Channels with custom release code will do their own thing.
                if (customRelease)
                {
                    note = newNote;
                }
                else
                {
                    if (note.Instrument != null)
                    {
                        for (int j = 0; j < Envelope.Count; j++)
                        {
                            if (envelopes[j] != null && envelopes[j].Release >= 0)
                            {
                                envelopeIdx[j] = envelopes[j].Release;
                            }
                        }
                    }
                }
            }
            else
            {
                bool instrumentChanged = note.Instrument != newNote.Instrument;
                bool arpeggioChanged   = note.Arpeggio != newNote.Arpeggio;

                note = newNote;

                if (note.IsMusical)
                {
                    // Set/clear override when changing arpeggio
                    if (arpeggioChanged)
                    {
                        if (note.Arpeggio != null)
                        {
                            envelopes[Envelope.Arpeggio] = note.Arpeggio.Envelope;
                            arpeggioEnvelopeOverride     = true;
                        }
                        else
                        {
                            envelopes[Envelope.Arpeggio] = null;
                            arpeggioEnvelopeOverride     = false;
                        }

                        envelopeIdx[Envelope.Arpeggio]    = 0;
                        envelopeValues[Envelope.Arpeggio] = 0;
                    }
                    // If same arpeggio, but note has an attack, reset it.
                    else if (note.HasAttack && arpeggioEnvelopeOverride)
                    {
                        envelopeIdx[Envelope.Arpeggio]    = 0;
                        envelopeValues[Envelope.Arpeggio] = 0;
                    }
                }

                if (instrumentChanged || note.HasAttack && !note.IsStop)
                {
                    for (int j = 0; j < Envelope.Count; j++)
                    {
                        if ((j != Envelope.Pitch || !pitchEnvelopeOverride) &&
                            (j != Envelope.Arpeggio || !arpeggioEnvelopeOverride))
                        {
                            envelopes[j] = note.Instrument == null ? null : note.Instrument.Envelopes[j];
                        }
                        envelopeIdx[j] = 0;
                    }

                    envelopeValues[Envelope.Pitch] = 0; // In case we use relative envelopes.
                    noteTriggered = true;
                }

                if (instrumentChanged)
                {
                    LoadInstrument(note.Instrument);
                }
            }
        }
Ejemplo n.º 4
0
        private unsafe RenderBitmap GetPatternBitmapFromCache(RenderGraphics g, Pattern p)
        {
            int patternSizeX = Song.PatternLength - 1;
            int patternSizeY = trackSizeY - patternHeaderSizeY - 1;

            RenderBitmap bmp;

            if (patternBitmapCache.TryGetValue(p.Id, out bmp))
            {
                if (bmp.Size.Width == patternSizeX)
                {
                    return(bmp);
                }
                else
                {
                    patternBitmapCache.Remove(p.Id);
                    bmp.Dispose();
                    bmp = null;
                }
            }

            uint[] data = new uint[patternSizeX * patternSizeY];

            Note minNote;
            Note maxNote;

            if (p.GetMinMaxNote(out minNote, out maxNote))
            {
                if (maxNote.Value == minNote.Value)
                {
                    minNote.Value = (byte)(minNote.Value - 5);
                    maxNote.Value = (byte)(maxNote.Value + 5);
                }
                else
                {
                    minNote.Value = (byte)(minNote.Value - 2);
                    maxNote.Value = (byte)(maxNote.Value + 2);
                }

                Note lastValid = new Note {
                    Value = Note.NoteInvalid
                };

                for (int i = 0; i < Song.PatternLength - 1; i++) // TODO: We always skip the last note.
                {
                    var n = p.Notes[i];

                    if (n.IsValid && !n.IsStop)
                    {
                        lastValid = p.Notes[i];
                    }

                    if (lastValid.IsValid)
                    {
                        float scaleY = (patternSizeY - noteSizeY) / (float)patternSizeY;

                        int x = i;
                        int y = Math.Min((int)Math.Round((lastValid.Value - minNote.Value) / (float)(maxNote.Value - minNote.Value) * scaleY * patternSizeY), patternSizeY - noteSizeY);

                        var instrument = lastValid.Instrument;
                        var color      = instrument == null ? ThemeBase.LightGreyFillColor1 : instrument.Color;

                        for (int j = 0; j < noteSizeY; j++)
                        {
                            data[(patternSizeY - 1 - (y + j)) * patternSizeX + x] = (uint)color.ToArgb();
                        }
                    }

                    //if (n.HasEffect)
                    //{
                    //    for (int y = 0; y < patternSizeY; y++)
                    //    {
                    //        data[y * patternSizeX + i] = 0xff000000;
                    //    }
                    //}
                }
            }

            bmp = g.CreateBitmap(patternSizeX, patternSizeY, data);
            patternBitmapCache[p.Id] = bmp;

            return(bmp);
        }