示例#1
0
        public static float ComputeBpmForGroove(bool pal, int[] groove, int notesPerBeat)
        {
            var grooveNumFrames = 0;
            var grooveLength    = 0;

            do
            {
                grooveNumFrames += Utils.Sum(groove);
                grooveLength    += groove.Length;
            }while ((grooveLength % notesPerBeat) != 0);

            float numer = pal ? 3000.0f : 3600.0f;
            float denom = grooveNumFrames / (float)grooveLength * notesPerBeat;

            return(numer / denom);
        }
示例#2
0
        public static byte[] GetTempoEnvelope(int[] groove, int groovePadMode, bool palSource)
        {
            // Look in the cache first.
            var key = new CachedTempoEnvelopeKey()
            {
                groove = groove, groovePadMode = groovePadMode, palSource = palSource
            };

            if (cachedTempoEnvelopes.TryGetValue(key, out var env))
            {
                return(env);
            }

            // Otherwise build.
            var dstFactor         = palSource ? 6 : 5;
            var srcFactor         = palSource ? 5 : 6;
            var noteLength        = Utils.Min(groove);
            var grooveNumFrames   = Utils.Sum(groove);
            var grooveRepeatCount = 1;

            // Repeat the groove until we have something perfectly divisible by 6 (5 on PAL).
            while ((grooveNumFrames % srcFactor) != 0)
            {
                grooveNumFrames += Utils.Sum(groove);
                grooveRepeatCount++;
            }

            // Figure out how many frames that is on the playback machine.
            var adaptedNumFrames = grooveNumFrames / srcFactor * dstFactor;

            // Mark some frames as "important", this will typically be the first
            // and last frame of the note. This will preserve the attack and
            // 1-frame silence between notes.
            var importantFrames = new bool[grooveNumFrames];
            var frameIndex      = 0;

            for (int i = 0; i < grooveRepeatCount; i++)
            {
                for (int j = 0; j < groove.Length; j++)
                {
                    if (groove[j] == noteLength)
                    {
                        importantFrames[frameIndex] = true;
                        importantFrames[frameIndex + noteLength - 1] = true;
                    }
                    else
                    {
                        if (groovePadMode != GroovePaddingType.Beginning || noteLength == 1)
                        {
                            importantFrames[frameIndex] = true;
                        }
                        else
                        {
                            importantFrames[frameIndex + 1] = true;
                        }

                        if (groovePadMode != GroovePaddingType.End || noteLength == 1)
                        {
                            importantFrames[frameIndex + noteLength] = true;
                        }
                        else
                        {
                            importantFrames[frameIndex + noteLength - 1] = true;
                        }
                    }

                    frameIndex += groove[j];
                }
            }

#if FALSE
            var numSkipFrames = palSource ? adaptedNumFrames - grooveNumFrames : grooveNumFrames - adaptedNumFrames;
            var bestScore     = int.MaxValue;
            var bestOffset    = -1;

            for (int i = 0; i < srcFactor; i++)
            {
                var score = 0;

                frameIndex = i;
                for (int j = 0; j < numSkipFrames; j++)
                {
                    if (importantFrames[frameIndex])
                    {
                        score++;
                    }
                    frameIndex += srcFactor;
                }

                if (score < bestScore)
                {
                    bestScore  = score;
                    bestOffset = i;
                }
            }
#else
            // Start by distributing the skip (or double) frames evenly.
            var numSkipFrames = palSource ? adaptedNumFrames - grooveNumFrames : grooveNumFrames - adaptedNumFrames;
            var skipFrames    = new bool[grooveNumFrames];

            frameIndex = srcFactor / 2;
            for (int i = 0; i < numSkipFrames; i++)
            {
                skipFrames[frameIndex] = true;
                frameIndex            += srcFactor;
            }

            int GetFrameCost(int idx)
            {
                if (!skipFrames[idx])
                {
                    return(0);
                }

                var cost = 0;

                // Penalize important frames
                if (importantFrames[idx])
                {
                    cost += srcFactor;
                }

                // Look right for another skipped frame.
                for (int i = 1; i < srcFactor; i++)
                {
                    var nextIdx = idx + i;
                    if (nextIdx >= skipFrames.Length)
                    {
                        nextIdx -= skipFrames.Length;
                    }
                    if (skipFrames[nextIdx])
                    {
                        // The closer we are, the higher the cost.
                        cost += (srcFactor - i);
                        break;
                    }
                }

                // Look left for another skipped frame.
                for (int i = 1; i < srcFactor; i++)
                {
                    var prevIdx = idx - i;
                    if (prevIdx < 0)
                    {
                        prevIdx += skipFrames.Length;
                    }
                    // The closer we are, the higher the cost.
                    if (skipFrames[prevIdx])
                    {
                        cost += (srcFactor - i);
                        break;
                    }
                }

                return(cost);
            }

            var frameCosts = new int[grooveNumFrames];

            // Optimize.
            for (int i = 0; i < 100; i++)
            {
                // Update costs.
                var maxCost      = -10;
                var maxCostIndex = -1;
                var totalCost    = 0;

                for (int j = 0; j < frameCosts.Length; j++)
                {
                    var cost = GetFrameCost(j);

                    frameCosts[j] = cost;
                    totalCost    += cost;

                    if (cost > maxCost)
                    {
                        maxCost      = cost;
                        maxCostIndex = j;
                    }
                }

                if (maxCost == 0)
                {
                    break;
                }

                var currentFrameCost = GetFrameCost(maxCostIndex);

                // Try to optimize the most expensive frame by moving it to the left.
                if (maxCostIndex > 0 && !skipFrames[maxCostIndex - 1] && !importantFrames[maxCostIndex - 1])
                {
                    Utils.Swap(ref skipFrames[maxCostIndex], ref skipFrames[maxCostIndex - 1]);
                    if (GetFrameCost(maxCostIndex - 1) < currentFrameCost)
                    {
                        continue;
                    }
                    Utils.Swap(ref skipFrames[maxCostIndex], ref skipFrames[maxCostIndex - 1]);
                }

                // Try to optimize the most expensive frame by moving it to the right.
                if (maxCostIndex < skipFrames.Length - 1 && !skipFrames[maxCostIndex + 1] && !importantFrames[maxCostIndex + 1])
                {
                    Utils.Swap(ref skipFrames[maxCostIndex], ref skipFrames[maxCostIndex + 1]);
                    if (GetFrameCost(maxCostIndex + 1) < currentFrameCost)
                    {
                        continue;
                    }
                    Utils.Swap(ref skipFrames[maxCostIndex], ref skipFrames[maxCostIndex + 1]);
                }

                break;
            }
#endif

            // Build the actual envelope.
            var lastFrameIndex  = -1;
            var firstFrameIndex = -1;
            var envelope        = new List <byte>();
            var sum             = 0;

            for (int i = 0; i < skipFrames.Length; i++)
            {
                if (skipFrames[i])
                {
                    var frameDelta = i - lastFrameIndex;
                    envelope.Add((byte)(frameDelta + (palSource ? 1 : -1)));
                    sum           += frameDelta;
                    lastFrameIndex = i;
                    if (firstFrameIndex < 0)
                    {
                        firstFrameIndex = i;
                    }
                }
            }

            if (palSource)
            {
                envelope[0]--;
            }

            var remainingFrames = skipFrames.Length - sum;
            if (remainingFrames != 0)
            {
                envelope.Add((byte)(remainingFrames + firstFrameIndex + 1 + (palSource ? 1 : -1)));
            }
            envelope.Add(0x80);

            env = envelope.ToArray();
            cachedTempoEnvelopes[key] = env;

            return(env);
        }