Exemplo n.º 1
0
        static void AddNote(Wave.PartialTimeline timeline, double time, double duration, double gain, double cents, Partial[] partials, double balance)
        {
            //
            double ta = 0.01;                         // attack
            double tr = Math.Max(0.1, duration - ta); // release

            //
            foreach (Partial p in partials)
            {
                double c     = cents + p.rational.ToCents();
                double hz    = Wave.Partials.CentsToHz(c);
                double level = Math.Pow(p.harmonicity, 7.0f);
                //
                timeline.AddPartial(
                    (int)(time * 1000),
                    hz,
                    (int)(ta * 1000),
                    (int)(tr * p.harmonicity * 1000),
                    (float)(gain * level / partials.Length),
                    (float)balance,
                    -4f
                    );
            }
        }
Exemplo n.º 2
0
        static void Render()
        {
            bool makeFrames = false;
            bool makeWave   = true;

            var levelCount = 10;

            Tree tree = new Tree(levelCount);

            Timeline cycles = new Timeline();

            int[] cycleLevels = cycles.CycleLevels;

            if (makeFrames)
            {
                TowerView view = new TowerView(levelCount, tree);

                string framesDir = "frames";
                RecreateDirectory(framesDir);

                for (int frameIndex = 0; ; ++frameIndex)
                {
                    // get cycle, level and phase
                    Split(
                        cycles.TimeToCycle(frameIndex / 30.0), // 30 fps
                        out int cycleIndex,
                        out double cyclePhase
                        );
                    if (cycleIndex >= cycleLevels.Length)
                    {
                        break;                                   // end
                    }
                    int levelIndex = cycleIndex >= 0 ? cycleLevels[cycleIndex] : -1;

                    Debug.WriteLine("Render frame {0}", frameIndex);

                    // render
                    string pngFile = String.Format(
                        //"frame_c{0:00}_l{1:00}_f{2:0000}.png", cycleIndex, levelIndex, frameIndex -- Pattern type 'glob' was selected but globbing is not supported by this libavformat build
                        "frame_f{0:0000}.png", frameIndex
                        );

                    Image image = view.RenderLevelPhase(levelIndex, cyclePhase);
                    image.WritePng(
                        System.IO.Path.Join(framesDir, pngFile),
                        true
                        );
                }
            }

            if (makeWave)
            {
                string waveFile = "towerOfHanoi1.wav";

                var waveFormat = new Wave.WaveFormat {
                    bytesPerSample = 2, sampleRate = 44100, channels = 2
                };
                var waveTimeline = new Wave.PartialTimeline(waveFormat);

                // define a rational note for each disk
                Rational[] diskRationals = new Rational[levelCount];
                for (int i = 0; i < levelCount; ++i)
                {
                    diskRationals[i] = Rational.Prime(i);
                }

                // define partials per level
                Partial[][]  levelPartials = new Partial[levelCount][];
                IHarmonicity harmonicity   = HarmonicityUtils.CreateHarmonicity("Barlow", normalize: true);
                for (int levelIndex = 1; levelIndex < levelCount; ++levelIndex)
                {
                    Rational[] subgroup = Rational.Primes(primeCount: levelIndex);
                    levelPartials[levelIndex] = Partial.MakePartials(harmonicity, subgroup, 20);
                }

                // fill wave timeline
                for (int cycleIndex = 0; cycleIndex < cycleLevels.Length; ++cycleIndex)
                {
                    int levelIndex = cycleLevels[cycleIndex];
                    int stateCount = tree.GetStateCount(levelIndex);

                    Debug.WriteLine("Cycle {0}. Level {1}", cycleIndex, levelIndex);

                    for (int stateIndex = 0; stateIndex < (stateCount - 1); ++stateIndex)   // last state has no step
                    // get rational to play note
                    {
                        int      rodIndex;
                        int[]    disks = tree.GetStepDestinationRod(levelIndex, stateIndex, out rodIndex);
                        int      disk  = disks.Last(); // moved disk
                        Rational r     = diskRationals[disk];
                        //
                        int[] skipped = Enumerable.Range(0, disk).Except(disks).ToArray();
                        if (skipped.Any())
                        {
                            // make inversion from sum of skipped intervals
                            Rational sum = Rational.One;
                            foreach (int s in skipped)
                            {
                                sum *= diskRationals[s];
                            }
                            r = sum / r;
                            // add some octaves to make closer to "rod height" interval
                            Rational limit = diskRationals[disks.Length - 1];
                            while (r < limit)
                            {
                                r *= 2;
                            }
                        }

                        Debug.WriteLine("  State {0}/{1}. Move disk {2}; skipped {3} -> play {4}",
                                        stateIndex + 1, stateCount,
                                        diskRationals[disk],
                                        skipped.Length == 0
                                ? "none"
                                : String.Join(",", skipped.Select(s => diskRationals[s].ToString())),
                                        r);

                        // gain
                        double diskNormal = (double)disk / levelIndex;  // [0..1)
                        double gain       = Math.Pow(7.0, -diskNormal); // always 1.0 for disk 0
                        // time & duration
                        int    diskLevel = disk + 1;
                        double p0        = (double)(stateIndex + 1) / stateCount;
                        double p1        = p0 + 1.0 / (1 << diskLevel);
                        double t0        = cycles.CycleToTime(cycleIndex + p0);
                        double t1        = cycles.CycleToTime(cycleIndex + p1);
                        double duration  = t1 - t0;
                        duration = Math.Max(1.2 / (1 << disk), duration); // set min length here. fine tuning for first cycles
                        // balance
                        double rodAngle = TowerView.GetLevelPhaseRodAngle(rodIndex, p0);
                        double balance  = Math.Cos(rodAngle); // -1 .. 1
                        //
                        AddNote(
                            waveTimeline,
                            t0, duration, gain * 1.62,
                            r.ToCents() - (1200 * 3),
                            levelPartials[levelIndex],
                            balance * 0.62
                            );
                    }
                }

                // export wave timeline to file
                Debug.WriteLine("Writing {0}", (object)waveFile);
                using (var w = new Wave.WaveWriter(waveFormat, waveFile)) {
                    byte[] buffer = new byte[waveFormat.bytesPerSample * waveFormat.sampleRate]; // for 1 sec
                    while (waveTimeline.Fill(buffer))
                    {
                        w.Write(buffer);
                    }
                }
            }

            // To join wave and frames
            //  https://trac.ffmpeg.org/wiki/Slideshow
            //  C:\Users\Massalogin\Downloads\Programs\ffmpeg-win-2.2.2\ffmpeg.exe -r 30 -i frames/frame_f%04d.png -i towerOfHanoi1.wav -r 30 -y out3.mp4
        }