Shinobu’s Secrets

October 26, 2009

Auto-update/make/compile script for Lilypond: Autolily

Filed under: Lilypond, Linux, Ruby — Shinobu @ 1:59 am

I’v so far used Lilypond twice to make nice, clean music of some classical pieces. If you don’t know what Lilypond is already, it is basically LaTeX, but for music. Since the music notation style is almost always the same across many different genres, LaTeX’s “what you see is what you mean” philosophy applies particularly well to your sheetmusic-making needs.

The basic way you work is — create an .ly file pursuant to the lilypond format, then save it, then call “lilypond [filename]” on it from the terminal, which will generate a .pdf, .ps, and .midi output all for you (that is, if you have a “\layout {}” and also a “\midi {}” in you “\score {}”). If you have dual monitors, then you can open up the .pdf file with evince, and then evince will automatically update to the latest .pdf version by itself (no need to close and re-open the same generated .pdf file).

This workflow is nice, but it could be better. For one, it takes time to switch windows and invoke “lilypond [filename]” every time you want to see a small change done. For me, I like to make sure at least every 1/2 measure or so that what I put in is correct. Thus, switching back and forth between all these windows every 30 seconds makes the whole process very time consuming.

So I wrote a small ruby script (I call it “Autolily”) to automatically invoke lilypond for me:


 1 #!/usr/bin/ruby
 2 #===============================================================================================================#
 3 # Program name: Autolily                                                                                        #
 4 # LICENSE: PUBLIC DOMAIN                                                                                        #
 5 # This program takes 1 argument, the name of a lilypond file (*.ly), and watches it for changes every 1 second. #
 6 # If there has been any change, it simply calls lilypond on it to create a new .pdf/.ps/.midi of it.            #
 7 #                                                                                                               #
 8 # Place this script somewhere, like in ~/scripts                                                                #
 9 # Then, open up a terminal and call it like so: ~/scripts/autolily.rb [lilypond file]                           #
10 # Be sure to call it from the directory where the lilypond file is located -- i.e., don't do [path/to/file]     #
11 #                                                                                                               #
12 # To exit, type CTRL-C.                                                                                         #
13 #===============================================================================================================#
14 
15 if ARGV.size > 0
16     file = ARGV.shift
17     file_data_orig = ""
18     `ls --full-time`.split("\n").each do |line|
19         if line.split(/\s/).last == file
20             file_data_orig = line
21             break
22         end
23     end
24     file_data_new = ""
25 
26     # enter infinite loop -- keep compiling the given lilypond file if it has changed in the past 1 second
27     while true
28         # detect the file size and also timestamp
29         lsarr = `ls --full-time`.split("\n")
30         lsarr.shift # get rid of the first line, since that is the size of all the files in the directory
31 
32         # find our file from ls's output!
33         lsarr.each do |line|
34             if line.split(/\s/).last == file
35                 file_data_new = line
36                 break
37             end
38         end
39 
40         # if there is any change detected, run lilypond on it
41         if file_data_orig != file_data_new
42             puts "\n\e[1;4;38;5;226mAutolily: Change detected in given file; invoking lilypond...\e[0m\n"
43             `lilypond "#{file}"`
44             file_data_orig = file_data_new
45         end
46         sleep 1
47     end
48 else
49     puts "No .ly file specified.\n"
50 end

This very simple script is an infinite loop which takes snapshots of the output of the "ls" command every second, and monitors any changes. Only the output line for the given lilypond file is monitored. Whenever this single line changes (any time you save your file, thus changing the modification timestamp of the file), the "lilypond [filename]" command is invoked to generate the .pdf, .ps, .midi outputs.

Now, every single time you save your .ly file, Autolily will run lilypond for you in the background, letting you know of any errors or such (and if you have evince open on the generated .pdf file, evince will update what your sheet music looks like as well).

Memory usage of this script is a bit embarrassing -- I get around 4.5 MB -- but hey, that's Ruby's fault.

Enjoy!

UPDATE November 12, 2009: Added quotes around _file_ so that a filename with spaces works correctly. (Though, if you're a Linux guy like me, you probably don't name your files with spaces in them.)

October 4, 2009

Linux Dual Monitor Setup: Nvidia & Xinerama Guide: Rotating just one monitor

Filed under: FYI, Linux — Shinobu @ 10:55 pm

I have 2 LCD monitors (both are widescreen) hooked up to an Nvidia card in Linux. For a while, I rotated both of them into a “portrait” view, because, aside from the other benefits a portrait view can give (to webpages and documents), putting two widescreens on a landscape configuration takes up too much horizontal space.

But yesterday, I decided to take a hybrid approach and put my left monitor in landscape view, but my right one into portrait view — i.e., I only wanted one monitor rotated 90 degrees. Of course, this is fairly straightforward in Windows with the proprietary Nvidia utility, but it takes a little bit of work with Linux. I googled around a bit, and found this page. I couldn’t really understand what was going on by just looking at the sample xorg.conf file on that page, so I decided to write out my usual in-depth explanation on the matter.

First, to get this hybrid approach, you have to use Xinerama, and NOT Nvidia’s popular TwinView utility/configuration. This is because TwinView (in Linux) only allows you to rotate BOTH monitors together in the same direction.

The only thing you have to do is edit your /etc/X11/xorg.conf file. First, if you already have TwinView set up to work in a dual monitor configuration, you have to run sudo nvidia-settings, and then change from “TwinView” to “Separate X screen” under the “X Server Display Configuration” settings, like so:

2009-10-04-133053_914x657_scrot

The reason we do this (if you already have nvidia-settings installed) is because it auto-generates a working xorg.conf for us. The thing is, under TwinView, you just have 1 option for everything in Xorg — i.e., you just have Device0, Screen0, Monitor0, but now with the “Separate X screen”, you have Device1, Screen1, etc. Not only that, but your old, irrelevant TwinView settings will be automatically deleted or commented out, so it will save you a couple minutes of work (and any typos as well — typos will prevent X from booting!). After that, it’s just a matter of tweaking the xorg.conf file to suit your needs. (BTW, the reason why the right monitor looks like it’s not aligned vertically correctly with the one on the left is because of a manual adjustment — it’s the 333 pixel shift adjustment I made; read on below for more on that).

Here is my current, working xorg.conf with just 1 monitor rotated:

  1 Section "ServerLayout"
  2     Identifier     "Layout0"
  3     #Screen      0  "Screen0" RightOf "Screen1" # put it on the RIGHT (other options are Below, Above, LeftOf, RightOf;
  4                                                 # alternatively, you can put an absolute, pixel X and Y offset (so RightOf
  5                                                 # would be 1680 0
  6     Screen      0  "Screen0" 1680 0 # NOTE: Screen 0 MUST be defined BEFORE Screen 1!!!
  7     Screen      1  "Screen1" 0 333 # put this screen on the top left, but adjust it down a little bit to match the portrait screen on the right
  8     InputDevice    "Keyboard0" "CoreKeyboard"
  9     InputDevice    "Mouse0" "CorePointer"
 10 EndSection
 11 
 12 Section "Files"
 13 EndSection
 14 
 15 Section "Module"
 16     Load           "dbe"
 17     Load           "extmod"
 18     Load           "type1"
 19     Load           "freetype"
 20     Load           "glx"
 21 EndSection
 22 
 23 Section "ServerFlags"
 24     Option         "AutoAddDevices" "False"
 25     Option         "Xinerama" "1"
 26 EndSection
 27 
 28 Section "InputDevice"
 29 
 30     # generated from default
 31     Identifier     "Mouse0"
 32     Driver         "mouse"
 33     Option         "Protocol" "auto"
 34     Option         "Device" "/dev/psaux"
 35     Option         "Emulate3Buttons" "no"
 36     #Option         "ZAxisMapping" "4 5" # for the Logitech MX 400
 37 EndSection
 38 
 39 Section "InputDevice"
 40     Identifier     "Keyboard0"
 41     Driver         "kbd"
 42     Option         "XkbModel" "pc105"
 43     Option         "XkbLayout" "us,fr,de"
 44     Option         "XkbOptions" "grp:shifts_toggle"
 45 EndSection
 46 
 47 Section "Monitor"
 48     Identifier     "Monitor0"
 49     VendorName     "Unknown"
 50     ModelName      "Acer X222W"
 51     HorizSync       31.0 - 81.0
 52     VertRefresh     56.0 - 75.0
 53     Option         "DPMS"
 54     Option         "Rotate" "Right" # for portrait mode
 55 EndSection
 56 
 57 Section "Monitor"
 58     Identifier     "Monitor1"
 59     VendorName     "Unknown"
 60     ModelName      "Acer X222W"
 61     HorizSync       31.0 - 81.0
 62     VertRefresh     56.0 - 75.0
 63     Option         "DPMS"
 64 EndSection
 65 
 66 Section "Device"
 67     Identifier     "Device0"
 68     Driver         "nvidia"
 69     VendorName     "NVIDIA Corporation"
 70     BoardName      "GeForce 8800 GTS"
 71     Option         "RandRRotation" "on"
 72     BusID          "PCI:1:0:0"
 73     Screen          0 # i.e., the monitor on the RIGHT (physically)
 74 EndSection
 75 
 76 Section "Device"
 77     Identifier     "Device1"
 78     Driver         "nvidia"
 79     VendorName     "NVIDIA Corporation"
 80     BoardName      "GeForce 8800 GTS"
 81     BusID          "PCI:1:0:0"
 82     Screen          1 # the monitor on the LEFT (physically)
 83 EndSection
 84 
 85 Section "Screen"
 86 
 87     Identifier     "Screen0"
 88     Device         "Device0"
 89     Monitor        "Monitor0"
 90     DefaultDepth    24
 91     Option         "metamodes" "DFP-0: 1680x1050 +0+0"
 92     SubSection     "Display"
 93         Depth       24
 94     EndSubSection
 95 EndSection
 96 
 97 Section "Screen"
 98     Identifier     "Screen1"
 99     Device         "Device1"
100     Monitor        "Monitor1"
101     DefaultDepth    24
102     Option         "metamodes" "DFP-1: 1680x1050 +0+0"
103     SubSection     "Display"
104         Depth       24
105     EndSubSection
106 EndSection
107

Probably the most important options are the “Screen” options under the “ServerLayout” section — these define which monitor starts where, and the relationship between the two monitors. The “Screen” options are defined by a X-axis (horizontal) and Y-axis (vertical) offset — you can only use positive numbers, I think, and so this means that if a screen is defined as “0 0″, it is on the top left corner of the imaginary monitor “plane,” so to speak. I.e., increasing the X-axis offset moves your screen to the right, whereas increasing the Y-axis offset moves your screen down. For me, my left monitor is slightly adjusted down by 333 pixels, so that it matches up smoothly with my rotated monitor on my right (my setup, physically, looks like a sideways “T”). I’m not sure if the “RandRRotation” option is necessary — it probably is not — I’m too lazy to check after fiddling with my xorg.conf all day yesterday. The other options should be self-explanatory, along with the comments.

Here are some of the not-so-obvious details: for me, my “DFP-1″ is my LEFT monitor and “DFP-0″ is my RIGHT monitor. Apparently, the “primary display” is by default DFP-o (e.g., in Windows, the Nvidia utility detects DFP-o (my RIGHT monitor) as screen “1 of 2″, so I have to manually set DFP-1 (2 of 2) as my primary screen) — the only reason why I have it like this is because for some reason, my BIOS stuff shows up on DFP-1, and not the default “primary” DFP-0, and I’ve always liked my BIOS to show up on my LEFT screen by default — hence the reason why DFP-1 is on my left and DFP-0 is on my right. I hope that made sense. It probably didn’t, so here is a screenshot:

2009-10-04-141105_2730x1680_scrot

Each monitor has a resolution of 1680×1050, but now, my virtual desktop has a 2730×1680 resolution (1680 + 1050 = 2730). DFP-0 is physically rotated so that the bottom side sticks out to the far right — i.e., it’s rotated counter-clockwise 90 degrees — so that the clean, button-less bezel of its top area matches up nicely with the unrotated DFP-1.  And so, we rotate “Right” under the “Monitor” section for Screen 0. This is probably the most sensible way for most people, since almost all monitors that do have buttons at all (to make an uneven, fatter bezel where the buttons are) have them at the bottom of the monitor.

Here are the advantages that you get with Xinerama in the above setup:

  • Moving the mouse from the right to the left is always continuous and seamless. This is even true when moving from the far edges of the portrait monitor into the widescreen one (see notes in screenshot). Finally, the virtual screen behaves EXACTLY like how the monitors look in the real world. (This behavior is available in Windows; however, there are two shortcomings that are not found in Xinerama: (1) when the mouse is on the top or  bottom edge of the portrait monitor, your mouse becomes “stuck” and does not transition smoothly over to the widescreen monitor on the other side (see screenshot above); (2) even though you can adjust the monitors by simply dragging the icons around (which is very user-friendly, I must say), the adjustments are very course and you cannot adjust things down to the last pixel.)
  • Maximizing a window maximizes to the monitor’s area (i.e., the window maximizes up to either the landscape or portrait view), and not the big, virtual landscape of 2730×1680 pixels; even the applications behave intelligently!

A (temporary) disadvantage:

  • Since I’m using Xmonad as my window manager, popup dialog boxes automatically spawn to to the top-left of the virtual screen — i.e., if they’re small enough, they fit entirely into the unviewable area on the top edge. I have to manually flatten the image into the tiled area to see the contents of it. However, this is Xmonad’s fault, and there is probably a hack out there to fix this sort of thing. Even so, since I don’t really use GUI applications that have a lot of popup dialog boxes in the first place, this is a non-issue.

For a long time I thought that Xinerama was an ATI thing, but apparently, it works for Nvidia as well. The xorg.conf layout looks much nicer, and simpler with Xinerama, and is much more flexible with it (e.g., the 333 pixel shift). If you change the few lines in my xorg.conf above that deal with rotations, you could even do without TwinView as well and just use Xinerama for a basic dual-head setup without any rotated screens.

Blog at WordPress.com.