 Oh no. Yeah, thank you for the heads up. Appreciate that Seagriver. Was that Seagriver? Yeah, thank you for the heads up Seagriver. Ah, that is, I am, I don't know about that one. I am like 99% sure I clicked it. It must have grayed out or something? Like the page changed afterwards or something? I swear I went over there and clicked it today. Anyway, off to a bit of a rocky start here. Let me, I guess redo a quick introduction since we did just go live on YouTube there. So yeah, sorry folks. Anybody who's watching YouTube is joining us now. Anybody who was watching on any other platforms, just a heads up, I had failed to start the YouTube stream, it seems. So we have a couple of folks. Anybody watching on YouTube will be just joining us now. And if anyone is watching on YouTube, looks, okay, now, yeah, thank you. I was gonna say, if anybody on YouTube is watching, let me know if we're good there now. Okay, so yeah, let's do a quick introduction again. Sorry about that. And then we'll continue on. So my name is Tim. This is the deep dive program. I go by foamy guy on GitHub and Discord. In this program, we're working on Circuit Python related stuff. In particular, I'll be working on the Cowbell sequencer again, but for folks that might be new or watching this in the future, if you don't really know what I'm talking about here, Circuit Python, this is the main website for it where you can learn more. CircuitPython.org is that website. This is basically an implementation of Python that runs on tiny computers called microcontrollers. There's a bunch of different pictures of them here on the downloads page. These are essentially just small computers capable of executing Python code that interacts with other hardware peripherals that's connected via all these IO pins, either along the sides here or alligator clips or some devices like this one. They don't necessarily have pins, but they have a bunch of built-in things. You can see a bunch of keys and neopixels and a knob and a screen. So all different shapes and sizes of these things run this Circuit Python code in order to interact with the hardware that you have connected to it, be it RGB lights or buttons, beepers, buzzers, knobs, whatever it may be that you have connected, you can write some Python code to interact with and control that external device. So that is kind of the high level, 50,000 foot view of what we are looking at on this program. We work on different areas, different weeks. This week we're working on the MIDI sequencer, which is actually the same thing I worked on last week, but sometimes we go inside the core, work on new functionality or testing out PRs inside the core. Sometimes we work at the library level. Just depends what's going on for the week and I have still been playing with this thing quite a bit. So this is what we're gonna work on again this week and then we'll probably be getting back into some PRs and testing interviews and stuff like that for the next couple of streams after this. But this is what we got for tonight. I should say thank you to Adafruit. Adafruit is a hardware and software company based out of New York. This is their website, Adafruit.com. Adafruit is the company that's paying the folks that work on the Circuit Python project. So there are some folks who work on the project full-time. Adafruit pays them to do so. There are other folks like me who work on the project part-time and Adafruit pays me to do that part-time. So of course thank you to them for making Circuit Python a possibility by doing that. And then thank you to everyone who wants to purchase hardware from them because you are also helping to make Circuit Python a possibility by buying hardware from them, which keeps the bills paid, keeps the lights on there at the factory and of course helps them be able to pay the folks working on the Circuit Python software and related stuff. Okay, so jumping back into the sequencer for the night. I wanted to start by just talking about a few of the things that I actually ended up implementing off stream. I did do a bunch of work on this device on the stream last week, but I also did some work over the break where I just didn't have it in me to stream at the time. So I didn't pop it on, but I was tinkering around and playing with it a bit. So I just wanted to talk about the current functionality and then talk about what I wanna implement next. So I will mention two before I jump straight in. The sequencer code is live on GitHub here. TR Cowbell Sequencer is up to my GitHub from a couple of days ago. This is definitely very early alpha version of the software. I don't know of any glaring bugs but I also don't guarantee it to be bug-free, certainly. And it is still limited functionality in some areas, obviously. So that's what we are gonna be working on is iterating on this code a bit more tonight. Some of the things though that I have done that I don't think I have talked about yet on stream, I wanted to go over. So I don't remember, I think we never, I think I was using the knob button for mode when I was on stream last and I now am using the center button of the D-pad here. It's maybe a bit difficult to see because of the focus but the little icon in the top left of the display there is changing back and forth when I press the center button of the D-pad. So that's how we change modes now. I'll leave it on this one, which is the select note mode. This is how we can change the musical notes that are on each of our steps. Oh yeah, thank you by the way. Yeah, I appreciate you mentioning that. Let me pull that up and mention that as well. Yeah, I was on the Simple Electronics podcast this week, YouTube Simple Electronics podcast that went out earlier this week. I was on there. So huge thanks to the Simple Electronics podcast for having me on. It was a great discussion. I was very happy to go and talk, had a nice time. And then I also have to say hug report for sure to Johnny. Johnny Bergdahl is the one who put me in touch. So thank you for that. Thank you for the pointer there. And yeah, if anybody wants to go and listen to that, that is out there now on YouTube. And I assume probably other podcast platforms I have only listened to the Simple Electronics through YouTube, but I'm sure it's probably syndicated beyond there as well. Okay, let me, I am gonna, for now I'm gonna just jump in a little bit further there and then focus in on the display just so we can see a little bit more of what's on the screen. There we go. Right now we are in the notes changing mode. Again, the center button on the D-pad is gonna change modes. This would be the step selecting mode, the step changing mode. This is allowing you to, you know, select which step is highlighted within the sequence. And then the other mode, this one, the note selecting mode. This is allowing you to change the actual note that is currently selected. So it was C5, I scrolled the wheel down a bit. Now it's F4. We could scroll it back up to F, C5 if we want. And so generally speaking, the mode essentially controls what the rotary encoder is gonna do. And then I think it also, there's a few places where it controls what the other buttons on the D-pad do. So for instance, if you go to note selecting mode, you can use the left and right buttons on the D-pad to move the selection. So even though you're in note selecting mode, where your rotary encoder is affecting the actual note that's being played, you can still use those D-pad buttons there to go left and right. So that's very convenient if you wanna edit one note and then move over and edit another note, move over, edit another note, so on and so forth. So that's one thing. And then you can also use the up and down to change the note as well. So you don't actually just have to use the encoder. If you wanna bump by one or two notes or whatever, you can just do it with the up and down buttons right there as well. So that's one of the main changes for the note selection mode. The other big change that I did not really do on stream or haven't talked about on stream yet is the ability to save and load loops. So the way that that works, if I remember correctly, I believe it's hold the mode button. So I'm gonna hold that mode button down. Yeah, we hold the mode button and then we go to a save mode. So now we're in a third different mode. It's probably hard to make out there, but it's a little floppy icon for the save mode. When you're in save mode, we have a few different possible things we can do here. We can either save the loop that is currently on the device or we can cycle through the loops that are already saved and choose one of them to load. So we kinda have both options available to us from this mode. Before I go and do either one or the other, I wanna just show where that stuff is saved. This is about where I had gotten to before I realized that we didn't get the YouTube going. Thanks again, by the way, see Grover for the heads up on that. I can't believe that didn't go this week. I swear I, anyway, it doesn't matter. So what I have on my device right now is I actually have a saved loops.json file. You can see actually down below there, I also have a saved loops.py, which is a older version of this, older implementation of this idea of saving and loading loops. The current one uses JSON. This is the one I think I'm gonna be using moving forward. This is just a data file that has essentially a list of loops inside of it. Each loop is one of these objects here. There's a main object with loops and then that's got the list in it and then the actual loop object is just a list of notes right there. This will be a length 16 list. So it's one index for each step in the sequencer. So those are our notes and then it's actually storing the selected index as well, which is the one that is currently highlighted orange, which is, it's not necessarily really part of the loop, but it's kind of like metadata that goes along with the loop. So that's everything that's stored right now. I have thought about some other ideas of maybe being able to store some other things in here, like control changes or program changes or something like that, which are some different MIDI concepts. I don't know for sure if I'll end up implementing that anytime soon or not, we'll see. But I've had a couple of other ideas. I'm not sure though. This is kind of the most basic version and I'll probably be sticking with it for a bit before I add much more in the way of like saving additional data. I will say the one thing I kind of want to be able to do, which is not there yet, this is probably the most likely thing to get done anytime soon is support for longer loops. This loop is essentially gonna have to be 16 notes long. Yeah, thanks for dropping the link there. See Grover appreciate that. This is gonna be assumed that it's always gonna be 16 notes. I think it'd be cool if we could do, even if we could just do another set of 16 or maybe three or four sets of 16, I think that would be kind of cool if we could get a little bit longer. Right now we have 16 steps, which if we consider each step like a quarter note or something, that's like four measures. We're playing pretty fast though. So each step is, I don't actually know if it's set to an eighth note or what, but it's pretty fast. So yeah, if we had a couple of more actual sequences, if we had a couple more pages, I'll say of steps per loop, I think that would be kind of cool. So that's one thing that I may do. But beyond that, I don't necessarily think there's gonna be too much of a change in terms of the way it stores and loads data and the major capabilities of it so far. And then you might be asking like, how do I do this right? Saved loops JSON. So there's actually one other bit of magic that makes that work, which of course is we, I now have a boot file, which sets the drive to be writable. It does look at the up button on the D-pad, the top button here. If you hold that up button down while the Pico is booting, then it will be writable to the computer. If you don't hold that up button down while the Pico is booting, then it will be writable to the circuit Python code. Therefore, it will be able to save loops. So let's save the, well, let me just add a couple of more notes to the loop that I do have and then save it. So I'll scoot this over here. I'll scoot this over here and I'll turn this on and then let me, let me know if the sound on that is too high or too low or anything. So right now I've got kind of beep, beep, boop, boop. And then what I would like is a doodaloo after that. So beep, beep, boop, boop. Maybe about here. Go all three of these and we're gonna have, I actually, I wanna get back to, let's see here, let me pause that for just a second. I do wanna get back to, actually wanna get back to playing mode, which is something I had not considered actually. So we actually, I don't have a way, I don't have a great way to go back to playing mode once we're in saving mode. We could just go ahead and save it. So in order to save this one, I would just click the center button again. So I'll do that and it, well, I didn't have this open, so we would not be able to see it there. I think it prints into there. It looks like it does actually just leave me into save mode. So for the first actual code change, let's go make it to where after it's done saving, it puts you back to playing mode. And then so probably this, it's not gonna let me actually save the changes to this. And unfortunately, it's also not gonna show me the changes either unless I disconnect, reconnect. But supposedly we did just save it. I do think I actually, I will just go ahead and disconnect, reconnect. And what I'll do is instead of actually disconnecting, reconnect. I'm just gonna reboot with the reset button, but I am going to hold the up button, which now means that the storage is gonna be writable to my computer. So I'm gonna hold up and I press reset. And then I'm just gonna keep holding up until after the thing is up and running all the way, basically. So I just need to make sure to be holding that down during the boot pie execution, essentially. Okay, so this changed. We want to load the one from the disk, which means what? Load in memory and on disk. We want the one from the disk. Why do these buttons not have disk in memory? Load file system? There we go. So we should theoretically now have an extra one of these loop objects. The very last one should be our new loop. So, oh, no, we've mode selecting file. No, that did not work. Okay, well, that's all right. But, well, either that or possibly this is still not showing me the correct version of the file. I have had that, unfortunately. Basically, I wanted to undo. I just want to undo, like no more changes. Get rid of all of my changes. I guess I can't do that. Oh, well, I'm just gonna put a backup of that before I do anything else. And then just make sure that I can actually write because I don't want to go and make a bunch of changes and then not be able to actually save. Okay, it did save. Let's make sure it ran it did. Yep, storage is read-only true. So I added this to print out whether it's read-only or not. When it is read-only, that means that you're not gonna be allowed to save files. You can load files. And when I say files, I guess I should actually say loops. You can, when it is read-only, you can load loops, but you cannot save them. When it's not read-only, then you can save and load loops. And then the gotcha there is that it needs to be read-only in order to edit your code pie file. So it can only be writable to either circuit Python or the computer. Therefore, when we want to edit our code pie, we need to make it writable to the computer. When we want to be able to save loops, we need to make it writable to circuit Python. So what I would like to do is a couple of things. When we're in the save mode, I would like to have a way to get back to just out of the save mode without loading or saving, essentially, just a way to go back altogether. We're gonna be looking in the buttons task here. I think what I'll do is just say the left button maybe, like when you're in the selecting file, which is technically the name of this mode. When you're in the selecting file mode, if you press the left button, then we'll just go back out of the selecting file mode and go to selecting note, either selecting note or step, which one do we want? Does it matter? Do we keep track of what state we were in? So where's the center button? Middle button, long press. Selected file is none. New mode is selecting file. Yeah, we don't keep the old mode. We could though, I guess. I don't know, I'll just hard code it. So we go to selecting file, when we long press the middle button, if we were selecting file and we regular press the middle button, this is where we're gonna either load or save. So if state.selected file is none, then that means we have not elected to load a new file, which means we are gonna save the loop that we have running currently. Otherwise, if that is not none, that means we have selected a loop to load. Therefore, we are gonna load that loop and essentially just go back to the selecting index mode, which is selecting the steps. And technically, it's already loaded by virtue of the fact that we pressed the up or down arrow to cycle through the list. So we want to just be able to go back. Left button fell, if we are in the, whoops, if we are in the selecting file, if state.mode equals equals selecting file, then we will just go back to selecting index. State.mode equals selecting index. We're gonna do index or note. Honestly, I find myself using the selecting note mode a lot more than selecting index. I find it's more convenient to be able to scroll the rotary encoder to change notes than it is to be able to scroll the rotary encoder to change the selected one. There's only 16 possible selected ones, but there's more than 16 possible notes, essentially is really what it boils down to. And usually when you're moving selections, just moving by one with the left and right buttons is I have found at least to be plenty accessible rather than needing the actual rotary encoder to go faster. How's it going, Johnny? Hug report, if you are just tuning in, hug report for you, I mentioned earlier for hooking me up with the selected, excuse me, the selected, the simple electronics podcast that went out this week, my episode of that. So thanks again for making that connection. I had a lot of fun with the conversation on that show. So this will take us back to selecting note. This kind of just gives us a way to bail on selecting files. So let's just make sure that's working. I'm gonna save this. I wonder, are we gonna boot back to read only or? Yeah, okay, yeah, we're good there. So I'm just gonna put a couple things on and then we're gonna go to loading mode. We're in loading mode. We have the little disk there. Again, it's pretty hard to see. I guess maybe we'll go in and just focus a bit on this and then eventually we can zoom back out some. But apologies if you address this, but is the circuit board something that is available for purchase or on GitHub to play with it on KiCAD? Yeah, first of all, no need to apologize at all. Even if I had discussed it already, if you didn't catch it, that's not a problem at all. I'm happy to answer the question any which way. It is not available for purchase. This is a piece of hardware that was designed by a community member, DJ Devon 3. It is not available for purchase. It is, I believe, on GitHub. It is an open source hardware certified device. I am not one to dive around into KiCAD too much. So I have not messed with it in there. I don't know exactly what all is available, but I do know there are some stuff, there is some stuff available here. I'll point you to where it's at as well. It's in here, it's in Pico, it's in Boards, Pico, no Raspberry Pi, Pico, Cowbell. This is the sort of main repository for this device, this piece of hardware, this sequencer. There is an older 1.0 version, there's a 1.2 version, which I believe is the one that I have, and there is all kinds of information in here. There's the pictures wiring diagram, there's a link. There's that link that goes to Oshawa. That's where you can find the actual schematic. There's that Oshawa link, so that's, there it is. That's a good point as well, so this is actually based on another design that was by Todpot, that's linked here as well if you want the kind of inception version of this thing. Where is, I swear, maybe we go inside of this one. Here's Oshawa lab, so this page has the diagram and then PCB file. Well, which I think might be part of what you're asking for. So yeah, thanks for the question. All of the information is available there for the designs for the PCB. However, it is not available for sale. So we are in save mode, so I'm gonna press left to go back. So now we're going, yep, just back to select note mode, which means now scrolling disk changes the note, perfect, all good. Let's make it so after we save, we get some kind of feedback, because right now we just, I didn't have TO connected, therefore we just didn't get to know if that succeeded or failed. So when we save, that's gonna be when we press the middle button, when we're in the selecting file, specifically when the selected file was none. So saving here, why don't we put, I think let's put on the screen, saving. We're not really doing anything with the rest of the sort of status bar at the top. Let's add a new label to that and have a little word where we can put some information, like saving, saved. Maybe we could put the mode if we wanted to, just a little label where we can put some pertinent information to the user. Where do we make the rest of our labels? Well, right, this would be on the other device, of course, so let's go to the, which means we'll have to have a new object to send over, that's fine, we'll just add on to the dictionary, no problem. So circuit pi one, this is the feather TFT, so this is not the Pico W that's plugged into the sequencer, but it's the feather TFT that has the display on it right there. So in this one, we wanna add a new label, let's just go maybe like, so this is creating labels, but it's doing a loop one for each note, essentially it's most similar to the mode icon here. So let's make it right by that, let's just go, I don't wanna call it mode label, let's just, I mean output label, status label, let's call it status label, status label equals a new label with just the built-in font, so terminal IO font, eText, we'll just start on blank, color white for default, I don't know that might be the default anyway, but okay, that's probably good. Let's add it to the main group, main group dot append, status label, let's move it around though, let's go status label dot anchored, anchor point equals, let's go to the right side, so 1.0 and then on the top, 0.0, status label, anchored position, we're gonna go the display, the right side of the display, which is display dot width, and then maybe we'll just bring it back a tad minus like, I don't know, eight or something, and then for the why, let's just go a little bit down from the top, I don't know, maybe two, not very much. Let's actually put something in this for testing, testing just so we can see it, there we go, okay perfect, yeah that's just up there in that corner, do we have enough room for scale too? I don't think we do, but let's try it, yeah not so much really, maybe, I mean I don't think so, but maybe it's not the worst looking thing ever, but it's definitely behind the top of the bit there. I mean it almost looks like we could even cheat towards the north a bit, right? And that's getting to be not too bad really. The G is definitely cut off, the descender, the descender is gone, that actually brought the descender back and we can still see the top of the I, I'm surprised. Okay that one was too much, that might be the winner, I think that's it, so we actually, we did manage to squeeze in there the scale too, we'll see as we go, if we end up with a too big of a word, or if some other letters don't end up looking as good, then maybe we end up changing it back, but for now we'll keep it at that and it does seem to work, so I'll take it back to empty, and I think what I'll do is just when we receive data we'll check for a new thing like here, we're just gonna say, you need to clean this up, huh? I don't remember what I was doing there. So this is the mode in between these really. If data object, let's go actually if status in data object keys, then go status label, text equals data object, status, why is this map? Oh, no, wait, why is this map unresolved? I guess it just doesn't know it's a dictionary I think. I'm pretty sure this is making it into a dictionary or like a regular object, which in Python, my mind is dictionary. This must just not know, honestly, I don't even, we might need this, might just be able to say if status in data object. Let's try it, so then when we, so I just jumped back to the Pico, this is now on the Pico, when we send data over to the display that is inside the update display task, for now I'm just gonna cheat and put some text in there. So we're just gonna say status equals hello, smiley face. This will just send something, we do need to save, oh right, that saved both actually, that should be good. Oh yeah, perfect, there we go, so that came through. Okay, so we don't wanna send that anymore. I think what we'll do is, I think we'll put a thing on state that's called status and then we'll change that as needed. We can put it to blank whenever we don't need to show anything, self.status equals empty string. When we start saving, when we start saving, now we're gonna go state.status equals saving. If and when it completes, successfully, we are gonna go state.status, I don't know if we're gonna have, we might need to put an async.io sleep in here, actually. Otherwise, I don't know if we'll actually update this or not, we'll have to see. Another thing we should do though is actually, let's make sure that this actually works by not sending a status, because the dot keys is the way I normally had done this. Go to the state.status equal, we just say saved, I'm gonna just go ahead and put it. I'm almost positive we're gonna have to have async.io.sleep, at least a tiny bit in here, or else it's not gonna, I don't think it's actually gonna update the display. We can try it, I guess. We are still read only true, so we are gonna have to go back to read only false when we wanna be able to save. Are we ready to test it? Oh, okay, so back in our display on this, we wanna go else status label dot text equals empty, and then we actually wanna just make this a bit more efficient by also saying if status label dot text equals is not equal to data object status. So if it's already on the right thing, then don't set it again, because that'll just do work needlessly. And then same thing here, actually, if it's already blank, if status label dot text does not equal empty string, then set it to empty string. It should make it so hello goes away, I think. Oh, I don't have to open to that one, so let's go to restart. Label has no text. Just an E somewhere, there we go. Here we go, okay, yeah, now we got nothing, okay. So, and then if we, well, okay, we gotta go to read only false. So I'm gonna just reset without holding the up button. I think that should get me back to read only false. However, we also do have the problem of, by the time TO gets connected, this has already printed, you know what? Okay, before we start saving one more thing, actually, I'm gonna make it so that it will put on our status whether we are read only or not. I'm actually gonna hold up and press reset again, which is gonna make it reboot and presumably gonna make it reboot into read only true. And to double check, I'll just control C, control D. That did work. So let's say when we first boot up on the Pico, instead of merely printing this here, let's also, oh, we don't even have a state here though. Maybe we should move this down some. So then let's go, we haven't created it yet though. We only defined the class. It's not gonna be until main. Rw, I almost wonder, we probably almost want two labels. Maybe, I don't know, we'll see. Rw if this, that's actually R, R-O. Well, we could say if not, I guess. If not, Rw if not that else, R-O. Update display, so yeah. So when we're in here, we need to go status. State that status. Okay, so right now we're read only. If I, after we save and then load again, this is gonna be gone. We're not gonna be able to see it anymore, unfortunately. Almost wanna make another label just so, cause like, I mean, we still have a fair amount of space. I almost, I think I'm gonna do it. Let's go make one more. So I'll keep status label. I'll keep status label, but we'll make one more that's called read only label. This one is gonna be in the same place. And then status label. Status label is gonna be read only to start with. Right on this, so then we're actually gonna say not display with anymore. Instead, we're gonna say read only label.x-10, let's say. So read only label will be on the far right. Status label will be to the left of that. And then we need to send that back separately. So now update display. You know, another thing maybe that would be cool to add is tempo, changing the tempo. Right now we're pretty much hard coded at the same tempo. It might be cool if it could modify its own tempo, like without changing the code. Read only equals, I mean, do we wanna look it up every time? I think we just save it, right? It doesn't, it's not gonna change every time. Set it once there as state read only. Let's not do the status there anymore. So read only is good, equal to that. And then here we say read only equals state.read only. Let's add a read only, did we do that? I don't think so, class state. Start on none, just to distinct between like the null value versus true and false. Although what do we do if we get none on the other side? I don't know. It should get set before it gets sent, so. Count on that for now and then fix it if it doesn't end up being the case. So now we're sending back read only as the boolean and then status is now the string. And so our status won't have read only in it anymore, but our new label will have read only there now. Let's get it to say saving. It did update, it did actually update saving dot dot dot. But it is gonna fail, I guess. As we are read only, 392, 92. We should be try catching here with OS error. Accept OS state dot status. Let's just go print RO file system error. Status equals, failed to save. That's too long though, probably. I think we just put failed. Or can't save, we just go failed. Or FS error, maybe we go FS error. File system error, FS error. And it should be right next to the RO anyway. It should be right next to it because of the way we put the other label there. Saving did work though, it's to the left of RO. So I think we're good there. Let's just get the error message so we can test that. There we go, FS error. Okay, so now I'm gonna reset it into normal mode, into writable mode, which is, I don't know, it's normal for this device because that's what Bootpy says, but there we go. Ooh, that actually does say RW, interesting. I mean RO, did we not, right, right, right, right. We need to actually check for it on the side. If read only, so this is on the display one again. If read only is in the data objects, then set the read only label RW if not data object, read only else. Okay, there we go. RW means that it's writable, so that means you're actually good to go. But we could make a couple of notes on, we could go to saving mode, we could save. We did not see saving. We jumped straight to saved, and I wonder if that is the async.io sleep thing. Can we tell it to execute a certain task outside of its normal flow? Could, no, I really can't, right, because this is a wild true loop though, I think. Saving, if you happen to catch it, then it will say saving, but I'll try it. I don't know if we're gonna actually be able to see it. After we set this, let's go async.io sleep for a little bit. The idea being hopefully we allow an update display task iteration to happen, but I don't know if it's actually gonna work like that. We are not gonna be able to write right now though, because it is in RW, which means that we can't write to it from our computer. So let's go to read only where we can. So I'll hold the up button on the D-pad and I'll press reset. You can put the actual update into a synchronous function. So we would basically just, I see. So we would factor this into a function. The meat of that is actually in there. And then here we go and call it. Just pass the state along. Yeah, thank you for the idea. Don't know the architecture or the code. It's interesting. It's mostly just built on top of the LED bit that was there. I kind of just started adding stuff to it. I did not necessarily, for better or worse, I did not really plan stuff out, so to speak. I just kind of mimicked the LEDs as best I could to make the notes and then only changed when I needed to do other stuff, which was make it a number instead of a Boolean. But there's obviously been a bunch of other things added on top since then, the display in particular, which is what we're dealing with here. So here though, this should work though. I think you're onto something. So we make this into synchronous. This calls it and then we can now, inside of the other one up here, we could say we don't need to sleep. Instead, we just need to call this to tell the feather to update. And I'll do it again at the end or when we're selecting the file. And then I am on RO, so I'm gonna save that. That should work. Still on RO, so I'm gonna reboot into RW. Here we go. Now we're on RW. We could try to look in the file. It actually did save our last one. Look on the right device though. It actually does not seem like it is or like it did. We'll have to troubleshoot why the actual saving is failing. Or maybe it's, is it putting them at the top? Maybe it's putting them at the top. Oh, I think it's putting them at the top. Is that looks like the most recent one? That one looks different. But maybe we only managed to save one successfully, which is that most recent one. Yeah, anyway, we'll double check into that. But I think it did save and I think it just put it at the top. So we don't actually wanna modify this file though. We're just gonna close that. Okay, so we're RW. So we wanna go through and do another test on saving. So let's go first note, last note, save mode. I'll try to do it this way. Oops, so you can actually see. Does that matter? So up and down there changes the note value. Doesn't really matter. We can go to save mode. We can click middle button to say we wanna save this file. Saving, let me get saved or anything else. Oh, oh, we didn't pass state. And of course we're now in RW, so we can't save this. So we're gonna reboot to RO. This is the joys of the sometimes writable, sometimes not. Unfortunately, it kind of adds a bit of TDM to the iteration cycle of changing your code, but it's not too bad. And I eventually, I guess what we'll do is like an SD card would probably be really nice. If we had an SD card, you could save basically infinite loops, right? Like these loops are taking up almost no space. Even if you had a couple of gigs in an SD card, you could fit a lot of loops before you started even coming close to using it. All right, so we're back to RO. I did save, so we're gonna go back to RW. Try another save again. See if our labels actually work correctly. And at some point here, I'd like to get into playing with pitch bend. That's one of the things I'd still like to play with some. Use apico and thawnee slash web workflow. Oh, interesting. Yeah, that's true. That's a good idea. Use web workflow because it needs it to be, yeah. There's a way to know the IP on the screen, but I guess, oh, that's apico W. 13, but I, like we've had trouble with this, right? Actually, no. Did I not do the right one? That's apico W. Oh, did I click the feather? Click the feather, accidentally. Okay, nice. Good call. Miss the start. What's the display time? What's the display this time? Yes, it is a feather TFT. This one is an S2, yeah. Connected via UART to the sequencer. Okay, so where are we? Let's get our display running again. I think what we need to be doing is saving. Let's do different ones. I think I did that before. Go to saving mode. We can turn to save. There we go. We got saving, dot, dot, dot, then saved. Do we wanna go back after that? I mean, I think we could just leave it. We definitely would wanna show it for some amount of time, right? Because if you, we don't wanna make it so you can miss it. Yeah, I think we just leave it there. Okay, so saving works. We now have an output label that shows us what's happening, which is nice. We can tell if it succeeds or not. At this point, we could, can you have multiple files in this open or is it one at a time? This one's probably gonna be hard to read, isn't it? Is it code mirror? Is there a code mirror? Format code, doesn't come with formatting. You can re-indent. Pretty, your answer is no. That's our first one, which is the newest one we saved. It was that two C4s near the beginning and then a C4 somewhere near the end there. Boy, the select is a bit, going a bit crazy for me on this. I don't know if that's just visual or, oh, you know what it's doing is it's highlighting all the other instances of that thing. Interesting. I see, okay. So that's our new one. Let's load. Oh, you know the other thing we need to do, actually, though, is after we're done saving, we also need to put the icon back. That's where we wanna edit here. Can we have two tabs open or anything? Is it like, nice to have my loops on one tab and then my code pie on one tab? Oh, that one seemed to think it was not. Maybe I'm pushing my luck with two. Maybe we shouldn't do two. I messed with two. Hopefully I didn't break the first one. Yeah, this thing doesn't go find the code very well. Or is that, I mean, it should be another one, right? Middle button, L, saving, saved. And recently we didn't find that one. When that happens, we should then also go mode selecting, no, we'll ask this, nope. We'll have to get some control-s going. Oh, why does it not, why is it not putting me save? I'm not actually connected right or something. File system is in read-only mode. I don't know why, though. It's not connected to the PC. Does being writable from boot pie stop this from working? Maybe being writable from boot pie does stop this from working. Did I try updating node? I did not, I need to get back to that. Yeah, thank you for the reminder. I may have updated node, but I don't think I got any further than that, unfortunately, and that definitely did slip my mind. So like I said, thanks for the reminder. Seems like this does not work with, I'm guessing it's the fact that we have it read-only in boot pie. It seems like maybe it needs to not be there. This is my best guess. I think we're just gonna go back to switching back and forth here. So right now we're RW, we're gonna go hold up, reset. Here's, paste it back. It was basically when we set saved. I didn't, it's like it was not finding the ones in the code. Well, but it found like one of them. I guess it was not finding the ones in the code that were not visible at the time or something. Okay, yeah, that's definitely got the new stuff in it. That would be a bug. Question mark, I'm not sure. Yeah, I don't know, is it expected to work when you have boot pie, when you have set this from boot pie? I don't know how this interacts with the web writable. My theory is kind of like maybe this is ganking it from whatever would be able to write to it from the web editor. The dejecting the file browser do a real unmounts. I don't know, I think so. I don't know if there's like a command I could run to check it. I'd be happy to run it, but I don't know how to, I don't know how to tell. As far as I can tell, yes, because it doesn't show up here anymore, but that's like the only way I know to look so that's back to RO. So we're gonna reboot again to get to RW because we saved already. You know, didn't care about the drive status. I'll try it when it's in, I'll put it back to read only here in a minute and I'll try it on that mode and see if it makes a difference. It could also just be that it's not working generally and that I read herring, attributed it to the boot pie mounting. We'll try it back again in a minute. But yeah, we wanna save a thing again. I'm gonna need to delete some of these. Uh-oh, uh-oh, that was weird. You see that? We didn't show all three notes for some reason. Never seen that before. Just save mode, saving, saved, and then back to selecting note. Okay, perfectly. So now we've closed that loop. I'm in RW right now. So let me try again like this. Oh, this says feather. Oh, I think I did a silly. I think I did a silly. Yeah. I totally connected to the feather, not the Pico, because I looked at the screen on the feather and saw the IP address. However, when we click through to there, now I don't know why it's not working because right now CircuitPie is doing the wrong device everywhere. Okay. So, okay, we're gonna unplug the feather. We're gonna replug the feather. That's, okay, that should be back too. So I have both now. So we want to dismount the Pico. I totally, I just flopped devices, honestly. I totally brain farted that and just was not halfway paying attention to the right device, halfway paying attention to the wrong device. That's the Pico W. We did not get the pop-up. So close that open from here. Look at anything in here right now because it's no longer connected. The Pico at least. We do have the save button. Okay. Okay. I think I have worked out. Oh, you know what I'll do is to test that it's working. What I'll do is when you go to loading mode, loading mode, I'll set the status back to blank or maybe loading, for instance. I think blank, actually. I think I'm just gonna set it to blank. That way it will get rid of the old status, the old saved in this case or failed or whatever happened last time. So that is back to here which I'm gonna have to get used to now. So when you first go to selecting file mode, the way you get there is this. Date.status is empty string. Save that. Save mode, saving, saved. And then if we go back to loading mode, yeah, now that's gone again. Okay. So one thing I would kinda like to do is delete some of those test ones. I am gonna go back to here. Go to saved loops, open those. Gonna cheat by just hopefully doing select all copy. Go into here, doing like this. And we're just gonna find all these ones that are like test ones. And I'll just save it back like this. I don't have a super easy way to re-minify it but the system will re-minify it for us when it saves next time. We'll save all this stuff here. Save and run. It's not gonna run. Hopefully that's not gonna try to run. I don't know if it would have succeeded actually. Seems okay. So let's go to loading mode. And so now you can cycle through the loaded ones by pressing the up and down D-pad buttons. Looks like we have two copies of that one. Not sure how we ended up with that. Doesn't matter too much, but oops. I accidentally clicked middle button, which is mode, which is load. In this case, when you select the one you want, you press middle button again in order to load it. Let me change the text at that point. So let's go back here. Let's say when you load one, why don't we let the user know that that occurred? That is here. I mean, I guess the state would have changed but oh my, the tab. Okay, wow, what did I do? Okay, note to self, do not use tab. That's not the tab. I don't know what that is. Oh, that's a scroll bar, not a cursor. I see, okay, so tab just must have focused this or something, took me down out of the code thing. It does status loaded. I wish there was just save and not save and run. I wanted to just stay there. Go to loading mode, cycle through, click middle button to load. Didn't actually work. Oh, this is the wrong spot. It's actually supposed to be here. Oh, this got rid of my, that changed my intention. After you load, I want to go back to selecting note instead of selecting index. No longer, we ended up with one being held extra long there for a second. I don't know what that was about. Okay. What I would like to do now is on the rotary encoder, I want to play with the pitch bend a little bit. I want it to be, if you press and hold the rotary encoder and turn it at the same time. So press it in and turn it. I want that to send a pitch bend. So it'll keep playing the sequence, but it will bend the pitch by some value related to how far you have turned it. I got this idea from another MIDI project, which ran on a macro pad, which I should have kept a link around for so I could point folks towards it. But unfortunately, I don't know that I can find it very easily right now. What I need to see though is in the MIDI library, we need to get some kind of hopefully example code for pitch bending because I don't, this is memtest. It'll change. Oh, weird. Interesting. This is just collecting a bunch of stuff. After importing to test how big stuff is, I guess. Intest. Now demo. Is there a pitch bend in here? Pitch bend. Do we import it? But it doesn't seem to use it. No example. Maybe we could add an example with a pitch bend. Let's look in the docs though. Oh, here's a pitch bend. Okay. Honestly, it might be nice to have this in the example code. Maybe it was. Was this in simple? I didn't look in simple test. I assumed it wouldn't have. Okay, that was my fault, yeah. I assumed pitch bend would be above the simple test. I assumed simple test would just be like send a note and that's it. Okay, so pitch bend. This one's doing random. That's not what we want. I will get the docs as well because I don't know the range. Is that just positive negative max int or what is the actual number? You pass to that pitch bend. 14-bit unsigned integer representing the degree of bend from zero through 81.92 bid point. No bend. Oh, okay, so no, that's the midpoint. Okay, so 81.92 is middle. That's default. And then double that is the top end and then of course zero is the low end. So we wanna go up or down from here. Some amount relative to how far the rotary encoder is turned. Do you have to keep sending this or do you ever send an off for this? Like it. Do you have to send pitch bend? Get it back to normal? It probably should. Is this it? Maybe this is it. This is what I have on my macro pad. This is where I got the idea for this pitch bend from. See if we can look at their code for a rotate event. Does it, do you send it when you send the note? Maybe I was wrong in assuming this was using pitch bend. Thought that I had seen that it was. This looks like it's doing settings midi name value. If self.pressed is not none. Seconds? Yeah, I don't know. I thought that was using a pitch bend but it does not seem to be. So it's got all of these but even that's velocity. Maybe we were only changing the velocity. Yeah, I assumed it was a pitch bend but it was the velocity. All right, well I wanna give it a try. So it's gonna have to come from the encoder task. We don't really want this anymore. This is what was previously saving changing modes when you press down on the encoder. Really what I want is when you turn it and I can't tab all of these in. Just down or up, I don't even know. Curve position less than. So this would be down I guess. Let's try that. Nice. Let's put some of these to see if we're correctly ignoring it. Yeah, okay, so we're only doing the new ones. We're not moving the selection here, which is good. We only want this to be doing the pitch bend when you have it pressed in. So that's actually working fine. That was actually easier than I thought it was gonna be. How I though? Does this keep jumping out in front of me? Here we go, okay. So when we do this, I want to send a pitch bend. Should we just send it all willy-nilly here? Do we want to like send some signal to the other? Oh, I actually still have some weird special logic for note number 61. I need to get rid of that. I was doing some experimentation with channels. Maybe eventually we'll add channels I guess. I don't know for sure though. I'll do channels or not. Beyond like experimenting. I'll have to see. So where do we actually call play note from? I wish I could also have this open in PyCharm. And I guess I could open the other repo. Ah, this is the wrong note from. Question is do we want to, inside blink the LEDs, which is actually a bit awkward, but it's all right. So do we want to inside of here, do we want to set some signal and then inside of here is where we would send our pitch bend? Or do we want to just send the pitch bend wherever we happen to be? I'm going to try it the ladder first. I'm going to say on state, we're going to have a self.bend amount equals zero to start with. I don't know why I'm editing it in here. That's useless. Bend amount zero. When we do our turning and pressing, we will change the bend amount and then we will send the pitch bend. So that was turning up, but we could just say state.bend amount plus equals one, that one can be minus. And then we want to send it right here as well is how I want to try it first. We just create a pitch bend then send the pitch bend. So what I'm going to do is 8192 plus bend amount. Maybe we'll multiply this just to make it move more per turn of the encoder, because I have no idea how big of an effect it is, but I'll start with just doing it straight up like this. So 8192 plus bend amount. If that's positive, it's going to go higher. If it's negative, it's going to go lower. We want the same thing down here and then we want to send it. So midi.send, I guess. I have no idea if this will work, but, or even what it will do if it does work. Yeah, I kind of have to do it right-handed. Pitch bend is not defined. I didn't get it, not get very far. So we need to import pitch bend from midi. Oh, or we could say, I guess we could say Adafruit midi pitch bend then. I wish I could do, I actually worked that time. I don't know what was up last time. Oh, attribute. I see. Is it inefficient to go import this whole thing and then also import this one part of it? Are we like duplicating stuff in memory or is it smart enough to use the one that's already there? Does anybody know? I guess for now I'll err on the side of, let's try to be a little bit more efficient. So it would be, control S doesn't work. Okay, that just, not working. I'm gonna stop being fancy with it and just import it the same way it was here. I don't know what's the difference. I'm not sure why that didn't work, but it didn't work, so. Okay, that seems to be working now. Did not get any more crashes. Theoretically, we sent those things. The thing really happened because we don't have this actually listening or playing or whatever. So let's do some of this. Make sure this is on. Yep. Doesn't seem to be really doing anything. Yeah, doesn't seem to be doing anything really. So let's make it, well, let's do a few things here. Let's print some stuff to make sure. Well, I guess we already have a print, but just encoder, where's encoder? Are we doing anything else when we send MIDI? Really, we just make the note object channel. I don't think we need to worry about. So then my guess is gonna be that perhaps it's working, but it's not making a big enough change to actually be audible. So let's say instead of just bend amount, because obviously like 8,192 minus a couple or plus a couple is not very much, right? So let's do a multiplier. Maybe a hundred? That too much? There we go. So it's definitely changing. It does stay, I think, at the new value or whatever. It doesn't set itself back, seemingly. So we might need to say when we release it, we should probably set it back. Because I want this to be like you push it and do it and then it undoes it when you let go. I don't know why I'm back in here. Actually do the full, this has to be an F string. Curious to see how far it thinks we're changing it. I do wanna set it back. So I assume we would send a new one for 8192 when we're ready. We basically wanna go, actually the opposite of this, we basically wanna go if encoder, encoder button. Is it just rose? I don't actually know. If it rose, then send a pitch bend at 8192 exact. And also state dot bend amount back to zero. Create a pitch bend at 8192, set the bend amount to zero, then send that and that's when the button rose. And I think I'll add one more bit of logic that says if state dot bend amount not equal zero, then we do this stuff. And I am gonna actually also turn up the modifier a bit. I can't tell if it went back. Interesting, I can't tell if it's actually working or not, truthfully. All right, let's go like this. This should make it super clear. It's definitely working though, we can hear it when we play it that way. I think the modifier might still be a bit low, truthfully, I think it's still, especially because you have to hold it down and now that it resets back to normal when you let go, that means you can't like push, turn a full rotation or as far as you can and then release and then go back and then like ratchet, right? You can't release and then turn back and then go down again and turn further. Therefore, I think we want our modifier to be bigger. Plus, okay, nice, about quarter step. Quarter step, okay. So why don't we should probably let's go ahead and make a modifier. I almost wanna make a settings.json or something as well or sequencer settings.json. It's gonna be hard to bounce back and forth between multiple files. I'm gonna be wrapping up before too long here. I'll keep going in this one for now but that might be one thing that would be cool as well is to have a sequencer settings.json and then in that sequencer settings.json you could specify some different things. Maybe that's how we could bring in the tempo change to the mix as well. It might be cool if the thing could control its tempo, like if somehow you could hook the knob or the D-pad up to tempo but even if not that maybe just having tempo in a JSON file that you can set you know, it's better than nothing. I will move it to a variable for now though. So yeah, I won't worry about the whole JSON file for now but I'll move it to a variable to say bend modifier or multiplier, multiplier. And I'll put it on, let's try 400. Another double, another factor or two. And then after this I'm just gonna play with it and make some beep boops and then probably wrap it for the night. So if anybody has questions, comments or anything else now is the time to get those in. Then I'll kind of play us out here after that by just playing around for a couple of minutes with it making some old beep boops. Where to, what am I looking for? Sorry, my brain's a bit scrambled at this point in the evening. The multiplier, yeah. So where we multiply our pitch bend which is in our read encoder here instead of 200 here this is gonna be a multiplier. I think I actually already copied that but oops. And the other thing I guess we could be bend amount we could just use as bigger numbers but I kind of like this better anyway. So now we have a variable we can change that as needed. This should be more bend per encoder step essentially. I won't go every note but I'm gonna load up every other note just so we can, I can hear it. Honestly, my ears are not as good with recognizing the notes. This is a nice multiplier because it makes it to where pretty much the edge of your ability to turn reasonably. Oh, we crashed it. Oops. All right, let's add some logic so that we don't crash there. This is what I was getting at though is this is a nice multiplier because you can push and turn and you're basically, you know there's a kind of a natural amount of how far the human wrist can turn basically, right? So this gets it to where that kind of natural amount to where we turn it to is right around the exact same as this which gives us basically the full range. You know, it's not as finely tuned as we had it with a smaller multiplier but we can sweep across the whole range which is what I want. You know what would be sweet would be to have a pedal for this. I want an analog pedal anyway. That's neither here nor there for this but if anybody knows, I'm not into like the guitar pedals and the different musical devices that are like modifying widgets or whatever. If anybody knows though, if there's a like an analog pedal that could be hooked up to work with this if anybody has experienced with that kind of stuff and knows of that kind of thing. Give me a link if you know or a point in the right direction I'd be interested to find something where I could have actually a pedal on my feet in control of the pitch bend. That would be kind of cool. An analog pedal would be nice. Maybe there's a, is there a midi pedal? There's probably a midi pedal, isn't there? I don't want to go too far down this rabbit hole but I will entertain it for just a second. The stain pedal, little pianos. Yeah, I'll definitely have to do some more research but I wonder if this thing can, if it can send back an analog value zero to 1024 or whatever, something like that so that you could hook it up to be a pitch bend or something in midi. Anyway, slider or a wheel, a volume pedal. There we go, volume pedal. Okay, and if we get a midi one then we could probably repurpose it to be like even if it was made for volume we could repurpose it in the midi software, hopefully. Pitch wheel, oh nice, okay, pitch wheel. That's what we would need. I want a pitch wheel inside a foot pedal. Where's our editor there? Okay, yeah, logic. So let's go, let's just, we'll tuck in a, do what we can do, is it just, is it just max or is it math.max? Oh weird, this is not letting me go to REPL. Weird, oh well, let's just do this one. Just max, okay. I guess maybe the web thing consumes the REPL? I don't know. So this one is, the problem is though we actually need both max and min. We need to clamp it to a range. Yeah, we can't do that, can we? This is not probably the best way to write this, but that's what I got for right now. We're even worse on our intention now somehow. I'm not getting like a whole row of spaces gone for some reason. What was the max? 16, 383. So if it's greater than 16, 383. I don't know why the spaces are all of a sudden messed up. If it's greater than that then just set it to that. If it's less than zero then just set it to zero then use bend value here. Unfortunately we need this code repeated. Attention, okay, save. There we go. Can't go past the top now. I think that is where we are calling it tonight, just a MIDI pedal. I'll have to look into MIDI pedals. Thank you for the heads up. By the way folks, it turns a potentiometer. Oh, oh interesting. There's actually a pedal in the learn guide, I'll have to look into that one. It uses deep potential. Oh, I think I remember this actually. I do remember seeing this, the 3D print. Yes, the very clever 3D print shape allows it to turn the knob on the potentiometer. I do, I know that you, now that I see the photo, that jogs memory, I remember seeing that before. I'll have to look into that one and possibly look at MIDI pedals as well as the consumer grade ones and see what's out there. But that's gonna do it for the night. So thanks everybody for watching. This will probably be the last sequencer stream for a little bit. I think we're gonna be getting back into PR reviews and stuff. I kind of been on a bit of a break from all that sort of stuff for the last couple of weeks over the holiday. But I'll definitely be getting back into library PRs and core PRs and testing and stuff related to that probably for the next couple of streams at least. So including tomorrow morning, I think that's what I'll be working on. So tomorrow morning, 10 a.m. central time, I'll be back here streaming over on my own channel. We'll be getting into some library PR reviews. Thank you everybody for watching. I hope everyone has a good rest of your Friday evening and just wishing everyone a happy new year. And I hope everyone has a great 2023 as well. So thanks again to Johnny for hooking me up with the Simple Electronics podcast host, Dan I believe. I hope that I'm not getting the wrong name. I apologize if so, but I believe Dan was their name. If not, I'll figure that out and make sure I get it right before the next one. But thanks to Johnny for that. Thanks to CGrover for dropping the link for anybody who joined afterwards. If you're interested in a podcast talking to me. So if you are interested in learning a bit more about me and my history and my relation to coding and Python and things like that, you can check out the Simple Electronics podcast on YouTube and other podcast services. The one that was released this week, I wanna say I saw it was number 62 but I'm not positive on the number. But the latest one as of right now has got me on there. So head over and listen to that if you are interested. And yeah, that's gonna do it. So I'll see everybody next time. Thanks.