last updated 16 April 2013
... well it is a work in progress. Feedback is welcome !
Csound-x is a set of editing modes and libraries intended to compose music for Csound without leaving Emacs. Its main purpose is to propose a full-featured textual front-end for Csound.
It is developed using GNU Emacs 23.
GNU Emacs 21 and 22 should be supported but this is not tested. XEmacs is not supported.
Csound-x provides indentation and fontification for scores and orchestras. Direct access to the Csound documentation allows insertion of opcode templates.
A CSD can be generated from a simple text containing a score and an orchestra, as pasted from a mail or a manual for example. The CSD can also be generated directly from the clipboard contents.
The package makes it easy to toggle from an orc/sco pair to a CSD. Compilation of both formats can be driven from Emacs.
Documents can be processed on-the-fly with multiple configurations of Csound which can be customized and made available from a menu. It is thus very easy to swap from DAC rendering to WAV file compilation, or from different versions of Csound.
Csound-x makes it possible to have richer Csound documents by adding either comments or mark-ups making sense for Emacs.
The documents are still valid for direct compilation by Csound; simply they also embed high-level information making them easier to work on. For example, a CSD can become tunable from an Emacs menu, or become an algorithmic template for a whole family of CSD documents.
A library for score generation is included (see The i library.) so that the score section of a CSD can be algorithmically generated or modified.
MIDI data can be included in various form, most notably using Keykit format (see Handling MIDI data.), (see The k library.)
Support for algorithmic generation of instruments is being implemented (see Csound Elisp.)
Csound-x can also work upside-down: instead of editing Csound documents within Emacs it is possible to directly compose in Emacs Lisp with the underlying temporary CSD document remaining implicit. (see Csound Elisp.)
Csound-x is a main component of Surmulot, an open system for musical composition (see (Surmulot)Top.)
Surmulot home page: http://www.zogotounga.net/surmulot/surmulot.html
Csound-x defines the following markups to be used outside the <CsSynthesizer>...</CsSynthesizer> section of a CSD document:
Csound-x also gives a specific meaning to comments in orchestras and scores that start with ;|
Csound-x is released under the General Public Licence, version 2.
The latest version is available as a stef-elisp.zip archive from http://www.zogotounga.net/comp/csoundx.html
Surmulot provides Csound-x and much more, with no installation hassle: http://www.zogotounga.net/surmulot/surmulot.html
The following programs are either required by Csound-x, or at least considered very useful:
The Calc package for Emacs. It is part of the newest Emacs versions.
If you do not have it installed in Emacs 21, the simplest way is to get it here: http://www.zogotounga.net/comp/calc-2.02f.zip
Then add the following to .emacs:
(setq load-path (nconc load-path (list "~/site-lisp/calc-2.02f"))) ;; (... change the path according to your installation)
The MMM mode for Emacs. This allows several major modes to coexists in the same buffer. Get it here: http://mmm-mode.sourceforge.net/
Cmask, a multiplatform stochastic event generator for Csound. Homepage: http://www.kgw.tu-berlin.de/~abart/CMaskMan/CMask-Download.htm
The SES package for Emacs. This is a spreadsheet, very convenient for manipulating matrices defined in a score. It is part of Emacs 22.
Keykit, a multiplatform interpreted language/environment for programming everything MIDI. Its command-line executable (lowkey, from the usual Keykit distribution) is required by csound-key (see Handling MIDI data)
Download Surmulot to have a fully working Csound-x with many extra features (notably graphical interfaces) by simply unzipping an archive: http://www.zogotounga.net/surmulot/surmulot.html
The following only applies if you want to install Csound-x by yourself.
First of all, install the required packages See required utilities.
On Emacs 22 you can skip this step, although you will miss MMM.
Unpack the stef-elisp.zip archive in your site-lisp directory, then add the following line to your .emacs file:
Evaluate it to have the package immediately available.
You may byte-compile every file in the distribution for faster execution.
A few variables need to be set in order to fit your system via
M-x customize-group csound-x
Most variables can keep their default setting. If you don't understand what they do, just leave them as is.
On windows system, your Csound installation should be detected automatically at start-up. You can test this for example by evaluating the code in See Csound Elisp.
Csound-x provides the major modes csound-sco-mode and csound-orc-mode for editing respectively score and orchestras. These are modified versions of the modes written by John Fitch.
See Score editing. See Orchestra editing.
Csound-x also provides a major mode for CSD buffers. It is recommended to use the CSD format when starting a project from scratch. See CSD editing.
When the MMM mode is installed (See Distribution.), it is possible to have the score and orchestra major modes enabled together in a single CSD buffer.
Alternatively, the score and orchestra sections of a CSD can be edited independently via indirects buffers. This should happen by default when the MMM mode is not available (in Emacs an indirect buffer is just another interface to the original buffer; so editing the score or orchestra there actually edit the CSD. See (emacs) Indirect Buffers.)
Both csound-sco-mode and csound-orc-mode menus provide a "Wrap in .CSD" item. The simplest way to use it is to divide the Emacs frame in two windows, with a score buffer in one and an orc buffer in the other. Invoking "Wrap in .CSD" from any of the buffers will create a new CSD buffer with the same name as the buffer you called it from, embedding the codes for orchestra and scores. If no associated buffer exist, a buffer with the same root name will be detected; if no such buffer exists, you will be asked if you want to go and fetch a file, or if the corresponding CSD section should be left empty.
The CSD buffer name is built from the original sco or orc buffer. Note that if a buffer with that name already exists and you accept to overwrite it, only the <Score> and <Orchestra> sections will actually be overwritten. This makes it possible to split a CSD buffer into an orc/sco pair, work on them, then assemble them back in the CSD, without loosing any extra information (such as the <Options> section) in the initial CSD.
When a CSD is first created, by default the "<Options>" section is left void. Customize the variable cscsd-default-options to define the content you want there.
"Wrap in .CSD" either updates an existing CSD buffer or creates one. The initial sco and/or orc buffers are never modified nor killed. The associated command is:
Associate the current buffer to a sco or orc file and create a csd; if `next-window' displays a complementary buffer, it is proposed as default otherwise we look for a complementary buffer with the same root name or else we look for a complementary file in the same directory ... and if all of this fails we just ask.
do nothing if we already are in a csd buffer
In the “CSD” menu, item "orc/sco -> Split in orc/sco" creates a score and an orchestra buffers from the current CSD buffer. The name of the initial buffer is kept; for example "yop.csd" leads to "yop.sco" and "yop.orc". The initial CSD is left untouched.
Still in the “CSD” menu, item "Build a true CSD" tries to analyse the buffer content which should contain one score and one orchestra, and attempts to wrap this into a true .csd format. The intent of this command is for example to easily convert a text from an email into a CSD. It tries to guess where starts orc and where starts sco by looking for lines matching the regexps listed in 'cscsd-orc-delimiter-regexps and 'cscsd-sco-delimiter-regexps. The associated command is:
List of regexps to be tried in order to guess where the orchestra starts in a plain text file known to contain a score and an orchestra with no csd syntax to wrap them.
List of regexps to be tried in order to guess where the score starts in a plain text file known to contain a score and an orchestra with no csd syntax to wrap them.
"Build a CSD from clipboard" is similar, only it creates a CSD from the content of the OS clipboard. Associated command is
The csound-score provides indentation, fontification and a “SCO” menu.
... this is not fully documented yet
Use `<tab>' to indent the line at point.
If the line only contains an opcode fragment, then successive `<tab>' strokes propose in turn all possible completions, that is all opcodes containing the fragment as a substring, along with all templates and some documentation; press `y' to accept a template, or `<esc>' to cancel the whole operation.
Alternatively, you can get another kind of completion with `M-<space>': this inserts in turn a one-line template, without documentation, for each opcode beginning with the fragment.
csound-csd-mode is a major mode for CSD files, which gets started whenever you visit a file with extension .csd.
The “CSD” menu is shared among all types of csound buffers.
With fewer items in simple orchestra and score buffers, it allows conversion to CSD and access to high-level editing features.
In CSD buffers the “CSD” menu appears in full; it can be divided in the following sections:
This part of the menu is dynamically computed to reflect the meta-controls defined in the CSD. See CSD meta-controls.
These include snapshots and Embedded Emacs Lisp (EEL) settings. EEL provides a clean way to integrate Emacs Lisp code within a CSD document, so that it can become tunable at a very high level, become the template for a whole family of algorithmic compositions, or even become a program in itself. It is a very powerful feature that can bring you very far away from usual csound programming... See Embedded elisp.
The "Structure" submenu allows both exploration of the CSD structure and individual edition of its different components.
Exploration can be driven via the speedbar which allows you to directly jump to critical places in the document: score, orchestra, intruments, f-tables, score matrices, SES areas, EEL meta-comments.
Score and orchestra can be separated either virtually, via indirect buffer, or really, by creating new independent score and orchestra buffers.
The submenu also provides ways to rebuild the CSD, or create one from the clipboard contents.
This section gives access to Csound HTML documentation. See Integrated documentation.
It also provides entry points to the orchestras library. See Libraries and repositories.
... this is not fully documented yet
The last items invoke external softwares (most notably Csound) for processing the .csd file. See Interactive invocation from CSD.
... this is not documented yet
The “orc/csd” sub-menu gives access to a user-defined library of orchestra code. You may search for a string or a regular expression within the whole library.
To define the directories constituting the library, use items “Set path” and “Set recursive path” (all directories in the recursive path add their subdirectories tree to the library). Within these directories, all *.orc, *.csd and *.udo files are part of the library.
The result of a query appears as a list of files in a buffer from where you can either jump to a given source (by clicking the file name) or directly process the file (using the left button, which may not exists if no score was found).
“Code repository” is a sub-menu common to all csound modes menus. It provides insertion of code chunks stored in *.csr files, a simple format used by CsEdit (Csound code editor for Windows) for its "code repository" feature. So these files can be shared between CsEdit and Emacs.
CsEdit home page is http://flavio.tordini.org/csound-editor
You need to customize the variables 'cscsd-csr-files (this one should be a list of the *.csr file to import within Emacs) and 'cscsd-current-csr-file (this one defines which file is getting edited when registering a new bit of code).
Once this is done, the region can be added to the repository with the menu item "Register region", while you can directly edit (and thus correct or improve) the corresponding .csr file with the item "Edit csr file"
By default, you should have some example code stored in the file somecode.csr (provided that csound-x is a folder in your ~/site-lisp, which is the recommended installation. see Installation)
Note: you may also want to customize 'cscsd-csr-author in order to provide a value for the "Author" fields in the *.csr
Csound can be operated as an Emacs sub-process provided that the following customizable variables are correctly set:
A list of pairs (NAME FUNCTION) or (NAME SYSTEM-CALL) among which you can choose which process is to be invoked by the "process file" and "process regions" items from the "Csd" menu.
This sub-menu provides a convenient way to keep different csound executables and/or different ways to process the file at hand.
When you choose the current processing here, this choice is saved and kept across Emacs sessions; internaly, this happens by setting the customizable variable 'cscsd-current-processing
Alternatively, you can make the processing buffer-local: only the current CSD document will be processed according to this choice. Other documents either keep their own local setting or obey the global one.
If you answer 'yes' to the prompt proposing to hard-code the processing value, a comment line is inserted at the beginning of the CSD buffer. The setting will be saved across Emacs sessions. (see File Variables).
If you happen to be in an indirect buffer, the setting is kept local to that buffer, and you will not be asked if you want to hard-code its value.
For this to work properly, the region must feature a subset of i-statements in the score (possibly with comments and empty lines). The first lines of the score will be kept, up to and excluding the first appearing i-statement, so that the possibly required ftables will hopefully still be present. Then the region will be added, followed by a "e" statement which ends the score. A time offset will be proposed at the minibuffer, so that the trimmed score starts at t=0. You may change it as you like.
This uses a temporary CSD file whose location you should set by customizing 'cscsd-temp-csd-file. If something gets wrong in the rendering process then this is the place to have a look at.
Processing a region is likely to fail for complex scores with tempo statements and multiple sections.
When the current processing compiles an audio file, its location is stored in the buffer-local variable 'cscsd-latest-audio-file. When this variable is non-nil, “Play file” and “Edit file” items are activated.
"Play file" does whatever the customizable shell command 'cscsd-shell-play-audio does. "Edit file" action is similarly defined by customizable shell command `cscsd-shell-edit-audio.
Record the name of the latest compiled audio file. It is buffer-local: an audio file can thus be associated to each CSD or score buffer.
Shell command used to play an audio file. It should feature a single %s occurrence, to be replaced with the audio file name.
Shell command used to edit an audio file. It should feature a single %s occurrence, to be replaced with the audio file name.
... this is not documented yet
Process the file visited by the current csd buffer. What this means exactly depends on the function or command associated to `cscsd-current-processing' in `cscsd-process-file'. If needed, the audio file name is built from the current buffer file name or fetched from the <CsOptions> area.
Compile CSD-FILE into AUDIO-FILE, with midi input from MIDI-FILE. SYSCALL is interpreted as a shell command by function `cscsd-interpret-command'.
Interpret SYSCALL as a shell command to be used in order to compile CSD-FILE into AUDIO-FILE, using midi input from MIDI-FILE.
SYSCALL must be a string containing up to three occurences of %s, for example: "$csound -dHWo %s %s"
if there is only one %s, it will stand for CSD-FILE.
if there are two of them, the first occurence of %s will be replaced by AUDIO-FILE, and the second %s by CSD-FILE, except if the string "DAC" is detected among the options, in which case the first %s is replaced with CSD-FILE and the second one with MIDI-FILE
if three occurences of %s appear, they stand for AUDIO-FILE, CSD-FILE and MIDI-FILE, respectively.
OMACROS is a list of cons cells of form '(("MacroName" . MacroValue) ..) defining orchestra macros, which are appended to SYSCALL as –omacro: options. SMACROS is a similar list defining –smacros: options, for score.
when AUDIO-FILE is nil (and required), it is built from CSD-FILE with an extension depending on the detected flags in the SYSCALL: .wav if a -W is detected, .aif if a -A, no extension otherwise. if no option flag is provided the <CsOptions> area is analysed and the filename can be detected there.
when MIDI-FILE is nil (and required), the user is interactively prompted for a file name.
SYSCALL may include the substring "$csound" which is then replaced by the returned value of function `cscsd-csound-binary'.
this function has the side effect of setting buffer-local variables `cscsd-latest-audio-file' and `cscsd-latest-midi-file', respectively to AUDIO-FILE and to the appropriate MIDI file, if any.
csound-doc provides straightforward access to Csound documentation: several brands of HTML manuals, Winhelp file, CsoundAV manual
the "canonical" HTML manual comes with Csound 5
the older "official" HTML manual (David Boothe's) homepage is http://www.lakewoodsound.com/csound/hypertext/manual.htm
the older "alternative" HTML manual (Kevin Conder's) homepage is http://www.kevindumpscore.com/download.html
the older Rasmus Ekman's Winhelp file can be possibly be found somewhere on the web...
go to the 'csound-doc customization group and define your settings as follow:
choose one of the HTML manual with 'csdoc-which-manual. this will be the manual used to document opcodes and insert opcode templates
set 'csdoc-html-directory and 'csdoc-html-entry-point as needed to fit your installation:
'csdoc-html-directory must be the directory where lives the manual you just choosed
'csdoc-html-entry-point can be any HTML page: it is only used by the “Browse HTML documentation” menu item. So it is possible to have another HTML manual at hand through this variable.
if you want to use the emacs w3 browser instead of the default external one, toggle 'csdoc-use-w3' on (note that w3 is an independant emacs package; it is not installed in the default configuration). in that case if can have the doc window pop up in a specific frame by adding the following to your .emacs:
(add-to-list 'special-display-buffer-names "*Csound Documentation*")
set 'csdoc-winhelp-file to the location of csound.help
if this is not correct, Winhelp support will simply be dropped altogether.
you may also provide access to CsoundAV HTML docs by setting 'csdoc-csoundAV-manual-directory
when working with csound-orc-mode, a couple of items should now appear in the “ORC” menu
Here are some of the corresponding interactive commands:
csound-mx allows the definition of matrices (“selections”) within a score, and provides ways to work on them; it also adds an extensible Matrix menu to the buffers in csound-sco mode, giving an easy access to the most useful functions of this package.
A "selection" is a rectangular part of a score identified by two comments. Example:
i 1 24.78 -0.94 2793.230.7 0.4 ;_meuh_b.5 i 1 16.06 -0.96 3413.510.6 0.3 i 1 29.98 -0.68 2792.310.6 0.7 ;_meuh_e.6 i 1 28.27 -0.443636.460.5 0.5
Here a selection called "meuh" (the selection id) begins at the 5th column in the first line and ends at the 6th column in the third line. It represents the matrix
0.7 0.4 0.6 0.3 0.6 0.7
You can define as many selections as you want by writing the corresponding comments. However, the two Matrix menu items "Select rectangular region" and "Select column" make it easy to do it with a few mouse clicks.
"Select rectangular region" makes a selection from the rectangular region between point and mark (if any), "Select column" scans the column at point upward and downward and selects a one-column matrix. The scan is stopped by any line which is not a i-statement, be it a comment or an empty line.
Both queries for a selection id (any keyword composed with letters and digits). You may just press <RETURN>, in which case the id will be "" and the selection will be the current one. The previous current selection, if any, will be lost.
The current selection is important since it is the one that can be directly accessed through the other menu commands. "Store current selection" queries for an id and give it to the current selection (which is not current any more, then), "Make a selection current" queries for an existing id and creates a new current selection from the corresponding one (this does not remove it) and "Remove a selection" queries for an id and erase the corresponding selection (this does not change the current one, except if it is the removed one)
If scomx-highlight-matrices is t, all selections are displayed with a background color: by default the color is scomx- selection-color for non-current selections, and it is scomx-main-selection-color for the current selection.
But, if a selection id is recognized as a color name by Emacs, the selection will be displayed in that color. So the simplest way to have an informative display is to call your selections "green", "orange", "gold", "blue", etc.
Alternatively, you may also customize scomx-colors-alist which defines a list of ids with associated background colors (note that the so-called "background colors" can actually be property lists defining a face better that the background color only; see the default alist for examples).
The "Sort" menu item queries for a column number and sort the current selection according to that column.
"Apply script on matrix" let you invoke an external script or utility (such as a shell command): just provide its name at the prompt. The script is given from stdin a temporary text file containing the current selection. It should output to stdout a selection of the same size, in the same format (plain text), which will replace the current selection.
"Operate on matrix" and "Operate on parameters" allow any mathematical operation to be performed on the current selection. They indirectly invoke the Calc package to perform the calculations (see (Calc)Top). This is a wide topic, because Calc is very powerful and also not so easy to work with . However, simple operations are performed simply... see the advanced usage chapter for more in-depth considerations (see Basic functions). We will proceed here with basic stuff.
"Operate on matrix" queries for a mathematical operation affecting variable m, which is the matrix represented by the current selection.
"Operate on parameters" queries for a mathematical operation affecting variable p, which takes in turn the value for each field in the matrix m. Besides, you can use variables nc and nr to represent the position of p in the matrix.
The expressions you input will be interpreted as Calc data (see (Calc)Programming). Two formats are available: algebraic and lisp.
By default, you are first queried for an algebraic expression. If you just press <RETURN>, you will be prompted for a lisp expression. This behaviour can be inversed by setting scomx-propose-algebraic-input-first to nil (do so via the customization system)
An example algebraic expression is
This offsets all values in the matrix by 10. The corresponding lisp form is:
(+ 10 m)
The tricky part with the lisp format is that what you input will be evaluated within a defmath macro. Please read the "programming" chapter of Calc info for what this means exactly (see (Calc)Defining Functions). The most important point is that floats must appear in a specific format. For example, it is an error to write
(+ 10.05 m)
Instead, you must write
(+ :"10.05" m)
In general, it is thus simpler to use the algebraic format. But lisp is much more powerful, so eventually it all depends on what you want to do.
"Operate on matrix" examples (algebraic format):
"Operate on matrix" examples (lisp format):
..note that in the last example it's "rows": this is because internaly the matrix is transposed
"Operate on parameters" examples (algebraic format):
Expressions within brackets such as [($CNT./3)+10] are understood and symbolically processed. For example, applying p^2 on such a parameter would result in [(($CNT./3)+10)^2]
The @ and @@ operators are supported. Note that the result of an operation is kept in symbolic form only if it can not be simplified. As an example, applying p or m (that is, the identical operation) on the matrix
[1+1] [1+$x.] [3^$CNT.] [3^(2+2)] [@513] [@@513] [@$n.] 0.15
2 [1+$x.] [3^$CNT.] 81 1024 513 [@$n.] 0.15
... while applying p^2 would result in:
4 [(1+$x.)^2] [3^(2*$CNT.)] 6561 1048576 263169 [@$n.^2] 0.0225
For complex manipulations, you will need to do some programming. Here are documented the most useful functions and macros defined by the csound-mx.el package.
Note: all functions having id (a selection name) as an argument must be called from within the score buffer.
Evaluates body within a defmath macro and returns a number
Evaluating within a defmath means that all Calc extensions are available, and that Calc format must be used (please refer to the Calc manual for details) See (Calc)Top.(/ 5 3) => 1 (with-defmath (/ 5 3)) => 1.6666667
Evaluates body within a defmath macro and returns the result as a string (in algebraic syntax)(with-alg-defmath (/ 5 3)) => "1.6666667"
Evaluates body within a defmath macro and returns a Calc data structure in raw form(with-raw-defmath (/ 5 3)) => (float (bigpos 667 666 16) -7)
Similar to 'with-defmath, but allows in VLIST the importation of symbols which will refer to the same value inside the defmath as outside (sort of "globals").(let ((A 5)(B 3)) (with-defmath-import (A B) (/ A B))) => 1.6666667
Reads the score selection id and returns a matrix in raw Calc format
Note: this function can not be used inside a defmath.i 1 24.78 -0.94 2793.230.7 0.4 ;_meuh_b.5 i 1 16.06 -0.96 3413.510.6 0.3 i 1 29.98 -0.68 2792.310.6 0.7 ;_meuh_e.6 (scomx-grab-matrix "meuh") => (vec (vec (float 7 -1) (float 6 -1) (float 6 -1)) (vec (float 4 -1) (float 3 -1) (float 7 -1)))
In the process, all macros are replaced by variables whose names start with macroi 1 24.78 -0.94 2793.23[$X.] 0.4 ;_meuh_b.5 i 1 16.06 -0.96 3413.51[$X.+0.1] 0.3 i 1 29.98 -0.68 2792.31[$X.+0.2] 0.7 ;_meuh_e.6 (scomx-grab-matrix "meuh") => (vec (vec (var macroX var-macroX) (+ (var macroX var-macroX) (float 1 -1)) (+ (var macroX var-macroX) (float 2 -1))) (vec (float 4 -1) (float 3 -1) (float 7 -1)))
Write matrix in the score selection id(scomx-yank-matrix '(vec (vec 1 2 3) (vec 4 5 6)) "meuh") => i 1 24.78 -0.94 2793.231 4 ;_meuh_b.5 i 1 16.06 -0.96 3413.512 5 i 1 29.98 -0.68 2792.313 6 ;_meuh_e.6
Invoke the shell command: script < input-file > output-file where input-file is a temporary file containing selection id as plain text, and output-file a temporary file where a replacement for id is read
Evaluates body within a defmath macro, interpreting the variable m as the matrix defined by selection id, and replaces that selection with the result of the evaluation (which must be a matrix of same dimensions)(scomx-operate-m "meuh" (neg m)) => replace all values in selection "meuh" with their opposites
Evaluates body within a defmath macro for each element of the matrix defined by selection id, replacing that element with the result of the evaluation. The variable p is interpreted as the element initial value, nr as its vertical position (starting with 1 at the top) and nc as its horizontal position in the matrix.(scomx-operate-p-nc-nr "meuh" (+ p (* 0.1 nr))) => offset all values in selection "meuh" by an amount depending on their vertical index
Fetch the element, a string or nil, at column COL and row ROW in matrix SID if SID is not provided, it defaults to "", the current selection. (note that columns and rows indexes start at 1)
If SET-TO is non nil, it replaces the previous matrix element. (SET-TO may be a string or a number)
This function does not perform any interpretation of the returned data. It is either nil if there is no element in the matrix at these coordinates, or the string at point matching the regexp “[^ \t\n\r]+”i 1 24.78 -0.94 2793.230.7 0.4 ;_meuh_b.5 i1 16.06 -0.96 3413.510.6 0.3 i 1 29.98 -0.68 2792.310.6 0.7 ;_meuh_e.6 (scomx-elt 1 1 "meuh") => "1" (scomx-elt 1 2 "meuh") => "i1"
Cmask is a powerful stochastic event generator, intended to generate full Csound scores. However, its is also very useful for simply filling a single matrix in a score. This is how csound-x uses it.
First of all, you must be somewhat familiar with Cmask concepts. Please read its very good documentation before you proceed with the rest of this chapter.
Cmask is not integrated by default. You need to turn 'scomx-Cmask-support on and to set the values of 'scomx-Cmask-binary and 'scomx-Cmask-tmp-file (do so via the customization system)
Now here is how it works:
Invoke Cmask for generating a p3 field which will be stored in the last column of matrix. Returns the modified matrix. If there is more than one column in matrix, the first one is considered as the p2 values of reference when generating the p3 field. Weither it is actually a p2 column does not matter, but it must be sorted.
If there is only one column, the p2 values are assumed to range evenly from 0 to 1.
cmask is intended to be used within a 'scomx-operate-m body (as it is defined by a 'defmath macro)
(scomx-operate-m "meuh" (cmask m "seg [0 1]" "quant 0.1 1")) => i 1 24.78 -0.94 2793.231 0 ;_meuh_b.5 i 1 16.06 -0.96 3413.512 0.5 i 1 29.98 -0.68 2792.313 1 ;_meuh_e.6
Here the cmask function had Cmask process the following code:
f 1 4 p1 const 1 p2 item cycle (1 1) p3 seg [0 1] quant 0.1 1
The first three lines are automatically calculated so that the p2 values mimic the first column of the targeted matrix, which must be sorted (otherwise cmask will refuse to proceed). If there is only one column, then the p2 values are supposed to range evenly from 0 to 1 within the scope of the matrix.
This is important since the p2 values are the reference for all time-dependent Cmask parameters.
In our example, there is no time dependency: seg [0 1] means that the generated values must go from 0 to 1. But consider osc sin 1 . This generate a sine with frequency 1, thus the p2 values matter: with the matrix "meuh", three periods would be spawned (because the first column is [1 2 3]), while with a single column matrix it would be only one period (since p2 is supposed to go from 0 to 1).
This may not be very clear, so I invite you to just try it and get the feeling. See also the "Cmask" submenu in the Matrix menu for an easier access to the full power of Cmask.
The following are example arguments for "Operate on matrix":
At the bottom of the "Matrix" menu is a code repository. It makes it faster to perform complex calculations on matrices, but it is worth noting that everything featured there can be done (albeit quite tediously) through the menu items "Operate on matrix" and "Operate on parameters".
The repository is built from the evaluation of the customizable list of function 'scomx-Matrix-menu-definitions. By default it only contains the symbol 'scomx-Matrix-menu, which is the name of the function defining the code provided by default. If you look at it, you will be able to write your own equivalent functions: adding their names to 'scomx-Matrix- menu-definitions will make your code available from the repository.
Whenever you change something in this code, invoke the "Update" item in order to refresh the menu.
Among the proposed submenus, one is called "Cmask" and gets enabled if 'scomx-Cmask-supportis non-nil. From there you can access most of Cmask features and apply them to the current matrix. Beware that only the last column is affected, and that the first column, if it is a different one, provide the p2 values for Cmask. See cmask.
If you installed the SES package, you should have the following four items in your Matrix menu (see Score matrices):
They allow for each defined selection (that is, a matrix) to be associated to a spreadsheet buffer. The spreadsheet is completely independent from the source sco or CSD file, but its content can be stuffed back in the source whenever you want, either directly or as a <SES>...</SES> markup in a CSD file, in which case the formulas can be kept accross sessions.
The usage is pretty simple. Here is an example: suppose we have a CSD buffer with the following score section:
<CsScore> i 1 0 3 i 2 4 3 i 3 8 3 i 4 12 3 i 5 16 5 e </CsScore>
... where we define a gold matrix as follows:
<CsScore> i 1 0 3 ;_gold_b.1 i 2 4 3 i 3 8 3 i 4 12 3 i 5 16 5 ;_gold_e.2 e </CsScore>
Now doing "Selection to spreadsheet" with argument "gold" creates a SES buffer with 2 columns for the source selection, plus a number of extra columns defined by scomx-spreadsheets-borders (by default itīs 3):
A B C D E --------------------------------------- 1 0 2 4 3 8 4 12 5 16
Depending on the value for scomx-framed-spreadsheets, the SES buffer may appear in the other window or in a specific frame.
Now we set the formula for B1 as (* 2 A1) and copy this to all cells in the B column (see the SES documentation for details, see (SES)Top.):
A B C D E --------------------------------------- 1 2 2 4 3 6 4 8 5 10
At this point, if we go back to the CSD buffer and do a "Spreadsheet to selection" with argument "gold", the gold matrix will be updated according to the content of the spreadsheet:
<CsScore> i 12 3 ;_gold_b.1 i 24 3 i 36 3 i 48 3 i 510 5 ;_gold_e.2 e </CsScore>
This did not save the formulas we used to calculate the second column. If we want to keep them, we do "Spreadsheet to XML", again with a "gold" argument. A <SES>...</SES> markup area is then inserted at the end of the CSD buffer. Later on, invoking "XML to spreadsheet" with "gold" will re-create the SES buffer complete with its formulas, making it possible to work again on the gold selection.
The parameter 'cscsd-ses-areas-invisible defines weither the content of a SES XML area is actually displayed in the CSD buffer. By defaults it is not.
Note that "Spreadsheet to XML" does not imply a "Speadsheet to selection". It does not change the score content: this is only a way to store the spreadsheet associated to a matrix for a later usage. If the matrix size or content is changed meanwhile, or if the selection disappear altogether, this will not be reflected in the XML area.
The extra columns (and rows) in the SES buffer may be used to store any temporary or seeding value. When the matrix is yanked back, they are ignored; when the spreadsheet is stored as an XML area, they are stored as any other component of the spreadsheet (see the SES documentation for details about its file format).
For example, we can set B1 formula to (* D1 A1) and copy this in the B column:
A B C D E --------------------------------------- 1 2 fact: 2 2 4 3 6 4 8 5 10
now if we change the D1 value to 3, the matrix becomes:
A B C D E --------------------------------------- 1 3 fact: 3 2 6 3 9 4 12 5 15
we can check that when stuffing back the values, only the two first columns are taken into account:
<CsScore> i 13 3 ;_gold_b.1 i 26 3 i 39 3 i 412 3 i 515 5 ;_gold_e.2 e </CsScore>
Note that Calc is integrated to the spreadsheet through the macros 'calc-cell and 'calc-alg-cell, so you may type this kind of forms to edit a cell contents:
RET (calc-cell (round (/ A1 B1))) RET (calc-alg-cell "round(A1/B1)")
The parameters 'scomx-ses-calc-shortcut (default is 'm:) and 'scomx-ses-calc-alg-shortcut (default is 's:) provide shortcuts:
RET (m: (round (/ A1 B1))) RET (s: "round(A1/B1)")
Csound-x can interface with several systems for musical composition; one of them is Keykit from which it borrows the MIDI data representation (see The k library).
Keykit home page: http://thompsonresidence.com/keykit
Another system is muO, based on Squeak (see (Surmulot)Top). Surmulot home page: http://www.zogotounga.net/surmulot/surmulot.html
If you have Keykit installed on your system, customize the group 'keykit so that csound-x knows how to work with it.
It is then possible to generate a MIDI file from within a CSD document. This happens in specific areas of the form:
<KeyPhrase label="name"> ... </KeyPhrase>
where ... stands for arbitrary Keykit code. This code is expected to define a KeyPhrase variable (whose value will become the MIDI file)
The “KeyPhrases” entry under the “CSD” menu lists all names for the currently existing <KeyPhrase> areas in the buffer. From the corresponding submenu it is possible to have the phrase as MIDI input.
The processing works as follows:
Also, a f0 statement corresponding to the length of the file (plus the value of the customizable variable 'cscsd-key-decay) is inserted at the end of the score section.
Since this may be rather clumsy, another mechanism is provided: if a macro MidiPhraseDuration is defined in the score, its value is set to the duration of the file (again, plus 'cscsd-key-decay). In this case no f0 statement is created.
Note that, for convenience, the CSD menu item "insert KeyPhrase area" (see The CSD menu) also writes the following at the beginning of the score section:
;#define MidiPhraseDuration ## ;f0 $MidiPhraseDuration.
..simply uncommenting these two lines should do the trick for most simple scores.
In very much the same way we can generate MIDI data from Squeak by defining specific areas of format
<MPhrase label="name"> ... </MPhrase>
where ... stands for Smalltalk code defining a mphrase variable.
Surmulot widgets can do this automatically: see (Surmulot)Musical phrase editor and see (Surmulot)MIDI.
Here is a full CSD example (the orc code is from Istvan Varga, simplified and adapted for MIDI by me):
<KeyPhrase label="loop"> KeyPhrase=repeat('ad50,e,d',10) </KeyPhrase> <KeyPhrase label="fractal"> KeyPhrase=fractal('ad200,e,d,g,a') </KeyPhrase> <MPhrase label="fractal"> |mphrase| mphrase:='ad200,e,d,g,a' kmusic fractal. </MPhrase> <CsoundSynthesizer> <CsInstruments> /* bsln1.orc - written by Istvan Varga */ sr = 44100 kr = 22050 ksmps = 2 nchnls = 1 massign 1, 1 instr 1 imp4 notnum ; pour MIDI imp5 veloc ; id imp6 midictrl 10, 80, 92 icps = 440*exp(log(2)*(imp4-69)/12) ; oscillator frequency iamp = 0.0039+imp5*imp5/16192 ; amplitude ifrq1 = 440*exp(log(2)*(imp6-69)/12) ; filter start freq. kamp linsegr 1, 0.5, 1, 0.025, 0 ; release envelope kcps port icps, 0.005, icps*0.5 ; osc. frequency knumh = sr/(2*kcps) kffrq port 0, 60/150, ifrq1 ; filter frequency a1 phasor kcps ; oscillator a1 = 1-2*a1 a1x butterbp a1, kffrq, icps*1.0 ; filters a1x = a1x*(2+kffrq/kcps) ; correct amplitude a1 = a1x+a1*0.5 a1 butterlp a1, kffrq a1 = taninv(a1* 2.5*iamp) ; distortion a1 = a1*kamp*20000 out a1 endin </CsInstruments> <CsScore> </CsScore> </CsoundSynthesizer>
Get this code in a CSD buffer by clicking anywhere inside it and doing `M-x cscsd-at-point'.
Now in the new CSD buffer the menu items "CSD->KeyPhrases->loop" and "CSD->KeyPhrases->fractal" will have Keykit process the corresponding MIDI phrases, as defined in the <KeyPhrase> areas.
Similarly, "CSD->MPhrases->fractal" will let you process or edit via ĩO the "fractal" phrase.
If you got Csound-x through the stef-elisp distribution, you should also find there embedded-elisp-library.el which provides the embedded-elisp minor mode.
Basically, doing "embedded elisp" means stuffing a document (in our case a CSD document) with lisp code areas intended to be evaluated interactively in order to modify or process the document.
To activate csound-csd-mode support for embedded elisp, select the “CSD -> Invoke EEL mode” menu item.
This gives birth to a “EEL” menu from which you can manage <ELISP> areas. Please refer to the documentation in embedded-elisp-library.el for general info about the minor mode.
When the customizable variable cscsd-use-EEL is non-nil, EEL mode is invoked automatically when an <ELISP> area is detected in the CSD document.
The next sections introduce a set of libraries intended to be used with embedded elisp:
Here is a CSD document with an <ELISP> area allowing the exploration of a set of possible configurations.
The code plays with the values of macros $m1., $m2. and $m3. so that it tries 8 different combinations.
The 8 corresponding CSD are written in the directory where lives the original CSD. For each of these files, a wave file is rendered in SFDIR.
<ELISP> ;;; [load] - [collapse/unfold] - [save] - [EVAL] - [;EVAL] (let ((cscsd-current-processing "csound wav") (cscsd-call-csound-always-sync t)) ;; the let statement gives the correct options to csound ;; and ensure that csound will not be executed asynchronously (dotimes (i1 2 3) (dotimes (i2 2 3) (dotimes (i3 2 3) (let* ((m1 (+ 2 i1)) (m2 (+ 1 i2 m1)) (m3 (+ 1 i3 m2))) (cscsd-set-macro-def "m1" m1) (cscsd-set-macro-def "m2" m2) (cscsd-set-macro-def "m3" m3) (let ((csd-file-name (expand-file-name (format "example%d-%d-%d.csd" m1 m2 m3) (file-name-directory (buffer-file-name))))) (write-region (point-min) (point-max) csd-file-name) (message "processing m1=%d m2=%d m3=%d ..." m1 m2 m3) (cscsd-process csd-file-name))))))) </ELISP> <CsoundSynthesizer> <CsInstruments> #define m3 #8# #define m2 #4# #define m1 #2# ; ************************************************************************ ; ACCCI: 02_43_1.ORC ; timbre: tibetan chant ; synthesis: additive same units(02) ; basic instrument with minimal differences in frequency(43) ; arpeggio instrument by Risset ; source: Phase6, Lorrain(1980); Boulanger(1990): risset1.orc ; coded: jpg 9/93 sr = 44100 kr = 441 ksmps= 100 nchnls = 2 instr 1; ***************************************************************** idur = p3 iamp = p4/9 ifq = p5 ioff1 = p6 ioff2 = $m1.*p6 ioff3 = $m2.*p6 ioff4 = $m3.*p6 irise = p7 idec = p8 ae linen iamp,irise,idur,idec a1 oscili ae, ifq, 1 a2 oscili ae, ifq+ioff1, 1 ; nine oscillators with the same ae a3 oscili ae, ifq+ioff2, 1 ; and waveform, but slightly different a4 oscili ae, ifq+ioff3, 1 ; frequencies create harmonic arpeggio a5 oscili ae, ifq+ioff4, 1 a6 oscili ae, ifq-ioff1, 1 a7 oscili ae, ifq-ioff2, 1 a8 oscili ae, ifq-ioff3, 1 a9 oscili ae, ifq-ioff4, 1 outs1 a1+a2+a3+a4+a5+a6+a7+a8+a9 endin instr 2; ***************************************************************** idur = p3 iamp = p4/9 ifq = p5 ioff1 = p6 ioff2 = $m1.*p6 ioff3 = $m2.*p6 ioff4 = $m3.*p6 irise = p7 idec = p8 ae linen iamp,irise,idur,idec a1 oscili ae, ifq, 1 a2 oscili ae, ifq+ioff1, 1 ; nine oscillators with the same ae a3 oscili ae, ifq+ioff2, 1 ; and waveform, but slightly different a4 oscili ae, ifq+ioff3, 1 ; frequencies create harmonic arpeggio a5 oscili ae, ifq+ioff4, 1 a6 oscili ae, ifq-ioff1, 1 a7 oscili ae, ifq-ioff2, 1 a8 oscili ae, ifq-ioff3, 1 a9 oscili ae, ifq-ioff4, 1 outs2 a1+a2+a3+a4+a5+a6+a7+a8+a9 endin </CsInstruments> <CsScore>; ************************************************************************ ; ACCCI: 02_43_1.SCO ; coded: jpg 9/93 ; GEN functions ********************************************************** ; carrier f1 0 1024 10 .3 0 0 0 .1 .1 .1 .1 .1 .1 ; score ****************************************************************** ; start idur iamp ifq ioff irise idec i1 0 35 8000 110 0.03 0.07 21 i1 20 20 9600 110 0.04 2 4 i1 28 30 8000 220 0.04 3 6 i1 32.1 23 8000 110 0.03 2.3 4.6 i2 5 20 9600 55 0.02 0.04 12 i2 20 15 8000 220 0.05 1.5 3 i2 32 26 9600 110 0.025 2.6 5.2 i2 36 22 8000 55 0.01 0.04 13 e </CsScore> </CsoundSynthesizer>
Csound-x defines a specific type of comment line in both orchestra and score sections. Those comments begins with ;| and the following keywords are interpreted as a directive for “meta-control”, that is a control flow or editing structure which has no meaning at the csound langage level and only makes sense for Emacs.
Once defined, such a control structure can be operated from the first part of the “CSD” menu and notably from the “Meta-setting” submenu.
The following meta-comments pairs can be used to easily comment out parts of the CSD:
;|menu ;|endmenu ;|toggles ;|endtoggles
See Meta settings tutorial.
The meta-comments pair
;|begpublic SomeName ;|endpublic
enclose a part of the CSD which is supposed to be often tweaked by the composer.
When such public sections appear in a CSD, the “CSD” menu propose an item “Public mode”. This builds and display a “public interface” frame for the CSD, where only the public sections are visibles.
Use item “Full editing mode” to restore the initial display.
Snapshots are <ELISP> areas (see Embedded elisp) which, when they are evaluated, restore the values for macros, meta-menus and toggles in the CSD.
Use "CSD -> Meta-Settings -> take a snapshot" to create a new snapshot; you will be prompted for a name in the minibuffer.
Use the submenu "CSD -> Meta-Settings -> restore snapshot ->" to override the current settings with the recorded ones.
See Meta settings tutorial.
Letīs start from the following composition from the Amsterdam catalog of Csound instruments. If you are reading this in an emacs buffer, just click anywhere in the code below and do `M-x cscsd-at-point' in order to have the CSD at hand. Here is the code:
<CsoundSynthesizer> <CsInstruments>; ************************************************************************ ; ACCCI: 02_43_1.ORC ; timbre: tibetan chant ; synthesis: additive same units(02) ; basic instrument with minimal differences in frequency(43) ; arpeggio instrument by Risset ; source: Phase6, Lorrain(1980); Boulanger(1990): risset1.orc ; coded: jpg 9/93 sr = 44100 kr = 441 ksmps= 100 nchnls = 2 instr 1; ***************************************************************** idur = p3 iamp = p4/9 ifq = p5 ioff1 = p6 ioff2 = 2*p6 ioff3 = 3*p6 ioff4 = 4*p6 irise = p7 idec = p8 ae linen iamp,irise,idur,idec a1 oscili ae, ifq, 1 a2 oscili ae, ifq+ioff1, 1 ; nine oscillators with the same ae a3 oscili ae, ifq+ioff2, 1 ; and waveform, but slightly different a4 oscili ae, ifq+ioff3, 1 ; frequencies create harmonic arpeggio a5 oscili ae, ifq+ioff4, 1 a6 oscili ae, ifq-ioff1, 1 a7 oscili ae, ifq-ioff2, 1 a8 oscili ae, ifq-ioff3, 1 a9 oscili ae, ifq-ioff4, 1 outs1 a1+a2+a3+a4+a5+a6+a7+a8+a9 endin instr 2; ***************************************************************** idur = p3 iamp = p4/9 ifq = p5 ioff1 = p6 ioff2 = 2*p6 ioff3 = 3*p6 ioff4 = 4*p6 irise = p7 idec = p8 ae linen iamp,irise,idur,idec a1 oscili ae, ifq, 1 a2 oscili ae, ifq+ioff1, 1 ; nine oscillators with the same ae a3 oscili ae, ifq+ioff2, 1 ; and waveform, but slightly different a4 oscili ae, ifq+ioff3, 1 ; frequencies create harmonic arpeggio a5 oscili ae, ifq+ioff4, 1 a6 oscili ae, ifq-ioff1, 1 a7 oscili ae, ifq-ioff2, 1 a8 oscili ae, ifq-ioff3, 1 a9 oscili ae, ifq-ioff4, 1 outs2 a1+a2+a3+a4+a5+a6+a7+a8+a9 endin </CsInstruments> <CsScore>; ************************************************************************ ; ACCCI: 02_43_1.SCO ; coded: jpg 9/93 ; GEN functions ********************************************************** ; carrier f1 0 1024 10 .3 0 0 0 .1 .1 .1 .1 .1 .1 ; score ****************************************************************** ; start idur iamp ifq ioff irise idec i1 0 35 8000 110 0.03 0.07 21 i1 20 20 9600 110 0.04 2 4 i1 28 30 8000 220 0.04 3 6 i1 32.1 23 8000 110 0.03 2.3 4.6 i2 5 20 9600 55 0.02 0.04 12 i2 20 15 8000 220 0.05 1.5 3 i2 32 26 9600 110 0.025 2.6 5.2 i2 36 22 8000 55 0.01 0.04 13 e </CsScore> </CsoundSynthesizer>
We are going to transform this into a template allowing an exploration of the algorithm. The instruments (identical, except from their ouput channel) are very simple: a reference oscillator at frequency p5 is doubled by height extra oscillators whose frequencies are slight variations of p5 controlled by p6. Given p6, the set of shifted frequencies is hardcoded: p5+p6, p5+2*p6, p5+3*p6, p5+4*p6, p5-p6, p5-2*p6, p5-3*p6 and p5-4*p6
Using the "macroify region" function (See The CSD menu.), it is easy to transform the CSD so that the p6 multipliers are now defined by the macros m1, m2 and m3, so that we obtain:
<CsoundSynthesizer> <CsInstruments> #define m3 #4# #define m2 #3# #define m1 #2# ... instr 1 idur = p3 iamp = p4/9 ifq = p5 ioff1 = p6 ioff2 = $m1.*p6 ioff3 = $m2.*p6 ioff4 = $m3.*p6 irise = p7 idec = p8 ... instr 2 idur = p3 iamp = p4/9 ifq = p5 ioff1 = p6 ioff2 = $m1.*p6 ioff3 = $m2.*p6 ioff4 = $m3.*p6 irise = p7 idec = p8 ...
Now let's click on "CSD -> Meta-Settings -> take a snapshot"; we give it the name "original" at the minibuffer.
Here is what gets inserted somewhere at the beginning of the buffer:
<ELISP> ;SNAPSHOT: original [export] [restore] [see/hide] (require 'csound-eel) (cseel-restore-snapshot '((:define "m3" :value "4" :in orc) (:define "m2" :value "3" :in orc) (:define "m1" :value "2" :in orc))) </ELISP> <CsoundSynthesizer> #define m3 #4# #define m2 #3# #define m1 #2# ...
The [...] parts in the first line should appear as buttons. If for some reason it is not the case, then clicking “EEL -> Wake up embedded buttons” should do the trick.
Now when clicking on the [restore] button, m1, m2 and m3 will get back to their original values.
Restoring a snapshot is also possible from the "Settings" menu, where a "Restore snapshot" submenu should appear, providing a list of all defined snapshots in the buffer.
So now we can go on and change the definitions for m1, m2, m3 to, say, 2, 4 and 8. Then, taking another snapshot called "248", we get this:
<ELISP> ;SNAPSHOT: 248 [export] [restore] [see/hide] (require 'csound-eel) (cseel-restore-snapshot '((:define "m3" :value "8" :in orc) (:define "m2" :value "4" :in orc) (:define "m1" :value "2" :in orc))) </ELISP> <ELISP> ;SNAPSHOT: original [export] [restore] [see/hide] (require 'csound-eel) (cseel-restore-snapshot '((:define "m3" :value "4" :in orc) (:define "m2" :value "3" :in orc) (:define "m1" :value "2" :in orc))) </ELISP> ...
Now, what about having only the upper frequencies added, or maybe only the lower ones ?
We can do so by defining a meta-menu. We just have to replace
;|menu 1Frequencies ;|all outs1 a1+a2+a3+a4+a5+a6+a7+a8+a9 ;|upper ; outs1 a1+a2+a3+a4+a5 ;|lower ; outs1 a1+a6+a7+a8+a9 ;|endmenu
... and do the same for instrument 2:
;|menu 2Frequencies ;|all outs2 a1+a2+a3+a4+a5+a6+a7+a8+a9 ;|upper ; outs2 a1+a2+a3+a4+a5 ;|lower ; outs2 a1+a6+a7+a8+a9 ;|endmenu
The ;|... comments are meta-comments. If you go to the "Meta-Settings" menu, you will see that there are now two more entries: a submenu "1Frequencies" and a submenu "2Frequencies", both providing the items "all", "upper" and "lower". By selecting those items, you actually comment and uncomment the corresponding sections in the CSD.
Note that this is also recorded when snapshotting. Letīs select "upper" in both instruments, then take a new snapshot "248up". Here is the resulting ELISP area:
<ELISP> ;SNAPSHOT: 248up [export] [restore] [see/hide] (require 'csound-eel) (cseel-restore-snapshot '((:define "m3" :value "8" :in orc) (:define "m2" :value "4" :in orc) (:define "m1" :value "2" :in orc) (:menu "1Frequencies" :type "menu" :item "all" :active nil) (:menu "1Frequencies" :type "menu" :item "upper" :active t) (:menu "1Frequencies" :type "menu" :item "lower" :active nil) (:menu "2Frequencies" :type "menu" :item "all" :active nil) (:menu "2Frequencies" :type "menu" :item "upper" :active t) (:menu "2Frequencies" :type "menu" :item "lower" :active nil))) </ELISP> ...
One more step: silenting out part of the score. Letīs replace
; start idur iamp ifq ioff irise idec i1 0 35 8000 110 0.03 0.07 21 i1 20 20 9600 110 0.04 2 4 i1 28 30 8000 220 0.04 3 6 i1 32.1 23 8000 110 0.03 2.3 4.6 i2 5 20 9600 55 0.02 0.04 12 i2 20 15 8000 220 0.05 1.5 3 i2 32 26 9600 110 0.025 2.6 5.2 i2 36 22 8000 55 0.01 0.04 13 e
; start idur iamp ifq ioff irise idec ;|toggles Score ;|i1 i1 0 35 8000 110 0.03 0.07 21 i1 20 20 9600 110 0.04 2 4 i1 28 30 8000 220 0.04 3 6 i1 32.1 23 8000 110 0.03 2.3 4.6 ;|i2 i2 5 20 9600 55 0.02 0.04 12 i2 20 15 8000 220 0.05 1.5 3 i2 32 26 9600 110 0.025 2.6 5.2 i2 36 22 8000 55 0.01 0.04 13 ;|endtoggles e
This gives birth to a new submenu "Score" in "Meta-Settings", where you can choose weither to include the parts for i1 and i2 in the score.
The difference between ;|menu and ;|toggles is that ;|menu allows only one among its items to be selected at a given time.
Our eventual CSD is now a template where structural choices can be performed in both score and orchestra, and where snapshots make it possible to keep at hand as many combinations of such choices and macro settings as we want. All of this being accessible through the "Meta-Settings" menu.
And because everything happens either in comment lines of in XML areas external to the <CsoundSynthesizer> one, the CSD is still acceptable as a plain input file by csound.
This library implements a set of functions making it very easy to compose a monophonic Csound score in one algorithmic shot.
The basic function is 'i, which insert a i-statement at point. Like this:
(i 3 27 0.5 1500 0.125) which, as you would expect, inserts: i 3 27 0.5 1500 0.125
'i always lives in a specific context, which I call a p-fields stream, because its main purpose is to keep a memory of what was previously inserted
For example, 'i has a sibling function called '|i which accepts a partial list of p-fields:
(|i :dur 0.8)
i 3 27 0.8
... because the implicit top-level stream remembers the previous values of p1 and p2.
Since only p1, p2 and p3 have a defined meaning, the top level stream does not attempt to manage the other p-fields. This has to be done explicitly by defining a local stream:
(with-pfields-stream '((4 (:pitch 8.01)) (5 (:volume 1000)) (6 (:pan 0))) (|i) (|i :start 30 :pitch 8.02) (|i :start 35 :volume 1500)) => i 3 27 0.8 8.01 1000 0 i 3 30 0.8 8.02 1000 0 i 3 35 0.8 8.02 1500 0
What happened here is that p1, p2 and p3 attributes where inherited from the top-level stream, so that again their values are remembered. In this top-level stream, the associated keys are :instr, :start and :dur, with default values 1, 0, 0
To ignore the top-level stream (that's cleaner), just initialize everything:
(with-pfields-stream '((1 (:instr 10)) (2 (:start 0)) (3 (:dur 0.5)) (4 (:pitch 8.01)) (5 (:volume 1000)) (6 (:pan 0))) (|i) (+i :pitch 8.02) (+i :volume 1500)) => i 10 0 0.5 8.01 1000 0 i 10 0.5 0.5 8.02 1000 0 i 10 1 0.5 8.02 1500 0
This example introduces '+i, another sibling of 'i behaving almost exactly like '|i while managing p2 so that the event starts at the end of the previous note.
Note that although the instrument number (p-field 1) may vary, the other p-fields will still have only one current value. This is why I said at the beginning of this page that i.el can be used to generate monophonic scores: each instrument should have its own stream of parameters.
All p-fields values, when set through '|i, '+i and 'with-pfields-stream can also be quoted lisp forms:
(with-pfields-stream '((1 (:instr 10)) (2 (:start 0)) (3 (:dur '(+ 0.5 (* 0.001 (random 100))))) (4 (:pitch 8.01)) (5 (:volume '(+ 1000 (* 100 (now))))) (6 (:pan '(random 127)))) (|i) (+i :pitch 8.02) (+i :volume '(+ 1500 (* 100 (now))))) => i 10 0 0.5950 8.0100 1000 60 i 10 0.5030 0.5120 8.0200 1050.3000 51 i 10 1.0690 0.5800 8.0200 1606.9000 28
This example also introduced 'now, a function returning the current value for p2. Here is an example generating longer and longer notes:
(with-pfields-stream '((1 (:instr 10)) (2 (:start 0)) (3 (:dur '(+ 0.5 (/ (now) 10)))) (4 (:pitch 8.01)) (5 (:volume '(+ 1000 (random 300))))) (dolist (p '(8.01 8.02 7.11 7.05 5.09 6.00 8.00)) (+i :pitch p))) => i 10 0 0.5000 8.0100 1120 i 10 0.5000 0.5500 8.0200 1087 i 10 1.0500 0.6050 7.1100 1208 i 10 1.6550 0.6655 7.0500 1252 i 10 2.3205 0.7320 5.0900 1064 i 10 3.0526 0.8053 6.0000 1109 i 10 3.8578 0.8858 8.0000 1176
There are other functions related to p2:
In the following example we jump around in time:
(with-pfields-stream '((1 (:p1 1)) (2 (:p2 150)) (3 (:dur 1.5)) (4 (:pit 8.01)) (5 (:vol 1000))) (NOTE "Example") (dotimes (bof 3) (+i :pit 7.11) (+i :pit 8.02 :vol ">")) (end-note "now is bingo") (now-> :bingo) (+i :dur 3.5) (+i :dur 0.8 :vol 1400) (note "jumping around:") (t- 3.5) (|i :dur 2 :vol 1500 :pit 9.00) (t+ 35) (|i) (note "back in time to bingo:") (now-be :bingo) (|i) (end-of-section))
This inserts the following score:
/* Example */ i 1 150 1.5 7.11 1000 i 1 151.5 1.5 8.02 > i 1 153.0 1.5 7.11 > i 1 154.5 1.5 8.02 > i 1 156.0 1.5 7.11 > i 1 157.5 1.5 8.02 > ; now is bingo i 1 159.0 3.5 8.02 > i 1 162.5 0.8 8.02 1400 ; jumping around: i 1 159.0 2 9.0 1500 i 1 194.0 2 9.0 1500 ; back in time to bingo: i 1 157.5 2 9.0 1500 e
As you can see, the stream continuity is a textual one, it is not time-wise: going back in time does not restore the previous default p-fields. This is an important point.
Defines a set of local variables used to maintain a stream of p-fields, then process BODY within that stream. The stream is effected by the insertion functions |i, +i and i
P-FIELDS is the local value of 'pfstream-pf-attributes. Format: '((NP (:KEY DEFAULT)) ...) where DEFAULT is either a number, a string or a quoted form
If it is incomplete, it will inherit its missing attributes from the value of 'pfstream-pf-attributes in the outside scope. The top level scope (global) only defines p1, p2 and p3
Insert an i-statement at point, using keys to identify the changing p-fields. The other p-fields values are inherited from the local stream
The following functions are only valid within a `with-pfields-stream' body. This allows for short and simple names without the risk of a name clash. You may use `pfstream-defun' to define a new function of this kind.
Define a new function valid only within a `with-pfields-stream' body. This macro itself can not be evaluated within a `with-pfields-stream' body !
Define an alias for function FULL-NAME, valid only within a pfield-stream
As described in the previous chapter, each p-field is associated to a keyword. It is possible and quite useful to define other keywords, called custom keywords, which are actually macros that expand into property lists, making it possible to set several p-fields at once.
In the following example we define a :side keywords used to set both the volume and the position of the sound:
(with-pfields-stream '((1 (:p1 1)) (2 (:p2 150)) (3 (:dur 1.5)) (4 (:pitch 8.01)) (5 (:volume 1000)) (6 (:pan 0))) (add-custom-key '(:side (:volume 'identity :pan (lambda (v) (/ v 1000.0))))) (|i) (+i :pitch 8.02 :side 700) (|i :pitch 8.05 :side 850)) => i 1 150 1.5000 8.0100 1000 0 i 1 151.5000 1.5000 8.0200 700 0.7000 i 1 151.5000 1.5000 8.0500 850 0.8500
Register a new custom keyword for setting pfields in the stream NEW-KEY-DEF is a list (KEY VAL) where VAL is a plist or a function of one variable returning a plist. KEY will be expanded into that plist. when VAL is a function its single argument will be the value associated to KEY when it is invoked."
'with-pfields-stream create a new stream by copying all the current stream “memory” (p-fields values, time markers, custom keys) into local variables.
Nesting streams allow any of these values to be temporarily modified, all changes being cancelled when jumping back to the parent stream.
As for the implicit top-level stream, it is buffer local. Still, stream-related operations should always be done within an enclosing 'with-pfields-stream form, so that there is no possible contamination of the top level between different chunks of code.
create a new p-field stream with all p-fields values, time markers and custom keys inherited from the current stream. once BODY is evaluated, everything gets restored. equivalent to 'with-pfields-stream with P-FIELDS argument nil
create a new p-field stream with all p-fields values, time markers and custom keys inherited from the current stream. once BODY is evaluated, everything gets restored except the current time which is set to the end time of the exited stream.
The functions i, +i and |i insert an i-statement at point. In some cases though, we may want to defer the insertion. We call “score line” an expression which, when evaluated, does insert an i-statement.
So '(i 1 0 5 200) or '(+i) are score lines.
A list of score lines is a score. Two powerful macros handles scores and score lines:
SPEC is a list (VAR-SYM SCORE [INDEX-SYM] [SCORE-SYM]) evaluate BODY with VAR taking in turn all note values in SCORE (a list) the macro optionaly binds one or two symbols in the scope of BODY: INDEX-SYM is the position of VAR in SCORE (a number starting at 0) SCORE-SYM is bound to SCORE thus VAR is always equal to (nth <INDEX-SYM> <SCORE-SYM>)
Modify EVENT (a score-line) with PLIST, then evaluate the result. Thus (score-line '(+i amp: 200) :dur 1) is like (+i :amp 200 :dur 1) Note that (score-line 'a :vol 127) or (score-line '(i 1 0 2 500)) are also legal syntaxes
'i> is an alias for 'score-line
(with-pfields-stream '((4 (:amp 0)) (5 (:vol 0))) (along-score (n '((i 1 0 5 150 100) (+i :amp 200) (|i :vol 63))) (i> n :duration 8) (end-note n))) => i 1 0 8 150 100 ; (i 1 0 5 150 100) i 1 8 8 200 100 ; (+i :amp 200) i 1 8 8 200 63 ; (|i :vol 63)
At times it is very useful to read the current value of a p-field, or to change it. This can be done with the following functions:
return the current value of P-FIELD (a number or its associated key) in the stream.
note that when it is a lisp form, it is not returned as such: intead, its evaluation as the body of a lambda expression is returned.
use 'current-pfield-form if you want the quoted form itself.
return the current value of P-FIELD (a number or its associated key) in the stream.
when the current value of a p-field is a quoted lisp form, it is returned as such.
use 'current-pfield if you want its current evaluation instead
set the current value of P-FIELD (a number or its associated key) to VAL (a number or a string or a quoted form)
this function does not accept a custom key: use 'set-in-stream instead
set the current values for KEYs to VALs, expanding a KEY if it is a custom key
It is also possible to query a score line (see above for a definition of this term) for the value it defines for a given keyword:
return the value for pfield PF (a number or a key) as associated in EVENT (an i-statement). return nil if PF is not explicitely defined in EVENT. the custom keywords are not expanded; PF may be a custom keyword
return the value for pfield PF (a number or a key) in EVENT (an i-statement). if PF is not explicitely defined, check if it appears when expanding the custom keys. if PF can still not be found in EVENT, return its current value in the stream . PF should not be a custom keyword
k.el is an extension of i.el that defines a specific format for musical notes based on Keykit representation of MIDI data.
In Keykit, musical phrases are represented like this: `ao2v100, e g, f+`
Here the first note has an explicit octave and volume. The following notes inherit from these values. So this format is actually very close to what I called a p-field stream in the discussion of i.el
Keykit notes, because they represent MIDI data, have specific ranges for volume and pitch: those are always integers from 0 to 127. Duration is also expressed with integers, called clicks, with 192 clicks in one second.
The macro 'with-keykit-stream defines and initialises a mapping by the mean of custom keys, so that notes can explicitely refer to :pit :dur :vol :ch and :time, using the Keykit format.
A large set of functions provide a wealth of shortcuts to write such notes. In the following we use four different ways to insert the same musical phrase:
;; explicit invocation of '|i and '+i (with-keykit-stream (|i :pit 57 :dur 150 :vol 90) (+i :pit 58) (+i :pit 59 :ch 2)) ;; using Keykit format for pitches ;; a note prepended by > is delayed to the end of the previous note (with-keykit-stream (ao2 :dur 150 :vol 90) (>b-) (>p59 :ch 2)) ;; with macro 'along-score: (with-keykit-stream (along-score (n '(ao2d150v90 >b- >p59c2)) (i> n))) ;; with macro 'along-keyphrase: (with-keykit-stream (along-keyphrase (n "'ao2d150v90, b-, bc2'") (i> n)))
'along-score is the most powerful enumerating macro: it accepts about any format for notes, as illustrated in the following example
(with-keykit-stream (along-score (n '((i 1 10) >ao2d150v90 (ao3d120 :vol 110) >b- >p59c2 +i (i 3 14 :vol 80))) (i> n))) => i 1 10 0.5000 440.0000 4960.6299 i 1 10.5000 0.7813 220.0000 7086.6142 i 1 10.5000 0.6250 440.0000 7086.6142 i 1 11.1250 0.6250 466.1638 7086.6142 i 2 11.7500 0.6250 246.9417 7086.6142 i 2 12.3750 0.6250 246.9417 7086.6142 i 3 14 0.6250 246.9417 6299.2126
'along-keyphrase actually handles any Keykit expression returning a phrase as argument. That expression is send to lowkey, evaluated there and the result is parsed. So we can write things like:
(with-keykit-stream (along-keyphrase (n "fractal('ao2d600v90, b-, bc2',2)") (i> n)))
The mapping from :vol and :pit is set by default within 'with-keykit-stream, and will need to be defined again in order to fit the orchestra. This can be done either by using functions 'add-custom-key or 'add-custom-keys, like this:
(add-custom-keys '((:vol (:amp (lambda (v) (midi-to-range v 0 300)))) (:pit (:pitch 'midi-to-pch))))
Equivalent and more convenient, we have functions `kstream:vol and 'kstream:pit
(kstream:vol '(:amp (lambda (v) (midi-to-range v 0 300)))) (kstream:pit '(:pitch 'midi-to-pch))
Defines a pfield-stream adapted to the transcription of Keykit phrases into a score
SPEC is a list (VAR KEYPHRASE [INDEX-SYM] [SCORE-SYM]) evaluate BODY with VAR taking in turn all note values in KEYPHRASE (a Keykit expression) the macro optionaly binds one or two symbols in the scope of BODY: 'INDEX-SYM is the order position of VAR in KEYPHRASE (starting at 0) 'SCORE-SYM is the computed list of all notes in KEYPHRASE thus VAR is always equal to (nth <INDEX-SYM> <SCORE-SYM>)
Csound-x can work upside-down: instead of editing Csound files you can directly compose in Emacs Lisp in a way that hides the underlying CSD structure:
(csl-play-composition (csound-composition :ftables (1 :sin 8192) :instr 1 [out (oscili :arate (oscili :krate p4 1/p3 p7) p5 p6)] :score (insert "f 2 0 513 5 1 12 1024 500 1" ?\n) :i-stream (i 1 0 4 8000 440 1 2) (loop for h in '(2200 600 215 1852 990) for d in '(2 2.5 2 1.5 4) do (+i :p5 h :p3 d))))
... this is very much a work in progress and still subject to important changes. please do not rely on the current API.
see doc strings, tests and examples in csound-lsp.el for details
Commands available via M-x prefix.
cscsd-at-point: Basic usage
cscsd-auto-import: Basic usage
cscsd-call-csound: Invocation functions
cscsd-import-from-clipboard: Basic usage
cscsd-interpret-command: Invocation functions
cscsd-process: Invocation functions
cscsd-process-region: Invocation functions
cscsd-wrap-buffer: Basic usage
csdoc-browse-html: Integrated documentation
csdoc-html-document-opcode: Integrated documentation
csdoc-insert-opcode-html-template: Integrated documentation
csdoc-refresh-html: Integrated documentation
Csound-x variables and hooks
cscsd-csound-binary: Csound invocation
cscsd-latest-audio-file: Interactive invocation from CSD
cscsd-orc-delimiter-regexps: Basic usage
cscsd-process-file: Csound invocation
cscsd-sco-delimiter-regexps: Basic usage
cscsd-shell-edit-audio: Interactive invocation from CSD
cscsd-shell-play-audio: Interactive invocation from CSD
Csound-x public functions
+i: The i library
add-custom-key: The i library
add-custom-keys: The i library
along-KeyPhrase: The k library
along-keyphrase: The k library
along-MIDIfile: The k library
along-score: The i library
cmask: Cmask integration
cscsd-at-point: Basic usage
cscsd-auto-import: Basic usage
cscsd-call-csound: Invocation functions
cscsd-import-from-clipboard: Basic usage
cscsd-interpret-command: Invocation functions
cscsd-process: Invocation functions
cscsd-process-region: Invocation functions
cscsd-wrap-buffer: Basic usage
current-pfield: The i library
current-pfield-form: The i library
end-note: The i library
end-of-section: The i library
i: The i library
kstream:pit: The k library
kstream:vol: The k library
NOTE: The i library
note: The i library
now: The i library
now->: The i library
now-be: The i library
pfield: The i library
pfstream-defalias: The i library
pfstream-defun: The i library
save-stream: The i library
save-stream-insert: The i library
scomx-elt: Basic functions
scomx-grab-matrix: Basic functions
scomx-operate-m: Basic functions
scomx-operate-p-nc-nr: Basic functions
scomx-shell-command-with-matrix: Basic functions
scomx-yank-matrix: Basic functions
score-line: The i library
score-line-pfield: The i library
section: The i library
set-in-stream: The i library
set-pfield: The i library
t+: The i library
t-: The i library
then: The i library
then->: The i library
with-alg-defmath: Basic functions
with-defmath: Basic functions
with-defmath-import: Basic functions
with-keykit-stream: The k library
with-pfields-stream: The i library
with-raw-defmath: Basic functions
|i: The i library
MMM-mode is not kind with imenu. Turning on the “Index” menu for all emacs-lisp-mode buffers will cause troubles at time.
Fix: deactivate imenu for emacs-lisp-mode buffers