Category Archives: V-Drums

V-Drum Explorer: Planning notes for MVVM

My current clunky “put all the code in the view” approach to V-Drum Explorer is creaking at the seams. I’m really not a WPF developer, and my understanding of MVVM is more theoretical than practical. I’ve read a reasonable amount, but quite a lot of aspects of V-Drum Explorer don’t really fit with the patterns described.

Eventually I’d like to present blog posts with a nice clean solution, and details of how it avoids all the current problems – but I’m not there yet, and pretending that good designs just drop fully formed onto the keyboard doesn’t feel healthy. So this blog post is a pretty raw copy/paste of the notes I’ve made around what I think I’ll do.

There’s no conclusion here, because life isn’t that neatly defined. I do hope I find time to tackle this reasonably soon – it’s going to be fun, but I’m not sure it’s going to be feasible to refactor in small steps.

Problems

The current design has the following issues:

  • A lot of the UI controls are created within the code-behind for the view, so styling is hard
  • The logic handling “how the UI reacts to changes” is really tricky, due to chain reactions:
  • Changing a tempo sync value changes which field is displayed next to it
  • Selecting a MultiFX option changes all the fields in the overlay
  • Selecting an instrument group changes which instruments are selected, and the vedit parameters available
  • Selecting an instrument can change the vedit parameters (e.g. cymbal size)
  • The UI currently “knows” about data segments, addresses etc – it shouldn’t need to care
  • The way that the schema is constructed makes the address logic bleed out; we can populate all the “fixed” containers and only generate fields for overlay containers when necessary
  • Using inheritance for the commonality between ModuleExplorer and KitExplorer is a bit weird

Ideas and questions that might end up being part of the answer

  • My schema has a “physical layout” and a “logical layout” – for example (in the TD-27), each kit has 24x KitPadCommon, 24x KitPadMain, 24x KitPadSub, 24x KitPadMainVEdit, 24xKitPadSubVEdit. It makes more sense to show “24x instrument info”. Is that information part of the model or the view-model?
    • I suspect it’s part of the model, effectively.
    • Perhaps the ViewModel shouldn’t even see the physical layout at all?
    • The logical layout is worryingly “tree view + details pane”-centric though
  • We have “snapshotting” in two ways when editing:
    • Overall commit/cancel
    • Remembering values for each multi-fx / vedit overlay while editing, so you can tweak, change to a different overlay accidentally, then change back to the original one and still have the tweaked values.
    • Should this logic be in the ViewModel or the Model?
  • Should the model implement INotifyPropertyChanged (et al)?
    • Looks like there’s genuine disagreement between practitioners on this
    • Feels like a pretty stark choice: either ViewModel controls everything, including how overlays work (i.e. when changing instrument, change the overlay too) or the model needs to tell the ViewModel when things are changing.
  • Where do we keep the actual data? Still in a ModuleData?
    • We could generate a model (e.g. with Kit, KitPadInst etc being full types)
    • We’d then either need to generate all the corresponding ViewModels, or access everything via reflection, which defeats half the point
    • Would definitely be easier to debug using a model
    • Generators are a lot of work…
  • Possibly reify “schema + data” in the model, to avoid having to pass the data in all over the place (hard to do binding on properties without this…)
    • Reify overlays in just-in-time fashion, to avoid creating vast numbers of fields.

Goals

  • Broadly the same UI we have now. At least the same features.
  • No code in the View – eventually. Not clear what creates a new window when we load a file etc, but we can try to remove as much as possible to start with and chip away at the rest.
  • ViewModel shouldn’t need to touch ModuleAddress. Access is view ModuleSchema, containers, ModuleData.
  • 7-bit addressing should be isolated even within the model, ideally to just ModuleAddress.
  • ViewModel interaction with fields should be simple.
  • The command line app should be able to work with the model easily – and may require lower-level access than the UI.

V-Drum Explorer: Memory and 7-bit addressing

So, just to recap, I’m writing an explorer for my Roland V-Drums set (currently a TD-17, but with a TD-27 upgrade on the way, excitingly). This involves copying configuration data from the module (the main bit of electronics involved) into the application, displaying with it, editing it, then copying it back again so that I can use the changes I’ve made. I use MIDI System Exclusive (SysEx) messages to request data from the module.

(Since I last wrote about all of this, MIDI 2.0 has been ratified, which has more sensible 2-way communication. However, I don’t think the V-Drums line supports MIDI 2.0 yet, and even if it does I expect we’ll need to wait a while for drivers, then managed APIs exposing them.)

In fact, most of the time that I’m working on the V-Drum Explorer I don’t have it connected to the drum kit: it’s much easier (and quicker) to load the data from a file. Once it’s in memory, it really doesn’t matter where the data came from. I’ll go into the file formats I’m using in another post.

This post is about how that configuration data is organized, and particularly about the 7-bit addressing it uses.

Download the docs!

If you’d like to know about all of this in more detail, it’s well worth downloading some of the reference documentation Roland very helpfully provides. The TD-17 is the simplest of the modules we’re talking about, so I’d suggest downloading the TD-17 MIDI implementation so you can go at least one level deeper than this blog post, if you’re interested. If you think you’re likely to want to do that, I’d suggest doing so before reading any further. The important bit starts on page 5 – the “Parameter Address Map” which is the bulk of the document.

Configuration as memory

The configuration data in the module isn’t stored in any kind of file system, or with separate SysEx messages for different kinds of data. Instead, it’s modeled as if the module contains a big chunk of memory, and different areas of that memory have different effects on the module. I don’t know what the implementation is like within the module itself, of course; this is just the interface presented over MIDI.

As the simplest possible example, address 0 in the memory (on all three of the modules I have documentation for) represents “the currently selected kit”. It accepts values between 0 and 99 inclusive, to represent kits 1 to 100 inclusive. (As a reminder, a kit is a configuration of all the pads, allowing you to switch the whole module between (say) a rock feel or something more electro-funky.) So as an example, if my module is currently on kit 11 (“Studio / Live room”), and I asked the module to give me the content of address 0, it would return 10. If instead I set the value of address 0 to 30, the module would display kit 31 (“More cowbell / pop”) and all the sounds would change accordingly.

The documentation describes a number of containers – that’s my own term for it, but it seems reasonable. Each container is named, and has its own description in terms of its content at different offsets. For example, address 0 belongs in the [Current] container, which is documented very simply:

[Current]
+-----------------------------------------------------+
| Offset Address | Description                        |
|----------------+------------------------------------+
|          00 00 | 0aaa aaaa | Drum Kit Number (0-99) |
|                |                             1-100  |
+----------------+------------------------------------+
| 00 00 00 01    | Total Size                         |
+----------------+------------------------------------+

The 0aaa aaaa shows that 7 bits of the value are used. Due to MIDI’s inherent 7-bit nature, each address can only store 7 bits. Whenever a larger number is required, it’s stored across multiple addresses, typically using only the bottom four bits of each 7 bit value.

The content of each container is broadly broken into three types of data – again, all the terminology is mine:

  • Primitive fields: strings, numbers etc
  • Fields which are other containers (often repeated, e.g. Kit 1 to Kit 100)
  • Overlaid containers (fields directly in this container, interpreted according to a different container)

I’ll talk about overlaid containers at length another time, as they’re tricky.

So basically you end up with a natural tree structure. So far, so good… except for 7-bit addressing.

7-bit addressing

I entirely understand why the values in memory are 7-bit values. That’s inherent in MIDI. But Roland also chose to use a 7-bit address space, which makes the code much more complex than it needs to be.

All addresses and offsets are documented using hex as if it were entirely normal – but the top bit of every byte of an address is always clear. So address 00 7F is followed directly by address 01 00 – even if they’re within the same container. Now this does mean that the values in the MIDI request messages are exactly the documented addresses: the top bit of each byte in the request message has to be clear, and that drops out naturally from this. But it makes everything else hard to reason about. I’ve been bitten multiple times by code which looks like it should be okay, but it’s either skipping some data when it shouldn’t, or it’s not skipping addresses when it should. By contrast, it would have been really simple (IMO) to document everything with a contiguous address space, and just specify that when requesting data, the address is specified in seven-bit chunks (so bits 27-21 in the first request byte, then 20-14, then 13-7, then 6-0).

I’ve tried to isolate this into a ModuleAddress struct, but the details have still leaked out into a few more places. Over the past few days I’ve tried to be more rigorous about this with a DisplayValue (7-bit) and a separate LogicalValue (contiguous), but it’s still leaking more than I want it to. I don’t think I can fix it without a more serious rewrite – which I probably want to attempt reasonably soon anyway.

You might wonder why I don’t just model everything using the logical contiguous address space, removing all the gaps entirely. The problem is that all the schema information is basically copied from the documentation – and that refers to the 7-bit addressing scheme. I really want the schema to match the documentation, so I can’t move away from that entirely. Another thing that makes it tricky is that a lot of the time I deal in offsets rather than addresses. For example, the “Kit Unit Common 1” part of a “Kit” container is always at offset 00 20 00 relative to the start of the container. That’s not too bad on its own, but I also need to express the “gap between offsets” which is a sort of offset in its own right (maybe). For example, “Kit Unit Common 2” is at offset 00 21 00 within a kit, so in the schema when I describe the “Kit Unit Common” repeated field, I describe it as having an initial offset of 00 20 00, with a gap of 00 01 00. That sounds fine – until you’ve got a repeated field which is large enough to have a gap in the middle, so you need to model that by making the offset have a gap as well. (I’m reminded of calendar arithmetic, which has similar weirdnesses.)

The lesson I’ve learned from this is that when there’s hairiness like this, it’s worth abstracting it away really thoroughly. I wish I’d stopped at the first abstraction leak and thought, “maybe I need to redesign rather than live with this”.

Conclusion

Even without 7-bit addressing, there would have been plenty of challenging choices in the design of the V-Drum Explorer, particularly in field and container representation. More details of those choices will come in future posts – but at least they feel inherently tricky… the kind of thing software engineers are expected to have to think about.

7-bit addressing feels like it was a choice made to make one small use case (MIDI request/response messages) simple, but made everything else trickier. I’d be fascinated to know the module code is a lot more complicated because of this as well, or whether so much is effectively hard-coded (because it needs to actually do stuff rather than just display data on a screen) that it doesn’t make much difference.

Next time: using data vs code to represent differences between modules.

V-Drum Explorer: MIDI interface

If this is the first blog post about V-Drum Explorer you’ve read, see the first post in this series for the background. In this post we’ll look at the MIDI interface I use in V-Drum Explorer to send and receive configuration information.

MIDI basics

(Apologies to anyone who really knows their stuff about MIDI – this section is the result of bits of reading and experience working with my drum kit. Please leave corrections in comments!)

MIDI is a protocol for communications between musical instruments, computers, synthesizers and the like. There are many different ways of connecting devices together, including dedicated MIDI cables, USB, TCP/IP and Bluetooth, but applications shouldn’t need to care much about this. They should only need to worry about the messages defined by the protocol.

MIDI is one-directional, in that APIs expose both input ports and output ports which are effectively independent even if they’re connected between the same pair of devices. Messages are sent on output ports and received on input ports.

Most electronic instruments (of all kinds, not just drums) support at least basic MIDI functionality. There are several simple messages with standard representations, including:

  • Channel Voice messages (such as “note on” and “note off”)
  • Channel Mode messages (such as “all sounds off”)
  • System Exclusive messages which are manufacturer-specific

The channel voice and channel mode messages apply to a specific MIDI channel. It’s possible for multiple devices to be daisy-chained together so that they can be controlled over a single port, but using different channels. Each of these messages has a fixed size.

Note: one irritating problem with the protocol is that I haven’t found a way of detecting which channel a particular device is on. This is why channel-specific operations in V-Drum Explorer (such as recording instrument sounds) need a user interface element. If there’s something I’ve missed here, I’d love to hear about it.

Only a few operations in V-Drum Explorer need to simulate or react to the channel messages. Most of the time we’re interested in the system exclusive messages which allow the configuration data to be fetched and set.

System Exclusive Messages

System Exclusive Messages – often abbreviated to SysEx – are manufacturer-specific messages for additional functionality. The size of the data isn’t fixed, but is indicated by a byte of 0xF0 indicating the start of the data, and another byte of 0xF7 indicating the end of the data. Each byte within the data has to have the top bit cleared.

The first byte of data is conventionally the manufacturer ID as assigned by the MIDI Manufacturers Association, which allows a device to ignore messages it wouldn’t properly understand. For example, all Roland SysEx messages have a first byte of 0x41.

Protocol design note: MIDI is a relatively old protocol, dating back to 1983. I’d hope that these days we’d allow more than one byte for a manufacturer ID, and use a length-prefix instead of restricting every data byte to 7 useful bits. Oh well.

SysEx messages are divided into realtime and non-realtime messages. There are also universal SysEx messages which are non-manufacturer-specific, using “fake” manufacturer IDs of 0x7E (for non-realtime) and 0x7F (for realtime).

For the V-Drum Explorer, we’re interested in four SysEx messages:

  • We send Identity Request (universal, non-realtime)
  • We receive Identity Reply (universal, non-realtime)
  • We send Data Request (Roland, non-realtime)
  • We send and receive Data Set (Roland, non-realtime)

The identity request and identity reply messages are used to discover information about the connected devices. V-Drum Explorer uses them to check whether you have any supported devices connected (currently just the TD-17 and TD-50) and present the appropriate schema for the device.

The Data Request and Data Set messages are more interesting from the perspective of V-Drum Explorer: they’re the messages used to communicate the configuration data.

You can think of the configuration data as a bank of memory in the module. (We’ll look at the layout next time.) The Data Request message indicates “Give me X bytes starting at address Y.” The Data Set message indicates “Here are some bytes starting at address X.” Interestingly, Data Set is used both to respond to Data Request and to set new data in the module. When loading data from the device, I send a Data Request and wait for a corresponding Data Set. When copying data to the device, I just send a Data Set message. The device also sends Data Set messages when the configuration data is modified on the module itself (if a particular setting is turned on).

There are a few tricky aspects to this though, all effectively related to the protocol being two independent streams rather than being request/response in the way that HTTP is, for example:

  • Can the code rely on all the data from a single Data Request being returned in a single Data Set? (Answer: no in theory, but in practice if you ask for small enough chunks, it’s fine.)
  • How long should the code wait for after sending an Identity Request until it can assume it’s seen all the Identity Reply messages it’s going to?
  • How long should the code wait for after sending a Data Request until it can assume there’s something wrong if it doesn’t get a reply?
  • How long should the code wait between sending Data Set messages when copying data to the device?
  • What should we do with messages we didn’t initiate?

V-Drum Explorer MIDI code

In the V-Drum Explorer MIDI code, I’ve modeled Identity Request / Identity Reply using an event for “I’ve seen this device”, and I just leave half a second for all devices to respond. For Data Request / Data Set things are a little trickier. For better or worse, I expose an asynchronous API along the lines of

async Task<byte[]> RequestDataAsync(int address, int size)

I keep a collection of “address/size pairs I’m waiting to see data about”, and when I receive a Data Set message I see whether I’m expecting it, I complete the corresponding task, with a buffer of unexpected messages for diagnostic purposes.

The current implementation uses the Sanford.Multimedia.Midi library, which at least assembles multiple packets into SysEx messages for me. I’m trying to move to managed-midi though, as that’s more cross-platform. (In particular, it should make a Xamarin client possible, as well as running a CLI on a Raspberry Pi.) The managed-midi library is quite low-level, so I’ll need to perform the reassembly myself – but that shouldn’t be too hard.

One thing I’m really hoping is that I can reimplement the VDrumExplorer.Midi project with almost no changes to VDrumExplorer.Wpf.

Next time…

In the next post, I’ll go into more detail about the layout of the configuration data, and the annoyance of 7-bit addressing.

V-Drum Explorer: Introduction

This is the first in what I expect to be quite a long series of blog posts, meandering over a fairly wide range of topics as they crop up. There’s nothing particularly technical in this introductory post. It’s really just a starting point.

V-Drums

In July 2019, inspired/encouraged by a friend name Alice, I bought an electronic drum kit. It was a Roland TD-17KV; I now have the Roland TD-17KVX because I’m a sucker for upgrades.

Both of these are kits using the TD-17 as the module. That’s the part of an electronic drum kit that turns electrical signals from the triggers on the pads into sound and/or MIDI messages. The module is sometimes known as the “brain” of the kit. There’s quite a lot of terminology involved in electronic drum kits, and I suspect I’ve barely scratched the surface in the few months since I’ve started.

The TD-17 series is just one option within a suite of products called Roland V-Drums. The TD-17 is currently the most recent part of this suite. It’s a mid-tier product; currently the high-end kit is the TD-50 and the entry level is the TD-1. When I was choosing between the TD-1 and thd TD-17 (with the TD-50 never being an option) I was basically drawn to the greater flexibility of the TD-17 module over the TD-1. I didn’t know how feasible it would be to configure it programmatically, but it at least felt like something I’d have fun investigating.

Configuring a TD-17

In order to understand why I was even considering coding for this, it’s worth having a look at what you can configure on the TD-17 module.

There are a few top-level things you rarely need to configure, such as which triggers you have attached to the module. Most of the configuration happens within a kit – and there are lots of kits. Now this is already slightly confusing because right at the start I talk about buying an “electronic drum kit”. The term is effectively overloaded, unfortunately, but most of the time in this blog series I’ll be using the term “kit” in the sense it’s used within the module.

The TD-17 supports 100 kits. There are 50 presets, and 50 user-defined kits. There’s no real difference between the presets and user-defined kits in terms of what you can do with them – you can think of it as “There are 100 kits available, but Roland got bored after creating 50 of them and left the other 50 in a default configuration.” A single kit is selected at a time, and this controls everything about how the drums sound.

At the top level of a kit, you can configure:

  • Its name and volume
  • The instruments (this is going to be another overloaded term)
  • Its MIDI configuration (which MIDI instruments are triggered)
  • Its ambience (simulations for being in a concert hall, or arena etc)
  • Its MultiFX (sound effects such as reverb)

There are 20 “instruments” to configure – one for the kick pedal, two for most other triggers (snare, toms, crash cymbals, hi-hat, and a general purpose aux), and three for the ride cymbal. There are two instruments for most triggers because you can configure the head and the edge/rim separately – as an extreme example, you could set the crash cymbal to sound like a gong when you hit the rim, and a snare drum when you hit the head. You probably wouldn’t want to, but you could.

For each of these instruments, you can set:

  • The left/right pan (primarily for playing with headphones, so everything sounds like it’s in the right place)
  • Equalizer settings (low/medium/high frequency response)
  • The instrument (this is the overloading…) – for example “13 inch maple tom”
  • The “sub-instrument” which is a second instrument layered under the first (e.g. the first preset kit has a beech kick with a sub-instrument of a deep shell kick) and how the two instruments are layered together
  • How much the ambience and sound effects affect this particular instrument

Each main/sub instrument then has its own extra tweaks in terms of tuning/size, muffling, and snare buzz – depending on the instrument.

Of course, you can just play the TD-17 with the preset kits, never going into any of this. But I saw possibilities… the user interface is designed very well considering the physical constraints, but I tend to think that complex configuration like this is more easily managed on a computer.

V-Drum Explorer

And so the idea of the V-Drum Explorer was born. Of course, it’s open source – it’s part of my demo code GitHub repository. There’s even documentation with screenshots so you can get the general idea, and a Windows installer downloadable from the GitHub releases page.

From the very start, I imagined a user interface with a tree view on the left and details on the right. It’s possible that by thinking of that from that early on, I’ve missed out on much better UI options.

This blog series is partially a diary of what I’ve done and the challenges I’ve faced, and partly a set of thoughts about how this affects professional development.

I expect to blog about:

  • The MIDI protocol used to communicate with the TD-17
  • The configuration data model
  • The challenges of 7-bit addressing
  • Enabling Hi-DPI for .NET Core 3 WinForms apps
  • Initial code vs schema considerations
  • The transition to a full schema
  • Displaying data more usefully: the logical tree
  • Working with real users: remote diagnostics
  • Immutabilty and cyclic references
  • Performance: WPF vs WinForms
  • Performance: field instances
  • Code signing
  • Windows installers
  • (Maybe) Xamarin and BLE MIDI
  • Moving back from schema JSON to code for ViewModels

That’s likely to take a very long time, of course. I hope it proves as interesting to read about as it has been to implement.