|
Piano roll macros (2024)
This page is very new!!!
At this moment the best introduction is here.
- 1. Prerequisites
- 2. Trying it
- 3. The video
This video is a bit about keyboard macros and a
lot about elisp macros. The video supposes that you
have read the two first sections of the main tutorial of eev,
and that you are a person who looks at this screenshot,
and thinks "This looks fun!" instead of "Sorry, my
workflow doesn't need that".
To try the code, run this,
and then run `M-x eval-buffer', and run the tests in comments.
The subtitles in Lua of the video are here.
The rest of this page contains a conversion of the subtitles in Lua
to a slightly more readable format.
Introduction
0:00 Hi! My name is Eduardo Ochs.
0:02 I'm the author of an Emacs package
0:04 called eev - its home page is here...
0:07 and this video is about this thing here, `eeks',
0:10 that plays a series of `k'eys "`s'paced in time",
0:18 like a pianola... like here.
0:21 If I execute this thing here with `M-e'
0:23 it waits a bit, then it simulates that the user is
0:27 typing RET, then RET again, then "Hello".
0:31 Let me demonstrate it.
0:37 That's it. Let me undo this thing.
0:41 This thing, `eeks', is not very interesting in itself,
0:45 but the implementation is very interesting,
0:48 and the video is going to be about it.
0:51 Now let me make a longer introduction.
0:55 Eev is a very obscure Emacs package
0:58 that treats Emacs in a very weird way...
1:00 I mean, it treats Emacs not as an editor,
1:04 but as something that started as a Lisp environment,
1:08 and then became an editor.
1:10 So, the best way to start learning Emacs is by
1:14 starting by learning the basics of Lisp.
1:18 The tutorial of Eev starts by this...
1:21 It starts by explaining how these Elisp expressions
1:25 work... and then it shows how to use Elisp
1:28 expressions as hyperlinks...
1:31 and then it explains that some things like this one
1:34 work like hyperlinks, but not really hyperlinks
1:39 that go to somewhere else... they work like
1:41 browser buttons that perform an action.
1:44 For example, if I execute this thing here, `eek',
1:47 it acts as if the user had pressed a series of keys -
1:52 like this...
1:55 I can run it several times...
1:58 and I can undo its output...
2:03 anyway, let me go back...
2:05 So, this, `eek', is one of the first things that
2:09 appears in the tutorial...
2:11 and this, `eek2', is a variant that does exactly
2:13 the same thing, but is implemented in a different way...
2:18 and this thing here, `eeks', is based on this new implementation.
1. How `eek' and `eek2' work
2:22 I said that these two expressions here do the same thing...
2:30 but they do it in different ways.
2:35 The first thing that this function here, `eek2', does,
2:39 is that it receives an argument,
2:42 and if that argument is a string it converts that argument
2:45 to a list of events.
2:48 The conversion is done by this function here, `ee-eek2',
2:51 and when `ee-eek2' receives this string here
2:54 it returns this series of events here -
2:57 note that each event is a pair...
2:59 The `t' is not important now, but the rest of the event is...
3:05 in this case, the rest of the event is always a number.
3:10 So, the RET becomes 13, the uppercase H becomes a 72, etc...
3:19 This function here, `eek2', can receive either a string
3:24 or a series of events in this internal format...
3:31 In most of my examples, I am going to use this representation
3:35 here, in which the events are numbers, instead of this
3:38 representation here, in which the events are written in a
3:42 way that is more readable for humans.
3:44 For example, this thing here is "sort of clearly" a RET,
3:50 this thing here is clearly an uppercase H, and so on....
3:53 Let's test... this thing here inserts a "Hello"...
3:56 and this thing here also inserts a "Hello".
4:02 And note that I can split this series of events into a series
4:08 of calls to `eek2'... so this `progn' here is going to do
4:14 the same thing as this big `eek2' here. Let me try... ta-da.
2. Spacing the events with timers
4:21 This is a copy of the expression in the previous slide...
4:28 and let's now change it to something that uses timers.
4:36 This first `run-at-time' here makes this small `eek2' here
4:42 be executed at some time in the future...
4:45 Then this second `run-at-time' does the same thing
4:49 for this small `eek2' here, and so on...
4:53 and, so... the first expression, the first (eek2 ...),
4:57 is run at this position in time, 0.25,
4:59 the second one is run at this position, 0.75,
5:01 the other one is run at this position, 1.25, and so on.
5:04 let's test...
5:06
5:11 And this last expression here, (eeks ...), does the same thing,
5:17 but it is written in a much more compact form. Let's test...
3. How the `run-at-time's are created
5:26 Now let's see how this expression, (eeks ...),
5:28 generates this (progn ...) here.
5:30 This (progn ...) here does exactly the same thing as this
5:33 (eeks ...) - it plays a series of keys spaced in time.
5:37 Let's test.
5:43 And, by the way, one of my favorite ways of explaining
5:48 eev to people is in this page here...
5:52 it is here, in the section "Taking apart",
5:58 and it is this sentence:
6:02 eev is more like a toy that is _slightly interesting_
6:06 if you play with it for a few seconds,
6:08 and _much more interesting_ if you open it and
6:10 take its pieces apart to see how everything works.
6:14 So... this is something that is not very useful yet,
6:19 but the implementation is very interesting,
6:22 so let's take everything apart.
6:24 I will need to shrink the font a bit.
The source code: `ee-let*-macro-eeks'
6:27 Here is the source code. I'm interested in this part here
6:33 of the source code, that explains a big macro...
6:37 Let me scroll it to center it on the screen...
6:46 These kinds of macros, my `let*-macro's, are
6:51 explained here - in this section of a tutorial...
6:53 but the explanation is very technical.
6:56 Note that it uses `cl-loop' at a few points...
7:00 Beginners with Lisp probably have never seen `cl-loop',
7:06 so if they want to learn it...
7:09 I added a section about `cl-loop' in one of my tutorials...
7:12 it is here - It has lots of examples...
7:17 Let me explain what this macro does.
7:23 It expects three arguments, here, that are usually a number,
7:26 another number, and a string, and then some "code".
7:37 What it does is that it It generates a big `let*', here...
7:43 and it runs the "code" in the context in which
7:48 not only these three variables are defined, t0, dt, and str,
7:54 but also these other things here are defined.
7:58 Let me show how this works. Let's try this thing here...
8:02 When I run this, `t0' is going to be 0.25,
8:11 `dt' is going to be 0.5,
8:13 and `str' is going to be "RET Hello",
8:16 and the "code" is just `tevents'.
8:19 And `tevents' is this thing here...
8:24 the "code" is going to be `tevents',
8:26 so the code is going to return the value
8:28 of the variable `tevents', that is defined here.
8:31 Let's try...
8:34 This line here shows that the value of `tevents'
8:38 is this sequence of pairs
8:41 made of a `t' and an event...
8:46 This thing here does something similar, but `ppushs'
8:50 is something that starts with a `progn'
8:53 and then it has a series of "pushes"... let's see...
8:57 Well, I think of these things as "pushes", but whatever...
9:05 And this thing here - with `prats' -
9:08 generates this thing here, that starts with a `progn'
9:11 and then it's a series of `run-at-time's.
9:15 I abbreviated run-at-time as R-A-T,
9:20 so "rats" is `run-at-time's.
9:22 This thing here, with (length tevents)...
9:25 is going to show the length of the variable `tevents'...
9:27 `tevents' is a list, and this shows the length of that list.
9:33 This thing here, with `find-eppp'...
9:36 is a trick for pretty-printing the value of `ppushs'...
9:43 if I run this, it creates a temporary buffer
9:45 with `ppushs' pretty-printed in a certain way.
9:51 `ppushs' is here, is a program with a series of pushes,
9:55 and this temporary buffer is read-write...
9:58 For example, I can test this thing here by typing M-e,
10:05 and it runs a RET, and then it types "Hello" immediately -
10:13 It doesn't wait.
10:15 And if I run this - with (find-eppp prats ...) -
10:17 it also creates a temporary buffer, but now with a
10:19 `progn' and a series of `run-at-time's.
10:23 And if I test this `progn', I get this.
10:27 The program simply creates a series of timers that,
10:34 well, Emacs stores those timers and run those timers at the
10:39 right time, so the `progn' returns immediately...
10:45 you can see the result here...
10:47 take a look here - at the echo area -
10:49 it is going to disappear very quickly...
10:52 and then Emacs executes the little `(eek2 ...)'s
10:57 that are stored in the timers.
11:01 So these (ee-let*-macro-eeks ...) give us ways
11:05 to explore all these temporary variables here,
11:07 in the `let*'...
11:11 And remember that the value of `prats'
11:16 is a list made of a `progn' and a series of `run-at-time's -
11:19 this one here...
11:21 And if instead of displaying `prats' with a pretty-printer
11:26 I eval `prats'...
11:29 I execute this `progn' here
11:32 instead of executing the program in the temporary buffer...
11:35 Let's try... one, two, three, go -
11:45 And this is a demo of something that I'm going to show later...
11:49 that is what happens when instead of giving a string to...
11:54 sorry, instead of sending a string to the variable `str',
11:59 we send a nil...
12:02 And, spoiler -
12:04 when str is nil,
12:09 then this (if (not str) ...) is executed here...
12:12 it calculates a certain distance,
12:14 and then it calculates a number of f8s,
12:18 and it generates a string made of a series of f8s.
12:24 Let's see...
12:26 `dist' is 12, `nf8s' is 11,
12:32 and `f8s' is this thing here.
12:37 Now let's go down a bit.
12:39 No, sorry, let's go up first.
12:41 This is the beginning of this file.
12:43 It first defines the "macro" here,
12:45 then it defines the function "eeks",
12:49 that plays a series of keys spaced in time,
12:52 and then it has some tests here - in "test-eepitch".
12:55 Let me scroll down...
12:58 Here is the definition of `eeks'...
13:02 It says that it's like `eek' and `eek2', but it
13:06 plays the events spaced in time, blablablah...
13:10 And the definition is just this thing here.
13:14 It calls the big macro above, `ee-let*-macro-eeks',
13:16 with a certain value for t0,
13:18 a certain value for dt,
13:21 and the str is _usually_ a string holding a series of keys...
13:29 then it simply runs (eval prats) here,
13:33 exactly like in this
13:36 demonstration here...
13:42 remember that the value of `prats' is here
13:45 and if we `(eval prats)' we get this...
13:55 So it has this `(eval prats)' here
13:57 and it has this `(list (length events) 'events)',
14:01 that gives a better return value.
14:06 Let's test it here...
14:11 Let me make everything start a bit later
14:16 so that we can see the return value here - in the echo area...
14:21 let's try...
14:23 It says "(6 events)" and then it runs the events.
14:30 So that's it.
The distance and the `f8's
14:32 I mentioned that when we don't give a string to `eeks'
14:37 it does something special. It calculates a certain distance,
14:41 distance, it calculates a certain number of f8s, and it
14:45 plays this series of f8s.
14:50 This is... anyway, no, sorry, I'm not going to explain
14:54 why this is useful to me - I'm just going to demonstrate it.
14:58 Let's create a test block here...
15:02 sorry, I need a smaller font...
15:05 sorry, let me use a very small font here...
15:09 Let me create a target buffer running a shell...
15:13 this is a comment because it starts with two red stars...
15:17 and it says "Type f8 on the line below and wait".
15:21 Let's do that - f8 here.
15:25 It generates a series of f8s spaced in time
15:27 and it "presses", between quotes,
15:32 these `f8's, and it executes this thing here.
15:36 So instead of...
15:39 Usually, I have to press all the `f8's,
15:42 but in certain occasions, I want to make the computer
15:47 "press these `f8's itself" like a pianola in a certain rhythm,
15:51 and this is what this `(eeks 0.5 0.5)' does.
15:54 Let's try it again...
15:58 Oops - If I execute this with f8...
16:02 Ah - sorry. Whatever.
4. Do try this at home
16:09 And that's it!
16:11 Please do try this at home!...
16:13 The instructions are in this page here.
16:15 This page is going to have all the instructions,
16:18 the subtitles of the video, etc...
16:21 And the easiest way to try the code
16:23 without downloading anything...
16:25 of course I'm supposing that you have eev...
16:27 but the easiest way is to just execute this exp,
16:31 that is in that page...
16:33 it downloads a local copy - no, sorry,
16:36 it downloads this page here, this elisp file here,
16:42 to a temporary buffer,
16:44 and then you can execute it with just `M-x eval-buffer'
16:47 and then you can try all these tests here.
16:50 And that's it! Thanks...
16:56 Actually, a) thanks for your attention
16:58 and b) thanks to Bruno Macedo, because many things
17:00 in this video were inspired by our conversations.
17:04 That's it. Bye!
|