├── src ├── config.nims ├── paramidib.nim └── paramidib │ └── common.nim ├── .gitattributes ├── .gitignore ├── nim.cfg ├── output.wav ├── dueling_banjos.wav ├── README.md ├── paramidib.nimble ├── LICENSE ├── index.nim └── index.html /src/config.nims: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimblecache/ 3 | htmldocs/ 4 | -------------------------------------------------------------------------------- /nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"./src" 2 | -d:nimibPreviewCodeAsInSource -------------------------------------------------------------------------------- /output.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pietroppeter/paramidib/main/output.wav -------------------------------------------------------------------------------- /dueling_banjos.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pietroppeter/paramidib/main/dueling_banjos.wav -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎶🐳 paramidib 2 | [paramidi] with [nimib], see [pietroppeter.github.io/paramidib/](https://pietroppeter.github.io/paramidib/). Install with `nimble install paramidib`. 3 | 4 | [paramidi]: https://github.com/paranim/paramidi 5 | [nimib]: https://github.com/pietroppeter/nimib 6 | -------------------------------------------------------------------------------- /paramidib.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Pietro Peterlongo" 5 | description = "paramidi with nimib" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.6.0" 13 | requires "paramidi >= 0.6.0" 14 | requires "paramidi_soundfonts >= 0.2.0" 15 | requires "parasound >= 0.2.0" -------------------------------------------------------------------------------- /src/paramidib.nim: -------------------------------------------------------------------------------- 1 | # adapted from https://github.com/paranim/paramidi_starter/blob/master/src/paramidi_starter.nim 2 | from paramidib / common import nil 3 | import paramidi 4 | import paramidi/tsf 5 | import paramidi_soundfonts 6 | import os 7 | 8 | export paramidi 9 | 10 | template saveMusic*(wavFile: string, score: untyped) = 11 | let scoreObject = score 12 | # get the sound font, read it from disc 13 | var sf = tsf_load_filename(cstring paramidi_soundfonts.getSoundFontPath("generaluser.sf2")) 14 | const sampleRate = 44100 15 | tsf_set_output(sf, TSF_MONO, sampleRate, 0) 16 | var res = render[cshort](compile(scoreObject), sf, sampleRate) 17 | tsf_close(sf) 18 | # create the wav file (without playing it) 19 | common.writeFile(wavFile, res.data, res.data.len.uint32, sampleRate) 20 | 21 | template nbAudio*(wavFile: string) = 22 | # todo: validate/escape wavFile 23 | nbText: """ 27 | """ 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pietro Peterlongo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/paramidib/common.nim: -------------------------------------------------------------------------------- 1 | # taken as is from https://github.com/paranim/paramidi_starter/blob/master/src/common.nim 2 | import parasound/dr_wav 3 | import parasound/miniaudio 4 | import os 5 | 6 | proc play*(data: string | seq[uint8], sleepMsecs: int): bool = 7 | ## if `data` is a string, it is interpreted as a filename. 8 | ## if `data` is a byte sequence, it is interpreted as an in-memory buffer. 9 | var engine = newSeq[uint8](ma_engine_size()) 10 | if MA_SUCCESS != ma_engine_init(nil, engine[0].addr): 11 | return false 12 | when data is string: 13 | if MA_SUCCESS != ma_engine_play_sound(engine[0].addr, data, nil): 14 | ma_engine_uninit(engine[0].addr) 15 | return false 16 | os.sleep(sleepMsecs) 17 | ma_engine_uninit(engine[0].addr) 18 | elif data is seq[uint8]: 19 | var decoder = newSeq[uint8](ma_decoder_size()) 20 | if MA_SUCCESS != ma_decoder_init_memory(data[0].unsafeAddr, data.len.csize_t, nil, decoder[0].addr): 21 | ma_engine_uninit(engine[0].addr) 22 | return false 23 | var sound = newSeq[uint8](ma_sound_size()) 24 | if MA_SUCCESS != ma_sound_init_from_data_source(engine[0].addr, decoder[0].addr, 0, nil, sound[0].addr): 25 | discard ma_decoder_uninit(decoder[0].addr) 26 | ma_engine_uninit(engine[0].addr) 27 | return false 28 | if MA_SUCCESS != ma_sound_start(sound[0].addr): 29 | ma_sound_uninit(sound[0].addr) 30 | discard ma_decoder_uninit(decoder[0].addr) 31 | ma_engine_uninit(engine[0].addr) 32 | return false 33 | os.sleep(sleepMsecs) 34 | ma_sound_uninit(sound[0].addr) 35 | discard ma_decoder_uninit(decoder[0].addr) 36 | ma_engine_uninit(engine[0].addr) 37 | true 38 | 39 | proc writeFile*(filename: string, data: var openArray[cshort], numSamples: uint32, sampleRate: uint32) = 40 | var 41 | wav = newSeq[uint8](drwav_size()) 42 | format: drwav_data_format 43 | format.container = drwav_container_riff 44 | format.format = DR_WAVE_FORMAT_PCM 45 | format.channels = 1 46 | format.sampleRate = sampleRate 47 | format.bitsPerSample = 16 48 | doAssert drwav_init_file_write(wav[0].addr, filename, addr(format), nil) 49 | doAssert numSamples == drwav_write_pcm_frames(wav[0].addr, numSamples, data.addr) 50 | discard drwav_uninit(wav[0].addr) 51 | 52 | proc writeMemory*(data: var openArray[cshort], numSamples: uint32, sampleRate: uint32): seq[uint8] = 53 | var 54 | wav = newSeq[uint8](drwav_size()) 55 | format: drwav_data_format 56 | format.container = drwav_container_riff 57 | format.format = DR_WAVE_FORMAT_PCM 58 | format.channels = 1 59 | format.sampleRate = sampleRate 60 | format.bitsPerSample = 16 61 | var 62 | outputRaw: pointer 63 | outputSize: csize_t 64 | doAssert drwav_init_memory_write_sequential(wav[0].addr, outputRaw.addr, outputSize.addr, format.addr, numSamples, nil) 65 | doAssert numSamples == drwav_write_pcm_frames(wav[0].addr, numSamples, data[0].addr) 66 | doAssert outputSize > 0 67 | result = newSeq[uint8](outputSize) 68 | copyMem(result[0].addr, outputRaw, outputSize) 69 | drwav_free(outputRaw, nil) 70 | discard drwav_uninit(wav[0].addr) 71 | -------------------------------------------------------------------------------- /index.nim: -------------------------------------------------------------------------------- 1 | import nimib 2 | 3 | nbInit 4 | nbText: """# 🎶🐳 paramidib 5 | 6 | paramidi**b** is an easy way to make [paramidi] available with [nimib]. 7 | Install with `nimble install paramidib`. 8 | 9 | It exports `paramidi` and provides two templates: 10 | 1. `saveMusic`: takes a valid score for paramidi and creates 11 | a `wav` file with the generated music. 12 | It is adapted from [paramidi_starter] and it can be used even 13 | without nimib (paramidib itself does not depend on nimib). 14 | 2. `nbAudio`: a new nimib block which create an audio control 15 | to play the `wav` file. 16 | In the following example is called with `nbAudio("output.wav")` 17 | 18 | [paramidi]: https://github.com/paranim/paramidi 19 | [nimib]: https://github.com/pietroppeter/nimib 20 | [paramidi_starter]: https://github.com/paranim/paramidi_starter 21 | 22 | """ 23 | nbCode: 24 | import paramidib 25 | 26 | saveMusic("output.wav"): 27 | (piano, 28 | (tempo: 74), 29 | 1/8, {-d, -a, e, fx}, a, 30 | 1/2, {fx, +d}, 31 | 1/8, {-e, e, +c}, a, 32 | 1/2, {c, e}, 33 | 1/8, {-d, -a, e, fx}, a, +d, +cx, +e, +d, b, +cx, 34 | 1/2, {-e, c, a}, {c, e}) 35 | nbAudio("output.wav") 36 | nbText: "Another example where the score is a json object generated at runtime:" 37 | nbCode: 38 | import json 39 | 40 | let 41 | measure1 = %*[1/16, "b", "c+", 1/8, "d+", "b", "c+", "a", "b", "g", "a"] 42 | measure2 = %*[1/16, "g", "g", 1/8, "g", "a", "b", "c+", "d+", "c+", 1/2, "b"] 43 | measure3 = %*[1/16, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "d", "b-", "g-"], 44 | 1/8, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "e", "c", "g-"], 45 | [{"mode": "concurrent"}, "d", "b-", "g-"]] 46 | measure4 = %*[1/16, "r", "r", 1/8, "g", "r", "d", "r", "g", "g", "d"] 47 | measure5 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, "a", "d"] 48 | measure6 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, [{"mode": "concurrent"}, "f", "a-"], "b", 1/4, "c"] 49 | 50 | score* = 51 | %*[{"tempo": 80, "octave": 3}, 52 | ["guitar", measure1], 53 | ["banjo", measure1], 54 | ["guitar", measure1], 55 | ["guitar", 1/2, "d", 1/8, "g", "g", "a", "b", "g", "b", 1/2, "a"], 56 | ["banjo", 1/8, "g", "g", "a", "b", 1/2, "g"], 57 | ["guitar", {"octave": 2}, measure2], 58 | ["banjo", measure2], 59 | ["guitar", {"octave": 2}, measure2], 60 | ["banjo", measure2], 61 | ["guitar", measure2], 62 | ["banjo", measure2], 63 | {"octave": 4}, 64 | ["guitar", measure3], 65 | ["banjo", measure3], 66 | ["guitar", measure3], 67 | ["banjo", measure3], 68 | ["guitar", {"octave": 2}, measure1], 69 | ["banjo", {"octave": 3}, measure1], 70 | {"tempo": 120, "octave": 3}, 71 | [{"mode": "concurrent"}, 72 | ["banjo", measure1], 73 | ["guitar", measure4]], 74 | [{"mode": "concurrent"}, 75 | ["banjo", measure1], 76 | ["guitar", measure4]], 77 | [{"mode": "concurrent"}, 78 | ["banjo", measure1], 79 | ["guitar", measure5]], 80 | [{"mode": "concurrent"}, 81 | ["banjo", measure1], 82 | ["guitar", measure6]]] 83 | 84 | saveMusic("dueling_banjos.wav", score) 85 | nbAudio("dueling_banjos.wav") 86 | nbText: "Originally created for Advent Of Nim 🎄👑, 2021, Day 3, [Whale Music](https://pietroppeter.github.io/adventofnim/2021/day03.html#whale_music) 🐳🎶." 87 | nbSave -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | index.nim 5 | 6 | 7 | 8 | 9 | 10 | 11 | 30 | 31 | 32 | 33 |
34 |
35 | 🏡 36 | index.nim 37 | 38 |
39 |
40 |
41 |

🎶🐳 paramidib

42 |

paramidib is an easy way to make paramidi available with nimib. 43 | Install with nimble install paramidib.

44 |

It exports paramidi and provides two templates:

45 |
    46 |
  1. saveMusic: takes a valid score for paramidi and creates 47 | a wav file with the generated music. 48 | It is adapted from paramidi_starter and it can be used even 49 | without nimib (paramidib itself does not depend on nimib).
  2. 50 |
  3. nbAudio: a new nimib block which create an audio control 51 | to play the wav file. 52 | In the following example is called with nbAudio("output.wav")
  4. 53 |
54 |
import paramidib
 55 | 
 56 | saveMusic("output.wav"):
 57 |   (piano,
 58 |     (tempo: 74),
 59 |     1/8, {-d, -a, e, fx}, a,
 60 |     1/2, {fx, +d},
 61 |     1/8, {-e, e, +c}, a,
 62 |     1/2, {c, e},
 63 |     1/8, {-d, -a, e, fx}, a, +d, +cx, +e, +d, b, +cx,
 64 |     1/2, {-e, c, a}, {c, e})
65 | 69 |

Another example where the score is a json object generated at runtime:

70 |
import json
 71 | 
 72 | let
 73 |   measure1 = %*[1/16, "b", "c+", 1/8, "d+", "b", "c+", "a", "b", "g", "a"]
 74 |   measure2 = %*[1/16, "g", "g", 1/8, "g", "a", "b", "c+", "d+", "c+", 1/2, "b"]
 75 |   measure3 = %*[1/16, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "d", "b-", "g-"],
 76 |                 1/8, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "e", "c", "g-"],
 77 |                 [{"mode": "concurrent"}, "d", "b-", "g-"]]
 78 |   measure4 = %*[1/16, "r", "r", 1/8, "g", "r", "d", "r", "g", "g", "d"]
 79 |   measure5 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, "a", "d"]
 80 |   measure6 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, [{"mode": "concurrent"}, "f", "a-"], "b", 1/4, "c"]
 81 | 
 82 |   score* =
 83 |     %*[{"tempo": 80, "octave": 3},
 84 |       ["guitar", measure1],
 85 |       ["banjo", measure1],
 86 |       ["guitar", measure1],
 87 |       ["guitar", 1/2, "d", 1/8, "g", "g", "a", "b", "g", "b", 1/2, "a"],
 88 |       ["banjo", 1/8, "g", "g", "a", "b", 1/2, "g"],
 89 |       ["guitar", {"octave": 2}, measure2],
 90 |       ["banjo", measure2],
 91 |       ["guitar", {"octave": 2}, measure2],
 92 |       ["banjo", measure2],
 93 |       ["guitar", measure2],
 94 |       ["banjo", measure2],
 95 |       {"octave": 4},
 96 |       ["guitar", measure3],
 97 |       ["banjo", measure3],
 98 |       ["guitar", measure3],
 99 |       ["banjo", measure3],
100 |       ["guitar", {"octave": 2}, measure1],
101 |       ["banjo", {"octave": 3}, measure1],
102 |       {"tempo": 120, "octave": 3},
103 |       [{"mode": "concurrent"},
104 |         ["banjo", measure1],
105 |         ["guitar", measure4]],
106 |       [{"mode": "concurrent"},
107 |         ["banjo", measure1],
108 |         ["guitar", measure4]],
109 |       [{"mode": "concurrent"},
110 |         ["banjo", measure1],
111 |         ["guitar", measure5]],
112 |       [{"mode": "concurrent"},
113 |         ["banjo", measure1],
114 |         ["guitar", measure6]]]
115 | 
116 | saveMusic("dueling_banjos.wav", score)
117 | 121 |

Originally created for Advent Of Nim 🎄👑, 2021, Day 3, Whale Music 🐳🎶.

122 | 123 |
124 | 132 |
133 |
import nimib
134 | 
135 | nbInit
136 | nbText: """# 🎶🐳 paramidib
137 | 
138 | paramidi**b** is an easy way to make [paramidi] available with [nimib].
139 | Install with `nimble install paramidib`.
140 | 
141 | It exports `paramidi` and provides two templates:
142 |   1. `saveMusic`: takes a valid score for paramidi and creates
143 |      a `wav` file with the generated music.
144 |      It is adapted from [paramidi_starter] and it can be used even
145 |      without nimib (paramidib itself does not depend on nimib).
146 |   2. `nbAudio`: a new nimib block which create an audio control
147 |      to play the `wav` file.
148 |      In the following example is called with `nbAudio("output.wav")`
149 | 
150 | [paramidi]: https://github.com/paranim/paramidi
151 | [nimib]: https://github.com/pietroppeter/nimib
152 | [paramidi_starter]: https://github.com/paranim/paramidi_starter
153 | 
154 | """
155 | nbCode:
156 |   import paramidib
157 | 
158 |   saveMusic("output.wav"):
159 |     (piano,
160 |       (tempo: 74),
161 |       1/8, {-d, -a, e, fx}, a,
162 |       1/2, {fx, +d},
163 |       1/8, {-e, e, +c}, a,
164 |       1/2, {c, e},
165 |       1/8, {-d, -a, e, fx}, a, +d, +cx, +e, +d, b, +cx,
166 |       1/2, {-e, c, a}, {c, e})
167 | nbAudio("output.wav")
168 | nbText: "Another example where the score is a json object generated at runtime:"
169 | nbCode:
170 |   import json
171 | 
172 |   let
173 |     measure1 = %*[1/16, "b", "c+", 1/8, "d+", "b", "c+", "a", "b", "g", "a"]
174 |     measure2 = %*[1/16, "g", "g", 1/8, "g", "a", "b", "c+", "d+", "c+", 1/2, "b"]
175 |     measure3 = %*[1/16, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "d", "b-", "g-"],
176 |                   1/8, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "e", "c", "g-"],
177 |                   [{"mode": "concurrent"}, "d", "b-", "g-"]]
178 |     measure4 = %*[1/16, "r", "r", 1/8, "g", "r", "d", "r", "g", "g", "d"]
179 |     measure5 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, "a", "d"]
180 |     measure6 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, [{"mode": "concurrent"}, "f", "a-"], "b", 1/4, "c"]
181 | 
182 |     score* =
183 |       %*[{"tempo": 80, "octave": 3},
184 |         ["guitar", measure1],
185 |         ["banjo", measure1],
186 |         ["guitar", measure1],
187 |         ["guitar", 1/2, "d", 1/8, "g", "g", "a", "b", "g", "b", 1/2, "a"],
188 |         ["banjo", 1/8, "g", "g", "a", "b", 1/2, "g"],
189 |         ["guitar", {"octave": 2}, measure2],
190 |         ["banjo", measure2],
191 |         ["guitar", {"octave": 2}, measure2],
192 |         ["banjo", measure2],
193 |         ["guitar", measure2],
194 |         ["banjo", measure2],
195 |         {"octave": 4},
196 |         ["guitar", measure3],
197 |         ["banjo", measure3],
198 |         ["guitar", measure3],
199 |         ["banjo", measure3],
200 |         ["guitar", {"octave": 2}, measure1],
201 |         ["banjo", {"octave": 3}, measure1],
202 |         {"tempo": 120, "octave": 3},
203 |         [{"mode": "concurrent"},
204 |           ["banjo", measure1],
205 |           ["guitar", measure4]],
206 |         [{"mode": "concurrent"},
207 |           ["banjo", measure1],
208 |           ["guitar", measure4]],
209 |         [{"mode": "concurrent"},
210 |           ["banjo", measure1],
211 |           ["guitar", measure5]],
212 |         [{"mode": "concurrent"},
213 |           ["banjo", measure1],
214 |           ["guitar", measure6]]]
215 | 
216 |   saveMusic("dueling_banjos.wav", score)
217 | nbAudio("dueling_banjos.wav")
218 | nbText: "Originally created for Advent Of Nim 🎄👑, 2021, Day 3, [Whale Music](https://pietroppeter.github.io/adventofnim/2021/day03.html#whale_music) 🐳🎶."
219 | nbSave
220 |
233 | --------------------------------------------------------------------------------