// IMPORTS --------------------------------------------------------------------- import beep/internal.{AudioNode, AudioParam, Connection, NodeProperty} import gleam/dynamic.{type Dynamic} import gleam/javascript/promise.{type Promise} import gleam/option.{None, Some} // TYPES ----------------------------------------------------------------------- /// An instance of the stateful audio engine that can construct Web Audio nodes /// and process sound in real time. In most cases only one instance of an audio /// engine is needed for an application and can be constructed using the [`new`](#new) /// function. /// /// Audio graphs are constructed much in the same way view code might construct /// HTML, and are then rendered to the audio engine using the [`commit`](#commit) /// function. /// pub type AudioEngine /// A description of a Web Audio node in the audio graph such as an oscillator /// or gain node. Constructing an audio node does not have any effect on the /// running audio engine until the node is commited to the engine as part of an /// audio graph passed to the [`commit`](#commit) function. /// pub type AudioNode = internal.AudioNode /// /// pub type Property = internal.Property /// Represents the current state of an audio engine. /// pub type AudioEngineState { Closed Interrupted Running Suspended } pub type NameRegistry = internal.NameRegistry pub type Name = internal.Name pub type Namespace = internal.Namespace // CONSTRUCTORS ---------------------------------------------------------------- /// Construct a new [`AudioEngine`](#AudioEngine) instance. /// @external(javascript, "./beep.ffi.mjs", "engine") pub fn new() -> Result(AudioEngine, Nil) { Error(Nil) } /// /// pub fn node( kind: String, properties: List(Property), connections: List(AudioNode), ) -> AudioNode { AudioNode(name: None, kind:, properties:, connections:) } /// /// pub fn named( name: Name, kind: String, properties: List(Property), connections: List(AudioNode), ) -> AudioNode { AudioNode(name: Some(name), kind:, properties:, connections:) } /// Create a connection to a [named node](#named) elsewhere in the audio graph. /// Cycles are allowed – feedback loops are a very important part of audio synthesis /// – but be careful to avoid loops that run away to infinity and explode your /// speakers! /// /// pub fn connection(to name: Name) -> AudioNode { Connection(name:, param: None) } /// Like [`connection`](#connection), this function lets you declare a connection /// to a [named node](#named) somewhere else in the audio graph. The second parameter /// is the name of the [AudioParam](https://developer.mozilla.org/en-US/docs/Web/API/AudioParam) /// on the target node to connect to. /// /// This lets the output of an audio node directly control a parameter like the /// frequency of an oscillator: that's called modulation! /// pub fn modulation(name: Name, param: String) -> AudioNode { Connection(name:, param: Some(param)) } /// Set an [AudioParam](https://developer.mozilla.org/en-US/docs/Web/API/AudioParam) /// on an audio node to a specific value. What params are available depends on the /// type of node and you should reference the [Web Audio API documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) /// for details. /// /// Most of the audio params for the built-in Web Audio nodes can be found in the /// [`beep/param`](./beep/param.html) module, but you may occassionally need this /// function if I've missed anything. /// pub fn param(name: String, value: Float) -> Property { AudioParam(name:, value:) } /// Set an object property directly on an audio node. Properties are used to /// configure different aspects of an audio node, like controlling the type of /// waveform an oscillator produces or the kind of filter a biquad filter node /// uses. /// /// You'll want to reference the [Web Audio API documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) /// for details on what properties are available for each type of node. /// /// Unlike [`param`s](#param), properties cannot be modulated by connecting the /// output of another audio node to them as they could be arbitrary JavaScript /// values. /// pub fn property(name: String, value: Dynamic) -> Property { NodeProperty(name:, value:) } /// /// pub fn name(registry: NameRegistry) -> Name { internal.name(registry) } /// /// pub fn namespace(registry: NameRegistry) -> Namespace { internal.namespace(registry) } /// /// pub fn namespaced_name(namespace: Namespace) -> Name { internal.namespaced_name(namespace) } // AUDIO NODES ----------------------------------------------------------------- pub fn oscillator( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("OscillatorNode", properties, connections) } pub fn gain( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("GainNode", properties, connections) } pub fn analyser( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("AnalyserNode", properties, connections) } pub fn biquad_filter( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("BiquadFilterNode", properties, connections) } pub fn channel_merger( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("ChannelMergerNode", properties, connections) } pub fn channel_splitter( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("ChannelSplitterNode", properties, connections) } pub fn constant_source( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("ConstantSourceNode", properties, connections) } pub fn delay( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("DelayNode", properties, connections) } pub fn dynamics_compressor( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("DynamicsCompressorNode", properties, connections) } pub fn panner( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("PannerNode", properties, connections) } pub fn stereo_panner( properties: List(Property), connections: List(AudioNode), ) -> AudioNode { node("StereoPannerNode", properties, connections) } pub fn destination() -> AudioNode { node("AudioDestinationNode", [], []) } // QUERIES --------------------------------------------------------------------- /// /// @external(javascript, "./beep.ffi.mjs", "state") pub fn state(_context: AudioEngine) -> AudioEngineState { Closed } /// /// @external(javascript, "./beep.ffi.mjs", "currentTime") pub fn current_time(_context: AudioEngine) -> Float { 0.0 } // MANIPULATIONS --------------------------------------------------------------- @external(javascript, "./beep.ffi.mjs", "commit") pub fn commit( _context: AudioEngine, _callback: fn(NameRegistry) -> List(AudioNode), ) -> Nil { Nil } @external(javascript, "./beep.ffi.mjs", "resume") pub fn resume(context: AudioEngine) -> Promise(Nil) @external(javascript, "./beep.ffi.mjs", "suspend") pub fn suspend(context: AudioEngine) -> Promise(Nil) @external(javascript, "./beep.ffi.mjs", "close") pub fn close(context: AudioEngine) -> Promise(Nil)