Esempio n. 1
0
 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);
            }
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
 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;
 }
Esempio n. 5
0
 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();
        }
Esempio n. 7
0
        /// <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);
            }
        }
Esempio n. 8
0
 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));
 }
Esempio n. 9
0
        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.");
            }
        }
Esempio n. 10
0
        /// <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));
        }
Esempio n. 11
0
 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();
 }
Esempio n. 12
0
        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;
        }
Esempio n. 13
0
 public DeviceController(RolandMidiClient client) : this(client, TimeSpan.FromSeconds(1))
 {
 }
Esempio n. 14
0
        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);
                }
            }
        }
Esempio n. 15
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);
        }
Esempio n. 16
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);
        }
Esempio n. 17
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);
        }
Esempio n. 18
0
 public DeviceController(RolandMidiClient client, ILogger logger) : this(client, logger, TimeSpan.FromSeconds(1))
 {
 }
Esempio n. 19
0
 private DeviceController(RolandMidiClient client, ILogger logger, TimeSpan loadSegmentTimeout) =>
 (this.client, this.logger, this.loadSegmentTimeout, Schema) =
Esempio n. 20
0
 protected override async Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client)
 {
     using (var device = new DeviceController(client))
     {
         return(await InvokeAsync(context, console, device));
     }
 }
Esempio n. 21
0
        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);
            }
        }
Esempio n. 22
0
 protected abstract Task <int> InvokeAsync(InvocationContext context, IStandardStreamWriter console, RolandMidiClient client);