/*
 * Copyright (C) 2016-2020 Kano Computing Ltd.
 * License: http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Part } from '../../part.js';
import { Monotron } from '../../../music/monotron/monotron.js';
import { part, property, component } from '../../decorators.js';
import { PartComponent } from '../../component.js';
import { transformLegacySynth } from './legacy.js';
const OSCILLATOR_FREQ_RANGE_LOW = 55; // --> A0 note in pitch standard tuning
const DEFAULT_FREQ = 220; // --> A2
class SynthComponent extends PartComponent {
    constructor() {
        super(...arguments);
        this.volume = 100;
        this.muted = false;
    }
}
__decorate([
    property({ type: Number, value: 100 })
], SynthComponent.prototype, "volume", void 0);
__decorate([
    property({ type: Boolean, value: false })
], SynthComponent.prototype, "muted", void 0);
let SynthPart = class SynthPart extends Part {
    constructor() {
        super();
        this.volumeAttenuation = 0.00075;
        this.waveType = 'sine';
        this.sources = [];
        this.monotron = null;
        this.core = this._components.get('core');
    }
    static transformLegacy(app) {
        transformLegacySynth(app);
    }
    onInstall(context) {
        this.ctx = context.audio.context;
        this.dest = context.audio.destination;
        this.gainNode = this.ctx.createGain();
        this.gainNode.connect(this.dest);
        this.setVolume(this.core.volume);
    }
    onStop() {
        this.sources.forEach((source) => {
            // Ignore sources not started
            try {
                source.stop();
            }
            catch (e) { }
        });
        this.sources.length = 0;
        this.stop();
    }
    setVolume(volume) {
        this.core.volume = Math.min(Math.max(volume, 0), 100);
        // the maximum value is limited to an ear-friendly volume
        this.volumeAttenuation = /sine|triangle/.test(this.waveType) ? 0.00185 : 0.00075;
        const value = this.core.muted ? 0 : (this.core.volume * this.volumeAttenuation);
        if (this.gainNode) {
            this.gainNode.gain.value = value;
        }
        this.core.invalidate();
    }
    _createMonotron() {
        if (!this.ctx) {
            throw new Error('Could not create monotron: AudioContext is not defined');
        }
        const opts = {
            ctx: this.ctx,
            waveType: this.waveType,
        };
        this.setVolume(this.core.volume);
        const monotron = new Monotron(opts);
        if (this.gainNode) {
            monotron.connect(this.gainNode);
        }
        return monotron;
    }
    clamp(value, min, max) {
        return Math.min(Math.max(value, min), max);
    }
    playFrequency(freq, length) {
        if (!this.ctx) {
            return;
        }
        freq = this.clamp(freq, 0, 100);
        length = this.clamp(length, 0, 1000 * 60 * 5);
        const monotron = this._createMonotron();
        // each cent increments the frequency with a quarter note
        freq = OSCILLATOR_FREQ_RANGE_LOW * Math.pow(2, freq / 24);
        monotron.noteOn(freq);
        monotron.noteOff(this.ctx.currentTime + length / 1000);
        monotron.vco.onended = () => {
            const index = this.sources.indexOf(monotron);
            if (index >= 0) {
                setTimeout(() => {
                    this.sources.splice(index, 1);
                }, 0);
            }
        };
        this.sources.push(monotron);
    }
    start() {
        if (!this.monotron) {
            this.monotron = this._createMonotron();
            this.monotron.noteOn(DEFAULT_FREQ);
            this.sources.push(this.monotron);
        }
    }
    stop() {
        if (this.monotron) {
            this.monotron.stop();
        }
        this.monotron = null;
    }
    set pitch(freq) {
        freq = this.clamp(freq, 0, 100);
        if (this.monotron) {
            // each cent increments the frequency with a quarter note
            freq = OSCILLATOR_FREQ_RANGE_LOW * Math.pow(2, freq / 24);
            this.monotron.noteOn(freq);
        }
    }
    set volume(volume) {
        let volumeNorm = this.clamp(volume, 0, 100);
        if (this.monotron) {
            this.monotron.setVolume(volumeNorm);
        }
    }
    set wave(wave) {
        this.waveType = wave;
        if (!this.monotron) {
            return;
        }
        this.monotron.setWaveType(wave);
        // Set volume as the attenuation depends on the wave type
        this.setVolume(this.core.volume);
    }
};
__decorate([
    component(SynthComponent)
], SynthPart.prototype, "core", void 0);
SynthPart = __decorate([
    part('synth')
], SynthPart);
export { SynthPart };
//# sourceMappingURL=synth.js.map