Emacs workspaces

I use Emacs for everything that can be done with it reasonably. I often run in a single Emacs session Gnus, Org agendas, command line actions, IRC, several development projects, etc. The question is how to switch between all those activities efficiently.

During the years, I tried several workspace, perspective or screen (whatever they are called) Emacs add-on packages. But I have never been completely satisfied with any of them. There have always been bugs, limitations and/or discomfort.

Then I applied the principle of simplicity: Use the simplest thing that may work. And the thing is Emacs frames. As simple as that, I’ve been using my workspaces based on Emacs frames for almost two years now and it has been working to my satisfaction so far.

The frames provide two crucial facilities:

  • Separate dynamic window configurations and switching between them easily. Frames provide that by definition. The only problem is that your window manager must be able to manage a lot of Emacs frames reasonably. But any civilized window manager should be able to do that and e.g. KDE is.
  • Switching to workspace specific buffers. Frames do that by default — at least with Ido, recently used buffers in the given frame are offered first when switching buffers. Unlike in some other workspace solutions, the buffers offered are not limited just to the given workspace, which is a killer feature. It’s much easier not to care about assignments of buffers to workspaces and to simply rely on the recent buffers approach.

Of course, C-x 5 key bindings are not enough to work with workspaces efficiently. I implemented my own workspace management mechanism, based on my wrappers around the previous solutions I used and described below.

First, it’s useful to track the last frame because returning to the last workspace is a very common action:

(defvar my-last-frame nil)

Let’s track last workspaces too:

(defvar my-last-workspaces '())

Frame switching may not work smoothly in some window environments, so let’s make a custom function to select a frame and to store the workspace to the variables defined above:

(defun my-select-frame (frame)
  (unless (eq frame (selected-frame))
    (setq my-last-frame (selected-frame)))
  (raise-frame frame)
  (x-focus-frame frame)
  (select-frame frame)
  (setq my-last-workspaces (cons frame (remove frame my-last-workspaces))))

I give my workspaces names, stored and displayed in frame titles. Let’s define functions retrieving workspace names:

(defun my-workspace-name (&optional frame)
  (let ((frame (or frame (selected-frame))))
    (or (frame-parameter frame 'title)
        (frame-parameter frame 'name))))

(defun my-all-workspace-names ()
  (setq my-last-workspaces (cl-delete-if-not #'frame-live-p my-last-workspaces))
  (mapcar 'my-workspace-name
          (delete-dups (append my-last-workspaces (frame-list)))))

Now we can switch among workspaces:

(defun my-workspace-switch (name)
  (interactive "sWorkspace: ")
  (let ((frame-list (frame-list))
        (frame nil)
        (new nil))
    (while (and (not frame) frame-list)
      (if (string= (my-workspace-name (car frame-list)) name)
          (setq frame (car frame-list))
        (setq frame-list (cdr frame-list))))
    (unless frame
      (setq frame (make-frame `((title . ,name))))
      (setq new t))
    (my-select-frame frame)
    new))

(defun my-last-workspace ()
  (interactive)
  (if (and my-last-frame (frame-live-p my-last-frame))
      (my-select-frame my-last-frame)
    (my-workspace-switch (read-string "Switch to workspace: "))))

As you can see, it’s possible to switch to an already present workspace or to a new workspace, using the same command. But typing the workspace name each time is cumbersome, let’s make it easier:

(defun my-switch-to-workspace (&optional key)
  (interactive)
  (unless key
    (setq key (logand last-command-event 255)))
  (let* ((prefix (char-to-string key))
         (current-name (my-workspace-name))
         ;; This will be explained later:
         (predefined (cl-assoc prefix my-workspaces :test #'string-prefix-p)))
    (if predefined
        (cl-destructuring-bind (name new-fn refresh-fn) predefined
          (if (equal current-name name)
              (when refresh-fn
                (funcall refresh-fn))
            (when (and (my-workspace-switch name)
                       new-fn)
              (funcall new-fn))))
      (let* ((candidates
              (remove current-name
                      (cl-remove-if-not
                       #'(lambda (n) (string-prefix-p prefix n))
                       (my-all-workspace-names))))
             (n (length candidates)))
        (cond
         ((= n 0)
          (my-workspace-switch
           (read-from-minibuffer "Switch to workspace: ")))
         ((= n 1)
          (my-workspace-switch
           (car candidates)))
         (t
          (my-workspace-switch
           (completing-read "Switch to workspace: " candidates nil t prefix))))))))

(global-set-key (kbd "<s-return>") 'my-last-workspace)
(dotimes (i 26)
  (global-set-key (kbd (format "s-%c" (+ ?a i))) 'my-switch-to-workspace))

This implements a single key workspace switching and assigns it to key bindings. I use a–z letters, as seen above, together with Super modifier to switch between workspaces. I name my workspaces in such a way that the name of each of them preferably starts with a different letter, making this mechanism work very well. I also define C-c z binding to be able to switch among workspaces when Super key happens not working:

(defun my-switch-to-workspace-key (key)
  (interactive "c")
  (my-switch-to-workspace key))  
(global-set-key (kbd "C-c z") 'my-switch-to-workspace-key)

As there are Emacs applications I run often, in their own workspaces, it’s useful to have predefined workspaces:

(defvar my-workspaces
  ;; name
  ;; new-fn
  ;; refresh-fn
  '(("emacs"
     nil
     (lambda ()
       (require 'bookmark)
       (unless bookmark-alist
         (bookmark-maybe-load-default-file))
       (bookmark-jump "init.d"))) 
    ("gnus"
     gnus
     (lambda ()
       (let ((group-buffer (get-buffer "*Group*")))
         (if group-buffer
             (switch-to-buffer group-buffer)
           (gnus)
           (cd "~")))))
    ("irc"
     my-erc-connect
     nil)
    ("org"
     my-org-agenda
     my-org-agenda)
    ("roam"
     org-roam-find-file
     org-roam-find-file)
    ("system"
     my-run-eshell
     (lambda ()
       (call-interactively
        (lambda ()
          (interactive)
          (ido-buffer-internal ido-default-buffer-method nil nil nil "eshell:")))))))

Each entry in this list defines a workspace name, the function to run when the workspace is created and the function to run when switching to the workspace I’m already in (a “refresh” function), which is especially useful to reset the workspace to its default state. The corresponding part in my-switch-to-workspace above takes care of the predefined workspaces.

One last bit is handling workspaces containing development projects. It’s natural to name them after their source directories but that could quickly lead to initial letter collisions with other workspaces. I dedicated a special x: prefix to those workspaces. I use Projectile to manage my source directories and integrate it with my workspaces:

(defconst my-projectile-workspace-prefix "x:")

(defun my-projectile-switch-project-action ()
  (when (my-workspace-switch (concat my-projectile-workspace-prefix
                                     projectile-project-name))
    (projectile-vc)
    (delete-other-windows)))
(setq projectile-switch-project-action 'my-projectile-switch-project-action)  

When I open a development project using Projectile, it automatically switches to its workspace, names it accordingly and opens Magit for it. Then I can switch to the project using s-x key. If I work on multiple development projects simultaneously, my-switch-to-workspace offers the last one used by default to avoid using completion all the time.

As you can see, utilizing Emacs frames as workspaces is easy and powerful. I don’t need to use or make special add-ons to have workspaces, Emacs frames are good enough and they can be customized as needed relatively easily.

speechd-el 2.11 released

speechd-el 2.11 release introduces index marking feature, i.e. moving the cursor as the text of a buffer is read. This feature has been planned for more than 10 years but due to various circumstances, its implementation, although not that difficult, has been delayed. Now it is finally implemented although it may need further improvements in future if performance problems are experienced in practice. Sending long texts to Speech Dispatcher in chunks may be one of useful improvements.

speechd-el was designed as a project requiring low maintenance that needn’t be updated with each new Emacs version. Time has proved it is indeed so and speechd-el rarely requires updates of its code base. However, speechd-el has been here long enough to experience some conceptual changes in Emacs. During the time, Emacs has deprecated cl library in favor of cl-lib, improved the function advising mechanism and introduced a true lexical let. 2.11 version adjusts to those changes and, together with some other fixes, removed all compilation warnings.

So speechd-el is now in a clean state and the last long-planned feature has been implemented. I currently don’t plan any further changes to speechd-el unless there are bug reports, feature requests or patches from its users.

Playing with high-resolution audio

Some time ago, I’ve found out that under special circumstances I can hear differences between 320 kbps mp3 and CD audio. Now, I’m curious whether I can hear any difference between CD and high-resolution audio.

There is a lot of discussions whether high-resolution audio is a gimmick or a useful thing. Maybe the answer is simple: some people can hear the difference against CD audio, some can’t and some think they can while they can’t. Either of that is fine, one enjoys the music the way he likes it.

However it’s not that easy to find relevant arguments about high-resolution audio. One of the most pro-arguments is that it’s better because it has a higher sampling frequency and a higher bit depth. Yes, it has both, but an explanation whether it actually matters and why is usually missing. On the other hand, there are cons-arguments about „Nyquist theorem“ and „dynamic range“. But they also usually miss meaningful explanations. Yes, according to the Nyquist theorem, CD audio is good enough to capture frequencies up to 22 kHz, i.e. covering the frequency limits of human hearing. But music is not only about frequency but also about volume and tone curve shapes. Imagine what happens e.g. to a sine or another 10 kHz or 11.025 kHz sound curve when it’s sampled on 44.1 kHz. Yes, you get exactly the same tone frequency, but possibly with a lower volume and sounding like a different instrument. And regarding dynamic range, it’s about resolution in the quiet parts. While 16-bit whole-range resolution covers the whole audible specter, less than 10-value range remains to represent quiet but still audible sounds, which looks a bit coarse.

So purely numerically, high resolution audio adds information within nominal human listening range. Whether we can recognize the added information is a different question as well as whether we can percieve inaudible frequencies some way. I guess my tentative answer above holds, but let’s return to the original problem, whether I can hear the difference. The best way to find out is to try it, which is not completely trivial.

The first step is finding true high-resolution audio. Some popular hi-res sites sell overpriced recordings, which I’m not going to buy. Another option is utilizing streaming services offering hi-res audio but I’m not their subscriber. Fortunately, there is a selection of Czech music of various genres in hi-res, not much more expensive than their CD quality versions. I bought five albums of different kinds there that I’d probably buy anyway sooner or later (three contemporary recordings, two older recordings from analog tapes; one classical orchestral work, two single-instrument classical works and two popular music albums with a lot of vocals; one 192 kHz, two 96 kHz, one 88.2 kHz, one 48 kHz and all 24-bit). For a total additional price of about one pizza I could obtain their hi-res versions. This is worth the fun, isn’t it?

The second step is how to play hi-res audio. Besides the obvious need of having an audio system capable of playing hi-res audio, there are also software issues. The PulseAudio thing clearly tries to solve different problems than playing hi-res audio (well, I wonder again which ones — my recent experiences include heavily distorted sound on normal play and inability to switch to a different sound input for a running application) and it’s best to avoid it. Fortunately, there is a way to prevent PulseAudio from touching a USB DAC. Then it’s possible to use the DAC safely with a player that can send sound data to the DAC directly, without any sound processing and conversions. I used VLC with ALSA output. Another option is to use Volumio on a dedicated device (such as Raspberry Pi), which does the right thing out of the box.

The third step is to get something to compare the hi-res version to. The mp3’s bundled with the hi-res recordings are lossy and I can’t be sure they weren’t processed differently. I used sox utility to convert the audio to a CD like quality, i.e. 16 bit depth and 48 or 44.1 kHz frequency (depending on the original hi-res frequency):

sox FILE.flac -r 48000 -b 16 FILE-cd.flac

The fourth step is finding a calm moment when it’s possible to listen to the music carefully, without disruptions and background noise. This is the most tricky thing!

Finally, the fifth step is actually listening to the music.

And the result? I couldn’t hear any difference.

Back to Vanilla Music

I have found out that I was mistaken, Odyssey Android music player cannot always play the tracks in the right order. Additionally, it has sometimes problems (as many Android music players have) with non-ASCII characters in file tags. So I’m returning back to using Vanilla Music. I abandoned it when it was incapable to play newly added albums whatever I tried to do. This seems to be fixed now. And there are also no problems with non-ASCII characters so far.

Another good news about my phone is that the occasional very short (and very annoying) interruptions of music play (regardless of the music player used) are, hopefully, also no longer present.

It’s sooo nice when one can play music on a mobile phone.

Switching from Firefox to Brave

The new Firefox ESR release has hit my desktop this week. Debian stable is stable; except for Firefox. Firefox ESR releases last only for about one year. Debian release cycle is typically about two years so in order to keep up with security fixes, Debian stable has to switch to a new major Firefox version once a year. That means replacing old bugs with new ones in Firefox more often than in the rest of the system.

This time, color management has stopped working for me after the update. The only way I’ve found to avoid green faces and/or completely false colors at some places was to disable color management completely. (Yes, using those cryptic undocumented numeric values in about:config.) I don’t know what’s wrong, the rest of my system hasn’t changed and all the other color management capable applications work normally. There are reports about color management bugs in Firefox 77 and later (the ESR version is 78). It’s not a critical problem, I can use a different browser when I need to see accurate colors, but it’s still annoying. But what is worse, such a kind of regression indicates that there is something wrong with Firefox development.

Firefox is dying and IMHO one of the reasons is that it stops offering attractive features to its users. Which of the new Firefox features introduced in the last years do you use? I can’t recall any. Which of the old Firefox features removed in the last years do you miss? I can recall several.

Firefox has still some significant advantages over most other web browsers: It can still browse the web, it’s free software and it doesn’t track its users (this may be a bit arguable but there is a clear difference against the most popular web browser). But I’m afraid Firefox won’t survive and we will be able to use only proprietary web browsers in the future, returning to what we had a quarter century ago. There is at least hope we won’t remain just with The Only Mighty Web Browser on GNU/Linux.

Enough is enough and I think it’s time to start looking around. Firefox has already set my web browser UI expectations so low during the time that there is little barrier to start preparing for the future by switching to a different browser. So I’m switching to Brave. Well, who knows how long that web browser survives. But it’s free software, based on a popular code base, led by Brendan Eich, and with an interesting approach to privacy, advertisements and content creators. And yes, color management works in Brave.

The Brave concept is definitely worth to try. Replacing spying advertisements with micropayments should have already been here long time ago and I’m happy we have finally something that starts pushing the very needed change seriously. It’s currently only a half-step IMO because of the use of cryptocurrencies (the concept of money based on wasting electricity is flawed) and requiring me either to accept privacy-respecting advertisements (no, thank you, I’m not interested in advertisements) or to upload my identity card to an internet service (I don’t know why Binance needs it for small exchanges; there is no such need when using real money). So I’m taking also only a half-step now and just switching to Brave and not joining its Rewards program. Hopefully the service will evolve and will allow me to use real money sooner or later to reward content creators.

Natalika

Who remembers the old times when most music albums were not available on streaming services and couldn’t be bought online as downloadable archives? In the pre-online music era, there was a lot of amazing recordings by non-mainstream artists that are no longer available nowadays.

I was lucky a couple of times and discovered great old CDs in some “forgotten” e-shops or in second-hand stores. A few of those old recordings were released again. But most of them are unfortunately inaccessible, maybe forever.

One of the recordings that I’ve missed were albums by Natalika. That amazing world music band was quite popular for a few years around 2000 (in the sense that it even appeared on TV several times). And then the band disappeared for long time. IIRC their CDs were never available in normal distribution so I’ve never seen them in CD shops. And I think their online presence was irregular during the time. From time to time, I recalled the band but couldn’t find their CDs anywhere at the moment.

I’ve recently recalled the band again and found out that they have a working web site and their 20 years old CDs are still available. What a discovery! I ordered the CDs and now I can enjoy listening to those great pieces of music, which were previously missing in my music archive. AFAIK those CDs are the last unsold pieces, which won’t be available anymore in any form once they are sold out. If you like that kind of music, grab them while you can, they are worth it.

User-Centric Payment System for streaming

Deezer’s push for User-Centric Payment System (UCPS) looks like the right step. It has always annoyed me that, because I don’t stream much, my money spent on streaming goes mostly to the popular music that I don’t listen to. I wonder whether the planned Deezer move to UCPS will really happen.

In any case, one of good ways to support the artists one listens to is buying their albums. If I really like some album, I buy it.

A usable music player for Android

It’s surprisingly difficult to find a music player that works. I have the following minimum requirements on a usable music player for Android:

  • It is free software.
  • It can play music.
  • It can play album tracks in the correct order.
  • It can search by artist and by album.
  • It can recognize music library updates.

There are many free music players for Android. But finding the album I want to play and to play it in the correct order is a problem for most of them.

I could find (exactly) one music player on F-Droid that works: Odyssey. So far it looks flawless, it’s well usable and can play music well. And it’s licensed under GPLv3, which is another plus.

Free navigation software

There is quite a lot of free navigation software based on OpenStreetMap, but it’s somewhat difficult to find something that actually works for the given purpose. I feel like I need to document for myself what to use. And why not share the documentation? Hence this blog post.

It’s easy on Android – there’s OsmAnd there. OsmAnd is somewhat difficult to use but otherwise it’s nice, working well and providing a lot of useful features.

It’s less easy on desktop. After some searching I got amazed by BRouter, which is a navigation backend capable of offline OpenStreetMap based navigation. That’s a killer feature itself, but BRouter also provides another outstanding feature. You can create complex BRouter profiles describing your navigational preferences, which is especially useful for bike navigation. BRouter can’t be used without reading its documentation first and it takes some effort to make it running. But it’s not too difficult and once BRouter is up, it’s amazing.

BRouter can be used on both desktop and Android, but it’s just a backend server. You need some frontend to actually use it. You can use OsmAnd on Android. There is a very plain BRouter application to set up the server and then BRouter can be chosen as the navigation backend in OsmAnd preferences. As for desktop, you can use Web client for BRouter. It can be used locally or as an online Web application, with your selected set of profiles. There is also an online demo for testing BRouter with its included profiles (there are more profiles available from third parties, see documentation).

For simpler planning, especially for making simple sequences of track points used e.g. by some Garmin devices, FoxtrotGPS is well usable. Unfortunately FoxtrotGPS seems incapable of displaying complex GPX tracks. But then there is GPX Viewer, which is nice for viewing GPX tracks.