├── .ruby-version ├── mise.toml ├── website ├── _layouts │ └── raw.html ├── .gitignore ├── images │ ├── .gitattributes │ ├── dac.jpg │ ├── eq.png │ ├── delay.png │ ├── hihat.jpg │ ├── hihat.png │ ├── mixer.png │ ├── notch.png │ ├── notes.png │ ├── wave.jpg │ ├── Envelope.png │ ├── Filter.png │ ├── airwaves.jpg │ ├── audio_flow.svg:Zone.Identifier │ ├── bandpass.png │ ├── highpass.png │ ├── kickdrum.jpg │ ├── kickdrum.png │ ├── lowpass.png │ ├── nyquist.jpg │ ├── SquareWave.png │ ├── depfu_mike.png │ ├── reverb_raw.png │ ├── roland_808.jpg │ ├── snare_drum.png │ ├── snaredrum.png │ ├── waveshaper.png │ ├── StateVarBlock.gif │ ├── depfu_example.png │ ├── filter_sketch.png │ ├── hihat_sketch.png │ ├── mixing_desk.jpg │ ├── pattern_song.png │ ├── snare_sketch.png │ ├── beats_and_bars.png │ ├── depfu-left-blue.png │ ├── digital_analog.png │ ├── hallgrimskirkja.jpg │ ├── kickdrum_sketch.png │ ├── reverb_diffused.png │ ├── digital_2_analog.jpg │ ├── loudspeaker_to_ear.jpg │ ├── depfu_engine_update.png │ ├── notes_patterns_songs.png │ ├── david_von_michelangelo.jpg │ ├── envelope-font-awesome.png │ ├── sequencer_grid_sketch.png │ ├── adsr.mp4 │ ├── chorus.mp4 │ ├── adsr.av1.mp4 │ ├── air-movie.mp4 │ ├── chorus.av1.mp4 │ ├── compressor.mp4 │ ├── full_song.mp4 │ ├── sonic_pi.mp4 │ ├── air-movie.av1.mp4 │ ├── compressor.av1.mp4 │ ├── full_song.av1.mp4 │ ├── sonic_pi.av1.mp4 │ ├── david_von_michelangelo.jpg:Zone.Identifier │ ├── laptop.svg │ ├── envelope-font-awesome.svg │ └── audio_flow.svg ├── docs │ ├── images │ │ ├── add.png │ │ ├── bug.png │ │ ├── brick.png │ │ ├── date.png │ │ ├── find.png │ │ ├── ruby.png │ │ ├── zoom.png │ │ ├── arrow_up.png │ │ ├── delete.png │ │ ├── package.png │ │ ├── plugin.png │ │ ├── tag_blue.png │ │ ├── wrench.png │ │ ├── brick_link.png │ │ ├── page_green.png │ │ ├── tag_green.png │ │ ├── bullet_black.png │ │ ├── macFFBgHack.png │ │ ├── transparent.png │ │ ├── wrench_orange.png │ │ ├── page_white_text.png │ │ ├── bullet_toggle_plus.png │ │ ├── loadingAnimation.gif │ │ ├── page_white_width.png │ │ └── bullet_toggle_minus.png │ ├── js │ │ ├── navigation.js.gz │ │ ├── searcher.js.gz │ │ ├── search_index.js.gz │ │ ├── search.js │ │ ├── navigation.js │ │ └── darkfish.js │ ├── fonts │ │ ├── Lato-Light.ttf │ │ ├── Lato-Regular.ttf │ │ ├── Lato-LightItalic.ttf │ │ ├── Lato-RegularItalic.ttf │ │ ├── SourceCodePro-Bold.ttf │ │ └── SourceCodePro-Regular.ttf │ ├── created.rid │ ├── Limiter.html │ ├── README_md.html │ ├── Damper.html │ ├── index.html │ ├── OnePoleLP.html │ ├── TunedDrum.html │ └── Utils.html ├── samples │ ├── bd.wav │ ├── chord.wav │ ├── clap.wav │ ├── delay.wav │ ├── drums.wav │ ├── noise.wav │ ├── square.wav │ ├── acid_synth.wav │ ├── amp_env.wav │ ├── chorus.wav │ ├── ducking.wav │ ├── filter_env.wav │ ├── filtered.wav │ ├── full_song.ogg │ ├── kick_drum.wav │ ├── lfo_wub.wav │ ├── main_chord.wav │ ├── piano_long.wav │ ├── pitch_env.wav │ ├── real_snare.wav │ ├── reverb.wav │ ├── snare_drum.wav │ ├── snare_only.wav │ ├── waveshaper.wav │ ├── lowpass_filtered.wav │ ├── main_chord_maj.wav │ ├── manual_snare.wav │ ├── monosynth_demo.wav │ ├── pitch_env_drum.wav │ ├── simple_song.wav │ ├── bandpass_filtered.wav │ └── highpass_filtered.wav ├── presentations │ ├── index.md │ ├── 2019-06-22_euruko.html │ ├── 2019-08-23_railsgrills.html │ └── 2020-07-18_rubyconf_by.html ├── _includes │ └── head-custom.html ├── glossary.md ├── test_opal.html ├── js │ ├── live_ruby.js │ └── player.js ├── index.md ├── _sass │ └── custom │ │ └── custom.scss ├── 04_drums.md ├── 01_introduction.md └── 05_sequencer.md ├── examples ├── opal_loader.rb ├── noise.rb ├── hihat.rb ├── snare_drum.rb ├── reverb_demo.rb ├── kick_drum.rb ├── delay_demo.rb ├── monosynth_demo.rb ├── lowpass_filtered.rb ├── highpass_filtered.rb ├── square_wave.rb ├── snare_only.rb ├── chord.rb ├── bandpass_filtered.rb ├── chorus_demo.rb ├── amp_env.rb ├── waveshaper.rb ├── clap.rb ├── polysynth_demo.rb ├── filter_env.rb ├── lfo_wub.rb ├── drum_demo.rb ├── pitch_env.rb ├── pitch_env_drum.rb ├── manual_snare.rb ├── ducking_demo.rb ├── simple_song.rb ├── sequencer.rb └── acid_synth.rb ├── drawings ├── adsr.png ├── chorus.png ├── sawtooth.png ├── compressor.png ├── reverb_bars.png ├── reverb_diffused.xcf ├── david_von_michelangelo.jpg ├── adsr.sifz ├── chorus.sifz ├── compressor.sifz ├── david_von_michelangelo.jpg:Zone.Identifier ├── highpass.svg ├── sawtooth.svg ├── waveshaper.svg ├── compressor.svg ├── lowpass.svg ├── eq.svg ├── delay.svg ├── bandpass.svg └── notch.svg ├── meta ├── todos.md └── outline.md ├── .gitignore ├── .gitattributes ├── Gemfile ├── Rakefile ├── README.md ├── _config.yml └── Gemfile.lock /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.1 2 | -------------------------------------------------------------------------------- /mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | ruby = "3.4.2" 3 | -------------------------------------------------------------------------------- /website/_layouts/raw.html: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | {{ content }} 4 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | -------------------------------------------------------------------------------- /examples/opal_loader.rb: -------------------------------------------------------------------------------- 1 | require 'opal-parser' 2 | require 'synth_blocks' 3 | -------------------------------------------------------------------------------- /website/images/.gitattributes: -------------------------------------------------------------------------------- 1 | *.ogv filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /drawings/adsr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/adsr.png -------------------------------------------------------------------------------- /drawings/chorus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/chorus.png -------------------------------------------------------------------------------- /drawings/sawtooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/sawtooth.png -------------------------------------------------------------------------------- /website/images/dac.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/dac.jpg -------------------------------------------------------------------------------- /website/images/eq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/eq.png -------------------------------------------------------------------------------- /drawings/compressor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/compressor.png -------------------------------------------------------------------------------- /drawings/reverb_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/reverb_bars.png -------------------------------------------------------------------------------- /website/images/delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/delay.png -------------------------------------------------------------------------------- /website/images/hihat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/hihat.jpg -------------------------------------------------------------------------------- /website/images/hihat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/hihat.png -------------------------------------------------------------------------------- /website/images/mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/mixer.png -------------------------------------------------------------------------------- /website/images/notch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/notch.png -------------------------------------------------------------------------------- /website/images/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/notes.png -------------------------------------------------------------------------------- /website/images/wave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/wave.jpg -------------------------------------------------------------------------------- /meta/todos.md: -------------------------------------------------------------------------------- 1 | # Todos 2 | - Exchange the Depfu example 3 | - Better SonicPi video fur sure 4 | 5 | 6 | -------------------------------------------------------------------------------- /website/docs/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/add.png -------------------------------------------------------------------------------- /website/docs/images/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/bug.png -------------------------------------------------------------------------------- /website/images/Envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/Envelope.png -------------------------------------------------------------------------------- /website/images/Filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/Filter.png -------------------------------------------------------------------------------- /website/images/airwaves.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/airwaves.jpg -------------------------------------------------------------------------------- /website/images/audio_flow.svg:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | HostUrl=about:internet 4 | -------------------------------------------------------------------------------- /website/images/bandpass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/bandpass.png -------------------------------------------------------------------------------- /website/images/highpass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/highpass.png -------------------------------------------------------------------------------- /website/images/kickdrum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/kickdrum.jpg -------------------------------------------------------------------------------- /website/images/kickdrum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/kickdrum.png -------------------------------------------------------------------------------- /website/images/lowpass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/lowpass.png -------------------------------------------------------------------------------- /website/images/nyquist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/nyquist.jpg -------------------------------------------------------------------------------- /drawings/reverb_diffused.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/reverb_diffused.xcf -------------------------------------------------------------------------------- /website/docs/images/brick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/brick.png -------------------------------------------------------------------------------- /website/docs/images/date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/date.png -------------------------------------------------------------------------------- /website/docs/images/find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/find.png -------------------------------------------------------------------------------- /website/docs/images/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/ruby.png -------------------------------------------------------------------------------- /website/docs/images/zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/zoom.png -------------------------------------------------------------------------------- /website/images/SquareWave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/SquareWave.png -------------------------------------------------------------------------------- /website/images/depfu_mike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/depfu_mike.png -------------------------------------------------------------------------------- /website/images/reverb_raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/reverb_raw.png -------------------------------------------------------------------------------- /website/images/roland_808.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/roland_808.jpg -------------------------------------------------------------------------------- /website/images/snare_drum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/snare_drum.png -------------------------------------------------------------------------------- /website/images/snaredrum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/snaredrum.png -------------------------------------------------------------------------------- /website/images/waveshaper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/waveshaper.png -------------------------------------------------------------------------------- /website/docs/images/arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/arrow_up.png -------------------------------------------------------------------------------- /website/docs/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/delete.png -------------------------------------------------------------------------------- /website/docs/images/package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/package.png -------------------------------------------------------------------------------- /website/docs/images/plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/plugin.png -------------------------------------------------------------------------------- /website/docs/images/tag_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/tag_blue.png -------------------------------------------------------------------------------- /website/docs/images/wrench.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/wrench.png -------------------------------------------------------------------------------- /website/docs/js/navigation.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/js/navigation.js.gz -------------------------------------------------------------------------------- /website/docs/js/searcher.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/js/searcher.js.gz -------------------------------------------------------------------------------- /website/images/StateVarBlock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/StateVarBlock.gif -------------------------------------------------------------------------------- /website/images/depfu_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/depfu_example.png -------------------------------------------------------------------------------- /website/images/filter_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/filter_sketch.png -------------------------------------------------------------------------------- /website/images/hihat_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/hihat_sketch.png -------------------------------------------------------------------------------- /website/images/mixing_desk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/mixing_desk.jpg -------------------------------------------------------------------------------- /website/images/pattern_song.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/pattern_song.png -------------------------------------------------------------------------------- /website/images/snare_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/snare_sketch.png -------------------------------------------------------------------------------- /website/docs/fonts/Lato-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/fonts/Lato-Light.ttf -------------------------------------------------------------------------------- /website/docs/images/brick_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/brick_link.png -------------------------------------------------------------------------------- /website/docs/images/page_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/page_green.png -------------------------------------------------------------------------------- /website/docs/images/tag_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/tag_green.png -------------------------------------------------------------------------------- /website/docs/js/search_index.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/js/search_index.js.gz -------------------------------------------------------------------------------- /website/images/beats_and_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/beats_and_bars.png -------------------------------------------------------------------------------- /website/images/depfu-left-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/depfu-left-blue.png -------------------------------------------------------------------------------- /website/images/digital_analog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/digital_analog.png -------------------------------------------------------------------------------- /website/images/hallgrimskirkja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/hallgrimskirkja.jpg -------------------------------------------------------------------------------- /website/images/kickdrum_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/kickdrum_sketch.png -------------------------------------------------------------------------------- /website/images/reverb_diffused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/reverb_diffused.png -------------------------------------------------------------------------------- /drawings/david_von_michelangelo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/drawings/david_von_michelangelo.jpg -------------------------------------------------------------------------------- /website/docs/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /website/docs/images/bullet_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/bullet_black.png -------------------------------------------------------------------------------- /website/docs/images/macFFBgHack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/macFFBgHack.png -------------------------------------------------------------------------------- /website/docs/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/transparent.png -------------------------------------------------------------------------------- /website/docs/images/wrench_orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/wrench_orange.png -------------------------------------------------------------------------------- /website/images/digital_2_analog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/digital_2_analog.jpg -------------------------------------------------------------------------------- /website/images/loudspeaker_to_ear.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/loudspeaker_to_ear.jpg -------------------------------------------------------------------------------- /website/docs/fonts/Lato-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/fonts/Lato-LightItalic.ttf -------------------------------------------------------------------------------- /website/docs/images/page_white_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/page_white_text.png -------------------------------------------------------------------------------- /website/images/depfu_engine_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/depfu_engine_update.png -------------------------------------------------------------------------------- /website/images/notes_patterns_songs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/notes_patterns_songs.png -------------------------------------------------------------------------------- /website/docs/fonts/Lato-RegularItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/fonts/Lato-RegularItalic.ttf -------------------------------------------------------------------------------- /website/docs/fonts/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/fonts/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /website/docs/images/bullet_toggle_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/bullet_toggle_plus.png -------------------------------------------------------------------------------- /website/docs/images/loadingAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/loadingAnimation.gif -------------------------------------------------------------------------------- /website/docs/images/page_white_width.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/page_white_width.png -------------------------------------------------------------------------------- /website/images/david_von_michelangelo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/david_von_michelangelo.jpg -------------------------------------------------------------------------------- /website/images/envelope-font-awesome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/envelope-font-awesome.png -------------------------------------------------------------------------------- /website/images/sequencer_grid_sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/images/sequencer_grid_sketch.png -------------------------------------------------------------------------------- /website/docs/fonts/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/fonts/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /website/docs/images/bullet_toggle_minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfbyte/rubysynth/HEAD/website/docs/images/bullet_toggle_minus.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test.wav 2 | debug.txt 3 | logs/* 4 | DEBUG.txt 5 | 6 | _site 7 | .sass-cache 8 | .jekyll-metadata 9 | /*.wav 10 | website/.jekyll-cache 11 | -------------------------------------------------------------------------------- /drawings/adsr.sifz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f10a1a6884b245f33ea7ace0c730981d5aa0b10764ae4747d544a04dafe970c0 3 | size 1916 4 | -------------------------------------------------------------------------------- /drawings/chorus.sifz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fc36f2cf3b9e0a10b54b5f12c0eb8043b0d8abb7786249dcd16e0bc91922979a 3 | size 1398 4 | -------------------------------------------------------------------------------- /drawings/compressor.sifz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f11c613916b63526a503ced9f8d20747ed980f8ed281c804c6dec89ba44c6bb2 3 | size 1678 4 | -------------------------------------------------------------------------------- /website/images/adsr.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0200e3cedabcd42a6f6c18de698484999501261f6d4fc73de1baa5dfc9b35b47 3 | size 159600 4 | -------------------------------------------------------------------------------- /website/images/chorus.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d55673a0230d6f840cf8b3bca444929149d2913cb76e43eef43df3a2728acdd3 3 | size 230634 4 | -------------------------------------------------------------------------------- /website/samples/bd.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a458e8f3957ac1f24896a4e075c8d089a3edb78f66182ba66c9c32e3a7372abf 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/chord.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:431a24cc55cd5fbbac1ad5787b91b966d55b5738301309f21bfc94659f4d045c 3 | size 441048 4 | -------------------------------------------------------------------------------- /website/samples/clap.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4a6be8ff44c8b1c6c49cf20fe859e3b564d673b636d0844c2a0e178d98e616b8 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/delay.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:162d00a7d9311df4b8f9bf9886f36f67107e91463155f7033fc6b7fd70b766dc 3 | size 176444 4 | -------------------------------------------------------------------------------- /website/samples/drums.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:db3127819d4da95d88088966683c45e2f09f80dd55d276d7875e6aac79e1c670 3 | size 529244 4 | -------------------------------------------------------------------------------- /website/samples/noise.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:169a009a5122ad4125ef1151a99790161840ac761facafb64f4aa42e0e572737 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/square.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5c1b82fd96982d62316eeb01484c956efb504523c1edac03b7402c8a247fdd39 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/images/adsr.av1.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:41ebe97cb1754ffcb5985d05eba78b4d82de69fb840100944663be89aec73282 3 | size 104316 4 | -------------------------------------------------------------------------------- /website/images/air-movie.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c9fea78f0ca09f34ca259c3974fcf0ac7c83ee549cfc8b15db13c15d3cc35b40 3 | size 2155921 4 | -------------------------------------------------------------------------------- /website/images/chorus.av1.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2096c70f861a074d8937c3a9790a5791ec977b5c4fbb58a15bf1e0c9e3a0c1db 3 | size 137287 4 | -------------------------------------------------------------------------------- /website/images/compressor.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6cef7de385f24a4dc25d2ca3fc6c3ddddd0ad2da89c7daf8c32e8b89e5de9651 3 | size 75854 4 | -------------------------------------------------------------------------------- /website/images/full_song.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6059f12f49fc47b507410984802ab7b8200ae91cd5f293b32bb708068f322180 3 | size 17593248 4 | -------------------------------------------------------------------------------- /website/images/sonic_pi.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:34e993f8d05f0bca7755121798cb09b7c3cac4634664351de8edc93f445c551e 3 | size 83376251 4 | -------------------------------------------------------------------------------- /website/samples/acid_synth.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b2fda89830beb11a5d24c7f7eb2dc3bbb4ec679efae49bfefcc108a5b477d391 3 | size 352844 4 | -------------------------------------------------------------------------------- /website/samples/amp_env.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4aedd80cde09b09a60c365fa475badb1b88a4bdb760ada4fb0ab21d040f895af 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/chorus.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:120d165d3b1b95f8f30a8df8d3a4e5be041f28f205296c898fe9809a91be40e4 3 | size 352844 4 | -------------------------------------------------------------------------------- /website/samples/ducking.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7caeaf56e9de41dbd2f4d2abacc53eca504b71e4274f25d68e662b07d7ada7f9 3 | size 441044 4 | -------------------------------------------------------------------------------- /website/samples/filter_env.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:42090754604887f58a209da8f43c62498e8098f09695b1a965e10c6735a94af1 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/filtered.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2e1d09600ddda06184cc36a21cc9aeffa7f3677bce8e46a896e5d4c7da5575b5 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/full_song.ogg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2926c8bdb880a60a235b1fbe9b26a58641b007cc3fa3178e81aa448e8f851200 3 | size 594027 4 | -------------------------------------------------------------------------------- /website/samples/kick_drum.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ac37e381619444f9b4398b79b80324a7b0d89e3482cfc692967d9163d560320b 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/lfo_wub.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:32d3a16a1518db342238a03e2aa1a6cd1c8a5bc103b5ffb8061919789fd29462 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/main_chord.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:413bdb84546cba89bea7e180e4ab6dc04e9751f74f6c227b373d617d8625c514 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/piano_long.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6361fa95711aba4df12c44c14dc67acf569d68a7031faa466657cec4697a8d9a 3 | size 376936 4 | -------------------------------------------------------------------------------- /website/samples/pitch_env.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ddab07cbd9357698fe171693ba962ba54179a21982c3cc3ae7ebb1787b7ed68d 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/real_snare.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:434f8c824e28da0d6423f4f7fd6eb56fb45e5cd061f870f5c7a2a3e4aa724ff2 3 | size 36908 4 | -------------------------------------------------------------------------------- /website/samples/reverb.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:227763e10d375a7bb52682f888f23bf8064c638e7e8e0afdd21bab4b21ce15ab 3 | size 176444 4 | -------------------------------------------------------------------------------- /website/samples/snare_drum.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e5e9e3587fe52643655d4447d1700b075d979581d80eb1c7151467535adc5426 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/snare_only.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:886e7b732bf432670d9fc79f058852cc5f3454e0efdba880ca95d5d06c30d659 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/waveshaper.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6ca329af9019a7897b4334341939bf9d77ecc5a34c53cfb5ef19ad95686096e4 3 | size 705644 4 | -------------------------------------------------------------------------------- /website/images/air-movie.av1.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:37087a24fce969a759fcea88271064a3959decb05a877c15ff1eee09b08c4ffb 3 | size 1005608 4 | -------------------------------------------------------------------------------- /website/images/compressor.av1.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:3233fb9135fc4bf0dd52b23dc4a3317c870b73303f61070a6f87e52512f1876e 3 | size 31990 4 | -------------------------------------------------------------------------------- /website/images/full_song.av1.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:caa692826c2f2029151d0eec77b6d0b449ab353051d3ba17258a4dd21cacf5e2 3 | size 10645798 4 | -------------------------------------------------------------------------------- /website/images/sonic_pi.av1.mp4: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0d3ff211abb083e4d7fdc34e9182d04fc63b8272a5a7fcbbf23e505ae8a413dd 3 | size 7686792 4 | -------------------------------------------------------------------------------- /website/samples/lowpass_filtered.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b4313b391265dc2e77e0a34857d46e7ef8ccde34a667f34c446cd4d25f76a55a 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/main_chord_maj.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f968f50158ed47ebef8cde6cf553ffbe4e5f71e238257c266c064410a89d425c 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/manual_snare.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dc2be1ef10c2f788907d42b86bb502d0ae79a87a96b63554267a48e8d6186d12 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/monosynth_demo.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f21182b0a347c34793462c5d4f60d9be4f726ad1b8eb35406a84cfdbe1c19287 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/pitch_env_drum.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c6736cc9d3e2673f51733608220ff54058c1376f7c673e6b30fbd52c9fcc3198 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/simple_song.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1af27ccac591b2cc64b04a460f23cb00d84bfe713551c5fc4707bf81bcb77424 3 | size 970244 4 | -------------------------------------------------------------------------------- /website/samples/bandpass_filtered.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:049b858727082f2f016a991af4cd52ec1c8c6b0dc0e5af25eb0719209e5d8d8b 3 | size 88244 4 | -------------------------------------------------------------------------------- /website/samples/highpass_filtered.wav: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e2d4edaee1862f1d0e0685ed962da3ee33de93c8b0db7c68b4d4ff0887e339d8 3 | size 88244 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ogg filter=lfs diff=lfs merge=lfs -text 2 | *.wav filter=lfs diff=lfs merge=lfs -text 3 | *.sifz filter=lfs diff=lfs merge=lfs -text 4 | *.mp4 filter=lfs diff=lfs merge=lfs -text 5 | -------------------------------------------------------------------------------- /drawings/david_von_michelangelo.jpg:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | ReferrerUrl=https://upload.wikimedia.org/wikipedia/commons/d/d5/David_von_Michelangelo.jpg 4 | HostUrl=https://upload.wikimedia.org/wikipedia/commons/d/d5/David_von_Michelangelo.jpg 5 | -------------------------------------------------------------------------------- /website/images/david_von_michelangelo.jpg:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | ReferrerUrl=https://upload.wikimedia.org/wikipedia/commons/d/d5/David_von_Michelangelo.jpg 4 | HostUrl=https://upload.wikimedia.org/wikipedia/commons/d/d5/David_von_Michelangelo.jpg 5 | -------------------------------------------------------------------------------- /examples/noise.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Almost white noise 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | 9 | out = SAMPLING_FREQUENCY.times.map do 10 | output = rand() * 2 - 1 11 | output *= 0.2 12 | end 13 | 14 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 15 | -------------------------------------------------------------------------------- /examples/hihat.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Demo of the High Hat drum sound 5 | # 6 | 7 | SRATE = 44100 8 | hat = SynthBlocks::Drum::Hihat.new(SRATE) 9 | 10 | hat.start(0.0) 11 | 12 | out = SRATE.times.map {|i| 0.5 * hat.run(i) } 13 | 14 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/snare_drum.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Let's generate a snare drum sound 5 | # 6 | 7 | SRATE = 44100 8 | drum = SynthBlocks::Drum::SnareDrum.new(SRATE) 9 | 10 | drum.start(0.0) 11 | 12 | out = SRATE.times.map {|i| 0.5 * drum.run(i) } 13 | 14 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 15 | -------------------------------------------------------------------------------- /website/presentations/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Presentations 3 | nav_order: 25 4 | has_children: true 5 | layout: default 6 | --- 7 | # Presentation files 8 | 9 | I've given this talk a couple of times and each time, tiny details change. In the interest of transparency, but also because I like to refer back to old versions, they are now here. 10 | -------------------------------------------------------------------------------- /examples/reverb_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | SRATE = 44100 3 | 4 | snare = SynthBlocks::Drum::SnareDrum.new(SRATE) 5 | reverb = SynthBlocks::Fx::GVerb.new(SRATE) 6 | snare.start(0) 7 | 8 | out = (SRATE * 2).times.map do |i| 9 | reverb.run(snare.run(i)) 10 | end 11 | 12 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/kick_drum.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Demo of the kick drum 5 | # 6 | 7 | SRATE = 44100 8 | 9 | kick = SynthBlocks::Drum::KickDrum.new(SRATE) 10 | kick.start(0.0) 11 | 12 | ws = SynthBlocks::Fx::Waveshaper.new(5) 13 | 14 | out = SRATE.times.map {|i| 0.3 * ws.run(kick.run(i)) } 15 | 16 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/delay_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Demo of the Delay/Echo effect 5 | # 6 | 7 | SRATE = 44100 8 | 9 | snare = SynthBlocks::Drum::SnareDrum.new(SRATE) 10 | delay = SynthBlocks::Fx::Delay.new(SRATE, time: 0.3) 11 | snare.start(0) 12 | 13 | out = (SRATE * 2).times.map do |i| 14 | delay.run(snare.run(i)) 15 | end 16 | 17 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 18 | -------------------------------------------------------------------------------- /examples/monosynth_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Simple Demo of the Monosyth 5 | # 6 | 7 | SFREQ = 44100 8 | 9 | synth = SynthBlocks::Synth::Monosynth.new(SFREQ, { 10 | flt_Q: 8, 11 | flt_frequency: 100 12 | }) 13 | 14 | synth.start(0, 48) 15 | synth.stop(0.5, 48) 16 | 17 | out = (SFREQ).times.map { |i| 18 | synth.run(i) * 0.5 19 | } 20 | 21 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/lowpass_filtered.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Lowpass Filter 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | 9 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 10 | 11 | out = SAMPLING_FREQUENCY.times.map do 12 | output = rand() * 2 - 1 13 | output = filter.run(output, 2000.0, 2, type: :lowpass) 14 | output *= 0.3 15 | end 16 | 17 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 18 | -------------------------------------------------------------------------------- /examples/highpass_filtered.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Highpass Filter 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | 9 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 10 | 11 | out = SAMPLING_FREQUENCY.times.map do 12 | output = rand() * 2 - 1 13 | output = filter.run(output, 6000.0, 2, type: :highpass) 14 | output *= 0.1 15 | end 16 | 17 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 18 | -------------------------------------------------------------------------------- /meta/outline.md: -------------------------------------------------------------------------------- 1 | # Talk Outline 2 | 3 | - [2] A quick demo of Sonic Pi 4 | - The first demo, explaining sox output 5 | - Synth 6 | - Squarewave 7 | - Filter 8 | - Envelopes 9 | - LFO 10 | - Notes -> Frequencies > Chords 11 | - Drums 12 | - Bassdrum 13 | - Snare 14 | - Hihat 15 | 16 | - Sequencing 17 | - Beats and Bars and Notes 18 | - Sequencing DSL 19 | 20 | - FX 21 | - Delay 22 | - Reverb 23 | - Waveshaper 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /website/images/laptop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/_includes/head-custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/square_wave.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks/core/wave_writer' 2 | 3 | # 4 | # Most simple example. Generates a square wave in 440 Hz for one second 5 | # 6 | SAMPLING_FREQUENCY=44100 7 | FREQUENCY=440 8 | 9 | 10 | in_cycle = 0 11 | samples = (SAMPLING_FREQUENCY).times.map do 12 | period = SAMPLING_FREQUENCY / FREQUENCY.to_f 13 | output = in_cycle > 0.5 ? -1.0 : 1.0 14 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 15 | output * 0.5 16 | end 17 | SynthBlocks::Core::WaveWriter.write_if_name_given(samples) -------------------------------------------------------------------------------- /examples/snare_only.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # A simple example of using amp, filter and pitch envelopes to shape a sound 5 | # But now as a draum sound 6 | # 7 | 8 | SAMPLING_FREQUENCY=44100 9 | 10 | amp_env = SynthBlocks::Mod::Adsr.new(0.001, 0.1, 0.5, 0.2) 11 | out = SAMPLING_FREQUENCY.times.map do |s| 12 | t = s.to_f / SAMPLING_FREQUENCY.to_f 13 | stopped = t >= 0.15 ? 0.15 : nil 14 | output = 0.3 * (rand() * 2) - 1 15 | output *= 0.3 * amp_env.run(t, stopped) 16 | end 17 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/chord.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | SRATE = 44100 3 | include SynthBlocks::Sequencer::SequencerDSL 4 | polysynth = SynthBlocks::Synth::Polysynth.new(SRATE) 5 | 6 | def_pattern(:chord, 16) do 7 | note_pattern polysynth, [ 8 | P, P, ['C2,D#2,G2', 2], P, 9 | P, P, P, P, 10 | ['C#2,E2,G#2', 2], P, P, P, 11 | P, P, P, P 12 | ] 13 | end 14 | 15 | my_song = song(bpm: 135) do 16 | pattern(:chord, at: 0, repeat: 2) 17 | end 18 | 19 | out = my_song.render(SRATE) do |i| 20 | 0.8 * polysynth.run(i) 21 | end 22 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/bandpass_filtered.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Bandpass Filter 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | FREQUENCY=440 9 | 10 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 11 | 12 | in_cycle = 0 13 | out = SAMPLING_FREQUENCY.times.map do 14 | period = SAMPLING_FREQUENCY / FREQUENCY.to_f 15 | output = in_cycle > 0.5 ? -1.0 : 1.0 16 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 17 | output = filter.run(output, 2000.0, 2, type: :bandpass) 18 | output *= 0.3 19 | end 20 | 21 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 22 | -------------------------------------------------------------------------------- /examples/chorus_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | SRATE = 44100 3 | 4 | # 5 | # Example of the Chorus effect 6 | # 7 | 8 | mono = SynthBlocks::Synth::Monosynth.new(SRATE, {osc_waveform: :sawtooth}) 9 | chorus = SynthBlocks::Fx::Chorus.new(SRATE, delay_time: 12) 10 | 11 | mono.start(0, 36) 12 | mono.stop(1, 36) 13 | 14 | mono.start(2, 36) 15 | mono.stop(3, 36) 16 | 17 | out = (SRATE * 4).times.map do |i| 18 | if (i >= SRATE * 2) 19 | chorus.run(mono.run(i)) * 0.8 20 | else 21 | mono.run(i) * 0.8 22 | end 23 | end 24 | 25 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 26 | -------------------------------------------------------------------------------- /examples/amp_env.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Example for an amp envelope 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | FREQUENCY=220 9 | 10 | env = SynthBlocks::Mod::Adsr.new(0.001, 0.2, 0.5, 0.2) 11 | in_cycle = 0 12 | out = SAMPLING_FREQUENCY.times.map do |s| 13 | t = s.to_f / SAMPLING_FREQUENCY.to_f 14 | stopped = t >= 0.4 ? 0.4 : nil 15 | period = SAMPLING_FREQUENCY / FREQUENCY.to_f 16 | output = in_cycle > 0.5 ? -1.0 : 1.0 17 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 18 | output *= 0.3 * env.run(t, stopped) 19 | end 20 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 21 | -------------------------------------------------------------------------------- /examples/waveshaper.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Example to show the effect of the waveshaper by increasingly distorting a kick drum 5 | # 6 | 7 | SRATE = 44100 8 | 9 | drum = SynthBlocks::Drum::KickDrum.new(SRATE) 10 | drum.start(0) 11 | drum.start(0.5) 12 | drum.start(1) 13 | drum.start(1.5) 14 | 15 | SHAPER_RATES = [1,2,4,8] 16 | 17 | 18 | out = [] 19 | 4.times do |r| 20 | shaper = SynthBlocks::Fx::Waveshaper.new(SHAPER_RATES[r]) 21 | 22 | (SRATE * 2).times do |i| 23 | out << shaper.run(drum.run(i)) 24 | end 25 | end 26 | 27 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | gem "jekyll", "~> 4.0" 8 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 9 | gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] 10 | # Performance-booster for watching directories on Windows 11 | gem "wdm", "~> 0.1.0" if Gem.win_platform? 12 | gem "just-the-docs", "~> 0.3" 13 | gem "synth_blocks", "~> 1.0" 14 | gem "rake", "~> 13.0" 15 | gem "opal" 16 | gem "irb" 17 | gem "logger" 18 | gem "csv" 19 | gem "base64" 20 | gem "bigdecimal" -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | SAMPLES = %w[pitch_env_drum waveshaper noise lowpass_filtered highpass_filtered amp_env filter_env pitch_env manual_snare snare_only] 2 | 3 | desc "update examples" 4 | task :update_examples => SAMPLES.map {|sample| "website/samples/#{sample}.wav"} 5 | 6 | SAMPLES.each do |sample| 7 | file "website/samples/#{sample}.wav" => ["examples/#{sample}.rb"] do |t| 8 | sh "bundle exec ruby #{t.prerequisites.first} #{t.name}" 9 | end 10 | end 11 | 12 | task :compile_opal => ['website/js/opal.js'] 13 | 14 | file 'website/js/opal.js' => ['examples/opal_loader.rb'] do |t| 15 | sh "bundle exec opal -g synth_blocks --compile #{t.prerequisites.first} >#{t.name}" 16 | end -------------------------------------------------------------------------------- /examples/clap.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Manual Clap 5 | # 6 | 7 | SFREQ = 44100 8 | 9 | filter = SynthBlocks::Core::StateVariableFilter.new(SFREQ) 10 | env = SynthBlocks::Mod::Envelope.new(0.001, 0.02) 11 | times = [[0, 1], [0.02, 0.8], [0.03, 0.6], [0.045, 0.4]] 12 | out = SFREQ.times.map do |i| 13 | t = i.to_f / SFREQ.to_f 14 | out = rand() * 2 - 1 15 | all_envs = 0 16 | times.each {|et| all_envs += env.run(t-et[0]) * et[1] if (t >= et[0]) } 17 | all_envs = [all_envs, 1].min 18 | ffreq = 100 + (2000 * all_envs) 19 | out = filter.run(out, ffreq, 4) * all_envs 20 | out *= 0.5 21 | end 22 | 23 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /website/glossary.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Glossary 3 | layout: page 4 | nav_order: 20 5 | --- 6 | Oscillator 7 | : A piece of electronics (or code) that produces a continuous waveform at a stable frequency, such as a sine wave, a sawtooth or a rectable. 8 | 9 | LFO (Low Frequency Oscillator) 10 | : An oscillator that runs a low frequencies, often way outside the audible range and is, instead of making sound directly, used to modulate parameters. 11 | 12 | Envelope 13 | : A signal genereator that, depending on events (usually: A keyboard key pressed down and then released again) generates a smooth curve to shape a sound in a more natural way than just switching in on and off. The ADSR (Attack, Decay, Sustain, Release) envelope is the most common one as it is a good compromise between control and ease of use 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/polysynth_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Simple Demo of the PolySynth 5 | # 6 | 7 | SRATE = 44100 8 | 9 | polysynth = SynthBlocks::Synth::Polysynth.new(SRATE, { 10 | amp_env_attack: 0.001, 11 | amp_env_release: 0.1, 12 | flt_env_attack: 0.001, 13 | flt_env_decay: 0.05, 14 | flt_env_sustain: 0.1, 15 | flt_frequency: 300, 16 | flt_envmod: 1000, 17 | flt_Q: 1, 18 | osc_waveform: :square 19 | }) 20 | 21 | polysynth.start(0, 60) 22 | polysynth.start(0, 60 + 4) 23 | polysynth.start(0, 60 + 7) 24 | polysynth.start(0, 60 - 12) 25 | 26 | polysynth.stop(0.125, 60) 27 | polysynth.stop(0.125, 60 + 4) 28 | polysynth.stop(0.125, 60 + 7) 29 | polysynth.stop(0.125, 60 - 12) 30 | 31 | out = SRATE.times.map{ |i| 0.6 * polysynth.run(i) } 32 | 33 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 34 | -------------------------------------------------------------------------------- /website/test_opal.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing Opal Integration 3 | layout: page 4 | nav_exclude: true 5 | --- 6 | 7 |
8 | 22 |
23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/filter_env.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # A filter envelope shapes the sound even more 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | FREQUENCY=220 9 | 10 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 11 | amp_env = SynthBlocks::Mod::Adsr.new(0.001, 0.2, 0.5, 0.2) 12 | filter_env = SynthBlocks::Mod::Adsr.new(0.01, 0.1, 0.1, 0.1) 13 | in_cycle = 0 14 | out = SAMPLING_FREQUENCY.times.map do |s| 15 | t = s.to_f / SAMPLING_FREQUENCY.to_f 16 | stopped = t >= 0.4 ? 0.4 : nil 17 | period = SAMPLING_FREQUENCY / FREQUENCY.to_f 18 | output = in_cycle > 0.5 ? -1.0 : 1.0 19 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 20 | output = filter.run(output, 500.0 + (8000.0 * filter_env.run(t, stopped)), 1) 21 | output *= 0.3 * amp_env.run(t, stopped) 22 | end 23 | 24 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 25 | -------------------------------------------------------------------------------- /examples/lfo_wub.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Let's wobble! 5 | # 6 | 7 | 8 | SAMPLING_FREQUENCY=44100 9 | FREQUENCY=55 10 | 11 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 12 | lfo = SynthBlocks::Core::Oscillator.new(SAMPLING_FREQUENCY) 13 | lfo_freq = 4 14 | env = SynthBlocks::Mod::Adsr.new(0.001, 0.2, 0.5, 0.2) 15 | 16 | in_cycle = 0 17 | out = SAMPLING_FREQUENCY.times.map do |s| 18 | t = s.to_f / SAMPLING_FREQUENCY.to_f 19 | stopped = t >= 0.8 ? 0.8 : nil 20 | period = SAMPLING_FREQUENCY / FREQUENCY.to_f 21 | output = in_cycle > 0.5 ? -1.0 : 1.0 22 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 23 | output = filter.run(output, 500.0 + ((lfo.run(lfo_freq, waveform: :sawtooth) + 1) * 2000.0), 2) 24 | output *= 0.3 * env.run(t, stopped) 25 | end 26 | 27 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 28 | -------------------------------------------------------------------------------- /examples/drum_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | include SynthBlocks::Sequencer::SequencerDSL 4 | 5 | SRATE = 44100 6 | TEMPO = 125 7 | P = nil # pause 8 | 9 | snare = SynthBlocks::Drum::SnareDrum.new(SRATE) 10 | kick = SynthBlocks::Drum::KickDrum.new(SRATE) 11 | hihat = SynthBlocks::Drum::Hihat.new(SRATE) 12 | open_hihat = SynthBlocks::Drum::Hihat.new(SRATE, amp_decay: 0.2) 13 | limiter = SynthBlocks::Fx::Limiter.new 14 | def_pattern(:drums_full, 16) do 15 | drum_pattern kick, '*---*---*---*---' 16 | drum_pattern snare, '----*-------*---' 17 | drum_pattern hihat, '--*---*---*---*-' 18 | drum_pattern open_hihat, '--*---*---*--*--' 19 | end 20 | 21 | my_song = song(bpm: TEMPO) do 22 | pattern(:drums_full, at: 0, repeat: 2) 23 | end 24 | 25 | out = my_song.render(SRATE) do |i| 26 | limiter.run(snare.run(i) + kick.run(i) + (0.3 * (hihat.run(i) + open_hihat.run(i)))) 27 | end 28 | 29 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/pitch_env.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # A simple example of using amp, filter and pitch envelopes to shape a sound 5 | # 6 | 7 | SAMPLING_FREQUENCY=44100 8 | FREQUENCY=220 9 | 10 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 11 | amp_env = SynthBlocks::Mod::Adsr.new(0.001, 0.2, 0.5, 0.2) 12 | filter_env = SynthBlocks::Mod::Adsr.new(0.01, 0.1, 0.1, 0.1) 13 | pitch_env = SynthBlocks::Mod::Adsr.new(0.01, 0.2, 0.0, 0.0) 14 | in_cycle = 0 15 | out = SAMPLING_FREQUENCY.times.map do |s| 16 | t = s.to_f / SAMPLING_FREQUENCY.to_f 17 | stopped = t >= 0.4 ? 0.4 : nil 18 | period = SAMPLING_FREQUENCY / (FREQUENCY.to_f * ((0.5 * pitch_env.run(t, stopped)) + 1)) 19 | output = in_cycle > 0.5 ? -1.0 : 1.0 20 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 21 | output = filter.run(output, 500.0 + (8000.0 * filter_env.run(t, stopped)), 1) 22 | output *= 0.3 * amp_env.run(t, stopped) 23 | end 24 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/pitch_env_drum.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # A simple example of using amp, filter and pitch envelopes to shape a sound 5 | # But now as a draum sound 6 | # 7 | 8 | SAMPLING_FREQUENCY=44100 9 | FREQUENCY=110 10 | 11 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 12 | amp_env = SynthBlocks::Mod::Adsr.new(0.001, 0.1, 0.5, 0.2) 13 | filter_env = SynthBlocks::Mod::Adsr.new(0.01, 0.025, 0.1, 0.1) 14 | pitch_env = SynthBlocks::Mod::Adsr.new(0.01, 0.03, 0.0, 0.0) 15 | in_cycle = 0 16 | out = SAMPLING_FREQUENCY.times.map do |s| 17 | t = s.to_f / SAMPLING_FREQUENCY.to_f 18 | stopped = t >= 0.15 ? 0.15 : nil 19 | period = SAMPLING_FREQUENCY / (FREQUENCY.to_f * ((2 * pitch_env.run(t, stopped)) + 1)) 20 | output = in_cycle > 0.5 ? -1.0 : 1.0 21 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 22 | output = filter.run(output, 200.0 + (2000.0 * filter_env.run(t, stopped)), 1) 23 | output *= 0.3 * amp_env.run(t, stopped) 24 | end 25 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /examples/manual_snare.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # A simple example of using amp, filter and pitch envelopes to shape a sound 5 | # But now as a draum sound 6 | # 7 | 8 | SAMPLING_FREQUENCY=44100 9 | FREQUENCY=110 10 | 11 | filter = SynthBlocks::Core::StateVariableFilter.new(SAMPLING_FREQUENCY) 12 | amp_env = SynthBlocks::Mod::Adsr.new(0.001, 0.1, 0.5, 0.2) 13 | filter_env = SynthBlocks::Mod::Adsr.new(0.01, 0.025, 0.1, 0.1) 14 | pitch_env = SynthBlocks::Mod::Adsr.new(0.01, 0.03, 0.0, 0.0) 15 | in_cycle = 0 16 | out = SAMPLING_FREQUENCY.times.map do |s| 17 | t = s.to_f / SAMPLING_FREQUENCY.to_f 18 | stopped = t >= 0.15 ? 0.15 : nil 19 | period = SAMPLING_FREQUENCY / (FREQUENCY.to_f * ((2 * pitch_env.run(t, stopped)) + 1)) 20 | output = in_cycle > 0.5 ? -1.0 : 1.0 21 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 22 | output = filter.run(output, 200.0 + (2000.0 * filter_env.run(t, stopped)), 1) 23 | output += 0.3 * (rand() * 2) - 1 24 | output *= 0.3 * amp_env.run(t, stopped) 25 | end 26 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /drawings/highpass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/ducking_demo.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # Each mixer channel has a ducking function that allows to simulate sidechain compression 5 | # 6 | 7 | SFREQ = 44100 8 | 9 | poly = SynthBlocks::Synth::Polysynth.new(SFREQ, {amp_env_release: 0.2}) 10 | channel = SynthBlocks::Mixer::MixerChannel.new(SFREQ, poly, insert_effects: [SynthBlocks::Fx::Chorus.new(SFREQ)], sends: [0.0], preset: { 11 | volume: 0.5, comp_threshold: 0.0, 12 | duck: 0.8 13 | }) 14 | 15 | poly.start(0, 48) 16 | poly.start(0, 48 + 3) 17 | poly.start(0, 48 + 7) 18 | 19 | poly.stop(2, 48) 20 | poly.stop(2, 48 + 3) 21 | poly.stop(2, 48 + 7) 22 | 23 | poly.start(2, 48 + 5) 24 | poly.start(2, 48 + 3 + 5) 25 | poly.start(2, 48 + 7 + 5) 26 | 27 | poly.stop(4, 48 + 5) 28 | poly.stop(4, 48 + 3 + 5) 29 | poly.stop(4, 48 + 7 + 5) 30 | channel.duck(0) 31 | channel.duck(0.5) 32 | channel.duck(1) 33 | channel.duck(1.5) 34 | channel.duck(2) 35 | channel.duck(2.5) 36 | channel.duck(3) 37 | channel.duck(3.5) 38 | channel.duck(4.0) 39 | channel.duck(4.5) 40 | 41 | out = (5 * SFREQ).times.map { |i| 42 | SynthBlocks::Fx::Limiter.new.run(channel.run(i) * 0.6) 43 | } 44 | 45 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) -------------------------------------------------------------------------------- /website/js/live_ruby.js: -------------------------------------------------------------------------------- 1 | function drawInCanvas(selector, data) { 2 | const canvas = document.querySelector(selector) 3 | const ctx = canvas.getContext('2d') 4 | ctx.clearRect(0, 0, canvas.width, canvas.height) 5 | const perStep = canvas.width / data.length 6 | const height = canvas.height 7 | ctx.strokeStyle = "#000" 8 | ctx.strokeWidth = 2 9 | ctx.beginPath() 10 | ctx.moveTo(0, height / 2) 11 | data.forEach((d, x) => ctx.lineTo(perStep * x, ((d + 1) / 2 * height))) 12 | ctx.stroke() 13 | } 14 | 15 | document.getElementById('run-button').onclick = function(e) { 16 | e.preventDefault() 17 | const code = document.getElementById('code').value 18 | console.log(code) 19 | const evaluated = Opal.eval(code) 20 | 21 | const context = new AudioContext() 22 | const buffer = context.createBuffer(1, evaluated.length, 44100) 23 | const data = buffer.getChannelData(0) 24 | evaluated.forEach((w, i) => data[i] = w) 25 | 26 | const samplePlayer = context.createBufferSource() 27 | samplePlayer.connect(context.destination) 28 | samplePlayer.buffer = buffer 29 | samplePlayer.start() 30 | drawInCanvas('#canvas', evaluated) 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RubySynth 2 | 3 | ``` 4 | _______ __ ___t____ __ __ 5 | | _ .--.--| |--.--.--| _ .--.--.-----| |_| |--. 6 | |. l | | | _ | | | 1___| | | | _| | 7 | |. _ |_____|_____|___ |____ |___ |__|__|____|__|__| 8 | |: | | |_____|: 1 |_____| 9 | |::.|:. | |::.. . | 10 | `--- ---' `-------' 11 | 12 | ``` 13 | 14 | This Repo holds all the data for both [rubysynth.fun](https://rubysynth.fun) and the presentation about it I gave at Euruko 2019 and a couple of other places. 15 | 16 | The low level code now lives in it's own [project](https://github.com/halfbyte/ruby_synth_blocks) and [gem](https://rubygems.org/gems/synth_blocks). 17 | 18 | The sound examples used in the presentation and on the website are generated with the code in [examples/](examples/). 19 | 20 | ## Licenses 21 | 22 | All code here is licensed under the AGPL 3.0 license as documented at [LICENSE](LICENSE) unless stated otherwise. 23 | 24 | The entire contents of the "website" folder of the repo (and with that, this website you're just reading) is licensed under a [Creative Commons Attribution-ShareAlike Unported license](https://creativecommons.org/licenses/by-sa/3.0/deed.en). The full license is at [website/LICENSE](website/LICENSE) 25 | 26 | -------------------------------------------------------------------------------- /website/docs/created.rid: -------------------------------------------------------------------------------- 1 | Mon, 24 Jun 2019 16:16:16 +0200 2 | README.md Mon, 24 Jun 2019 12:00:05 +0200 3 | lib/adsr.rb Mon, 24 Jun 2019 12:36:10 +0200 4 | lib/chorus.rb Mon, 24 Jun 2019 16:11:32 +0200 5 | lib/compressor.rb Mon, 24 Jun 2019 16:11:47 +0200 6 | lib/delay.rb Mon, 24 Jun 2019 13:06:32 +0200 7 | lib/envelope.rb Mon, 24 Jun 2019 12:40:02 +0200 8 | lib/eq.rb Mon, 24 Jun 2019 16:12:03 +0200 9 | lib/g_verb.rb Mon, 24 Jun 2019 13:18:43 +0200 10 | lib/hihat.rb Mon, 24 Jun 2019 13:28:27 +0200 11 | lib/kick_drum.rb Mon, 24 Jun 2019 13:42:33 +0200 12 | lib/limiter.rb Mon, 24 Jun 2019 16:12:37 +0200 13 | lib/mixer_channel.rb Mon, 24 Jun 2019 13:57:39 +0200 14 | lib/monosynth.rb Mon, 24 Jun 2019 14:59:35 +0200 15 | lib/moog_filter.rb Mon, 24 Jun 2019 16:12:57 +0200 16 | lib/oscillator.rb Mon, 24 Jun 2019 16:07:07 +0200 17 | lib/polysynth.rb Mon, 24 Jun 2019 16:13:22 +0200 18 | lib/ruby_synth.rb Sat, 22 Jun 2019 10:17:58 +0200 19 | lib/send_channel.rb Mon, 24 Jun 2019 16:14:00 +0200 20 | lib/sequencer_dsl.rb Mon, 24 Jun 2019 16:16:10 +0200 21 | lib/snare_drum.rb Mon, 24 Jun 2019 15:50:37 +0200 22 | lib/sound.rb Mon, 24 Jun 2019 16:00:16 +0200 23 | lib/state_variable_filter.rb Mon, 24 Jun 2019 16:01:14 +0200 24 | lib/tuned_drum.rb Mon, 24 Jun 2019 16:02:36 +0200 25 | lib/utils.rb Sat, 18 May 2019 22:50:18 +0200 26 | lib/waveshaper.rb Mon, 24 Jun 2019 16:04:35 +0200 27 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | 11 | # Site settings 12 | # These are used to personalize your new site. If you look in the HTML files, 13 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 14 | # You can create any custom variable you would like, and they will be accessible 15 | # in the templates via {{ site.myvariable }}. 16 | title: RubySynth 17 | email: jan@krutisch.de 18 | description: >- # this means to ignore newlines until "baseurl:" 19 | The documentation website for my Ruby Synth talk 20 | baseurl: "" # the subpath of your site, e.g. /blog 21 | url: "https://ruby-synth.fun" # the base hostname & protocol for your site, e.g. http://example.com 22 | github_username: halfbyte 23 | 24 | # Build settings 25 | markdown: kramdown 26 | theme: just-the-docs 27 | 28 | source: website 29 | timezone: Berlin 30 | 31 | 32 | # WHAT THE F 33 | compress_html: 34 | ignore: 35 | envs: all -------------------------------------------------------------------------------- /examples/simple_song.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | include SynthBlocks::Sequencer::SequencerDSL 3 | 4 | SRATE = 44100 5 | P = nil # pause 6 | 7 | kick_drum = SynthBlocks::Drum::KickDrum.new(SRATE) 8 | snare_drum = SynthBlocks::Drum::SnareDrum.new(SRATE) 9 | hihat = SynthBlocks::Drum::Hihat.new(SRATE) 10 | monosynth = SynthBlocks::Synth::Monosynth.new(SRATE) 11 | polysynth = SynthBlocks::Synth::Polysynth.new(SRATE) 12 | 13 | def_pattern(:drums_full, 16) do 14 | drum_pattern kick_drum, '*---*---*---*---' 15 | drum_pattern snare_drum, '----*-------*---' 16 | drum_pattern hihat, '--*---*---*---*-' 17 | end 18 | 19 | def_pattern(:bassline, 16) do 20 | note_pattern monosynth, [ 21 | ['C1', 4], P, P, P, 22 | P, P, P, P, 23 | ['C#1', 6], P, P, P, 24 | P, P, P, P 25 | ] 26 | end 27 | 28 | def_pattern(:chord, 16) do 29 | note_pattern polysynth, [ 30 | P, P, ['C2,D#2,G2', 2], P, 31 | P, P, P, P, 32 | ['C#2,E2,G#2', 2], P, P, P, 33 | P, P, P, P 34 | ] 35 | end 36 | 37 | 38 | 39 | my_song = song(bpm: 115) do 40 | pattern(:drums_full, at: 0, repeat: 1) 41 | pattern(:drums_full, at: 2, repeat: 2) 42 | pattern(:bassline, at: 0, repeat: 4) 43 | pattern(:chord, at: 0, repeat: 4) 44 | end 45 | 46 | out = my_song.render(SRATE) do |i| 47 | 0.3 * (kick_drum.run(i) + hihat.run(i) + snare_drum.run(i) + monosynth.run(i) + polysynth.run(i)) 48 | end 49 | 50 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 51 | -------------------------------------------------------------------------------- /examples/sequencer.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-prof' 2 | 3 | require 'kick_drum' 4 | require 'monosynth' 5 | require 'polysynth' 6 | require 'snare_drum' 7 | require 'hihat' 8 | require 'sequencer_dsl' 9 | include SequencerDSL 10 | 11 | SRATE = 44100 12 | P = nil # pause 13 | 14 | kick_drum = KickDrum.new(SRATE) 15 | snare_drum = SnareDrum.new(SRATE) 16 | hihat = Hihat.new(SRATE) 17 | monosynth = Monosynth.new(SRATE) 18 | polysynth = Polysynth.new(SRATE) 19 | 20 | def_pattern(:drums_full, 16) do 21 | drum_pattern kick_drum, '*---*---*---*---' 22 | drum_pattern snare_drum, '----*-------*---' 23 | drum_pattern hihat, '--*---*---*---*-' 24 | end 25 | 26 | def_pattern(:bassline, 16) do 27 | note_pattern monosynth, [ 28 | ['C1', 4], P, P, P, 29 | P, P, P, P, 30 | ['C#1', 6], P, P, P, 31 | P, P, P, P 32 | ] 33 | end 34 | 35 | def_pattern(:chord, 16) do 36 | note_pattern polysynth, [ 37 | P, P, ['C2,D#2,G2', 2], P, 38 | P, P, P, P, 39 | ['C#2,E2,G#2', 2], P, P, P, 40 | P, P, P, P 41 | ] 42 | end 43 | 44 | 45 | length = song(bpm: 115) do 46 | pattern(:drums_full, at: 0, repeat: 1) 47 | pattern(:drums_full, at: 2, repeat: 2) 48 | pattern(:bassline, at: 0, repeat: 4) 49 | pattern(:chord, at: 0, repeat: 4) 50 | end 51 | 52 | RubyProf.start 53 | output = [] 54 | (length * SRATE).times do |i| 55 | output << 0.3 * (kick_drum.run(i) + hihat.run(i) + snare_drum.run(i) + monosynth.run(i) + polysynth.run(i)) 56 | end 57 | result = RubyProf.stop 58 | printer = RubyProf::FlatPrinter.new(result) 59 | File.open('prof.txt', 'wb') do |file| 60 | printer.print(file) 61 | end 62 | print output.pack('e*') 63 | -------------------------------------------------------------------------------- /examples/acid_synth.rb: -------------------------------------------------------------------------------- 1 | require 'synth_blocks' 2 | 3 | # 4 | # A more complex example on how to compose synth blocks 5 | # 6 | 7 | include SynthBlocks::Utils 8 | 9 | SFREQ = 44100 10 | NOTES = [24, 24, 48, 37] 11 | OFFSETS = [0, 0, 3, 7] 12 | TEMPO = 120 13 | 14 | def n2f(n) 15 | (2.0 ** ((n - 69) / 12.0)) * 440.0 16 | end 17 | 18 | oscillator = SynthBlocks::Core::Oscillator.new(SFREQ) 19 | filter = SynthBlocks::Core::StateVariableFilter.new(SFREQ) 20 | vol_ar = SynthBlocks::Mod::Envelope.new(0.001,0.1) 21 | flt_ar = SynthBlocks::Mod::Envelope.new(0.02,0.04) 22 | verb = SynthBlocks::Fx::GVerb.new(SFREQ, max_room_size: 1200.0, room_size: 5.0, rev_time: 2.0, damping:0.3, spread: 15.0, input_bandwidth: 0.5, early_level:0.8, tail_level: 0.5) 23 | delay = SynthBlocks::Fx::Delay.new(SFREQ, time: 15.0 / TEMPO.to_f * 3) 24 | eq = SynthBlocks::Fx::Eq.new(SFREQ) 25 | eq.low_gain = 0.8 26 | eq.mid_gain = 0.5 27 | eq.high_gain = 1.0 28 | 29 | delay_eq = SynthBlocks::Fx::Eq.new(SFREQ) 30 | delay_eq.low_gain = 0.2 31 | delay_eq.mid_gain = 0.6 32 | 33 | 34 | out = (4 * SFREQ).times.map do |sample| 35 | t = sample.to_f / SFREQ.to_f # time in seconds 36 | s_per_b = 15.0 / TEMPO.to_f # seconds per quarternote 37 | b = t / s_per_b # quarternote 38 | t_in_b = t % s_per_b #time in quarternote 39 | l = (b / 4).floor # loop 40 | freq = n2f(24 + NOTES[b % NOTES.length] + OFFSETS[l % OFFSETS.length]) 41 | v = oscillator.run(freq, waveform: :sawtooth) 42 | v = filter.run(v, 200 + flt_ar.run(t_in_b) * 1500, 4) 43 | v *= vol_ar.run(t_in_b) 44 | v = simple_waveshaper(v, 2) 45 | v = eq.run(v) 46 | v = delay.run(v) do |f| 47 | delay_eq.run(f) 48 | end 49 | v = v + (verb.run(v) * 0.2) 50 | v *= 0.33 51 | [1.0, [-1.0, v].max].min 52 | end 53 | 54 | SynthBlocks::Core::WaveWriter.write_if_name_given(out) 55 | -------------------------------------------------------------------------------- /website/images/envelope-font-awesome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /website/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome to RubySynth 3 | layout: home 4 | nav_exclude: true 5 | --- 6 | # RubySynth 7 | 8 | RubySynth is a collection of DSP code snippets, held together with duct tape (as one does), to generate (electronic) music with pure Ruby. 9 | 10 | I've built this for my Talk at Euruko 2019 in Rotterdam called "The musical Ruby". This website primarily acts as additional reading material for people who have seen the talk, either online or at the conference. You probably want to [start here](01_introduction.html) 11 | 12 | You can find all source code, including the code for this website at [halfbyte/rubysynth](https://github.com/halfbyte/rubysynth) on GitHub. 13 | 14 | ## Credits 15 | 16 | Thank you to Sam Aaron for [SonicPi](https://sonic-pi.net/) 17 | 18 | Thank you to Lance Norskog for creating [SoX](sox.sourceforge.net/) and Chris Bagwell for maintaining it for more that 20 years now. 19 | 20 | Thank you to the amazing [MusicDSP mailing list](http://musicdsp.org) of where most of the more complicated DSP code has found it's way into this project. 21 | 22 | ## Image/Video Sources 23 | 24 | If not stated otherwise, the Images and Videos on this website are created by myself and subject to the content license mentioned below. All other images are taken from the Wikimedia Commons and are listed below: 25 | 26 | - [DAC chip](https://commons.wikimedia.org/wiki/File:CirrusLogicCS4282-AB.jpg) 27 | - [Loudspeaker](https://commons.wikimedia.org/wiki/File:Loudspeaker_side_en.svg) 28 | - Kick drum 29 | - Snare drum 30 | - [Hihat](https://commons.wikimedia.org/wiki/File:Hi-hat.jpg) 31 | - [Mixing Desk](https://commons.wikimedia.org/wiki/File:Image_of_a_mixing_desk_2014-02-16_00-50.jpg) 32 | - Roland 808 33 | - [Hallgrímskirkja](https://commons.wikimedia.org/wiki/File:Interior_of_Hallgr%C3%ADmskirkja_Church.jpg) 34 | 35 | 36 | ## Licenses 37 | 38 | All code here is licensed under the AGPL 3.0 license as documented at [LICENSE](https://github.com/halfbyte/rubysynth/blob/master/LICENSE) unless stated otherwise. 39 | 40 | The entire contents of the "website" folder of the repo (and with that, this website you're just reading) is licensed under a [Creative Commons Attribution-ShareAlike Unported license](https://creativecommons.org/licenses/by-sa/3.0/deed.en). The full license is at [website/LICENSE](https://github.com/halfbyte/rubysynth/blob/master/website/LICENSE) 41 | 42 | -------------------------------------------------------------------------------- /drawings/sawtooth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 41 | 46 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 68 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /website/images/audio_flow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/docs/js/search.js: -------------------------------------------------------------------------------- 1 | Search = function(data, input, result) { 2 | this.data = data; 3 | this.$input = $(input); 4 | this.$result = $(result); 5 | 6 | this.$current = null; 7 | this.$view = this.$result.parent(); 8 | this.searcher = new Searcher(data.index); 9 | this.init(); 10 | } 11 | 12 | Search.prototype = $.extend({}, Navigation, new function() { 13 | var suid = 1; 14 | 15 | this.init = function() { 16 | var _this = this; 17 | var observer = function(e) { 18 | switch(e.originalEvent.keyCode) { 19 | case 38: // Event.KEY_UP 20 | case 40: // Event.KEY_DOWN 21 | return; 22 | } 23 | _this.search(_this.$input[0].value); 24 | }; 25 | this.$input.keyup(observer); 26 | this.$input.click(observer); // mac's clear field 27 | 28 | this.searcher.ready(function(results, isLast) { 29 | _this.addResults(results, isLast); 30 | }) 31 | 32 | this.initNavigation(); 33 | this.setNavigationActive(false); 34 | } 35 | 36 | this.search = function(value, selectFirstMatch) { 37 | value = jQuery.trim(value).toLowerCase(); 38 | if (value) { 39 | this.setNavigationActive(true); 40 | } else { 41 | this.setNavigationActive(false); 42 | } 43 | 44 | if (value == '') { 45 | this.lastQuery = value; 46 | this.$result.empty(); 47 | this.$result.attr('aria-expanded', 'false'); 48 | this.setNavigationActive(false); 49 | } else if (value != this.lastQuery) { 50 | this.lastQuery = value; 51 | this.$result.attr('aria-busy', 'true'); 52 | this.$result.attr('aria-expanded', 'true'); 53 | this.firstRun = true; 54 | this.searcher.find(value); 55 | } 56 | } 57 | 58 | this.addResults = function(results, isLast) { 59 | var target = this.$result.get(0); 60 | if (this.firstRun && (results.length > 0 || isLast)) { 61 | this.$current = null; 62 | this.$result.empty(); 63 | } 64 | 65 | for (var i=0, l = results.length; i < l; i++) { 66 | var item = this.renderItem.call(this, results[i]); 67 | item.setAttribute('id', 'search-result-' + target.childElementCount); 68 | target.appendChild(item); 69 | }; 70 | 71 | if (this.firstRun && results.length > 0) { 72 | this.firstRun = false; 73 | this.$current = $(target.firstChild); 74 | this.$current.addClass('search-selected'); 75 | } 76 | if (jQuery.browser.msie) this.$element[0].className += ''; 77 | 78 | if (isLast) this.$result.attr('aria-busy', 'false'); 79 | } 80 | 81 | this.move = function(isDown) { 82 | if (!this.$current) return; 83 | var $next = this.$current[isDown ? 'next' : 'prev'](); 84 | if ($next.length) { 85 | this.$current.removeClass('search-selected'); 86 | $next.addClass('search-selected'); 87 | this.$input.attr('aria-activedescendant', $next.attr('id')); 88 | this.scrollIntoView($next[0], this.$view[0]); 89 | this.$current = $next; 90 | this.$input.val($next[0].firstChild.firstChild.text); 91 | this.$input.select(); 92 | } 93 | return true; 94 | } 95 | 96 | this.hlt = function(html) { 97 | return this.escapeHTML(html). 98 | replace(/\u0001/g, ''). 99 | replace(/\u0002/g, ''); 100 | } 101 | 102 | this.escapeHTML = function(html) { 103 | return html.replace(/[&<>]/g, function(c) { 104 | return '&#' + c.charCodeAt(0) + ';'; 105 | }); 106 | } 107 | 108 | }); 109 | 110 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.7) 5 | public_suffix (>= 2.0.2, < 7.0) 6 | ast (2.4.3) 7 | base64 (0.3.0) 8 | bigdecimal (3.2.1) 9 | colorator (1.1.0) 10 | concurrent-ruby (1.3.5) 11 | csv (3.3.4) 12 | date (3.4.1) 13 | em-websocket (0.5.3) 14 | eventmachine (>= 0.12.9) 15 | http_parser.rb (~> 0) 16 | erb (5.0.1) 17 | eventmachine (1.2.7) 18 | ffi (1.17.2) 19 | forwardable-extended (2.6.0) 20 | google-protobuf (4.31.1) 21 | bigdecimal 22 | rake (>= 13) 23 | http_parser.rb (0.8.0) 24 | i18n (1.14.7) 25 | concurrent-ruby (~> 1.0) 26 | io-console (0.8.0) 27 | irb (1.15.2) 28 | pp (>= 0.6.0) 29 | rdoc (>= 4.0.0) 30 | reline (>= 0.4.2) 31 | jekyll (4.4.1) 32 | addressable (~> 2.4) 33 | base64 (~> 0.2) 34 | colorator (~> 1.0) 35 | csv (~> 3.0) 36 | em-websocket (~> 0.5) 37 | i18n (~> 1.0) 38 | jekyll-sass-converter (>= 2.0, < 4.0) 39 | jekyll-watch (~> 2.0) 40 | json (~> 2.6) 41 | kramdown (~> 2.3, >= 2.3.1) 42 | kramdown-parser-gfm (~> 1.0) 43 | liquid (~> 4.0) 44 | mercenary (~> 0.3, >= 0.3.6) 45 | pathutil (~> 0.9) 46 | rouge (>= 3.0, < 5.0) 47 | safe_yaml (~> 1.0) 48 | terminal-table (>= 1.8, < 4.0) 49 | webrick (~> 1.7) 50 | jekyll-include-cache (0.2.1) 51 | jekyll (>= 3.7, < 5.0) 52 | jekyll-sass-converter (3.1.0) 53 | sass-embedded (~> 1.75) 54 | jekyll-seo-tag (2.8.0) 55 | jekyll (>= 3.8, < 5.0) 56 | jekyll-watch (2.2.1) 57 | listen (~> 3.0) 58 | json (2.12.2) 59 | just-the-docs (0.10.1) 60 | jekyll (>= 3.8.5) 61 | jekyll-include-cache 62 | jekyll-seo-tag (>= 2.0) 63 | rake (>= 12.3.1) 64 | kramdown (2.5.1) 65 | rexml (>= 3.3.9) 66 | kramdown-parser-gfm (1.1.0) 67 | kramdown (~> 2.0) 68 | liquid (4.0.4) 69 | listen (3.9.0) 70 | rb-fsevent (~> 0.10, >= 0.10.3) 71 | rb-inotify (~> 0.9, >= 0.9.10) 72 | logger (1.7.0) 73 | mercenary (0.4.0) 74 | opal (1.8.2) 75 | ast (>= 2.3.0) 76 | parser (~> 3.0, >= 3.0.3.2) 77 | parser (3.3.8.0) 78 | ast (~> 2.4.1) 79 | racc 80 | pathutil (0.16.2) 81 | forwardable-extended (~> 2.6) 82 | pp (0.6.2) 83 | prettyprint 84 | prettyprint (0.2.0) 85 | psych (5.2.6) 86 | date 87 | stringio 88 | public_suffix (6.0.2) 89 | racc (1.8.1) 90 | rake (13.3.0) 91 | rb-fsevent (0.11.2) 92 | rb-inotify (0.11.1) 93 | ffi (~> 1.0) 94 | rdoc (6.14.0) 95 | erb 96 | psych (>= 4.0.0) 97 | reline (0.6.1) 98 | io-console (~> 0.5) 99 | rexml (3.4.1) 100 | rouge (4.5.2) 101 | safe_yaml (1.0.5) 102 | sass-embedded (1.89.1) 103 | google-protobuf (~> 4.31) 104 | rake (>= 13) 105 | stringio (3.1.7) 106 | synth_blocks (1.0.3) 107 | wavefile (~> 1.1) 108 | terminal-table (3.0.2) 109 | unicode-display_width (>= 1.1.1, < 3) 110 | unicode-display_width (2.6.0) 111 | wavefile (1.1.2) 112 | webrick (1.9.1) 113 | 114 | PLATFORMS 115 | ruby 116 | 117 | DEPENDENCIES 118 | base64 119 | bigdecimal 120 | csv 121 | irb 122 | jekyll (~> 4.0) 123 | just-the-docs (~> 0.3) 124 | logger 125 | opal 126 | rake (~> 13.0) 127 | synth_blocks (~> 1.0) 128 | tzinfo-data 129 | 130 | BUNDLED WITH 131 | 2.6.9 132 | -------------------------------------------------------------------------------- /website/_sass/custom/custom.scss: -------------------------------------------------------------------------------- 1 | // // 2 | // // Typography 3 | // // 4 | // 5 | // $body-font-family: -apple-system, BlinkMacSystemFont, "helvetica neue", helvetica, roboto, noto, "segoe ui", arial, sans-serif; 6 | // $mono-font-family: "SFMono-Regular", Menlo, Consolas, Monospace; 7 | // $root-font-size: 16px; // Base font-size for rems 8 | // $body-line-height: 1.4; 9 | // $body-heading-line-height: 1.15; 10 | // 11 | // // 12 | // // Colors 13 | // // 14 | // 15 | // $white: #fff; 16 | // 17 | // $grey-dk-000: #959396; 18 | // $grey-dk-100: #5c5962; 19 | // $grey-dk-200: #44434d; 20 | // $grey-dk-250: #302d36 !default; 21 | // $grey-dk-300: #27262b; 22 | // 23 | // $grey-lt-000: #f5f6fa; 24 | // $grey-lt-100: #eeebee; 25 | // $grey-lt-200: #ecebed; 26 | // $grey-lt-300: #e6e1e8; 27 | // 28 | // $purple-000: #7253ed; 29 | // $purple-100: #5e41d0; 30 | // $purple-200: #4e26af; 31 | // $purple-300: #381885; 32 | // 33 | // $blue-000: #2c84fa; 34 | // $blue-100: #2869e6; 35 | // $blue-200: #264caf; 36 | // $blue-300: #183385; 37 | // 38 | // $green-000: #41d693; 39 | // $green-100: #11b584; 40 | // $green-200: #009c7b; 41 | // $green-300: #026e57; 42 | // 43 | // $body-background-color: $white !default; 44 | // $sidebar-color: $grey-lt-000 !default; 45 | // $code-background-color: $grey-lt-000 !default; 46 | 47 | // $body-text-color: $grey-dk-100 !default; 48 | // $body-heading-color: $grey-dk-300 !default; 49 | // $nav-child-link-color: $grey-dk-100 !default; 50 | // $link-color: $purple-000 !default; 51 | // $btn-primary-color: $purple-100 !default; 52 | // $base-button-color: #f7f7f7 !default; 53 | // 54 | // // 55 | // // Media queries in pixels 56 | // // 57 | // 58 | // $media-queries: ( 59 | // xs: 320px, 60 | // sm: 500px, 61 | // md: 740px, 62 | // lg: 1120px, 63 | // xl: 1400px 64 | // ); 65 | // 66 | // // 67 | // // Spacing 68 | // // 69 | // 70 | // $spacing-unit: 1rem; // 1rem == 16px 71 | // 72 | // $spacers: ( 73 | // sp-0: 0, 74 | // sp-1: $spacing-unit * 0.25, 75 | // sp-2: $spacing-unit * 0.5, 76 | // sp-3: $spacing-unit * 0.75, 77 | // sp-4: $spacing-unit, 78 | // sp-5: $spacing-unit * 1.5, 79 | // sp-6: $spacing-unit * 2, 80 | // sp-7: $spacing-unit * 2.5, 81 | // sp-8: $spacing-unit * 3, 82 | // sp-9: $spacing-unit * 3.5, 83 | // sp-10: $spacing-unit * 4 84 | // ); 85 | // 86 | // $sp-1: map-get($spacers, sp-1); // 0.25 rem == 4px 87 | // $sp-2: map-get($spacers, sp-2); // 0.5 rem == 8px 88 | // $sp-3: map-get($spacers, sp-3); // 0.75 rem == 12px 89 | // $sp-4: map-get($spacers, sp-4); // 1 rem == 16px 90 | // $sp-5: map-get($spacers, sp-5); // 1.5 rem == 24px 91 | // $sp-6: map-get($spacers, sp-6); // 2 rem == 32px 92 | // $sp-7: map-get($spacers, sp-7); // 2.5 rem == 40px 93 | // $sp-8: map-get($spacers, sp-8); // 3 rem == 48px 94 | // $sp-9: map-get($spacers, sp-9); // 4 rem == 48px 95 | // $sp-10: map-get($spacers, sp-10); // 4.5 rem == 48px 96 | // 97 | // // 98 | // // Borders 99 | // // 100 | // 101 | // $border: 1px solid; 102 | // $border-radius: 4px; 103 | // $border-color: $grey-lt-100; 104 | // 105 | // // 106 | // // Grid system 107 | // // 108 | // 109 | // $gutter-spacing: $sp-6; 110 | // $gutter-spacing-sm: $sp-4; 111 | // $nav-width: 232px; 112 | // $content-width: 800px; 113 | // 114 | // $media-queries: ( 115 | // xs: 320px, 116 | // sm: 500px, 117 | // md: 740px, 118 | // lg: 800px, 119 | // xl: 1316px 120 | // ); 121 | 122 | .main-content video { 123 | width: 100%; 124 | } 125 | 126 | .main-content img { 127 | display: block; 128 | margin: 0 auto 1em auto; 129 | max-width: 100%; 130 | } 131 | 132 | canvas.scope { 133 | background: #000; 134 | width: 600px; 135 | } 136 | .player { 137 | width: 600px; 138 | display: inline-block; 139 | margin-bottom: 1em; 140 | } 141 | .player-fft { 142 | width: 600px; 143 | display: inline-block; 144 | } 145 | .player button { 146 | display: block; 147 | width: 100%; 148 | background: green; 149 | border: 1px solid black; 150 | box-shadow: 2px 2px 3px rgba(0,0,0,0.2); 151 | border-radius: 5px; 152 | padding: 5px; 153 | color: #fff; 154 | } 155 | -------------------------------------------------------------------------------- /drawings/waveshaper.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 41 | 46 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 68 | 75 | 81 | 86 | 91 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /website/04_drums.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Drum sounds 3 | layout: page 4 | nav_order: 4 5 | --- 6 | # Drum Sounds 7 | 8 | We're now perfectly equipped to create some lush or harsh synthesizer sounds, so we should be able to make some good ambient music. But while I do like me some good ambient, I'm more of a dance music kind of guy and for that we're missing an important part. Now, I could cheat here and just use sampling - That's what most dance music producers nowadays do, but that would be cheating, right? 9 | 10 | Additionally, if you look at how music styles like House and Techno came about, they were literally based on the sounds of two drum machines, both from Roland, the TR-808 and the TR-909 - The 808 is using analog synthesis for everything while the 909 uses a limited amount of samples - So generating drums with the sythesizing technologies we've explored so far would give us a good understanding of how these machines worked. 11 | 12 | # Kick drum 13 | 14 | ![A photo of a kick drum (or bass drum)](images/kickdrum.jpg) 15 | 16 | Let's start with the foundation of every house or techno groove, the bassdrum or kickdrum. If you analyze the sound of an accoustic kick drum (or basically any drum with a similar design with a drum head and a drum corpus), as if you were a Roland engineer in the 80's, you'll notice two very distinct parts of a drum sound. The sharp attack, when the drum stick hits the skin of the drum head, which is really more like a shark click and then the sound of the drum body, where the full body including the two skins (and the air trapped in them) vibrates. 17 | 18 | To simulate these two parts, We can use a sine wave (or a filtered down squarewave) and quickly modulate it's pitch from a relatively high value to a relatively low value. By varying the speed and the two frequencies, we can get a surprising amout of different percussion sounds and some of them do sound like a bassdrum. 19 | 20 | ![Structure of the kick drum sound](images/kickdrum.png) 21 | 22 | In this example, I've also modulated the filter in a way so that it is a bit more open at the very beginning, adding to that sharpness of the first attack. 23 | 24 | # Snare drum 25 | 26 | ![A drawing of a snare drum](images/snaredrum.png) 27 | .png) 28 | A snare drum is, together with the kick drum, the backbone of every rock groove. What a snare drum is, esentially, is a tom, so a midsized drum, that has a so called snare carpet that lies on the bottom drum skin. This leads to the characteristic noisy sound of a snare that is much more than just the sound of the drum, which would sound like this. 29 | 30 | To construct a snare sound, we pretty much need two components - A version of our kick drum sound that is just slightly pitched up (as a smaller drum usually creates a higher pitche sounds), like this, and then something to emulate the snares. To me, this actually sounds a lot like white noise and so that's what we're going to use. White noise is surprisingly easy to create: 31 | 32 | ```ruby 33 | noise_out = rand * 2.0 - 1.0 34 | ``` 35 | 36 | On top of that, we're going to emulate the snare wires being a bit more bright in the beginning and then dampening down while they settle by putting a lowpass filter on top with an envelope shaping the sound a little it. Of course I aso put another envelope on the volume. 37 | 38 | ![Structure of the snare drum sound](images/snare_drum.png) 39 | 40 | 41 | # Hihat 42 | 43 | ![A photo of a hihat (or bass drum)](images/hihat.jpg) 44 | 45 | This is our last drum sound. The hihat, in a traditional drum kit, looks like this - It's essentially two cymbals on top of each other and you can move them together with a foot pedal, resulting in either very short or longer sounds. 46 | 47 | Our hihat emulation will be somewhat crude, but it will actually work quite well within the groove we want to build with this. 48 | 49 | Again, we'll start with white noise. Having a shorter or longer decay on our volume envelope gives us the chance to emulate an open or a closed hihat. By adding a static filter, a bandpass in this case, we can sort of tune the noise into something that at least starts to sound like a real hihat. To be honest, in my own music, I often favour these clean sounding hihats to accoustic ones. 50 | 51 | ![Structure of the hihat sound](images/hihat.png) 52 | -------------------------------------------------------------------------------- /website/docs/js/navigation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Navigation allows movement using the arrow keys through the search results. 3 | * 4 | * When using this library you will need to set scrollIntoView to the 5 | * appropriate function for your layout. Use scrollInWindow if the container 6 | * is not scrollable and scrollInElement if the container is a separate 7 | * scrolling region. 8 | */ 9 | Navigation = new function() { 10 | this.initNavigation = function() { 11 | var _this = this; 12 | 13 | $(document).keydown(function(e) { 14 | _this.onkeydown(e); 15 | }).keyup(function(e) { 16 | _this.onkeyup(e); 17 | }); 18 | 19 | this.navigationActive = true; 20 | } 21 | 22 | this.setNavigationActive = function(state) { 23 | this.navigationActive = state; 24 | this.clearMoveTimeout(); 25 | } 26 | 27 | this.onkeyup = function(e) { 28 | if (!this.navigationActive) return; 29 | 30 | switch(e.keyCode) { 31 | case 37: //Event.KEY_LEFT: 32 | case 38: //Event.KEY_UP: 33 | case 39: //Event.KEY_RIGHT: 34 | case 40: //Event.KEY_DOWN: 35 | this.clearMoveTimeout(); 36 | break; 37 | } 38 | } 39 | 40 | this.onkeydown = function(e) { 41 | if (!this.navigationActive) return; 42 | switch(e.keyCode) { 43 | case 37: //Event.KEY_LEFT: 44 | if (this.moveLeft()) e.preventDefault(); 45 | break; 46 | case 38: //Event.KEY_UP: 47 | if (e.keyCode == 38 || e.ctrlKey) { 48 | if (this.moveUp()) e.preventDefault(); 49 | this.startMoveTimeout(false); 50 | } 51 | break; 52 | case 39: //Event.KEY_RIGHT: 53 | if (this.moveRight()) e.preventDefault(); 54 | break; 55 | case 40: //Event.KEY_DOWN: 56 | if (e.keyCode == 40 || e.ctrlKey) { 57 | if (this.moveDown()) e.preventDefault(); 58 | this.startMoveTimeout(true); 59 | } 60 | break; 61 | case 13: //Event.KEY_RETURN: 62 | if (this.$current) e.preventDefault(); 63 | this.select(this.$current); 64 | break; 65 | } 66 | if (e.ctrlKey && e.shiftKey) this.select(this.$current); 67 | } 68 | 69 | this.clearMoveTimeout = function() { 70 | clearTimeout(this.moveTimeout); 71 | this.moveTimeout = null; 72 | } 73 | 74 | this.startMoveTimeout = function(isDown) { 75 | if (!$.browser.mozilla && !$.browser.opera) return; 76 | if (this.moveTimeout) this.clearMoveTimeout(); 77 | var _this = this; 78 | 79 | var go = function() { 80 | if (!_this.moveTimeout) return; 81 | _this[isDown ? 'moveDown' : 'moveUp'](); 82 | _this.moveTimeout = setTimeout(go, 100); 83 | } 84 | this.moveTimeout = setTimeout(go, 200); 85 | } 86 | 87 | this.moveRight = function() { 88 | } 89 | 90 | this.moveLeft = function() { 91 | } 92 | 93 | this.move = function(isDown) { 94 | } 95 | 96 | this.moveUp = function() { 97 | return this.move(false); 98 | } 99 | 100 | this.moveDown = function() { 101 | return this.move(true); 102 | } 103 | 104 | /* 105 | * Scrolls to the given element in the scrollable element view. 106 | */ 107 | this.scrollInElement = function(element, view) { 108 | var offset, viewHeight, viewScroll, height; 109 | offset = element.offsetTop; 110 | height = element.offsetHeight; 111 | viewHeight = view.offsetHeight; 112 | viewScroll = view.scrollTop; 113 | 114 | if (offset - viewScroll + height > viewHeight) { 115 | view.scrollTop = offset - viewHeight + height; 116 | } 117 | if (offset < viewScroll) { 118 | view.scrollTop = offset; 119 | } 120 | } 121 | 122 | /* 123 | * Scrolls to the given element in the window. The second argument is 124 | * ignored 125 | */ 126 | this.scrollInWindow = function(element, ignored) { 127 | var offset, viewHeight, viewScroll, height; 128 | offset = element.offsetTop; 129 | height = element.offsetHeight; 130 | viewHeight = window.innerHeight; 131 | viewScroll = window.scrollY; 132 | 133 | if (offset - viewScroll + height > viewHeight) { 134 | window.scrollTo(window.scrollX, offset - viewHeight + height); 135 | } 136 | if (offset < viewScroll) { 137 | window.scrollTo(window.scrollX, offset); 138 | } 139 | } 140 | } 141 | 142 | -------------------------------------------------------------------------------- /website/presentations/2019-06-22_euruko.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Euruko 2019 3 | parent: Presentations 4 | --- 5 | 6 | 7 | 8 | The musical Ruby - A presentation by Jan Krutisch for Euruko 2019 9 | 10 | 119 | 120 | 121 | 124 | 125 | 127 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /website/01_introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | layout: page 4 | nav_order: 1 5 | --- 6 | # Introduction 7 | 8 | We'll start this off with a short demonstration of a software called SonicPi, which allows you to live code music: 9 | 10 | 14 | 15 | Sonic Pi is a wonderful piece of software. It can teach both the basics of programming and the basics of music making while sounding quite professional. That's due to the great foundation Sonic Pi is based on: Supercollider is a system specifically built for live coding and has been under continuous development since 1996! 16 | 17 | But today we're going to leave those comfortable foundations behind and going to try to recreate at least parts of it with pure ruby. The idea here is that we can use an expressive and easy to read language like ruby and show how to implement stuff that is usually written in C or C++, with lots of performance optimizations, which doesn't necessarily makes for the most readable code. 18 | 19 | We're going to do this in three parts. At first we're going to take a look at how to generate typical synthesizer sounds, including some drum sounds. Next, we're going to figure out how to turn these into music by looking at sequencing (or, in old school terms, arranging). Last but not least we'll add some effects that will bring the sound to the pro level. 20 | 21 | I'll show a lot of code in this talk, but don't concentrate too much on it, though, it's all available for you to study afterwards. And, unfortunately, since we have a very full todo list for the rest of this talk, I need to glance over a couple of things I would have loved to talk about in detail - But fear not, the content is all there, if only in written form. If you go to ruby-synth.fun, you'll find a lot of additional material there, including, of course, all the code examples. 22 | 23 | ## Making noise with Ruby 24 | 25 | Ruby doesn't have a simple, integrated way of outputting sound directly to your computer's soundcard. While there may be libraries, I opted to use the power of Unix and leverage a tool called SoX, (short for "Sound eXchange"), which is essentially the swiss army knife of audio file conversion. It's been around for ages, I even used it back when I still had my AMIGA computer at the end of the 90's. Originally conceived by Lance Norskog, and then maintained by Chris Bagwell, it has thousands of contributors and is a great example for a long running open source project that does one thing and does it well. By playing around with Ruby's quirky `Array#pack` method and SoX's command line switches, I came up with the following snippet to make ruby output a binary stream of 32 bit, little endian float values: 26 | 27 | ```ruby 28 | SAMPLING_FREQUENCY=44100 29 | FREQUENCY=440 30 | 31 | in_cycle = 0 32 | samples = SAMPLING_FREQUENCY.times.map do 33 | period = SAMPLING_FREQUENCY / FREQUENCY.to_f 34 | output = in_cycle > 0.5 ? -1.0 : 1.0 35 | in_cycle = (in_cycle + (1.0 / period)) % 1.0 36 | output * 0.5 37 | end 38 | print samples.pack('e*') 39 | ``` 40 | we then can use the following bash script that utilizes SoX's `play` command to pipe the output of the ruby script to your soundcard: 41 | 42 | ```bash 43 | #!/bin/bash 44 | # play.sh 45 | ruby -Ilib $1 | play -t raw -b 32 -r 44100 -c 1 \ 46 | -e floating-point --endian little - 47 | ``` 48 | 49 | ```bash 50 | $ ./play.sh examples/square_wave.rb 51 | ``` 52 | 53 | If, instead of sending the data to our soundcard, we want to save it to a soundfile, to, for example, play the file from a webpage (how very self referential of me, right?), we can use the following snippet and invocation: 54 | 55 | ```bash 56 | #!/bin/bash 57 | # save.sh 58 | ruby -Ilib $1 | sox -t raw -b 32 -r 44100 -c 1 \ 59 | -e floating-point --endian little - -t wav -b 16 $2 60 | ``` 61 | 62 | ```bash 63 | $ ./save.sh examples/square_wave.rb square_wave.wav 64 | ``` 65 | 66 | ## Try it yourself 67 | 68 | 1. Check out the repo [halfbyte/rubysynth](https://github.com/halfbyte/rubysynth) 69 | 1. Install SoX (Available on Homebrew, all linux package managers and Chocolatey) 70 | 1. `./bin/play.sh examples/square_wave.rb` or `./bin/play.sh examples/square_wave.rb square_wave.wav` 71 | 72 | I've developed and tested all of this on Linux, it should run fine on MacOS. 73 | 74 | I'll try to provide Windows .bat files as well later on. 75 | 76 | -------------------------------------------------------------------------------- /website/presentations/2019-08-23_railsgrills.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Railsgrills 3.1 Praha 3 | parent: Presentations 4 | --- 5 | 6 | 7 | 8 | The musical Ruby - A presentation by Jan Krutisch for Railsgrills 3.1 9 | 10 | 119 | 120 | 121 | 124 | 125 | 127 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /website/05_sequencer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Building a sequencer 3 | layout: page 4 | nav_order: 5 5 | --- 6 | # Sequencing basics 7 | 8 | Now that we have our sound repertoire, we can start to think about how to turn these single sounds into actual music. 9 | 10 | For that, we need to learn a little about sequencing and rhythm. 11 | 12 | ## A bar and a beat 13 | 14 | Most modern dance music (as is most music you would hear on the radio) is writting in a straight 4/4 metric. But what does that mean? 15 | 16 | It means that a measure (or bar) of our music is comprised of four quarter notes. A quarter note in in this measure represents a beat. 17 | 18 | I'm not going to go into more detail about rhythm, because the 4/4 is so easy to work with and this can get complicated very quickly - just one quick example of a different beat, here's a 6/8 walz. 19 | 20 | Tempo in music is often measured in beats per minute or short BPM. For 4/4, this very conveniently means that we can simply count the quarter notes. To make things even more simpler, we can stick to a very typical (for, say, slow house music) 120 BPM and thus we can determine that we'll need 2 beats per second, meaning that each beat is half a second long. 21 | 22 | Each 16th note will thus be 1/8 of a second long. This is relevant because a 16th note is a very common unit used in so calles step sequencers, made popular by drum machines like the legendary Roland 808, pictured below, which used 16 buttons to make it easy to program these 16 steps in a bar in a very convenient manner. 23 | 24 | ![The Roland 808](images/roland_808.jpg) 25 | 26 | Here's how you would do the math in code: 27 | 28 | ``` ruby 29 | BPM = 120 30 | beat_length_in_seconds = 60 / BPM # = 0.5s 31 | bar_length = beat_length_in_seconds * 4 # = 2s 32 | sixteenth_note_length = beat_length_in_seconds / 4 # = 0.125s 33 | 34 | ``` 35 | 36 | ## Patterns 37 | 38 | Most modern dance music is developed on a basis of so called patterns. patterns, in this case mean small chunks of music, often a bar for example or multiples thereof, that can then be assembled into full songs. A pattern could be a drum pattern for example. 39 | 40 | Here's a screenshot of how that looks in a modern music production software, in this case Ableton Live, which I also use to produce music if I'm not programming it in Ruby: 41 | 42 | ![Screenshot of the arrange view of Ableton Live](../presentation/images/notes_patterns_songs.png) 43 | 44 | In a way, you can think of patterns as abstractions that can be reused. Which brings us back to the question of how we could best represent this in code. 45 | 46 | ## Let's make a DSL for that. 47 | 48 | Here's some "dream code" to define a drum pattern: 49 | 50 | ``` ruby 51 | def_pattern(:drums_full, 16) do 52 | drum_pattern kick_drum, '*---*---*---*---' 53 | drum_pattern snare_drum, '----*-------*---' 54 | drum_pattern hihat, '--*---*---*---*-' 55 | end 56 | ``` 57 | 58 | The following example is a first crude idea on how melody pattern definitions could look like: 59 | 60 | ``` ruby 61 | def_pattern(:bassline, 16) do 62 | note_pattern monosynth, [ 63 | ['C1', 4], P, P, P, 64 | P, P, P, P, 65 | ['C#1', 6], P, P, P, 66 | P, P, P, P 67 | ] 68 | end 69 | ``` 70 | 71 | The P in there is just a constant set to nil, and stands for "Pause". The array contains a note (note and octave) and a length in steps (or 1/16 notes). 72 | 73 | Now, let's take these pattern definitions and then turn them into a song: 74 | 75 | ``` ruby 76 | length = song(bpm: 115) do 77 | pattern(:drums_full, at: 0, repeat: 1) 78 | pattern(:drums_full, at: 2, repeat: 2) 79 | pattern(:bassline, at: 0, repeat: 4) 80 | pattern(:chord, at: 0, repeat: 4) 81 | end 82 | ``` 83 | 84 | So, you can start a pattern at a certain position (specified in bars this time) and you can repeat them as many times as you want. Together with setting the tempo, this code can now schdule all notes to all the instruments. 85 | 86 | The code to make this all work is a bit too long to list it all here, so please, by all means take a look at [lib/sequencer_dsl.rb](https://github.com/halfbyte/rubysynth/blob/master/lib/sequencer_dsl.rb). 87 | 88 | Additonally, take a look at [lib/sound.rb](https://github.com/halfbyte/rubysynth/blob/master/lib/sound.rb) which is the base class for every sound generator and among other things completely handles interpreting the scheduled events into a coherent view usable by the generator to render the actual sounds. 89 | 90 | 91 | -------------------------------------------------------------------------------- /website/presentations/2020-07-18_rubyconf_by.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: RubyConfOnline.by 2020 3 | parent: Presentations 4 | --- 5 | 6 | 7 | 8 | Ruby Patterns for contempory dance music - A presentation by Jan Krutisch for RubyConfOnline.by 2020 9 | 10 | 138 | 139 | 140 | 143 | 144 | 146 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /website/docs/Limiter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | class Limiter - RDoc Documentation 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 82 | 83 |
84 |

85 | class Limiter 86 |

87 | 88 |
89 | 90 |

Simple soft limiter Taken from github.com/pichenettes/stmlib/blob/448babb082dfe7b0a1ffbf0b349eefde64691b49/dsp/dsp.h#L97

91 | 92 |
93 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 |
107 |

Public Instance Methods

108 |
109 | 110 | 111 |
112 | 113 |
114 | run(x) 116 | 117 | click to toggle source 118 | 119 |
120 | 121 | 122 |
123 | 124 |

run limiter

125 | 126 | 127 | 128 | 129 |
130 |
# File lib/limiter.rb, line 8
131 | def run(x)
132 |   x * (27.0 + x * x) / (27.0 + 9.0 * x * x)
133 | end
134 |
135 | 136 |
137 | 138 | 139 | 140 | 141 |
142 | 143 | 144 |
145 | 146 |
147 | 148 |
149 | 150 | 151 | 156 | 157 | -------------------------------------------------------------------------------- /website/docs/README_md.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | README - RDoc Documentation 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 80 | 81 |
82 | 83 |

RubySynth

84 | 85 |
_______       __          ___t____             __   __
 86 | |   _   .--.--|  |--.--.--|   _   .--.--.-----|  |_|  |--.
 87 | |.  l   |  |  |  _  |  |  |   1___|  |  |     |   _|     |
 88 | |.  _   |_____|_____|___  |____   |___  |__|__|____|__|__|
 89 | |:  |   |           |_____|:  1   |_____|
 90 | |::.|:. |                 |::.. . |
 91 | `--- ---'                 `-------'
92 | 99 | 100 |

Eventually I'll try to turn the primitives into a proper gem and better tests, but that will depend on your interest and if I can give this talk a couple of more times.

101 | 102 |

Contributing or just playing around

103 | 110 | 111 |

Licenses

112 | 113 |

All code here is licensed under the AGPL 3.0 license as documented at LICENSE unless stated otherwise.

114 | 115 |

The entire contents of the “website” folder of the repo (and with that, this website you're just reading) is licensed under a Creative Commons Attribution-ShareAlike Unported license. The full license is at website/LICENSE

116 | 117 |
118 | 119 | 120 | 121 | 126 | 127 | -------------------------------------------------------------------------------- /website/js/player.js: -------------------------------------------------------------------------------- 1 | const context = new AudioContext() 2 | 3 | class DemoPlayer { 4 | constructor(slideshow, context, element) { 5 | this.active = false 6 | this.slideshow = slideshow 7 | this.context = context 8 | this.element = element 9 | this.sourceNode = new MediaElementAudioSourceNode(this.context, {mediaElement: element}) 10 | this.analyzer = context.createAnalyser() 11 | this.analyzer.fftSize = 4096 12 | console.log(this.analyzer.frequencyBinCount) 13 | this.sourceNode.connect(this.analyzer) 14 | this.sourceNode.connect(this.context.destination) 15 | const player = element.getAttribute('data-player') 16 | const [playerType, playerVariant] = player.split('-') 17 | this.playerType = playerType 18 | this.playerVariant = playerVariant 19 | this.slideParent = this.element.closest('.remark-slide-content') 20 | this.insertUI() 21 | if (this.slideshow != null) { 22 | this.slideshow.on('hideSlide', () => { this.active = false}) 23 | } 24 | this.element.addEventListener('play', this.play.bind(this)) 25 | this.element.addEventListener('ended', this.ended.bind(this)) 26 | } 27 | insertUI() { 28 | const wrapper = document.createElement('div') 29 | wrapper.className = `player player-${this.playerType}` 30 | const playButton = document.createElement('button') 31 | const buttonText = document.createTextNode("Play ▶") 32 | playButton.appendChild(buttonText) 33 | this.element.parentNode.insertBefore(wrapper, this.element.nextSibling) 34 | playButton.addEventListener('click', (e) => this.element.play()) 35 | 36 | this.scope = document.createElement('canvas') 37 | this.scopeContext = this.scope.getContext('2d') 38 | this.scope.width = 2048 39 | this.scope.height = 1152 40 | 41 | if (this.playerType !== 'simple') { 42 | if (this.playerVariant === 'full' && this.slideParent != null) { 43 | this.scope.className = "scope-full" 44 | this.slideParent.appendChild(this.scope) 45 | } else { 46 | this.scope.className = "scope" 47 | wrapper.appendChild(this.scope) 48 | } 49 | } 50 | 51 | if (this.playerType === 'scope') { 52 | this.updateScope = this.updateScope.bind(this) 53 | } 54 | if (this.playerType === 'fft') { 55 | this.updateFFT = this.updateFFT.bind(this) 56 | } 57 | wrapper.appendChild(playButton) 58 | } 59 | updateScope() { 60 | const dataArray = new Float32Array(this.analyzer.fftSize) 61 | const halfHeight = this.scope.height / 2 62 | this.analyzer.getFloatTimeDomainData(dataArray) 63 | this.scopeContext.clearRect(0, 0, this.scope.width, this.scope.height) 64 | if (this.playerVariant === 'full') { 65 | this.scopeContext.strokeStyle = "rgba(0,128,0,0.5)" 66 | } else { 67 | this.scopeContext.strokeStyle = "#0f0" 68 | } 69 | this.scopeContext.lineWidth = 4 70 | this.scopeContext.beginPath() 71 | this.scopeContext.moveTo(0, halfHeight) 72 | dataArray.forEach(function(v, i) { 73 | const x = i / this.analyzer.fftSize * this.scope.width 74 | const y = v * halfHeight + halfHeight 75 | this.scopeContext.lineTo(x,y) 76 | }, this) 77 | this.scopeContext.stroke() 78 | if (this.active) { 79 | requestAnimationFrame(this.updateScope) 80 | } else { 81 | this.scopeContext.clearRect(0, 0, this.scope.width, this.scope.height) 82 | } 83 | } 84 | updateFFT() { 85 | const dataArray = new Uint8Array(this.analyzer.frequencyBinCount) 86 | this.analyzer.getByteFrequencyData(dataArray) 87 | // console.log(dataArray) 88 | this.scopeContext.clearRect(0, 0, this.scope.width, this.scope.height) 89 | if (this.playerVariant === 'full') { 90 | this.scopeContext.strokeStyle = "rgba(0,128,0,0.5)" 91 | } else { 92 | this.scopeContext.strokeStyle = "#0f0" 93 | } 94 | this.scopeContext.lineWidth = 4 95 | this.scopeContext.beginPath() 96 | dataArray.forEach(function(v, i) { 97 | const x = i / this.analyzer.frequencyBinCount * this.scope.width 98 | const halfHeight = this.scope.height / 2 99 | const y = this.scope.height - (this.scope.height * (v / 255)) 100 | if (i == 0) { this.scopeContext.moveTo(x, y) } 101 | this.scopeContext.lineTo(x,y) 102 | }, this) 103 | this.scopeContext.stroke() 104 | if (this.active) { 105 | requestAnimationFrame(this.updateFFT) 106 | } else { 107 | this.scopeContext.clearRect(0, 0, this.scope.width, this.scope.height) 108 | } 109 | } 110 | play() { 111 | this.context.resume() 112 | if (!this.active) { 113 | this.active = true 114 | if (this.playerType === 'scope') { 115 | this.updateScope() 116 | } 117 | if (this.playerType === 'fft') { 118 | this.updateFFT() 119 | } 120 | } 121 | } 122 | ended() { 123 | this.active = false 124 | } 125 | 126 | } 127 | 128 | function initPlayer() { 129 | document.querySelectorAll('audio[data-player]').forEach(function(element) { 130 | let sl = null 131 | if (typeof slideshow !== 'undefined') { 132 | sl = slideshow 133 | } 134 | new DemoPlayer(sl, context, element) 135 | }) 136 | } 137 | -------------------------------------------------------------------------------- /drawings/compressor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 35 | 36 | 44 | 50 | 51 | 52 | 72 | 77 | 82 | 87 | 92 | 93 | 95 | 96 | 98 | image/svg+xml 99 | 101 | 102 | 103 | 104 | 105 | 110 | 117 | 125 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /website/docs/js/darkfish.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Darkfish Page Functions 4 | * $Id: darkfish.js 53 2009-01-07 02:52:03Z deveiant $ 5 | * 6 | * Author: Michael Granger 7 | * 8 | */ 9 | 10 | /* Provide console simulation for firebug-less environments */ 11 | if (!("console" in window) || !("firebug" in console)) { 12 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", 13 | "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; 14 | 15 | window.console = {}; 16 | for (var i = 0; i < names.length; ++i) 17 | window.console[names[i]] = function() {}; 18 | }; 19 | 20 | 21 | /** 22 | * Unwrap the first element that matches the given @expr@ from the targets and return them. 23 | */ 24 | $.fn.unwrap = function( expr ) { 25 | return this.each( function() { 26 | $(this).parents( expr ).eq( 0 ).after( this ).remove(); 27 | }); 28 | }; 29 | 30 | 31 | function showSource( e ) { 32 | var target = e.target; 33 | var codeSections = $(target). 34 | parents('.method-detail'). 35 | find('.method-source-code'); 36 | 37 | $(target). 38 | parents('.method-detail'). 39 | find('.method-source-code'). 40 | slideToggle(); 41 | }; 42 | 43 | function hookSourceViews() { 44 | $('.method-heading').click( showSource ); 45 | }; 46 | 47 | function hookSearch() { 48 | var input = $('#search-field').eq(0); 49 | var result = $('#search-results').eq(0); 50 | $(result).show(); 51 | 52 | var search_section = $('#search-section').get(0); 53 | $(search_section).show(); 54 | 55 | var search = new Search(search_data, input, result); 56 | 57 | search.renderItem = function(result) { 58 | var li = document.createElement('li'); 59 | var html = ''; 60 | 61 | // TODO add relative path to 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 84 | 85 |
86 |

87 | class Damper 88 |

89 | 90 |
91 | 92 |
93 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 |
107 |

Public Class Methods

108 |
109 | 110 | 111 |
112 | 113 |
114 | new(damping) 116 | 117 | click to toggle source 118 | 119 |
120 | 121 | 122 |
123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 |
# File lib/g_verb.rb, line 44
131 | def initialize(damping)
132 |   @damping = damping
133 |   @delay = 0.0
134 | end
135 |
136 | 137 |
138 | 139 | 140 | 141 | 142 |
143 | 144 | 145 |
146 | 147 |
148 |
149 |

Public Instance Methods

150 |
151 | 152 | 153 |
154 | 155 |
156 | run(x) 158 | 159 | click to toggle source 160 | 161 |
162 | 163 | 164 |
165 | 166 | 167 | 168 | 169 | 170 | 171 |
172 |
# File lib/g_verb.rb, line 49
173 | def run(x)
174 |   y = x * (1.0-@damping) + @delay * @damping;
175 |   @delay = y
176 |   y
177 | end
178 |
179 | 180 |
181 | 182 | 183 | 184 | 185 |
186 | 187 | 188 |
189 | 190 |
191 | 192 |
193 | 194 | 195 | 200 | 201 | -------------------------------------------------------------------------------- /drawings/eq.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 35 | 36 | 44 | 50 | 51 | 52 | 73 | 78 | 83 | 88 | 93 | 94 | 96 | 97 | 99 | image/svg+xml 100 | 102 | 103 | 104 | 105 | 106 | 111 | 119 | 128 | 137 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /drawings/delay.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 34 | 35 | 43 | 48 | 49 | 50 | 70 | 75 | 80 | 85 | 90 | 91 | 93 | 94 | 96 | image/svg+xml 97 | 99 | 100 | 101 | 102 | 103 | 107 | 112 | 114 | 119 | 124 | 129 | 134 | 139 | 144 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /drawings/bandpass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 32 | 37 | 38 | 46 | 51 | 52 | 53 | 73 | 78 | 83 | 88 | 93 | 98 | 103 | 108 | 113 | 114 | 116 | 117 | 119 | image/svg+xml 120 | 122 | 123 | 124 | 125 | 126 | 130 | 138 | 147 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /website/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | RDoc Documentation 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 127 | 128 |
129 | 130 | 131 |

RubySynth

132 | 133 |
_______       __          ___t____             __   __
134 | |   _   .--.--|  |--.--.--|   _   .--.--.-----|  |_|  |--.
135 | |.  l   |  |  |  _  |  |  |   1___|  |  |     |   _|     |
136 | |.  _   |_____|_____|___  |____   |___  |__|__|____|__|__|
137 | |:  |   |           |_____|:  1   |_____|
138 | |::.|:. |                 |::.. . |
139 | `--- ---'                 `-------'
140 | 147 | 148 |

Eventually I'll try to turn the primitives into a proper gem and better tests, but that will depend on your interest and if I can give this talk a couple of more times.

149 | 150 |

Contributing or just playing around

151 | 158 | 159 |

Licenses

160 | 161 |

All code here is licensed under the AGPL 3.0 license as documented at LICENSE unless stated otherwise.

162 | 163 |

The entire contents of the “website” folder of the repo (and with that, this website you're just reading) is licensed under a Creative Commons Attribution-ShareAlike Unported license. The full license is at website/LICENSE

164 | 165 | 166 |
167 | 168 | 169 | 170 | 175 | 176 | -------------------------------------------------------------------------------- /drawings/notch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 32 | 37 | 38 | 46 | 51 | 52 | 53 | 73 | 78 | 83 | 88 | 93 | 98 | 103 | 108 | 113 | 114 | 116 | 117 | 119 | image/svg+xml 120 | 122 | 123 | 124 | 125 | 126 | 130 | 138 | 147 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /website/docs/OnePoleLP.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | class OnePoleLP - RDoc Documentation 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 84 | 85 |
86 |

87 | class OnePoleLP 88 |

89 | 90 |
91 | 92 |
93 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 |
107 |

Public Class Methods

108 |
109 | 110 | 111 |
112 | 113 |
114 | new() 116 | 117 | click to toggle source 118 | 119 |
120 | 121 | 122 |
123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 |
# File lib/chorus.rb, line 2
131 | def initialize
132 |   @outputs = 0.0
133 | end
134 |
135 | 136 |
137 | 138 | 139 | 140 | 141 |
142 | 143 | 144 |
145 | 146 |
147 |
148 |

Public Instance Methods

149 |
150 | 151 | 152 |
153 | 154 |
155 | run(input, cutoff) 157 | 158 | click to toggle source 159 | 160 |
161 | 162 | 163 |
164 | 165 | 166 | 167 | 168 | 169 | 170 |
171 |
# File lib/chorus.rb, line 5
172 | def run(input, cutoff)
173 |   p = (cutoff * 0.98) * (cutoff * 0.98) * (cutoff * 0.98) * (cutoff * 0.98);
174 |   @outputs = (1.0 - p) * input + p * @outputs
175 | end
176 |
177 | 178 |
179 | 180 | 181 | 182 | 183 |
184 | 185 | 186 |
187 | 188 |
189 | 190 |
191 | 192 | 193 | 198 | 199 | -------------------------------------------------------------------------------- /website/docs/TunedDrum.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | class TunedDrum - RDoc Documentation 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 82 | 83 |
84 |

85 | class TunedDrum 86 |

87 | 88 |
89 | 90 |

Special case of the kick drum that allows to run it from a note pattern to create percussive sounds

91 | 92 |
93 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 |
107 |

Public Instance Methods

108 |
109 | 110 | 111 |
112 | 113 |
114 | run(offset) 116 | 117 | click to toggle source 118 | 119 |
120 | 121 | 122 |
123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 |
# File lib/tuned_drum.rb, line 5
131 | def run(offset)
132 |   t = time(offset)
133 |   events = active_events(t)
134 |   if events.empty?
135 |     0.0
136 |   else
137 |     event = events[events.keys.last]
138 |     note = events.keys.last
139 |     base_freq = frequency(note)
140 |     local_started = t - event[:started]
141 |     osc_out = @oscillator.run(base_freq + @pitch_env.run(local_started) * @preset[:pitch_mod].to_f, waveform: :sine)
142 |     osc_out = osc_out * 1.0 * @amp_env.run(local_started)
143 |   end
144 | end
145 |
146 | 147 |
148 | 149 | 150 | 151 | 152 |
153 | 154 | 155 |
156 | 157 |
158 | 159 |
160 | 161 | 162 | 167 | 168 | -------------------------------------------------------------------------------- /website/docs/Utils.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | module Utils - RDoc Documentation 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 77 | 78 |
79 |

80 | module Utils 81 |

82 | 83 |
84 | 85 |

Some simple utils

86 | 87 |
88 | 89 | 90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
101 |
102 |

Public Instance Methods

103 |
104 | 105 | 106 |
107 | 108 |
109 | bitreduce(input, bits=8) 111 | 112 | click to toggle source 113 | 114 |
115 | 116 | 117 |
118 | 119 |

a simple bitreducer using rounding bits is the number of bits you want to reduce to

120 | 121 | 122 | 123 | 124 |
125 |
# File lib/utils.rb, line 5
126 | def bitreduce(input, bits=8)
127 |   (input * bits.to_f).round.to_f / bits.to_f
128 | end
129 |
130 | 131 |
132 | 133 | 134 | 135 | 136 |
137 | 138 | 139 |
140 | 141 |
142 | simple_waveshaper(input, a) 144 | 145 | click to toggle source 146 | 147 |
148 | 149 | 150 |
151 | 152 |

waveshaper, source www.musicdsp.org/en/latest/Effects/41-waveshaper.html a can go from 1 to … oo the higher a the stronger is the distortion

153 | 154 | 155 | 156 | 157 |
158 |
# File lib/utils.rb, line 12
159 | def simple_waveshaper(input, a)
160 |   input * (input.abs + a) / (input ** 2 + (a - 1) * input.abs + 1)
161 | end
162 |
163 | 164 |
165 | 166 | 167 | 168 | 169 |
170 | 171 | 172 |
173 | 174 |
175 | 176 |
177 | 178 | 179 | 184 | 185 | --------------------------------------------------------------------------------