// perc.js - module to handle %%percmap
//
// Copyright (C) 2018-2023 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%percmap" appears in a ABC source.
//
// Parameters (from W. Vree)
//	%%percmap ABC_note percussion [note_head]
// The percussion may be a number (MIDI percussion number range 35..81),
// a ABC note or a possibly abbreviated percussion name.
// See https://wim.vree.org/js2/tabDrumDoc.html for more information.

// Using this command creates a voicemap named "MIDIdrum".

if (typeof abc2svg == "undefined")
    var	abc2svg = {}

abc2svg.perc = {

    // parse %%percmap
    do_perc: function(parm) {
    var	pits = new Int8Array([0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6]),
	accs = new Int8Array([3, 1, 3, -1, 3, 3, 1, 3, -1, 3, -1, 3])

// GM drum
// 35 B,,,	Acoustic Bass Drum	a-b-d
// 36 C,,	Bass Drum 1		b-d-1
// 37 ^C,,	Side Stick		s-s
// 38 D,,	Acoustic Snare		a-s
// 39 ^D,,	Hand Clap		h-c
// 40 E,,	Electric Snare		e-s
// 41 F,,	Low Floor Tom		l-f-t
// 42 ^F,,	Closed Hi Hat		c-h-h
// 43 G,,	High Floor Tom		h-f-t
// 44 ^G,,	Pedal Hi-Hat		p-h-h
// 45 A,,	Low Tom			l-to
// 46 ^A,,	Open Hi-Hat		o-h-h
// 47 B,,	Low-Mid Tom		l-m-t
// 48 C,	Hi Mid Tom		h-m-t
// 49 ^C,	Crash Cymbal 1		c-c-1
// 50 D,	High Tom		h-to
// 51 ^D,	Ride Cymbal 1		r-c-1
// 52 E,	Chinese Cymbal		c-c
// 53 F,	Ride Bell		r-b
// 54 ^F,	Tambourine		t
// 55 G,	Splash Cymbal		s-c
// 56 ^G,	Cowbell			co
// 57 A,	Crash Cymbal 2		c-c-2
// 58 ^A,	Vibraslap		v
// 59 B,	Ride Cymbal 2		r-c-2
// 60 C		Hi Bongo		h-b
// 61 ^C	Low Bongo		l-b
// 62 D		Mute Hi Conga		m-h-c
// 63 ^D	Open Hi Conga		o-h-c
// 64 E		Low Conga		l-c
// 65 F		High Timbale		h-ti
// 66 ^F	Low Timbale		l-ti
// 67 G		High Agogo		h-a
// 68 ^G	Low Agogo		l-a
// 69 A		Cabasa			ca
// 70 ^A	Maracas			m
// 71 B		Short Whistle		s-w
// 72 c		Long Whistle		l-w
// 73 ^c	Short Guiro		s-g
// 74 d		Long Guiro		l-g
// 75 ^d	Claves			cl
// 76 e		Hi Wood Block		h-w-b
// 77 f		Low Wood Block		l-w-b
// 78 ^f	Mute Cuica		m-c
// 79 g		Open Cuica		o-c
// 80 ^g	Mute Triangle		m-t
// 81 a		Open Triangle		o-t

// percussion reduced names (alphabetic order)
var prn = {
	"a-b-d": 35,
	"a-s":   38,
	"b-d-1": 36,
	"ca":    69,
	"cl":    75,
	"co":    56,
	"c-c":   52,
	"c-c-1": 49,
	"c-c-2": 57,
	"c-h-h": 42,
	"e-s":   40,
	"h-a":   67,
	"h-b":   60,
	"h-c":   39,
	"h-f-t": 43,
	"h-m-t": 48,
	"h-ti":  65,
	"h-to":  50,
	"h-w-b": 76,
	"l-a":   68,
	"l-b":   61,
	"l-c":   64,
	"l-f-t": 41,
	"l-g":   74,
	"l-m-t": 47,
	"l-ti":  66,
	"l-to":  45,
	"l-w":   72,
	"l-w-b": 77,
	"m":     70,
	"m-c":   78,
	"m-h-c": 62,
	"m-t":   80,
	"o-c":   79,
	"o-h-c": 63,
	"o-h-h": 46,
	"o-t":   81,
	"p-h-h": 44,
	"r-b":   53,
	"r-c-1": 51,
	"r-c-2": 59,
	"s-c":   55,
	"s-g":   73,
	"s-s":   37,
	"s-w":   71,
	"t":     54,
	"v":     58
}

    // convert a drum instrument to a ABC note
    function toabc(p) {
    var	i, j, s, pit

	if (/^[_^]*[A-Ga-g][,']*$/.test(p))	// '
		return p		// ABC note

	// if not a MIDI pitch, try a drum instrument name
	pit = Number(p)
	if (isNaN(pit)) {
		p = p.toLowerCase(p);
		s = p[0];		// get the 1st letters after '-'
		i = 0
		while (1) {
			j = p.indexOf('-', i)
			if (j < 0)
				break
			i = j + 1;
			s += '-' + p[i]
		}
		pit = prn[s]

		// solve some specific cases
		if (!pit) {
			switch (p[0]) {
			case 'c':
				switch (p[1]) {
				case 'a': pit = prn.ca; break
				case 'l': pit = prn.cl; break
				case 'o': pit = prn.co; break
				}
				break
			case 'h':
			case 'l':
				i = p.indexOf('-')
				if (p[i + 1] != 't')
					break
				switch (p[i + 2]) {
				case 'i':
				case 'o':
					pit = prn[s + p[i + 2]]
					break
				}
				break
			}
			if (!pit)
				return
		}
	}

	p = ["C","^C","D","_E","E","F","^F","G","^G","A","_B","B"][pit % 12]
	while (pit < 60) {
		p += ','
		pit += 12
	}
	while (pit >= 72) {
		p += "'"
		pit -= 12
	}
	return p
    } // toabc()

    // do_perc()
    var	a = parm.split(/\s+/),
	p = a[1].replace(/[=_^]/, '')

	this.do_pscom("map MIDIdrum " + a[1] +
		" play=" + toabc(a[2]) +
		" print=" + p +
		(a[3] ? (" heads=" + a[3]) : ''))
	this.set_v_param("perc", "MIDIdrum")
    }, // do_perc()

    // set the MIDI parameters in the current voice
    set_perc: function(a) {
    var	i, item, s,
	curvoice = this.get_curvoice()

	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "perc=":				// %%percmap
			if (!curvoice.map)
				curvoice.map = {}
			curvoice.map = a[i + 1];
			s = this.new_block("midiprog")
			s.play = s.invis = 1 //true
			curvoice.chn = s.chn = 9	// channel 10
			break
		}
	}
    }, // set_perc()

    do_pscom: function(of, text) {
	if (text.slice(0, 8) == "percmap ")
		abc2svg.perc.do_perc.call(this, text)
	else
		of(text)
    },

    set_vp: function(of, a) {
	abc2svg.perc.set_perc.call(this, a);
	of(a)
    },

    set_hooks: function(abc) {
	abc.do_pscom = abc2svg.perc.do_pscom.bind(abc, abc.do_pscom);
	abc.set_vp = abc2svg.perc.set_vp.bind(abc, abc.set_vp)
    }
} // perc

if (!abc2svg.mhooks)
	abc2svg.mhooks = {}
abc2svg.mhooks.perc = abc2svg.perc.set_hooks