├── .gitignore ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | tuxguitar-code 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # parse-gp5 3 | 4 | Parser for the Guitar Pro 5 file format. 5 | 6 | Work in progress! Pull requests welcome :) 7 | 8 | ## Example 9 | 10 | ```js 11 | const parse = require('parse-gp5'); 12 | const fs = require('fs'); 13 | 14 | const buf = fs.readFileSync(__dirname + '/tab.gp5'); 15 | const tab = parse(buf); 16 | 17 | console.log(tab); 18 | ``` 19 | 20 | # Kudos 21 | 22 | This is mostly a port of what TuxGuitar has implemented in Java. 23 | 24 | # License 25 | 26 | MIT 27 | 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const versions = [ 4 | 'FICHIER GUITAR PRO v5.00', 5 | 'FICHIER GUITAR PRO v5.10' 6 | ]; 7 | 8 | const QUARTER_TIME = 960; 9 | const QUARTER = 4; 10 | 11 | module.exports = buf => { 12 | let version; 13 | let versionIndex; 14 | 15 | const readVersion = () => { 16 | version = readStringByte(30); 17 | }; 18 | 19 | const readColor = () => { 20 | let color = {}; 21 | color.r = readUnsignedByte(); 22 | color.g = readUnsignedByte(); 23 | color.b = readUnsignedByte(); 24 | skip(1); 25 | return color; 26 | }; 27 | 28 | const isSupportedVersion = (version) => { 29 | for (let i = 0; i < versions.length; i++) { 30 | if (versions[i] === version) { 31 | versionIndex = i; 32 | return true; 33 | } 34 | } 35 | return false; 36 | }; 37 | 38 | const readUnsignedByte = () => { 39 | const num = buf[0] & 0xff; 40 | buf = buf.slice(1); 41 | return num; 42 | }; 43 | 44 | const readByte = () => { 45 | const byte = buf[0]; 46 | buf = buf.slice(1); 47 | return byte; 48 | }; 49 | 50 | const readInt = () => { 51 | const bytes = buf.slice(0, 4); 52 | buf = buf.slice(4); 53 | return ((bytes[3] & 0xff) << 24) | ((bytes[2] & 0xff) << 16) | ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff); 54 | }; 55 | 56 | const readString = (size, len) => { 57 | if (typeof len == 'undefined') len = size; 58 | const bytes = buf.slice(0, size > 0 59 | ? size 60 | : len); 61 | buf = buf.slice(bytes.length); 62 | return bytes.toString('utf8', 0, len >= 0 && len <= bytes.length 63 | ? len 64 | : size); 65 | }; 66 | 67 | const readStringByte = (size) => { 68 | return readString(size, readUnsignedByte()); 69 | }; 70 | 71 | const readStringByteSizeOfInteger = () => { 72 | return readStringByte(readInt() - 1); 73 | }; 74 | 75 | const readStringInteger = () => { 76 | return readString(readInt()); 77 | }; 78 | 79 | const skip = (n) => { 80 | buf = buf.slice(n); 81 | }; 82 | 83 | const readKeySignature = () => { 84 | let keySignature = readByte(); 85 | if (keySignature < 0) keySignature = 7 - keySignature; 86 | return keySignature; 87 | }; 88 | 89 | const readLyrics = () => { 90 | let lyric = {}; 91 | lyric.from = readInt(); 92 | lyric.lyric = readStringInteger(); 93 | for (let i = 0; i < 4; i++) { 94 | readInt(); 95 | readStringInteger(); 96 | } 97 | return lyric; 98 | }; 99 | 100 | const readPageSetup = () => { 101 | skip(versionIndex > 0 102 | ? 49 103 | : 30); 104 | for (let i = 0; i < 11; i++) { 105 | skip(4); 106 | readStringByte(0); 107 | } 108 | }; 109 | 110 | const readChannels = () => { 111 | let channels = []; 112 | for (let i = 0; i < 64; i++) { 113 | let channel = {}; 114 | channel.program = readInt(); 115 | channel.volume = readByte(); 116 | channel.balance = readByte(); 117 | channel.chorus = readByte(); 118 | channel.reverb = readByte(); 119 | channel.phaser = readByte(); 120 | channel.tremolo = readByte(); 121 | channel.bank = i == 9 122 | ? 'default percussion bank' 123 | : 'default bank' 124 | if (channel.program < 0) channel.program = 0; 125 | channels.push(channel); 126 | skip(2); 127 | }; 128 | return channels; 129 | }; 130 | 131 | readVersion(); 132 | if (!isSupportedVersion(version)) throw new Error('unsupported version'); 133 | 134 | const [, major, minor] = /v(\d+)\.(\d+)/.exec(version); 135 | const title = readStringByteSizeOfInteger(); 136 | const subtitle = readStringByteSizeOfInteger(); 137 | const artist = readStringByteSizeOfInteger(); 138 | const album = readStringByteSizeOfInteger(); 139 | const lyricsAuthor = readStringByteSizeOfInteger(); 140 | const musicAuthor = readStringByteSizeOfInteger(); 141 | const copyright = readStringByteSizeOfInteger(); 142 | const tab = readStringByteSizeOfInteger(); 143 | const instructions = readStringByteSizeOfInteger(); 144 | const commentsLen = readInt(); 145 | const comments = []; 146 | for (let i = 0; i < commentsLen; i++) { 147 | comments.push(readStringByteSizeOfInteger()); 148 | } 149 | 150 | const lyricTrack = readInt(); 151 | const lyric = readLyrics(); 152 | 153 | readPageSetup(); 154 | 155 | const tempoValue = readInt(); 156 | 157 | if (versionIndex > 0) skip(1); 158 | 159 | let keySignature = readKeySignature(); 160 | skip(3); 161 | 162 | // octave 163 | readByte(); 164 | 165 | const channels = readChannels(); 166 | 167 | skip(42); 168 | 169 | const measures = readInt(); 170 | const trackCount = readInt(); 171 | 172 | const measureHeaders = []; 173 | let timeSignature = { numerator: 4, denominator: { 174 | value: QUARTER, 175 | division: { enters: 1, times: 1 } 176 | }}; 177 | for (let i = 0; i < measures; i++) { 178 | if (i > 0) skip(1); 179 | let flags = readUnsignedByte(); 180 | let header = {}; 181 | header.number = i+1; 182 | header.start = 0; 183 | header.tempo = 120; 184 | header.repeatOpen = (flags & 0x04) != 0; 185 | if ((flags & 0x01) != 0) timeSignature.numerator = readByte(); 186 | if ((flags & 0x02) != 0) timeSignature.denominator.value = readByte(); 187 | header.timeSignature = JSON.parse(JSON.stringify(timeSignature)); 188 | if ((flags & 0x08) != 0) header.repeatClose = (readByte() & 0xff) - 1; 189 | if ((flags & 0x20) != 0) { 190 | let marker = header.marker = {}; 191 | marker.measure = header.number; 192 | marker.title = readStringByteSizeOfInteger(); 193 | marker.color = readColor(); 194 | } 195 | if ((flags & 0x10) != 0) header.repeatAlternative = readUnsignedByte(); 196 | if ((flags & 0x40) != 0) { 197 | keySignature = readKeySignature(); 198 | skip(1); 199 | } 200 | if ((flags & 0x01) != 0 || (flags & 0x02) != 0) skip(4); 201 | if ((flags & 0x10) == 0) skip(1); 202 | let tripletFeel = readByte(); 203 | if (tripletFeel === 1) header.tripletFeel = 'eigth'; 204 | else if (tripletFeel === 2) header.tripletFeel = 'sixteents'; 205 | else header.tripletFeel = 'none'; 206 | measureHeaders.push(header); 207 | } 208 | 209 | const readChannel = (track) => { 210 | const gmChannel1 = readInt() -1; 211 | const gmChannel2 = readInt() -1; 212 | if (gmChannel1 >= 0 && gmChannel1 < channels.length) { 213 | const gmChannel1Param = {}; 214 | const gmChannel2Param = {}; 215 | 216 | gmChannel1Param.key = 'gm channel 1'; 217 | gmChannel1Param.value = String(gmChannel1); 218 | gmChannel2Param.key = 'gm channel 2'; 219 | gmChannel2Param.value = String(gmChannel1 != 9 ? gmChannel2 : gmChannel1); 220 | 221 | const channel = JSON.parse(JSON.stringify(channels[gmChannel1])); 222 | 223 | for (let i = 0; i < channels.length; i++) { 224 | let channelAux = channels[i]; 225 | // TODO 226 | /*for (let n = 0; n < channelAux.; i++) { 227 | 228 | }*/ 229 | } 230 | if (channel.id === 0) { 231 | channel.id = channels.length + 1; 232 | channel.name = 'TODO'; 233 | channel.parameters.push(gmChannel1Param); 234 | channel.parameters.push(gmChannel2Param); 235 | channels.push(channel); 236 | } 237 | track.channelId = channel.id; 238 | } 239 | } 240 | 241 | const tracks = []; 242 | for (let number = 1; number <= trackCount; number++) { 243 | let track = {}; 244 | readUnsignedByte(); 245 | if (number === 1 || versionIndex === 0) skip(1); 246 | track.number = number; 247 | track.lyrics = number == lyricTrack 248 | ? lyric 249 | : {}; 250 | track.name = readStringByte(40); 251 | track.strings = []; 252 | let stringCount = readInt(); 253 | for (let i = 0; i < 7; i++) { 254 | let tuning = readInt(); 255 | if (stringCount > i) { 256 | let string = {}; 257 | string.number = i + 1; 258 | string.value = tuning; 259 | track.strings.push(string); 260 | } 261 | } 262 | readInt(); 263 | readChannel(track); 264 | readInt(); 265 | track.offset = readInt(); 266 | track.color = readColor(); 267 | track.measures = []; 268 | skip(versionIndex > 0 ? 49 : 44); 269 | if (versionIndex > 0) { 270 | readStringByteSizeOfInteger(); 271 | readStringByteSizeOfInteger(); 272 | } 273 | tracks.push(track); 274 | } 275 | skip(versionIndex == 0 ? 2 : 1); 276 | 277 | const readBeat = (start, measure, track, tempo, voiceIndex) => { 278 | const flags = readUnsignedByte(); 279 | 280 | const beat = getBeat(measure, start); 281 | const voice = beat.voices[voiceIndex]; 282 | if ((flags & 0x40) != 0) { 283 | const beatType = readUnsignedByte(); 284 | voice.empty = (beatType & 0x02) === 0; 285 | } 286 | const duration = readDuration(flags); 287 | const effect = {}; 288 | if ((flags & 0x02) != 0) readChord(track.strings, beat); 289 | if ((flags & 0x04) != 0) readText(beat); 290 | if ((flags & 0x08) != 0) readBeatEffects(beat, effect); 291 | if ((flags & 0x10) != 0) readMixChange(tempo); 292 | const stringFlags = readUnsignedByte(); 293 | for (let i = 6; i>= 0; i--) { 294 | if ((stringFlags & (1 << i)) != 0 && (6 - i) < track.strings.length) { 295 | let string = JSON.parse(JSON.stringify(track.strings[6 - i])); 296 | let note = readNote(string, track, JSON.parse(JSON.stringify(effect))); 297 | voice.notes.push(note); 298 | } 299 | voice.duration = JSON.parse(JSON.stringify(duration)); 300 | } 301 | 302 | skip(1); 303 | 304 | const read = readByte(); 305 | if ((read & 0x02) != 0) skip(1); 306 | 307 | return (voice.notes.length ? duration : 0); 308 | }; 309 | 310 | const readNote = (string, track, effect) => { 311 | const flags = readUnsignedByte(); 312 | const note = {}; 313 | note.string = string.number; 314 | note.effect = effect; 315 | note.effect.accentuatedNote = (flags & 0x40) != 0; 316 | note.effect.heavyAccentuatedNote = (flags & 0x02) != 0; 317 | note.effect.ghostNote = (flags & 0x04) != 0; 318 | if ((flags & 0x20) != 0) { 319 | const noteType = readUnsignedByte(); 320 | note.tiedNote = noteType === 0x02; 321 | note.effect.deadNote = noteType === 0x03; 322 | } 323 | if ((flags & 0x10) != 0) { 324 | note.velocity = 'TGVelocities.MIN_VELOCITY' + ('TGVElocities.VELOCITY_INCREMENT' * readByte()) - 'TGVelocities.VELOCITY_INCREMENT'; // TODO 325 | } 326 | if ((flags & 0x20) != 0) { 327 | const fret = readByte(); 328 | const value = note.tiedNote 329 | ? getTiedNoteValue(string.number, track) 330 | : fret; 331 | note.value = value >= 0 && value < 100 332 | ? value 333 | : 0; 334 | } 335 | if ((flags & 0x80) != 0) skip(2); 336 | if ((flags & 0x01) != 0) skip(8); 337 | skip(1); 338 | if ((flags & 0x08) != 0) readNoteEffects(note.effect); 339 | return note; 340 | }; 341 | 342 | const readNoteEffects = noteEffect => { 343 | const flags1 = readUnsignedByte(); 344 | const flags2 = readUnsignedByte(); 345 | if ((flags1 & 0x01) != 0) readBend(noteEffect); 346 | if ((flags1 & 0x10) != 0) readGrace(noteEffect); 347 | if ((flags2 & 0x04) != 0) readTremoloPicking(noteEffect); 348 | if ((flags2 & 0x08) != 0) { 349 | noteEffect.slide = true; 350 | readByte(); 351 | } 352 | if ((flags2 & 0x10) != 0) readArtificialHarmonic(noteEffect); 353 | if ((flags2 & 0x20) != 0) readTrill(noteEffect); 354 | noteEffect.hammer = (flags1 & 0x02) != 0; 355 | noteEffect.letRing = (flags1 & 0x08) != 0; 356 | noteEffect.vibrato = (flags2 & 0x40) != 0; 357 | noteEffect.palmMute = (flags2 & 0x02) != 0; 358 | noteEffect.staccato = (flags2 & 0x01) != 0; 359 | }; 360 | 361 | const readTrill = effect => { 362 | const fret = readByte(); 363 | const period = readByte(); 364 | const trill = { fret: fret, duration: {} }; 365 | if (period === 1) { 366 | trill.duration.value = 'sixteenth'; 367 | effect.trill = trill; 368 | } else if (period === 2) { 369 | trill.duration.value = 'thirty_second'; 370 | effect.trill = trill; 371 | } else if (period === 3) { 372 | trill.duration.value = 'sixty_fourth'; 373 | effect.trill = trill; 374 | } 375 | }; 376 | 377 | const readArtificialHarmonic = effect => { 378 | const type = readByte(); 379 | const harmonic = { data: 0 }; 380 | if (type === 1) { 381 | harmonic.type = 'natural'; 382 | effect.harmonic = harmonic; 383 | } else if (type === 2) { 384 | skip(3); 385 | harmonic.type = 'artificial'; 386 | effect.harmonic = harmonic; 387 | } else if (type === 3) { 388 | skip(1); 389 | harmonic.type = 'tapped'; 390 | effect.harmonic = harmonic; 391 | } else if (type === 4) { 392 | harmonic.type = 'pinch'; 393 | effect.harmonic = harmonic; 394 | } else if (type === 5) { 395 | harmonic.type = 'semi'; 396 | effect.harmonic = harmonic; 397 | } 398 | }; 399 | 400 | const readTremoloPicking = effect => { 401 | const value = readUnsignedByte(); 402 | const tp = {}; 403 | if (value === 1) { 404 | tp.duration.value = 'eigth'; 405 | effect.tremoloPicking = tp; 406 | } else if (value === 2) { 407 | tp.duration.value = 'sixteenth'; 408 | effect.tremoloPicking = tp; 409 | } else if (value === 3) { 410 | tp.duration.value = 'thirty_second'; 411 | effect.tremoloPicking = tp; 412 | } 413 | }; 414 | 415 | const readGrace = effect => { 416 | const fret = readUnsignedByte(); 417 | const dynamic = readUnsignedByte(); 418 | const transition = readByte(); 419 | const duration = readUnsignedByte(); 420 | const flags = readUnsignedByte(); 421 | const grace = {}; 422 | grace.fret = fret; 423 | grace.dynamic = ('TGVelocities.MIN_VELOCITY' + ('TGVelocities.VELOCITY_INCREMENT' * dynamic)) - 'TGVelocities.VELOCITY_INCREMENT'; 424 | grace.duration = duration; 425 | grace.dead = (flags & 0x01) != 0; 426 | grace.onBeat = (flags & 0x02) != 0; 427 | if (transition === 0) grace.transition = 'none'; 428 | else if (transition === 1) grace.transition = 'slide'; 429 | else if (transition === 2) grace.transition = 'bend'; 430 | else if (transition === 3) grace.transition = 'hammer'; 431 | effect.grace = grace; 432 | }; 433 | 434 | const readBend = effect => { 435 | skip(5); 436 | const bend = { points: [] }; 437 | const numPoints = readInt(); 438 | for (let i = 0; i < numPoints; i++) { 439 | let bendPosition = readInt(); 440 | let bendValue = readInt(); 441 | readByte(); 442 | 443 | let pointPosition = Math.round(bendPosition * 'TGEffectBend.MAX_POSITION_LENGTH / GP_BEND_POSITION'); 444 | let pointValue = Math.round(bendValue * 'TGEffectBend.SEMITONE_LENGHT' / 'GP_BEND_SEMITONE'); 445 | bend.points.push({ pointPosition, pointValue }); 446 | } 447 | if (bend.points.length) effect.bend = bend; 448 | }; 449 | 450 | const getTiedNoteValue = (string, track) => { 451 | const measureCount = track.measures.length; 452 | if (measureCount > 0) { 453 | for (let m = measureCount - 1; m >= 0; m--) { 454 | let measure = track.measures[m]; 455 | for (let b = measure.beats.length - 1; b >= 0; b--) { 456 | let beat = measure.beats[b]; 457 | for (let v = 0; v < beat.voices.length; v++) { 458 | let voice = beat.voices[v]; 459 | if (!voice.empty) { 460 | for (let n = 0; n < voice.notes.length; n++) { 461 | let note = voice.notes[n]; 462 | if (note.string === string) return note.value; 463 | } 464 | } 465 | } 466 | } 467 | } 468 | } 469 | }; 470 | 471 | const readMixChange = tempo => { 472 | readByte(); // instrument 473 | 474 | skip(16); 475 | const volume = readByte(); 476 | const pan = readByte(); 477 | const chorus = readByte(); 478 | const reverb = readByte(); 479 | const phaser = readByte(); 480 | const tremolo = readByte(); 481 | readStringByteSizeOfInteger(); // tempoName 482 | const tempoValue = readInt(); 483 | if (volume >= 0) readByte(); 484 | if (pan >= 0) readByte(); 485 | if (chorus >= 0) readByte(); 486 | if (reverb >= 0) readByte(); 487 | if (phaser >= 0) readByte(); 488 | if (tremolo >= 0) readByte(); 489 | if (tempoValue >= 0) { 490 | tempo.value = tempoValue; 491 | skip(1); 492 | if (versionIndex > 0) skip(1); 493 | } 494 | readByte(); 495 | skip(1); 496 | if (versionIndex > 0) { 497 | readStringByteSizeOfInteger(); 498 | readStringByteSizeOfInteger(); 499 | } 500 | }; 501 | 502 | const readTremoloBar = effect => { 503 | skip(5); 504 | const tremoloBar = { points: [] }; 505 | const numPoints = readInt(); 506 | for (let i = 0; i < numPoints; i++) { 507 | let position = readInt(); 508 | let value = readInt(); 509 | readByte(); 510 | 511 | let pointPosition = Math.round(position * 'max position length' / 'bend position'); // TODO 512 | let pointValue = Math.round(value / ('GP_BEND_SEMITONE' * 0x2f)); //TODO 513 | tremoloBar.points.push({ pointPosition, pointValue }); 514 | } 515 | if (tremoloBar.points.length > 0) effect.tremoloBar = tremoloBar; 516 | }; 517 | 518 | const readBeatEffects = (beat, noteEffect) => { 519 | const flags1 = readUnsignedByte(); 520 | const flags2 = readUnsignedByte(); 521 | noteEffect.fadeIn = (flags1 & 0x10) != 0; 522 | noteEffect.vibrato = (flags1 & 0x02) != 0; 523 | if ((flags1 & 0x20) != 0) { 524 | const effect = readUnsignedByte(); 525 | noteEffect.tapping = effect === 1; 526 | noteEffect.slapping = effect === 2; 527 | noteEffect.popping = effect === 3; 528 | } 529 | if ((flags2 & 0x04) != 0) readTremoloBar(noteEffect); 530 | if ((flags1 & 0x40) != 0) { 531 | const strokeUp = readByte(); 532 | const strokeDown = readByte(); 533 | // TODO 534 | if (strokeUp > 0) { 535 | beat.stroke.direction = 'stroke_up'; 536 | beat.stroke.value = 'stroke_down'; 537 | } else if (strokeDown > 0) { 538 | beat.stroke.direction = 'stroke_down'; 539 | beat.stroke.value = 'stroke_down'; 540 | } 541 | } 542 | if ((flags2 & 0x02) != 0) readByte(); 543 | }; 544 | 545 | const readText = beat => { 546 | const text = {}; 547 | text.value = readStringByteSizeOfInteger(); 548 | beat.text = text; 549 | }; 550 | 551 | const readChord = (strings, beat) => { 552 | const chord = { strings, frets: [] }; 553 | skip(17); 554 | chord.name = readStringByte(21); 555 | skip(4); 556 | chord.frets[0] = readInt(); 557 | for (let i = 0; i < 7; i++) { 558 | let fret = readInt(); 559 | if (i < chord.strings.length) { 560 | chord.frets[i] = fret; 561 | } 562 | } 563 | skip(32); 564 | if (chord.strings.length > 0) beat.chord = chord; 565 | } 566 | 567 | const readDuration = (flags) => { 568 | const duration = {}; 569 | duration.value = (Math.pow(2, (readByte() + 4)) / 4); 570 | duration.dotted = (flags & 0x01) != 0; 571 | duration.division = {}; 572 | if ((flags & 0x20) != 0) { 573 | const divisionType = readInt(); 574 | switch (divisionType) { 575 | case 3: 576 | duration.division.enters = 3; 577 | duration.division.times = 2; 578 | break; 579 | case 5: 580 | duration.division.enters = 5; 581 | duration.division.times = 5; 582 | break; 583 | case 6: 584 | duration.division.enters = 6; 585 | duration.division.times = 4; 586 | break; 587 | case 7: 588 | duration.division.enters = 7; 589 | duration.division.times = 4; 590 | break; 591 | case 9: 592 | duration.division.enters = 9; 593 | duration.division.times = 8; 594 | break; 595 | case 10: 596 | duration.division.enters = 10; 597 | duration.division.times = 8; 598 | break; 599 | case 11: 600 | duration.division.enters = 11; 601 | duration.division.times = 8; 602 | break; 603 | case 12: 604 | duration.division.enters = 12; 605 | duration.division.times = 8; 606 | break; 607 | case 13: 608 | duration.division.enters = 13; 609 | duration.division.times = 8; 610 | break; 611 | } 612 | } 613 | if (!('enters' in duration.division)) { 614 | duration.division.enters = 1; 615 | duration.division.times = 1; 616 | } 617 | return getTime(duration); 618 | }; 619 | 620 | const getTime = duration => { 621 | let time = QUARTER_TIME * 4.0 / duration.value; 622 | if (duration.dotted) { 623 | time += time / 2; 624 | } else if (duration.doubleDotted) { 625 | time += (time / 4) * 3; 626 | } 627 | return time * duration.division.times / duration.division.enters; 628 | }; 629 | 630 | const getBeat = (measure, start) => { 631 | for (let beat of measure.beats) { 632 | if (beat.start === start) return beat; 633 | } 634 | const beat = { start: start, voices: [{notes:[]},{notes:[]}] }; 635 | measure.beats.push(beat); 636 | return beat; 637 | } 638 | 639 | const readMeasure = (measure, track, tempo) => { 640 | for (let voice = 0; voice < 2; voice++) { 641 | let start = measure.start; 642 | let beats = readInt(); 643 | for (let k = 0; k < beats; k++) { 644 | start += readBeat(start, measure, track, tempo, voice); 645 | } 646 | }; 647 | 648 | const emptyBeats = []; 649 | for (let i = 0; i < measure.beats.length; i++) { 650 | let beat = measure.beats[i]; 651 | let empty = true; 652 | for (let v = 0; v < beat.voices.length; v++) { 653 | if (beat.voices[v].notes.length) empty = false; 654 | } 655 | if (empty) emptyBeats.push(beat); 656 | } 657 | for (let beat of emptyBeats) { 658 | measure.beats.splice(measure.beats.indexOf(beat), 1); 659 | } 660 | measure.clef = getClef(track); 661 | measure.keySignature = keySignature; 662 | } 663 | 664 | const getClef = track => { 665 | if (!isPercussionChannel(track.channelId)) { 666 | for (let string of track.strings) { 667 | if (string.value <= 34) return 'CLEF_BASS'; 668 | } 669 | } 670 | return 'CLEF_TREBLE'; 671 | } 672 | 673 | const isPercussionChannel = channelId => { 674 | for (let channel of channels) { 675 | if (channel.id === channelId) return channel.isPercussionChannel; 676 | } 677 | }; 678 | 679 | const getLength = (header) => { 680 | return header.timeSignature.numerator * getTime(header.timeSignature.denominator); 681 | }; 682 | 683 | let tempo = { value: tempoValue }; 684 | let start = QUARTER_TIME; 685 | for (let i = 0; i < measures; i++) { 686 | let header = measureHeaders[i]; 687 | header.start = start; 688 | for (let j = 0; j < trackCount; j++) { 689 | let track = tracks[j]; 690 | let measure = { header: header, start: start, beats: [] }; 691 | track.measures.push(measure); 692 | readMeasure(measure, track, tempo); 693 | skip(1); 694 | } 695 | header.tempo = JSON.parse(JSON.stringify(tempo)); 696 | start += getLength(header); 697 | } 698 | 699 | return { 700 | version: { major, minor }, 701 | title, 702 | subtitle, 703 | artist, 704 | album, 705 | lyricsAuthor, 706 | musicAuthor, 707 | copyright, 708 | tab, 709 | instructions, 710 | comments, 711 | lyric, 712 | tempoValue, 713 | keySignature, 714 | channels, 715 | measures, 716 | trackCount, 717 | measureHeaders, 718 | tracks 719 | }; 720 | }; 721 | 722 | if (!module.parent) { 723 | console.log(JSON.stringify(module.exports(require('fs').readFileSync(process.argv[1])), null, ' ')); 724 | } 725 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parse-gp5", 3 | "description": "Parser for the Guitar Pro 5 file format", 4 | "version": "1.0.1", 5 | "license": "MIT", 6 | "repository": "juliangruber/parse-gp5" 7 | } 8 | --------------------------------------------------------------------------------