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); }
/* * private async Task RestoreData(ModuleData data) * { * logger.Log("Restoring original data"); * try * { * foreach (var segment in data.GetSegments()) * { * midiClient.SendData(segment.Start.Value, segment.CopyData()); * // Note: no cancellation token; the token may already have been cancelled! * await Task.Delay(40); * } * } * catch (Exception e) * { * logger.Log($"Error restoring data", e); * } * } * * private async Task LoadContainerAsync(ModuleData data, FixedContainer context) * { * if (data.GetSegmentOrNull(context.Address) != null) * { * return; * } * var timerToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token; * var effectiveToken = CancellationTokenSource.CreateLinkedTokenSource(CancellationToken, timerToken).Token; * var segment = await device.RequestDataAsync(context.Address.Value, context.Container.Size, effectiveToken); * data.Populate(context.Address, segment); * }*/ private async Task RecordInstruments(CancellationToken token) { var selectedInstrumentGroup = schema.InstrumentGroups.FirstOrDefault(ig => ig.Description == Settings.SelectedInstrumentGroup); var instrumentsToRecord = schema.PresetInstruments.Where(inst => selectedInstrumentGroup is null || inst.Group == selectedInstrumentGroup) .Concat(schema.UserSampleInstruments.Take(Settings.UserSamples)) .ToList(); Progress.TotalInstruments = instrumentsToRecord.Count; Progress.CompletedInstruments = 0; // Load the details for the whole kit. // We don't need all of it, but it doesn't take *that* long, compared with the rest of the process. var schemaKitRoot = schema.KitRoots[Settings.KitNumber - 1]; var moduleData = ModuleData.FromLogicalRootNode(schemaKitRoot); var dataKitRoot = moduleData.LogicalRoot; // TODO: Don't assume this layout, and express it more cleanly. var triggerRoot = dataKitRoot.Children.First(n => n.SchemaNode.Name == "Triggers") .Children.First(n => n.SchemaNode.Name == "Trigger[1]"); Progress.CurrentInstrumentRecording = "Loading kit data"; logger.LogInformation($"Loading data from kit {Settings.KitNumber} to restore later"); var snapshot = await LoadSnapshotFromDevice(moduleData, token); // Populate the module data with the snapshot we've created. moduleData.LoadSnapshot(snapshot); var midiNote = triggerRoot.GetMidiNote() ?? throw new InvalidOperationException($"Node {triggerRoot.SchemaNode.Path} has no MIDI note"); // TODO: Reset everything on the kit (because of mfx, vedit etc) var instrumentFields = triggerRoot.Details .OfType <FieldContainerDataNodeDetail>() .SelectMany(child => child.Fields.OfType <InstrumentDataField>()) .ToList(); if (instrumentFields.Count != 2) { throw new InvalidOperationException("Expected to find two instrument fields: main and sub"); } // TODO: How do we save a FieldContainerDataNodeDetail to a DataSegment? New method? // TODO: Can we just poke the instrument field itself? (Well, the two bits of it...) logger.LogInformation($"Recording {instrumentsToRecord.Count} instruments"); try { // Just simulate it for the moment... for (int i = 0; i < instrumentsToRecord.Count; i++) { Progress.CurrentInstrumentRecording = instrumentsToRecord[i].Name; await Task.Delay(100, token); Progress.CompletedInstruments = i + 1; } } finally { Progress.CurrentInstrumentRecording = "Restoring kit data"; logger.LogInformation($"Restoring snapshot to kit {Settings.KitNumber}"); // Don't cancel restoring the snapshot await SaveSnapshotToDevice(snapshot, CancellationToken.None); } logger.LogInformation($"Recording complete"); Progress.CurrentInstrumentRecording = "Complete"; }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, DeviceController device) { var kit = context.ParseResult.ValueForOption <int>("kit"); var triggerRoot = device.Schema.GetTriggerRoot(kit, trigger: 1); var deviceData = ModuleData.FromLogicalRootNode(triggerRoot); var modelData = ModuleData.FromLogicalRootNode(triggerRoot); var defaultValuesSnapshot = modelData.CreateSnapshot(); var deviceDataRoot = new DataTreeNode(deviceData, triggerRoot); await device.LoadDescendants(deviceDataRoot, targetAddress : null, progressHandler : null, CancellationToken.None); var originalSnapshot = deviceData.CreateSnapshot(); var instrumentField = device.Schema.GetMainInstrumentField(kit, trigger: 1); var modelInstrumentField = (InstrumentDataField)modelData.GetDataField(instrumentField); var instrumentContainers = triggerRoot.DescendantFieldContainers(); var differences = new List <Difference>(); var logger = new ConsoleLogger(console); try { // Reset the device to an empty snapshot deviceData.LoadSnapshot(defaultValuesSnapshot, logger); await device.SaveDescendants(deviceDataRoot, targetAddress : null, progressHandler : null, CancellationToken.None); foreach (var instrument in device.Schema.PresetInstruments) { // Make the change on the real module and load the data. // Assumption: the segment containing the instrument itself (e.g. KitPadInst) doesn't // have any implicit model changes to worry about. await device.SetInstrumentAsync(kit, trigger : 1, instrument, CancellationToken.None); await device.LoadDescendants(deviceDataRoot, targetAddress : null, progressHandler : null, CancellationToken.None); // Make the change in the model. modelData.LoadSnapshot(defaultValuesSnapshot, logger); modelInstrumentField.Instrument = instrument; // Compare the two. bool anyDifferences = false; foreach (var container in instrumentContainers) { // We won't compare InstrumentDataField, TempoDataField or StringDataField this way, but that's okay. var realFields = deviceData.GetDataFields(container).SelectMany(ExpandOverlays).OfType <NumericDataFieldBase>().ToList(); var modelFields = modelData.GetDataFields(container).SelectMany(ExpandOverlays).OfType <NumericDataFieldBase>().ToList(); if (realFields.Count != modelFields.Count) { console.WriteLine($"Major failure: for instrument {instrument.Id} ({instrument.Group} / {instrument.Name}), found {realFields.Count} real fields and {modelFields.Count} model fields in container {container.Path}"); return(1); } foreach (var pair in realFields.Zip(modelFields)) { var real = pair.First; var model = pair.Second; if (real.SchemaField != model.SchemaField) { console.WriteLine($"Major failure: for instrument {instrument.Id} ({instrument.Group} / {instrument.Name}), mismatched schema field for {container.Path}: {real.SchemaField.Name} != {model.SchemaField.Name}"); return(1); } var realValue = real.RawValue; var predictedValue = model.RawValue; if (realValue != predictedValue) { anyDifferences = true; differences.Add(new Difference(instrument, container, real.SchemaField, realValue, predictedValue)); } } } console.Write(anyDifferences ? "!" : "."); } } finally { // Restore the original data deviceData.LoadSnapshot(originalSnapshot, logger); await device.SaveDescendants(deviceDataRoot, targetAddress : null, progressHandler : null, CancellationToken.None); } console.WriteLine(); foreach (var difference in differences) { console.WriteLine(difference.ToString()); } console.WriteLine($"Total differences: {differences.Count}"); return(0); IEnumerable <IDataField> ExpandOverlays(IDataField field) => field is OverlayDataField odf ? odf.CurrentFieldList.Fields : Enumerable.Repeat(field, 1); }
protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, DeviceController device) { var path = context.ParseResult.ValueForOption <string>("path"); var switchFieldName = context.ParseResult.ValueForOption <string>("switch"); var parametersFieldName = context.ParseResult.ValueForOption <string>("parameters"); var mfxNode = device.Schema.LogicalRoot.ResolveNode(path); var mfxContainer = mfxNode.Container; var switchField = (EnumField)mfxContainer.ResolveField(switchFieldName); var parametersField = (OverlayField)mfxContainer.ResolveField(parametersFieldName); var deviceData = ModuleData.FromLogicalRootNode(mfxNode); var deviceParametersField = (OverlayDataField)deviceData.GetDataField(parametersField); console.WriteLine($"Loading original MFX data"); await device.LoadDescendants(deviceData.LogicalRoot, null, null, default); var originalSnapshot = deviceData.CreateSnapshot(); var modelData = ModuleData.FromLogicalRootNode(mfxNode); var modelTypeField = (EnumDataField)modelData.GetDataField(switchField); var modelParametersField = (OverlayDataField)modelData.GetDataField(parametersField); try { // Set it to the max value so that we'll be resetting it. await SetDeviceMfx(switchField.Max); for (int mfxType = switchField.Min; mfxType <= switchField.Max; mfxType++) { // Make the change on the device... await SetDeviceMfx(mfxType); await device.LoadDescendants(deviceData.LogicalRoot, null, null, default); // Make the change in the model... modelTypeField.RawValue = mfxType; var modelFields = modelParametersField.CurrentFieldList; var deviceFields = deviceParametersField.CurrentFieldList; if (modelFields.Description != deviceFields.Description) { console.WriteLine($"Mismatch in description: '{modelFields.Description}' != '{deviceFields.Description}'. Skipping."); continue; } console.WriteLine($"Comparing fields for {modelFields.Description}"); foreach (var(modelField, deviceField) in AsNumericFields(modelFields.Fields).Zip(AsNumericFields(deviceFields.Fields))) { if (modelField.RawValue != deviceField.RawValue) { console.WriteLine($"{modelField.SchemaField.Name}: Device={deviceField.RawValue}; Model={modelField.RawValue}"); } } console.WriteLine(); } } finally { // Restore the original data console.WriteLine($"Restoring original kit data"); deviceData.LoadSnapshot(originalSnapshot, NullLogger.Instance); await device.SaveDescendants(deviceData.LogicalRoot, targetAddress : null, progressHandler : null, CancellationToken.None); } return(0); async Task SetDeviceMfx(int type) { var segment = new DataSegment(mfxContainer.Address + switchField.Offset, new[] { (byte)type }); await device.SaveSegment(segment, CancellationToken.None); } IEnumerable <NumericDataFieldBase> AsNumericFields(IEnumerable <IDataField> fields) { foreach (var field in fields) { switch (field) { case NumericDataFieldBase numeric: yield return(numeric); break; case TempoDataField tempo: yield return(tempo.SwitchDataField); yield return(tempo.NumericDataField); yield return(tempo.MusicalNoteDataField); break; default: throw new InvalidOperationException($"Can't convert {field.GetType()} into a numeric field"); } } } }