<commit_before>import { optionsFromArguments } from "../../core/util/Defaults";
import { readOnly } from "../../core/util/Interface";
import { isNumber, isString } from "../../core/util/TypeCheck";
import { Signal } from "../../signal/Signal";
import { Source } from "../Source";
import { AMOscillator, AMOscillatorOptions } from "./AMOscillator";
import { FatOscillator, FatOscillatorOptions } from "./FatOscillator";
import { FMOscillator, FMOscillatorOptions } from "./FMOscillator";
import { Oscillator, OscillatorInterface,
	ToneOscillatorBaseType, ToneOscillatorOptions,
	ToneOscillatorType } from "./Oscillator";
import { PulseOscillator, PulseOscillatorOptions } from "./PulseOscillator";
import { PWMOscillator, PWMOscillatorOptions } from "./PWMOscillator";

/**
 * All of the oscillator types that OmniOscillator can take on
 */
type AnyOscillator = Oscillator | PWMOscillator | PulseOscillator | FatOscillator | AMOscillator | FMOscillator;

/**
 * The constructor of each of the OmniOscillator types
 */
type TypeofAnyOscillator = typeof Oscillator | typeof PWMOscillator |
	typeof PulseOscillator | typeof FatOscillator |
	typeof AMOscillator | typeof FMOscillator;

/**
 * The type of the oscillator. Can be any of the basic types: sine, square, triangle, sawtooth. Or
 * prefix the basic types with "fm", "am", or "fat" to use the FMOscillator, AMOscillator or FatOscillator
 * types. The oscillator could also be set to "pwm" or "pulse". All of the parameters of the
 * oscillator's class are accessible when the oscillator is set to that type, but throws an error
 * when it's not.
 */
type OmniOscillatorType = string;

interface OmniOscillatorOptions extends ToneOscillatorOptions {
	type: OmniOscillatorType;
}

/**
 * Select the Oscillator's Options depending on the generic type.
 */
type ConditionalOptions<Osc extends AnyOscillator> =
	Osc extends FMOscillator ? FMOscillatorOptions :
	Osc extends Oscillator ? ToneOscillatorOptions :
	Osc extends AMOscillator ? AMOscillatorOptions :
	Osc extends FatOscillator ? FatOscillatorOptions :
	Osc extends PulseOscillator ? PulseOscillatorOptions :
	Osc extends PWMOscillator ? PWMOscillatorOptions :
	ToneOscillatorOptions;

/**
 * All of the Oscillator constructor types mapped to their name.
 */
interface OmniOscillatorSource {
	"fm": FMOscillator;
	"am": AMOscillator;
	"pwm": PWMOscillator;
	"pulse": PulseOscillator;
	"oscillator": Oscillator;
	"fat": FatOscillator;
}

/**
 * The available oscillator types.
 */
export type OmniOscSourceType = keyof OmniOscillatorSource;

// Conditional Types
type IsAmOrFmOscillator<Osc, Ret> = Osc extends AMOscillator ? Ret : Osc extends FMOscillator ? Ret : undefined;
type IsFatOscillator<Osc, Ret> = Osc extends FatOscillator ? Ret : undefined;
type IsPWMOscillator<Osc, Ret> = Osc extends PWMOscillator ? Ret : undefined;
type IsPulseOscillator<Osc, Ret> = Osc extends PulseOscillator ? Ret : undefined;
type IsFMOscillator<Osc, Ret> = Osc extends FMOscillator ? Ret : undefined;

// tslint:disable-next-line: variable-name
const OmniOscillatorSourceMap: {
	[key in OmniOscSourceType] : TypeofAnyOscillator
} = {
	am: AMOscillator,
	fat: FatOscillator,
	fm: FMOscillator,
	oscillator: Oscillator,
	pulse: PulseOscillator,
	pwm: PWMOscillator,
};

/**
 * OmniOscillator aggregates Tone.Oscillator, Tone.PulseOscillator,
 * Tone.PWMOscillator, Tone.FMOscillator, Tone.AMOscillator, and Tone.FatOscillator
 * into one class. The oscillator class can be changed by setting the `type`.
 * `omniOsc.type = "pwm"` will set it to the Tone.PWMOscillator. Prefixing
 * any of the basic types ("sine", "square4", etc.) with "fm", "am", or "fat"
 * will use the FMOscillator, AMOscillator or FatOscillator respectively.
 * For example: `omniOsc.type = "fatsawtooth"` will create set the oscillator
 * to a FatOscillator of type "sawtooth".
 *
 * @param frequency The initial frequency of the oscillator.
 * @param type The type of the oscillator.
 * @example
 * var omniOsc = new OmniOscillator("C#4", "pwm");
 */
export class OmniOscillator<OscType extends AnyOscillator>
extends Source<OmniOscillatorOptions> implements OscillatorInterface {

	name = "OmniOscillator";

	/**
	 *  The frequency control.
	 */
	readonly frequency: Signal<Frequency> = new Signal({
		context: this.context,
		units: "frequency",
	});

	/**
	 *  The detune control.
	 */
	readonly detune: Signal<Cents> = new Signal({
		context: this.context,
		units: "cents",
	});

	/**
	 * The oscillator that can switch types
	 */
	private _oscillator!: AnyOscillator;

	/**
	 *  the type of the oscillator source
	 */
	private _sourceType!: OmniOscSourceType;

	constructor(options?: Partial<ConditionalOptions<OscType>>);
	constructor(frequency?: Frequency, type?: OmniOscillatorType);
	constructor() {

		super(optionsFromArguments(OmniOscillator.getDefaults(), arguments, ["frequency", "type"]));
		const options = optionsFromArguments(OmniOscillator.getDefaults(), arguments, ["frequency", "type"]);

		this.frequency.setValueAtTime(options.frequency, 0);
		this.detune.setValueAtTime(options.detune, 0);
		readOnly(this, ["frequency", "detune"]);

		// // set the oscillator
		// this.type = options.type;
		// set the options
		this.set<Oscillator>(options);
	}

	static getDefaults(): OmniOscillatorOptions {
		return Oscillator.getDefaults();
	}

	/**
	 *  start the oscillator
	 */
	protected _start(time: Time): void {
		this._oscillator.start(time);
	}

	/**
	 *  start the oscillator
	 */
	protected _stop(time: Time): void {
		this._oscillator.stop(time);
	}

	restart(time?: Time): this {
		this._oscillator.restart(time);
		return this;
	}

	/**
	 * The type of the oscillator. Can be any of the basic types: sine, square, triangle, sawtooth. Or
	 * prefix the basic types with "fm", "am", or "fat" to use the FMOscillator, AMOscillator or FatOscillator
	 * types. The oscillator could also be set to "pwm" or "pulse". All of the parameters of the
	 * oscillator's class are accessible when the oscillator is set to that type, but throws an error
	 * when it's not.
	 * @example
	 * omniOsc.type = "pwm";
	 * //modulationFrequency is parameter which is available
	 * //only when the type is "pwm".
	 * omniOsc.modulationFrequency.value = 0.5;
	 * @example
	 * //an square wave frequency modulated by a sawtooth
	 * omniOsc.type = "fmsquare";
	 * omniOsc.modulationType = "sawtooth";
	 */
	get type(): OmniOscillatorType {
		let prefix = "";
		if (["am", "fm", "fat"].some(p => this._sourceType === p)) {
			prefix = this._sourceType;
		}
		return prefix + this._oscillator.type;
	}
	set type(type) {
		if (type.substr(0, 2) === "fm") {
			this._createNewOscillator("fm");
			this._oscillator = this._oscillator as FMOscillator;
			this._oscillator.type = type.substr(2);
		} else if (type.substr(0, 2) === "am") {
			this._createNewOscillator("am");
			this._oscillator = this._oscillator as AMOscillator;
			this._oscillator.type = type.substr(2);
		} else if (type.substr(0, 3) === "fat") {
			this._createNewOscillator("fat");
			this._oscillator = this._oscillator as FatOscillator;
			this._oscillator.type = type.substr(3);
		} else if (type === "pwm") {
			this._createNewOscillator("pwm");
			this._oscillator = this._oscillator as PWMOscillator;
		} else if (type === "pulse") {
			this._createNewOscillator("pulse");
		} else {
			this._createNewOscillator("oscillator");
			this._oscillator = this._oscillator as Oscillator;
			this._oscillator.type = type;
		}
	}

	/**
	 * The partials of the waveform. A partial represents
	 * the amplitude at a harmonic. The first harmonic is the
	 * fundamental frequency, the second is the octave and so on
	 * following the harmonic series.
	 * Setting this value will automatically set the type to "custom".
	 * The value is an empty array when the type is not "custom".
	 * This is not available on "pwm" and "pulse" oscillator types.
	 * @example
	 * osc.partials = [1, 0.2, 0.01];
	 */
	get partials(): number[] {
		return this._oscillator.partials;
	}
	set partials(partials) {
		if (!this._getOscType(this._oscillator, "pulse") && !this._getOscType(this._oscillator, "pwm")) {
			this._oscillator.partials = partials;
		}
	}

	/**
	 * The partial count of the oscillator. This is not available on "pwm" and "pulse" oscillator types.
	 * @example
	 * //set the maximum number of partials
	 * osc.partialCount = 0;
	 */
	get partialCount(): number {
		return this._oscillator.partialCount;
	}
	set partialCount(partialCount) {
		if (!this._getOscType(this._oscillator, "pulse") && !this._getOscType(this._oscillator, "pwm")) {
			this._oscillator.partialCount = partialCount;
		}
	}

	/**
	 * Set the parameters at once. Either pass in an
	 * object mapping parameters to values, or to set a
	 * single parameter, by passing in a string and value.
	 * The last argument is an optional ramp time which
	 * will ramp any signal values to their destination value
	 * over the duration of the rampTime.
	 * @param  params
	 * @example
	 * //set values using an object
	 * filter.set({
	 * 	"frequency" : 300,
	 * 	"type" : "highpass"
	 * });
	 */
	set<Osc extends AnyOscillator = OscType>(props: Partial<ConditionalOptions<Osc>>): this {
		// make sure the type is set first
		if (Reflect.has(props, "type") && props.type) {
			this.type = props.type;
			// delete props.type;
		}
		// then set the rest
		super.set(props);
		return this;
	}

	/**
	 *  Get the object's attributes. Given no arguments get
	 *  will return all available object properties and their corresponding
	 *  values. Pass in a single attribute to retrieve or an array
	 *  of attributes. The attribute strings can also include a "."
	 *  to access deeper properties.
	 */
	get<Osc extends AnyOscillator = OscType>(): ConditionalOptions<Osc> {
		const options = this._oscillator.get();
		options.type = this.type;
		return options as ConditionalOptions<Osc>;
	}

	/**
	 *  connect the oscillator to the frequency and detune signals
	 */
	private _createNewOscillator(oscType: OmniOscSourceType): void {
		if (oscType !== this._sourceType) {
			this._sourceType = oscType;
			const oscConstructor = OmniOscillatorSourceMap[oscType];
			// short delay to avoid clicks on the change
			const now = this.now();
			if (this._oscillator) {
				const oldOsc = this._oscillator;
				oldOsc.stop(now);
				// dispose the old one
				this.context.setTimeout(() => oldOsc.dispose(), this.blockTime);
			}
			this._oscillator = new oscConstructor({
				// @ts-ignore
				context : this.context,
			});
			this.frequency.connect(this._oscillator.frequency);
			this.detune.connect(this._oscillator.detune);
			this._oscillator.connect(this.output);
			if (this.state === "started") {
				this._oscillator.start(now);
			}
		}
	}

	/**
	 * The phase of the oscillator in degrees.
	 */
	get phase(): Degrees {
		return this._oscillator.phase;
	}
	set phase(phase) {
		this._oscillator.phase = phase;
	}

	/**
	 * The source type of the oscillator.
	 * @example
	 * var omniOsc = new OmniOscillator(440, "fmsquare");
	 * omniOsc.sourceType // 'fm'
	 */
	get sourceType(): OmniOscSourceType {
		return this._sourceType;
	}
	set sourceType(sType) {
		// the basetype defaults to sine
		let baseType = "sine";
		if (this._oscillator.type !== "pwm" && this._oscillator.type !== "pulse") {
			baseType = this._oscillator.type;
		}

		// set the type
		if (sType === "fm") {
			this.type = "fm" + baseType;
		} else if (sType === "am") {
			this.type = "am" + baseType;
		} else if (sType === "fat") {
			this.type = "fat" + baseType;
		} else if (sType === "oscillator") {
			this.type = baseType;
		} else if (sType === "pulse") {
			this.type = "pulse";
		} else if (sType === "pwm") {
			this.type = "pwm";
		}
	}

	private _getOscType<SourceType extends OmniOscSourceType>(
		osc: AnyOscillator,
		sourceType: SourceType,
	): osc is OmniOscillatorSource[SourceType] {
		return osc instanceof OmniOscillatorSourceMap[sourceType];
	}

	/**
	 * The base type of the oscillator.
	 * @example
	 * var omniOsc = new OmniOscillator(440, "fmsquare4");
	 * omniOsc.sourceType // 'fm'
	 * omniOsc.baseType //'square'
	 * omniOsc.partialCount //4
	 */
	get baseType(): ToneOscillatorBaseType {
		return this._oscillator.baseType;
	}
	set baseType(baseType) {
		if (!this._getOscType(this._oscillator, "pulse") &&
			!this._getOscType(this._oscillator, "pwm") &&
			baseType !== "pulse" && baseType !== "pwm") {
			this._oscillator.baseType = baseType;
		}
	}

	/**
	 * The width of the oscillator when sourceType === "pulse".
	 * @example
	 * var omniOsc = new OmniOscillator(440, "pulse");
	 * //can access the width attribute only if type === "pulse"
	 * omniOsc.width.value = 0.2;
	 */
	get width(): IsPulseOscillator<OscType, Signal<AudioRange>> {
		if (this._getOscType(this._oscillator, "pulse")) {
			return this._oscillator.width as IsPulseOscillator<OscType, Signal<AudioRange>> ;
		} else {
			return undefined as IsPulseOscillator<OscType, Signal<AudioRange>> ;
		}
	}

	/**
	 * The number of detuned oscillators, when sourceType === "fat".
	 */
	get count(): IsFatOscillator<OscType, number> {
		if (this._getOscType(this._oscillator, "fat")) {
			return this._oscillator.count as IsFatOscillator<OscType, number>;
		} else {
			return undefined as IsFatOscillator<OscType, number>;
		}
	}
	set count(count) {
		if (this._getOscType(this._oscillator, "fat") && isNumber(count)) {
			this._oscillator.count = count;
		}
	}

	/**
	 * The detune spread between the oscillators. If "count" is
	 * set to 3 oscillators and the "spread" is set to 40,
	 * the three oscillators would be detuned like this: [-20, 0, 20]
	 * for a total detune spread of 40 cents. See Tone.FatOscillator
	 * for more info.
	 */
	get spread(): IsFatOscillator<OscType, Cents> {
		if (this._getOscType(this._oscillator, "fat")) {
			return this._oscillator.spread as IsFatOscillator<OscType, Cents>;
		} else {
			return undefined as IsFatOscillator<OscType, Cents>;
		}
	}
	set spread(spread) {
		if (this._getOscType(this._oscillator, "fat") && isNumber(spread)) {
			this._oscillator.spread = spread;
		}
	}

	/**
	 * The type of the modulator oscillator. Only if the oscillator
	 * is set to "am" or "fm" types. see. Tone.AMOscillator or Tone.FMOscillator
	 * for more info.
	 */
	get modulationType(): IsAmOrFmOscillator<OscType, ToneOscillatorType> {
		if (this._getOscType(this._oscillator, "fm") || this._getOscType(this._oscillator, "am")) {
			return this._oscillator.modulationType as IsAmOrFmOscillator<OscType, ToneOscillatorType>;
		} else {
			return undefined as IsAmOrFmOscillator<OscType, ToneOscillatorType>;
		}
	}
	set modulationType(mType) {
		if ((this._getOscType(this._oscillator, "fm") || this._getOscType(this._oscillator, "am")) && isString(mType)) {
			this._oscillator.modulationType = mType;
		}
	}

	/**
	 * The modulation index which is in essence the depth or amount of the modulation. In other terms it is the
	 * ratio of the frequency of the modulating signal (mf) to the amplitude of the
	 * modulating signal (ma) -- as in ma/mf.
	 * See Tone.FMOscillator for more info.
	 */
	get modulationIndex(): IsFMOscillator<OscType, Signal<Positive>> {
		if (this._getOscType(this._oscillator, "fm")) {
			return this._oscillator.modulationIndex as IsFMOscillator<OscType, Signal<Positive>>;
		} else {
			return undefined as IsFMOscillator<OscType, Signal<Positive>>;
		}
	}

	/**
	 *  Harmonicity is the frequency ratio between the carrier and the modulator oscillators.
	 *  A harmonicity of 1 gives both oscillators the same frequency.
	 *  Harmonicity = 2 means a change of an octave. See Tone.AMOscillator or Tone.FMOscillator
	 *  for more info.
	 */
	get harmonicity(): IsAmOrFmOscillator<OscType, Signal<Positive>> {
		if (this._getOscType(this._oscillator, "fm") || this._getOscType(this._oscillator, "am")) {
			return this._oscillator.harmonicity as IsAmOrFmOscillator<OscType, Signal<Positive>>;
		} else {
			return undefined as IsAmOrFmOscillator<OscType, Signal<Positive>>;
		}
	}

	/**
	 * The modulationFrequency Signal of the oscillator
	 * (only if the oscillator type is set to pwm). See
	 * Tone.PWMOscillator for more info.
	 * @example
	 * var omniOsc = new OmniOscillator(440, "pwm");
	 * //can access the modulationFrequency attribute only if type === "pwm"
	 * omniOsc.modulationFrequency.value = 0.2;
	 */
	get modulationFrequency(): IsPWMOscillator<OscType, Signal<Frequency>> {
		if (this._getOscType(this._oscillator, "pwm")) {
			return this._oscillator.modulationFrequency as IsPWMOscillator<OscType, Signal<Frequency>>;
		} else {
			return undefined as IsPWMOscillator<OscType, Signal<Frequency>>;
		}
	}

	/**
	 *  Clean up.
	 */
	dispose(): this {
		super.dispose();
		this.detune.dispose();
		this.frequency.dispose();
		this._oscillator.dispose();
		return this;
	}
}
<commit_msg>deriving OmniOscillatorOptions from ConditionalOptions<commit_after>import { optionsFromArguments } from "../../core/util/Defaults";
import { readOnly } from "../../core/util/Interface";
import { isNumber, isString } from "../../core/util/TypeCheck";
import { Signal } from "../../signal/Signal";
import { Source } from "../Source";
import { AMOscillator, AMOscillatorOptions } from "./AMOscillator";
import { FatOscillator, FatOscillatorOptions } from "./FatOscillator";
import { FMOscillator, FMOscillatorOptions } from "./FMOscillator";
import { Oscillator, OscillatorInterface,
	ToneOscillatorBaseType, ToneOscillatorOptions,
	ToneOscillatorType } from "./Oscillator";
import { PulseOscillator, PulseOscillatorOptions } from "./PulseOscillator";
import { PWMOscillator, PWMOscillatorOptions } from "./PWMOscillator";

/**
 * All of the oscillator types that OmniOscillator can take on
 */
type AnyOscillator = Oscillator | PWMOscillator | PulseOscillator | FatOscillator | AMOscillator | FMOscillator;

/**
 * The constructor of each of the OmniOscillator types
 */
type TypeofAnyOscillator = typeof Oscillator | typeof PWMOscillator |
	typeof PulseOscillator | typeof FatOscillator |
	typeof AMOscillator | typeof FMOscillator;

/**
 * The type of the oscillator. Can be any of the basic types: sine, square, triangle, sawtooth. Or
 * prefix the basic types with "fm", "am", or "fat" to use the FMOscillator, AMOscillator or FatOscillator
 * types. The oscillator could also be set to "pwm" or "pulse". All of the parameters of the
 * oscillator's class are accessible when the oscillator is set to that type, but throws an error
 * when it's not.
 */
type OmniOscillatorType = string;

/**
 * Select the Oscillator's Options depending on the generic type.
 */
type ConditionalOptions<Osc extends AnyOscillator> =
	Osc extends FMOscillator ? FMOscillatorOptions :
	Osc extends Oscillator ? ToneOscillatorOptions :
	Osc extends AMOscillator ? AMOscillatorOptions :
	Osc extends FatOscillator ? FatOscillatorOptions :
	Osc extends PulseOscillator ? PulseOscillatorOptions :
	Osc extends PWMOscillator ? PWMOscillatorOptions :
	ToneOscillatorOptions;

/**
 * The aggregate options of all of the oscillators
 */
export type OmniOscillatorOptions = ConditionalOptions<AnyOscillator>;

/**
 * All of the Oscillator constructor types mapped to their name.
 */
interface OmniOscillatorSource {
	"fm": FMOscillator;
	"am": AMOscillator;
	"pwm": PWMOscillator;
	"pulse": PulseOscillator;
	"oscillator": Oscillator;
	"fat": FatOscillator;
}

/**
 * The available oscillator types.
 */
export type OmniOscSourceType = keyof OmniOscillatorSource;

// Conditional Types
type IsAmOrFmOscillator<Osc, Ret> = Osc extends AMOscillator ? Ret : Osc extends FMOscillator ? Ret : undefined;
type IsFatOscillator<Osc, Ret> = Osc extends FatOscillator ? Ret : undefined;
type IsPWMOscillator<Osc, Ret> = Osc extends PWMOscillator ? Ret : undefined;
type IsPulseOscillator<Osc, Ret> = Osc extends PulseOscillator ? Ret : undefined;
type IsFMOscillator<Osc, Ret> = Osc extends FMOscillator ? Ret : undefined;

// tslint:disable-next-line: variable-name
const OmniOscillatorSourceMap: {
	[key in OmniOscSourceType] : TypeofAnyOscillator
} = {
	am: AMOscillator,
	fat: FatOscillator,
	fm: FMOscillator,
	oscillator: Oscillator,
	pulse: PulseOscillator,
	pwm: PWMOscillator,
};

/**
 * OmniOscillator aggregates Tone.Oscillator, Tone.PulseOscillator,
 * Tone.PWMOscillator, Tone.FMOscillator, Tone.AMOscillator, and Tone.FatOscillator
 * into one class. The oscillator class can be changed by setting the `type`.
 * `omniOsc.type = "pwm"` will set it to the Tone.PWMOscillator. Prefixing
 * any of the basic types ("sine", "square4", etc.) with "fm", "am", or "fat"
 * will use the FMOscillator, AMOscillator or FatOscillator respectively.
 * For example: `omniOsc.type = "fatsawtooth"` will create set the oscillator
 * to a FatOscillator of type "sawtooth".
 *
 * @param frequency The initial frequency of the oscillator.
 * @param type The type of the oscillator.
 * @example
 * var omniOsc = new OmniOscillator("C#4", "pwm");
 */
export class OmniOscillator<OscType extends AnyOscillator>
extends Source<OmniOscillatorOptions> implements OscillatorInterface {

	name = "OmniOscillator";

	/**
	 *  The frequency control.
	 */
	readonly frequency: Signal<Frequency> = new Signal({
		context: this.context,
		units: "frequency",
	});

	/**
	 *  The detune control.
	 */
	readonly detune: Signal<Cents> = new Signal({
		context: this.context,
		units: "cents",
	});

	/**
	 * The oscillator that can switch types
	 */
	private _oscillator!: AnyOscillator;

	/**
	 *  the type of the oscillator source
	 */
	private _sourceType!: OmniOscSourceType;

	constructor(options?: Partial<ConditionalOptions<OscType>>);
	constructor(frequency?: Frequency, type?: OmniOscillatorType);
	constructor() {

		super(optionsFromArguments(OmniOscillator.getDefaults(), arguments, ["frequency", "type"]));
		const options = optionsFromArguments(OmniOscillator.getDefaults(), arguments, ["frequency", "type"]);

		this.frequency.setValueAtTime(options.frequency, 0);
		this.detune.setValueAtTime(options.detune, 0);
		readOnly(this, ["frequency", "detune"]);

		// // set the oscillator
		// this.type = options.type;
		// set the options
		this.set<Oscillator>(options);
	}

	static getDefaults(): OmniOscillatorOptions {
		return Oscillator.getDefaults();
	}

	/**
	 *  start the oscillator
	 */
	protected _start(time: Time): void {
		this._oscillator.start(time);
	}

	/**
	 *  start the oscillator
	 */
	protected _stop(time: Time): void {
		this._oscillator.stop(time);
	}

	restart(time?: Time): this {
		this._oscillator.restart(time);
		return this;
	}

	/**
	 * The type of the oscillator. Can be any of the basic types: sine, square, triangle, sawtooth. Or
	 * prefix the basic types with "fm", "am", or "fat" to use the FMOscillator, AMOscillator or FatOscillator
	 * types. The oscillator could also be set to "pwm" or "pulse". All of the parameters of the
	 * oscillator's class are accessible when the oscillator is set to that type, but throws an error
	 * when it's not.
	 * @example
	 * omniOsc.type = "pwm";
	 * //modulationFrequency is parameter which is available
	 * //only when the type is "pwm".
	 * omniOsc.modulationFrequency.value = 0.5;
	 * @example
	 * //an square wave frequency modulated by a sawtooth
	 * omniOsc.type = "fmsquare";
	 * omniOsc.modulationType = "sawtooth";
	 */
	get type(): OmniOscillatorType {
		let prefix = "";
		if (["am", "fm", "fat"].some(p => this._sourceType === p)) {
			prefix = this._sourceType;
		}
		return prefix + this._oscillator.type;
	}
	set type(type) {
		if (type.substr(0, 2) === "fm") {
			this._createNewOscillator("fm");
			this._oscillator = this._oscillator as FMOscillator;
			this._oscillator.type = type.substr(2);
		} else if (type.substr(0, 2) === "am") {
			this._createNewOscillator("am");
			this._oscillator = this._oscillator as AMOscillator;
			this._oscillator.type = type.substr(2);
		} else if (type.substr(0, 3) === "fat") {
			this._createNewOscillator("fat");
			this._oscillator = this._oscillator as FatOscillator;
			this._oscillator.type = type.substr(3);
		} else if (type === "pwm") {
			this._createNewOscillator("pwm");
			this._oscillator = this._oscillator as PWMOscillator;
		} else if (type === "pulse") {
			this._createNewOscillator("pulse");
		} else {
			this._createNewOscillator("oscillator");
			this._oscillator = this._oscillator as Oscillator;
			this._oscillator.type = type;
		}
	}

	/**
	 * The partials of the waveform. A partial represents
	 * the amplitude at a harmonic. The first harmonic is the
	 * fundamental frequency, the second is the octave and so on
	 * following the harmonic series.
	 * Setting this value will automatically set the type to "custom".
	 * The value is an empty array when the type is not "custom".
	 * This is not available on "pwm" and "pulse" oscillator types.
	 * @example
	 * osc.partials = [1, 0.2, 0.01];
	 */
	get partials(): number[] {
		return this._oscillator.partials;
	}
	set partials(partials) {
		if (!this._getOscType(this._oscillator, "pulse") && !this._getOscType(this._oscillator, "pwm")) {
			this._oscillator.partials = partials;
		}
	}

	/**
	 * The partial count of the oscillator. This is not available on "pwm" and "pulse" oscillator types.
	 * @example
	 * //set the maximum number of partials
	 * osc.partialCount = 0;
	 */
	get partialCount(): number {
		return this._oscillator.partialCount;
	}
	set partialCount(partialCount) {
		if (!this._getOscType(this._oscillator, "pulse") && !this._getOscType(this._oscillator, "pwm")) {
			this._oscillator.partialCount = partialCount;
		}
	}

	/**
	 * Set the parameters at once. Either pass in an
	 * object mapping parameters to values, or to set a
	 * single parameter, by passing in a string and value.
	 * The last argument is an optional ramp time which
	 * will ramp any signal values to their destination value
	 * over the duration of the rampTime.
	 * @param  params
	 * @example
	 * //set values using an object
	 * filter.set({
	 * 	"frequency" : 300,
	 * 	"type" : "highpass"
	 * });
	 */
	set<Osc extends AnyOscillator = OscType>(props: Partial<ConditionalOptions<Osc>>): this {
		// make sure the type is set first
		if (Reflect.has(props, "type") && props.type) {
			this.type = props.type;
			// delete props.type;
		}
		// then set the rest
		super.set(props);
		return this;
	}

	/**
	 *  Get the object's attributes. Given no arguments get
	 *  will return all available object properties and their corresponding
	 *  values. Pass in a single attribute to retrieve or an array
	 *  of attributes. The attribute strings can also include a "."
	 *  to access deeper properties.
	 */
	get<Osc extends AnyOscillator = OscType>(): ConditionalOptions<Osc> {
		const options = this._oscillator.get();
		options.type = this.type;
		return options as ConditionalOptions<Osc>;
	}

	/**
	 *  connect the oscillator to the frequency and detune signals
	 */
	private _createNewOscillator(oscType: OmniOscSourceType): void {
		if (oscType !== this._sourceType) {
			this._sourceType = oscType;
			const oscConstructor = OmniOscillatorSourceMap[oscType];
			// short delay to avoid clicks on the change
			const now = this.now();
			if (this._oscillator) {
				const oldOsc = this._oscillator;
				oldOsc.stop(now);
				// dispose the old one
				this.context.setTimeout(() => oldOsc.dispose(), this.blockTime);
			}
			this._oscillator = new oscConstructor({
				// @ts-ignore
				context : this.context,
			});
			this.frequency.connect(this._oscillator.frequency);
			this.detune.connect(this._oscillator.detune);
			this._oscillator.connect(this.output);
			if (this.state === "started") {
				this._oscillator.start(now);
			}
		}
	}

	/**
	 * The phase of the oscillator in degrees.
	 */
	get phase(): Degrees {
		return this._oscillator.phase;
	}
	set phase(phase) {
		this._oscillator.phase = phase;
	}

	/**
	 * The source type of the oscillator.
	 * @example
	 * var omniOsc = new OmniOscillator(440, "fmsquare");
	 * omniOsc.sourceType // 'fm'
	 */
	get sourceType(): OmniOscSourceType {
		return this._sourceType;
	}
	set sourceType(sType) {
		// the basetype defaults to sine
		let baseType = "sine";
		if (this._oscillator.type !== "pwm" && this._oscillator.type !== "pulse") {
			baseType = this._oscillator.type;
		}

		// set the type
		if (sType === "fm") {
			this.type = "fm" + baseType;
		} else if (sType === "am") {
			this.type = "am" + baseType;
		} else if (sType === "fat") {
			this.type = "fat" + baseType;
		} else if (sType === "oscillator") {
			this.type = baseType;
		} else if (sType === "pulse") {
			this.type = "pulse";
		} else if (sType === "pwm") {
			this.type = "pwm";
		}
	}

	private _getOscType<SourceType extends OmniOscSourceType>(
		osc: AnyOscillator,
		sourceType: SourceType,
	): osc is OmniOscillatorSource[SourceType] {
		return osc instanceof OmniOscillatorSourceMap[sourceType];
	}

	/**
	 * The base type of the oscillator.
	 * @example
	 * var omniOsc = new OmniOscillator(440, "fmsquare4");
	 * omniOsc.sourceType // 'fm'
	 * omniOsc.baseType //'square'
	 * omniOsc.partialCount //4
	 */
	get baseType(): ToneOscillatorBaseType {
		return this._oscillator.baseType;
	}
	set baseType(baseType) {
		if (!this._getOscType(this._oscillator, "pulse") &&
			!this._getOscType(this._oscillator, "pwm") &&
			baseType !== "pulse" && baseType !== "pwm") {
			this._oscillator.baseType = baseType;
		}
	}

	/**
	 * The width of the oscillator when sourceType === "pulse".
	 * @example
	 * var omniOsc = new OmniOscillator(440, "pulse");
	 * //can access the width attribute only if type === "pulse"
	 * omniOsc.width.value = 0.2;
	 */
	get width(): IsPulseOscillator<OscType, Signal<AudioRange>> {
		if (this._getOscType(this._oscillator, "pulse")) {
			return this._oscillator.width as IsPulseOscillator<OscType, Signal<AudioRange>> ;
		} else {
			return undefined as IsPulseOscillator<OscType, Signal<AudioRange>> ;
		}
	}

	/**
	 * The number of detuned oscillators, when sourceType === "fat".
	 */
	get count(): IsFatOscillator<OscType, number> {
		if (this._getOscType(this._oscillator, "fat")) {
			return this._oscillator.count as IsFatOscillator<OscType, number>;
		} else {
			return undefined as IsFatOscillator<OscType, number>;
		}
	}
	set count(count) {
		if (this._getOscType(this._oscillator, "fat") && isNumber(count)) {
			this._oscillator.count = count;
		}
	}

	/**
	 * The detune spread between the oscillators. If "count" is
	 * set to 3 oscillators and the "spread" is set to 40,
	 * the three oscillators would be detuned like this: [-20, 0, 20]
	 * for a total detune spread of 40 cents. See Tone.FatOscillator
	 * for more info.
	 */
	get spread(): IsFatOscillator<OscType, Cents> {
		if (this._getOscType(this._oscillator, "fat")) {
			return this._oscillator.spread as IsFatOscillator<OscType, Cents>;
		} else {
			return undefined as IsFatOscillator<OscType, Cents>;
		}
	}
	set spread(spread) {
		if (this._getOscType(this._oscillator, "fat") && isNumber(spread)) {
			this._oscillator.spread = spread;
		}
	}

	/**
	 * The type of the modulator oscillator. Only if the oscillator
	 * is set to "am" or "fm" types. see. Tone.AMOscillator or Tone.FMOscillator
	 * for more info.
	 */
	get modulationType(): IsAmOrFmOscillator<OscType, ToneOscillatorType> {
		if (this._getOscType(this._oscillator, "fm") || this._getOscType(this._oscillator, "am")) {
			return this._oscillator.modulationType as IsAmOrFmOscillator<OscType, ToneOscillatorType>;
		} else {
			return undefined as IsAmOrFmOscillator<OscType, ToneOscillatorType>;
		}
	}
	set modulationType(mType) {
		if ((this._getOscType(this._oscillator, "fm") || this._getOscType(this._oscillator, "am")) && isString(mType)) {
			this._oscillator.modulationType = mType;
		}
	}

	/**
	 * The modulation index which is in essence the depth or amount of the modulation. In other terms it is the
	 * ratio of the frequency of the modulating signal (mf) to the amplitude of the
	 * modulating signal (ma) -- as in ma/mf.
	 * See Tone.FMOscillator for more info.
	 */
	get modulationIndex(): IsFMOscillator<OscType, Signal<Positive>> {
		if (this._getOscType(this._oscillator, "fm")) {
			return this._oscillator.modulationIndex as IsFMOscillator<OscType, Signal<Positive>>;
		} else {
			return undefined as IsFMOscillator<OscType, Signal<Positive>>;
		}
	}

	/**
	 *  Harmonicity is the frequency ratio between the carrier and the modulator oscillators.
	 *  A harmonicity of 1 gives both oscillators the same frequency.
	 *  Harmonicity = 2 means a change of an octave. See Tone.AMOscillator or Tone.FMOscillator
	 *  for more info.
	 */
	get harmonicity(): IsAmOrFmOscillator<OscType, Signal<Positive>> {
		if (this._getOscType(this._oscillator, "fm") || this._getOscType(this._oscillator, "am")) {
			return this._oscillator.harmonicity as IsAmOrFmOscillator<OscType, Signal<Positive>>;
		} else {
			return undefined as IsAmOrFmOscillator<OscType, Signal<Positive>>;
		}
	}

	/**
	 * The modulationFrequency Signal of the oscillator
	 * (only if the oscillator type is set to pwm). See
	 * Tone.PWMOscillator for more info.
	 * @example
	 * var omniOsc = new OmniOscillator(440, "pwm");
	 * //can access the modulationFrequency attribute only if type === "pwm"
	 * omniOsc.modulationFrequency.value = 0.2;
	 */
	get modulationFrequency(): IsPWMOscillator<OscType, Signal<Frequency>> {
		if (this._getOscType(this._oscillator, "pwm")) {
			return this._oscillator.modulationFrequency as IsPWMOscillator<OscType, Signal<Frequency>>;
		} else {
			return undefined as IsPWMOscillator<OscType, Signal<Frequency>>;
		}
	}

	/**
	 *  Clean up.
	 */
	dispose(): this {
		super.dispose();
		this.detune.dispose();
		this.frequency.dispose();
		this._oscillator.dispose();
		return this;
	}
}

{'fraction_non_alphanumeric': 0.12302034521620499, 'fraction_numerical': 0.0032623524526958895, 'mean_word_length': 4.00504675671664, 'pattern_counts': {'":': 12, '<': 110, '<?xml version=': 0, '>': 114, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2636143', 'n_tokens_mistral': 12605, 'n_tokens_neox': 11114, 'n_words': 3091}