@pro-gram/flux 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.txt +4 -3
- package/README.md +42 -8
- package/dependencies.json +2 -1
- package/package.json +3 -3
- package/payload/DrumBuddy-Setup.exe +0 -0
- package/scripts/install.js +4 -2
- package/source/AssetBootstrap.cs +1 -1
- package/source/Audio Decoders.txt +230 -0
- package/source/AudioDeviceSettings.cs +1 -1
- package/source/AudioEndpointWatcher.cs +25 -0
- package/source/AudioOutputs.cs +40 -60
- package/source/AudioWorkspace.cs +2 -1
- package/source/BrandArtwork.cs +2 -2
- package/source/Build Instructions.txt +9 -3
- package/source/Build.ps1 +21 -9
- package/source/EditorCompactLayout.cs +36 -0
- package/source/EditorPlaybackTests.cs +87 -0
- package/source/EditorSelectionTests.cs +73 -0
- package/source/EditorToolInteractions.cs +227 -0
- package/source/EditorTools.cs +78 -0
- package/source/EditorToolsTests.cs +93 -0
- package/source/EditorView.cs +247 -75
- package/source/Flux-Logo.png +0 -0
- package/source/InterfaceSettings.cs +10 -4
- package/source/InterfaceTests.cs +6 -0
- package/source/Main.cs +19 -13
- package/source/Main.xaml +25 -28
- package/source/PlaybackTests.cs +13 -6
- package/source/Quick Start.txt +28 -6
- package/source/SettingsWindow.cs +4 -3
- package/source/Setup.cs +9 -5
- package/source/Third Party Notices.txt +4 -3
- package/source/Timeline.cs +24 -17
- package/source/TimelineZoom.cs +21 -0
- package/source/WaveformDetail.cs +104 -0
- package/source/WindowsAudioPlayer.cs +137 -0
- package/source/WorkspaceTests.cs +7 -3
- package/source/ZoomTests.cs +95 -0
|
@@ -29,7 +29,7 @@ namespace DrumBuddy
|
|
|
29
29
|
if ((mods & ModifierKeys.Alt) != 0) text += "Alt+";
|
|
30
30
|
if ((mods & ModifierKeys.Shift) != 0) text += "Shift+";
|
|
31
31
|
if ((mods & ModifierKeys.Windows) != 0) text += "Win+";
|
|
32
|
-
return text + (Key == "Back" ? "Backspace" : Key == "OemComma" ? "," : Key.StartsWith("D") && Key.Length == 2 && char.IsDigit(Key[1]) ? Key.Substring(1) : Key);
|
|
32
|
+
return text + (Key == "Back" ? "Backspace" : Key == "OemComma" ? "," : Key == "OemPlus" ? "=" : Key == "OemMinus" ? "-" : Key.StartsWith("D") && Key.Length == 2 && char.IsDigit(Key[1]) ? Key.Substring(1) : Key);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -53,19 +53,22 @@ namespace DrumBuddy
|
|
|
53
53
|
public static readonly string SettingsFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DrumBuddy", "interface-settings.xml");
|
|
54
54
|
public static readonly Dictionary<string, string> Actions = new Dictionary<string, string> {
|
|
55
55
|
{ "play", "Play / pause" }, { "stop", "Stop playback" }, { "record", "Start / stop recording" },
|
|
56
|
-
{ "split", "Split at playhead" }, { "scissors", "Toggle
|
|
56
|
+
{ "split", "Split at playhead" }, { "scissors", "Toggle slice cursor (legacy)" }, { "selectTool", "Selection cursor" }, { "copy", "Copy selection" },
|
|
57
|
+
{ "slipTool", "Slip source contents tool" }, { "fadeTool", "Fade tool" }, { "duplicate", "Duplicate selected audio" }, { "repeatClips", "Repeat selected audio" }, { "muteClips", "Mute / unmute selected clips" }, { "crossfade", "Crossfade selected clips" }, { "clearFades", "Clear clip fades" },
|
|
57
58
|
{ "cut", "Cut selection" }, { "paste", "Paste at playhead" }, { "delete", "Delete selection" },
|
|
58
59
|
{ "deleteAlternate", "Delete selection (alternate)" },
|
|
59
60
|
{ "undo", "Undo" }, { "redo", "Redo" }, { "save", "Save session" }, { "saveAs", "Save session as" },
|
|
60
61
|
{ "open", "Open session" }, { "new", "New session" }, { "import", "Import audio" }, { "export", "Export mix" },
|
|
61
62
|
{ "start", "Go to start" }, { "end", "Go to end" }, { "home", "Open Home" }, { "editor", "Open Editor" },
|
|
62
63
|
{ "converter", "Open Converter" }, { "player", "Open Media Player" }, { "settings", "Open Settings" }
|
|
63
|
-
, { "timelineSettings", "Open Timeline settings" }
|
|
64
|
+
, { "timelineSettings", "Open Timeline settings" }, { "zoomIn", "Editor zoom in" }, { "zoomInPlus", "Editor zoom in (+ key)" }, { "zoomInKeypad", "Editor zoom in (keypad)" }, { "zoomOut", "Editor zoom out" }, { "zoomOutKeypad", "Editor zoom out (keypad)" }, { "zoomFit", "Fit project in Editor" }, { "zoomSelection", "Fit selection in Editor" }
|
|
64
65
|
};
|
|
65
66
|
public static InterfaceSettings Defaults()
|
|
66
67
|
{
|
|
67
68
|
var settings = new InterfaceSettings { KeymapRevision = 1 };
|
|
68
|
-
string[] simple = { "play:Space", "stop:Space:Shift", "record:R", "split:S", "scissors:
|
|
69
|
+
string[] simple = { "play:Space", "stop:Space:Shift", "record:R", "split:S", "scissors:None", "copy:C:Control", "cut:X:Control", "paste:V:Control", "delete:Back", "deleteAlternate:Delete", "undo:Z:Control", "redo:Y:Control", "save:S:Control", "saveAs:S:Control,Shift", "open:O:Control", "new:N:Control", "import:I:Control", "export:E:Control", "start:Home", "end:End", "home:D1:Control", "editor:D2:Control", "converter:D3:Control", "player:D4:Control", "settings:OemComma:Control", "timelineSettings:None" };
|
|
70
|
+
simple = simple.Concat(new[] { "selectTool:V", "zoomIn:OemPlus:Control", "zoomInPlus:OemPlus:Control,Shift", "zoomInKeypad:Add:Control", "zoomOut:OemMinus:Control", "zoomOutKeypad:Subtract:Control", "zoomFit:D0:Control", "zoomSelection:D0:Control,Shift" }).ToArray();
|
|
71
|
+
simple = simple.Concat(new[] { "slipTool:Y", "fadeTool:F", "duplicate:D:Control", "repeatClips:L:Control", "muteClips:M", "crossfade:X", "clearFades:None" }).ToArray();
|
|
69
72
|
foreach (string text in simple) {
|
|
70
73
|
string[] parts = text.Split(':');
|
|
71
74
|
settings.Keybinds.Add(new ShortcutBinding { Action = parts[0], Key = parts[1], Modifiers = parts.Length > 2 ? (int)(ModifierKeys)Enum.Parse(typeof(ModifierKeys), parts[2]) : 0 });
|
|
@@ -81,6 +84,7 @@ namespace DrumBuddy
|
|
|
81
84
|
Validate(settings);
|
|
82
85
|
}
|
|
83
86
|
catch { settings = Defaults(); settings.AudioOutputKey = legacyOutput ?? "default"; settings.AudioInputKey = legacyInput; }
|
|
87
|
+
settings.AudioOutputKey = AudioOutputs.NormalizeKey(settings.AudioOutputKey);
|
|
84
88
|
Current = settings; ThemePalette.Apply(settings.Theme == "Dark"); AudioOutputs.LatencyMilliseconds = settings.PlaybackLatency;
|
|
85
89
|
}
|
|
86
90
|
public static void Validate(InterfaceSettings settings)
|
|
@@ -102,6 +106,7 @@ namespace DrumBuddy
|
|
|
102
106
|
}
|
|
103
107
|
public static void Upgrade(InterfaceSettings settings)
|
|
104
108
|
{
|
|
109
|
+
settings.AudioOutputKey = AudioOutputs.NormalizeKey(settings.AudioOutputKey);
|
|
105
110
|
if (settings.Grid == null) settings.Grid = new TempoGridOptions();
|
|
106
111
|
if (settings.Keybinds == null) settings.Keybinds = new List<ShortcutBinding>();
|
|
107
112
|
if (settings.KeymapRevision < 1) {
|
|
@@ -162,6 +167,7 @@ namespace DrumBuddy
|
|
|
162
167
|
{ "#F0F8F5", "#192139" }, { "#E4ECEA", "#202536" }, { "#DCEFEB", "#1C2943" }, { "#EDF3F1", "#171B26" },
|
|
163
168
|
{ "#D6E1DF", "#2C3349" }, { "#E0E8E5", "#242B3C" }, { "#DAE5E0", "#2A2B43" }, { "#B9DDD2", "#334567" },
|
|
164
169
|
{ "#438B77", "#59CCFF" }, { "#5E9D89", "#7D9CFA" }, { "#CBD8D4", "#353951" }, { "#D0772C", "#FFB55E" },
|
|
170
|
+
{ "#DAD5FA", "#3A315D" }, { "#7359C7", "#B497FF" },
|
|
165
171
|
{ "#D17626", "#FFB55E" }, { "#277FA5", "#64CBFF" }, { "#386BA8", "#A28CFF" }, { "#5596C8", "#6BAAFF" },
|
|
166
172
|
{ "#15877C", "#45B8F0" }, { "#16887D", "#45B8F0" }, { "#B52C36", "#EE657D" }, { "#D16A19", "#B3A0FF" }
|
|
167
173
|
};
|
package/source/InterfaceTests.cs
CHANGED
|
@@ -74,6 +74,7 @@ namespace DrumBuddy
|
|
|
74
74
|
Picture(new Image { Source = BrandArtwork.Load(), Stretch = Stretch.Uniform }, Path.Combine(root, "refined-vector-logo.png"), 840, 640);
|
|
75
75
|
var main = new MainWindow(Path.Combine(root, "legacy-preferences.xml"), true);
|
|
76
76
|
Check(main.NativeWindow.Title == "Flux", "Use exactly Flux as the window title");
|
|
77
|
+
Check(object.ReferenceEquals(main.NativeWindow.Icon, BrandArtwork.Raster()), "Use the current Flux mark directly for the window and taskbar icon");
|
|
77
78
|
main.OpenEditorForTest(); Check(main.IntegratedRecorderForTest, "Mount recording controls inside the main Editor while retaining named input controls");
|
|
78
79
|
string demoPath = Path.Combine(root, "Generated demo.wav"); var demo = new float[Audio.Rate * 8];
|
|
79
80
|
for (int i = 0; i < demo.Length; i++) demo[i] = (float)(.7 * Math.Exp(-(i % (Audio.Rate / 2)) / 3000.0) * Math.Sin(i * .06));
|
|
@@ -84,6 +85,7 @@ namespace DrumBuddy
|
|
|
84
85
|
for (int i = 0; i < names.Length; i++) { demoSession.Tracks[i].Name = names[i]; demoSession.Regions.Add(new TimelineRegion { Source = demoClip.Source, Name = names[i], Track = i, Start = (i % 3) * Audio.Rate / 2, Length = (6 - i % 3) * Audio.Rate }); }
|
|
85
86
|
main.EditorFixtureForTest(demoSession, new Dictionary<string, Clip> { { demoClip.Source, demoClip } });
|
|
86
87
|
Check(!Buttons(main.RecordingPaneForTest.MainContent).Any(b => new[] { "Cut", "Copy", "Paste" }.Contains(b.Content as string)), "Remove Cut, Copy and Paste buttons from the editor toolbar");
|
|
88
|
+
Check(!Buttons(main.RecordingPaneForTest.MainContent).Any(b => ((b.Content as string) ?? "").Contains("Split") || ((b.Content as string) ?? "").Contains("Scissors")), "Keep split on the S action and remove redundant scissors/split toolbar buttons");
|
|
87
89
|
Check(main.NativeWindow.FindName("SettingsTimeline") != null, "Expose Timeline / grid in Settings");
|
|
88
90
|
var initialGrid = InterfacePreferences.Current.Copy(); initialGrid.Grid = main.GridForTest;
|
|
89
91
|
string gridSettingsFile = Path.Combine(root, "saved-grid-settings.xml");
|
|
@@ -105,7 +107,11 @@ namespace DrumBuddy
|
|
|
105
107
|
main.SetGridForTest(new TempoGridOptions { Division = 128, Triplet = true }, 1280); Picture((FrameworkElement)main.NativeWindow.Content, Path.Combine(root, "editor-grid-128-triplet-dark.png"), 1280, 800);
|
|
106
108
|
main.EditorFixtureForTest(demoSession, new Dictionary<string, Clip> { { demoClip.Source, demoClip } }); main.SetGridForTest(demoSession.Grid, 40); main.OpenEditorForTest();
|
|
107
109
|
Picture((FrameworkElement)main.NativeWindow.Content, Path.Combine(root, "editor-shell-dark.png"), 1280, 800);
|
|
110
|
+
Check(main.EditorTimelineHeightForTest >= 550, "Reserve at least 550 vertical pixels for the timeline at 1280 x 800");
|
|
111
|
+
log.Add("Timeline viewport at 1280 x 800: " + main.EditorTimelineHeightForTest.ToString("0") + " px.");
|
|
108
112
|
Picture((FrameworkElement)main.NativeWindow.Content, Path.Combine(root, "editor-compact-dark.png"), 850, 560);
|
|
113
|
+
Check(main.EditorTimelineHeightForTest >= 250, "Keep a usable timeline after packing controls at 850 x 560");
|
|
114
|
+
log.Add("Timeline viewport at 850 x 560: " + main.EditorTimelineHeightForTest.ToString("0") + " px.");
|
|
109
115
|
main.RecordingPanelForTest(); Picture((FrameworkElement)main.NativeWindow.Content, Path.Combine(root, "editor-record-dark.png"), 1280, 800);
|
|
110
116
|
Picture((FrameworkElement)main.NativeWindow.Content, Path.Combine(root, "editor-record-compact-dark.png"), 850, 560);
|
|
111
117
|
main.RecordingPaneForTest.SetBottom(true); Picture((FrameworkElement)main.NativeWindow.Content, Path.Combine(root, "editor-record-bottom-compact-dark.png"), 850, 560);
|
package/source/Main.cs
CHANGED
|
@@ -20,7 +20,7 @@ using NAudio.Wave.SampleProviders;
|
|
|
20
20
|
|
|
21
21
|
[assembly: AssemblyTitle("Flux")]
|
|
22
22
|
[assembly: AssemblyDescription("PCM/float media player, 16-track audio editor, H5studio recording and R16 control")]
|
|
23
|
-
[assembly: AssemblyVersion("1.
|
|
23
|
+
[assembly: AssemblyVersion("1.6.1.0")]
|
|
24
24
|
|
|
25
25
|
namespace DrumBuddy
|
|
26
26
|
{
|
|
@@ -34,9 +34,8 @@ namespace DrumBuddy
|
|
|
34
34
|
public sealed class OutputChoice
|
|
35
35
|
{
|
|
36
36
|
public string Name, Driver, EndpointId;
|
|
37
|
-
public
|
|
38
|
-
public
|
|
39
|
-
public string Key { get { return Compatibility ? "compat:" + (EndpointId ?? "default") : Driver != null ? "asio:" + Driver : EndpointId != null ? "windows:" + EndpointId : "default"; } }
|
|
37
|
+
public string DefaultEndpointId;
|
|
38
|
+
public string Key { get { return Driver != null ? "asio:" + Driver : EndpointId != null ? "windows:" + EndpointId : "default"; } }
|
|
40
39
|
public override string ToString() { return Name; }
|
|
41
40
|
}
|
|
42
41
|
public sealed class ModeChoice
|
|
@@ -102,8 +101,7 @@ namespace DrumBuddy
|
|
|
102
101
|
window.Height = Math.Min(window.Height, SystemParameters.WorkArea.Height - 30);
|
|
103
102
|
window.Width = Math.Min(window.Width, SystemParameters.WorkArea.Width - 30);
|
|
104
103
|
C<Image>("BrandLogo").Source = BrandArtwork.Raster();
|
|
105
|
-
window.Icon =
|
|
106
|
-
Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
|
|
104
|
+
window.Icon = BrandArtwork.Raster();
|
|
107
105
|
try { using (var s = File.OpenRead(settings)) prefs = (Preferences)new XmlSerializer(typeof(Preferences)).Deserialize(s); }
|
|
108
106
|
catch { prefs = new Preferences(); }
|
|
109
107
|
if (!testMode) InterfacePreferences.Load(prefs.AudioOutputKey, prefs.AudioInputKey);
|
|
@@ -253,7 +251,7 @@ namespace DrumBuddy
|
|
|
253
251
|
var inputs = AudioInputs.Choices(); configuringCapture = true; C<ComboBox>("InputDevice").ItemsSource = inputs; C<ComboBox>("InputDevice").SelectedItem = inputs.FirstOrDefault(c => c.Driver == null); configuringCapture = false;
|
|
254
252
|
ConfigureRecordingDevice(); editorView.ShowRecordingOptions(true);
|
|
255
253
|
}
|
|
256
|
-
internal void SetThemeForTest(bool dark) { ThemePalette.Apply(dark); ThemePalette.Install(window); C<Button>("ThemeToggle").Content = dark ? "Light
|
|
254
|
+
internal void SetThemeForTest(bool dark) { ThemePalette.Apply(dark); ThemePalette.Install(window); C<Button>("ThemeToggle").Content = dark ? "Light" : "Dark"; }
|
|
257
255
|
internal void OpenConverterForTest() { SwitchPage(false); }
|
|
258
256
|
internal DockablePane ConverterPaneForTest { get { return converterPane; } }
|
|
259
257
|
internal DockablePane RecordingPaneForTest { get { return editorView.RecordingPaneForTest; } }
|
|
@@ -262,6 +260,7 @@ namespace DrumBuddy
|
|
|
262
260
|
internal void ApplySettingsForTest(InterfaceSettings value) { InterfacePreferences.Current = value.Copy(); ThemePalette.Apply(value.Theme == "Dark"); ApplyInterfaceSettings(); }
|
|
263
261
|
internal TempoGridOptions GridForTest { get { return editorView.GridOptions; } }
|
|
264
262
|
internal void SetGridForTest(TempoGridOptions value, double zoom) { editorView.ApplyGridOptions(value); editorView.ZoomForTest(zoom); }
|
|
263
|
+
internal double EditorTimelineHeightForTest { get { return editorView.TimelineHeightForTest; } }
|
|
265
264
|
internal bool IntegratedRecorderForTest { get { return editorView != null && C<StackPanel>("RecordPage").Parent != null && C<ComboBox>("InputDevice") != null; } }
|
|
266
265
|
private static Brush Brush(string hex) { return ThemePalette.Brush(hex); }
|
|
267
266
|
private void Status(string text) { C<TextBlock>("StatusText").Text = text; C<Border>("StatusHost").Visibility = string.IsNullOrWhiteSpace(text) ? Visibility.Collapsed : Visibility.Visible; }
|
|
@@ -411,7 +410,7 @@ namespace DrumBuddy
|
|
|
411
410
|
}
|
|
412
411
|
private void ApplyInterfaceSettings()
|
|
413
412
|
{
|
|
414
|
-
ThemePalette.Install(window); C<Button>("ThemeToggle").Content = ThemePalette.Dark ? "Light
|
|
413
|
+
ThemePalette.Install(window); C<Button>("ThemeToggle").Content = ThemePalette.Dark ? "Light" : "Dark";
|
|
415
414
|
var settings = InterfacePreferences.Current;
|
|
416
415
|
string gridSignature = settings.Grid.Signature();
|
|
417
416
|
if (appliedGridDefaults != gridSignature) { appliedGridDefaults = gridSignature; if (editorView != null) editorView.ApplyGridOptions(settings.Grid); }
|
|
@@ -455,8 +454,8 @@ namespace DrumBuddy
|
|
|
455
454
|
string action = InterfacePreferences.Resolve(key, System.Windows.Input.Keyboard.Modifiers);
|
|
456
455
|
if (action == null) return;
|
|
457
456
|
bool text = System.Windows.Input.Keyboard.FocusedElement is TextBox || System.Windows.Input.Keyboard.FocusedElement is ComboBox;
|
|
458
|
-
if (text && !new[] { "save", "saveAs", "open", "new", "settings" }.Contains(action)) return;
|
|
459
|
-
if (e.IsRepeat) { e.Handled = true; return; }
|
|
457
|
+
if (text && !action.StartsWith("zoom", StringComparison.Ordinal) && !new[] { "save", "saveAs", "open", "new", "settings" }.Contains(action)) return;
|
|
458
|
+
if (e.IsRepeat && !action.StartsWith("zoomIn", StringComparison.Ordinal) && !action.StartsWith("zoomOut", StringComparison.Ordinal)) { e.Handled = true; return; }
|
|
460
459
|
if (workspace != 3 && !(workspace == 2 && new[] { "play", "stop", "start", "end" }.Contains(action)) && !new[] { "home", "editor", "converter", "player", "settings", "timelineSettings", "new", "open", "save", "saveAs", "import", "export" }.Contains(action)) return;
|
|
461
460
|
e.Handled = true; await ExecuteInterfaceAction(action);
|
|
462
461
|
}
|
|
@@ -708,7 +707,7 @@ namespace DrumBuddy
|
|
|
708
707
|
private void ShowHelp()
|
|
709
708
|
{
|
|
710
709
|
MessageBox.Show(window,
|
|
711
|
-
"AUDIO DEVICES\nUse Audio devices at the top of the app to choose playback output and recording input. Test speakers plays a short quiet tone.
|
|
710
|
+
"AUDIO DEVICES\nUse Audio devices at the top of the app to choose playback output and recording input. Test speakers plays a short quiet tone. Windows default follows the current Windows speakers/headphones; named outputs select a specific device. Save diagnostics exports device formats and recent playback errors. Windows microphones capture 16-bit PCM; use H5studio ASIO for native 32-bit float.\n\n" +
|
|
712
711
|
"HOME AND CONVERTER\nOpen Converter from View or Home. Drop audio or video, choose an output type and click Convert files. WAV PCM, WAV float, AIFF, FLAC, MP3 and AAC copies are supported. Output settings can be resized, collapsed, or displayed on the side or bottom. The source is preserved.\n\n" +
|
|
713
712
|
"Set safe level brings the loudest peak to −1 dBFS. Prevent clipping caps excessive gain evenly; it preserves the dynamics of your drums.\n\n" +
|
|
714
713
|
"ZOOM R16\nExports are 44.1 kHz PCM WAV, mono or stereo, with names up to 8 uppercase characters. Use the bit depth of your R16 project. Copy WAVs to that project's AUDIO folder, then assign them to tracks on the R16.\n\n" +
|
|
@@ -721,26 +720,33 @@ namespace DrumBuddy
|
|
|
721
720
|
"Install Drivers opens a short guide and links to Zoom's official driver download pages.\n\n" +
|
|
722
721
|
"MEDIA PLAYER\nOpen audio or video to listen. Gain / Save amplifies or reduces playback and saves adjusted WAV copies; Playback volume changes listening level only. Reset, Set safe level and Prevent clipping are available. Choose speakers/headphones under Audio output. Gain / Save can be resized, collapsed, or displayed on the side or bottom.\n\n" +
|
|
723
722
|
"EDITOR\nA 16-track audio timeline. Drag the upper ruler and release to place the playhead. The left edge snaps to zero; Home/To start goes to zero and End goes to the timeline end. Hold Alt to bypass snapping. Drag across a track to select time; two thinner blue sticks mark the selection start/end. Drag their lower-ruler handles to adjust the boundaries. Cut/copy/paste, split, undo/redo, Space to play/pause and Ctrl+S to save. Cuts leave a gap. Choose Selected track or All 16 tracks for edits. Save .dbedit sessions and export PCM or float WAV mixes. Original source files are preserved.\n\n" +
|
|
724
|
-
"THEME AND SETTINGS\nUse the top-right switch for dark or light theme. Settings has categories for General, Audio and Keybinds. Click Edit on a keybind and press the new shortcut immediately. Save writes the changes; leaving with pending changes asks whether to save them. S splits at the playhead;
|
|
725
|
-
"Basic tools run locally without an account. This release is for Windows; macOS is planned.\n\nFlux 1.
|
|
723
|
+
"THEME AND SETTINGS\nUse the top-right switch for dark or light theme. Settings has categories for General, Audio and Keybinds. Click Edit on a keybind and press the new shortcut immediately. Save writes the changes; leaving with pending changes asks whether to save them. S splits at the playhead; V returns to the selection cursor.\n\n" +
|
|
724
|
+
"Basic tools run locally without an account. This release is for Windows; macOS is planned.\n\nFlux 1.6.1 · FFmpeg + NAudio",
|
|
726
725
|
"Flux — quick help",MessageBoxButton.OK,MessageBoxImage.Information);
|
|
727
726
|
}
|
|
728
727
|
}
|
|
729
728
|
|
|
730
729
|
public static class Program
|
|
731
730
|
{
|
|
731
|
+
[System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
|
|
732
|
+
private static extern int SetCurrentProcessExplicitAppUserModelID(string appId);
|
|
732
733
|
[STAThread]
|
|
733
734
|
public static int Main(string[] args)
|
|
734
735
|
{
|
|
735
736
|
if (args.Length > 0 && args[0] == "--self-test") return SelfTest.Run(args.Skip(1).ToArray());
|
|
736
737
|
if (args.Length > 0 && args[0] == "--float-self-test") return FloatRecordingTests.Run(args.Skip(1).ToArray());
|
|
737
738
|
if (args.Length > 0 && args[0] == "--grid-self-test") return TempoGridTests.Run(args.Skip(1).ToArray());
|
|
739
|
+
if (args.Length > 0 && args[0] == "--zoom-self-test") return ZoomTests.Run(args.Skip(1).ToArray());
|
|
740
|
+
if (args.Length > 0 && args[0] == "--selection-self-test") return EditorSelectionTests.Run(args.Skip(1).ToArray());
|
|
741
|
+
if (args.Length > 0 && args[0] == "--tools-self-test") return EditorToolsTests.Run(args.Skip(1).ToArray());
|
|
738
742
|
if (args.Length > 0 && args[0] == "--workspace-self-test") return WorkspaceTests.Run(args.Skip(1).ToArray());
|
|
739
743
|
if (args.Length > 0 && args[0] == "--interface-self-test") return InterfaceTests.Run(args.Skip(1).ToArray());
|
|
740
744
|
if (args.Length > 0 && args[0] == "--conversion-self-test") return ConversionTests.Run(args.Skip(1).ToArray());
|
|
741
745
|
if (args.Length > 0 && args[0] == "--output-self-test") return PlaybackTests.Run(args.Skip(1).ToArray());
|
|
746
|
+
if (args.Length > 1 && args[0] == "--editor-playback-self-test") return EditorPlaybackTests.Run(args.Skip(1).ToArray());
|
|
742
747
|
if (args.Length > 1 && args[0] == "--audio-diagnostics") { File.WriteAllText(args[1], AudioDiagnostics.Report(null), System.Text.Encoding.UTF8); return 0; }
|
|
743
748
|
if (args.Length > 0 && args[0] == "--control-self-test") return ControlTests.Run(args.Skip(1).ToArray());
|
|
749
|
+
SetCurrentProcessExplicitAppUserModelID("ca.fluxtempo.Flux");
|
|
744
750
|
var app = new Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
|
|
745
751
|
app.DispatcherUnhandledException += delegate(object sender, DispatcherUnhandledExceptionEventArgs e) {
|
|
746
752
|
try { string folder = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"DrumBuddy"); Directory.CreateDirectory(folder); File.AppendAllText(System.IO.Path.Combine(folder,"error.log"),DateTime.Now + " " + e.Exception + Environment.NewLine); } catch { }
|
package/source/Main.xaml
CHANGED
|
@@ -66,7 +66,9 @@
|
|
|
66
66
|
</Style>
|
|
67
67
|
</Window.Resources>
|
|
68
68
|
<DockPanel Margin="16,10,16,10">
|
|
69
|
-
<
|
|
69
|
+
<Grid DockPanel.Dock="Top" Margin="0,0,0,6">
|
|
70
|
+
<Grid.RowDefinitions><RowDefinition Height="26" /><RowDefinition Height="36" /></Grid.RowDefinitions>
|
|
71
|
+
<Menu Margin="-5,0,0,0" FontSize="12" Background="{DynamicResource ThemeF3F5F5}" Foreground="{DynamicResource Theme182C37}">
|
|
70
72
|
<MenuItem Header="_File">
|
|
71
73
|
<MenuItem x:Name="FileNew" Header="New session" />
|
|
72
74
|
<MenuItem x:Name="FileOpen" Header="Open session..." />
|
|
@@ -99,33 +101,28 @@
|
|
|
99
101
|
<MenuItem x:Name="HelpGuide" Header="Quick help..." />
|
|
100
102
|
<MenuItem x:Name="HelpAbout" Header="About Flux..." />
|
|
101
103
|
</MenuItem>
|
|
102
|
-
</Menu>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<Button x:Name="RecordTab" Content="Record" Visibility="Collapsed" Width="90" Margin="0,0,12,0" />
|
|
125
|
-
<Button x:Name="MediaPlayerTab" Content="Media Player" Width="122" Margin="0,0,12,8" />
|
|
126
|
-
<Button x:Name="EditorTab" Content="Editor + Record" Width="140" Margin="0,0,12,8" />
|
|
127
|
-
<Button x:Name="ControlTab" Content="R16 Control" Width="115" Margin="0,0,0,8" />
|
|
128
|
-
</WrapPanel>
|
|
104
|
+
</Menu>
|
|
105
|
+
<Grid Grid.Row="1">
|
|
106
|
+
<Grid.ColumnDefinitions><ColumnDefinition Width="Auto" /><ColumnDefinition /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
|
|
107
|
+
<StackPanel Orientation="Horizontal" Margin="0,0,14,0">
|
|
108
|
+
<Image x:Name="BrandLogo" Width="44" Height="34" Stretch="Uniform" Margin="0,0,5,0" />
|
|
109
|
+
<TextBlock Text="Flux" FontSize="15" FontWeight="SemiBold" VerticalAlignment="Center" />
|
|
110
|
+
</StackPanel>
|
|
111
|
+
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
|
112
|
+
<Button x:Name="HomeTab" Content="Home" MinHeight="26" Width="60" FontSize="12" Padding="7,2" Margin="0,0,4,0" />
|
|
113
|
+
<Button x:Name="RecordTab" Content="Record" Visibility="Collapsed" />
|
|
114
|
+
<Button x:Name="MediaPlayerTab" Content="Player" MinHeight="26" Width="64" FontSize="12" Padding="7,2" Margin="0,0,4,0" />
|
|
115
|
+
<Button x:Name="EditorTab" Content="Editor + Record" MinHeight="26" Width="116" FontSize="12" Padding="7,2" Margin="0,0,4,0" />
|
|
116
|
+
<Button x:Name="ControlTab" Content="R16 Control" MinHeight="26" Width="92" FontSize="12" Padding="7,2" />
|
|
117
|
+
</StackPanel>
|
|
118
|
+
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
|
|
119
|
+
<Button x:Name="ThemeToggle" Content="Light" MinHeight="26" FontSize="12" Padding="8,2" Margin="0,0,5,0" ToolTip="Switch between dark and light themes" />
|
|
120
|
+
<Button x:Name="AudioDevicesButton" Content="Audio devices" MinHeight="26" FontSize="12" Padding="8,2" ToolTip="Select playback and recording devices" />
|
|
121
|
+
<Button x:Name="InstallDriversButton" Content="Install Drivers" Visibility="Collapsed" />
|
|
122
|
+
<Button x:Name="HelpButton" Content="Help" Visibility="Collapsed" />
|
|
123
|
+
</StackPanel>
|
|
124
|
+
</Grid>
|
|
125
|
+
</Grid>
|
|
129
126
|
<Border x:Name="StatusHost" DockPanel.Dock="Bottom" Background="{DynamicResource ThemeF3F5F5}" BorderBrush="{DynamicResource ThemeDFE5E6}" BorderThickness="0,1,0,0" Padding="6,4" Margin="0,6,0,0" Visibility="Collapsed">
|
|
130
127
|
<Grid>
|
|
131
128
|
<Grid.ColumnDefinitions>
|
package/source/PlaybackTests.cs
CHANGED
|
@@ -44,8 +44,10 @@ namespace DrumBuddy
|
|
|
44
44
|
var nativeFormat = new WaveFormatExtensible(48000, 32, 6);
|
|
45
45
|
var exact = new EndpointWaveProvider(new Samples(new float[6], 48000, 6), nativeFormat);
|
|
46
46
|
check(object.ReferenceEquals(exact.WaveFormat, nativeFormat), "Keep the exact native endpoint descriptor, including its speaker mask");
|
|
47
|
-
check(
|
|
48
|
-
"
|
|
47
|
+
check(AudioOutputs.NormalizeKey("compat:default") == "default" && AudioOutputs.NormalizeKey("compat:endpoint") == "windows:endpoint",
|
|
48
|
+
"Migrate retired compatibility selections to the same real Windows endpoint");
|
|
49
|
+
check(WindowsAudioPlayer.Flags(true) == NAudio.CoreAudioApi.AudioClientStreamFlags.EventCallback && WindowsAudioPlayer.Flags(false) == NAudio.CoreAudioApi.AudioClientStreamFlags.None,
|
|
50
|
+
"Open an exact-mix shared stream without redundant Windows conversion flags");
|
|
49
51
|
var mono = ReadAll(AudioOutputs.Adapt(new Samples(new[] { .25f, -.75f, 1.5f }, 44100, 1), 44100, 2));
|
|
50
52
|
check(mono.SequenceEqual(new[] { .25f, .25f, -.75f, -.75f, 1f, 1f }), "Duplicate mono into both headphones and bound playback headroom");
|
|
51
53
|
var stereo = ReadAll(AudioOutputs.Adapt(new Samples(new[] { .5f, -.25f, -1f, .5f }, 44100, 2), 44100, 2));
|
|
@@ -97,16 +99,20 @@ namespace DrumBuddy
|
|
|
97
99
|
{
|
|
98
100
|
string root = Path.GetFullPath(args.Length > 0 ? args[0] : Path.Combine(Path.GetTempPath(), "DrumBuddyOutput_" + Guid.NewGuid().ToString("N")));
|
|
99
101
|
Directory.CreateDirectory(root); var log = new List<string>();
|
|
102
|
+
string oldLog = AudioOutputs.TestErrorLog; AudioOutputs.TestErrorLog = Path.Combine(root, "output-errors.txt");
|
|
100
103
|
try {
|
|
101
|
-
var
|
|
102
|
-
|
|
104
|
+
var listed = AudioOutputs.Choices();
|
|
105
|
+
if (listed.Any(c => c.Name.IndexOf("Compatibility", StringComparison.OrdinalIgnoreCase) >= 0) || listed.Select(c => c.Key).Distinct().Count() != listed.Count) throw new Exception("Output choices contain synthetic or duplicate backend entries.");
|
|
106
|
+
log.Add("PASS List Windows default and real endpoints without compatibility devices");
|
|
107
|
+
var choices = new List<OutputChoice> { listed.First(c => c.Key == "default") };
|
|
108
|
+
var named = listed.FirstOrDefault(c => c.EndpointId == choices[0].DefaultEndpointId);
|
|
103
109
|
if (named != null) choices.Add(named);
|
|
104
110
|
foreach (var choice in choices) foreach (int channels in new[] { 1, 2 }) {
|
|
105
111
|
using (var completed = new ManualResetEvent(false))
|
|
106
112
|
using (var route = AudioOutputs.Open(new Samples(new float[4410 * channels], 44100, channels), choice)) {
|
|
107
113
|
Exception error = null;
|
|
108
114
|
route.Player.PlaybackStopped += delegate(object sender, StoppedEventArgs e) { error = e.Exception; completed.Set(); };
|
|
109
|
-
if (
|
|
115
|
+
if (!(route.Player is WindowsAudioPlayer)) throw new Exception("Expected native Windows shared playback.");
|
|
110
116
|
log.Add("Output: " + route.Description + ", " + route.Counter.WaveFormat.Channels + " channels");
|
|
111
117
|
route.Player.Play();
|
|
112
118
|
if (!completed.WaitOne(5000)) throw new Exception("Timed out while playing silent samples.");
|
|
@@ -116,7 +122,7 @@ namespace DrumBuddy
|
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
bool unavailable = false;
|
|
119
|
-
try { using (AudioOutputs.
|
|
125
|
+
try { using (AudioOutputs.Open(new Samples(new float[4410], 44100, 1), new OutputChoice { EndpointId = "missing-endpoint-for-test" })) { } }
|
|
120
126
|
catch (IOException) { unavailable = true; }
|
|
121
127
|
if (!unavailable) throw new Exception("A missing selected endpoint was replaced by another output.");
|
|
122
128
|
log.Add("PASS Refuse to route a missing explicitly selected endpoint to a different device");
|
|
@@ -129,6 +135,7 @@ namespace DrumBuddy
|
|
|
129
135
|
File.WriteAllLines(Path.Combine(root, "output-test-results.txt"), log, Encoding.UTF8); return 0;
|
|
130
136
|
}
|
|
131
137
|
catch (Exception ex) { log.Add("FAIL " + ex); File.WriteAllLines(Path.Combine(root, "output-test-results.txt"), log, Encoding.UTF8); return 1; }
|
|
138
|
+
finally { AudioOutputs.TestErrorLog = oldLog; }
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
}
|
package/source/Quick Start.txt
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
FLUX 1.
|
|
1
|
+
FLUX 1.6.1
|
|
2
2
|
|
|
3
3
|
INSTALL / UPDATE
|
|
4
4
|
Close the app. Run npm.cmd install -g @pro-gram/flux@latest
|
|
5
|
+
If npm skips its setup script, run flux --setup to finish the desktop install
|
|
6
|
+
and create the Flux Desktop and Start menu shortcuts. With PowerShell scripts
|
|
7
|
+
disabled, use flux.cmd --setup instead. First launch also completes setup.
|
|
5
8
|
Launch flux.cmd, or the Flux Desktop shortcut.
|
|
6
9
|
Migrating: npm.cmd uninstall -g @pro-gram/drumbuddy before installing Flux.
|
|
7
10
|
Windows 10/11 x64 only in this release.
|
|
@@ -10,12 +13,24 @@ Macintosh support is planned.
|
|
|
10
13
|
EDITOR + RECORD
|
|
11
14
|
The Editor opens by default. Home contains the separate mini-app sections.
|
|
12
15
|
File contains New, Open session, Save, Save as and Add audio.
|
|
16
|
+
The compact header keeps navigation beside the logo. Grid and zoom share
|
|
17
|
+
one toolbar; playback and the actual output device share the bottom strip.
|
|
18
|
+
Export settings and extra range/import options expand when needed.
|
|
13
19
|
Resize recording options by dragging the divider. Collapse hides its content;
|
|
14
20
|
Show restores it. Its Side/Bottom selector changes the panel position.
|
|
15
21
|
Add/drop audio, select time by dragging, then cut/copy/paste or split.
|
|
22
|
+
V selects the normal pointer. Click a clip to select it; Shift-click selects
|
|
23
|
+
additional clips. Selected items have purple fill and a bright border.
|
|
24
|
+
Drag within audio to select a time range automatically. S splits at the playhead.
|
|
25
|
+
Ctrl+Plus/Minus and Ctrl+mouse wheel zoom. Numeric-keypad +/- also work.
|
|
16
26
|
Drag the ruler to place the playhead. The left edge snaps to exact zero.
|
|
17
27
|
The thinner blue sticks mark selection start/end. Alt bypasses snapping.
|
|
18
|
-
Range
|
|
28
|
+
Range opens numeric selection controls.
|
|
29
|
+
Zoom: use the visible - / + buttons and slider, or Ctrl+mouse wheel over audio.
|
|
30
|
+
Wheel zoom keeps the time under the pointer in place. Shift+wheel pans sideways.
|
|
31
|
+
Fit shows the full project; Selection fills the view with the selected range.
|
|
32
|
+
Ctrl+= / Ctrl+- zoom; Ctrl+0 fits the project; Ctrl+Shift+0 fits the selection.
|
|
33
|
+
Zoom can reveal individual samples. Close-up waveforms load in the background.
|
|
19
34
|
Record options opens the sidebar. Select input, channels, rate and format.
|
|
20
35
|
The take is saved at the playhead on the selected track; separate inputs use
|
|
21
36
|
consecutive tracks. Stop saves and inserts it. Source files are preserved.
|
|
@@ -23,8 +38,9 @@ Use direct headphone monitoring. Software overdub synchronization is not include
|
|
|
23
38
|
|
|
24
39
|
AUDIO DEVICES
|
|
25
40
|
Audio devices at the top right opens Settings > Audio. Save device changes.
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
Windows default shows and follows the current Windows speakers/headphones.
|
|
42
|
+
Named outputs select the actual active device; there are no compatibility
|
|
43
|
+
device entries. Test speakers plays a short quiet tone.
|
|
28
44
|
Save diagnostics exports device information and recent playback errors.
|
|
29
45
|
Playback buffer: 40-250 ms; increase it if audio breaks up.
|
|
30
46
|
Windows microphones/Realtek/USB headsets receive 16-bit PCM. Saving that
|
|
@@ -39,6 +55,8 @@ General formats: PCM WAV, float WAV, AIFF, FLAC, MP3 and AAC/M4A.
|
|
|
39
55
|
MP3 -> AIFF and AIFF -> WAV are supported. Gain tools are in Media Player.
|
|
40
56
|
Choose source/44.1/48/96 kHz; MP3 uses at most 48 kHz.
|
|
41
57
|
Conversion options resize, collapse and dock on the side or bottom.
|
|
58
|
+
The installer includes FFmpeg with 227 audio decoders, including float/PCM WAV;
|
|
59
|
+
import and conversion do not require Windows Media Player codec packs.
|
|
42
60
|
The Zoom R16 preset writes classic 44.1 kHz PCM with compatible filenames.
|
|
43
61
|
Existing sources and exports are never overwritten.
|
|
44
62
|
|
|
@@ -50,7 +68,11 @@ No discards changes. Duplicate bindings prevent saving until corrected.
|
|
|
50
68
|
|
|
51
69
|
DEFAULT KEYS
|
|
52
70
|
Space: play/pause or stop active recording. R: start/stop recording.
|
|
53
|
-
Shift+Space: stop.
|
|
71
|
+
Shift+Space: stop. V: select pointer. S: split at playhead.
|
|
72
|
+
Drag a clip header to move it; drag its left/right edges to trim it.
|
|
73
|
+
Y: slip source contents. F: fade tool; selected clips also show fade handles.
|
|
74
|
+
Ctrl+D: duplicate. Ctrl+L: repeat selected clips/range. M: clip mute. X: crossfade.
|
|
75
|
+
Right-click audio for the editing menu. Edits, fades and mute support Undo.
|
|
54
76
|
Ctrl+X/C/V: cut/copy/paste. Backspace/Delete: remove selection. Ctrl+Z/Y: undo/redo.
|
|
55
77
|
Ctrl+S: save session. Ctrl+Shift+S: save as. Ctrl+O/N: open/new.
|
|
56
78
|
Ctrl+I/E: import/export. Home/End: timeline beginning/end.
|
|
@@ -74,4 +96,4 @@ Editor BPM/division controls or Settings > Timeline / grid.
|
|
|
74
96
|
Grid and clip-edge snapping are independent. Alt bypasses both.
|
|
75
97
|
Tempo changes grid spacing without stretching audio. Save .dbedit to keep it.
|
|
76
98
|
Ctrl+C, Backspace, position playhead, Ctrl+V. Copies survive deletion.
|
|
77
|
-
Zoom
|
|
99
|
+
Zoom into fine subdivisions or individual samples; disable snap for free chops.
|
package/source/SettingsWindow.cs
CHANGED
|
@@ -26,7 +26,7 @@ namespace DrumBuddy
|
|
|
26
26
|
public SettingsWindow(Window owner, string category, bool audioLocked, Action<InterfaceSettings> persist = null, InterfaceSettings initial = null)
|
|
27
27
|
{
|
|
28
28
|
this.persist = persist ?? InterfacePreferences.Save;
|
|
29
|
-
Owner = owner; this.audioLocked = audioLocked; Title = "Flux - Settings";
|
|
29
|
+
Owner = owner; this.audioLocked = audioLocked; Title = "Flux - Settings"; Icon = BrandArtwork.Raster();
|
|
30
30
|
Width = 930; Height = Math.Min(700, SystemParameters.WorkArea.Height - 40); MinWidth = 700; MinHeight = 460;
|
|
31
31
|
FontFamily = owner == null ? new System.Windows.Media.FontFamily("Segoe UI") : owner.FontFamily; FontSize = 13; WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
|
32
32
|
ThemePalette.Install(this); draft = (initial ?? InterfacePreferences.Current).Copy(); saved = draft.Signature();
|
|
@@ -109,10 +109,11 @@ namespace DrumBuddy
|
|
|
109
109
|
}
|
|
110
110
|
private void ShowAudio()
|
|
111
111
|
{
|
|
112
|
-
var panel = Page("Audio", "Choose
|
|
112
|
+
var panel = Page("Audio", "Choose your actual speakers/headphones and recording input. Windows default follows your current Windows output.");
|
|
113
113
|
panel.Children.Add(WorkspaceUi.Text("Speakers / headphones", 15));
|
|
114
114
|
var outputs = AudioOutputs.Choices();
|
|
115
|
-
var output = new ComboBox { ItemsSource = outputs, SelectedItem = outputs.FirstOrDefault(c => c.Key == draft.AudioOutputKey), MinHeight = 34, FontSize = 13, Margin = new Thickness(0, 7, 0, 14), IsEnabled = !audioLocked };
|
|
115
|
+
var output = new ComboBox { ItemsSource = outputs, SelectedItem = outputs.FirstOrDefault(c => c.Key == draft.AudioOutputKey) ?? outputs[0], MinHeight = 34, FontSize = 13, Margin = new Thickness(0, 7, 0, 14), IsEnabled = !audioLocked };
|
|
116
|
+
draft.AudioOutputKey = ((OutputChoice)output.SelectedItem).Key;
|
|
116
117
|
panel.Children.Add(output); output.SelectionChanged += delegate { var item = output.SelectedItem as OutputChoice; if (item != null) { draft.AudioOutputKey = item.Key; UpdateDirty(); } };
|
|
117
118
|
panel.Children.Add(WorkspaceUi.Text("Recording input", 15)); var inputs = AudioInputs.Choices();
|
|
118
119
|
var input = new ComboBox { ItemsSource = inputs, SelectedItem = inputs.FirstOrDefault(c => c.Key == draft.AudioInputKey), MinHeight = 34, FontSize = 13, Margin = new Thickness(0, 7, 0, 14), IsEnabled = !audioLocked };
|
package/source/Setup.cs
CHANGED
|
@@ -14,7 +14,7 @@ using System.Windows.Markup;
|
|
|
14
14
|
using Microsoft.Win32;
|
|
15
15
|
|
|
16
16
|
[assembly: AssemblyTitle("Flux Setup")]
|
|
17
|
-
[assembly: AssemblyVersion("1.
|
|
17
|
+
[assembly: AssemblyVersion("1.6.1.0")]
|
|
18
18
|
|
|
19
19
|
namespace DrumBuddySetup
|
|
20
20
|
{
|
|
@@ -22,6 +22,8 @@ namespace DrumBuddySetup
|
|
|
22
22
|
{
|
|
23
23
|
public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"Programs","DrumBuddy");
|
|
24
24
|
public const string RegistryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall\DrumBuddy";
|
|
25
|
+
private static string BrandIcon { get { return Path.Combine(InstallPath,"Flux-" + Assembly.GetExecutingAssembly().GetName().Version.ToString(3) + ".ico"); } }
|
|
26
|
+
[System.Runtime.InteropServices.DllImport("shell32.dll")] private static extern void SHChangeNotify(uint eventId, uint flags, IntPtr item1, IntPtr item2);
|
|
25
27
|
private static Window window;
|
|
26
28
|
private static bool working, installed;
|
|
27
29
|
private static T C<T>(string name) where T : FrameworkElement { return (T)window.FindName(name); }
|
|
@@ -42,6 +44,7 @@ namespace DrumBuddySetup
|
|
|
42
44
|
var app = new Application();
|
|
43
45
|
using (var s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Setup.xaml")) window = (Window)XamlReader.Load(s);
|
|
44
46
|
C<Image>("SetupLogo").Source = DrumBuddy.BrandArtwork.Raster();
|
|
47
|
+
window.Icon = DrumBuddy.BrandArtwork.Raster();
|
|
45
48
|
window.Height = Math.Min(window.Height,SystemParameters.WorkArea.Height - 30);
|
|
46
49
|
C<Button>("InstallButton").Click += async delegate {
|
|
47
50
|
if (installed) { LaunchApp(); window.Close(); return; }
|
|
@@ -98,7 +101,7 @@ namespace DrumBuddySetup
|
|
|
98
101
|
Directory.CreateDirectory(Path.GetDirectoryName(destination));
|
|
99
102
|
File.Copy(source,destination,true);
|
|
100
103
|
}
|
|
101
|
-
File.WriteAllText(Path.Combine(InstallPath,".drumbuddy-installed"),"Flux 1.
|
|
104
|
+
File.WriteAllText(Path.Combine(InstallPath,".drumbuddy-installed"),"Flux 1.6.1\n" + InstallPath,Encoding.UTF8);
|
|
102
105
|
string[] files = Directory.GetFiles(stage,"*",SearchOption.AllDirectories).Select(f => f.Substring(stage.Length + 1)).Concat(new[] { ".drumbuddy-installed","installed-files.txt" }).ToArray();
|
|
103
106
|
File.WriteAllLines(Path.Combine(InstallPath,"installed-files.txt"),files,Encoding.UTF8);
|
|
104
107
|
string exe = Path.Combine(InstallPath,"DrumBuddy.exe");
|
|
@@ -109,13 +112,14 @@ namespace DrumBuddySetup
|
|
|
109
112
|
RemoveLegacyShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),"Programs","Drum Buddy.lnk"),exe);
|
|
110
113
|
RemoveLegacyShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),"Drum Buddy.lnk"),exe);
|
|
111
114
|
using (var key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(RegistryKey)) {
|
|
112
|
-
key.SetValue("DisplayName","Flux"); key.SetValue("DisplayVersion","1.
|
|
113
|
-
key.SetValue("InstallLocation",InstallPath); key.SetValue("DisplayIcon",
|
|
115
|
+
key.SetValue("DisplayName","Flux"); key.SetValue("DisplayVersion","1.6.1"); key.SetValue("Publisher","Flux");
|
|
116
|
+
key.SetValue("InstallLocation",InstallPath); key.SetValue("DisplayIcon",BrandIcon);
|
|
114
117
|
key.SetValue("UninstallString","\"" + Path.Combine(InstallPath,"Uninstall.exe") + "\"");
|
|
115
118
|
key.SetValue("NoModify",1); key.SetValue("NoRepair",1);
|
|
116
119
|
long size = Directory.GetFiles(InstallPath,"*",SearchOption.AllDirectories).Sum(f => new FileInfo(f).Length);
|
|
117
120
|
key.SetValue("EstimatedSize",(int)(size / 1024));
|
|
118
121
|
}
|
|
122
|
+
SHChangeNotify(0x08000000,0,IntPtr.Zero,IntPtr.Zero);
|
|
119
123
|
}
|
|
120
124
|
finally { try { if (Directory.Exists(stage)) Directory.Delete(stage,true); } catch { } }
|
|
121
125
|
}
|
|
@@ -180,7 +184,7 @@ namespace DrumBuddySetup
|
|
|
180
184
|
}
|
|
181
185
|
type.InvokeMember("TargetPath",BindingFlags.SetProperty,null,link,new object[] { exe });
|
|
182
186
|
type.InvokeMember("WorkingDirectory",BindingFlags.SetProperty,null,link,new object[] { Path.GetDirectoryName(exe) });
|
|
183
|
-
type.InvokeMember("IconLocation",BindingFlags.SetProperty,null,link,new object[] {
|
|
187
|
+
type.InvokeMember("IconLocation",BindingFlags.SetProperty,null,link,new object[] { BrandIcon + ",0" });
|
|
184
188
|
type.InvokeMember("Save",BindingFlags.InvokeMethod,null,link,null);
|
|
185
189
|
}
|
|
186
190
|
finally { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(link); }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Flux 1.
|
|
1
|
+
Flux 1.6.1 - third-party components
|
|
2
2
|
|
|
3
3
|
FFmpeg 9.0.2, release essentials build by Gyan Doshi
|
|
4
4
|
Used as a separate command-line decoding process.
|
|
@@ -20,8 +20,9 @@ Copyright (c) .NET Foundation and Contributors. MIT license.
|
|
|
20
20
|
https://github.com/dotnet/runtime/blob/main/LICENSE.TXT
|
|
21
21
|
|
|
22
22
|
Zoom H5studio and R16/R24 drivers: linked from Zoom's official support pages, not bundled.
|
|
23
|
-
R16 reference panel image: copyright Zoom Corporation. The
|
|
24
|
-
|
|
23
|
+
R16 reference panel image: copyright Zoom Corporation. The reference image
|
|
24
|
+
is included from Zoom's public product website; its source URL and SHA-256
|
|
25
|
+
are recorded in dependency-lock.json.
|
|
25
26
|
Zoom and R16 are trademarks of Zoom Corporation. This is an independent app.
|
|
26
27
|
|
|
27
28
|
The original drummer mascot was generated for Drum Buddy using OpenAI's
|