http://mathling.com/music/noise library module
http://mathling.com/music/noise
Music: Using music as a noise-generating function.
All noise functions are 2D.
Input points will be modded to [0, sample] range, so scale to that range
if you want to avoid mod-based repetitions.
Copyright© Mary Holstege 2023
CC-BY (https://creativecommons.org/licenses/by/4.0/)
Status: Bleeding edge
Imports
http://mathling.com/noise/annotationsimport module namespace ann="http://mathling.com/noise/annotations" at "../noise/annotations.xqy"http://mathling.com/music
import module namespace music="http://mathling.com/music" at "../music/music.xqy"http://mathling.com/core/utilities
import module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"http://mathling.com/core/vector
import module namespace v="http://mathling.com/core/vector" at "../core/vector.xqy"http://mathling.com/geometric/rectangle
import module namespace box="http://mathling.com/geometric/rectangle" at "../geo/rectangle.xqy"http://mathling.com/image/matrix
import module namespace matrix="http://mathling.com/image/matrix" at "../image/matrix.xqy"http://mathling.com/core/errors
import module namespace errors="http://mathling.com/core/errors" at "../core/errors.xqy"http://mathling.com/geometric/point
import module namespace point="http://mathling.com/geometric/point" at "../geo/point.xqy"
Functions
Function: context
declare function context($sources as xs:string*,
$sample as xs:integer,
$aligned as xs:boolean) as map(xs:string,item()*)
declare function context($sources as xs:string*, $sample as xs:integer, $aligned as xs:boolean) as map(xs:string,item()*)
context()
Create context for the music noise.
Params
- sources as xs:string*: locations of source MusicXML scores
- sample as xs:integer: how large a sample grid to use to map the score onto
- aligned as xs:boolean: should parts be aligned or sequential? (default=true())
Returns
- map(xs:string,item()*): context object
declare function this:context( $sources as xs:string*, $sample as xs:integer, $aligned as xs:boolean ) as map(xs:string,item()*) { map { "sources": $sources, "sample": $sample, "aligned": $aligned }=>this:recalculate() }
Function: context
declare function context($sources as xs:string*,
$sample as xs:integer) as map(xs:string,item()*)
declare function context($sources as xs:string*, $sample as xs:integer) as map(xs:string,item()*)
Params
- sources as xs:string*
- sample as xs:integer
Returns
- map(xs:string,item()*)
declare function this:context( $sources as xs:string*, $sample as xs:integer ) as map(xs:string,item()*) { map { "sources": $sources, "sample": $sample, "aligned": true() }=>this:recalculate() }
Function: sources
declare function sources($context as map(xs:string,item()*),
$sources as xs:string*) as map(xs:string,item()*)
declare function sources($context as map(xs:string,item()*), $sources as xs:string*) as map(xs:string,item()*)
sources()
Setter for sources. Will recalculate matrix.
Params
- context as map(xs:string,item()*): context object
- sources as xs:string*: sequence of locations of MusicXML scores
Returns
- map(xs:string,item()*): context object with new sources and recalculated matrix
declare function this:sources( $context as map(xs:string,item()*), $sources as xs:string* ) as map(xs:string,item()*) { $context=>map:put("sources", $sources)=>this:recalculate() }
Function: sources
declare function sources($context as map(xs:string,item()*)) as xs:string*
declare function sources($context as map(xs:string,item()*)) as xs:string*
sources()
Accessor for sources.
Params
- context as map(xs:string,item()*): context object
Returns
- xs:string*: sequence of locations of MusicXML scores
declare function this:sources( $context as map(xs:string,item()*) ) as xs:string* { $context("sources") }
Function: sample
declare function sample($context as map(xs:string,item()*),
$sample as xs:double) as map(xs:string,item()*)
declare function sample($context as map(xs:string,item()*), $sample as xs:double) as map(xs:string,item()*)
sample()
Setter for grid size. Will recalculate matrix.
Params
- context as map(xs:string,item()*): context object
- sample as xs:double: size of sample grid to map music to
Returns
- map(xs:string,item()*): context object with sample size and recalculated matrix
declare function this:sample( $context as map(xs:string,item()*), $sample as xs:double ) as map(xs:string,item()*) { $context=>map:put("sample", $sample)=>this:recalculate() }
Function: sample
declare function sample($context as map(xs:string,item()*)) as xs:double
declare function sample($context as map(xs:string,item()*)) as xs:double
sample()
Accessor for grid size.
Params
- context as map(xs:string,item()*): context object
Returns
- xs:double: size of sample grid to map music to
declare function this:sample( $context as map(xs:string,item()*) ) as xs:double { $context("sample") }
Function: aligned
declare function aligned($context as map(xs:string,item()*),
$aligned as xs:boolean) as map(xs:string,item()*)
declare function aligned($context as map(xs:string,item()*), $aligned as xs:boolean) as map(xs:string,item()*)
aligned()
Setter for alignement flag. Will recalculate matrix.
Params
- context as map(xs:string,item()*): context object
- aligned as xs:boolean
Returns
- map(xs:string,item()*): context object with new alignment flag and recalculated matrix
declare function this:aligned( $context as map(xs:string,item()*), $aligned as xs:boolean ) as map(xs:string,item()*) { $context=>map:put("aligned", $aligned)=>this:recalculate() }
Function: aligned
declare function aligned($context as map(xs:string,item()*)) as xs:boolean
declare function aligned($context as map(xs:string,item()*)) as xs:boolean
aligned()
Accessor for alignment flag.
Params
- context as map(xs:string,item()*): context object
Returns
- xs:boolean: whether measures are aligned between parts, or mapped from parts sequentially
declare function this:aligned( $context as map(xs:string,item()*) ) as xs:boolean { $context("aligned") }
Function: matrix
declare function matrix($context as map(xs:string,item()*)) as map(*)
declare function matrix($context as map(xs:string,item()*)) as map(*)
matrix()
Accessor for mapped measure matrix.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): mapped matrix of measures
declare function this:matrix( $context as map(xs:string,item()*) ) as map(*) { $context("matrix") }
Function: scores
declare function scores($context as map(xs:string,item()*)) as map(xs:string,item()*)*
declare function scores($context as map(xs:string,item()*)) as map(xs:string,item()*)*
scores()
Accessor for parsed MusicXML scores.
Params
- context as map(xs:string,item()*): context object
Returns
- map(xs:string,item()*)*: parsed scores
declare function this:scores( $context as map(xs:string,item()*) ) as map(xs:string,item()*)* { $context("scores") }
Function: noise
declare function noise($context as map(xs:string,item()*)) as map(*)
declare function noise($context as map(xs:string,item()*)) as map(*)
noise()
Get the default noise function whose value is based on sum of pitches
for location in score mapped to input point.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): callable noise function (point form)
declare %art:noise function this:noise( $context as map(xs:string,item()*) ) as map(*) { ann:f("music:noise", function($point as map(xs:string,item()*)) as xs:double { this:pitch(point:pcoordinates($point), $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }
Function: noise-vector
declare function noise-vector($context as map(xs:string,item()*)) as map(*)
declare function noise-vector($context as map(xs:string,item()*)) as map(*)
noise-vector()
Get the default noise function whose value is based on sum of pitches
for location in score mapped to input point.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): callable noise function (vector form)
declare %art:noise function this:noise-vector( $context as map(xs:string,item()*) ) as map(*) { ann:fv("music:noise", function($point as xs:double*) as xs:double { this:pitch($point, $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }
Function: noise-density
declare function noise-density($context as map(xs:string,item()*)) as map(*)
declare function noise-density($context as map(xs:string,item()*)) as map(*)
noise-density()
Get the default noise function whose value is based on the count of notes
active at location in score mapped to input point.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): callable noise function (point form)
declare %art:noise function this:noise-density( $context as map(xs:string,item()*) ) as map(*) { ann:f("music:noise", function($point as map(xs:string,item()*)) as xs:double { this:density(point:pcoordinates($point), $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }
Function: noise-density-vector
declare function noise-density-vector($context as map(xs:string,item()*)) as map(*)
declare function noise-density-vector($context as map(xs:string,item()*)) as map(*)
noise-density-vector()
Get the default noise function whose value is based on the count of notes
active at location in score mapped to input point.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): callable noise function (vector form)
declare %art:noise function this:noise-density-vector( $context as map(xs:string,item()*) ) as map(*) { ann:fv("music:noise", function($point as xs:double*) as xs:double { this:density($point, $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }
Function: noise-extreme
declare function noise-extreme($context as map(xs:string,item()*)) as map(*)
declare function noise-extreme($context as map(xs:string,item()*)) as map(*)
noise-extreme()
Get the default noise function whose value is based on the most extreme pitch
of notes active at location in score mapped to input point.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): callable noise function (point form)
declare %art:noise function this:noise-extreme( $context as map(xs:string,item()*) ) as map(*) { ann:f("music:noise", function($point as map(xs:string,item()*)) as xs:double { this:extreme(point:pcoordinates($point), $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }
Function: noise-extreme-vector
declare function noise-extreme-vector($context as map(xs:string,item()*)) as map(*)
declare function noise-extreme-vector($context as map(xs:string,item()*)) as map(*)
noise-extreme-vector()
Get the default noise function whose value is based on the most extreme pitch
of notes active at location in score mapped to input point.
Params
- context as map(xs:string,item()*): context object
Returns
- map(*): callable noise function (vector form)
declare %art:noise function this:noise-extreme-vector( $context as map(xs:string,item()*) ) as map(*) { ann:fv("music:noise", function($point as xs:double*) as xs:double { this:extreme($point, $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }
Original Source Code
xquery version "3.1"; (:~ : Music: Using music as a noise-generating function. : All noise functions are 2D. : Input points will be modded to [0, sample] range, so scale to that range : if you want to avoid mod-based repetitions. : : Copyright© Mary Holstege 2023 : : CC-BY (https://creativecommons.org/licenses/by/4.0/) : @since June 2023 : @custom:Status Bleeding edge :) module namespace this="http://mathling.com/music/noise"; import module namespace errors="http://mathling.com/core/errors" at "../core/errors.xqy"; import module namespace util="http://mathling.com/core/utilities" at "../core/utilities.xqy"; import module namespace ann="http://mathling.com/noise/annotations" at "../noise/annotations.xqy"; import module namespace v="http://mathling.com/core/vector" at "../core/vector.xqy"; import module namespace matrix="http://mathling.com/image/matrix" at "../image/matrix.xqy"; import module namespace point="http://mathling.com/geometric/point" at "../geo/point.xqy"; import module namespace box="http://mathling.com/geometric/rectangle" at "../geo/rectangle.xqy"; import module namespace music="http://mathling.com/music" at "../music/music.xqy"; declare namespace art="http://mathling.com/art"; declare namespace map="http://www.w3.org/2005/xpath-functions/map"; declare namespace array="http://www.w3.org/2005/xpath-functions/array"; declare namespace math="http://www.w3.org/2005/xpath-functions/math"; declare namespace saxon="http://saxon.sf.net/"; (:~ : context() : Create context for the music noise. : : @param $sources: locations of source MusicXML scores : @param $sample: how large a sample grid to use to map the score onto : @param $aligned: should parts be aligned or sequential? (default=true()) : @return context object :) declare function this:context( $sources as xs:string*, $sample as xs:integer, $aligned as xs:boolean ) as map(xs:string,item()*) { map { "sources": $sources, "sample": $sample, "aligned": $aligned }=>this:recalculate() }; declare function this:context( $sources as xs:string*, $sample as xs:integer ) as map(xs:string,item()*) { map { "sources": $sources, "sample": $sample, "aligned": true() }=>this:recalculate() }; (:~ : recalulate() : Recalculate the music matrix for the context. : @param $context: context object : @return context object with matrix and parsed scores :) declare %private function this:recalculate( $context as map(xs:string,item()*) ) as map(xs:string,item()*) { let $sample := this:sample($context) let $sources := this:sources($context) let $aligned := this:aligned($context) let $space := box:box(1,1,$sample,$sample) let $width := box:width($space) let $height := box:width($space) let $scores := for $source in $sources return music:parse( doc($source) ) let $m := sum($scores!music:measures(.)) let $p := sum($scores!music:parts(.)) let $k := (floor(math:sqrt($p * $m)) cast as xs:integer) let $all-measures := ( for $score at $s in $scores return ( if ($aligned) then ( let $parts := music:part-list($score) for $measure in 1 to music:measures($score) for $part in $parts return (music:measure-list($part)[$measure])!map:put(., "score", $s) ) else ( for $part in music:part-list($score) return music:measure-list($part)!map:put(., "score", $s) ) ) ) let $limit := $k * ($p * $m idiv $k) let $matrix := matrix:array($p * $m idiv $k, $k, ($all-measures, $all-measures)[position() <= $limit]) return ( $context=>map:put("matrix", $matrix)=>map:put("scores", $scores) ) }; (:~ : sources() : Setter for sources. Will recalculate matrix. : : @param $context: context object : @param $sources: sequence of locations of MusicXML scores : @return context object with new sources and recalculated matrix :) declare function this:sources( $context as map(xs:string,item()*), $sources as xs:string* ) as map(xs:string,item()*) { $context=>map:put("sources", $sources)=>this:recalculate() }; (:~ : sources() : Accessor for sources. : : @param $context: context object : @return sequence of locations of MusicXML scores :) declare function this:sources( $context as map(xs:string,item()*) ) as xs:string* { $context("sources") }; (:~ : sample() : Setter for grid size. Will recalculate matrix. : : @param $context: context object : @param $sample: size of sample grid to map music to : @return context object with sample size and recalculated matrix :) declare function this:sample( $context as map(xs:string,item()*), $sample as xs:double ) as map(xs:string,item()*) { $context=>map:put("sample", $sample)=>this:recalculate() }; (:~ : sample() : Accessor for grid size. : : @param $context: context object : @return size of sample grid to map music to :) declare function this:sample( $context as map(xs:string,item()*) ) as xs:double { $context("sample") }; (:~ : aligned() : Setter for alignement flag. Will recalculate matrix. : : @param $context: context object : @param $sources: whether to align measures between parts, or map measures from parts sequentially : @return context object with new alignment flag and recalculated matrix :) declare function this:aligned( $context as map(xs:string,item()*), $aligned as xs:boolean ) as map(xs:string,item()*) { $context=>map:put("aligned", $aligned)=>this:recalculate() }; (:~ : aligned() : Accessor for alignment flag. : : @param $context: context object : @return whether measures are aligned between parts, or mapped from parts sequentially :) declare function this:aligned( $context as map(xs:string,item()*) ) as xs:boolean { $context("aligned") }; (:~ : matrix() : Accessor for mapped measure matrix. : : @param $context: context object : @return mapped matrix of measures :) declare function this:matrix( $context as map(xs:string,item()*) ) as map(*) { $context("matrix") }; (:~ : scores() : Accessor for parsed MusicXML scores. : : @param $context: context object : @return parsed scores :) declare function this:scores( $context as map(xs:string,item()*) ) as map(xs:string,item()*)* { $context("scores") }; (:~ : notes() : Get the set of notes in play at the given offset in the given measure. : : @param $measure: index of measure in full list of measures : @param $offset: position in the measure (a fraction between 0 and 1) : @return the set of non-grace notes and rests active at that point in the measure :) declare %private function this:notes( $measure as map(xs:string,item()*), $offset as xs:double ) as map(xs:string,item()*)* { music:notes($measure)[ (music:kind(.)="rest" or not(music:grace(.))) and util:twixt(music:start(.), $offset, music:start(.)+music:duration(.)) ] }; (:~ : score() : Return the score index for the given measure. : : @param $measure: measure object : @return index of the score object that contributed this measure :) declare %private function this:score( $measure as map(xs:string,item()*) ) as xs:integer { $measure("score") }; (:~ : noise() : Get the default noise function whose value is based on sum of pitches : for location in score mapped to input point. : : @param $context: context object : @return callable noise function (point form) :) declare %art:noise function this:noise( $context as map(xs:string,item()*) ) as map(*) { ann:f("music:noise", function($point as map(xs:string,item()*)) as xs:double { this:pitch(point:pcoordinates($point), $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }; (:~ : noise-vector() : Get the default noise function whose value is based on sum of pitches : for location in score mapped to input point. : : @param $context: context object : @return callable noise function (vector form) :) declare %art:noise function this:noise-vector( $context as map(xs:string,item()*) ) as map(*) { ann:fv("music:noise", function($point as xs:double*) as xs:double { this:pitch($point, $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }; (:~ : noise-density() : Get the default noise function whose value is based on the count of notes : active at location in score mapped to input point. : : @param $context: context object : @return callable noise function (point form) :) declare %art:noise function this:noise-density( $context as map(xs:string,item()*) ) as map(*) { ann:f("music:noise", function($point as map(xs:string,item()*)) as xs:double { this:density(point:pcoordinates($point), $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }; (:~ : noise-density-vector() : Get the default noise function whose value is based on the count of notes : active at location in score mapped to input point. : : @param $context: context object : @return callable noise function (vector form) :) declare %art:noise function this:noise-density-vector( $context as map(xs:string,item()*) ) as map(*) { ann:fv("music:noise", function($point as xs:double*) as xs:double { this:density($point, $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }; (:~ : noise-extreme() : Get the default noise function whose value is based on the most extreme pitch : of notes active at location in score mapped to input point. : : @param $context: context object : @return callable noise function (point form) :) declare %art:noise function this:noise-extreme( $context as map(xs:string,item()*) ) as map(*) { ann:f("music:noise", function($point as map(xs:string,item()*)) as xs:double { this:extreme(point:pcoordinates($point), $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }; (:~ : noise-extreme-vector() : Get the default noise function whose value is based on the most extreme pitch : of notes active at location in score mapped to input point. : : @param $context: context object : @return callable noise function (vector form) :) declare %art:noise function this:noise-extreme-vector( $context as map(xs:string,item()*) ) as map(*) { ann:fv("music:noise", function($point as xs:double*) as xs:double { this:extreme($point, $context) }, $context=>map:remove("matrix")=>map:remove("scores") ) }; (:~ : pitch() : Workhorse for default noise function. : : @param $point: point to map to noise value (vector) : @param $context: context object : @return noise value based on sum of pitches of notes at mapped location in score :) declare %private function this:pitch( $point as xs:double*, $context as map(xs:string,item()*) ) as xs:double { let $sample := this:sample($context) let $matrix := this:matrix($context) let $scores := this:scores($context) let $rows := matrix:rows($matrix) let $columns := matrix:columns($matrix) let $xf := util:mix(1, $columns, (v:px($point) mod $sample) div $sample) let $x := floor($xf) cast as xs:integer let $y := util:intmix(1, $rows, (v:py($point) mod $sample) div $sample) let $measure := $matrix=>matrix:get($y, $x) let $m := $measure=>music:measure() let $offset := $xf - $x let $notes := this:notes($measure, $offset) let $n := count($notes) return ( sum( for $note in $notes let $score := $scores[$measure("score")] return ( util:mix(-1.0 div $n, 1.0 div $n, if (music:kind($note)="rest") then 0 else (music:pitch($note) - music:min-octave($score)) div music:octaves($score) ) ) ) ) }; (:~ : density() : Workhorse for density noise function. : : @param $point: point to map to noise value (vector) : @param $context: context object : @return noise value based on number notes at mapped location in score :) declare %private function this:density( $point as xs:double*, $context as map(xs:string,item()*) ) as xs:double { let $sample := this:sample($context) let $matrix := this:matrix($context) let $rows := matrix:rows($matrix) let $columns := matrix:columns($matrix) let $xf := util:mix(1, $columns, (v:px($point) mod $sample) div $sample) let $x := floor($xf) cast as xs:integer let $y := util:intmix(1, $rows, (v:py($point) mod $sample) div $sample) let $measure := $matrix=>matrix:get($y, $x) let $m := $measure=>music:measure() let $offset := $xf - $x let $notes := this:notes($measure, $offset) let $n := count($notes) return ( sum( for $note in $notes return ( util:mix(-1.0 div $n, 1.0 div $n, if (music:kind($note)="rest") then 0 else music:duration($note) ) ) ) ) }; (:~ : extreme() : Workhorse for default noise function. : : @param $point: point to map to noise value (vector) : @param $context: context object : @return noise value based on most extreme pitch of notes at mapped location in score :) declare %private function this:extreme( $point as xs:double*, $context as map(xs:string,item()*) ) as xs:double { let $sample := this:sample($context) let $matrix := this:matrix($context) let $scores := this:scores($context) let $rows := matrix:rows($matrix) let $columns := matrix:columns($matrix) let $xf := util:mix(1, $columns, (v:px($point) mod $sample) div $sample) let $x := floor($xf) cast as xs:integer let $y := util:intmix(1, $rows, (v:py($point) mod $sample) div $sample) let $measure := $matrix=>matrix:get($y, $x) let $m := $measure=>music:measure() let $offset := $xf - $x let $notes := this:notes($measure, $offset) let $n := count($notes) return ( if ($n = 0) then 0 else util:extreme( for $note in $notes let $score := $scores[$measure("score")] return ( util:mix(-1.0, 1.0, if (music:kind($note)="rest") then 0 else (music:pitch($note) - music:min-octave($score)) div music:octaves($score) ) ) ) ) };