Magically import images and more into Gamecraft as blocks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

147 lines
5.7KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using GamecraftModdingAPI;
  5. using GamecraftModdingAPI.Players;
  6. using GamecraftModdingAPI.Blocks;
  7. using GamecraftModdingAPI.Utility;
  8. using Melanchall.DryWetMidi.Core;
  9. using Melanchall.DryWetMidi.Devices;
  10. using Melanchall.DryWetMidi.Interaction;
  11. using Pixi.Common;
  12. using Unity.Mathematics;
  13. namespace Pixi.Audio
  14. {
  15. public class MidiImporter : Importer
  16. {
  17. public int Priority { get; } = 1;
  18. public bool Optimisable { get; } = false;
  19. public string Name { get; } = "Midi~Spell";
  20. public BlueprintProvider BlueprintProvider { get; } = null;
  21. private Dictionary<string, MidiFile> openFiles = new Dictionary<string, MidiFile>();
  22. public static bool ThreeDee = false;
  23. public static float Spread = 1f;
  24. public bool Qualifies(string name)
  25. {
  26. return name.EndsWith(".mid", StringComparison.InvariantCultureIgnoreCase);
  27. }
  28. public BlockJsonInfo[] Import(string name)
  29. {
  30. MidiFile midi = MidiFile.Read(name);
  31. openFiles[name] = midi;
  32. Logging.MetaLog($"Found {midi.GetNotes().Count()} notes over {midi.GetDuration<MidiTimeSpan>().TimeSpan} time units");
  33. BlockJsonInfo[] blocks = new BlockJsonInfo[(midi.GetNotes().Count() * 2) + 2];
  34. #if DEBUG
  35. // test (for faster, but incomplete, imports)
  36. if (blocks.Length > 102) blocks = new BlockJsonInfo[102];
  37. #endif
  38. // convert Midi notes to sfx blocks
  39. Dictionary<long, uint> breadthCache = new Dictionary<long, uint>();
  40. uint count = 0;
  41. foreach (Note n in midi.GetNotes())
  42. {
  43. // even blocks are counters,
  44. long microTime = n.TimeAs<MetricTimeSpan>(midi.GetTempoMap()).TotalMicroseconds;
  45. float breadth = 1f;
  46. if (breadthCache.ContainsKey(microTime))
  47. {
  48. breadth += breadthCache[microTime]++;
  49. }
  50. else
  51. {
  52. breadthCache[microTime] = 1;
  53. }
  54. blocks[count] = new BlockJsonInfo
  55. {
  56. name = GamecraftModdingAPI.Blocks.BlockIDs.Timer.ToString(),
  57. position = new float[] { breadth * 0.2f * Spread, 2 * 0.2f, microTime * 0.00001f * 0.2f * Spread},
  58. rotation = new float[] { 0, 0, 0},
  59. color = new float[] { -1, -1, -1},
  60. scale = new float[] { 1, 1, 1},
  61. };
  62. count++;
  63. blocks[count] = new BlockJsonInfo
  64. {
  65. name = GamecraftModdingAPI.Blocks.BlockIDs.SFXBlockInstrument.ToString(),
  66. position = new float[] { breadth * 0.2f * Spread, 1 * 0.2f, microTime * 0.00001f * 0.2f * Spread},
  67. rotation = new float[] { 0, 0, 0},
  68. color = new float[] { -1, -1, -1},
  69. scale = new float[] { 1, 1, 1},
  70. };
  71. count++;
  72. #if DEBUG
  73. // test (for faster, but incomplete, imports)
  74. if (count >= 100) break;
  75. #endif
  76. }
  77. // playback IO (reset & play)
  78. blocks[count] = new BlockJsonInfo
  79. {
  80. name = GamecraftModdingAPI.Blocks.BlockIDs.SimpleConnector.ToString(),
  81. position = new float[] { -0.2f, 2 * 0.2f, 0},
  82. rotation = new float[] { 0, 0, 0},
  83. color = new float[] { -1, -1, -1},
  84. scale = new float[] { 1, 1, 1},
  85. }; // play is second last (placed above reset)
  86. count++;
  87. blocks[count] = new BlockJsonInfo
  88. {
  89. name = GamecraftModdingAPI.Blocks.BlockIDs.SimpleConnector.ToString(),
  90. position = new float[] { -0.2f, 1 * 0.2f, 0},
  91. rotation = new float[] { 0, 0, 0},
  92. color = new float[] { -1, -1, -1},
  93. scale = new float[] { 1, 1, 1},
  94. }; // reset is last (placed below play)
  95. return blocks;
  96. }
  97. public void PreProcess(string name, ref ProcessedVoxelObjectNotation[] blocks)
  98. {
  99. Player p = new Player(PlayerType.Local);
  100. float3 pos = p.Position;
  101. for (int i = 0; i < blocks.Length; i++)
  102. {
  103. blocks[i].position += pos;
  104. }
  105. }
  106. public void PostProcess(string name, ref Block[] blocks)
  107. {
  108. // playback IO
  109. LogicGate startConnector = blocks[blocks.Length - 2].Specialise<LogicGate>();
  110. LogicGate resetConnector = blocks[blocks.Length - 1].Specialise<LogicGate>();
  111. uint count = 0;
  112. foreach (Note n in openFiles[name].GetNotes())
  113. {
  114. // set timing info
  115. Timer t = blocks[count].Specialise<Timer>();
  116. t.Start = 0;
  117. t.End = n.TimeAs<MetricTimeSpan>(openFiles[name].GetTempoMap()).TotalMicroseconds * 0.000001f;
  118. count++;
  119. // set notes info
  120. SfxBlock sfx = blocks[count].Specialise<SfxBlock>();
  121. sfx.Pitch = n.NoteNumber - 60; // In MIDI, 60 is middle C, but GC uses 0 for middle C
  122. sfx.TrackIndex = 5; // Piano
  123. sfx.Is3D = ThreeDee;
  124. count++;
  125. // connect wires
  126. t.Connect(0, sfx, 0);
  127. startConnector.Connect(0, t, 0);
  128. resetConnector.Connect(0, t, 2);
  129. #if DEBUG
  130. // test (for faster, but incomplete, imports)
  131. if (count >= 100) break;
  132. #endif
  133. }
  134. openFiles.Remove(name);
  135. }
  136. }
  137. }