/**
* This file is part of HARMONICARIUM, a web app which allows users to play
* the Harmonic Series dynamically by changing its fundamental tone in real-time.
* It is available in its latest version from:
* https://github.com/IndustrieCreative/Harmonicarium
*
* @license
* Copyright (C) 2017-2020 by Walter Mantovani (http://armonici.it).
* Written by Walter Mantovani.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
"use strict";
/**
* The HARMONICARIUM main class
*/
class HUM {
/**
* Create an instance of DHC
* @param {number} id - The id for the new instance of HUM
*/
constructor(id) {
/**
* The id of the HUM instance
*
* @type {number}
*/
this.id = id;
/**
* Namespace for base settings
*
* @type {Object}
*
* @property {number} dhcQty - How many DHCs must be generated
* @property {boolean} dpPad - If the Diphonic Pad must be included
*/
this.settings = {
dhcQty: 1,
dpPad: true,
};
/**
* Namespace container for module components
*
* @type {Object}
*
* @property {Object.<string, HUM.DHC>} availableDHCs - How many DHCs must be generated
* @property {HUM.DpPad} dpPad - If the Diphonic Pad must be included
* @property {HUM.BackendUtils} backendUtils - If the Backend Utils must be included
*/
this.components = {
availableDHCs: {},
dpPad: null,
backendUtils: null,
};
/**
* The dimensions of the main reference HTML container
*
* @type {Object}
*
* @property {number} x - Width in pixel
* @property {number} y - Height in pixel
*/
this.viewportDim = {
x: 0,
y: 0
};
/**
* Namespace container for all the injected HTML templates
*
* @type {Object}
*
* @property {HTMLElement} instancesContainer - The main HTML container of all HUM instances
* @property {HTMLElement} appContainer - The HTML container of this HUM instance
* @property {HTMLElement} dpPadPage - The HTML container of the DpPad instance (just one per harmonicarium)
* @property {HTMLElement} sidePanel - The main HTML container of the side panel's objects (.logoBox and .sideContents)
* @property {HTMLElement} logTextBox - The HTML container of the log text box for the BackendUtils instance
* @property {HTMLElement} svgIcons - The HTML container of the SVG icons palette
* @property {HTMLElement} logoBox - The HTML container of the logo/menu box
* @property {HTMLElement} sideContents - The HTML container of the .sideMenu
* @property {HTMLElement} sideMenu - The HTML container of .dpPadAccordion (just one) and all accordions of each DHC instance
* @property {HTMLElement} dpPadAccordion - The HTML container of the HTML accordion of the DpPad instance
* @property {Object.<string, HTMLElement>} hstackTab - All the DHC's Hstak HTML containers
* @property {Object.<string, HTMLElement>} pianoTab - All the DHC's Hancock HTML containers
* @property {Object.<string, HTMLElement>} dhcTab - All the DHC's main settings HTML containers
* @property {Object.<string, HTMLElement>} synthTab - All the DHC's Synth HTML containers
* @property {Object.<string, HTMLElement>} midiTab - All the DHC's Midi HTML containers
* @property {Object.<string, HTMLElement>} fmTab - All the DHC's FM settings HTML containers
* @property {Object.<string, HTMLElement>} ftTab - All the DHC's FT settings HTML containers
* @property {Object.<string, HTMLElement>} htTab - All the DHC's HT settings HTML containers
*/
this.html = {
// Body content
instancesContainer: document.getElementById('harmonicaria'),
// This Harmonicarium Instance content
appContainer: HUM.tmpl.appContainer(this.id),
// App contents
dpPadPage: HUM.tmpl.dpPadPage(this.id),
sidePanel: HUM.tmpl.sidePanel(this.id),
logTextBox: HUM.tmpl.logTextBox(this.id),
svgIcons: HUM.tmpl.dpIcons(this.id),
// Side Panel contents
logoBox: HUM.tmpl.logoBox(this.id),
sideContents: HUM.tmpl.sideContents(this.id),
// Side Contents content
sideMenu: HUM.tmpl.sideMenu(this.id),
// Side Menu content
dpPadAccordion: HUM.tmpl.dpPadAccordion(this.id),
// dhcAccordion: HUM.tmpl.dhcAccordion(this.id),
// visualiserBox: HUM.tmpl.visualiserBox(this.id),
// dhc Container contents (DHC-specific Boxes)
// accordion: {},
hstackTab: {},
pianoTab: {},
dhcTab: {},
synthTab: {},
midiTab: {},
fmTab: {},
ftTab: {},
htTab: {},
};
if (!this.html.instancesContainer) {
alert('No DIV Html element with ID "harmonicaria" has been found!\n\nApplication loading aborted.');
return undefined;
}
}
/**
* Inject in the document all the main HTML templates
*/
_initTemplates() {
// into Side Menu
this.html.sideMenu.children[0].appendChild(this.html.dpPadAccordion);
// this.html.sideMenu.appendChild(this.html.dhcAccordion);
// this.html.sideMenu.appendChild(this.html.visualiserBox);
// into Side Contents
this.html.sideContents.appendChild(this.html.sideMenu);
// into Side Panel
this.html.sidePanel.appendChild(this.html.logoBox);
this.html.sidePanel.appendChild(this.html.sideContents);
// into App Container
this.html.appContainer.appendChild(this.html.dpPadPage);
this.html.appContainer.appendChild(this.html.sidePanel);
this.html.appContainer.appendChild(this.html.logTextBox);
this.html.appContainer.appendChild(this.html.svgIcons);
// into Instances Container
this.html.instancesContainer.appendChild(this.html.appContainer);
}
/**
* Initialize all the necessary DHCs
*/
_initDHCs() {
let hrmID = this.id;
// Create the DHCs needed
for (let id=0; id<this.settings.dhcQty; id++) {
let dhcID = hrmID+'-'+id;
let dhcAccordion = HUM.tmpl.dhcAccordion(dhcID);
// this.html.accordion[dhcID] = dhcAccordion;
this.html.synthTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'synth', 'Built-in Synth');
this.html.midiTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'midi', 'MIDI I/O');
this.html.pianoTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'piano', 'Piano Keymap');
this.html.dhcTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'dhcSettings', 'DHC Settings');
this.html.fmTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'fm', 'Fundamental Mother');
this.html.ftTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'ft', 'Fundamental Tones');
this.html.htTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'ht', 'Harmonic Tones');
this.html.hstackTab[dhcID] = HUM.tmpl.accordionTab(dhcID, 'hstack', 'Hstack');
this.html.hstackTab[dhcID].children[2].appendChild(HUM.tmpl.hstackBox(dhcID));
this.html.pianoTab[dhcID].children[2].appendChild(HUM.tmpl.pianoBox(dhcID));
this.html.dhcTab[dhcID].children[2].appendChild(HUM.tmpl.dhcBox(dhcID));
this.html.synthTab[dhcID].children[2].appendChild(HUM.tmpl.synthBox(dhcID));
this.html.midiTab[dhcID].children[2].appendChild(HUM.tmpl.midiBox(dhcID));
this.html.fmTab[dhcID].children[2].appendChild(HUM.tmpl.fmBox(dhcID));
this.html.ftTab[dhcID].children[2].appendChild(HUM.tmpl.ftBox(dhcID));
this.html.htTab[dhcID].children[2].appendChild(HUM.tmpl.htBox(dhcID));
dhcAccordion.children[0].children[0].appendChild(this.html.synthTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.midiTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.pianoTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.dhcTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.fmTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.ftTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.htTab[dhcID]);
dhcAccordion.children[0].children[0].appendChild(this.html.hstackTab[dhcID]);
this.html.sideMenu.children[0].appendChild(dhcAccordion);
this.components.availableDHCs[id] = new HUM.DHC(dhcID, this);
// this.components.availableDHCs[id].init();
}
}
/**
* Initialize a single instance of HUM
*/
_init() {
this._initTemplates();
this.components.backendUtils = new HUM.BackendUtils(this);
this._initDHCs();
// Create the DiphonicPad if required and initiaize it
// (for now, use the first DHC available)
if (this.settings.dpPad) {
this.components.dpPad = new HUM.DpPad(this, this.components.availableDHCs[0]);
this.components.dpPad.init();
}
this.html.dpPadPage.style.width = "100%";
this.html.dpPadPage.style.height = "100%";
window.addEventListener('resize', () => this.windowResize());
window.addEventListener('orientationchange', () => this.windowResize());
this.windowResize();
}
/**
* Recompute the drawn geometries in all the components that need to be resized
* accordingly to the reference HTML container's dimensions
*/
windowResize() {
// window.requestAnimationFrame( () => {
this.updateViewportSize();
this.html.appContainer.style.width = this.viewportDim.x+'px';
this.html.appContainer.style.height = this.viewportDim.y+'px';
this.components.dpPad.windowResize();
// });
}
/**
* Update the reference HTML container's dimensions (currently the browser viewport)
*/
updateViewportSize() {
// @todo - no difference if hiding scrollbar ?!
// this.viewportDim.x = window.innerWidth - 1;
// this.viewportDim.y = window.innerHeight - 1;
this.viewportDim.x = document.documentElement.clientWidth - 1;
this.viewportDim.y = document.documentElement.clientHeight - 1;
}
}