1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Sorceress
// Copyright (C) 2021  Wesley Merkel
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#![forbid(unsafe_code)]

//! A Rust environment for sound synthesis and algorithmic composition, powered by
//! [SuperCollider](https://supercollider.github.io/).
//!
//! # Introduction
//!
//! This crate provides Rust bindings to the SuperCollider audio synthesis and algorithmic
//! composition platform.
//!
//! There is no better overview of SuperCollider than the summary from its home page:
//!
//! > "SuperCollider features three major components:
//! >
//! > * **scsynth**, a real-time audio server, forms the core of the platform. It features 400+
//! >   unit generators (“UGens”) for analysis, synthesis, and processing. Its granularity allows
//! >   the fluid combination of many known and unknown audio techniques, moving between additive
//! >   and subtractive synthesis, FM, granular synthesis, FFT, and physical modeling. You can
//! >   write your own UGens in C++, and users have already contributed several hundred more to the
//! >   sc3-plugins repository.
//! >
//! > * **sclang**, an interpreted programming language. It is focused on sound, but not limited to
//! >   any specific domain. sclang controls scsynth via Open Sound Control. You can use it for
//! >   algorithmic composition and sequencing, finding new sound synthesis methods, connecting
//! >   your app to external hardware including MIDI controllers, network music, writing GUIs and
//! >   visual displays, or for your daily programming experiments. It has a stock of
//! >   user-contributed extensions called Quarks.
//! >
//! > * **scide** is an editor for sclang with an integrated help system."
//!
//! Sorceress is a Rust alternative to **sclang**. It provides features for interacting with
//! SuperCollider similar to those found in sclang. It currently contains:
//!
//! * [`synthdef`](crate::synthdef) - Core types for designing new synthesizers by creating "synth
//!   definitions". Synth definitions are graphs of connected unit generators.
//!
//! * [`ugen`](crate::ugen) - A library of unit generators or "UGens". **scsynth** provides
//!   hundreds of unit generators and sorceress aims to provide interfaces for all of them some
//!   day, but its going to take a while. Please raise an issue on the source repository if there
//!   are UGens that you'd like to see implemented so I know what to prioritize.
//!
//! * [`pattern`](crate::pattern) - A module for defining patterns. Patterns are collections of
//!   events with timing information. Patterns are created by combining collections of events in
//!   sequence or in parallel. Patterns are the primary way of composing music with sorceress. The
//!   [`scheduler`](crate::scheduler) module can be used in combination with the
//!   [`pattern::player`](crate::pattern::player) submodule to play patterns.
//!
//! * [`scheduler`](crate::scheduler) - A module for running code on a recurring schedule. The
//!   scheduler is responsible handling the timing details of executing patterns.
//!
//! * [`server`](crate::server) - A [`Server`](server::Server) type that manages communication
//!   with scsynth and Rust definitions of all of the OSC commands understood by the server.
//!   Similar to the library of UGens, that are many commands to implement, around 65, so please
//!   create an issue if you would like to see certain command implemented sooner.
//!
//! # Examples
//!
//! This example plays the opening melody Stravinsky's The Rite of Spring.
//!
//! ```no_run
//! use sorceress::{
//!     pattern::{
//!         player::{Player, Tempo},
//!         sequence, Pattern,
//!     },
//!     scheduler::Scheduler,
//!     server::{self, Bundle, Control, Server},
//!     synthdef::{encoder::encode_synth_defs, Input, SynthDef},
//!     ugen,
//! };
//!
//! type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
//!
//! fn main() -> Result<()> {
//!     // Create a new SuperCollider server client. A SuperCollider server must be started
//!     // outside of this program and be configured to listen on this UDP port.
//!     let server = Server::connect("127.0.0.1:57110")?;
//!
//!     // Create a synth definition.
//!     let sine_wave = {
//!         // Create and name the synth definition. We will use the name given here to refer
//!         // to this synth definition in all server commands.
//!         SynthDef::new("sine_wave", |params| {
//!             // Parameters allow synths created by this definition to be configured with
//!             // different values when they are created, or be changed while they are playing.
//!             let freq = params.named("freq", 0.0);
//!
//!             // Creates a sine wave that oscillates between -7 and 7, 5 times per second.
//!             let vibrato = ugen::SinOsc::ar().freq(5).mul(7);
//!
//!             // Sends the audio signal to bus 0, which represents your speakers.
//!             ugen::OffsetOut::ar()
//!                 .bus(0)
//!                 // Pan2 turns a mono signal into a stereo signal.
//!                 .channels(
//!                     ugen::Pan2::ar()
//!                         // SuperCollider lets us plug UGens into the inputs of other UGens
//!                         // to create complex signal processing graphs. Here we are using
//!                         // the vibrato SinOsc UGen defined above to slightly oscillate the
//!                         // pitch of the sine wav we will actually hear.
//!                         .input(freq.mul(vibrato)),
//!                 )
//!         })
//!     };
//!
//!     // Send the synth definition to the SuperCollider server so that we can make synths
//!     // using it.
//!     let encoded_synthdef = encode_synth_defs(vec![sine_wave]);
//!     server.send_sync(server::SynthDefRecv::new(&encoded_synthdef))?;
//!
//!     // Play a sequence of notes. The first number is the duration in beats and the second
//!     // number is the midi note number.
//!     let pattern: Pattern<Event> = sequence(|s| {
//!         s.play(1.2, Event::StartNote(72.0));
//!         s.play(1.5, Event::SetPitch(71.0));
//!         s.play(1.5, Event::SetPitch(72.0));
//!         s.play(3.0, Event::SetPitch(71.0));
//!         s.play(3.0, Event::SetPitch(67.0));
//!         s.play(3.0, Event::SetPitch(64.0));
//!         s.play(3.0, Event::SetPitch(71.0));
//!         s.play(6.0, Event::SetPitch(69.0));
//!         s.play(4.0, Event::SetPitch(72.0));
//!         s.play(2.0, Event::SetPitch(71.0));
//!         s.play(2.0, Event::SetPitch(72.0));
//!         s.play(4.0, Event::SetPitch(71.0));
//!         s.play(4.0, Event::SetPitch(67.0));
//!         s.play(4.0, Event::SetPitch(64.0));
//!         s.play(4.0, Event::SetPitch(71.0));
//!         s.play(6.0, Event::SetPitch(69.0));
//!     });
//!
//!     let event_handler = |time, event| {
//!         // We launch a single synth and set its pitch multiple times to create a melody. Synth
//!         // IDs are numbers that let us to refer to synths after we crate them.
//!         let synth_id: i32 = 1000;
//!
//!         let result = match event {
//!             Event::StartNote(midi_note) => {
//!                 // Create a new synth from the synth definition we registered earlier.
//!                 let command = server::SynthNew::new("sine_wave", 1)
//!                     // Convert the midi note into hertz. The "freq" string refers to the string
//!                     // given to the `Parameter` in the synth definition above.
//!                     .controls(vec![Control::new("freq", midi_to_hz(midi_note))])
//!                     // Explicitly set the synth ID so that we can control it later.
//!                     .synth_id(synth_id);
//!
//!                 server
//!                     // Ask SuperCollider to schedule this event at exactly this time.
//!                     .send(Bundle::new(time, vec![command]))
//!             }
//!             Event::SetPitch(midi_note) => {
//!                 // Convert the midi note into hertz.
//!                 let controls = vec![Control::new("freq", midi_to_hz(midi_note))];
//!
//!                 // Changes the pitch of the actively playing synth.
//!                 let command = server::NodeSet::new(synth_id, controls);
//!                 server.send(Bundle::new(time, vec![command]))
//!             }
//!         };
//!         if let Err(err) = result {
//!             println!("error: sending event to server: {}", err);
//!         };
//!     };
//!
//!     // Play the pattern using the scheduler.
//!     Scheduler::new().run(Player::new(pattern, event_handler).tempo(Tempo::from_bpm(60.0)))?;
//!
//!     // Stop all sounds on the server.
//!     server.reset()?;
//!
//!     Ok(())
//! }
//!
//! // Our event type.
//! enum Event {
//!     StartNote(f32),
//!     SetPitch(f32),
//! }
//!
//! // Convert a midi note number into a frequency in hertz.
//! fn midi_to_hz(note: f32) -> f32 {
//!     let exp = (note - 69.0) / 12.0;
//!     440.0 * 2f32.powf(exp)
//! }
//! ```
//!
//! # Learning SuperCollider
//!
//! SuperCollider is a powerful platform and there are many concepts to learn on the path to
//! proficiency. Currently, the documentation in this crate is not thorough enough to let people to
//! get started using SuperCollider for the first time with sorceress. However, the Supercollider
//! project provides [excellent documentation](https://doc.sccode.org/Help.html) and much of it
//! carries over to using sorceress. If this is your first time learning about SuperCollider I
//! recommend going through the following items before diving into sorceress:
//!
//! * Read the [Client vs Server](https://doc.sccode.org/Guides/ClientVsServer.html) documentation
//!   to understand the different roles that SuperCollider and sorceress play.  Remember that
//!   sorceress is a replacement for the sclang, SuperCollider's interpreted object-oriented
//!   language.
//!
//! * Go through the [Getting Started With SuperCollider] tutorial series. This will teach you the
//!   all of the fundementals of SuperCollider and how to boot **scsynth** on your computer.
//!
//! * Poke around the [Server Command Reference] to understand all of the OSC commands that
//!   SuperCollider accepts. The [server](crate::server) module does not yet contain all of these
//!   commands, so this document is useful for seeing what is available and what is currently
//!   missing.
//!
//! # Planned Features
//!
//! * Declarative resource management - sclang allows arbitrary sections of code in a source code
//!   file to be executed in any order using **scide**. This makes it very easy to run setup code
//!   that creates server-side resources such as buffers and synthdefs once before executing the
//!   code that creates synths and makes sounds. Instead of replicating this in Rust through
//!   condition execution of parts of code, we can create a declarative interface for managing
//!   server side resources. Code using this crate will *declare* what resources it needs, and
//!   resource management code provided by this crate will find the difference between what is
//!   currently provisioned and what is desired and automatically reconcile the two.
//!
//! * A live reloading development workflow - Dynamic interpreted languages like sclang provide an
//!   excellent environment for creative coding. The nature of how code is parsed and executed in
//!   those languages makes it easy to replace portions of code on the fly, at runtime, which is
//!   excellent for iterating on musical pieces. Rust presents a unique challenge in this area, as
//!   code cannot as easily be replaced during runtime. A similar workflow can still be achieved in
//!   Rust using a preemptible scheduler that can be paused and resumed when a new version of the
//!   code successfully compiles. If we add compilation triggered by filesystem notifications, we
//!   can create a streamlined creative workflow for creating music with Rust.
//!
//! [Server Command Reference]: https://doc.sccode.org/Reference/Server-Command-Reference.html
//! [Getting Started With SuperCollider]: https://doc.sccode.org/Tutorials/Getting-Started/01-Introductory-Remarks.html
//! [`Pseq`]: https://doc.sccode.org/Classes/Pseq.html
//! [`Ppar`]: https://doc.sccode.org/Classes/Ppar.html
//! [`Pn`]: https://doc.sccode.org/Classes/Pn.html
//! [`TempoClock`]: https://doc.sccode.org/Classes/TempoClock.html
//! [Sequencing with Patterns]: https://doc.sccode.org/Tutorials/Getting-Started/16-Sequencing-with-Patterns.html

pub mod pattern;
pub mod scheduler;
pub mod server;
pub mod synthdef;
pub mod ugen;
mod vectree;