Quick
index
main
eev
eepitch
maths
angg
blogme
dednat6
littlelangs
PURO
(C2,C3,C4,
 λ,ES,
 GA,MD,
 Caepro,
 textos,
 Chapa 1)

emacs
lua
(la)tex
maxima
 qdraw
git
lean4
agda
forth
squeak
icon
tcl
tikz
fvwm
debian
irc
contact

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


1. Prerequisites

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,

(find-eev-quick-intro "2. Evaluating Lisp")
(find-eev-quick-intro "3. Elisp hyperlinks")

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".


2. Trying it

To try the code, run this,

(find-wget-elisp "http://anggtwu.net/elisp/2024-piano-roll-macros.el")

and then run `M-x eval-buffer', and run the tests in comments.


3. The video

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!