internal IEnumerable <IContainer> ToContainers(ModuleSchema schema, ModuleJson module, ModuleAddress parentAddress, string?parentPath, SchemaVariables variables) { variables = variables.WithVariables(ExtraVariables); ModuleOffset offset = ModuleOffset.FromDisplayValue(Offset.Value); if (Repeat is null) { yield return(resolvedContainer.ToContainer(schema, module, ResolvedName, ResolvedDescription, parentAddress + offset, parentPath, variables)); } else { int gap = ModuleOffset.FromDisplayValue(Repeat.Gap.Value).LogicalValue; foreach (var tuple in module.GetRepeatSequence(Repeat.Items, variables)) { var itemVariables = variables.WithVariable(Repeat.IndexVariable, tuple.index, Repeat.IndexTemplate); var formattedDescription = tuple.variables.Replace(ResolvedDescription); var formattedName = Invariant($"{ResolvedName}[{tuple.index}]"); yield return(resolvedContainer.ToContainer(schema, module, formattedName, formattedDescription, parentAddress + offset, parentPath, itemVariables)); offset += gap; } } }
private IField ToField(ModuleSchema schema, ModuleJson module, string name, int offset, string description, FieldCondition?condition) { // TODO: Validate that we don't have "extra" parameters? return(Type switch { "boolean" => (IField) new BooleanField(BuildCommon(1)), "boolean32" => new BooleanField(BuildCommon(4)), "range8" => BuildNumericField(1), "range16" => BuildNumericField(2), "range32" => BuildNumericField(4), "enum" => BuildEnumField(1), "enum16" => BuildEnumField(2), "enum32" => BuildEnumField(4), "dynamicOverlay" => BuildDynamicOverlay(), "instrument" => new InstrumentField(BuildCommon(4), ValidateNotNull(BankOffset, nameof(BankOffset)).Value, ValidateNotNull(VeditOffset, nameof(VeditOffset)).Value), "musicalNote" => new EnumField(BuildCommon(4), MusicalNoteValues, 0, 0), "volume32" => new NumericField(BuildCommon(4), -601, 60, 0, 10, null, 0, "dB", (-601, "-INF")), "string" => BuildStringField(1), "string16" => BuildStringField(2), "midi32" => new MidiNoteField(BuildCommon(4)), string text when text.StartsWith(ContainerPrefix) => BuildContainer(), _ => throw new InvalidOperationException($"Unknown field type: {Type}") });
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 Audio.InstrumentAudio ToModel(ModuleSchema schema) { var bank = Preset ? schema.PresetInstruments : schema.UserSampleInstruments; var instrument = bank[InstrumentId]; return(new Audio.InstrumentAudio(instrument, AudioData.ToByteArray())); }
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); } }
internal FieldContainer(ModuleSchema schema, string name, string description, ModuleAddress address, string path, int size, IEnumerable <FieldBase> fields) : base(schema, name, description, address, path) { Fields = fields.ToReadOnlyList(field => field.WithParent(this)); Size = size; fieldsByName = Lazy.Create(() => Fields.ToDictionary(f => f.Name).AsReadOnly()); }
internal ContainerContainer(ModuleSchema schema, string name, string description, ModuleAddress address, string path, List <IContainer> containers) : base(schema, name, description, address, path) { Containers = containers; foreach (ContainerBase container in Containers) { container.Parent = this; } ContainersByName = Containers.ToDictionary(c => c.Name).AsReadOnly(); }
internal OverlayDataField(OverlayField field, ModuleSchema schema) : base(field) { this.schema = schema; fieldLists = SchemaField.FieldLists .ToDictionary( pair => pair.Key, pair => Lazy.Create(() => new FieldList(pair.Value, schema))) .AsReadOnly(); // Default to the first overlay, just to make sure we have a valid value right from the start. switchIndex = SchemaField.FieldLists.First().Key; }
internal static bool TryGetKitRoot(string text, ModuleSchema schema, ILogger logger, out VisualTreeNode kitRoot) { if (!TryParseInt32(text, out var kitNumber) || !schema.KitRoots.TryGetValue(kitNumber, out kitRoot)) { logger?.Log($"Invalid kit number: {text}"); kitRoot = null; return(false); } return(true); }
public CopyKitTargetDialog(ModuleSchema schema, ModuleData data, VisualTreeNode sourceKitNode) : this() { this.schema = schema; this.data = data; sourceKitName.Content = sourceKitNode.KitOnlyDescription.Format(sourceKitNode.Context, data); // This is done after other control initialization so that everything is set up. // It would probably be more elegant to do everything in a view model and use binding, // admittedly. kitNumber.TextChanged += HandleKitNumberChanged; HandleKitNumberChanged(null, null); kitNumber.PreviewTextInput += TextConversions.CheckDigits; }
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); }
internal static IDataField CreateDataField(IField field, ModuleSchema schema) { return(field switch { StringField f => new StringDataField(f), BooleanField f => new BooleanDataField(f), NumericField f => new NumericDataField(f), EnumField f => new EnumDataField(f), InstrumentField f => new InstrumentDataField(f, schema), OverlayField f => new OverlayDataField(f, schema), TempoField f => new TempoDataField(f), _ => throw new ArgumentException($"Can't handle {field} yet") });
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(); }
internal Container ToContainer(ModuleSchema schema, ModuleJson module, string name, int offset, string description, FieldCondition?condition) { // TODO: This works and is pleasantly efficient, but it's pretty ugly. if (cachedFields == null) { // TODO: Check that all fields are either primitive or container, check the size etc. cachedFields = Fields .SelectMany(fieldJson => fieldJson.ToFields(schema, module)) .ToList() .AsReadOnly(); } var lastField = Fields.LastOrDefault(); int size = Size?.Value ?? lastField?.Offset?.Value ?? 0; var common = new FieldBase.Parameters(schema, name, offset, size, description, condition); return(new Container(common, cachedFields)); }
/// <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(); }
public InstrumentAudioRecorderSettingsViewModel(IViewServices viewServices, ModuleSchema schema, string midiName) { this.viewServices = viewServices; this.schema = schema; var groups = schema.InstrumentGroups .Where(ig => ig.Preset) .Select(ig => ig.Description) .ToList(); groups.Insert(0, "(All)"); InstrumentGroups = groups; selectedInstrumentGroup = groups[0]; InputDevices = AudioDevices.GetInputDeviceNames(); // Try to guess at a reasonable input based on known inputs including the MIDI name. var expectedInputDevices = new[] { $"MASTER ({midiName})", $"IN ({midiName})", $"KICK ({midiName})" }; SelectedInputDevice = InputDevices.FirstOrDefault(inputName => expectedInputDevices.Contains(inputName)); kitNumber = schema.KitRoots.Count; SelectOutputFileCommand = new DelegateCommand(SelectOutputFile, true); }
public IContainer ToContainer(ModuleSchema schema, ModuleJson module, string name, string description, ModuleAddress address, string?parentPath, SchemaVariables variables) { string path = PathUtilities.AppendPath(parentPath, name); if (Fields is object) { var fieldList = requiresOverlayResolution ? resolvedFields.Select(FinalizeField) : resolvedFields; var realSize = ModuleOffset.FromDisplayValue(Size.Value).LogicalValue; return(new FieldContainer(schema, name, description, address, path, realSize, fieldList)); FieldBase FinalizeField(FieldBase field) => field is OverlayField overlay?overlay.WithPath(variables.Replace(overlay.SwitchPath)) : field; } else { var containers = new List <IContainer>(); foreach (var container in Containers) { containers.AddRange(container.ToContainers(schema, module, address, path, variables)); } return(new ContainerContainer(schema, name, description, address, path, containers)); } }
internal FieldContainer(ModuleSchema schema, string name, string description, ModuleAddress address, string path, int size, IReadOnlyList <IField> fields) : base(schema, name, description, address, path) => (Size, Fields, FieldsByName) = (size, fields, fields.ToDictionary(f => f.Name).AsReadOnly());
public Parameters(ModuleSchema schema, string name, int offset, int size, string description, FieldCondition?condition) => (Schema, Name, Offset, Size, Description, Condition) = (schema, name, offset, size, description, condition);
internal IEnumerable <IField> ToFields(ModuleSchema schema, ModuleJson module) { string description = ValidateNotNull(Description, nameof(Description)); string name = Name ?? description; int? repeat = module.GetCount(Repeat); var offset = ValidateNotNull(Offset, nameof(Offset)).Value; FieldCondition?condition = GetCondition(); if (repeat == null) { yield return(ToField(schema, module, name, offset, description, condition)); } else { var gap = ValidateNotNull(Gap, nameof(Gap)).Value; List <string>?lookup = null; if (DescriptionLookup != null) { ValidateNotNull(module.Lookups, nameof(module.Lookups)); lookup = module.Lookups.FirstOrDefault(candidate => candidate.Name == DescriptionLookup)?.Values; Validate(lookup != null, "Lookup ${DescriptionLookup} not found for descriptions."); Validate(lookup !.Count == repeat, $"Lookup ${DescriptionLookup} has {lookup.Count} elements; field has repeats {repeat} times."); } for (int i = 1; i <= repeat; i++) { string indexedName = Invariant($"{name}[{i}]"); string indexValue = i.ToString(CultureInfo.InvariantCulture); string?lookupValue = lookup?[i - 1]; string fullDescription = string.Format(Description, indexValue, lookupValue); yield return(ToField(schema, module, indexedName, offset, fullDescription, condition)); offset += gap; // TODO: This is ugly. We need it because otherwise in SetList we end up with offsets of // 0x03_00_00_00 // 0x03_00_10_00 // 0x03_00_20_00 // .... // 0x03_00_70_00 // 0x03_00_80_00 => This will be promoted to an *address* of 0x03_01_00_00 automatically... // ... // 0x03_00_f0_00 // 0x03_01_00_00 => We now get 0x03_01_00_00 again :( // 0x03_01_10_00 // But it's annoying to have this *here*... if ((offset & 0x80) != 0) { offset += 0x80; } if ((offset & 0x80_00) != 0) { offset += 0x80_00; } if ((offset & 0x80_00_00) != 0) { offset += 0x80_00_00; } } } FieldCondition?GetCondition() { if (Condition is null) { return(null); } int conditionOffset = ValidateNotNull(Condition.Offset, nameof(Condition.Offset)).Value; int requiredValue = ValidateNotNull(Condition.RequiredValue, nameof(Condition.RequiredValue)); return(new FieldCondition(conditionOffset, requiredValue)); } }
/// <summary> /// Creates a new instance. /// </summary> /// <param name="schema">The schema of the module whose audio was sampled.</param> /// <param name="format">The format of all the samples.</param> /// <param name="durationPerInstrument">The length of each sample.</param> /// <param name="captures">The captured audio data.</param> public ModuleAudio(ModuleSchema schema, AudioFormat format, TimeSpan durationPerInstrument, IReadOnlyList <InstrumentAudio> captures) => (Schema, Format, DurationPerInstrument, Captures) = (schema, format, durationPerInstrument, captures);
public ModuleSchema LoadTD27Schema() => ModuleSchema.FromAssemblyResources(typeof(ModuleSchema).Assembly, "TD27", "TD27.json");
internal InstrumentDataField(InstrumentField field, ModuleSchema schema) : base(field) { // TODO: Use primitive data fields as we do in TempoDataField? Schema = schema; }
private protected ContainerBase(ModuleSchema schema, string name, string description, ModuleAddress address, string path) => (Schema, Name, Description, Address, Path) = (schema, name, description, address, path);
internal ContainerContainer BuildPhysicalRoot(ModuleSchema schema) => (ContainerContainer)Containers !["Root"].ToContainer(