 All right, let's get started. So I'm Brian Mitchell. This talk is Bits, Bites, and Blobs. And basically, this talk is about a lot of things, but I think the easiest thing to go over right now is what it's not about. So we don't get confused about what the goal is here. This is not about replacing tech-based protocols. It's not really about how to necessarily design a new protocol. It's not about when you should use one or another. It's really about how to really work with protocols that may exist already or something that you had a need to design. So of course, this talk could be about Zepet, but it really isn't. It could be about Erlang. It could be about all of the wonderful systems. We just heard about Burt RPC in the other room. Tom did a great job talking about a wonderful way to communicate between processes, and it was inspired by this wonderful language. It could be about JavaScript. You know, it's everywhere. It's not really about that either, though. It could be about LISP. Everyone knows everything can be written in LISP. Or Ruby. But it's really none of those. This talk is about inter-operation. It's about taking what we have and what we're coding against, and reaching out and actually inter-operating with other things. Stuff that maybe we didn't write. Maybe stuff that was already there that someone else wrote on the same project. We really need to break out of the bubble that we may be living in, and really try and connect with everything we can. So we're obviously at RubyConf. Ruby's the lingua franca here. You know, I can show Ruby code, and pretty much everyone out there is going to be able to follow anything I put on a slide. That's great, so I'll stick to Ruby, but Ruby by itself is a really boring world. I mean, we don't connect to anything. Now, today, the big vogue is clouds. I've got to have a picture of a cloud in my presentation, right? There's no way I could pass at a conference these days without a cloud. And HTTP is a big way to talk to that. So, you know, the standard way that Ruby programmers approach the talking to things tends to be, oh, we will use HTTP and we'll talk to services out there, and that's good, because we can connect everything to the cloud. The problem is sometimes not everything out there is already connected to the cloud, or sometimes there's something out there that you need to hook up to the cloud, and when you're stuck with writing that code, you don't have a lot of options. You can still speak HTTP, but, you know, what are we going to put here? We could use the world's perfect language, or we could use Ruby. Now, I'm a polyglot, so I'm perfectly comfortable with putting anything out there, but the big part about this is options, and talking about how we can use Ruby to improve these cases. These arrows are an important part of this system, and it's really the key to this talk. Now, if I were speaking to a room of MBAs, you know, this is where the talk would end, and we'd all be very happy, you know, arrows. Go home, think about it. But, now we're programmers, we actually have to get something working, we actually have to make something that delivers a real product to a customer, or completes whatever we have in mind. So, with that, I'll sort of start with some of the basics, but go through really fast. No offense, but it's surprising, it's actually shocking to see how much people don't know about binary, binary formats, binary protocols, and even how to work with those in Ruby. It's shockingly simple. It starts with ones and zeros. I hope you all know that by now. Ruby may be great at text, it has a great heritage. We come from a long list of languages. If you paid attention to the keynote earlier today, you may have seen things about functional programming, things about distributive programming, number crunching, object-oriented languages. All of these are great, but we have a really good heritage in text processing. We have first-class regular expressions. We definitely have better encoding in one nine. All of this is becoming more important and much more real, but that does not hide the fact that Ruby still can handle ones and zeros just perfectly fine. So, to quickly give an outline of what we're gonna run through here really quickly is what shapes and sizes binary data is sort of used in and what levels we can start thinking at. The first is the bits, the ones and zeros. Bites are pretty obvious. Sometimes we need to read a certain number, a certain shape or size of bits, so eight tends to be a pretty good size for bytes, but sometimes you have other sizes that matter. And then blobs. Now, the word blob may be confusing. I'll get to that later, but in this case, I'm not talking about some opaque thing you store in a database and forget about and make someone else handle. So, working with bits, I'll go really fast. We have really easy ways to represent binary data. There are numbers in Ruby. So, I have a couple representations. One is just writing it out as a regular number or a hexadecimal number, and the other is really just what it looks like to the machine, the series of ones and zeros. We have operations on these numbers. So, this is how we manipulate bits in Ruby. We have binary and, very, very simple. Anything that's not two, one basically becomes a zero. Same with or, except anything with just one, one, anywhere becomes a one. Very, very simple. We have exclusive or, which is very useful. We can build it a number of ways, but it's great that it's built into the language. A lot of people don't know that this is in Ruby. They just assume that it's one of those characters they don't write all that often. We also have this crazy character, the tilde is somewhat confusing at first. If you run it against the number, you may get some confusing negative numbers back. What we call this is one's complement. And the rule you really need to know for this is not so much what number you're expecting, but that if you take a number and you end it against its one's complement, you will get zero back. So, this is effectively like flipping all of your bits. The ones become zeros and the zeros become one. Very useful operation. Finally, we have bit shifts. This is like moving data left and right. It's like multiplying by powers of two. So, two to the power of one times one is two, and so on. You can see that multiplying and this is like dividing the other direction. So, it's a very, very easy quick way to move and shuffle bits around. Hopefully not too difficult to understand. Here's a little snippet of code that shows how this might appear in real code. I had a need to split a 64 bit number into two parts. So, I return an array. One part I use a bit mask. Very, very useful concept. And I end it against the number. So, basically everywhere there are ones, it will return those bits back to me. So, the first 32 bits are returned to that first side. And then I'm just assuming here that I'm getting a 64 bit number so I don't care about using a mask again. I just shift all the other bits off the edge of the number and I return the rest. So, another 32 bit number for the other more significant bits. Very, very simple. So, working with bytes might be a little more interesting. But in Ruby, this is where it might actually become a little confusing. Ruby doesn't really have a byte type. You have numbers, which can represent any number of bits. And then you have data, which is usually stored in something like this, a string. So, a string, binary data. This is getting confusing because before we were talking about, well, we're talking about binary data, not text. But in Ruby, we store these in the same utility. This may get confusing even more so as we move into Ruby 1.9, where we may have an encoding on this. Sometimes the binary encoding on your string, sometimes maybe it could be Unicode, or sometimes it might be Shift-DIS, whatever it is. When we're talking about binary data in a string, we're talking about a string which has an encoding of binary. And when you're in 1.9, you need to make sure you set that. So, again, we can look at this string. This is with a 1.9 API. We'll work in later versions of 1.8. But if you run this, just assume it's gonna break on 1.8.6 and maybe 1.8.7, I forget. But, we can see we get a value for each character in the string. So, hello SFO, basically looks like this to the computer. Now, we could also display this as ones and zeros, but that would probably take too much space. So, very, very simple way to access data through strings. And we can also get specific bytes. Again, this is a 1.9 API, but you can also use square brackets. You're on an earlier version. I prefer using slightly more future-proof APIs here. But, we get a 70 back here for the seventh byte, which is the uppercase F. And, obviously, we can do that for the rest of the characters. It's very easy for accessing specific data by index. And setting bytes is pretty easy as well. So, in this case, we want to take the last character, the O, and replace it with 33. Now, I'm just treating 33 as a piece of data, and I print the character, the string out, and get hello as that. So, it's pretty easy to modify that. Now, it happened to be a printable character, but there's no reason why this could be any amount of arbitrary data. But the key with byte in Ruby to remember here is that we're dealing with eight bits, and it just assumes that that little space of data is going to be somewhere between zero and 255. So, if you try and set something else, you'll probably get very unexpected results, usually in error, although there are places where that doesn't even occur. So, be very, very careful to understand what byte means, where you use it. And again, on one nine, make sure that if you're going to use any non-explicit APIs on strings and you're expecting binary data, you should use force encoding binary on that string to make sure that you're always talking to it in that format. Because if you use indexing or something, you're going to be operating on character codes, and you may find that regular ASCII or text like that seems to work fine until you have something that actually means something in, say, UTF-8, some form of Unicode or another multi-byte encoding. And, well, at that point, you're screwed because you'll find that you're indexing no longer as consistent. So, definitely watch out for this one. So, up until now, we haven't talked much about how we actually interoperate, though. We've just talked about storage and structures. Strings are great, but if we can't send them anywhere, it's quite pointless. So, I.O. is a big part of what makes this work. So, in this case, I.O. is going to basically be our door to the outside world. It could be a file, it could be a socket, it could be a string buffer of some sort, whatever we need, we're basically communicating through some sort of write and some sort of read. This lays the foundation for allowing us to build really, really interesting ways to communicate. But the problem with just writing binary values is we really don't have any structure. So, if we're sending the value 42 down this pipe and receiving n says, oh, I'm expecting a number, it might know that, oh, I'm just expecting to read four bytes, then I'm gonna figure out what number that is, but there's not really any way to deal with interesting structure. And that's the key to working with I.O. So, the next thing we really need is blobs. And working with blobs is actually pretty easy in Ruby, but a little esoteric, this is probably the part where maybe some of you guys are getting new information. First thing you need to know is pack and unpack. So, pack is a method on array that generates a string of binary data. And unpack is a method on string that generates an array of data values. So, there is a lot of documentation out there. You can Google this pretty easily. One of the better ways to get a quick idea of how this works is just to use RI and look at the R doc. This is pretty long and verbose to go through line by line. So, I'm not going to. Don't even try and read this slide. The really, really interesting point here is that there are a lot of options. And it takes a while to get used to all of these to really understand what should I use when I write this data value. It may, it may depend on the protocol you're using. It may depend on what you're trying to design, but you need to make sure that you use the right characters. It's tricky, maybe a little esoteric, but very, very important in this case. One thing you'll notice though as you're going through this list is a lot of them say, oh, this is for packing a little endian value and this one's for packing a big endian value. Maybe some are signed and unsigned. My recommendation generally is just use network by order. And what that means is big endian by order. There's really no reason unless it's specified in what you're trying to communicate with to use little endian encoding anymore. Please, seriously. It's not worth it. So Ruby has a lot of blobs that you may encounter quite often and a lot of quick and simple ways that you may not realize of generating these things. One that we use very often and sort of take for granted is Marshall. So Marshall.dump. This basically generates a string that serializes whatever I pass like this hash into binary. It's very, very fast, but the big problem with this is it still only communicates to the Ruby world. Now, you could technically write something out there that parses Marshall.dump. The problem is, and I'm sure the Rubinius guys can talk about this, Marshall.dump gets really quirky. It has a lot of interesting parts that deal with little optimizations here and there that Ruby uses for certain types of data storage. So you get a lot of interesting little quirks in the actual format. So it's probably not worth using this as a interoperable format. Another one that a lot of you probably just heard about is BERT. BERT really picks acts. I mean, gem install BERT right now if you can, if you've got wifi, because it's that awesome. BERT looks kind of the same. Actually, it's like BERT and code, whatever data you have. Symbols don't really make too much of a difference in BERT, but in this case, I'm basically serializing the same semantic data structure and the other end can basically deal with that same way. What we get though is something like this. If you look at the string from encode, we see a lot of binary data. We can kind of see the text in there, but it's not very readable, but we should probably understand how it's getting there first. So if you open up the BERT source code, you'll see in part of the code, the encoder, you'll see these lines. Write one, write two, write four. So one byte, two bytes, and four bytes. In this case, it's basically writing network byte order data just in the exact number of bytes as labeled. This is the core that BERT builds up on to be able to do something like this. So in Ruby, we have fixed num, and when we want output that number, it needs to be in a specific range. So when we write fixed num, it checks against what range, and then it writes a tag, small int, and then it writes the number. In this case, if it's small enough to fit inside of one byte, it writes just one byte for the data. If it's bigger than that, we may have to write four bytes, and if it somehow does not fit in that range, we call another method to write something bigger or out of that range. Now the thing about this code is we've got a lot of constants in here, and this is something you start seeing developing in a lot of implementations for binary protocols. Small int, what is small int, and what is max int, and what are we actually writing here? If we look at another file in BERT, we see something like this. Small int equals 97, int equals 98. What all of these are are magic numbers. Some sort of symbolic representation for a machine that says, when you send me this number, I'm going to treat the rest of the screen this way or that way. Readable names are really important. When you write your code, you obviously want other programs and yourself to be able to understand it later. This is something that sometimes gets old, but is very important to write for binary protocol. Make sure you use constants where possible. So we can make some of this quite, quite fast. Another reason to use binary data is for speed. A lot of things in Ruby tend to be really slow, and a lot of the community just assumes that we're sort of stuck at a certain amount of performance. It's kind of silly that I see a lot of the community writing Rails apps and they're sort of getting used to the point where, oh, it's just a millisecond. That's not a lot of time, right? I mean, if my request takes five milliseconds, a millisecond's a huge amount of time. It's just a millisecond. We have 1,000 of them per second. But in the real world, a millisecond is ages, ages. And we can actually write fast Ruby code where a millisecond becomes too big to actually measure and where we need to start using microseconds. Now, there's a fact to get out of the way. Whenever you're writing something to be fast, there's probably something else that's going to be faster. Ruby's never going to be seed. We can always pretend, but why? I don't know, there's always going to be something else. And one of the reasons why is things go faster if you don't run them. There are so many things that Ruby is doing that all of our libraries are doing that might not happen in another library. We don't need to drop all of the nice things we have in Ruby to go into this other world where, oh, we get all the speed and none of the benefits. So really, what are the real problems then that keep us from going fast in Ruby? What are the problem points? One reason is, honestly, Ruby's garbage collection tends to suck in most implementation. Now, this is mostly because we use a really, really simple approach to MRI. There are benefits to that, one of which makes seed extensions really easy to write, but a conservative garbage collector has not allowed us to do a lot of things that would, say, make J. Ruby run really fast. Blobs can create lots of objects. And this is where a garbage collection becomes an even bigger pain point. When you're serializing and un-serializing things in pure Ruby, you may not realize, but when you have a string literal right in that code, you may not modify that, you may not assign it outside of that function, but when you have that string literal appear anywhere in your code, it actually creates that string object. When you're doing all of these operations, you need to be very, very careful. You could end up creating per second, tens or 20,000 objects or more per second that don't need to be created just because, well, you were lazy or didn't want to think about the code you were writing. So this sort of operation is a big key to making your binary code go back. So solutions to these, there are a lot of ways to think about this, and honestly, you don't always want to try and go for the fastest code possible, but one option is to, well, use JRuby, it's got a fast garbage collector, right? My problem is JRuby isn't a silver bullet. I really, really enjoy using JRuby, but it's on the JVM, and that's its biggest strength and its biggest weakness. It has a lot of things to offer, but if this doesn't fit in your ecosystem, there are other options you need to start considering and other ways to improve your code base. One big tip I have is to void extra strings. When you're dealing with binary data, it's all in these strings, and you really only need to create the ones that you actually send and actually communicate with in the outside world. If you're constantly chopping things up, recreating strings for intermediate values and computations, your performance is going to hurt. One area I found a lot of data points on this is when I wrote a Memcache client recently. Remixedash was more of an experiment on API design, but I at the same point decided that I would actually try and make this thing perform and also try and see what I could do with the, now at that point, stable binary protocol for Memcache. So I looked up the docs and I saw something like this. So they love these ASCII diagrams, which ironically is inside of an XML file. I don't know who invented that, but an XML file with an ASCII diagram, and we see that it's got a pretty simple format. We've got a magic code at the beginning and then we've got something that tells it what to do and a key length, a few other things and eventually all the rest of the data totals 24 bytes. That's great. The best thing about this protocol is this header right here, this request I send off is a fixed size, which means I can generate something, it's a fixed size, push it through and really make a lot of assumptions. This is a big, big win. This is one reason why I can make the Memcache client go really, really fast. So this translates to code something like this. So I've cut out a lot of other code, but you can see I use constants a lot here. So I have a header format. You can see I have this, again, esoteric string of how many bits and bytes to pack for each part of the array. I have some magic numbers here, requests, magic numbers, eight, zero and X and set is one. And then to make a set packet, I have the header, which is that fixed 24 bytes plus the payload. So I have two strings there. If you look at the a, esoteric size, what those are. And then to actually generate it, it's very, very simple. What's great about this piece of code is to actually send it out. I only create two objects, two new objects. I create the array and then the string app is packed. The zeros and eights are technically new objects, but they're immediate to Ruby. So fortunately, only two objects actually hit the heap or are allocated outside of the actual reference to the data. So immediate values are a big win here. And we can send that through to the IO very, very quickly. Now I read a response and that creates other objects. I won't go into that, but that code is very, very, very quick and very tight. What this gives me though, as a result, is I can do for memcache with this pure Ruby code, per process I get 15,000 operations per second. That's really fast. Now we were talking about milliseconds before. That's about 0.06 milliseconds per second per operation. So that is very, very fast. Now the amount of work to measure it is something that took a while and actually is very hard in Ruby because of garbage collection and other things that make timing very, very unstable. But you can start seeing what the benefit of doing this sort of thing is and what it opens the door to actually integrate with. But that's all about speed. Sometimes we need to optimize for the developer. Sometimes we don't need to worry so much about how fast can we send this data? How quick can we get a response back? What's the latency? What's the memory pressure on the garbage collector? All of those things aren't sometimes a primary objective. Sometimes we need to make development a lot easier. We still have to deal with these protocols. We still have to integrate, but we shouldn't necessarily do work we don't have to do. One good example of this that I liked was MQP. So this is a messaging protocol that was built by a standards committee. You'd be surprised that it's actually pretty pragmatic in how it's actually constructed. There may be one or two things that aren't so great in the spec, but all in all it's very simple and very tight. But one of the best parts about this committee is they decided we're going to have the regular tech specification, but also we're going to give people our specification in a machine readable format. Now what does this mean? It means we get something like this. This is an excerpt in JSON format of the MQP spec. You see something like ID 20. So it's about what numerical ID this command is and what arguments it takes. So what to send down the line and what formats they are, what names they are so that the API that the programmer uses can be defined. And things like default values. Very, very, very easy to use in a computer language. So what we can do from this is we can build code off of this. We can actually generate this code. Now whether you want to do it at compile time or run time or even just do it via reflection is up to you. A lot of MQP libraries just generate the file. Generating styles is not so popular with some people, but it's actually very appropriate when maybe you don't have the freedom to design your own protocol. So in this case, I don't know how readable that is. The light's a little light on it, but you can see that we basically have this really, really straightforward declaration where you can see we have almost the DSL where it's short and then it's the name of that item right after it and bit and table and all of those things. Now all of these things actually mean something to the code. So internally to the generator, you just have to core or kernel for actually dealing with these names of types. What it ends up looking like is something like write your type in the data. So you have this case statement in this case. This is from the Bunny MQP client which uses the crack generated code, whatever they wanted to name it. Anyway, you see you can go through each case, octet, short, long, long, long. Some of these are really simple. So you write data and you can see the pack and unpack character codes there. But sometimes it gets a little more complicated. Long, long, you see that upper and lower part. Ruby doesn't have a good way to do network by order 64 bits. So again, they're doing the same sort of thing with bit masks, although in this case they are masking the upper bound of the data as well. Short strings, long strings, everything you need. And this continues for quite a few data types and is actually quite rich. Code generators are not always the answer, but they can definitely help development speed up, especially when it's something that you really need to abstract and actually hide. Something that might be in a legacy system or something that might be dealing with a language that doesn't really have the sort of flexibility or data types that you're used to dealing with. There are a lot of projects out there to make this a little easier. I'm not necessarily going to advocate one or another, but it's good to pass through these. Thrift is one that's gaining some popularity in certain circles. It sort of looks like this. You have a structure, you define the fields and the order of the fields and operations. So you sometimes have optional parts of the structures. You sometimes have complex data types as members in a structure. But the key here is that you basically define your structure. Now Thrift also goes to the extent of trying to define service APIs and what to call and how to call it. I kind of think that part is overkill, but some people find it very useful. If it's applicable and you think it's good for your use case, go for it. It definitely helps you write things across more than one language. Protocol Buffers is another interesting code generation method. Still sort of the same IDL mindset. So we have message person and we have all of these parts. And it's interesting that we still haven't really gotten out of that same sort of point of view, but at least we get a little farther away from trying to define service layer APIs. We're just sending this down a socket. That's much, much easier to deal with in some cases where we know what's listening in one end or we have a smart piece of code that can proxy something. We don't always need all of that soap style web service layer API thing. But many more to come. Now this question is interesting in the sense that if you guys attended the BERT talk, I definitely agree with Tom in the sense that a lot of these tools are something that you're going to see for older code. Code that is using things that well, if it's not BERT, you can't use BERT code. If it's written in BERT, you're going to have to understand BERT. And that's basically why we need to understand and be able to allow all of these things to integrate. Of course, nothing's worth doing unless it's a little fun, because this is Ruby. So of course, there are a lot of interesting protocols out there and a lot of things we can do once we actually extend and embrace some of these. One fun one that I've been having a lot of fun with is OSC. OSC stands for open sound control. It's actually kind of like new generation MIDI. It's MIDI that doesn't suck. It's little binary packets you send over the network. And these packets encode everything from little note data to wave data to whatever you want to send. And the other side reacts and does something. It's very popular to use in both the live performance of music and the VGA scenes. It's starting to extend into devices. So actual hardware devices are starting to speak OSC directly. What this allows us to do is in Ruby, we can actually speak OSC. So we have this case statement here and this code taken from Ruby OSC. Same encoding pattern we've seen before. We see all of these data types we can send over OSC and how to encode them. It's very, very trivial. The thing I like about OSC though is it's very pragmatic too. In this case it doesn't make the strict distinction between we're in all binary format or we're text based. One thing you'll notice in OSC is all your targets for messages are actually string paths. Kind of like slash one, push two, whatever you want it. So you have a push button. You hit this button on channel one. You trigger push two and someone gets a message. And a listener can define some sort of pattern or expression to listen for this. Now I'm not gonna go into exactly how OSC works at the deep level but I think this is interesting because it opens something new for us to work with because we understand how to deal with binary data. So, of course this would be more fun with a demo. So I'm going to actually attempt to write some code. You bear with me for a second. Okay, so to basically use these libraries, usually they're packaged up in RubyGems and in addition we, oh yeah, sorry. Is that readable? More contrast, let me see if I can. Less color, okay, let me see. My wallpaper's better on this screen. I could switch it if you want. How's that? All right, okay. So OSC Ruby is what I'm going to use for this demo. So in the background here I have a program running called Ableton Live. And I just quickly put together a quick set. I'm not gonna really go into the depths of it but here I've got this track which is just a basic guitar sound. Now this doesn't have a lot of interesting effects other than, here let me see if I can, back to you. So I've basically put this guitar sound on it and then when it receives meeting notes I put an arpeggiator on it. Basically what an arpeggiator does is it takes a note and then from that note plays more notes in a pattern maybe with some effects of four filters or whatever. So what I get is a pretty easy way to play notes. So when we send a note, here we can basically get an interesting sound. Now I'm not gonna guarantee anything extremely musical here but we can see what we can get. So let's go back to our code and let's see what we can do. So Live, we should actually make a connection first. So OSC Client, New, Vocal Hosts and I'm gonna connect to 8000. Now what this thing is that I'm connecting to is actually a proxy because Live does not speak OSC directly so I'm going to use this little program called Osculator which basically takes OSC and listens on a specific port and then forwards things to whatever I specify. So Osculator is quite useful in this case so we'll connect and then let's try sending it a message. So the first thing I really need to do is Live send, thank you. So we need a new OSC message here. So first parameter here is going to be the path so let's just say note. Let's give the note a name, let's say C2. We could basically say okay, let's play this note. One basically means it's on. Let's sleep for say three seconds then let's send another message C2 and zero is basically off. Close that last one. Okay so let's see what actually happens when we run this. Now I'll prepare you, it's not actually going to do anything interesting. It's going to run, sorry, it's going to run and exit. And we didn't hear a thing partly because I'm not wired up here but partly because we haven't actually told anything what this message means. So if we drag Oscillator back over we can see that it received this note message and we need to tell it what it is. I'm going to tell it it's a MIDI note and I'm going to drag it down to C2. So this basically is telling it, oh send this note to anything listening on this channel, channel one and send and forget. So it handles everything for us. So if I detach this and come back to RubyConf here I can run this program and we should hear a note. Okay that was a little quiet. I think I have my velocity down. Let me set this up. Actually I think I have something that should have a couple snippets over here. Set the velocity at, I don't know why it's so quiet right now actually. Okay oh I know why. Note velocity right here. The reason it's not changing velocity is Oscillator needs to know about that as well. So let's quickly set that up. That should fix it. So change this to a channel velocity. There we go. You can hear the arpeggiation. So this is a little boring right at this point. So we're sending a command to play a note and well at this point it's really a useless program. So until we add a little more it's not really going to be that interesting. So to save time and partly because I know a lot of you want to go have some lunch. I wrote a little more code here. Let's see if I can show more of it. Okay so that's really close to the edge of the screen. But you can see I create the same connection. I create some constants just to make things a little more readable. I create a delay so in Ableton Live I have the beats per minute set to 125. So 60 divided by that basically gives me the delay between each beat. And then I define a couple notes C2, G2 and F2 and then I loop through this four times with the delay of four beats. And then I set a few things randomly. Note velocity is random. I was working on this, well that doesn't matter, that line doesn't matter. And then at the end I make sure everything's off because we don't want the note to just continually play without stopping. So if we run this and then switch back to Osculator we can set these extra notes. And let's just follow our naming convention. There are other ways to send notes but this one was easy enough so just trying to keep things simple. Okay so now we have our notes set up so we can try and see what this actually will sound like. So let's switch back to our code. You can hear how it's actually changing notes. So we're getting a little farther but it's pretty short. So if I extend this to say a little four times longer. So you can hear it basically changing velocity, changing notes and changing arpeggiation patterns. If you guys have questions on this I can definitely answer more but this should be good enough for a demo for now. Let's go back to the presentation. So basically that's it. If you guys have questions you can contact me either during this conference or on Twitter at Binary42 on GitHub as Binary42, at GML as Binary42. Most things Binary42. So any questions? All right, thanks.