├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── fire.sql ├── generated ├── music0.mp4 ├── music1.mp4 ├── music2.mp4 └── music3.mp4 ├── music0.sql ├── music1.sql ├── music2.sql ├── music2.sql.sh ├── music3.sql ├── music4.sql ├── music5.sql └── play.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-detectable 2 | * linguist-language=SQL 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016-2024 ClickHouse, Inc. 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright 2016-2024 ClickHouse, Inc. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NoiSQL — Generating Music With SQL Queries 2 | 3 | NoiSQL (named after [Robert Noyce](https://en.wikipedia.org/wiki/Robert_Noyce)) shows how to play sound and music with declarative SQL queries. 4 | 5 | It contains oscillators for basic waves, envelopes, sequencers, arpeggiators, effects (distortion, delay), noise generators, AM and FM, and LFO, ... 6 | Sometimes it can generate something nice, but usually not. 7 | 8 | # Table of Contents 9 | - [NoiSQL — Generating Music With SQL Queries](#noisql---generating-music-with-sql-queries) 10 | * [Quick Start - Linux](#quick-start---linux) 11 | * [Quick Start - MacOS](#quick-start---macos) 12 | * [Bleeps and Bloops - a demonstration](#bleeps-and-bloops---a-demonstration) 13 | * [Examples](#examples) 14 | - [How It Works](#how-it-works) 15 | * [Making Something Interesting](#making-something-interesting) 16 | + [Input](#input) 17 | + [Output Control](#output-control) 18 | + [Basic waves](#basic-waves) 19 | + [Helpers and LFO](#helpers-and-lfo) 20 | + [Noise](#noise) 21 | + [Distortion](#distortion) 22 | + [Envelopes](#envelopes) 23 | + [Sequencers](#sequencers) 24 | + [Delay](#delay) 25 | + [Components](#components) 26 | + [Combine It](#combine-it) 27 | + [Additional Commands](#additional-commands) 28 | * [Limitations](#limitations) 29 | * [Further Directions](#further-directions) 30 | - [Motivation](#motivation) 31 | - [Contributing](#contributing) 32 | * [Reading Corner](#reading-corner) 33 | 34 | ## Quick Start - Linux 35 | Clone the Repository. 36 | 37 | Download clickhouse-local: 38 | ``` 39 | curl https://clickhouse.com/ | sh 40 | ``` 41 | 42 | Install (Linux): 43 | ``` 44 | sudo ./clickhouse install 45 | ``` 46 | 47 | Check the installation: 48 | ``` 49 | clickhouse-local --version 50 | ``` 51 | 52 | ## Quick Start - MacOS 53 | Before beginning, please ensure that you have [homebrew](https://brew.sh/) installed. 54 | 55 | Clone the Repository. 56 | 57 | Change to the Repository folder (i.e. `cd NoiSQL`) 58 | 59 | Install ClickHouse (macOS): 60 | ``` 61 | mkdir -p bin 62 | curl https://clickhouse.com/ | sh 63 | mv clickhouse bin/clickhouse-local 64 | export PATH="$(pwd)/bin:$PATH" 65 | ``` 66 | 67 | *NOTE: If you want this to live past a terminal restart add to your profile. That may look something like the below or `.bash_profile` or `.zshrc` depending on your terminal of choice.* 68 | 69 | ``` 70 | echo 'export PATH="'$(pwd)'/bin:$PATH"' >> .profile 71 | ``` 72 | 73 | In order to playback audio from the terminal (via STDIN) we use an open source project (with a convenient brew recipe) called 'sox' (note that Xcode developer tools is a dependency, and if you don't have it installed, you will need to install it first via `xcode-select --install`) 74 | 75 | ``` 76 | brew install sox 77 | ``` 78 | 79 | ## Bleeps and Bloops - a demonstration 80 | Now, ClickHouse Local is setup and it is time to make our first noises. 81 | 82 | Demo (Linux): 83 | ``` 84 | ./music2.sql.sh | aplay -f cd 85 | ``` 86 | 87 | Demo (macOS): 88 | ``` 89 | ./music2.sql.sh | play -t raw -b 16 -e signed -c 2 -r 44100 -v .75 - 90 | ``` 91 | 92 | Live editing (Linux): 93 | ``` 94 | sudo apt install inotifytools 95 | ./play.sh music0.sql 96 | ``` 97 | 98 | You can edit the `music0.sql` file, and the changes will automatically apply while playing. On macOS this is not possible due to the lack of inotifytools BUT the play script can be used to play any of the samples music*.sql files provided 99 | 100 | ## Examples 101 | If, you are unable to use it yourself. You can still hear the output as .mp4 here. 102 | 103 | https://user-images.githubusercontent.com/18581488/229301700-d71d5a9c-ad7e-492b-80ba-d49114fd0bfe.mp4 104 | 105 | https://user-images.githubusercontent.com/18581488/229301704-d82d89b7-7650-44eb-a525-0630888d2080.mp4 106 | 107 | https://user-images.githubusercontent.com/18581488/229301706-8e14b0c1-01a9-47a5-84dd-8a069832e63f.mp4 108 | 109 | https://user-images.githubusercontent.com/18581488/229301709-9a102865-be02-4072-8707-48a6a571c500.mp4 110 | 111 | -------------------------- 112 | # How It Works 113 | 114 | An SQL query selects from the `system.numbers` table, containing a sequence of natural numbers, and transforms this sequence into a PCM audio signal in the CD format. This output signal is piped to the `aplay -f cd` tool (on Linux) to play it. 115 | 116 | The CD (Compact Disc Audio) format is a 44100 Hz 16-bit stereo PCM signal. 117 | - **44100 Hz** is the "sampling frequency", meaning that the signal has a value 44 100 times every second; 118 | - **16 bit** is the precision of every value, and the value is represented by `Int16` (signed, two's complement, little endian) integer; 119 | - **stereo** means that we have two values at every sample - for the left channel and for the right channel; 120 | 121 | The signal is represented in binary, corresponding to the ClickHouse's `RowBinary` format. Therefore, the bit rate of the signal is 16 * 2 * 44100 = 1411 kBit. 122 | 123 | Although we could also use the classic 8-bit mono 9 kHz format, the CD format gives more possibilities. 124 | 125 | To get the idea, run this: 126 | ``` 127 | clickhouse-local --query " 128 | SELECT 129 | (number % 44100)::Int16 AS left, 130 | ((number + 22050) % 44100)::Int16 AS right 131 | FROM system.numbers 132 | FORMAT RowBinary" | aplay -f cd 133 | ``` 134 | 135 | It will give you an uninteresting clicky sound. 136 | 137 | ``` 138 | clickhouse-local --query " 139 | WITH number * 2 * pi() / 44100 AS time 140 | SELECT 141 | (sin(time * 200) * 0x7FFF)::Int16 AS left, 142 | (sin(time * 400) * 0x7FFF)::Int16 AS right 143 | FROM system.numbers 144 | FORMAT RowBinary" | aplay -f cd 145 | ``` 146 | 147 | It will give you uninteresting waves. 148 | 149 | ## Making Something Interesting 150 | 151 | Here is a query from [music0.sql](music0.sql), that generates something at least listenable. Let's walk through this SQL query. 152 | 153 | The WITH clause in the SQL query allows defining expressions for further use. 154 | We can define both constants and functions (in form of lambda expressions). 155 | We use the `allow_experimental_analyzer` setting to make this query possible. 156 | 157 | ### Input 158 | 159 | Let's define the `time` column to be a floating point value representing the number of seconds. 160 | 161 | ``` 162 | WITH 163 | 164 | 44100 AS sample_frequency 165 | , number AS tick 166 | , tick / sample_frequency AS time 167 | ``` 168 | 169 | ### Output Control 170 | 171 | Let us work with the signal in the floating point format, with values in the `[-1, 1]` range. 172 | Here are the functions to convert it to the output PCM CD signal. 173 | 174 | A knob for the volume: 175 | ``` 176 | , 1 AS master_volume 177 | ``` 178 | 179 | If the signal exceeds the boundaries, it will be clipped: 180 | ``` 181 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 182 | ``` 183 | 184 | Floating point values are converted to Int16 for the output: 185 | ``` 186 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 187 | ``` 188 | 189 | If we don't care about stereo - we can simply output a tuple (pair) of identical values: 190 | ``` 191 | , x -> (x, x) AS mono 192 | ``` 193 | 194 | ### Basic waves 195 | 196 | We define oscillators as functions of time, with the period adjusted to be one second. 197 | You can modify the frequency simply by multiplying the time argument. 198 | For example, `sine_wave(time * 400)` - a sine wave of 400 Hz frequency. 199 | 200 | The sine wave gives the cleanest and most boring sound. 201 | ``` 202 | , time -> sin(time * 2 * pi()) AS sine_wave 203 | ``` 204 | 205 | The [square wave](https://en.wikipedia.org/wiki/Square_wave) gives a very harsh sound; it can be imagined as a maximally-distorted sine wave. 206 | ``` 207 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 208 | ``` 209 | 210 | Sawtooth wave 211 | ``` 212 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 213 | ``` 214 | 215 | Triangle wave 216 | ``` 217 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 218 | ``` 219 | 220 | ### Helpers and LFO 221 | 222 | LFO means "Low-Frequency Oscillation," and it is used to control a parameter of one signal with another low-frequency signal. 223 | It can have multiple interesting effects. 224 | 225 | For example, AM - amplitude modulation is modulating the volume of one signal with another signal, and it will give a trembling effect, making your sine waves sound more natural. 226 | 227 | Another example, FM - frequency modulation, is making the frequency of one signal change with another frequency. 228 | See [the explanation](https://www.youtube.com/watch?v=vvBl3YUBUyY). 229 | 230 | We take the wave and map it to the `[from, to]` interval: 231 | ``` 232 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 233 | ``` 234 | 235 | Here is a discrete version of an LFO. It allows changing the signal as a step function: 236 | ``` 237 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 238 | ``` 239 | 240 | For some unknown reason, the frequencies of musical notes ("musical scale") are exponentially distributed, so sometimes we have to apply computations in the [logarithmic coordinates](https://en.wikipedia.org/wiki/Curvilinear_coordinates) to make a more pleasant sound: 241 | ``` 242 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 243 | ``` 244 | 245 | ### Noise 246 | 247 | Generating noise is easy. We just need random numbers. 248 | 249 | But we want the noise to be deterministic (determined by the time) for further processing. 250 | That's why we use `cityHash64` instead of a random and `erf` instead of `randNormal`. 251 | 252 | All the following are variants of white noise. Although we can also generate [brown noise](https://en.wikipedia.org/wiki/Brownian_noise) with the help of `runningAccumulate`. 253 | 254 | ``` 255 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 256 | , time -> erf(uniform_noise(time)) AS white_noise 257 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 258 | ``` 259 | 260 | ### Distortion 261 | 262 | Distortion alters the signal in various ways to make it sound less boring. 263 | 264 | The harshest distortion - clipping - amplifies the signal, then clips what's above the range. 265 | It adds higher harmonics and makes sound more metallic, and makes sine waves more square. 266 | ``` 267 | , (x, amount) -> clamp(x * amount) AS clipping 268 | ``` 269 | 270 | For a milder version, it makes sense to apply the `pow` function, such as square root to the `[-1, 1]` signal: 271 | ``` 272 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 273 | ``` 274 | 275 | We can reduce the number of bits in the values of the signal, making it more coarse. 276 | It adds some sort of noise, making it sound worn out. 277 | ``` 278 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 279 | ``` 280 | 281 | Lowering the sample rate makes the signal dirty. Try to apply it for white noise. 282 | ``` 283 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 284 | ``` 285 | 286 | Something like compressing the periodic components in time and adding empty space - no idea why I need it: 287 | ``` 288 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 289 | ``` 290 | 291 | Skewing the waves in time making sine ways more similar to sawtooth waves: 292 | ``` 293 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 294 | ``` 295 | 296 | ### Envelopes 297 | 298 | The envelope is a way to make the signal sound like a note of a keyboard musical instrument, such as a piano. 299 | 300 | It modulates the volume of the signal by: 301 | - attack - time for the sound to appear; 302 | - hold - time for the sound to play at maximum volume; 303 | - release - time for the sound to decay to zero; 304 | 305 | This is a simplification of what typical envelopes are, but it's good enough. 306 | ``` 307 | , (time, offset, attack, hold, release) -> 308 | time < offset ? 0 309 | : (time < offset + attack ? ((time - offset) / attack) 310 | : (time < offset + attack + hold ? 1 311 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 312 | : 0))) AS envelope 313 | ``` 314 | 315 | We can make the musical note sound periodically to define a rhythm. 316 | For convenience, we define "bpm" as "beats per minute" and make it sound once in every beat. 317 | ``` 318 | , (bpm, time, offset, attack, hold, release) -> 319 | envelope( 320 | time * (bpm / 60) - floor(time * (bpm / 60)), 321 | offset, 322 | attack, 323 | hold, 324 | release) AS running_envelope 325 | ``` 326 | 327 | ### Sequencers 328 | 329 | To create a melody, we need a sequence of notes. A sequencer generates it. 330 | 331 | In the first example, we simply take it from an array and make it repeat indefinitely: 332 | ``` 333 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 334 | ``` 335 | 336 | But the obvious way to generate a melody is to take a [Sierpinski triangle](https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle). 337 | 338 | Sierpinski triangles sound delicious: 339 | ``` 340 | , time -> bitAnd(time::UInt8, time::UInt8 * 8) AS sierpinski 341 | ``` 342 | 343 | Another way is to map the number of bits in a number to a musical note: 344 | ``` 345 | , time -> bitCount(time::UInt64) AS bit_count 346 | ``` 347 | 348 | Calculating the number of trailing zero bits gives us a nice arpeggiator: 349 | ``` 350 | , time -> log2(time::UInt64 > 0 ? bitXor(time::UInt64, time::UInt64 - 1) : 1) AS trailing_zero_bits 351 | ``` 352 | 353 | ### Delay 354 | 355 | If you ever wanted to generate dub music, you cannot go without a delay effect: 356 | ``` 357 | , (time, wave, delay, decay, count) -> arraySum(n -> wave(time - delay * n) * pow(decay, n), range(count)) AS delay 358 | ``` 359 | 360 | ### Components 361 | 362 | Here is a kick: 363 | ``` 364 | sine_wave(time * 50) * running_envelope(120, time, 0, 0.01, 0.01, 0.025) AS kick, 365 | ``` 366 | 367 | Here is a snare: 368 | ``` 369 | white_noise(time) * running_envelope(120, time, 0.5, 0.01, 0.01, 0.05) AS snare, 370 | ``` 371 | 372 | Let's also define five melodies. 373 | 374 | What is the idea? Let's take a [Sierpinski triangle](https://www.youtube.com/watch?v=IZHiBJGcrqI), put it into a sine wave, add FM to make it even fancier, and apply a few LFOs over the place. 375 | 376 | ``` 377 | sine_wave( 378 | time * (100 * exp2(trailing_zero_bits(time * 8) % 12 / 6)) 379 | + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) 380 | * running_envelope(480, time, 0, 0.01, 0.1, 0.5) 381 | * lfo(0, 1, sine_wave, time / 12 382 | ) AS melody1, 383 | 384 | sine_wave(time * (200 * exp2(bit_count(time * 8) % 12 / 6)) 385 | + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) 386 | * running_envelope(480, time, 0, 0.11, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 24 387 | ) AS melody2, 388 | 389 | sine_wave(time * (400 * exp2(sierpinski(time * 8) % 12 / 6)) 390 | + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) 391 | * running_envelope(480, time, 0, 0.21, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 32 392 | ) AS melody3, 393 | 394 | sine_wave(time * (800 / exp2(trailing_zero_bits(time * 8) % 12 / 6)) 395 | + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) 396 | * running_envelope(480, time, 0, 0.31, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 16 397 | ) AS melody4 398 | ``` 399 | 400 | ### Combine It 401 | 402 | So, what will happen if we mix together some garbage and listen to it? 403 | ``` 404 | SELECT 405 | 406 | mono(output( 407 | 1.0 * melody1 408 | + 0.5 * melody2 409 | + 0.25 * melody3 410 | + 1.0 * melody4 411 | + 1 * kick 412 | + 0.025 * snare)) 413 | 414 | FROM table; 415 | ``` 416 | 417 | ### Additional Commands 418 | 419 | Generate five minutes of audio and write to a `.pcm` file: 420 | ``` 421 | clickhouse-local --format RowBinary --query "SELECT * FROM system.numbers LIMIT 44100 * 5 * 60" \ 422 | | clickhouse-local --allow_experimental_analyzer 1 --format RowBinary --structure "number UInt64" --queries-file music0.sql \ 423 | > music0.pcm 424 | ``` 425 | 426 | Convert `pcm` to `wav`: 427 | ``` 428 | ffmpeg -f s16le -ar 44.1k -ac 2 -i music0.pcm music0.wav 429 | ``` 430 | 431 | Convert `pcm` to `mp4`: 432 | ``` 433 | ffmpeg -f s16le -ar 44.1k -ac 2 -i music0.pcm music0.mp4 434 | ``` 435 | 436 | ## Limitations 437 | 438 | I haven't, yet, found a good way to implement filters (low-pass, high-pass, band-pass, etc.). It does not have Fourier transform, and we cannot operate on the frequency domain. However, the moving average can suffice as a simple filter. 439 | 440 | ## Further Directions 441 | 442 | You can use ClickHouse as a sampler - storing the prepared musical samples in the table and arranging them with SELECT queries. For example, the [Mod Archive](https://modarchive.org/) can help. 443 | 444 | You can use ClickHouse as a vocoder. Just provide the microphone input signal instead of the `system.numbers` as a table to `clickhouse-local`. 445 | 446 | You can make the queries parameterized, replacing all the hundreds of constants with parameters. Then attach a device with hundreds of knobs and faders to your PC and provide their values of them as a streaming input table. Then you can control your sound like a pro. 447 | 448 | Real-time video generation can be added as well. 449 | 450 | # Motivation 451 | 452 | This is a fun project and neither a good nor convenient solution to a problem. Better solutions exist. 453 | 454 | There is not much sense in this project, although it can facilitate testing ClickHouse. 455 | 456 | You could argue that modern AI, for example, [Riffusion](https://www.riffusion.com/), can do a better job. The counterargument is - if you enjoy what you are doing, it's better not to care if someone does it better but with less pleasure. 457 | 458 | # Contributing 459 | 460 | If you want to share new interesting examples, please make a pull request, adding them directly to this repository! 461 | 462 | 463 | ## Reading Corner 464 | 465 | - [Sound](https://ciechanow.ski/sound/) by Bartosz Ciechanowski; 466 | - [Bytebeat](https://www.youtube.com/watch?v=tCRPUv8V22o); 467 | - [Color of Noise](https://en.wikipedia.org/wiki/Colors_of_noise); 468 | - [Glitchmachine](https://www.youtube.com/watch?v=adoF2Lc70J8); 469 | - [Audio Synthesis](https://www.youtube.com/watch?v=F1RsE4J9k9w); 470 | - [44,100 Hz](https://en.wikipedia.org/wiki/44,100_Hz); 471 | -------------------------------------------------------------------------------- /fire.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | , (a, b, t) -> (1 - t) * a + t * b AS linear_transition 26 | , (a, b, t) -> linear_transition(a, b, (1 - sin((t + 1/2) * pi())) / 2) AS smooth_transition 27 | 28 | -- Noise 29 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 30 | , time -> erf(uniform_noise(time)) AS white_noise 31 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 32 | 33 | , (time, frequency) -> cityHash64(floor(time * frequency)) / 0xFFFFFFFFFFFFFFFF 34 | AS step_noise 35 | 36 | , (time, frequency) -> linear_transition( 37 | cityHash64(floor(time * frequency)), 38 | cityHash64(1 + floor(time * frequency)), 39 | (time * frequency - floor(time * frequency))) / 0xFFFFFFFFFFFFFFFF 40 | AS linear_noise 41 | 42 | , (time, frequency) -> 43 | smooth_transition( 44 | cityHash64(floor(time * frequency)), 45 | cityHash64(1 + floor(time * frequency)), 46 | (time * frequency - floor(time * frequency))) / 0xFFFFFFFFFFFFFFFF 47 | AS sine_noise 48 | 49 | -- Distortion 50 | , (x, amount) -> clamp(x * amount) AS clipping 51 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 52 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 53 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 54 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 55 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 56 | 57 | -- Combining 58 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 59 | 60 | -- Envelopes 61 | , (time, offset, attack, hold, release) -> 62 | time < offset ? 0 63 | : (time < offset + attack ? ((time - offset) / attack) 64 | : (time < offset + attack + hold ? 1 65 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 66 | : 0))) AS envelope 67 | 68 | , (bpm, time, offset, attack, hold, release) -> 69 | envelope( 70 | time * (bpm / 60) - floor(time * (bpm / 60)), 71 | offset, 72 | attack, 73 | hold, 74 | release) AS running_envelope 75 | 76 | , (bpm, time, offset, swing, attack, hold, release) -> 77 | envelope( 78 | time * (bpm / 60) - floor(time * (bpm / 60)), 79 | (time * (bpm / 60))::UInt64 % 2 ? offset - swing : offset + swing, 80 | attack, 81 | hold, 82 | release) AS running_envelope_swing 83 | 84 | -- Sequencers 85 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 86 | , time -> bitAnd(time::UInt8, time::UInt8 * 8) AS sierpinski 87 | , time -> bitCount(time::UInt64) AS bit_count 88 | , time -> log2(time::UInt64 > 0 ? bitXor(time::UInt64, time::UInt64 - 1) : 1) AS trailing_zero_bits 89 | 90 | -- Delay 91 | , (time, wave, del, decay, count) -> arraySum(n -> wave(time - del * n) * pow(decay, n), range(count)) AS delay 92 | 93 | SELECT 94 | 95 | mono(output(delay(time, (time -> power_distortion(sine_noise(time, 400), 100)), 0.25, 0.25, 2))) 96 | 97 | FROM table; 98 | -------------------------------------------------------------------------------- /generated/music0.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClickHouse/NoiSQL/48670b2106dbc75ea53b42e6d3f1d105abfb5763/generated/music0.mp4 -------------------------------------------------------------------------------- /generated/music1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClickHouse/NoiSQL/48670b2106dbc75ea53b42e6d3f1d105abfb5763/generated/music1.mp4 -------------------------------------------------------------------------------- /generated/music2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClickHouse/NoiSQL/48670b2106dbc75ea53b42e6d3f1d105abfb5763/generated/music2.mp4 -------------------------------------------------------------------------------- /generated/music3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClickHouse/NoiSQL/48670b2106dbc75ea53b42e6d3f1d105abfb5763/generated/music3.mp4 -------------------------------------------------------------------------------- /music0.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | -- Noise 26 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 27 | , time -> erf(uniform_noise(time)) AS white_noise 28 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 29 | 30 | -- Distortion 31 | , (x, amount) -> clamp(x * amount) AS clipping 32 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 33 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 34 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 35 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 36 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 37 | 38 | -- Envelopes 39 | , (time, offset, attack, hold, release) -> 40 | time < offset ? 0 41 | : (time < offset + attack ? ((time - offset) / attack) 42 | : (time < offset + attack + hold ? 1 43 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 44 | : 0))) AS envelope 45 | 46 | , (bpm, time, offset, attack, hold, release) -> 47 | envelope( 48 | time * (bpm / 60) - floor(time * (bpm / 60)), 49 | offset, 50 | attack, 51 | hold, 52 | release) AS running_envelope 53 | 54 | -- Sequencers 55 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 56 | , time -> bitAnd(time::UInt8, time::UInt8 * 8) AS sierpinski 57 | , time -> bitCount(time::UInt64) AS bit_count 58 | , time -> log2(time::UInt64 > 0 ? bitXor(time::UInt64, time::UInt64 - 1) : 1) AS trailing_zero_bits 59 | 60 | -- Delay 61 | , (time, wave, delay, decay, count) -> arraySum(n -> wave(time - delay * n) * pow(decay, n), range(count)) AS delay 62 | 63 | 64 | SELECT 65 | 66 | mono(output( 67 | 1 * 1. * sine_wave(time * (100 * exp2(trailing_zero_bits(time * 8) % 12 / 6)) + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) * running_envelope(480, time, 0, 0.01, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 12) 68 | + 1 * 0.5 * sine_wave(time * (200 * exp2(bit_count(time * 8) % 12 / 6)) + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) * running_envelope(480, time, 0, 0.11, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 24) 69 | + 1 * 0.25 * sine_wave(time * (400 * exp2(sierpinski(time * 8) % 12 / 6)) + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) * running_envelope(480, time, 0, 0.21, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 32) 70 | + 1 * sine_wave(time * (800 / exp2(trailing_zero_bits(time * 8) % 12 / 6)) + sine_wave(time * 3 + 1/4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) * running_envelope(480, time, 0, 0.31, 0.1, 0.5) * lfo(0, 1, sine_wave, time / 16) 71 | 72 | + 1 * sine_wave(time * 50) * running_envelope(120, time, 0, 0.01, 0.01, 0.025) 73 | + 0.025 * white_noise(time) * running_envelope(120, time, 0.5, 0.01, 0.01, 0.05) 74 | )) 75 | 76 | FROM table; 77 | -------------------------------------------------------------------------------- /music1.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | -- Noise 26 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 27 | , time -> erf(uniform_noise(time)) AS white_noise 28 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 29 | 30 | -- Distortion 31 | , (x, amount) -> clamp(x * amount) AS clipping 32 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 33 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 34 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 35 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 36 | 37 | -- Combining 38 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 39 | 40 | -- Envelopes 41 | , (time, offset, attack, hold, release) -> 42 | time < offset ? 0 43 | : (time < offset + attack ? ((time - offset) / attack) 44 | : (time < offset + attack + hold ? 1 45 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 46 | : 0))) AS envelope 47 | 48 | , (bpm, time, offset, attack, hold, release) -> 49 | envelope( 50 | time * (bpm / 60) - floor(time * (bpm / 60)), 51 | offset, 52 | attack, 53 | hold, 54 | release) AS running_envelope 55 | 56 | -- Sequencers 57 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 58 | 59 | SELECT 60 | 61 | mono(output( 62 | arraySum(x -> 1 / (x + 1) * running_envelope(120 / (x + 1), time + 100, 0.01 * x, 0.005, 0.1, 0.1) * sine_wave(time * 50 * exp2(x / 12)), range(72)) 63 | )) 64 | 65 | FROM table; 66 | -------------------------------------------------------------------------------- /music2.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | -- Noise 26 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 27 | , time -> erf(uniform_noise(time)) AS white_noise 28 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 29 | 30 | -- Distortion 31 | , (x, amount) -> clamp(x * amount) AS clipping 32 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 33 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 34 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 35 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 36 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 37 | 38 | -- Combining 39 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 40 | 41 | -- Envelopes 42 | , (time, offset, attack, hold, release) -> 43 | time < offset ? 0 44 | : (time < offset + attack ? ((time - offset) / attack) 45 | : (time < offset + attack + hold ? 1 46 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 47 | : 0))) AS envelope 48 | 49 | , (bpm, time, offset, attack, hold, release) -> 50 | envelope( 51 | time * (bpm / 60) - floor(time * (bpm / 60)), 52 | offset, 53 | attack, 54 | hold, 55 | release) AS running_envelope 56 | 57 | -- Sequencers 58 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 59 | 60 | SELECT 61 | 62 | mono(output( 63 | arraySum(x -> 1 / 6 64 | * running_envelope(30 * (1 + x / 6), time, 0.05 * x, 0.005, lfo(0, 0.25, sine_wave, time / 8), 0.1) 65 | * sine_wave(time * 80 * exp2(x / 3)), 66 | range(12)))) 67 | 68 | FROM table; 69 | -------------------------------------------------------------------------------- /music2.sql.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S clickhouse-local --max_block_size 441000 --allow_experimental_analyzer 1 --format RowBinary --queries-file 2 | # Usage: ./music2.sql.sh | aplay -f cd 3 | 4 | WITH 5 | 6 | -- Input 7 | 44100 AS sample_frequency 8 | , number AS tick 9 | , tick / sample_frequency AS time 10 | 11 | -- Output control 12 | , 1 AS master_volume 13 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 14 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 15 | , x -> (x, x) AS mono 16 | 17 | -- Basic waves 18 | , time -> sin(time * 2 * pi()) AS sine_wave 19 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 20 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 21 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 22 | 23 | -- Helpers 24 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 25 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 26 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 27 | 28 | -- Noise 29 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 30 | , time -> erf(uniform_noise(time)) AS white_noise 31 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 32 | 33 | -- Distortion 34 | , (x, amount) -> clamp(x * amount) AS clipping 35 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 36 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 37 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 38 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 39 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 40 | 41 | -- Combining 42 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 43 | 44 | -- Envelopes 45 | , (time, offset, attack, hold, release) -> 46 | time < offset ? 0 47 | : (time < offset + attack ? ((time - offset) / attack) 48 | : (time < offset + attack + hold ? 1 49 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 50 | : 0))) AS envelope 51 | 52 | , (bpm, time, offset, attack, hold, release) -> 53 | envelope( 54 | time * (bpm / 60) - floor(time * (bpm / 60)), 55 | offset, 56 | attack, 57 | hold, 58 | release) AS running_envelope 59 | 60 | -- Sequencers 61 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 62 | 63 | SELECT 64 | 65 | mono(output( 66 | arraySum(x -> 1 / 6 67 | * running_envelope(30 * (1 + x / 6), time, 0.05 * x, 0.005, lfo(0, 0.25, sine_wave, time / 8), 0.1) 68 | * sine_wave(time * 80 * exp2(x / 3)), 69 | range(12)))) 70 | 71 | FROM system.numbers 72 | SETTINGS max_threads = 1; 73 | -------------------------------------------------------------------------------- /music3.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | -- Noise 26 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 27 | , time -> erf(uniform_noise(time)) AS white_noise 28 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 29 | 30 | -- Distortion 31 | , (x, amount) -> clamp(x * amount) AS clipping 32 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 33 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 34 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 35 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 36 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 37 | 38 | -- Combining 39 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 40 | 41 | -- Envelopes 42 | , (time, offset, attack, hold, release) -> 43 | time < offset ? 0 44 | : (time < offset + attack ? ((time - offset) / attack) 45 | : (time < offset + attack + hold ? 1 46 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 47 | : 0))) AS envelope 48 | 49 | , (bpm, time, offset, attack, hold, release) -> 50 | envelope( 51 | time * (bpm / 60) - floor(time * (bpm / 60)), 52 | offset, 53 | attack, 54 | hold, 55 | release) AS running_envelope 56 | 57 | -- Sequencers 58 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 59 | 60 | -- Delay 61 | , (time, wave, delay, decay, count) -> arraySum(n -> wave(time - delay * n) * pow(decay, n), range(count)) AS delay 62 | 63 | 64 | SELECT 65 | 66 | (output( 67 | delay(time, (time -> power_distortion(sine_wave(time * 80 + sine_wave(time * 2)), lfo(0.5, 1, sine_wave, time / 16)) 68 | * running_envelope(60, time, 0, 0.0, 0.01, 0.1)), 69 | 0.2, 0.5, 5) 70 | 71 | + (time < 16 ? 0 : 0.2 * delay(time, (time -> 72 | power_distortion( 73 | sine_wave(time * 50 + 1 * sine_wave(time * sequencer([100, 200, 400, 600, 50], time) + 1/4)) 74 | * running_envelope(60, time, 0, 0.01, 0.01, 0.1), 75 | lfo(1, 0.75, triangle_wave, time / 8))), 76 | 0.2, 0.5, 10) 77 | * lfo(0.5, 1, triangle_wave, time / 7)) 78 | 79 | + (time < 24 ? 0 : 0.3 * delay(time, (time -> 80 | power_distortion( 81 | sine_wave(time * sequencer([50, 100, 200, 400], time / 2) + 1 * sine_wave(time * sequencer([50, 100, 200], time / 4) + 1/4)) 82 | * running_envelope(60, time, 0.5, 0.01, 0.01, 0.1), 83 | lfo(1, 0.75, triangle_wave, time / 8))), 84 | 0.2, 0.5, 10) 85 | * lfo(0.5, 1, triangle_wave, 16 + time / 11)) 86 | 87 | + 0.1 * delay(time, (time -> 88 | white_noise(time) * running_envelope(60, time, 0.75, 0.01, 0.01, 0.1)), 89 | 0.2, 0.5, 10) 90 | * lfo(0.5, 1, triangle_wave, 24 + time / 13) 91 | 92 | + (time < 8 ? 0 : sine_wave(time * sequencer([250, 450, 150, 350, 450], time * 4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 10) * running_envelope(240, time, 0, 0.01, 0.01, 0.9)) 93 | ), 94 | 95 | output( 96 | delay(time, (time -> power_distortion(sine_wave(time * 80 + sine_wave(time * 2)), lfo(0.5, 1, sine_wave, time / 16)) 97 | * running_envelope(60, time, 0, 0.0, 0.01, 0.1)), 98 | 0.2, 0.5, 5) 99 | 100 | + (time < 16 ? 0 : 0.2 * delay(time + 0.01, (time -> 101 | power_distortion( 102 | sine_wave(time * 50 + 1 * sine_wave(time * sequencer([50, 100, 200, 400, 600], time) + 1/4)) 103 | * running_envelope(60, time, 0, 0.01, 0.01, 0.1), 104 | lfo(1, 0.75, triangle_wave, time / 8))), 105 | 0.2, 0.5, 10) 106 | * lfo(0.5, 1, triangle_wave, time / 7)) 107 | 108 | + (time < 24 ? 0 : 0.3 * delay(time - 0.01, (time -> 109 | power_distortion( 110 | sine_wave(time * sequencer([50, 100, 200, 400], time / 2) + 1 * sine_wave(time * sequencer([50, 100, 200], time / 4) + 1/4)) 111 | * running_envelope(60, time, 0.5, 0.01, 0.01, 0.1), 112 | lfo(1, 0.75, triangle_wave, time / 8))), 113 | 0.2, 0.5, 10) 114 | * lfo(0.5, 1, triangle_wave, 16 + time / 11)) 115 | 116 | + 0.1 * delay(time + 0.005, (time -> 117 | white_noise(time) * running_envelope(60, time, 0.75, 0.01, 0.01, 0.1)), 118 | 0.2, 0.5, 10) 119 | * lfo(0.5, 1, triangle_wave, 24 + time / 13) 120 | 121 | + (time < 8 ? 0 : sine_wave(time * sequencer([200, 400, 100, 300], time * 4)) * 0.25 * lfo(0.5, 1, sine_wave, time * 11) * running_envelope(240, time, 0, 0.01, 0.01, 0.9)) 122 | )) 123 | 124 | FROM table; 125 | -------------------------------------------------------------------------------- /music4.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | , (a, b, t) -> (1 - t) * a + t * b AS linear_transition 26 | , (a, b, t) -> linear_transition(a, b, (1 - sin((t + 1/2) * pi())) / 2) AS smooth_transition 27 | 28 | -- Noise 29 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 30 | , time -> erf(uniform_noise(time)) AS white_noise 31 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 32 | 33 | , (time, frequency) -> cityHash64(floor(time * frequency)) / 0xFFFFFFFFFFFFFFFF 34 | AS step_noise 35 | 36 | , (time, frequency) -> linear_transition( 37 | cityHash64(floor(time * frequency)), 38 | cityHash64(1 + floor(time * frequency)), 39 | (time * frequency - floor(time * frequency))) / 0xFFFFFFFFFFFFFFFF 40 | AS linear_noise 41 | 42 | , (time, frequency) -> 43 | smooth_transition( 44 | cityHash64(floor(time * frequency)), 45 | cityHash64(1 + floor(time * frequency)), 46 | (time * frequency - floor(time * frequency))) / 0xFFFFFFFFFFFFFFFF 47 | AS sine_noise 48 | 49 | -- Distortion 50 | , (x, amount) -> clamp(x * amount) AS clipping 51 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 52 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 53 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 54 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 55 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 56 | 57 | -- Combining 58 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 59 | 60 | -- Envelopes 61 | , (time, offset, attack, hold, release) -> 62 | time < offset ? 0 63 | : (time < offset + attack ? ((time - offset) / attack) 64 | : (time < offset + attack + hold ? 1 65 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 66 | : 0))) AS envelope 67 | 68 | , (bpm, time, offset, attack, hold, release) -> 69 | envelope( 70 | time * (bpm / 60) - floor(time * (bpm / 60)), 71 | offset, 72 | attack, 73 | hold, 74 | release) AS running_envelope 75 | 76 | , (bpm, time, offset, swing, attack, hold, release) -> 77 | envelope( 78 | time * (bpm / 60) - floor(time * (bpm / 60)), 79 | (time * (bpm / 60))::UInt64 % 2 ? offset - swing : offset + swing, 80 | attack, 81 | hold, 82 | release) AS running_envelope_swing 83 | 84 | -- Sequencers 85 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 86 | , time -> bitAnd(time::UInt8, time::UInt8 * 8) AS sierpinski 87 | , time -> bitCount(time::UInt64) AS bit_count 88 | , time -> log2(time::UInt64 > 0 ? bitXor(time::UInt64, time::UInt64 - 1) : 1) AS trailing_zero_bits 89 | 90 | , (from, to, time, seed) -> from + cityHash64(seed, floor(time)) % (1 + to - from) AS timed_rand 91 | 92 | -- Notes and Octaves 93 | , n -> transform(n, ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'], (SELECT arrayMap(x -> 220 * exp2((x + 3) / 12), range(12))), 0) AS note 94 | , ns -> arrayMap(n -> note(n), splitByChar(' ', ns)) AS notes 95 | , (ns, octave) -> arrayMap(x -> x * exp2(octave - 4), notes(ns)) AS notes_in_octave 96 | 97 | -- Delay 98 | , (time, wave, del, decay, count) -> arraySum(n -> wave(time - del * n) * pow(decay, n), range(count)) AS delay 99 | 100 | -- Melodies 101 | , 'G G A B A G E G G A B A G E G G A B A G E G A A B C B A G' AS chatgpt_melody1 102 | , 'E E G A G E E G A G E E E G A G E E D D F# G F# E D D F# G F# E' AS chatgpt_melody2 103 | , 'C C E G G E C C E G G E C C E G G E G G F F G A G F F G A G' AS chatgpt_melody3 104 | 105 | SELECT 106 | 107 | mono(output( 108 | arraySum(x -> 0.25 * sine_wave(time * notes_in_octave('C D E F G A B', 4)[x + 1] 109 | + sine_wave(time * notes_in_octave('C D E F G A B', 2)[x + 1]) 110 | ) * running_envelope(120 / exp2(x), time, 0.1 * x, 0.01 / (x + 1), 0.01 / (x + 1), 0.25 / (x + 1)), range(10)) 111 | )) 112 | 113 | FROM table; 114 | -------------------------------------------------------------------------------- /music5.sql: -------------------------------------------------------------------------------- 1 | WITH 2 | 3 | -- Input 4 | 44100 AS sample_frequency 5 | , number AS tick 6 | , tick / sample_frequency AS time 7 | 8 | -- Output control 9 | , 1 AS master_volume 10 | , level -> least(1.0, greatest(-1.0, level)) AS clamp 11 | , level -> (clamp(level) * 0x7FFF * master_volume)::Int16 AS output 12 | , x -> (x, x) AS mono 13 | 14 | -- Basic waves 15 | , time -> sin(time * 2 * pi()) AS sine_wave 16 | , time -> time::UInt64 % 2 * 2 - 1 AS square_wave 17 | , time -> (time - floor(time)) * 2 - 1 AS sawtooth_wave 18 | , time -> abs(sawtooth_wave(time)) * 2 - 1 AS triangle_wave 19 | 20 | -- Helpers 21 | , (from, to, wave, time) -> from + ((wave(time) + 1) / 2) * (to - from) AS lfo 22 | , (from, to, steps, time) -> from + floor((time - floor(time)) * steps) / steps * (to - from) AS step_lfo 23 | , (from, to, steps, time) -> exp(step_lfo(log(from), log(to), steps, time)) AS exp_step_lfo 24 | 25 | , (a, b, t) -> (1 - t) * a + t * b AS linear_transition 26 | , (a, b, t) -> linear_transition(a, b, (1 - sin((t + 1/2) * pi())) / 2) AS smooth_transition 27 | 28 | -- Noise 29 | , time -> cityHash64(time) / 0xFFFFFFFFFFFFFFFF AS uniform_noise 30 | , time -> erf(uniform_noise(time)) AS white_noise 31 | , time -> cityHash64(time) % 2 ? 1 : -1 AS bernoulli_noise 32 | 33 | , (time, frequency) -> cityHash64(floor(time * frequency)) / 0xFFFFFFFFFFFFFFFF 34 | AS step_noise 35 | 36 | , (time, frequency) -> linear_transition( 37 | cityHash64(floor(time * frequency)), 38 | cityHash64(1 + floor(time * frequency)), 39 | (time * frequency - floor(time * frequency))) / 0xFFFFFFFFFFFFFFFF 40 | AS linear_noise 41 | 42 | , (time, frequency) -> 43 | smooth_transition( 44 | cityHash64(floor(time * frequency)), 45 | cityHash64(1 + floor(time * frequency)), 46 | (time * frequency - floor(time * frequency))) / 0xFFFFFFFFFFFFFFFF 47 | AS sine_noise 48 | 49 | -- Distortion 50 | , (x, amount) -> clamp(x * amount) AS clipping 51 | , (x, amount) -> clamp(x > 0 ? pow(x, amount) : -pow(-x, amount)) AS power_distortion 52 | , (x, amount) -> round(x * exp2(amount)) / exp2(amount) AS bitcrush 53 | , (time, sample_frequency) -> round(time * sample_frequency) / sample_frequency AS desample 54 | , (time, wave, amount) -> (time - floor(time) < (1 - amount)) ? wave(time * (1 - amount)) : 0 AS thin 55 | , (time, wave, amount) -> wave(floor(time) + pow(time - floor(time), amount)) AS skew 56 | 57 | -- Combining 58 | , (a, b, weight) -> a * (1 - weight) + b * weight AS combine 59 | 60 | -- Envelopes 61 | , (time, offset, attack, hold, release) -> 62 | time < offset ? 0 63 | : (time < offset + attack ? ((time - offset) / attack) 64 | : (time < offset + attack + hold ? 1 65 | : (time < offset + attack + hold + release ? (offset + attack + hold + release - time) / release 66 | : 0))) AS envelope 67 | 68 | , (bpm, time, offset, attack, hold, release) -> 69 | envelope( 70 | time * (bpm / 60) - floor(time * (bpm / 60)), 71 | offset, 72 | attack, 73 | hold, 74 | release) AS running_envelope 75 | 76 | , (bpm, time, offset, swing, attack, hold, release) -> 77 | envelope( 78 | time * (bpm / 60) - floor(time * (bpm / 60)), 79 | (time * (bpm / 60))::UInt64 % 2 ? offset - swing : offset + swing, 80 | attack, 81 | hold, 82 | release) AS running_envelope_swing 83 | 84 | -- Sequencers 85 | , (sequence, time) -> sequence[1 + time::UInt64 % length(sequence)] AS sequencer 86 | , time -> bitAnd(time::UInt8, time::UInt8 * 8) AS sierpinski 87 | , time -> bitCount(time::UInt64) AS bit_count 88 | , time -> log2(time::UInt64 > 0 ? bitXor(time::UInt64, time::UInt64 - 1) : 1) AS trailing_zero_bits 89 | 90 | , (from, to, time, seed) -> from + cityHash64(seed, floor(time)) % (1 + to - from) AS timed_rand 91 | 92 | -- Notes and Octaves 93 | , n -> transform(n, ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'], (SELECT arrayMap(x -> 220 * exp2((x + 3) / 12), range(12))), 0) AS note 94 | , ns -> arrayMap(n -> note(n), splitByChar(' ', ns)) AS notes 95 | , (ns, octave) -> arrayMap(x -> x * exp2(octave - 4), notes(ns)) AS notes_in_octave 96 | 97 | -- Delay 98 | , (time, wave, del, decay, count) -> arraySum(n -> wave(time - del * n) * pow(decay, n), range(count)) AS delay 99 | 100 | -- Melodies 101 | , 'G G A B A G E G G A B A G E G G A B A G E G A A B C B A G' AS chatgpt_melody1 102 | , 'E E G A G E E G A G E E E G A G E E D D F# G F# E D D F# G F# E' AS chatgpt_melody2 103 | , 'C C E G G E C C E G G E C C E G G E G G F F G A G F F G A G' AS chatgpt_melody3 104 | 105 | SELECT 106 | 107 | mono(output( 108 | 0.2 * sine_wave( 109 | time * sequencer(notes_in_octave(chatgpt_melody2, timed_rand(1, 7, time * 4 / 7, 1)), time * 4) 110 | + sine_wave(time * sequencer(notes_in_octave(chatgpt_melody2, timed_rand(1, 5, time * 4 / 7, 2)), time * 4))) 111 | * lfo(0.5, 1, sine_wave, time * 10) * running_envelope(240 * 1, time, 0, 0.01, 0.01, 0.9) 112 | 113 | + 0.9 * clipping(sine_wave(time * 80 + 1 * sine_wave(time * 120 / 60 + 1/2)) * running_envelope(120, time, 0, 0.01, 0.01, 0.1), 2) 114 | + 0.5 * sine_wave(time * 80) * running_envelope(60, time, 0.25, 0.01, 0.01, 0.1) 115 | + 0.5 * sine_wave(time * 80) * running_envelope(60, time, 0.75, 0.01, 0.01, 0.1) 116 | 117 | + 0.25 * white_noise(time) * running_envelope(240, time, 0, 0.01, 0.01, 0.05) 118 | + 0.125 * white_noise(time) * running_envelope(60, time, 0.05, 0.01, 0.01, 0.05) 119 | + 0.0625 * white_noise(time) * running_envelope(60, time, 0.1, 0.01, 0.01, 0.05) 120 | )) 121 | 122 | /*mono(output( 123 | delay(time, (time -> 124 | 0.25 * 125 | skew(time, (time -> 126 | sine_wave(time * sequencer([250, 450, 150, 350, 450], time * 4)) * lfo(0.5, 1, sine_wave, time * 10) * running_envelope(240, time, 0, 0.01, 0.01, 0.9)), 127 | 0.5)), 128 | 0.0975, 0.5, 2) 129 | ))*/ 130 | 131 | FROM table; 132 | -------------------------------------------------------------------------------- /play.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # sudo apt install inotifytools 4 | # 5 | # This script implements live reload of musical queries. 6 | # 7 | # Run it as 8 | # ./play.sh music0.sql 9 | # 10 | # It will start playing and watch for the changes in the file. 11 | # When the file is changed, it reloads and continues playing 12 | # from about the same moment (preserving the time counter). 13 | # 14 | # This enables live performance environment :) 15 | # You open an SQL query in your favorite text editor, 16 | # edit and save it, listening to the updates immediately. 17 | 18 | MUSIC="$1" 19 | 20 | OS=$(uname -s) 21 | 22 | if [ "${OS}" = "Linux" ] 23 | then 24 | PIPE=$(mktemp -u) 25 | mkfifo $PIPE 26 | exec 3<>$PIPE 27 | rm $PIPE 28 | 29 | clickhouse-local --format RowBinary --max_threads 1 --query "SELECT number FROM system.numbers" >&3 & 30 | 31 | while true 32 | do 33 | clickhouse-local --allow_experimental_analyzer 1 --format RowBinary --max_threads 1 --structure "number UInt64" --query "$(cat $MUSIC)" <&3 | aplay -f cd & 34 | PID=$! 35 | inotifywait -e modify $MUSIC 36 | echo "Music changed." >&2 37 | kill -9 $PID 38 | kill -9 $(pidof aplay) 2>/dev/null 39 | done 40 | else 41 | # Fallback to a simple option: 42 | 43 | clickhouse-local --format RowBinary --max_threads 1 --query "SELECT number FROM system.numbers" | \ 44 | clickhouse-local --allow_experimental_analyzer 1 --format RowBinary --max_threads 1 --structure "number UInt64" --query "$(cat $MUSIC)" | \ 45 | play -t raw -b 16 -e signed -c 2 -v .75 -r 44100 - 46 | fi 47 | --------------------------------------------------------------------------------