 Hey, so I'll be talking about Go and MIDI, but we're really talking about calling to the Linux system from Go. MIDI is just a great example of ancient stuff that's still great. So there's a couple different tracks here, six tracks. We're currently in the first track, that's the presentation header. We'll be moving into what MIDI is, then on to Linux. Hardware for this, a demo, maybe, and a conclusion. So if you don't know, MIDI is not just a train station you guys got here on. It's a protocol from the early 1980s, used to connect computers and musical instruments with synthesizers and effects and all sorts of wonderful things. It's still in active use, it's at 21k bits per second. It supports 16 channels multiplexed, and no audio data is transmitted. It's just triggering notes on remote devices. So it's like GRPC, but a long time ago. So live messages is to be like you're in an actual performance. There's really two types of messages that you would be working with. Channel messages, which is voice or note data. And the mode, which pretty much is about silencing or changing how particular channels work. And system messages, which is real-time clock information. Common messages about timing code. There's two different ways of doing timing in MIDI. It's great that way. Changing banks. And exclusive, which is just a stream of bytes that most devices will ignore. And then some, the one device you care about will probably misinterpret. And then we have the file version of MIDI, which is, you're not live, you're working with it, you know, on a computer or back in the old days on a physical device. Those pretty much contain the MIDI messages that then you can do in different order. It stores those messages along with their position. And there's three different versions. The first version is single track. So you only have one track of data that can contain 16 channels. Which is really useful for old equipment because they could start at the beginning of the file and work sequentially through the file and not have to worry about anything. Multi-track allows you to do more work with sequences and have different channels and different tracks to work with things a bit easier. There's a synchronous mode, which is version two, version one, sorry. Which is the one you're more likely to find in an asynchronous mode, version two, which I've never seen in the wild. An important thing to note here is since there's no audio data here, it sounds only as good as the hardware synthesizing it. So you can have really bad synthesizers found in your 90s computer that are really bad sounding and then you can have much more extensive synthesizers that sound very good. In these files, there's three different types of messages that you can find. Meta messages, things you would find later in ID3 for music files such as title, copyright, lyrics, and time signature, tempo, and key. Those are important because in MIDI files the positions are based on pretty much changes in tempo and the time signature and it computes on the fly when it should play the next note in a fun way of conserving space from the 80s. And then channel messages and system messages from before. There's also a 2.0 that came out earlier this year, last month, while a draft came out. They haven't really told us what's going to be it other than pretty much everything in the rest of my talk is now outdated. Except it's backwards compatible, but they haven't explained how. So Linux, which is probably the more interesting part of my talk. Kind of set the stage here, I'm using a Linux distribution for Raspberry Pi's called Go Crazy. It only has a Go runtime. There's no C except for the kernel which is fun because you don't have to worry about C causing set faults or anything which is a bit sad because you get to do everything from scratch. So there's just calls. The simplest way of calling into the kernel is to directly call into the kernel. And there's a lot of stuff that's exposed this way. Here's the Unix package. It's not the entire table of contents. There's a 5 megabyte limit in Chrome creating a picture of a page. So that's where the 5 megabyte limit is. There's more things in the table of contents. Three things that are probably the most important in here is the syscall, the syscall 6, and the syscall no error functions. And there's a bunch of wrapping functions to make this way easier to use, especially across multiple architectures. First two are differences based on the number of parameters you pass to the kernel. And the last one is if you don't want, the message you're sending doesn't have an error code returned from the kernel. As you can see, everything is Uint pointer. So where I use this in my MIDI project, for a while I didn't have a Go MIDI library. So I was just using one from C and kind of cheating and throwing in a C runtime. So I wanted a file that I had in memory to be played by the C program. And option one is to create a temporary file on disk. But you might not have permissions to temporary directory. It does not guarantee to exist. You have to remember to clean it up at the end. You have to, in some environments, care about allocations and you're using memory but not counting for it. And it's a big mess. Option two is to ask the kernel, hey, give me a file descriptor that's backed by memory. You do that with MIM FD create, which is from the Unix package. Give it a string, which is used only in case something goes wrong. And you need to look at kernel memory. And flags, which is used for this cool thing about sealing things and making sure you can only do certain operations on it that I just ignore. F trunk 8 to set the size of that file. And then M map to map that memory for the file into our program so that we can then change the byte array and that will change what the file looks like to a program that's using that file descriptor. And then the cool thing in here is when you stop having any open file descriptors that memory just goes away and you don't have to remember to clean up and remove the file. Second way that you can interact with the kernel is with IO control or IO cuddle if you're a Kubernetes person. In this case, we want to look at the information about the Ethernet device, ETH0. We wouldn't be talking to the Linux kernel if at some point in these slides we didn't find an unsafe pointer. So here we're setting up a struct that exists in C library or kernel headers. We're opening up a Unix socket for the IPv4 interface and we're calling the IO control syscall with these parameters and then we get back in the struct populated information about that Ethernet device. So a little bit more higher level as we can use sockets similar to how we just used it but with passing data directly over the socket. And here we're looking at the information about the NAT table in the IP tables. And so we open a socket as before this time with SOCRAW and IPv... or IPRAW. And we're asking the kernel to populate a stock with the IP get info command and then we can look up in that struct how many entries are in that IP table. There's probably a person at this conference who just had a heart attack do not do this, especially when you want to iterate on the table later and you find out that the memory you care about is after the struct and then you make go sad by incurrenting memory past what it knows about. So these previous ways have some problems mostly that we're passing memory around and that's fine for small values such as an individual number but kind of annoying for structs, especially as they get more complicated. We have a couple of options here. One I prefer is Linux Netlink, which is an IPC mechanism between the kernel and user space or between multiple user space programs. They never leave the local host and they are pretty much a list of attributes and those attributes may be nested and for each family, which is a type of thing that you might be caring about there's a separate bus that you would connect to. It's a cool library that I use to help with the encoding and decoding of those attributes managing those underlying sockets and managing sequence IDs so you don't really have to care about them. On top of Netlink is a thing called generic Netlink which makes it easier for kernel modules to expose things to Netlink instead of having each kernel module make its own bus just using a generic bus with a node message format and we can query the kernel to learn about what generic Netlink families knows about and surprise surprise the same person has a useful library. So here we want to know about the Wi-Fi interfaces on our computer so we can create a generic Netlink client. We want to look up the family ID for it and then once we have the family ID we construct a message to the N180211 command get interface command with that family and then that will give us back a bunch of messages and if we parse those messages which is really easy we're just pretty much doing a switch and populating a struct that we can get all the Wi-Fi devices on the device we're running on. So it's a lot easier than constructing structs for every type that could possibly exist in C and then making sure you get the right types on those. So quick aside to desktop Linux I want to know about disks being changed in my mini player. On desktop Linux there's a user space daemon that would listen to events from the kernel and those would apply policies or create some links on dev tempfs and maintain metadata and you probably know those by names like systemd or eudev or vdev they maintain the metadata in memory usually also writing those up to disk somewhere at run and then emitting those out to debus so that other things could further process those events such as uDisks2 exposing RPCs over debus specifically about disks or uDevil which automatically will mount them. Those are all written in C so I can't use them but fortunately they are using things that are exposed such as uEvent and kObject so Linux creates and manages kObjects whenever devices are pretty much added to the system and updates them or removes them when they are changed and then those changes are also announced over netlink and kObject so that we could listen to them in our program and then the values for each device's last event are also exposed in sysfs surprise there's a library for this so since all the devices all the events we get are after we start listening we need to know about the devices that already exist so we can walk sysfs read the uEvent file and parse the key pair from each line it's pretty simple function pretty much just splitting on the equal sign and then populating a struct more interesting now hardware so back in the 90s Roland made this cool device allowed you to play many files from a floppy disk it was meant for teaching people how to play music because you could slow down the tempo and it would be one click per whatever division you want I just wanted it to play at normal speed so I thought about buying one of these but they are slightly more expensive than I want to pay for one these prices are actually down from when I last looked I was going for like $150 a couple months ago so I built my own using raspberry pi a midi synthesizer attached to the serial port floppy drive over USB and a player written in go why go? well there's a lot of great reasons for go cross compilation so it's easy to compile for raspberry pi concurrency primitives, fast, code reuse I say it's not C, I don't really know it very well I can read it, I can't write it well I can write it but then it doesn't work and out of the box tool chain so that it's really easy to test things so you know that they work because you can just run go test so the hardware I'm using is a wave blaster compatible midi synthesizer back in the day a bunch of names you've probably heard of if you're in music made these devices and there's a couple of modern ones that are available here in Belgium that is a picture of the original one I made a raspberry pi hat to interface with these wave table or wave blaster cards and then have an output for the audio this is what it looks like when it was working oh I shouldn't have said that yet and now the demo well the demo gods got me I made the mistake of looking at it or sneezing in the wrong direction and it doesn't work anymore but I'm going to try and fix it outside I have a floppy drive floppy disk with doom music ready to play for this demo and not working so conclusion I'll go to the next slide really fast at some point I have to say we're hiring at Cloudflare thanks to developer of all the libraries I'm using Matt, good crazy Michael and a chill who has a large collection of wave blaster draw the boards and answered a bunch of questions about how they work under the hood I have a blog I've blogged about this project once you can get the code for this project on my website and you can ask me questions on Twitter or now I guess any questions? I don't buy it I'm not sure that's why you needed to use netlink the question was why do I need to use netlink mostly I wanted to know when a floppy disk was inserted and removed and that's available from KObject which was on top of netlink or I could continually pull and see if the disk is in there but then the drive makes lots of noise yes the question was how do I plan on using this device I think, correct me if I'm wrong pretty much I just wanted to put it in the rack of AV equipment and occasionally pull a floppy disk off the shelf and put it in and listen to songs from video games I like and I could do that with a computer but that's not very fun they don't invite you if that's all you do I have some of those PCBs outside if anyone has any other questions thanks