Hello Everybody!
I wanted to ask the community, if there has someone a practice in writing scores in / with supercollider. With score i mean common music notation scores in form of lilypond files oder musicXML.
Hello Everybody!
I wanted to ask the community, if there has someone a practice in writing scores in / with supercollider. With score i mean common music notation scores in form of lilypond files oder musicXML.
Great question, the approach really depends on the artistic context youāre working in.
Thereās actually a small but lively corner of composers using sclang for contemporary instrumental work, often seasoning their pieces with a bit of generative magic. This topic has floated around the community before, for instance here:
If youāre not married to real-time notation on the fly, the pragmatic route is just routing your event streams into a DAW such as Reaper, export the clips as MIDI files, and hand it off to a notation tool like Sibelius to clean things up into something performers can read without raising an eyebrow. If, however, you do want to watch your midi events to crystallize into staff notation on the fly, then a solid option is pairing Max/MSP with the bach library, which is built for score-based output.
If youāre curious, hereās one of my more recent generative instrumental pieces driven largely by sclangās pattern library doing the heavy lifting behind the scenes.
It is a bit unfortunate that the second edition of The SuperCollider Book, published fairly recently, discusses the use of FOSC, even though FOSC now seems no longer to work (see also: Possible missing dependency: `gs` required for `FoscNote(...).show` Ā· Issue #8 Ā· n-armstrong/fosc Ā· GitHub). That feels particularly unfortunate for new readers trying to follow the examples.
At the moment, it seems that neither FOSC nor FOMUS is really usable. From what I can tell, SCStaves and Notator may be the two remaining possibilities, and if MusicXML is needed, Notator may be the only practical option.
If anyone has managed to get FOSC or FOMUS working, or has suggestions for a current notation workflow in SuperCollider, I would be very interested to hear them.
More generally, I often find that discussions of notation in SuperCollider end up pointing towards LilyPond syntax. That is understandable, but I am increasingly interested in the possibility of a more SuperColliderānative approach: using things like SequenceableCollection, IdentityDictionary, Set, or Patterns as the musical representation, and generating MusicXML directly from that.
Personally, I would prefer a SuperColliderānative approach that does not rely on LilyPond, Guido, or ABC syntax. If I were to revisit my own Notator, that is probably the direction I would take and, if successful, I would consider releasing it properly as a Quark.
The difficulty is that there is no single obvious model for such an approach, so it would really need discussion with other users. Still, I would be very interested to know whether others think this would be a worthwhile direction.
It is a lot of work to do this. And itās more work in SuperCollider, as it doesnāt have libraries to do a lot of the grunt stuff for you.
If you can do score generation in something else then Iād recommend it (i.e. Python, Common Lisp and Haskell).
Actually, i tried FOSC now and its working with my lilypond (-> 2.20.0), i donāt get an error and the example is created as pdf.
Maybe try brew install ghostscript on MacOS.
Thank you for your report!
brew install ghostscript on macOS does not resolve the problem in my case.
I retested using lilypond-2.25.81 on macOS 15.7.5 with an M1 Max (ARM, not Intel).
The score is rendered and displayed, but FOSC still prints the following error:
ERROR: Changing working directory to: `/Users/prko/Library/Application Support/SuperCollider/fosc-output'ERROR:
Processing `/Users/prko/Library/Application Support/SuperCollider/fosc-output/0016.ly'
Parsing...
Interpreting music...
Preprocessing graphical objects...
Finding the ideal number of pages...
Fitting music on 1 page...
Drawing systems...
Converting to `0016.pdf'...
ERROR: Success: compilation successfully completed
-> a FoscNote
So LilyPond reports success, but FOSC still emits an error message.
PDF is not generated and FOSC does not complete the process.
On Windows 11, I could not get it working at all (tested with lilypond-2.25.81 and lilypond-2.24.4).
I think the documentation should clarify:
Real errors generally stop the process, but here, no failure is reported, and the process continues to its end, with an output file.
So the conclusion is not that the library is unusable, but rather that thereās some incorrect messaging in it (status messages being tagged as errors, e.g. āERROR: Success:ā
) ā which can be found and fixed.
hjh
OK. FOSC is usable when certain conditions are met. Based on my tests (In summary, based on my tests, FOSC functions reliably on Intelābased macOS and Linux systems. On ARM64 hardware, it works only under macOS. On Windows, it fails to operate on both Intel and ARM64 architectures.):
With SC3.13.0 and SC3.14.1 on macOS 15.7.5 (ARM64), I could not get FOSC to work with either LilyPond version due to the following error:
-> Fosc
ERROR: Message 'alterations' not understood.
RECEIVER:
nil
ARGS:
KEYWORD ARGUMENTS:
CALL STACK:
DoesNotUnderstandError:reportError
arg this = <instance of DoesNotUnderstandError>
Nil:handleError
arg this = nil
arg error = <instance of DoesNotUnderstandError>
Thread:handleError
arg this = <instance of Thread>
arg error = <instance of DoesNotUnderstandError>
Object:throw
arg this = <instance of DoesNotUnderstandError>
Object:doesNotUnderstand
arg this = nil
arg selector = 'alterations'
arg args = [*0]
arg kwargs = [*0]
Meta_FoscPitchManager:pitchClassNumberToPitchClassName
arg this = <instance of Meta_FoscPitchManager>
arg number = 60
arg accidental = nil
var integer = 0
var frac = 0
var alterations = nil
var index = nil
var accidentalName = nil
var diatonicPitchClassName = nil
var result = nil
Meta_FoscPitchManager:midinoteToPitchName
arg this = <instance of Meta_FoscPitchManager>
arg midinote = 60
arg accidental = nil
var pitchClassName = nil
var octaveNumber = nil
var octaveName = nil
var result = nil
FoscPitch:init
arg this = <instance of FoscPitch>
arg argName = 60
FoscNoteHead:writtenPitch_
arg this = <instance of FoscNoteHead>
arg pitch = 60
FoscNoteHead:init
arg this = <instance of FoscNoteHead>
arg writtenPitch = 60
arg argClient = <instance of FoscNote>
arg argIsCautionary = false
arg argIsForced = false
arg argIsParenthesized = false
arg argTweaks = nil
var noteHead = nil
var key = nil
var val = nil
FoscNote:initFoscNote
arg this = <instance of FoscNote>
arg writtenPitch = 60
arg isCautionary = false
arg isForced = false
arg isParenthesized = false
< closed FunctionDef > (no arguments or variables)
Interpreter:interpretPrintCmdLine
arg this = <instance of Interpreter>
var res = nil
var func = <instance of Function>
var code = "(
a = FoscNote(60, 1/4);
a.s..."
var doc = nil
var ideClass = <instance of Meta_ScIDE>
Process:interpretPrintCmdLine
arg this = <instance of Main>
^^ ERROR: Message 'alterations' not understood.
RECEIVER: nil
On Linux (ARM64) and Windows (Intel, ARM64), I could not find a way to resolve the problems yet.
Fosc.lilypondPath = "/home/parallels/Downloads/lilypond-2.25.81-linux-x86_64/lilypond-2.25.81/bin/lilypond"
returns:
-> Fosc
However,
Fosc.lilypondVersion;
returns:
ERROR: /bin/sh: 1: /home/parallels/Downloads/lilypond-2.25.81-linux-x86_64/lilypond-2.25.81/bin/lilypond: Exec format error
ERROR: Message 'at' not understood.
RECEIVER:
nil
ARGS:
Integer 0
KEYWORD ARGUMENTS:
CALL STACK:
DoesNotUnderstandError:reportError
arg this = <instance of DoesNotUnderstandError>
Nil:handleError
arg this = nil
arg error = <instance of DoesNotUnderstandError>
Thread:handleError
arg this = <instance of Thread>
arg error = <instance of DoesNotUnderstandError>
Object:throw
arg this = <instance of DoesNotUnderstandError>
Object:doesNotUnderstand
arg this = nil
arg selector = 'at'
arg args = [*1]
arg kwargs = [*0]
Meta_Fosc:lilypondVersion
arg this = <instance of Meta_Fosc>
var str = ""
Interpreter:interpretPrintCmdLine
arg this = <instance of Interpreter>
var res = nil
var func = <instance of Function>
var code = "Fosc.lilypondVersion;"
var doc = nil
var ideClass = <instance of Meta_ScIDE>
Process:interpretPrintCmdLine
arg this = <instance of Main>
^^ ERROR: Message 'at' not understood.
RECEIVER: nil
Messages with a similar name understood by the receiver:
as
rate
halt
Many other objects respond to the message 'at' (found 36 superclasses).
āExec format errorā indicates the binary is not compatible with ARM64. You cannot run the x86_64 LilyPond binary on ARM64.
LilyPond itself runs and returns the version correctly, but FOSC fails when trying to open the generated PDF:
Fosc.lilypondPath = "C:/Users/prko/Downloads/lilypond-2.25.81/bin/lilypond.exe"
returns:
-> Fosc
Fosc.lilypondVersion;
returns:
-> 2.25.81
However,
(
a = FoscNote(60, 1/4);
a.show
)
returns:
ERROR: Changing working directory to: `'C:/Users/prko/AppData/Local/SuperCollider/fosc-output'
fatal error: unable to change directory to: `'C:/Users/prko/AppData/Local/SuperCollider/fosc-output'
ERROR: FoscIOManager:openFile: path does not exist: C:\Users\prko\AppData\Local\SuperCollider/fosc-output/0005.pdf.
CALL STACK:
Object:reportError
arg this = "FoscIOManager:openFile: path..."
Nil:handleError
arg this = nil
arg error = "FoscIOManager:openFile: path..."
Thread:handleError
arg this = <instance of Thread>
arg error = "FoscIOManager:openFile: path..."
Object:throw
arg this = "FoscIOManager:openFile: path..."
Fosc:show
arg this = <instance of FoscNote>
arg paperSize = nil
arg staffSize = nil
arg includes = nil
var illustrateEnvir = <instance of Event>
var path = "C:\Users\prko\AppData\Local\..."
Interpreter:interpretPrintCmdLine
arg this = <instance of Interpreter>
var res = nil
var func = <instance of Function>
var code = "(
a = FoscNote(60, 1/4);
a.s..."
var doc = nil
var ideClass = <instance of Meta_ScIDE>
Process:interpretPrintCmdLine
arg this = <instance of Main>
The folder does exist:
"C:/Users/prko/AppData/Local/SuperCollider/fosc-output".openOS
So this seems to be a path-handling issue in FOSC on Windows.
The relevant code is here:
Changing line 147 as follows suppresses the error messages on macOS 15.7.5, Ubuntu 22.04.5, and Ubuntu 24.04.3:
exitCode = systemCmd(
command +
Platform.case(
\osx, { "2>/dev/null" },
\linux, { "2>/dev/null" },
\windows, { "2>NUL" }
)
);
However, this also hides important LilyPond warnings and errors, such as:
ERROR: warning: g_spawn_sync failed (-1): gs: Failed to execute child process āgsā (No such file or directory)
ERROR: warning: `(gs -q -dNODISPLAY -dNOSAFER -dNOPAUSE -dBATCH -dAutoRotatePages=/None -dPrinted=false /var/folders/2_/hdf9s2tx6kg7_yqv0bwqtlcm0000gn/T//lilypond-tmp-372811)' failed (-1)
ERROR: /opt/homebrew/Cellar/lilypond/2.24.4/share/lilypond/2.24.4/ly/init.ly:65:2: error: Guile signaled an error for the expression beginning here
#
(let ((book-handler (if (defined? 'default-toplevel-book-handler)
ERROR: Throw to key `ly-file-failed' with args `()'.
ERROR: FoscIOManager:openFile: path does not exist: /Users/prko/Library/Application Support/SuperCollider/fosc-output/0028.pdf.
CALL STACK:
Object:reportError
arg this = "FoscIOManager:openFile: path..."
Nil:handleError
arg this = nil
arg error = "FoscIOManager:openFile: path..."
Thread:handleError
arg this = <instance of Thread>
arg error = "FoscIOManager:openFile: path..."
Object:throw
arg this = "FoscIOManager:openFile: path..."
Fosc:show
arg this = <instance of FoscNote>
arg paperSize = nil
arg staffSize = nil
arg includes = nil
var illustrateEnvir = <instance of Event>
var path = "/Users/prko/Library/Applicat..."
Interpreter:interpretPrintCmdLine
arg this = <instance of Interpreter>
var res = nil
var func = <instance of Function>
var code = "(
a = FoscNote(60, 1/4);
a.s..."
var doc = nil
var ideClass = <instance of Meta_ScIDE>
Process:interpretPrintCmdLine
arg this = <instance of Main>
So suppressing stderr entirely is not ideal.
Filtering only the ānoiseā while keeping real errors visible seems better:
*runLilypond { |path, flags, outputPath, executablePath, clean=false|
var lilypondBase, filterText, command, exitCode, success;
executablePath = executablePath ?? { Fosc.lilypondPath };
lilypondBase = path.splitext[0];
outputPath = outputPath ? lilypondBase;
flags = ((flags ? "") ++ " %").format("-dno-point-and-click -o");
command = "% % % %".format(executablePath, flags, outputPath.shellQuote, path.shellQuote);
filterText = " 2>&1 | grep -vE " ++
"'^(Processing|Parsing\\.\\.\\.|Interpreting music\\.\\.\\.|Preprocessing graphical objects\\.\\.\\.|Finding the ideal number of pages\\.\\.\\.|Fitting music on [0-9]+ page[s]?\\.\\.\\.|Drawing systems\\.\\.\\.|Converting to .+)$' | " ++
"grep -vE '^$'";
exitCode = systemCmd(
command +
Platform.case(
\osx, { filterText },
\linux, { filterText },
\windows, { " 2>NUL" }
)
);
success = (exitCode == 0);
if (success && clean) { File.delete(path) };
^success;
}
I donāt use LilyPond regularly, but whenever I test it, it often breaks due to architecture mismatches (PowerBook ā MacBook, Intel ā ARM). This has been the main reason I havenāt been able to adopt LilyPond more fully.
Iām surprised Opusmodus isnāt talked about more, itās an amazing tool ( not free but well worth the cost IMO ). Itās a Lisp coding environment for algorithmic composition that has its own language for working with notes/harmony and common music notation. You code something up and it creates the notation/score which you can either export as music xml or import directly into a notation app like Dorico/Musescore/Sib. Iāve only used it with orchestral sample libraries, but it integrates with SC using cl-collider.
For me, I Iove SC for synthesis/sound design but IMO itās woefully inadequate when trying to create/represent music of any complexity with conventional notes/harmony/notation.
If youāre using a Mac then I recommend installing Homebrew and getting Lilypond from that. Homebrew simplifies a lot of things on the mac.
I donāt think itās very well known. I stumbled across after Iād already been using Common Lisp for a while, and by that point Iād already invested time in other approaches. But it does look good.
Thereās also Common Music for Common Lisp, which someone resurrected - and which itself uses an old (but it still works fine) Common Lisp port of Fomus, which can generate Lilypond and MusicXML pretty well. I have my own library for Common Lisp which is pulling out the good bits of Common Music (cool concepts, seriously shitty code), but isnāt really read for prime time atm.
And for completeness there is this:
Iāve only used it with orchestral sample libraries, but it integrates with SC using cl-collider.
If youāre comfortable with Common Lisp, then CL-Collider is highly recommended. It can do everything that SCLang can do, and is a very nice environment for building synthdefs. Itās a developer did a really good job. And in Emacs with Slime/Sly, itās a wonderful environment for livecoding.
Thanks, but installing LilyPond via Homebrew on a silicon Mac currently raises a problem described here:
The best way to install LilyPond per OS and per architecture is described here:
I have opened a PR that:
Fosc;Fosc; andWith these changes, the current compatibility status of Fosc with SC 3.15.0 is as follows:
Windows 11
macOS 15.7.5
Ubuntu 22.04.5 LTS
For reference, Fosc and LilyPond work with SuperCollider 3.13.0 on macOS 10.14.6 without this PR. This has been tested with LilyPond 2.24.4 and 2.25.81 (x86_64). N.B. The LilyPond path must not contain whitespace.
Pretty sure thatās an FOSC problem, as Lillypond works fine for me.
FOSC probably just doesnāt have the right paths for OSX (the locations changed for ARM), of thereās an issue with your environment variables.
Thanks for the suggestion.
I tested the following paths:
Fosc.lilypondPath = "/opt/homebrew/Cellar/lilypond/2.24.4/bin/lilypond"
Fosc.lilypondPath = "/opt/homebrew/bin/lilypond"
Since I tested both the direct Cellar path and the Homebrew symlink under /opt/homebrew/bin, I do not think this is simply a matter of Fosc using the wrong path.
Evaluating the following code with both paths produces the same error when LilyPond is installed via Homebrew, but not when using the official LilyPond download:
(
a = FoscNote(60, 1/4);
a.show;
)
Error message:
warning: g_spawn_sync failed (-1): gs: Failed to execute child process āgsā (No such file or directory)
warning: `(gs -q -dNODISPLAY -dNOSAFER -dNOPAUSE -dBATCH -dAutoRotatePages=/None -dPrinted=false /var/folders/2_/hdf9s2tx6kg7_yqv0bwqtlcm0000gn/T//lilypond-tmp-7128040)' failed (-1)
/opt/homebrew/Cellar/lilypond/2.24.4/share/lilypond/2.24.4/ly/init.ly:65:2: error: Guile signaled an error for the expression beginning here
I also compared the two lilypond binaries:
Homebrew-installed LilyPond
/opt/homebrew/Cellar/lilypond/2.24.4/binOfficial LilyPond download
/Users/prko/Dropbox/prko/_macApps@Dropbox/LilyPond/lilypond-2.24.4_x86_64/lilypond-2.24.4/binSo although the version number is the same, the binaries do not appear to be identical.
Based on this, my current hypothesis is that the Homebrew LilyPond build on ARM64 macOS does not behave in the same way as the official LilyPond binary, and that this is at least part of what I am seeing here. For that reason, I do not think it is clear that this is an Fosc issue alone.
Could you share your setup details, including:
Without that background, it is difficult to compare results or determine whether the issue lies in Fosc, the LilyPond build, or the local environment.
Youāre comparing an ARM binary to an intel binary (the official one - x86_64 is Intel).
Iām using Lilypond on a Mac (Arm) which was installed using homebrew. It is the latest version. It compiles Lilypond files with no problem.
If there is a problem with your Lilypond install, then the way to test that is to try and compile a simple Lilypond file. If it works, then the problem is not your Lilypond.
Iām guessing the problem is either that it canāt find Lilypond, or more likely it canāt find some of the library files that Lilypond needs. Thatās a FOSC problem. On ARM these files are in a different location to intel, and thatās probably the issue.
You were right.
I found the problem and fixed it:
Thank you very much, and Iām sorry it took me so long to understand.