text: 'use strict'
  
  function NoteParser()
  {
     
     // util
     function fillStr (s, num) { return Array(num + 1).join(s) }
     function isNum (x) { return typeof x === 'number' }
     function isStr (x) { return typeof x === 'string' }
     function isDef (x) { return typeof x !== 'undefined' }
     function midiToFreq (midi, tuning) {
       return Math.pow(2, (midi - 69) / 12) * (tuning || 440)
     }
     
     var REGEX = /^([a-gA-G])(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)\s*$/
     /**
      * A regex for matching note strings in scientific notation.
      *
      * @name regex
      * @function
      * @return {RegExp} the regexp used to parse the note name
      *
      * The note string should have the form `letter[accidentals][octave][element]`
      * where:
      *
      * - letter: (Required) is a letter from A to G either upper or lower case
      * - accidentals: (Optional) can be one or more `b` (flats), `#` (sharps) or `x` (double sharps).
      * They can NOT be mixed.
      * - octave: (Optional) a positive or negative integer
      * - element: (Optional) additionally anything after the duration is considered to
      * be the element name (for example: 'C2 dorian')
      *
      * The executed regex contains (by array index):
      *
      * - 0: the complete string
      * - 1: the note letter
      * - 2: the optional accidentals
      * - 3: the optional octave
      * - 4: the rest of the string (trimmed)
      *
      * @example
      * var parser = require('note-parser')
      * parser.regex.exec('c#4')
      * // => ['c#4', 'c', '#', '4', '']
      * parser.regex.exec('c#4 major')
      * // => ['c#4major', 'c', '#', '4', 'major']
      * parser.regex().exec('CMaj7')
      * // => ['CMaj7', 'C', '', '', 'Maj7']
      */
     function regex () { return REGEX }
     
     var SEMITONES = [0, 2, 4, 5, 7, 9, 11]
     /**
      * Parse a note name in scientific notation an return it's components,
      * and some numeric properties including midi number and frequency.
      *
      * @name parse
      * @function
      * @param {String} note - the note string to be parsed
      * @param {Boolean} isTonic - true the strings it's supposed to contain a note number
      * and some category (for example an scale: 'C# major'). It's false by default,
      * but when true, en extra tonicOf property is returned with the category ('major')
      * @param {Float} tunning - The frequency of A4 note to calculate frequencies.
      * By default it 440.
      * @return {Object} the parsed note name or null if not a valid note
      *
      * The parsed note name object will ALWAYS contains:
      * - letter: the uppercase letter of the note
      * - acc: the accidentals of the note (only sharps or flats)
      * - pc: the pitch class (letter + acc)
      * - step: s a numeric representation of the letter. It's an integer from 0 to 6
      * where 0 = C, 1 = D ... 6 = B
      * - alt: a numeric representation of the accidentals. 0 means no alteration,
      * positive numbers are for sharps and negative for flats
      * - chroma: a numeric representation of the pitch class. It's like midi for
      * pitch classes. 0 = C, 1 = C#, 2 = D ... 11 = B. Can be used to find enharmonics
      * since, for example, chroma of 'Cb' and 'B' are both 11
      *
      * If the note has octave, the parser object will contain:
      * - oct: the octave number (as integer)
      * - midi: the midi number
      * - freq: the frequency (using tuning parameter as base)
      *
      * If the parameter `isTonic` is set to true, the parsed object will contain:
      * - tonicOf: the rest of the string that follows note name (left and right trimmed)
      *
      * @example
      * var parse = require('note-parser').parse
      * parse('Cb4')
      * // => { letter: 'C', acc: 'b', pc: 'Cb', step: 0, alt: -1, chroma: -1,
      *         oct: 4, midi: 59, freq: 246.94165062806206 }
      * // if no octave, no midi, no freq
      * parse('fx')
      * // => { letter: 'F', acc: '##', pc: 'F##', step: 3, alt: 2, chroma: 7 })
      */
     function parse (str, isTonic, tuning) {
       if (typeof str !== 'string') return null
       var m = REGEX.exec(str)
       if (!m || (!isTonic && m[4])) return null
     
       var p = { letter: m[1].toUpperCase(), acc: m[2].replace(/x/g, '##') }
       p.pc = p.letter + p.acc
       p.step = (p.letter.charCodeAt(0) + 3) % 7
       p.alt = p.acc[0] === 'b' ? -p.acc.length : p.acc.length
       var pos = SEMITONES[p.step] + p.alt
       p.chroma = pos < 0 ? 12 + pos : pos % 12
       if (m[3]) { // has octave
         p.oct = +m[3]
         p.midi = pos + 12 * (p.oct + 1)
         p.freq = midiToFreq(p.midi, tuning)
       }
       if (isTonic) p.tonicOf = m[4]
       return p
     }
     
     var LETTERS = 'CDEFGAB'
     function accStr (n) { return !isNum(n) ? '' : n < 0 ? fillStr('b', -n) : fillStr('#', n) }
     function octStr (n) { return !isNum(n) ? '' : '' + n }
     
     /**
      * Create a string from a parsed object or `step, alteration, octave` parameters
      * @param {Object} obj - the parsed data object
      * @return {String} a note string or null if not valid parameters
      * @since 1.2
      * @example
      * parser.build(parser.parse('cb2')) // => 'Cb2'
      *
      * @example
      * // it accepts (step, alteration, octave) parameters:
      * parser.build(3) // => 'F'
      * parser.build(3, -1) // => 'Fb'
      * parser.build(3, -1, 4) // => 'Fb4'
      */
     function build (s, a, o) {
       if (s === null || typeof s === 'undefined') return null
       if (s.step) return build(s.step, s.alt, s.oct)
       if (s < 0 || s > 6) return null
       return LETTERS.charAt(s) + accStr(a) + octStr(o)
     }
     
     /**
      * Get midi of a note
      *
      * @name midi
      * @function
      * @param {String|Integer} note - the note name or midi number
      * @return {Integer} the midi number of the note or null if not a valid note
      * or the note does NOT contains octave
      * @example
      * var parser = require('note-parser')
      * parser.midi('A4') // => 69
      * parser.midi('A') // => null
      * @example
      * // midi numbers are bypassed (even as strings)
      * parser.midi(60) // => 60
      * parser.midi('60') // => 60
      */
     function midi (note) {
       if ((isNum(note) || isStr(note)) && note >= 0 && note < 128) return +note
       var p = parse(note)
       return p && isDef(p.midi) ? p.midi : null
     }
     
     /**
      * Get freq of a note in hertzs (in a well tempered 440Hz A4)
      *
      * @name freq
      * @function
      * @param {String} note - the note name or note midi number
      * @param {String} tuning - (Optional) the A4 frequency (440 by default)
      * @return {Float} the freq of the number if hertzs or null if not valid note
      * @example
      * var parser = require('note-parser')
      * parser.freq('A4') // => 440
      * parser.freq('A') // => null
      * @example
      * // can change tuning (440 by default)
      * parser.freq('A4', 444) // => 444
      * parser.freq('A3', 444) // => 222
      * @example
      * // it accepts midi numbers (as numbers and as strings)
      * parser.freq(69) // => 440
      * parser.freq('69', 442) // => 442
      */
     function freq (note, tuning) {
       var m = midi(note)
       return m === null ? null : midiToFreq(m, tuning)
     }
     
     function letter (src) { return (parse(src) || {}).letter }
     function acc (src) { return (parse(src) || {}).acc }
     function pc (src) { return (parse(src) || {}).pc }
     function step (src) { return (parse(src) || {}).step }
     function alt (src) { return (parse(src) || {}).alt }
     function chroma (src) { return (parse(src) || {}).chroma }
     function oct (src) { return (parse(src) || {}).oct }
     
     return {
        convert: function(note) {
           return parse(note).midi;
         }
       };;
  }
  