private void InitSoundEngines() { // Init sound var availableOutputs = new List <SoundSettings.EOutput>(); availableOutputs.Add(SoundSettings.EOutput.None); #if USE_MIDI try { _midiPlayer = new Midi.MidiPlayer(0); //!!! make device index configurable availableOutputs.Add(SoundSettings.EOutput.Midi); } catch (Exception ex) { Console.Error.WriteLine("Can't initialize Midi: {0}", ex.Message); } #endif #if USE_WAVE try { var format = new Wave.WaveFormat { bytesPerSample = 2, sampleRate = 22050, channels = 1, }; _waveEngine = new Wave.WaveEngine(format, bufferLengthMs: 60); _partialProvider = new Wave.PartialProvider(); _waveEngine.SetSampleProvider(_partialProvider); availableOutputs.Add(SoundSettings.EOutput.Wave); availableOutputs.Add(SoundSettings.EOutput.WavePartialsTempered); } catch (Exception ex) { Console.Error.WriteLine("Can't initialize WaveEngine: {0}", ex.Message); } #endif _availableSoundOutputs = availableOutputs.ToArray(); // Find controls _comboBoxSoundOutput = ExpectControl <ComboBox>(this, "comboBoxSoundOutput"); _comboBoxSoundOutput.Items = _availableSoundOutputs; //_textBoxWavePartials = ExpectControl<TextBox2>(this, "textBoxWavePartials"); }
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 }