internal DeviceLoaderDialog(ILogger logger, RolandMidiClient client, ModuleSchema schema) : this() { this.logger = logger; this.client = client; this.schema = schema; cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(10)); }
internal SoundRecorderDialog(ILogger logger, ModuleSchema schema, RolandMidiClient midiClient) : this() { this.logger = logger; this.schema = schema; this.midiClient = midiClient; kitNumber.PreviewTextInput += TextConversions.CheckDigits; userSamples.PreviewTextInput += TextConversions.CheckDigits; cancellationTokenSource = new CancellationTokenSource(); kitNumber.Text = TextConversions.Format(schema.KitRoots.Count); // Capture the input device names, and attempt to guess a reasonable default. var allInputDevices = AudioDevices.GetInputDeviceNames(); var midiName = midiClient.OutputName; var expectedInputDevices = new[] { $"MASTER ({midiName})", $"IN ({midiName})", $"KICK ({midiName})" }; inputDevice.ItemsSource = allInputDevices; inputDevice.SelectedIndex = allInputDevices.FindIndex(inputName => expectedInputDevices.Contains(inputName)); foreach (var group in schema.InstrumentGroups) { instrumentGroupSelector.Items.Add(group.Description); } }
public async Task RequestData_TD50X() { var input = new FakeMidiInput(); var output = new FakeMidiOutput(); var client = new RolandMidiClient(input, output, "TD-50X", "TD-50X", 0x10, ModuleIdentifier.TD50X); // Test for "Current kit" - simplest possible message. var task = client.RequestDataAsync(0, 1, new CancellationTokenSource(1000).Token); // Reply on behalf of the module var responseBytes = new byte[] { 0xF0, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x07, 0x12, 0x00, 0x00, 0x00, 0x00, 0x58, 0x28, 0xF7 }; var responseMessage = new MidiMessage(responseBytes); // We should get the response input.SupplyMessage(responseMessage); var result = await task; Assert.AreEqual(new byte[] { 0x58 }, result); // Now check that the request was correct Assert.AreEqual(1, output.Messages.Count); var requestMessage = output.Messages[0]; var actualRequestData = requestMessage.Data; var expectedRequestData = new byte[] { 0xF0, 0x41, 0x10, // SYSEX, Roland, DevId 0x00, 0x00, 0x00, 0x00, 0x07, // TD-50X 0x11, // RQ1 0x00, 0x00, 0x00, 0x00, // Address 0x00, 0x00, 0x00, 0x01, // Size 0x7f, 0xF7 // Checksum and EOX }; Assert.AreEqual(expectedRequestData, actualRequestData); }
internal KitExplorer(ILogger logger, Kit kit, RolandMidiClient midiClient, string fileName) : base(logger, kit.Schema, kit.Data, kit.KitRoot, midiClient, fileName, SaveFileFilter, "Kit explorer") { this.kit = kit; copyToDeviceButton.Content = "Copy kit to device"; defaultKitNumber.Text = TextConversions.Format(kit.DefaultKitNumber); copyToDeviceKitNumber.Text = defaultKitNumber.Text; }
internal SoundRecorderDialog(ILogger logger, ModuleSchema schema, RolandMidiClient midiClient) : this() { this.logger = logger; this.schema = schema; this.midiClient = midiClient; kitNumber.PreviewTextInput += TextConversions.CheckDigits; userSamples.PreviewTextInput += TextConversions.CheckDigits; cancellationTokenSource = new CancellationTokenSource(); inputDevice.ItemsSource = AudioDevices.GetInputDeviceNames(); kitNumber.Text = TextConversions.Format(schema.KitRoots.Count); }
public InstrumentAudioRecorderViewModel(IViewServices viewServices, ILogger logger, DeviceViewModel deviceViewModel) { this.logger = logger; schema = deviceViewModel.ConnectedDeviceSchema ?? throw new InvalidOperationException("Cannot record audio without a connected device"); device = deviceViewModel.ConnectedDevice ?? throw new InvalidOperationException("Cannot record audio without a connected device"); Settings = new InstrumentAudioRecorderSettingsViewModel(viewServices, schema, device.InputName); Progress = new InstrumentAudioRecorderProgressViewModel(); Title = $"Instrument Audio Recorder ({schema.Identifier.Name})"; StartRecordingCommand = new DelegateCommand(StartRecording, false); CancelCommand = new DelegateCommand(Cancel, false); Settings.PropertyChanged += (sender, args) => UpdateButtonStatus(); }
/// <summary> /// Writes a kit to a device. /// </summary> internal static async Task WriteKit(RolandMidiClient client, Kit kit, int kitNumber, IStandardStreamWriter console) { var targetKitRoot = kit.Schema.KitRoots[kitNumber]; var clonedData = kit.KitRoot.Context.CloneData(kit.Data, targetKitRoot.Context.Address); var segments = clonedData.GetSegments(); console.WriteLine($"Writing {segments.Count} containers to device {kit.Schema.Identifier.Name} kit {kitNumber}"); foreach (var segment in segments) { client.SendData(segment.Start.Value, segment.CopyData()); await Task.Delay(40); } }
internal ModuleExplorer(ILogger logger, Module module, RolandMidiClient midiClient, string fileName) : base(logger, module.Schema, module.Data, module.Schema.LogicalRoot, midiClient, fileName, SaveFileFilter, "Module explorer") { this.module = module; kitNumberLabel.Visibility = Visibility.Collapsed; copyToDeviceButton.Content = "Copy data to device"; copyToDeviceKitNumber.Visibility = Visibility.Collapsed; defaultKitPanel.Visibility = Visibility.Collapsed; AddKitContextMenus(); CommandBindings.Add(new CommandBinding(DataExplorerCommands.OpenCopyInKitExplorer, OpenCopyInKitExplorer)); CommandBindings.Add(new CommandBinding(DataExplorerCommands.ImportKitFromFile, ImportKitFromFile)); CommandBindings.Add(new CommandBinding(DataExplorerCommands.CopyKit, CopyKit)); CommandBindings.Add(new CommandBinding(DataExplorerCommands.ExportKit, ExportKit)); }
private static async Task PopulateSegment(RolandMidiClient client, ModuleData data, AnnotatedContainer annotatedContainer, CancellationToken token, IStandardStreamWriter console) { var timerToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token; var effectiveToken = CancellationTokenSource.CreateLinkedTokenSource(token, timerToken).Token; try { var segment = await client.RequestDataAsync(annotatedContainer.Context.Address.Value, annotatedContainer.Container.Size, effectiveToken); data.Populate(annotatedContainer.Context.Address, segment); } catch (OperationCanceledException) when(timerToken.IsCancellationRequested) { console.WriteLine($"Device didn't respond for container {annotatedContainer.Path}; skipping."); } }
/// <summary> /// Reads a kit from a device. /// </summary> internal static async Task <Kit> ReadKit(ModuleSchema schema, RolandMidiClient client, int kitNumber, IStandardStreamWriter console) { // Allow up to 30 seconds in total, and 1 second per container. var overallToken = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; var moduleData = new ModuleData(); var kitRoot = schema.KitRoots[kitNumber]; var containers = kitRoot.Context.AnnotateDescendantsAndSelf().Where(c => c.Container.Loadable).ToList(); console.WriteLine($"Reading {containers.Count} containers from device {schema.Identifier.Name} kit {kitNumber}"); foreach (var container in containers) { await PopulateSegment(client, moduleData, container, overallToken, console); } var clonedData = kitRoot.Context.CloneData(moduleData, schema.KitRoots[1].Context.Address); return(new Kit(schema, clonedData, kitNumber)); }
internal DataExplorer(ILogger logger, ModuleSchema schema, ModuleData data, VisualTreeNode rootNode, RolandMidiClient midiClient, string fileName, string saveFileFilter, string explorerName) : this() { Logger = logger; Schema = schema; Data = data; MidiClient = midiClient; RootNode = rootNode; this.saveFileFilter = saveFileFilter; this.explorerName = explorerName; this.fileName = fileName; if (midiClient == null) { mainPanel.Children.Remove(midiPanel); } Data.DataChanged += HandleModuleDataChanged; LoadView(); UpdateTitle(); }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { using (var device = new DeviceController(client)) { var schema = device.Schema; var channel = context.ParseResult.ValueForOption <int>("channel"); var keys = context.ParseResult.ValueForOption <string>("keys"); var targetKit = context.ParseResult.ValueForOption <int>("kit"); if (targetKit < 1 || targetKit + 1 > schema.Kits) { console.WriteLine($"Kit {targetKit} is out of range for {schema.Identifier.Name} for this command."); console.WriteLine("Note that one extra kit is required after the specified one."); return(1); } // Detect the current kit var currentKit = await device.GetCurrentKitAsync(CancellationToken.None); // Copy current kit to target kit and target kit + 1 var kit = await device.LoadKitAsync(currentKit, progressHandler : null, CreateCancellationToken()); var dataNode = new DataTreeNode(kit.Data, kit.KitRoot); await device.SaveDescendants(dataNode, schema.GetKitRoot(targetKit).Container.Address, progressHandler : null, CreateCancellationToken()); await device.SaveDescendants(dataNode, schema.GetKitRoot(targetKit + 1).Container.Address, progressHandler : null, CreateCancellationToken()); await device.SetCurrentKitAsync(targetKit, CancellationToken.None); var programChangeCommand = (byte)(0xc0 | (channel - 1)); // Now listen for the foot switch... client.MessageReceived += async(sender, message) => { if (message.Data.Length == 2 && message.Data[0] == programChangeCommand) { console.WriteLine("Turning the page..."); SendKeysUtilities.SendWait(keys); await device.SetCurrentKitAsync(targetKit, CancellationToken.None); } }; console.WriteLine("Listening for foot switch"); await Task.Delay(TimeSpan.FromHours(1)); } return(0); CancellationToken CreateCancellationToken() => new CancellationTokenSource(10000).Token; }
public DeviceController(RolandMidiClient client) : this(client, TimeSpan.FromSeconds(1)) { }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { var address = ModuleAddress.FromDisplayValue(HexInt32.Parse(context.ParseResult.ValueForOption <string>("address")).Value); var size = HexInt32.Parse(context.ParseResult.ValueForOption <string>("size")).Value; var timeout = context.ParseResult.ValueForOption <int>("timeout"); // Wait up to 10 seconds to receive all the requested data... int sizeReceived = 0; var delayTask = Task.Delay(TimeSpan.FromSeconds(timeout)); var completeTaskCts = new TaskCompletionSource <int>(); client.DataSetMessageReceived += DumpMessage; client.SendDataRequestMessage(address.DisplayValue, size); await Task.WhenAny(delayTask, completeTaskCts.Task); return(0); void DumpMessage(object sender, DataSetMessage message) { ModuleAddress address = ModuleAddress.FromDisplayValue(message.Address); console.WriteLine($"Address: {address} Length: {message.Length:x4}"); int index = 0; while (index < message.Length) { var builder = new StringBuilder(); var textBuilder = new StringBuilder(); builder.Append(address); builder.Append(" "); for (int i = 0; i < 16 && index < message.Length; i++) { byte b = message.Data[index]; textBuilder.Append(b >= 32 && b < 127 ? (char)b : ' '); builder.Append(b.ToString("x2")); builder.Append(" "); index++; } string text = builder.ToString().PadRight(9 + 16 * 3) + textBuilder; console.WriteLine(text); address = address.PlusLogicalOffset(16); } console.WriteLine(); if (Interlocked.Add(ref sizeReceived, message.Length) >= size) { completeTaskCts.SetResult(0); } } }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { var schema = ModuleSchema.KnownSchemas[ModuleIdentifier.AE10].Value; var instrumentField = schema.PhysicalRoot.ResolveField("TemporaryStudioSet/Part[1]/Instrument"); var instrumentDataField = new EnumDataField((EnumField)instrumentField); var deviceController = new DeviceController(client, new ConsoleLogger(console)); var temporaryKitRoot = schema.LogicalRoot.ResolveNode("TemporaryStudioSet"); // First 100 presets client.SendData(0x01_00_00_05, new byte[] { 64 }); for (int i = 1; i <= 100; i++) { console.WriteLine($"Copying studio set {i}"); client.SendData(0x01_00_00_06, new byte[] { (byte)((i - 1) & 0x7f) }); await Task.Delay(40); var data = ModuleData.FromLogicalRootNode(temporaryKitRoot); await deviceController.LoadDescendants(data.LogicalRoot, null, progressHandler : null, cancellationToken : default); await deviceController.SaveDescendants(data.LogicalRoot, schema.GetKitRoot(i).Container.Address, progressHandler : null, cancellationToken : default); } return(0); }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { var channel = context.ParseResult.ValueForOption <int>("channel"); var keys = context.ParseResult.ValueForOption <string>("keys"); var midiNote = context.ParseResult.ValueForOption <int>("note"); var noteOn = (byte)(0x90 | (channel - 1)); // Now listen for the foot switch... client.MessageReceived += (sender, message) => { if (message.Data.Length == 3 && message.Data[0] == noteOn && message.Data[1] == midiNote) { console.WriteLine("Turning the page..."); SendKeysUtilities.SendWait(keys); } }; console.WriteLine("Listening for MIDI note"); await Task.Delay(TimeSpan.FromHours(1)); return(0); }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { var schema = ModuleSchema.KnownSchemas[ModuleIdentifier.AE10].Value; var instrumentField = schema.PhysicalRoot.ResolveField("TemporaryStudioSet/Part[1]/Instrument"); var instrumentDataField = new EnumDataField((EnumField)instrumentField); // Presets client.SendData(0x01_00_00_05, new byte[] { 64 }); for (int i = 1; i <= 128; i++) { await ShowStudioSet('P', i); } // Preset 129 is in a different bank... client.SendData(0x01_00_00_05, new byte[] { 65 }); for (int i = 129; i <= 129; i++) { await ShowStudioSet('P', i); } // User sets client.SendData(0x01_00_00_05, new byte[] { 0 }); for (int i = 1; i <= 100; i++) { await ShowStudioSet('U', i); } async Task ShowStudioSet(char prefix, int index) { client.SendData(0x01_00_00_06, new byte[] { (byte)((index - 1) & 0x7f) }); await Task.Delay(50); var common = await client.RequestDataAsync(0x18_00_00_00, 16, CancellationToken.None); string name = Encoding.UTF8.GetString(common); console.WriteLine($"{prefix}:{index:D3}: {name}"); for (int partIndex = 0; partIndex < 4; partIndex++) { var part = await client.RequestDataAsync(0x18_00_20_00 + partIndex * 0x1_00, 9, CancellationToken.None); if (part[1] != 1) { continue; } // The address doesn't matter, as only the offset is passed. var segment = new DataSegment(ModuleAddress.FromDisplayValue(0), part); instrumentDataField.Load(segment); console.WriteLine($"Part {partIndex + 1}: {instrumentDataField.Value}"); } console.WriteLine(); } return(0); }
public DeviceController(RolandMidiClient client, ILogger logger) : this(client, logger, TimeSpan.FromSeconds(1)) { }
private DeviceController(RolandMidiClient client, ILogger logger, TimeSpan loadSegmentTimeout) => (this.client, this.logger, this.loadSegmentTimeout, Schema) =
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { using (var device = new DeviceController(client)) { return(await InvokeAsync(context, console, device)); } }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client) { using var writer = File.CreateText(context.ParseResult.ValueForOption <string>("file")); int chunkSize = context.ParseResult.ValueForOption <int>("chunkSize"); TimeSpan timeout = TimeSpan.FromMilliseconds(context.ParseResult.ValueForOption <int>("timeout")); var address = ModuleAddress.FromDisplayValue(0); int messagesReceived = 0; client.DataSetMessageReceived += DumpMessage; // TODO: Make this tighter. But we're unlikely to really get here. while (address.DisplayValue < 0x74_00_00_00) { int receivedAtStartOfChunk = messagesReceived; client.SendDataRequestMessage(address.DisplayValue, chunkSize); int receivedAtStartOfDelay; do { receivedAtStartOfDelay = messagesReceived; await Task.Delay(timeout); } while (messagesReceived > receivedAtStartOfDelay); int receivedForChunk = messagesReceived - receivedAtStartOfChunk; console.WriteLine($"Received {receivedForChunk} messages in chunk at {address}"); writer.Flush(); address = address.PlusLogicalOffset(chunkSize); } return(0); void DumpMessage(object sender, DataSetMessage message) { ModuleAddress address = ModuleAddress.FromDisplayValue(message.Address); writer.WriteLine($"Address: {address} Length: {message.Length:x4}"); int index = 0; while (index < message.Length) { var builder = new StringBuilder(); var textBuilder = new StringBuilder(); builder.Append(address); builder.Append(" "); for (int i = 0; i < 16 && index < message.Length; i++) { byte b = message.Data[index]; textBuilder.Append(b >= 32 && b < 127 ? (char)b : ' '); builder.Append(b.ToString("x2")); builder.Append(" "); index++; } string text = builder.ToString().PadRight(9 + 16 * 3) + textBuilder; writer.WriteLine(text); address = address.PlusLogicalOffset(16); } writer.WriteLine(); Interlocked.Increment(ref messagesReceived); } }
protected abstract Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client);