musicvis-lib
Version:
Music analysis and visualization library
545 lines (540 loc) • 16.3 kB
JavaScript
import patterns from './Patterns.js'
/**
* Maps internal drum names to MusicXML instrument names and note display
*
* @type {Map<string,object>}
*/
export const drumInstrumentMap = new Map([
[
'bass', {
instr: 'P1-I36',
display: ['F', '4'],
notehead: '',
stem: 'down'
}
],
[
'snare', {
instr: 'P1-I39',
display: ['C', '5'],
notehead: '',
stem: 'down'
}
],
[
'hihat-closed', {
instr: 'P1-I43',
display: ['G', '5'],
notehead: 'x',
stem: 'up'
}
],
[
'hihat-pedal', {
instr: 'P1-I45',
display: ['D', '4'],
notehead: 'x',
stem: 'down'
}
],
[
'hihat-open', {
instr: 'P1-I47',
display: ['E', '5'],
notehead: 'x',
stem: 'up'
}
],
[
'cymbal-crash', {
instr: 'P1-I50',
display: ['A', '5'],
notehead: 'x',
stem: 'up'
}
],
[
'cymbal-ride', {
instr: 'P1-I52',
display: ['F', '5'],
notehead: 'up',
stem: 'x'
}
],
[
'cymbal-chinese', {
instr: 'P1-I53',
display: ['B', '5'],
notehead: 'x',
stem: 'up'
}
],
[
'cymbal-splash', {
instr: 'P1-I56',
display: ['B', '5'],
notehead: 'x',
stem: 'up'
}
],
[
'tom-hi', {
instr: 'P1-I51',
display: ['F', '5'],
notehead: '',
stem: 'up'
}
],
[
'tom-mid-hi', {
instr: 'P1-I49',
display: ['F', '5'],
notehead: '',
stem: 'up'
}
],
[
'tom-mid-low', {
instr: 'P1-I48',
display: ['E', '5'],
notehead: '',
stem: 'up'
}
],
[
'tom-low', {
instr: 'P1-I46',
display: ['D', '5'],
notehead: '',
stem: 'up'
}
],
[
'tom-floor-hi', {
instr: 'P1-I44',
display: ['A', '4'],
notehead: '',
stem: 'down'
}
],
[
'tom-floor-low', {
instr: 'P1-I42',
display: ['A', '4'],
notehead: '',
stem: 'up'
}
]
])
/**
* Takes a baseline pattern and moves it to the correct position on the fretboard
*
* @param {string} patternType pattern type
* @param {number} [repeat=1] number of repeats
* @returns {number[][]} array of [string, fret] positions
*/
export function generatePattern (patternType, repeat = 1) {
const pattern = patterns.get(patternType).hits
if (repeat === 1) {
return pattern
}
// Repeat with or without alternative direction
let result = pattern
if (repeat > 1) {
for (let repetition = 1; repetition < repeat; repetition++) {
result = [...result, ...pattern]
}
}
// console.log(pattern);
return result
}
/**
* Generates MusicXML text from a pattern
*
* @param {string} name name
* @param {number} tempo tempo in bpm
* @param {string} timeSig time signature e.g. 4/4
* @param {number[][]} drumHits the output of generatePattern
* @returns {string} MusicXML string
*/
export function generateXml (name, tempo, timeSig, drumHits) {
const credit = 'github.com/fheyen/musicvis-lib'
timeSig = timeSig.split('/').map(d => +d)
const notesPerMeasure = timeSig[0]
let currentMeasure = 1
let currentNoteInMeasure = 1
let measuresString = ''
for (const hit of drumHits) {
if (currentNoteInMeasure > notesPerMeasure) {
// Start new measure
currentMeasure++
currentNoteInMeasure = 1
measuresString = `${measuresString}
</measure>
<measure number="${currentMeasure}">`
}
currentNoteInMeasure++
// Get instrument etc.
if (hit.length === 1) {
const drum = hit[0]
if (drum === 'rest') {
measuresString = `${measuresString}
<note>
<rest/>
<duration>1</duration>
<voice>1</voice>
<type>quarter</type>
</note>`
} else {
const details = drumInstrumentMap.get(drum)
measuresString = `${measuresString}
<note>
<unpitched>
<display-step>${details.display[0]}</display-step>
<display-octave>${details.display[1]}</display-octave>
</unpitched>
<duration>1</duration>
<instrument id="${details.instr}"/>
<voice>1</voice>
<type>quarter</type>
<stem>${details.stem}</stem>${details.notehead === ''
? ''
: `
<notehead>${details.notehead}</notehead>`}
</note>`
}
} else {
// Handle 'chords'
for (const [i, drum] of hit.entries()) {
const details = drumInstrumentMap.get(drum)
measuresString = `${measuresString}
<note>${i > 0
? `
<chord/>`
: ''}
<unpitched>
<display-step>${details.display[0]}</display-step>
<display-octave>${details.display[1]}</display-octave>
</unpitched>
<duration>1</duration>
<instrument id="${details.instr}"/>
<voice>1</voice>
<type>quarter</type>
<stem>${details.stem}</stem>${details.notehead === ''
? ''
: `
<notehead>${details.notehead}</notehead>`}
</note>`
}
}
}
return `
<score-partwise version="3.1">
<work>
<work-title>Drums</work-title>
</work>
<identification>
<encoding>
<software>${credit}</software>
</encoding>
</identification>
<credit page="1">
<credit-words default-x="595.44" default-y="1626.67" justify="center" valign="top" font-size="24">${name}</credit-words>
</credit>
<part-list>
<score-part id="P1">
<part-name>${name}</part-name>
<part-abbreviation>Drs.</part-abbreviation>
<score-instrument id="P1-I36">
<instrument-name>Acoustic Bass Drum</instrument-name>
</score-instrument>
<score-instrument id="P1-I37">
<instrument-name>Bass Drum 1</instrument-name>
</score-instrument>
<score-instrument id="P1-I38">
<instrument-name>Side Stick</instrument-name>
</score-instrument>
<score-instrument id="P1-I39">
<instrument-name>Acoustic Snare</instrument-name>
</score-instrument>
<score-instrument id="P1-I41">
<instrument-name>Electric Snare</instrument-name>
</score-instrument>
<score-instrument id="P1-I42">
<instrument-name>Low Floor Tom</instrument-name>
</score-instrument>
<score-instrument id="P1-I43">
<instrument-name>Closed Hi-Hat</instrument-name>
</score-instrument>
<score-instrument id="P1-I44">
<instrument-name>High Floor Tom</instrument-name>
</score-instrument>
<score-instrument id="P1-I45">
<instrument-name>Pedal Hi-Hat</instrument-name>
</score-instrument>
<score-instrument id="P1-I46">
<instrument-name>Low Tom</instrument-name>
</score-instrument>
<score-instrument id="P1-I47">
<instrument-name>Open Hi-Hat</instrument-name>
</score-instrument>
<score-instrument id="P1-I48">
<instrument-name>Low-Mid Tom</instrument-name>
</score-instrument>
<score-instrument id="P1-I49">
<instrument-name>Hi-Mid Tom</instrument-name>
</score-instrument>
<score-instrument id="P1-I50">
<instrument-name>Crash Cymbal 1</instrument-name>
</score-instrument>
<score-instrument id="P1-I51">
<instrument-name>High Tom</instrument-name>
</score-instrument>
<score-instrument id="P1-I52">
<instrument-name>Ride Cymbal 1</instrument-name>
</score-instrument>
<score-instrument id="P1-I53">
<instrument-name>Chinese Cymbal</instrument-name>
</score-instrument>
<score-instrument id="P1-I54">
<instrument-name>Ride Bell</instrument-name>
</score-instrument>
<score-instrument id="P1-I55">
<instrument-name>Tambourine</instrument-name>
</score-instrument>
<score-instrument id="P1-I56">
<instrument-name>Splash Cymbal</instrument-name>
</score-instrument>
<score-instrument id="P1-I57">
<instrument-name>Cowbell</instrument-name>
</score-instrument>
<score-instrument id="P1-I58">
<instrument-name>Crash Cymbal 2</instrument-name>
</score-instrument>
<score-instrument id="P1-I60">
<instrument-name>Ride Cymbal 2</instrument-name>
</score-instrument>
<score-instrument id="P1-I64">
<instrument-name>Open Hi Conga</instrument-name>
</score-instrument>
<score-instrument id="P1-I65">
<instrument-name>Low Conga</instrument-name>
</score-instrument>
<midi-device port="1"></midi-device>
<midi-instrument id="P1-I36">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>36</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I37">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>37</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I38">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>38</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I39">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>39</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I41">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>41</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I42">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>42</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I43">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>43</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I44">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>44</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I45">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>45</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I46">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>46</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I47">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>47</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I48">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>48</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I49">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>49</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I50">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>50</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I51">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>51</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I52">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>52</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I53">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>53</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I54">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>54</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I55">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>55</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I56">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>56</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I57">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>57</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I58">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>58</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I60">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>60</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I64">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>64</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
<midi-instrument id="P1-I65">
<midi-channel>10</midi-channel>
<midi-program>1</midi-program>
<midi-unpitched>65</midi-unpitched>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
</score-part>
</part-list>
<part id="P1">
<measure number="1" width="309.39">
<print>
<system-layout>
<system-margins>
<left-margin>0.00</left-margin>
<right-margin>-0.00</right-margin>
</system-margins>
<top-system-distance>170.00</top-system-distance>
</system-layout>
</print>
<attributes>
<divisions>1</divisions>
<key>
<fifths>0</fifths>
</key>
<time>
<beats>${timeSig[0]}</beats>
<beat-type>${timeSig[1]}</beat-type>
</time>
<clef>
<sign>percussion</sign>
<line>2</line>
</clef>
</attributes>
<direction placement="above">
<direction-type>
<metronome parentheses="no" default-x="-35.96" relative-y="20.00">
<beat-unit>quarter</beat-unit>
<per-minute>${tempo}</per-minute>
</metronome>
</direction-type>
<sound tempo="${tempo}"/>
</direction>
${measuresString}
</measure>
</part>
</score-partwise>`
}