Example #1
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);
        }
        /*
         * 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";
        }
Example #3
0
        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);
        }
Example #4
0
        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");
                    }
                }
            }
        }