[Open all] [Close all]

CVS Import/Export Standard Humdrum files are encoded as [TSV](https://en.wikipedia.org/wiki/Tab-separated_values) (Tab Separated Values). The minHumdurm library can also read and write data in the [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) (Comma Separate Values) format. This serves two purposes: (1) for people who cannot wrap their minds around the concept of a tab character, and (2) for text processing which may munge tabs (particularly when copy/pasting Humdrum files into email messages). Use the HumdrumFile::printCSV funtion to print Humdrum data in the CSV format. The example file in CSV format:
``` **kern **kern *M4/4 *M4/4 8C 12d . 12e 8B . . 12f * *^ 4A 2g 4d 4G . 4c * *v *v = = *- *- ``` ``` **kern,**kern *M4/4,*M4/4 8C,12d .,12e 8B,. .,12f *,*^ 4A,2g,4d 4G,.,4c *,*v,*v =,= *-,*- ```
CSV files can be read into a HumdrumFile object by appending "CSV" to the various read functions, such as readCSV() and parseCSV().


XML Export The humlib library includes a prototype for a Humdrum XML representation; a sample conversion given below. The XML data includes automatic analytical markup done internally within humlib classes. HumdrumFile objects map to <sequence> elements. HumdrumLine objects map to <frame> elements if HumdrumLine::hasSpines() returns true; otherwise map to <metaFrame> elements if HumdrumLine is a reference record, global comment, or empty record. HumdrumToken objects map to <field> elements. Duration values are given as floating point numbers. When there is a fractional part to a durational value, the fractional part is also expressed as a rational number with the @ratfrac attribute. For example if there is a durational attribute float="1.66667", then there will be an additional attribute ratfrac="2/3" which means that the fractional part of the duration can be precisely expressed as 2/3. ``` !!!OTL: Test piece **kern **kern **text !! This is a "global comment" *M4/4 *M4/4 * =1- =1- =1- 8C 12d This . 12e . 8B . . . 12f . * *^ * 4A 2g 4d is 4G . 4c some * *v *v * 4B 4b text = = = *- *- *- ``` ```xml 17 6 3 </sequenceInfo> reference OTL Test piece </metaFrame> 3 interpretation kern manipulator note kern manipulator note text manipulator global-comment </metaFrame> 3 interpretation kern interpretation kern interpretation text null 3 barline kern barline kern barline text barline 3 data kern data note kern data note text data 3 data kern null kern data note text null 3 data kern data note kern null text null 3 data kern null kern data note text null 3 interpretation kern null kern manipulator text null 4 local-comment kern local-comment note kern null kern null text null 4 data kern data note kern data note kern data note text data 4 data kern data note kern null kern data note text data 4 interpretation kern null kern manipulator kern manipulator text null 3 data kern data note kern data note text data 3 barline kern barline kern barline text barline 3 interpretation kern manipulator kern manipulator text manipulator </frames> </sequence> ``` </details>
Parameters humlib can processes non-data parameters embedded in local or global comments before the data token, interpretation or barline in a spine. The form of a parameter is: ``` ![!][ns1]:[ns2]:key1=[value1][:key2=value[:key3=value3] ``` The parameter system includes two optional namespace prefixes to the list of parameters. A parameter in the default namespace will start with two colons (:), followed by the parameter list. For example here is a parameter list which does not include any namespace qualifiers: ``` !::A=a:B=b ``` With one namespace qualifier, the form would look like this: ``` !:C:A=a:B=b ``` And with a full name space description: ``` !:D:C:A=a:B=b ``` Namespaces and keys may not contain spaces or colons, and ideally should only contain letters, digits, dashes (-), and underscores (_). Values may contain any character except for colons or newlines. To represent a colon, use `:` in place of `:`. When the minHumdurm parser reads a Humdrum file with parameters, it will automatically parse the parameters and attaches them to the next non-null token in the spine which follows. These parameters can then be accessed from a HumdrumToken with the functions: * HumHash::getValue which returns the string for the parameter, * HumHash::getValueInt for parsing it as an integer, * HumHash::getValueBool for a boolean, * HumHash::getValueFloat for a double, or * HumHash::getValueFraction as a HumHash fraction. Here is an example Humdrum file which adds the parameter key/value "Z=z" for a data token: ``` **A a1 !::Z=z a2 a3 *- ``` The following code would print the value of the Z parameter ("z"): ``` HumdrumFile infile; cout << infile.token(3,0).getValue("Z"); ``` The list of parameters for a token are exposed when generating a HumdruXML file for the data. will be given as a child of the <field> element that represents the data token. Here is the field element for the "a2" in XML format: ``` A data ``` The parameter@idref attribute is an XML id which points to the source line used to set the parameter. Parameters may be split into separate comments. These two forms are equivalent: ``` **A a1 !::Z=z:Y=y a2 a3 *- ``` ``` **A a1 !::Z=z !::Y=y a2 a3 *- ``` This will cause token `a2` to have two parameters `Z=z` and `Y=y`. When the same key is used to set the parameter for a token, only the first value of the parameter will be used. In the following example, `.getValue("Z")` will return `z1`: ``` **A a1 !::Z=z1 !::Z=z2 a2 a3 *- ``` Example with namespaces: ``` **A a1 !ns1:ns2:Z=z1 a2 a3 *- ``` To access the parameter for `a2`, the following two cases are equivalent: ``` infile.token(3, 0).getValue("ns1:ns2:Z"); infile.token(3, 0).getValue("ns1", "ns2", Z"); ``` If a parameter key is not followed by a parameter, it's value is set to the string "true" which also returns a true when accessing the .getValueBool() function.
Layout parameters Layout parameters can be embedded in \*\*kern data that give graphical printing information which is not otherwise specified in the data. Layout codes have the form: ``` ![!]LO:category:key=value ``` Local comments, starting with a single `!` indicate a local layout parameter which alters the next non-null token in a spine. Global comments, starting with `!!`, indicate that the parameter effect all parts.

Slur orientation

Slurs can be forced above notes by placing the local layout code `!LO:S:a` before the token staring a slur (which is an open parenthesis in \*\*kern data). Use `!LO:S:b` to force the slur below the group of notes.
<img src=/images/layout/slur.png>

Generic text directions

Any arbitrary text can be placed in the music by using parameters in the `!LO:TX:` namespace:

!LO:TX:b:X=-15:Z=15:t=ad lib.!
!LO:TX:i:rj:X=20:t=D.C. al fine!

Text parameters:

  • t = text to display.
  • rj = right justify text (boolean).
  • cj = center justify text (boolean).
  • b = bold text.
  • X = horizontal offset of text.
  • Y = placement height below bottom line of staff.
  • Z = placement height above top line of staff. </ul> </td></tr></table> </p> </details>
    Rational rhythms The \*\*recip data type in the standard Humdrum Toolkit cannot represent non-integral subdivisions of whole notes, and has difficulty representing rhythms larger than a whole note. The humlib library understands a rational-number extension to \*\*recip data (i.e., **kern rhythms). For example, triplet whole notes are equivalent to 2/3rds of a whole note. This is represented as `3%2` in the extended **recip format, where the denominator of the whole-note division is given first, followed by a percent sign, and then the numerator. By extension, a quarter note which is usually represented as `4` can alternately be be represented as `4%1` in the extended format. <a href=http://www.humdrum.org/humextra/rhythm/#extensions-to-recip-and-kern-rhythms>More info</a>.
    Strands All tokens in a Humdrum file can be uniquely iterated through with two methods. The most straightforward way is to iterate through each line in the data, and then for each line, iterating through each field on the line. Here is some example code which will print the Humdrum file which will be identical to an input TSV Humdrum file. ```cpp HumdrumFile infile; for (int i=0; i<infile.getLineCount(); i++) { for (int j=0; j<infile[i].getFieldCount(); j++) { cout << infile.token(i, j); // or: cout << infile[i].token(j); if (j < infile[i].getFieldCount() { cout << '\t'; } } cout << '\n'; } ``` This format of iterating through the file is suitable for temporal sequence processing of all spine tokens at the same time. The lines in the file represent a sequence where the lines are sorted in time order. To iterate through spines in an orthogonal manner, the humlib library introduces a concept of strands. A strand is a sequence of tokens in a particular spine which does not include spine splits or merges. The following figure shows an example Humdrum data stream with individual strands given different colors. <img width=100% src="/images/strand.svg"> Each spine consists of a primary strand which is continuous throughout the total length of the spine. When a spine splits into sub-spines, a new strand starts at the beginning of the right-side branch of the split, while the previous strand continues along the left-side branch. The strand segments can be used to iterate through all tokens in the file (excluding non-spine lines, which are global comments and reference records). A one dimensional iteration is illustrated in the following code: ```cpp Humdrum infile; HumdrumToken* tok; for (int i=0; i<infile.getStrandCount(); i++) { tok = infile.getStrandStart(i); while (!tok->isStrandEnd()) { cout << *tok << endl; tok->getNextToken(); } } ``` The above illustration contains seven 1D strands, so the above code will generate this data sequence (removing duplicate adjacent tokens in the sequence): a a a a a1 a1 a a a a a1 a1 a a a2 a2 a2 a2 b b b1 b1 b1 b1 b1 b1 b1,21 b1,21 b b b b b2 b2 b21 b21 b21 b21 b22 b22 b22 b22 b22 b22 c c c c c c c c c c c c c c A two-dimensional iteration through the spine tokens can generate the same ordering. In the following example the strands are first iterated through by spine index, and then by strands in the spines, always starting with the primary strand. ```cpp Humdrum infile; HumdrumToken* tok; for (int i=0; i<infile.getSpineCount(); i++) { for (int j=0; j<infile.getStrandCount(i); j++) { tok = infile.getStrandStart(i, j); while (!tok->isStrandEnd()) { cout << *tok << endl; tok->getNextToken(); } } ``` When spines do not split or merge, then strands are equivalent to spines. The following code examples will generate the same data ordering: ```cpp for (i=0; i<infile.getSpineCount(); i++) { tok = infile.getSpineStart(i); while (!tok->isTerminator()) { cout << *tok << endl; tok = tok->getNextToken(); } } for (int i=0; i<infile.getStrandCount(); i++) { tok = infile.getStrandStart(i); while (!tok->isStrandEnd()) { cout << *tok << endl; tok->getNextToken(); } } ``` Primary strands always start with an exclusive interpretation (interpretation token which starts with \*\* followed by the data type), and are ended with the terminate manipulator (`*-`). Secondary strands always start with a non-exclusive interpretation, and typically end at a merge manipulator (`*v`), although unmerged sub-strands will end with a terminate token.