Created
April 2, 2025 12:48
-
-
Save HelloWorld017/f93758d534b3115115189fe1a924374e to your computer and use it in GitHub Desktop.
Convert your Songsterr GuitarPro (*.gp) files suitable for Musescore Editing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Drumsterr</title> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,[email protected],100..900&display=swap" rel="stylesheet"> | |
<style> | |
*:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) { | |
all: unset; | |
display: revert; | |
} | |
*, *::before, *::after { | |
box-sizing: border-box; | |
} | |
:root { | |
font-size: 12px; | |
} | |
a, button { | |
cursor: revert; | |
} | |
img { | |
max-inline-size: 100%; | |
max-block-size: 100%; | |
} | |
</style> | |
<style> | |
:root { | |
background: #202020; | |
} | |
#app { | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
width: 100%; | |
height: 100svh; | |
max-width: 70rem; | |
margin: 0 auto; | |
color: #ffffff; | |
padding: 4rem; | |
font-family: Inter, system-ui, sans-serif; | |
letter-spacing: -0.01em; | |
} | |
h1 { | |
font-size: 2.4rem; | |
font-weight: 700; | |
margin-bottom: 1.6rem; | |
letter-spacing: -0.02em; | |
} | |
h3 { | |
font-size: 2rem; | |
font-weight: 700; | |
margin-bottom: 1.6rem; | |
letter-spacing: -0.02em; | |
} | |
section { | |
margin-bottom: 3.6rem; | |
} | |
blockquote { | |
border-left: 3px solid #707070; | |
padding-left: 2rem; | |
& p { | |
font-size: 1.5rem; | |
line-height: 2rem; | |
} | |
} | |
p { | |
color: #707070; | |
font-size: 1.4rem; | |
line-height: 1.8rem; | |
} | |
ol { | |
margin-left: 2rem; | |
list-style-type: decimal; | |
} | |
li { | |
font-size: 1.4rem; | |
line-height: 1.8rem; | |
margin: 0.4rem 0; | |
} | |
code { | |
background: #20243a; | |
color: #0080ff; | |
padding: 0.4rem 0.8rem; | |
border-radius: 0.8rem; | |
} | |
.Dropzone { | |
border: 1px solid #404040; | |
border-radius: 1.5rem; | |
padding: 6rem 3rem; | |
text-align: center; | |
transition: all .4s ease; | |
& i { | |
display: inline-flex; | |
font-size: 2.4rem; | |
padding: 1rem; | |
margin-bottom: 3rem; | |
border: 1px solid #404040; | |
border-radius: 1rem; | |
} | |
} | |
.Dropzone--active { | |
border-color: #0080ff; | |
background: #292929; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app"> | |
<h1>Drumsterr</h1> | |
<section> | |
<blockquote> | |
<p>Convert your Songsterr GuitarPro (*.gp) files suitable for Musescore Editing.</p> | |
</blockquote> | |
</section> | |
<h3>Instructions</h3> | |
<section> | |
<ol> | |
<li>Export your score in Songsterr</li> | |
<li>Drag it to this page</li> | |
<li>Download converted file and open it in Musescore</li> | |
<li>Go to layout tab in your sidebar, click the config icon, and click <code>Replace instrument</code></li> | |
<li>Find <code>Drum kit (common)</code>.</li> | |
<li> | |
To remove accidentals (which are shown by the Musescore bug), follow these steps. | |
<ol> | |
<li>click an accidental, click <code>Select > Similar</code></li> | |
<li>Go to properties tab in your sidebar, uncheck the <code>visible</code> checkbox.</li> | |
</ol> | |
</li> | |
</ol> | |
</section> | |
<h3>Converter</h3> | |
<div id="input"></div> | |
</div> | |
<script type="module"> | |
import { createRoot } from 'https://esm.sh/[email protected]/client'; | |
import { html } from 'https://esm.sh/[email protected]/react'; | |
import { useCallback } from 'https://esm.sh/[email protected]'; | |
import { useDropzone } from 'https://esm.sh/[email protected]'; | |
import JSZip from 'https://esm.sh/[email protected]'; | |
import { XMLParser, XMLBuilder } from 'https://esm.sh/[email protected]'; | |
// TODO add elements remapping | |
const DRUM_LOOKUP = { | |
Bass: 36, | |
Snare: 38, | |
SnareRim: 37, | |
Cowbell: 56, | |
CymbalsCrash1: 49, | |
CymbalsCrash2: 57, | |
CymbalsRide1: 51, | |
CymbalsRide2: 59, | |
CymbalsRideCup: 53, | |
CymbalsSplash: 55, | |
HiHatClosed: 42, | |
HiHatOpened: 46, | |
HiHatPedal: 44, | |
TomsLow: 45, | |
TomsMidLow: 47, | |
TomsMid: 48, | |
TomsHigh: 50, | |
TomsFloor: 43, | |
TomsFloorLow: 41, | |
}; | |
const opts = { | |
attributeNamePrefix: '', | |
ignoreAttributes: false, | |
preserveOrder: true | |
}; | |
const parser = new XMLParser(opts); | |
const builder = new XMLBuilder(opts); | |
const alterNode = (target, tagName, originalNode, transformedNode) => { | |
const index = target?.findIndex(item => Object.hasOwn(item, tagName) && item[tagName] === originalNode); | |
return target?.toSpliced(index, index < 0 ? 0 : 1, { [tagName]: transformedNode }); | |
}; | |
const findByTagName = (target, tagName) => | |
target?.find(item => Object.hasOwn(item, tagName))?.[tagName]; | |
const findByAttribute = (target, tagName, [attrName, attrValue]) => | |
target?.find(item => Object.hasOwn(item, tagName) && item[':@']?.[attrName] === attrValue)?.[tagName]; | |
const isDrumNote = properties => { | |
const pitch = findByTagName(findByAttribute(properties, 'Property', ['name', 'ConcertPitch']), 'Pitch'); | |
const step = findByTagName(findByTagName(pitch, 'Step'), '#text'); | |
const octave = findByTagName(findByTagName(pitch, 'Octave'), '#text'); | |
return step === 'C' && octave === -1; | |
}; | |
const addMIDIProperty = properties => { | |
const midiProperty = findByAttribute(properties, 'Property', ['name', 'Midi']); | |
if (midiProperty) return properties; | |
const fretProperty = findByAttribute(properties, 'Property', ['name', 'Fret']); | |
const fret = findByTagName(findByTagName(fretProperty, 'Fret'), '#text'); | |
return [ | |
...properties, | |
{ | |
':@': { name: 'Midi' }, | |
Property: [ | |
{ Number: [{ '#text': fret }] } | |
] | |
} | |
]; | |
}; | |
const transform = root => { | |
const gpif = findByTagName(root, 'GPIF'); | |
const notes = findByTagName(gpif, 'Notes'); | |
const notesTransformed = notes.map(note => { | |
if (!Object.hasOwn(note, 'Note')) return note; | |
const properties = findByTagName(note.Note, 'Properties'); | |
if (!isDrumNote(properties)) return note; | |
const propertiesTransformed = addMIDIProperty(properties); | |
const noteTransformed = alterNode(note.Note, 'Properties', properties, propertiesTransformed); | |
return { ...note, Note: noteTransformed }; | |
}); | |
const gpifTransformed = alterNode(gpif, 'Notes', notes, notesTransformed); | |
const rootTransformed = alterNode(root, 'GPIF', gpif, gpifTransformed); | |
return rootTransformed; | |
}; | |
const download = (url, name) => { | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = name; | |
a.click(); | |
}; | |
const Input = () => { | |
const onDrop = useCallback(async ([file]) => { | |
const zip = await JSZip.loadAsync(file); | |
const content = await zip.file('Content/score.gpif').async('string'); | |
const contentObject = parser.parse(content); | |
const outputBlob = await zip.file('Content/score.gpif', builder.build(transform(contentObject))) | |
.generateAsync({ type: 'blob' }); | |
download(URL.createObjectURL(outputBlob), file.name.replace(/\.gp$/, '-drumsterr.gp')); | |
}, []); | |
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); | |
return html` | |
<div ...${getRootProps()} class="${`Dropzone${ isDragActive ? ' Dropzone--active' : ''}`}"> | |
<input ...${getInputProps()} /> | |
<i> | |
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
<path d="m2 2 8 8"/> | |
<path d="m22 2-8 8"/> | |
<ellipse cx="12" cy="9" rx="10" ry="5"/> | |
<path d="M7 13.4v7.9"/> | |
<path d="M12 14v8"/> | |
<path d="M17 13.4v7.9"/> | |
<path d="M2 9v8a10 5 0 0 0 20 0V9"/> | |
</svg> | |
</i> | |
<h3>Click or Drag & Drop</h3> | |
<p> | |
Drop a Songsterr GuitarPro file here.<br /> | |
The file will not be uploaded and processed in your device. | |
</p> | |
</div> | |
`; | |
}; | |
createRoot(document.querySelector('#input')).render(html`<${Input} />`); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Drumsterr
Link
Drumsterr
Usage
Replace instrument
Drum kit (common)
.Select > Similar
Visible
checkbox.