Skip to content

Instantly share code, notes, and snippets.

@HelloWorld017
Created April 2, 2025 12:48
Show Gist options
  • Save HelloWorld017/f93758d534b3115115189fe1a924374e to your computer and use it in GitHub Desktop.
Save HelloWorld017/f93758d534b3115115189fe1a924374e to your computer and use it in GitHub Desktop.
Convert your Songsterr GuitarPro (*.gp) files suitable for Musescore Editing.
<!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>
@HelloWorld017
Copy link
Author

HelloWorld017 commented Apr 2, 2025

Drumsterr

Convert your Songsterr GuitarPro (*.gp) files suitable for Musescore Editing.

Link

Drumsterr

Usage

  1. Export your score in Songsterr
  2. Drag it to the Drumsterr
  3. Download converted file and open it in Musescore
  4. Go to layout tab in your sidebar, click the config icon, and click Replace instrument
  5. Find Drum kit (common).
  6. To remove accidentals (which are shown by the Musescore bug), follow these steps.
    1. Click an accidental, right click and select Select > Similar
    2. Go to properties tab in your sidebar, uncheck the Visible checkbox.

@HelloWorld017
Copy link
Author

HelloWorld017 commented Apr 2, 2025

How it works

The Songsterr-exported *.gp files does not have MIDI mapping.
So directly replacing the instrument yields this unwanted result:
image

This application adds the missing MIDI mappings.

Video Tutorial for Replacing Instrument

OeOXUtEEnV.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment